574 lines
32 KiB
JavaScript
574 lines
32 KiB
JavaScript
export function createChatStatements(db) {
|
|
return {
|
|
insertChatSession: db.prepare(
|
|
"INSERT INTO chat_sessions (username, title, assistant_persona_id, user_persona_id, folder_name, is_pinned, archived_at, scenario_prompt, scenario_summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
),
|
|
listChatSessions: db.prepare(`
|
|
SELECT cs.id,
|
|
CASE
|
|
WHEN cs.assistant_persona_id IS NOT NULL AND cs.source_chat_id IS NULL THEN COALESCE(p.name, cs.title)
|
|
ELSE cs.title
|
|
END AS title,
|
|
cs.title AS stored_title,
|
|
cs.assistant_persona_id,
|
|
cs.personality_id,
|
|
p.name AS assistant_persona_name,
|
|
cs.user_persona_id,
|
|
up.name AS user_persona_name,
|
|
cs.folder_name,
|
|
cs.is_pinned,
|
|
cs.archived_at,
|
|
cs.scenario_prompt,
|
|
cs.scenario_summary,
|
|
cs.context_summary,
|
|
cs.context_summary_message_id,
|
|
cs.active_leaf_message_id,
|
|
cs.source_chat_id,
|
|
cs.branched_from_message_id,
|
|
cs.preferred_model,
|
|
cs.temperature,
|
|
cs.context_length,
|
|
cs.response_length,
|
|
cs.system_instruction,
|
|
cs.created_at,
|
|
cs.updated_at,
|
|
(SELECT substr(m.content, 1, 180) FROM chat_messages m
|
|
WHERE m.chat_id = cs.id AND m.content <> ''
|
|
ORDER BY m.id DESC LIMIT 1) AS last_message_preview
|
|
FROM chat_sessions cs
|
|
LEFT JOIN personas p ON p.id = cs.assistant_persona_id
|
|
LEFT JOIN personas up ON up.id = cs.user_persona_id
|
|
WHERE cs.username = ?
|
|
ORDER BY cs.is_pinned DESC, datetime(cs.updated_at) DESC
|
|
`),
|
|
countChatSessions: db.prepare("SELECT COUNT(*) as count FROM chat_sessions WHERE username = ? AND archived_at IS NULL"),
|
|
getChatSession: db.prepare(`
|
|
SELECT cs.id,
|
|
CASE
|
|
WHEN cs.assistant_persona_id IS NOT NULL AND cs.source_chat_id IS NULL THEN COALESCE(p.name, cs.title)
|
|
ELSE cs.title
|
|
END AS title,
|
|
cs.title AS stored_title,
|
|
cs.assistant_persona_id,
|
|
cs.personality_id,
|
|
p.name AS assistant_persona_name,
|
|
cs.user_persona_id,
|
|
up.name AS user_persona_name,
|
|
cs.folder_name,
|
|
cs.is_pinned,
|
|
cs.archived_at,
|
|
cs.scenario_prompt,
|
|
cs.scenario_summary,
|
|
cs.context_summary,
|
|
cs.context_summary_message_id,
|
|
cs.active_leaf_message_id,
|
|
cs.source_chat_id,
|
|
cs.branched_from_message_id,
|
|
cs.preferred_model,
|
|
cs.temperature,
|
|
cs.context_length,
|
|
cs.response_length,
|
|
cs.system_instruction,
|
|
cs.created_at,
|
|
cs.updated_at
|
|
FROM chat_sessions cs
|
|
LEFT JOIN personas p ON p.id = cs.assistant_persona_id
|
|
LEFT JOIN personas up ON up.id = cs.user_persona_id
|
|
WHERE cs.id = ?
|
|
AND cs.username = ?
|
|
`),
|
|
getChatSessionByParticipants: db.prepare(`
|
|
SELECT cs.id,
|
|
CASE
|
|
WHEN cs.assistant_persona_id IS NOT NULL AND cs.source_chat_id IS NULL THEN COALESCE(p.name, cs.title)
|
|
ELSE cs.title
|
|
END AS title,
|
|
cs.title AS stored_title,
|
|
cs.assistant_persona_id,
|
|
cs.personality_id,
|
|
p.name AS assistant_persona_name,
|
|
cs.user_persona_id,
|
|
up.name AS user_persona_name,
|
|
cs.folder_name,
|
|
cs.is_pinned,
|
|
cs.archived_at,
|
|
cs.scenario_prompt,
|
|
cs.scenario_summary,
|
|
cs.context_summary,
|
|
cs.context_summary_message_id,
|
|
cs.active_leaf_message_id,
|
|
cs.source_chat_id,
|
|
cs.branched_from_message_id,
|
|
cs.preferred_model,
|
|
cs.temperature,
|
|
cs.context_length,
|
|
cs.response_length,
|
|
cs.system_instruction,
|
|
cs.created_at,
|
|
cs.updated_at
|
|
FROM chat_sessions cs
|
|
LEFT JOIN personas p ON p.id = cs.assistant_persona_id
|
|
LEFT JOIN personas up ON up.id = cs.user_persona_id
|
|
WHERE cs.username = ?
|
|
AND cs.assistant_persona_id = ?
|
|
AND ((cs.user_persona_id IS NULL AND ? IS NULL) OR cs.user_persona_id = ?)
|
|
AND cs.source_chat_id IS NULL
|
|
ORDER BY cs.is_pinned DESC, datetime(cs.updated_at) DESC
|
|
LIMIT 1
|
|
`),
|
|
updateChatSessionTitle: db.prepare("UPDATE chat_sessions SET title = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatUserPersona: db.prepare("UPDATE chat_sessions SET user_persona_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatPersonality: db.prepare("UPDATE chat_sessions SET personality_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatSessionScene: db.prepare("UPDATE chat_sessions SET scenario_prompt = ?, scenario_summary = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatContextSummary: db.prepare("UPDATE chat_sessions SET context_summary = ?, context_summary_message_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatGenerationSettings: db.prepare(`
|
|
UPDATE chat_sessions
|
|
SET preferred_model = ?, temperature = ?, context_length = ?, response_length = ?, system_instruction = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ? AND username = ?
|
|
`),
|
|
updateChatPreferredModel: db.prepare("UPDATE chat_sessions SET preferred_model = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatActiveLeaf: db.prepare("UPDATE chat_sessions SET active_leaf_message_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
updateChatBranchSource: db.prepare("UPDATE chat_sessions SET source_chat_id = ?, branched_from_message_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
setChatActiveLeafById: db.prepare("UPDATE chat_sessions SET active_leaf_message_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"),
|
|
updateChatSessionOrganization: db.prepare(`
|
|
UPDATE chat_sessions
|
|
SET folder_name = ?,
|
|
is_pinned = ?,
|
|
archived_at = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
AND username = ?
|
|
`),
|
|
touchChatSession: db.prepare("UPDATE chat_sessions SET updated_at = CURRENT_TIMESTAMP WHERE id = ? AND username = ?"),
|
|
deleteChatSession: db.prepare("DELETE FROM chat_sessions WHERE id = ? AND username = ?"),
|
|
insertChatMessage: db.prepare("INSERT INTO chat_messages (chat_id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
updateChatMessageDelivery: db.prepare("UPDATE chat_messages SET delivery_status = ?, error_message = ? WHERE id = ? AND chat_id = ?"),
|
|
updateChatMessageSources: db.prepare("UPDATE chat_messages SET sources = ? WHERE id = ? AND chat_id = ?"),
|
|
updateChatMessageThinking: db.prepare("UPDATE chat_messages SET thinking = ? WHERE id = ? AND chat_id = ?"),
|
|
getChatMessages: db.prepare(`
|
|
SELECT thinking, sources, id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at
|
|
FROM chat_messages
|
|
WHERE chat_id = ?
|
|
ORDER BY id
|
|
`),
|
|
getRecentChatMessages: db.prepare(`
|
|
WITH RECURSIVE branch AS (
|
|
SELECT cm.* FROM chat_messages cm
|
|
JOIN chat_sessions cs ON cs.id = cm.chat_id
|
|
WHERE cm.chat_id = ?
|
|
AND cm.id = COALESCE(cs.active_leaf_message_id, (SELECT MAX(id) FROM chat_messages WHERE chat_id = cm.chat_id))
|
|
UNION ALL
|
|
SELECT parent.* FROM chat_messages parent
|
|
JOIN branch child ON child.parent_message_id = parent.id
|
|
)
|
|
SELECT id, role, content FROM branch
|
|
WHERE delivery_status != 'failed'
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
`),
|
|
getRecentChatMessagesUpToId: db.prepare(`
|
|
WITH RECURSIVE branch AS (
|
|
SELECT * FROM chat_messages WHERE chat_id = ? AND id = ?
|
|
UNION ALL
|
|
SELECT parent.* FROM chat_messages parent
|
|
JOIN branch child ON child.parent_message_id = parent.id
|
|
)
|
|
SELECT id, role, content FROM branch
|
|
WHERE delivery_status != 'failed'
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
`),
|
|
getChatBranchThroughMessage: db.prepare(`
|
|
WITH RECURSIVE branch AS (
|
|
SELECT * FROM chat_messages WHERE chat_id = ? AND id = ?
|
|
UNION ALL
|
|
SELECT parent.* FROM chat_messages parent
|
|
JOIN branch child ON child.parent_message_id = parent.id
|
|
)
|
|
SELECT id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used,
|
|
retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at, sources, thinking
|
|
FROM branch
|
|
WHERE delivery_status = 'complete'
|
|
ORDER BY id
|
|
`),
|
|
getActiveBranchMessages: db.prepare(`
|
|
WITH RECURSIVE branch AS (
|
|
SELECT cm.* FROM chat_messages cm
|
|
JOIN chat_sessions cs ON cs.id = cm.chat_id
|
|
WHERE cm.chat_id = ?
|
|
AND cm.id = COALESCE(cs.active_leaf_message_id, (SELECT MAX(id) FROM chat_messages WHERE chat_id = cm.chat_id))
|
|
UNION ALL
|
|
SELECT parent.* FROM chat_messages parent
|
|
JOIN branch child ON child.parent_message_id = parent.id
|
|
)
|
|
SELECT id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used,
|
|
retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at, sources, thinking
|
|
FROM branch ORDER BY id
|
|
`),
|
|
getSiblingChatMessages: db.prepare(`
|
|
SELECT id, role, content, parent_message_id
|
|
FROM chat_messages
|
|
WHERE chat_id = ? AND role = ? AND parent_message_id IS ?
|
|
ORDER BY id
|
|
`),
|
|
getLatestDescendantMessage: db.prepare(`
|
|
WITH RECURSIVE descendants AS (
|
|
SELECT id FROM chat_messages WHERE chat_id = ? AND id = ?
|
|
UNION ALL
|
|
SELECT child.id FROM chat_messages child JOIN descendants parent ON child.parent_message_id = parent.id
|
|
WHERE child.chat_id = ? AND child.delivery_status != 'failed'
|
|
)
|
|
SELECT cm.* FROM chat_messages cm JOIN descendants d ON d.id = cm.id ORDER BY cm.id DESC LIMIT 1
|
|
`),
|
|
getChatMessageById: db.prepare(`
|
|
SELECT thinking, sources, id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at
|
|
FROM chat_messages
|
|
WHERE id = ?
|
|
AND chat_id = ?
|
|
`),
|
|
getChatMessageByIndex: db.prepare(`
|
|
SELECT thinking, sources, id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at
|
|
FROM chat_messages
|
|
WHERE chat_id = ?
|
|
ORDER BY id
|
|
LIMIT 1 OFFSET ?
|
|
`),
|
|
getLatestChatMessage: db.prepare(`
|
|
SELECT id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at
|
|
FROM chat_messages
|
|
WHERE chat_id = ?
|
|
ORDER BY id DESC
|
|
LIMIT 1
|
|
`),
|
|
getPreviousUserMessage: db.prepare(`
|
|
SELECT id, role, content, model_name, retry_variants, retry_active_index, retry_retries_used, retry_prompt_message_id, delivery_status, error_message, parent_message_id, created_at
|
|
FROM chat_messages
|
|
WHERE chat_id = ?
|
|
AND role = 'user'
|
|
AND id < ?
|
|
ORDER BY id DESC
|
|
LIMIT 1
|
|
`),
|
|
listChatMemories: db.prepare(`
|
|
SELECT id, username, chat_id, assistant_persona_id, user_persona_id, fact, created_at, updated_at
|
|
FROM chat_memories
|
|
WHERE chat_id = ?
|
|
AND username = ?
|
|
ORDER BY datetime(updated_at) DESC, id DESC
|
|
`),
|
|
getChatMemory: db.prepare(`
|
|
SELECT id, username, chat_id, assistant_persona_id, user_persona_id, fact, created_at, updated_at
|
|
FROM chat_memories
|
|
WHERE id = ?
|
|
AND chat_id = ?
|
|
AND username = ?
|
|
`),
|
|
insertChatMemory: db.prepare(`
|
|
INSERT INTO chat_memories (username, chat_id, assistant_persona_id, user_persona_id, fact)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
`),
|
|
updateChatMemory: db.prepare(`
|
|
UPDATE chat_memories
|
|
SET fact = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
AND chat_id = ?
|
|
AND username = ?
|
|
`),
|
|
deleteChatMemory: db.prepare("DELETE FROM chat_memories WHERE id = ? AND chat_id = ? AND username = ?"),
|
|
getPersonaChatState: db.prepare(`
|
|
SELECT id,
|
|
username,
|
|
chat_id,
|
|
assistant_persona_id,
|
|
user_persona_id,
|
|
relationship_notes,
|
|
tone,
|
|
current_location,
|
|
goals,
|
|
unresolved_threads,
|
|
boundaries,
|
|
created_at,
|
|
updated_at
|
|
FROM persona_chat_state
|
|
WHERE chat_id = ?
|
|
AND username = ?
|
|
`),
|
|
upsertPersonaChatState: db.prepare(`
|
|
INSERT INTO persona_chat_state (
|
|
username,
|
|
chat_id,
|
|
assistant_persona_id,
|
|
user_persona_id,
|
|
relationship_notes,
|
|
tone,
|
|
current_location,
|
|
goals,
|
|
unresolved_threads,
|
|
boundaries
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT(chat_id) DO UPDATE SET
|
|
username = excluded.username,
|
|
assistant_persona_id = excluded.assistant_persona_id,
|
|
user_persona_id = excluded.user_persona_id,
|
|
relationship_notes = excluded.relationship_notes,
|
|
tone = excluded.tone,
|
|
current_location = excluded.current_location,
|
|
goals = excluded.goals,
|
|
unresolved_threads = excluded.unresolved_threads,
|
|
boundaries = excluded.boundaries,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
`),
|
|
updateChatMessage: db.prepare("UPDATE chat_messages SET content = ? WHERE id = ? AND chat_id = ?"),
|
|
updateChatMessageWithRetryState: db.prepare("UPDATE chat_messages SET content = ?, retry_variants = ?, retry_active_index = ?, retry_retries_used = ?, retry_prompt_message_id = ? WHERE id = ? AND chat_id = ?"),
|
|
deleteChatMessage: db.prepare("DELETE FROM chat_messages WHERE id = ? AND chat_id = ?"),
|
|
deleteChatMessages: db.prepare("DELETE FROM chat_messages WHERE chat_id = ?"),
|
|
searchChats: db.prepare(`
|
|
SELECT cs.id,
|
|
CASE
|
|
WHEN cs.assistant_persona_id IS NOT NULL AND cs.source_chat_id IS NULL THEN COALESCE(p.name, cs.title)
|
|
ELSE cs.title
|
|
END AS title,
|
|
cs.title AS stored_title,
|
|
cs.assistant_persona_id,
|
|
cs.personality_id,
|
|
p.name AS assistant_persona_name,
|
|
cs.user_persona_id,
|
|
up.name AS user_persona_name,
|
|
cs.folder_name,
|
|
cs.is_pinned,
|
|
cs.archived_at,
|
|
cs.scenario_prompt,
|
|
cs.scenario_summary,
|
|
cs.context_summary,
|
|
cs.context_summary_message_id,
|
|
cs.active_leaf_message_id,
|
|
cs.source_chat_id,
|
|
cs.branched_from_message_id,
|
|
cs.preferred_model,
|
|
cs.temperature,
|
|
cs.context_length,
|
|
cs.response_length,
|
|
cs.system_instruction,
|
|
cs.created_at,
|
|
cs.updated_at,
|
|
(SELECT substr(m.content, 1, 180) FROM chat_messages m
|
|
WHERE m.chat_id = cs.id AND m.content <> ''
|
|
ORDER BY m.id DESC LIMIT 1) AS last_message_preview,
|
|
cm.id AS matched_message_id,
|
|
cm.content AS matched_content
|
|
FROM chat_sessions cs
|
|
LEFT JOIN personas p ON p.id = cs.assistant_persona_id
|
|
LEFT JOIN personas up ON up.id = cs.user_persona_id
|
|
LEFT JOIN chat_messages cm ON cm.id = (
|
|
SELECT m.id FROM chat_messages m
|
|
WHERE m.chat_id = cs.id AND LOWER(m.content) LIKE ?
|
|
ORDER BY m.id DESC LIMIT 1
|
|
)
|
|
WHERE cs.username = ?
|
|
AND (
|
|
LOWER(CASE
|
|
WHEN cs.assistant_persona_id IS NOT NULL AND cs.source_chat_id IS NULL THEN COALESCE(p.name, cs.title)
|
|
ELSE cs.title
|
|
END) LIKE ?
|
|
OR cm.id IS NOT NULL
|
|
)
|
|
ORDER BY cs.is_pinned DESC, datetime(cs.updated_at) DESC
|
|
`)
|
|
};
|
|
}
|
|
|
|
export function createChatRepository(db, config, statements) {
|
|
const completeChatTurnTransaction = db.transaction((chatId, userMessageId, assistantContent, retryState, modelName, sources, thinking) => {
|
|
const assistantResult = statements.insertChatMessage.run(
|
|
chatId,
|
|
"bot",
|
|
String(assistantContent || ""),
|
|
modelName || null,
|
|
JSON.stringify(retryState.retryVariants),
|
|
retryState.retryActiveIndex,
|
|
retryState.retryRetriesUsed,
|
|
retryState.retryPromptMessageId ?? null,
|
|
"complete",
|
|
null,
|
|
userMessageId
|
|
);
|
|
statements.updateChatMessageDelivery.run("complete", null, userMessageId, chatId);
|
|
if (sources) statements.updateChatMessageSources.run(JSON.stringify(sources), assistantResult.lastInsertRowid, chatId);
|
|
if (thinking) statements.updateChatMessageThinking.run(thinking, assistantResult.lastInsertRowid, chatId);
|
|
statements.setChatActiveLeafById.run(assistantResult.lastInsertRowid, chatId);
|
|
return assistantResult;
|
|
});
|
|
|
|
const insertBranchChatMessageTransaction = db.transaction((chatId, role, content, retryState, modelName, deliveryStatus, errorMessage, parentMessageId) => {
|
|
const result = statements.insertChatMessage.run(
|
|
chatId, role, String(content || ""), modelName || null,
|
|
retryState ? JSON.stringify(retryState.retryVariants) : null,
|
|
retryState?.retryActiveIndex ?? 0,
|
|
retryState?.retryRetriesUsed ?? 0,
|
|
retryState?.retryPromptMessageId ?? null,
|
|
deliveryStatus,
|
|
errorMessage,
|
|
parentMessageId ?? null
|
|
);
|
|
statements.setChatActiveLeafById.run(result.lastInsertRowid, chatId);
|
|
return result;
|
|
});
|
|
|
|
const failChatTurnTransaction = db.transaction((chatId, userMessageId, errorMessage, parentMessageId) => {
|
|
statements.updateChatMessageDelivery.run("failed", errorMessage, userMessageId, chatId);
|
|
statements.setChatActiveLeafById.run(parentMessageId ?? null, chatId);
|
|
});
|
|
return {
|
|
listChats: (username) => statements.listChatSessions.all(username),
|
|
searchChats(username, query) {
|
|
const like = `%${String(query || "").trim().toLowerCase()}%`;
|
|
return statements.searchChats.all(like, username, like);
|
|
},
|
|
countChats: (username) => statements.countChatSessions.get(username).count,
|
|
getChat: (chatId, username) => statements.getChatSession.get(chatId, username),
|
|
getChatByParticipants: (username, assistantPersonaId, userPersonaId) => statements.getChatSessionByParticipants.get(username, assistantPersonaId, userPersonaId, userPersonaId),
|
|
createChat(username, title, assistantPersonaId = null, userPersonaId = null, scenarioPrompt = null, scenarioSummary = null, organization = {}) {
|
|
const chatId = statements.insertChatSession.run(
|
|
username,
|
|
title,
|
|
assistantPersonaId,
|
|
userPersonaId,
|
|
organization.folderName ?? null,
|
|
organization.isPinned ? 1 : 0,
|
|
organization.archivedAt ?? null,
|
|
scenarioPrompt,
|
|
scenarioSummary
|
|
).lastInsertRowid;
|
|
return statements.getChatSession.get(chatId, username);
|
|
},
|
|
updateChatTitle: (chatId, username, title) => statements.updateChatSessionTitle.run(title, chatId, username),
|
|
updateChatUserPersona: (chatId, username, personaId) => statements.updateChatUserPersona.run(personaId ?? null, chatId, username),
|
|
updateChatPersonality: (chatId, username, personalityId) => statements.updateChatPersonality.run(personalityId, chatId, username),
|
|
updateChatScene: (chatId, username, scenarioPrompt, scenarioSummary) => statements.updateChatSessionScene.run(scenarioPrompt, scenarioSummary, chatId, username),
|
|
updateChatContextSummary: (chatId, username, summary, messageId) => statements.updateChatContextSummary.run(summary, Number(messageId || 0), chatId, username),
|
|
getPersonaChatState: (chatId, username) => statements.getPersonaChatState.get(chatId, username) || null,
|
|
updatePersonaChatState(username, chat, state) {
|
|
statements.upsertPersonaChatState.run(
|
|
username,
|
|
chat.id,
|
|
chat.assistant_persona_id ?? null,
|
|
chat.user_persona_id ?? null,
|
|
state.relationship_notes ?? null,
|
|
state.tone ?? null,
|
|
state.current_location ?? null,
|
|
state.goals ?? null,
|
|
state.unresolved_threads ?? null,
|
|
state.boundaries ?? null
|
|
);
|
|
return statements.getPersonaChatState.get(chat.id, username);
|
|
},
|
|
updateChatOrganization(chatId, username, {folderName = null, isPinned = false, archivedAt = null}) {
|
|
return statements.updateChatSessionOrganization.run(folderName, isPinned ? 1 : 0, archivedAt, chatId, username);
|
|
},
|
|
touchChat: (chatId, username) => statements.touchChatSession.run(chatId, username),
|
|
deleteChat: (chatId, username) => statements.deleteChatSession.run(chatId, username),
|
|
listChatMessages: (chatId) => statements.getChatMessages.all(chatId),
|
|
listActiveBranchMessages: (chatId) => statements.getActiveBranchMessages.all(chatId),
|
|
listChatBranchThroughMessage: (chatId, messageId) => statements.getChatBranchThroughMessage.all(chatId, messageId),
|
|
listSiblingChatMessages: (chatId, role, parentMessageId) => statements.getSiblingChatMessages.all(chatId, role, parentMessageId ?? null),
|
|
getLatestDescendantMessage: (chatId, messageId) => statements.getLatestDescendantMessage.get(chatId, messageId, chatId),
|
|
getRecentChatMessages: (chatId) => statements.getRecentChatMessages.all(chatId, config.chatHistoryLimit),
|
|
getRecentChatMessagesUpToId: (chatId, messageId) => statements.getRecentChatMessagesUpToId.all(chatId, messageId, config.chatHistoryLimit),
|
|
getChatMessage: (chatId, messageId) => statements.getChatMessageById.get(messageId, chatId),
|
|
getChatMessageByIndex: (chatId, index) => statements.getChatMessageByIndex.get(chatId, index),
|
|
getLatestChatMessage: (chatId) => statements.getLatestChatMessage.get(chatId),
|
|
getPreviousUserMessage: (chatId, messageId) => statements.getPreviousUserMessage.get(chatId, messageId),
|
|
listChatMemories: (chatId, username) => statements.listChatMemories.all(chatId, username),
|
|
getChatMemory: (chatId, memoryId, username) => statements.getChatMemory.get(memoryId, chatId, username),
|
|
createChatMemory(username, chat, fact) {
|
|
const result = statements.insertChatMemory.run(
|
|
username,
|
|
chat.id,
|
|
chat.assistant_persona_id ?? null,
|
|
chat.user_persona_id ?? null,
|
|
String(fact || "").trim()
|
|
);
|
|
return statements.getChatMemory.get(result.lastInsertRowid, chat.id, username);
|
|
},
|
|
updateChatMemory: (chatId, memoryId, username, fact) => statements.updateChatMemory.run(String(fact || "").trim(), memoryId, chatId, username),
|
|
deleteChatMemory: (chatId, memoryId, username) => statements.deleteChatMemory.run(memoryId, chatId, username),
|
|
insertChatMessage(chatId, role, content, retryState = null, modelName = null, deliveryStatus = "complete", errorMessage = null, parentMessageId = null) {
|
|
return statements.insertChatMessage.run(
|
|
chatId,
|
|
role,
|
|
String(content || ""),
|
|
modelName || null,
|
|
retryState ? JSON.stringify(retryState.retryVariants) : null,
|
|
retryState?.retryActiveIndex ?? 0,
|
|
retryState?.retryRetriesUsed ?? 0,
|
|
retryState?.retryPromptMessageId ?? null,
|
|
deliveryStatus,
|
|
errorMessage,
|
|
parentMessageId
|
|
);
|
|
},
|
|
insertBranchChatMessage: (chatId, role, content, retryState = null, modelName = null, deliveryStatus = "complete", errorMessage = null, parentMessageId = null) =>
|
|
insertBranchChatMessageTransaction(chatId, role, content, retryState, modelName, deliveryStatus, errorMessage, parentMessageId),
|
|
updateChatMessageDelivery: (chatId, messageId, deliveryStatus, errorMessage = null) =>
|
|
statements.updateChatMessageDelivery.run(deliveryStatus, errorMessage, messageId, chatId),
|
|
completeChatTurn: (chatId, userMessageId, assistantContent, retryState, modelName = null, sources = null, thinking = null) =>
|
|
completeChatTurnTransaction(chatId, userMessageId, assistantContent, retryState, modelName, sources, thinking),
|
|
updateChatMessageThinking: (chatId, messageId, thinking) =>
|
|
statements.updateChatMessageThinking.run(thinking, messageId, chatId),
|
|
updateChatMessageSources: (chatId, messageId, sources) =>
|
|
statements.updateChatMessageSources.run(JSON.stringify(sources), messageId, chatId),
|
|
failChatTurn: (chatId, userMessageId, errorMessage, parentMessageId = null) =>
|
|
failChatTurnTransaction(chatId, userMessageId, errorMessage, parentMessageId),
|
|
updateChatActiveLeaf: (chatId, username, messageId) => statements.updateChatActiveLeaf.run(messageId, chatId, username),
|
|
updateChatBranchSource: (chatId, username, sourceChatId, messageId) =>
|
|
statements.updateChatBranchSource.run(sourceChatId, messageId ?? null, chatId, username),
|
|
updateChatGenerationSettings: (chatId, username, settings) => statements.updateChatGenerationSettings.run(
|
|
settings.preferredModel ?? null,
|
|
settings.temperature ?? null,
|
|
settings.contextLength ?? null,
|
|
settings.responseLength ?? null,
|
|
settings.systemInstruction ?? null,
|
|
chatId,
|
|
username
|
|
),
|
|
updateChatPreferredModel: (chatId, username, model) => statements.updateChatPreferredModel.run(model || null, chatId, username),
|
|
listMessageVersions: (messageId) => db.prepare("SELECT * FROM chat_message_versions WHERE message_id = ? ORDER BY id").all(messageId),
|
|
saveMessageVersion: (chatId, messageId, version = null, selectedIndex = null) => db.transaction(() => {
|
|
const row = statements.getChatMessageById.get(messageId, chatId);
|
|
if (!row) throw new Error("Message not found");
|
|
let versions = db.prepare("SELECT * FROM chat_message_versions WHERE message_id = ? ORDER BY id").all(messageId);
|
|
const insert = db.prepare("INSERT INTO chat_message_versions (message_id, content, thinking, sources, model_name) VALUES (?, ?, ?, ?, ?)");
|
|
if (!versions.length) {
|
|
db.prepare("INSERT INTO chat_message_versions (message_id, content, thinking, sources, model_name, created_at) VALUES (?, ?, ?, ?, ?, ?)")
|
|
.run(messageId, row.content, row.thinking, row.sources, row.model_name, row.created_at);
|
|
} else {
|
|
// Edits keep their existing semantics: update the selected answer.
|
|
db.prepare("UPDATE chat_message_versions SET content = ?, thinking = ?, sources = ?, model_name = ? WHERE id = ?")
|
|
.run(row.content, row.thinking, row.sources, row.model_name, versions[row.retry_active_index]?.id ?? versions[0].id);
|
|
}
|
|
if (version) insert.run(messageId, version.content, version.thinking || null, JSON.stringify(version.sources ?? null), version.modelName);
|
|
versions = db.prepare("SELECT * FROM chat_message_versions WHERE message_id = ? ORDER BY id").all(messageId);
|
|
const index = version ? versions.length - 1 : selectedIndex;
|
|
if (!Number.isInteger(index) || !versions[index]) throw new Error("Version is out of range");
|
|
const active = versions[index];
|
|
db.prepare(`UPDATE chat_messages SET content = ?, thinking = ?, sources = ?, model_name = ?,
|
|
retry_variants = ?, retry_active_index = ?, retry_retries_used = ? WHERE id = ? AND chat_id = ?`)
|
|
.run(active.content, active.thinking, active.sources, active.model_name, JSON.stringify(versions.map(item => item.content)), index, versions.length - 1, messageId, chatId);
|
|
})(),
|
|
updateChatMessage: (chatId, messageId, content) => statements.updateChatMessage.run(content, messageId, chatId),
|
|
updateChatMessageRetryState: (chatId, messageId, payload) => statements.updateChatMessageWithRetryState.run(
|
|
payload.content,
|
|
JSON.stringify(payload.retryVariants),
|
|
payload.retryActiveIndex,
|
|
payload.retryRetriesUsed,
|
|
payload.retryPromptMessageId ?? null,
|
|
messageId,
|
|
chatId
|
|
),
|
|
deleteChatMessage: (chatId, messageId) => statements.deleteChatMessage.run(messageId, chatId),
|
|
clearChatMessages: (chatId) => statements.deleteChatMessages.run(chatId)
|
|
};
|
|
}
|