1 line
87 KiB
JSON
1 line
87 KiB
JSON
{"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"}}}} |