Files

1 line
87 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "cd547/xtsqlqueryagent", "files": {"backend/src/routes/session.js": {"d18718428ba5ef930a7ffc755283be7b07f6c9d7": {"t": 1785746023.2084477, "n": 9549, "h": "c37a094531de4a40", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { authRequired, sessionBelongsToUser } from '../services/auth.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 所有会话接口都要求登录\nrouter.use(authRequired);\n\n// 获取所有会话(分页)\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n // 分页参数:默认 20 条/页,单次上限 100 防止误传大数\n const limit = Math.max(1, Math.min(100, parseInt(req.query.limit, 10) || 20));\n const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);\n\n // #PERF-06:把相关子查询改成 LEFT JOIN + GROUP BY。\n // 旧写法依赖 SQLite 优化器把子查询\"展开\"为 join;\n // 显式 JOIN 后总是 1 次扫描 messages 表(可走 idx_messages_session_role 索引),\n // 避免 N+1 风险(每个 session 一次 SUM 扫描)。\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE(SUM(CASE WHEN m.role = 'usage' THEN m.total_tokens END), 0) AS total_tokens\n FROM sessions s\n LEFT JOIN messages m ON m.session_id = s.id\n WHERE s.user_id = ?\n GROUP BY s.id\n ORDER BY s.id DESC\n LIMIT ? OFFSET ?\n `).all(req.user.id, limit, offset);\n\n const total = db.prepare('SELECT COUNT(*) AS cnt FROM sessions WHERE user_id = ?').get(req.user.id).cnt;\n res.json({ sessions, total, hasMore: offset + sessions.length < total });\n } catch (error) {\n logger.error('Get sessions failed', { error: error.message, userId: req.user.id });\n res.status(500).json({ error: error.message, sessions: [], total: 0, hasMore: false });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(404).json({ error: '会话不存在', total_tokens: 0 });\n }\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n logger.error('Get session tokens failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前用户会话中的最大 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions WHERE user_id = ?').get(req.user.id);\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order, user_id) VALUES (?, ?, ?)').run(sessionName, newOrder, req.user.id);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n logger.error('Create session failed', { error: error.message, userId: req.user.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(404).json({ error: '会话不存在', messages: [] });\n }\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n logger.error('Get session messages failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n logger.error('Save message failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n logger.error('Update session name failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n logger.error('Delete session failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n\n try {\n if (!sessionBelongsToUser(sessionId, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.status(400).json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.status(400).json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.status(400).json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n\n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.status(500).json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "4f755ee5d7a130ad588f3691a4d6cb3024e5a831": {"t": 1785758881.529998, "n": 9549, "h": "c37a094531de4a40", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { authRequired, sessionBelongsToUser } from '../services/auth.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 所有会话接口都要求登录\nrouter.use(authRequired);\n\n// 获取所有会话(分页)\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n // 分页参数:默认 20 条/页,单次上限 100 防止误传大数\n const limit = Math.max(1, Math.min(100, parseInt(req.query.limit, 10) || 20));\n const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);\n\n // #PERF-06:把相关子查询改成 LEFT JOIN + GROUP BY。\n // 旧写法依赖 SQLite 优化器把子查询\"展开\"为 join;\n // 显式 JOIN 后总是 1 次扫描 messages 表(可走 idx_messages_session_role 索引),\n // 避免 N+1 风险(每个 session 一次 SUM 扫描)。\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE(SUM(CASE WHEN m.role = 'usage' THEN m.total_tokens END), 0) AS total_tokens\n FROM sessions s\n LEFT JOIN messages m ON m.session_id = s.id\n WHERE s.user_id = ?\n GROUP BY s.id\n ORDER BY s.id DESC\n LIMIT ? OFFSET ?\n `).all(req.user.id, limit, offset);\n\n const total = db.prepare('SELECT COUNT(*) AS cnt FROM sessions WHERE user_id = ?').get(req.user.id).cnt;\n res.json({ sessions, total, hasMore: offset + sessions.length < total });\n } catch (error) {\n logger.error('Get sessions failed', { error: error.message, userId: req.user.id });\n res.status(500).json({ error: error.message, sessions: [], total: 0, hasMore: false });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(404).json({ error: '会话不存在', total_tokens: 0 });\n }\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n logger.error('Get session tokens failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前用户会话中的最大 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions WHERE user_id = ?').get(req.user.id);\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order, user_id) VALUES (?, ?, ?)').run(sessionName, newOrder, req.user.id);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n logger.error('Create session failed', { error: error.message, userId: req.user.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(404).json({ error: '会话不存在', messages: [] });\n }\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n logger.error('Get session messages failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n logger.error('Save message failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n logger.error('Update session name failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n logger.error('Delete session failed', { error: error.message, sessionId: req.params.id });\n res.status(500).json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n\n try {\n if (!sessionBelongsToUser(sessionId, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.status(400).json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.status(400).json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.status(400).json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n\n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.status(500).json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "0934175e273e4f8cd681d54e94f0cb61dbee41ad": {"t": 1785758883.2725077, "n": 8731, "h": "e2ba543113c9ca04", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { authRequired, sessionBelongsToUser } from '../services/auth.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 所有会话接口都要求登录\nrouter.use(authRequired);\n\n// 获取所有会话(分页)\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n // 分页参数:默认 20 条/页,单次上限 100 防止误传大数\n const limit = Math.max(1, Math.min(100, parseInt(req.query.limit, 10) || 20));\n const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);\n\n // #PERF-06:把相关子查询改成 LEFT JOIN + GROUP BY。\n // 旧写法依赖 SQLite 优化器把子查询\"展开\"为 join;\n // 显式 JOIN 后总是 1 次扫描 messages 表(可走 idx_messages_session_role 索引),\n // 避免 N+1 风险(每个 session 一次 SUM 扫描)。\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE(SUM(CASE WHEN m.role = 'usage' THEN m.total_tokens END), 0) AS total_tokens\n FROM sessions s\n LEFT JOIN messages m ON m.session_id = s.id\n WHERE s.user_id = ?\n GROUP BY s.id\n ORDER BY s.id DESC\n LIMIT ? OFFSET ?\n `).all(req.user.id, limit, offset);\n\n const total = db.prepare('SELECT COUNT(*) AS cnt FROM sessions WHERE user_id = ?').get(req.user.id).cnt;\n res.json({ sessions, total, hasMore: offset + sessions.length < total });\n } catch (error) {\n res.json({ error: error.message, sessions: [], total: 0, hasMore: false });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', total_tokens: 0 });\n }\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前用户会话中的最大 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions WHERE user_id = ?').get(req.user.id);\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order, user_id) VALUES (?, ?, ?)').run(sessionName, newOrder, req.user.id);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', messages: [] });\n }\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n\n try {\n if (!sessionBelongsToUser(sessionId, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "05c2ef6123fb35b8cb3af4713e07ec31bb2a0d06": {"t": 1785758884.6004243, "n": 8312, "h": "15424ed92bf5a91c", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { authRequired, sessionBelongsToUser } from '../services/auth.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 所有会话接口都要求登录\nrouter.use(authRequired);\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n // #PERF-06:把相关子查询改成 LEFT JOIN + GROUP BY。\n // 旧写法依赖 SQLite 优化器把子查询\"展开\"为 join;\n // 显式 JOIN 后总是 1 次扫描 messages 表(可走 idx_messages_session_role 索引),\n // 避免 N+1 风险(每个 session 一次 SUM 扫描)。\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE(SUM(CASE WHEN m.role = 'usage' THEN m.total_tokens END), 0) AS total_tokens\n FROM sessions s\n LEFT JOIN messages m ON m.session_id = s.id\n WHERE s.user_id = ?\n GROUP BY s.id\n ORDER BY s.id DESC\n `).all(req.user.id);\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', total_tokens: 0 });\n }\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前用户会话中的最大 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions WHERE user_id = ?').get(req.user.id);\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order, user_id) VALUES (?, ?, ?)').run(sessionName, newOrder, req.user.id);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', messages: [] });\n }\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n\n try {\n if (!sessionBelongsToUser(sessionId, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "299d837bdc410f333b4eac6091856eb5095445ee": {"t": 1785758888.8553338, "n": 8074, "h": "e7e84632dafafc17", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { authRequired, sessionBelongsToUser } from '../services/auth.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 所有会话接口都要求登录\nrouter.use(authRequired);\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s\n WHERE s.user_id = ?\n ORDER BY s.id DESC\n `).all(req.user.id);\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', total_tokens: 0 });\n }\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前用户会话中的最大 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions WHERE user_id = ?').get(req.user.id);\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order, user_id) VALUES (?, ?, ?)').run(sessionName, newOrder, req.user.id);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.json({ error: '会话不存在', messages: [] });\n }\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n if (!sessionBelongsToUser(req.params.id, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n\n try {\n if (!sessionBelongsToUser(sessionId, req.user.id)) {\n return res.status(403).json({ error: '无权访问此会话' });\n }\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "0957e985058802344626f5f2bf6d6b45fed40cf6": {"t": 1785758890.1712284, "n": 7115, "h": "79b654f9b475e8c8", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { clearSessionRegistry } from '../services/llm.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n // 释放工具调用注册表\n clearSessionRegistry(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n \n try {\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "4aa1ca2001eaf0c33e7a16ab440d7b5887b02866": {"t": 1785758892.0168092, "n": 6998, "h": "fcc822c00fc40a37", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n // 先删除 llm_messages 表中相关记录(有外键约束)\n db.prepare('DELETE FROM llm_messages WHERE session_id = ?').run(req.params.id);\n // 再删除 messages 表中相关记录\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n // 最后删除会话记录\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n \n try {\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "83442067a5b7a820eb578cc850c5635498734d3e": {"t": 1785758893.39975, "n": 6833, "h": "8590b7fe6a5ba377", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\nimport { getLlmConfig } from '../services/config.js';\nimport { logger } from '../logger.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 总结会话\nrouter.post('/:id/summarize', async (req, res) => {\n const sessionId = req.params.id;\n \n try {\n const db = getDb();\n \n // 获取会话消息\n const messages = db.prepare(`\n SELECT role, content, sql, results FROM messages \n WHERE session_id = ? AND role IN ('user', 'assistant')\n ORDER BY id ASC\n `).all(sessionId);\n \n if (messages.length === 0) {\n return res.json({ error: '没有聊天记录可以总结', summary: '', name: '' });\n }\n \n // 构建对话内容\n let conversationText = '';\n for (const msg of messages) {\n if (msg.role === 'user') {\n conversationText += `用户: ${msg.content}\\n`;\n } else if (msg.role === 'assistant') {\n if (msg.sql) {\n conversationText += `助手: SQL: ${msg.sql}\\n`;\n }\n if (msg.content) {\n conversationText += `助手: ${msg.content}\\n`;\n }\n }\n }\n \n // 获取LLM配置\n let config;\n try {\n config = getLlmConfig();\n } catch (e) {\n return res.json({ error: 'LLM未配置', summary: '', name: '' });\n }\n \n const { provider, apiKey, model } = config;\n \n // 调用LLM API\n const prompt = `请总结以下对话内容,生成:\n1. 一个简洁的总结(100字左右)\n2. 一个20字以内的会话标签(用于显示在会话列表中,直接返回标签文字,不要加引号)\n\n请用JSON格式返回:\n{\"summary\": \"总结内容\", \"name\": \"标签内容\"}\n\n对话内容:\n${conversationText}`;\n \n let baseURL, llmModel;\n switch (provider) {\n case 'openai':\n baseURL = 'https://api.openai.com/v1';\n llmModel = model || 'gpt-4o';\n break;\n case 'deepseek':\n baseURL = 'https://api.deepseek.com';\n llmModel = model || 'deepseek-chat';\n break;\n case 'minimax':\n baseURL = 'https://api.minimax.chat/v1';\n llmModel = model || 'abab6.5s-chat';\n break;\n case 'ollama':\n baseURL = apiKey || 'http://localhost:11434';\n llmModel = model || 'llama3.2';\n break;\n default:\n return res.json({ error: `不支持的provider: ${provider}`, summary: '', name: '' });\n }\n \n const response = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify({\n model: llmModel,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0\n })\n });\n \n if (!response.ok) {\n const err = await response.json();\n throw new Error(err.error?.message || 'API调用失败');\n }\n \n const data = await response.json();\n const content = data.choices?.[0]?.message?.content || '';\n \n // 解析JSON\n let summary = '';\n let name = '';\n \n try {\n // 尝试提取JSON\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n summary = parsed.summary || '';\n name = parsed.name || '';\n } else {\n throw new Error('未找到JSON');\n }\n } catch (e) {\n // 尝试简单解析\n const summaryMatch = content.match(/[\"']summary[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n const nameMatch = content.match(/[\"']name[\"']\\s*[::]\\s*[\"']([^\"']+)[\"']/);\n summary = summaryMatch ? summaryMatch[1] : content.substring(0, 100);\n name = nameMatch ? nameMatch[1] : content.substring(0, 20);\n }\n \n // 截断name到20字\n if (name.length > 20) {\n name = name.substring(0, 20);\n }\n \n // 更新会话记录\n db.prepare('UPDATE sessions SET name = ?, summary = ? WHERE id = ?')\n .run(name, summary, sessionId);\n \n logger.info('Session summarized', { sessionId, name, summaryLength: summary.length });\n \n res.json({ success: true, summary, name });\n } catch (error) {\n logger.error('Summarize session failed', { error: error.message, sessionId });\n res.json({ error: error.message, summary: '', name: '' });\n }\n});\n\nexport default router;\n\n"}, "5d271d668ab46cf02da8cba87b37352da00b0e17": {"t": 1785758894.5848908, "n": 2935, "h": "30cc4ce9a896a449", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\nexport default router;\n\n"}, "cb8d2d86a14dd9e4df378bb1221bdf6a813512f8": {"t": 1785758895.8843946, "n": 3098, "h": "996959f41cff7db3", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id AND role = 'usage'), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ? AND role = ?').get(req.params.id, 'usage');\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n console.log('保存消息:', { sessionId: req.params.id, role, content: content?.substring(0, 30), sql: sql?.substring(0, 30) });\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n console.error('保存消息失败:', error);\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\nexport default router;\n\n"}, "3787698655c33e03787e93b8b13f6cb9aae82fc2": {"t": 1785758897.1863787, "n": 3057, "h": "7a904373d71732f3", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare(`\n SELECT s.id, s.name, s.sort_order, s.created_at,\n COALESCE((SELECT SUM(total_tokens) FROM messages WHERE session_id = s.id), 0) as total_tokens\n FROM sessions s ORDER BY s.id DESC\n `).all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 获取会话的 token 统计\nrouter.get('/:id/tokens', (req, res) => {\n try {\n const db = getDb();\n const result = db.prepare('SELECT COALESCE(SUM(total_tokens), 0) as total_tokens FROM messages WHERE session_id = ?').get(req.params.id);\n res.json({ total_tokens: result?.total_tokens || 0 });\n } catch (error) {\n res.json({ error: error.message, total_tokens: 0 });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n console.log('保存消息:', { sessionId: req.params.id, role, content: content?.substring(0, 30), sql: sql?.substring(0, 30) });\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n console.error('保存消息失败:', error);\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\nexport default router;\n\n"}, "e19aa02363373996e90061fb786e408d8b5b62aa": {"t": 1785758898.3461804, "n": 2509, "h": "8a0d0789e1e7d567", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare('SELECT * FROM sessions ORDER BY id DESC').all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const sessionName = name || `新对话#${newOrder}`;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(sessionName, newOrder);\n res.json({ id: result.lastInsertRowid, name: sessionName, sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n console.log('保存消息:', { sessionId: req.params.id, role, content: content?.substring(0, 30), sql: sql?.substring(0, 30) });\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n console.error('保存消息失败:', error);\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\nexport default router;\n\n"}, "fd5099d4f9f9faa8492babc3558d56abf46600bb": {"t": 1785758899.8232756, "n": 2462, "h": "c165a5f42b6f6bd5", "c": "import { Router } from 'express';\nimport { getDb } from '../db/sqlite.js';\n\nconst router = Router();\n\n// 获取所有会话\nrouter.get('/', (req, res) => {\n try {\n const db = getDb();\n const sessions = db.prepare('SELECT * FROM sessions ORDER BY id DESC').all();\n res.json({ sessions });\n } catch (error) {\n res.json({ error: error.message, sessions: [] });\n }\n});\n\n// 创建新会话\nrouter.post('/', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body || {};\n // 获取当前最大的 sort_order\n const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM sessions').get();\n const newOrder = (maxOrder?.max || 0) + 1;\n const result = db.prepare('INSERT INTO sessions (name, sort_order) VALUES (?, ?)').run(name || '新对话', newOrder);\n res.json({ id: result.lastInsertRowid, name: name || '新对话', sort_order: newOrder });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 获取会话消息\nrouter.get('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const messages = db.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC').all(req.params.id);\n res.json({ messages });\n } catch (error) {\n res.json({ error: error.message, messages: [] });\n }\n});\n\n// 保存消息\nrouter.post('/:id/messages', (req, res) => {\n try {\n const db = getDb();\n const { role, content, sql, results } = req.body;\n console.log('保存消息:', { sessionId: req.params.id, role, content: content?.substring(0, 30), sql: sql?.substring(0, 30) });\n db.prepare('INSERT INTO messages (session_id, role, content, sql, results) VALUES (?, ?, ?, ?, ?)')\n .run(req.params.id, role, content, sql || '', results || '');\n res.json({ success: true });\n } catch (error) {\n console.error('保存消息失败:', error);\n res.json({ error: error.message });\n }\n});\n\n// 更新会话名称\nrouter.put('/:id', (req, res) => {\n try {\n const db = getDb();\n const { name } = req.body;\n db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\n// 删除会话\nrouter.delete('/:id', (req, res) => {\n try {\n const db = getDb();\n db.prepare('DELETE FROM messages WHERE session_id = ?').run(req.params.id);\n db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);\n res.json({ success: true });\n } catch (error) {\n res.json({ error: error.message });\n }\n});\n\nexport default router;\n\n"}, "9fc3242d9c393f5a1a517483e745ac6220cc6bdf": {"t": 1785758901.9870675, "n": 84, "h": "46590040d5a052c1", "c": "import { Router } from 'express';\nconst router = Router();\n\nexport default router;\n\n"}}}}