new chatlistview component, file attachments, performance & primitive test
This commit is contained in:
@@ -65,10 +65,19 @@ struct GenerationRow {
|
||||
QVector<SegmentRow> segments;
|
||||
};
|
||||
|
||||
struct AttachmentRow {
|
||||
QString name;
|
||||
QString path;
|
||||
int kind = 1;
|
||||
QString mime;
|
||||
qint64 size = 0;
|
||||
};
|
||||
|
||||
struct MessageRow {
|
||||
bool user = false;
|
||||
qint64 timestamp = 0;
|
||||
QVector<GenerationRow> generations;
|
||||
QVector<AttachmentRow> attachments;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -160,6 +169,20 @@ void ChatStore::openDb() {
|
||||
" timestamp INTEGER NOT NULL\n"
|
||||
")"));
|
||||
}
|
||||
{
|
||||
QSqlQuery query(db);
|
||||
query.exec(QStringLiteral(
|
||||
"CREATE TABLE IF NOT EXISTS attachments (\n"
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
|
||||
" message_id INTEGER NOT NULL REFERENCES messages "
|
||||
"(id) ON DELETE CASCADE,\n"
|
||||
" name TEXT NOT NULL,\n"
|
||||
" path TEXT NOT NULL,\n"
|
||||
" kind INTEGER NOT NULL DEFAULT 1,\n"
|
||||
" mime TEXT NOT NULL DEFAULT '',\n"
|
||||
" size INTEGER NOT NULL DEFAULT 0\n"
|
||||
")"));
|
||||
}
|
||||
{
|
||||
QSqlQuery query(db);
|
||||
query.exec(QStringLiteral(
|
||||
@@ -171,6 +194,9 @@ void ChatStore::openDb() {
|
||||
query.exec(QStringLiteral(
|
||||
"CREATE INDEX IF NOT EXISTS idx_segments_generation "
|
||||
"ON segments (generation_id)"));
|
||||
query.exec(QStringLiteral(
|
||||
"CREATE INDEX IF NOT EXISTS idx_attachments_message "
|
||||
"ON attachments (message_id)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +259,7 @@ void ChatStore::removeSession(ChatSession* session) {
|
||||
query.bindValue(":id", session->id());
|
||||
query.exec();
|
||||
}
|
||||
QDir(ChatSession::sandboxDirFor(session->id())).removeRecursively();
|
||||
m_sessions.removeOne(session);
|
||||
session->deleteLater();
|
||||
notify(before);
|
||||
@@ -330,6 +357,11 @@ bool ChatStore::saveSession(ChatSession* session) {
|
||||
"timestamp) VALUES (:gid, :type, :text, :name, "
|
||||
":tool_call_id, :arguments, :result, :status, :elapsed_ms, "
|
||||
":timestamp)");
|
||||
QSqlQuery attachmentInsert(handle);
|
||||
ok = ok && attachmentInsert.prepare(
|
||||
"INSERT INTO attachments (message_id, name, path, kind, "
|
||||
"mime, size) VALUES (:mid, :name, :path, :kind, :mime, "
|
||||
":size)");
|
||||
const auto* model = session->messagesModel();
|
||||
for (int row = model->rowCount() - 1; ok && row >= 0; --row) {
|
||||
const auto* message = model->at(row);
|
||||
@@ -390,6 +422,26 @@ bool ChatStore::saveSession(ChatSession* session) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
for (const auto* attachment : message->attachments()) {
|
||||
attachmentInsert.bindValue(":mid", messageId);
|
||||
attachmentInsert.bindValue(
|
||||
":name", sqlText(attachment->name()));
|
||||
attachmentInsert.bindValue(
|
||||
":path", sqlText(attachment->path()));
|
||||
attachmentInsert.bindValue(
|
||||
":kind", static_cast<int>(attachment->kind()));
|
||||
attachmentInsert.bindValue(
|
||||
":mime", sqlText(attachment->mimeType()));
|
||||
attachmentInsert.bindValue(":size", attachment->size());
|
||||
if (!attachmentInsert.exec()) {
|
||||
ok = false;
|
||||
qWarning() << "ChatStore: saveSession" << id
|
||||
<< "attachment insert failed:"
|
||||
<< attachmentInsert.lastError().text();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok || !handle.commit()) {
|
||||
@@ -497,6 +549,34 @@ void ChatStore::loadMessagesInto(ChatSession* session) {
|
||||
<< messageId << ":"
|
||||
<< generationQuery.lastError().text();
|
||||
}
|
||||
QSqlQuery attachmentQuery(db);
|
||||
attachmentQuery.prepare(
|
||||
"SELECT name, path, kind, mime, size FROM "
|
||||
"attachments WHERE message_id = :mid "
|
||||
"ORDER BY rowid");
|
||||
attachmentQuery.bindValue(":mid", messageId);
|
||||
if (attachmentQuery.exec()) {
|
||||
while (attachmentQuery.next()) {
|
||||
AttachmentRow attachment;
|
||||
attachment.name =
|
||||
attachmentQuery.value(0).toString();
|
||||
attachment.path =
|
||||
attachmentQuery.value(1).toString();
|
||||
attachment.kind =
|
||||
attachmentQuery.value(2).toInt();
|
||||
attachment.mime =
|
||||
attachmentQuery.value(3).toString();
|
||||
attachment.size =
|
||||
attachmentQuery.value(4).toLongLong();
|
||||
message.attachments.append(attachment);
|
||||
}
|
||||
} else {
|
||||
qWarning()
|
||||
<< "ChatStore: failed to load attachments "
|
||||
"for message"
|
||||
<< messageId << ":"
|
||||
<< attachmentQuery.lastError().text();
|
||||
}
|
||||
rows.append(message);
|
||||
}
|
||||
}
|
||||
@@ -523,6 +603,22 @@ void ChatStore::loadMessagesInto(ChatSession* session) {
|
||||
row.user ? ChatMessage::Role::User
|
||||
: ChatMessage::Role::Assistant,
|
||||
row.timestamp);
|
||||
if (!row.attachments.isEmpty()) {
|
||||
QList<Attachment*> attachments;
|
||||
for (const AttachmentRow& attachmentRow :
|
||||
row.attachments) {
|
||||
attachments.append(new Attachment(
|
||||
attachmentRow.name,
|
||||
attachmentRow.path,
|
||||
attachmentRow.kind == 0
|
||||
? Attachment::Kind::Image
|
||||
: Attachment::Kind::File,
|
||||
attachmentRow.mime,
|
||||
attachmentRow.size,
|
||||
message));
|
||||
}
|
||||
message->setAttachments(attachments);
|
||||
}
|
||||
int activeIndex = 0;
|
||||
for (int i = 0; i < row.generations.size(); ++i) {
|
||||
const GenerationRow& generationRow =
|
||||
|
||||
Reference in New Issue
Block a user