mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-23 23:20:55 +08:00
feat: debug_context 记录完整 LLM 上下文
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import { getDefaultAssistantConfig, buildPiModel } from '../llm'
|
||||
import { getAllTools, createActivateSkillTool } from '../tools'
|
||||
import type { ToolContext } from '../tools/types'
|
||||
import { getHistoryForAgent } from '../conversations'
|
||||
import { getHistoryForAgent, setPendingDebugContext } from '../conversations'
|
||||
import { aiLogger, isDebugMode } from '../logger'
|
||||
import { t as i18nT } from '../../i18n'
|
||||
import { Agent as PiAgentCore } from '@mariozechner/pi-agent-core'
|
||||
@@ -23,7 +23,7 @@ import { buildSystemPrompt } from './prompt-builder'
|
||||
import { extractThinkingContent, stripToolCallTags } from './content-parser'
|
||||
import { AgentEventHandler } from './event-handler'
|
||||
|
||||
type SimpleHistoryMessage = { role: 'user' | 'assistant' | 'system'; content: string }
|
||||
type SimpleHistoryMessage = { role: 'user' | 'assistant' | 'summary'; content: string }
|
||||
|
||||
// Re-export types for external consumers
|
||||
export type { AgentConfig, AgentStreamChunk, AgentResult, TokenUsage, AgentRuntimeStatus, SkillContext } from './types'
|
||||
@@ -194,7 +194,22 @@ export class Agent {
|
||||
|
||||
try {
|
||||
if (isDebugMode()) {
|
||||
aiLogger.debug('Agent', `[DEBUG] System prompt`, systemPrompt)
|
||||
// 捕获发给 LLM 的完整上下文(system prompt + history + user message),写入 DB
|
||||
if (this.context.conversationId) {
|
||||
try {
|
||||
const debugMessages = [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
...historyMessages.map((m) => ({
|
||||
role: m.role === 'summary' ? 'assistant' : m.role,
|
||||
content: m.content,
|
||||
})),
|
||||
{ role: 'user', content: userMessage },
|
||||
]
|
||||
setPendingDebugContext(this.context.conversationId, JSON.stringify(debugMessages, null, 2))
|
||||
} catch {
|
||||
// 静默失败,不影响主流程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await coreAgent.prompt(userMessage)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 1. 计算当前上下文总 token → 未超阈值则跳过
|
||||
* 2. 确定缓冲区:最近 bufferSizePercent% context window 的消息原文
|
||||
* 3. 缓冲区之前的消息(含旧 system 摘要)→ LLM 压缩为新摘要
|
||||
* 4. 写入 ai_message(role='system'),替换旧摘要
|
||||
* 4. 写入 ai_message(role='summary'),替换旧摘要
|
||||
* 5. Thrashing 检查
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getAllUserAssistantMessages,
|
||||
addSummaryMessage,
|
||||
getMessageCountAfterSummary,
|
||||
setDebugContext,
|
||||
type ContentBlock,
|
||||
type AIMessageRole,
|
||||
} from '../conversations'
|
||||
@@ -179,30 +180,6 @@ export async function checkAndCompress(
|
||||
inputTokensEstimate: countTokens(compressInput),
|
||||
})
|
||||
|
||||
// DEBUG 模式下输出原始消息列表和完整压缩输入
|
||||
if (isDebugMode()) {
|
||||
aiLogger.debug('Compression', 'Messages to compress (raw)', {
|
||||
messages: messagesToCompress.map((m, i) => ({
|
||||
index: i,
|
||||
role: m.role,
|
||||
contentLength: m.content.length,
|
||||
contentPreview: m.content.slice(0, 200),
|
||||
})),
|
||||
})
|
||||
aiLogger.debug('Compression', 'Buffer messages (kept as-is)', {
|
||||
messages: bufferMessages.map((m, i) => ({
|
||||
index: i,
|
||||
role: m.role,
|
||||
contentLength: m.content.length,
|
||||
contentPreview: m.content.slice(0, 200),
|
||||
})),
|
||||
})
|
||||
aiLogger.debug('Compression', 'Full compression input sent to LLM', compressInput)
|
||||
if (summary) {
|
||||
aiLogger.debug('Compression', 'Previous summary content', summary.content)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用默认助手模型压缩,失败则强制截断
|
||||
let summaryText: string | null = null
|
||||
|
||||
@@ -219,9 +196,6 @@ export async function checkAndCompress(
|
||||
outputLength: summaryText.length,
|
||||
outputTokensEstimate: countTokens(summaryText),
|
||||
})
|
||||
if (isDebugMode()) {
|
||||
aiLogger.debug('Compression', 'Generated summary content', summaryText)
|
||||
}
|
||||
}
|
||||
|
||||
// 写入 summary:时间戳 = NOW(UI 中显示在触发压缩的位置)
|
||||
@@ -242,7 +216,19 @@ export async function checkAndCompress(
|
||||
bufferCount: bufferMessages.length,
|
||||
})
|
||||
|
||||
addSummaryMessage(conversationId, summaryText, summaryMeta)
|
||||
const summaryMsg = addSummaryMessage(conversationId, summaryText, summaryMeta)
|
||||
|
||||
// DEBUG 模式:记录发给压缩 LLM 的完整上下文(与 assistant 的 debug_context 格式一致)
|
||||
if (isDebugMode()) {
|
||||
try {
|
||||
const template = isProgressive ? PROGRESSIVE_COMPRESSION_PROMPT : INITIAL_COMPRESSION_PROMPT
|
||||
const fullPrompt = template.replace('{maxTokens}', String(targetTokens)).replace('{messages}', compressInput)
|
||||
const debugMessages = [{ role: 'user', content: fullPrompt }]
|
||||
setDebugContext(summaryMsg.id, JSON.stringify(debugMessages, null, 2))
|
||||
} catch {
|
||||
// 静默失败,不影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
// Thrashing 检查:压缩后重新计算 token(summary + buffer 消息)
|
||||
const afterTokenCount: Array<{ role: string; content: string }> = [
|
||||
|
||||
@@ -13,6 +13,9 @@ const DEFAULT_GENERAL_ID = 'general_cn'
|
||||
// AI 数据库实例
|
||||
let AI_DB: Database.Database | null = null
|
||||
|
||||
// DEBUG 模式:暂存待写入的 debug context(key = conversationId)
|
||||
const pendingDebugContextMap = new Map<string, string>()
|
||||
|
||||
/**
|
||||
* 获取 AI 数据库目录
|
||||
*/
|
||||
@@ -94,6 +97,12 @@ function migrateAiDatabase(db: Database.Database): void {
|
||||
console.log('[AI DB Migration] Adding token_usage column to ai_message')
|
||||
}
|
||||
|
||||
// 检查并添加 debug_context 列(仅 DEBUG 模式下写入,存储发给 LLM 的完整上下文)
|
||||
if (!messageColumns.includes('debug_context')) {
|
||||
db.exec('ALTER TABLE ai_message ADD COLUMN debug_context TEXT')
|
||||
console.log('[AI DB Migration] Adding debug_context column to ai_message')
|
||||
}
|
||||
|
||||
// 获取 ai_conversation 表的列信息
|
||||
const convTableInfo = db.pragma('table_info(ai_conversation)') as Array<{ name: string }>
|
||||
const convColumns = convTableInfo.map((col) => col.name)
|
||||
@@ -230,7 +239,7 @@ export type ContentBlock =
|
||||
/**
|
||||
* AI 消息类型
|
||||
*/
|
||||
export type AIMessageRole = 'user' | 'assistant' | 'system'
|
||||
export type AIMessageRole = 'user' | 'assistant' | 'summary'
|
||||
|
||||
export interface TokenUsageData {
|
||||
promptTokens: number
|
||||
@@ -389,10 +398,16 @@ export function addMessage(
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const id = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
|
||||
// 检查是否有暂存的 debug context 需要写入(仅 assistant 消息)
|
||||
const pendingDebug = role === 'assistant' ? pendingDebugContextMap.get(conversationId) : undefined
|
||||
if (pendingDebug) {
|
||||
pendingDebugContextMap.delete(conversationId)
|
||||
}
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO ai_message (id, conversation_id, role, content, timestamp, data_keywords, data_message_count, content_blocks, token_usage)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO ai_message (id, conversation_id, role, content, timestamp, data_keywords, data_message_count, content_blocks, token_usage, debug_context)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
).run(
|
||||
id,
|
||||
@@ -403,7 +418,8 @@ export function addMessage(
|
||||
dataKeywords ? JSON.stringify(dataKeywords) : null,
|
||||
dataMessageCount ?? null,
|
||||
contentBlocks ? JSON.stringify(contentBlocks) : null,
|
||||
tokenUsage ? JSON.stringify(tokenUsage) : null
|
||||
tokenUsage ? JSON.stringify(tokenUsage) : null,
|
||||
pendingDebug ?? null
|
||||
)
|
||||
|
||||
// 更新对话的 updated_at
|
||||
@@ -480,6 +496,30 @@ export function deleteMessage(messageId: string): boolean {
|
||||
return result.changes > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂存 debug context,等待下一次 addMessage(assistant) 时自动写入
|
||||
*/
|
||||
export function setPendingDebugContext(conversationId: string, debugContext: string): void {
|
||||
pendingDebugContextMap.set(conversationId, debugContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息的 debug_context 字段(仅 DEBUG 模式下调用)
|
||||
*/
|
||||
export function setDebugContext(messageId: string, debugContext: string): void {
|
||||
const db = getAiDb()
|
||||
db.prepare('UPDATE ai_message SET debug_context = ? WHERE id = ?').run(debugContext, messageId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键清除所有消息的 debug_context 数据
|
||||
*/
|
||||
export function clearAllDebugContext(): number {
|
||||
const db = getAiDb()
|
||||
const result = db.prepare('UPDATE ai_message SET debug_context = NULL WHERE debug_context IS NOT NULL').run()
|
||||
return result.changes
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话的累计 token 使用量(聚合所有 assistant 消息的 token_usage)
|
||||
*/
|
||||
@@ -520,44 +560,44 @@ export function getConversationTokenUsage(conversationId: string): TokenUsageDat
|
||||
export function getHistoryForAgent(
|
||||
conversationId: string,
|
||||
maxMessages?: number
|
||||
): Array<{ role: 'user' | 'assistant' | 'system'; content: string }> {
|
||||
): Array<{ role: 'user' | 'assistant' | 'summary'; content: string }> {
|
||||
const messages = getMessages(conversationId)
|
||||
const validMessages = messages.filter(
|
||||
(m) => (m.role === 'user' || m.role === 'assistant' || m.role === 'system') && m.content?.trim()
|
||||
(m) => (m.role === 'user' || m.role === 'assistant' || m.role === 'summary') && m.content?.trim()
|
||||
)
|
||||
|
||||
// 查找最新的 system (summary) 消息
|
||||
let systemMsg: AIMessage | undefined
|
||||
// 查找最新的 summary 消息
|
||||
let summaryMsg: AIMessage | undefined
|
||||
for (let i = validMessages.length - 1; i >= 0; i--) {
|
||||
if (validMessages[i].role === 'system') {
|
||||
systemMsg = validMessages[i]
|
||||
if (validMessages[i].role === 'summary') {
|
||||
summaryMsg = validMessages[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let result: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>
|
||||
let result: Array<{ role: 'user' | 'assistant' | 'summary'; content: string }>
|
||||
|
||||
if (systemMsg) {
|
||||
if (summaryMsg) {
|
||||
// 从 content_blocks 中解析 summary_meta 获取 buffer 边界
|
||||
const metaBlock = systemMsg.contentBlocks?.find(
|
||||
const metaBlock = summaryMsg.contentBlocks?.find(
|
||||
(b): b is Extract<ContentBlock, { type: 'summary_meta' }> => b.type === 'summary_meta'
|
||||
)
|
||||
const bufferBoundary = metaBlock?.bufferBoundaryTimestamp
|
||||
|
||||
if (!metaBlock) {
|
||||
aiLogger.warn('Conversations', 'system message missing summary_meta; agent context will be summary-only', {
|
||||
aiLogger.warn('Conversations', 'summary message missing summary_meta; agent context will be summary-only', {
|
||||
conversationId,
|
||||
messageId: systemMsg.id,
|
||||
messageId: summaryMsg.id,
|
||||
})
|
||||
}
|
||||
|
||||
// 取 timestamp >= boundary 的 user/assistant 消息(buffer + 新消息)
|
||||
const contextMessages = bufferBoundary
|
||||
? validMessages.filter((m) => m.role !== 'system' && m.timestamp >= bufferBoundary)
|
||||
? validMessages.filter((m) => m.role !== 'summary' && m.timestamp >= bufferBoundary)
|
||||
: []
|
||||
|
||||
result = [
|
||||
{ role: 'system' as const, content: systemMsg.content },
|
||||
{ role: 'summary' as const, content: summaryMsg.content },
|
||||
...contextMessages.map((m) => ({ role: m.role, content: m.content })),
|
||||
]
|
||||
} else {
|
||||
@@ -565,7 +605,7 @@ export function getHistoryForAgent(
|
||||
}
|
||||
|
||||
if (maxMessages && result.length > maxMessages) {
|
||||
if (result.length > 0 && result[0].role === 'system') {
|
||||
if (result.length > 0 && result[0].role === 'summary') {
|
||||
const rest = result.slice(1)
|
||||
const truncated = rest.slice(-(maxMessages - 1))
|
||||
return [result[0], ...truncated]
|
||||
@@ -578,7 +618,7 @@ export function getHistoryForAgent(
|
||||
// ==================== Summary / 压缩专用 ====================
|
||||
|
||||
/**
|
||||
* 添加 system 消息并替换旧的 system(每个对话只保留一条最新压缩摘要)
|
||||
* 添加 summary 消息并替换旧的 summary(每个对话只保留一条最新压缩摘要)
|
||||
*
|
||||
* Summary 时间戳 = NOW(UI 中显示在触发压缩的位置)。
|
||||
* Buffer 边界信息存入 content_blocks 的 summary_meta block 中,供 getHistoryForAgent 使用。
|
||||
@@ -590,7 +630,7 @@ export function addSummaryMessage(
|
||||
): AIMessage {
|
||||
const db = getAiDb()
|
||||
|
||||
db.prepare("DELETE FROM ai_message WHERE conversation_id = ? AND role = 'system'").run(conversationId)
|
||||
db.prepare("DELETE FROM ai_message WHERE conversation_id = ? AND role = 'summary'").run(conversationId)
|
||||
|
||||
const contentBlocks: ContentBlock[] = [
|
||||
{
|
||||
@@ -600,7 +640,7 @@ export function addSummaryMessage(
|
||||
},
|
||||
]
|
||||
|
||||
return addMessage(conversationId, 'system', content, undefined, undefined, contentBlocks)
|
||||
return addMessage(conversationId, 'summary', content, undefined, undefined, contentBlocks)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,7 +654,7 @@ export function getLatestSummary(conversationId: string): AIMessage | null {
|
||||
SELECT id, conversation_id as conversationId, role, content, timestamp,
|
||||
data_keywords as dataKeywords, data_message_count as dataMessageCount, content_blocks as contentBlocks
|
||||
FROM ai_message
|
||||
WHERE conversation_id = ? AND role = 'system'
|
||||
WHERE conversation_id = ? AND role = 'summary'
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
@@ -200,6 +200,17 @@ export function registerAIHandlers({ win }: IpcContext): void {
|
||||
aiLogger.info('Config', `Debug mode ${enabled ? 'enabled' : 'disabled'}`)
|
||||
})
|
||||
|
||||
ipcMain.handle('ai:clearDebugContext', async () => {
|
||||
try {
|
||||
const cleared = aiConversations.clearAllDebugContext()
|
||||
aiLogger.info('Debug', `Cleared debug_context for ${cleared} messages`)
|
||||
return { success: true, cleared }
|
||||
} catch (error) {
|
||||
console.error('Failed to clear debug context:', error)
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== AI 对话管理 ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,7 @@ export type ContentBlock =
|
||||
}
|
||||
| { type: 'skill'; skillId: string; skillName: string }
|
||||
|
||||
export type AIMessageRole = 'user' | 'assistant' | 'system'
|
||||
export type AIMessageRole = 'user' | 'assistant' | 'summary'
|
||||
|
||||
export interface TokenUsageData {
|
||||
promptTokens: number
|
||||
@@ -541,6 +541,13 @@ export const aiApi = {
|
||||
return ipcRenderer.invoke('ai:showLogFile')
|
||||
},
|
||||
|
||||
/**
|
||||
* 一键清除所有消息的 debug_context 数据
|
||||
*/
|
||||
clearDebugContext: (): Promise<{ success: boolean; cleared: number }> => {
|
||||
return ipcRenderer.invoke('ai:clearDebugContext')
|
||||
},
|
||||
|
||||
getDefaultDesensitizeRules: (locale: string): Promise<DesensitizeRule[]> => {
|
||||
return ipcRenderer.invoke('ai:getDefaultDesensitizeRules', locale)
|
||||
},
|
||||
|
||||
Vendored
+2
-1
@@ -310,7 +310,7 @@ type AIContentBlock =
|
||||
compressedMessageCount: number
|
||||
}
|
||||
|
||||
type AIMessageRole = 'user' | 'assistant' | 'system'
|
||||
type AIMessageRole = 'user' | 'assistant' | 'summary'
|
||||
|
||||
interface AITokenUsageData {
|
||||
promptTokens: number
|
||||
@@ -396,6 +396,7 @@ interface AiApi {
|
||||
getConversationTokenUsage: (conversationId: string) => Promise<AITokenUsageData>
|
||||
deleteMessage: (messageId: string) => Promise<boolean>
|
||||
showAiLogFile: () => Promise<{ success: boolean; path?: string; error?: string }>
|
||||
clearDebugContext: () => Promise<{ success: boolean; cleared: number }>
|
||||
getDefaultDesensitizeRules: (locale: string) => Promise<DesensitizeRule[]>
|
||||
mergeDesensitizeRules: (existingRules: DesensitizeRule[], locale: string) => Promise<DesensitizeRule[]>
|
||||
getToolCatalog: () => Promise<ToolCatalogEntry[]>
|
||||
|
||||
@@ -13,7 +13,7 @@ const toast = useToast()
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
role: 'user' | 'assistant' | 'summary'
|
||||
content: string
|
||||
timestamp: number
|
||||
isStreaming?: boolean
|
||||
@@ -30,7 +30,7 @@ const formattedTime = computed(() => {
|
||||
|
||||
// 是否是用户消息
|
||||
const isUser = computed(() => props.role === 'user')
|
||||
const isSystem = computed(() => props.role === 'system')
|
||||
const isSummary = computed(() => props.role === 'summary')
|
||||
|
||||
// 创建 markdown-it 实例
|
||||
const md = new MarkdownIt({
|
||||
@@ -302,11 +302,11 @@ async function handleCopyMarkdown() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-start gap-3" :class="[isUser ? 'flex-row-reverse' : '', isSystem ? 'justify-center' : '']">
|
||||
<div class="flex items-start gap-3" :class="[isUser ? 'flex-row-reverse' : '', isSummary ? 'justify-center' : '']">
|
||||
<!-- 消息内容 -->
|
||||
<div :class="[isSystem ? 'w-full min-w-0' : 'max-w-[85%] min-w-0']">
|
||||
<div :class="[isSummary ? 'w-full min-w-0' : 'max-w-[85%] min-w-0']">
|
||||
<!-- System 消息:可折叠的上下文总结 -->
|
||||
<template v-if="isSystem">
|
||||
<template v-if="isSummary">
|
||||
<details
|
||||
class="w-full rounded-lg border border-gray-200 bg-gray-50/80 dark:border-gray-700/50 dark:bg-gray-800/40"
|
||||
>
|
||||
@@ -465,7 +465,7 @@ async function handleCopyMarkdown() {
|
||||
|
||||
<!-- 时间戳 + 操作按钮(summary 消息和流式输出中不显示) -->
|
||||
<div
|
||||
v-if="!isSystem && !isStreaming"
|
||||
v-if="!isSummary && !isStreaming"
|
||||
class="mt-1 flex items-center gap-2 px-1"
|
||||
:class="[isUser ? 'flex-row-reverse' : '']"
|
||||
>
|
||||
|
||||
@@ -31,6 +31,7 @@ const totalPages = computed(() => Math.max(1, Math.ceil(totalRows.value / pageSi
|
||||
|
||||
const editingRowIndex = ref<number | null>(null)
|
||||
const editingValues = ref<Record<string, string>>({})
|
||||
const isClearingDebug = ref(false)
|
||||
|
||||
const isAiDb = computed(() => dbSource.value === 'ai')
|
||||
|
||||
@@ -240,6 +241,21 @@ function confirmDelete(rowIndex: number) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleClearDebugContext() {
|
||||
if (!confirm(t('analysis.debug.tableBrowser.confirmClearDebug'))) return
|
||||
isClearingDebug.value = true
|
||||
try {
|
||||
const res = await window.aiApi.clearDebugContext()
|
||||
if (res.success) {
|
||||
await loadTableData()
|
||||
}
|
||||
} catch (e: any) {
|
||||
error.value = e.message || String(e)
|
||||
} finally {
|
||||
isClearingDebug.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text: string) {
|
||||
navigator.clipboard.writeText(text)
|
||||
}
|
||||
@@ -342,6 +358,18 @@ onMounted(async () => {
|
||||
|
||||
<div class="flex-1" />
|
||||
|
||||
<UButton
|
||||
v-if="isAiDb && selectedTable === 'ai_message'"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
color="error"
|
||||
icon="i-heroicons-trash"
|
||||
:loading="isClearingDebug"
|
||||
@click="handleClearDebugContext"
|
||||
>
|
||||
{{ t('analysis.debug.tableBrowser.clearDebugContext') }}
|
||||
</UButton>
|
||||
|
||||
<UButton variant="ghost" size="xs" icon="i-heroicons-arrow-path" :loading="isLoading" @click="loadTableData">
|
||||
{{ t('analysis.debug.tableBrowser.refresh') }}
|
||||
</UButton>
|
||||
|
||||
@@ -50,7 +50,9 @@
|
||||
"allConversations": "All Conversations",
|
||||
"copyCell": "Copy Selected",
|
||||
"editRow": "Edit",
|
||||
"copyRow": "Copy Row (JSON)"
|
||||
"copyRow": "Copy Row (JSON)",
|
||||
"clearDebugContext": "Clear Debug Context",
|
||||
"confirmClearDebug": "Clear debug_context for all messages? This cannot be undone."
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
|
||||
@@ -50,7 +50,9 @@
|
||||
"allConversations": "すべての会話",
|
||||
"copyCell": "選択項目をコピー",
|
||||
"editRow": "編集",
|
||||
"copyRow": "行をコピー (JSON)"
|
||||
"copyRow": "行をコピー (JSON)",
|
||||
"clearDebugContext": "デバッグコンテキストをクリア",
|
||||
"confirmClearDebug": "すべてのメッセージの debug_context を削除しますか?この操作は取り消せません。"
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
|
||||
@@ -50,7 +50,9 @@
|
||||
"allConversations": "全部对话",
|
||||
"copyCell": "复制选中项",
|
||||
"editRow": "编辑",
|
||||
"copyRow": "复制整行 (JSON)"
|
||||
"copyRow": "复制整行 (JSON)",
|
||||
"clearDebugContext": "清除 Debug 上下文",
|
||||
"confirmClearDebug": "确认清除所有消息的 debug_context 数据?此操作不可撤销。"
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
|
||||
@@ -50,7 +50,9 @@
|
||||
"allConversations": "全部對話",
|
||||
"copyCell": "複製選取項",
|
||||
"editRow": "編輯",
|
||||
"copyRow": "複製整行 (JSON)"
|
||||
"copyRow": "複製整行 (JSON)",
|
||||
"clearDebugContext": "清除 Debug 上下文",
|
||||
"confirmClearDebug": "確認清除所有訊息的 debug_context 資料?此操作不可撤銷。"
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
|
||||
@@ -59,7 +59,7 @@ export type ContentBlock =
|
||||
// 消息类型
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
role: 'user' | 'assistant' | 'summary'
|
||||
content: string
|
||||
timestamp: number
|
||||
dataSource?: {
|
||||
@@ -870,14 +870,14 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
|
||||
|
||||
case 'compression_done':
|
||||
if (chunk.compressionResult) {
|
||||
const systemMsg: ChatMessage = {
|
||||
id: `system-${Date.now()}`,
|
||||
role: 'system',
|
||||
const summaryMsg: ChatMessage = {
|
||||
id: `summary-${Date.now()}`,
|
||||
role: 'summary',
|
||||
content: chunk.compressionResult.summaryContent,
|
||||
timestamp: chunk.compressionResult.timestamp,
|
||||
}
|
||||
const insertIdx = Math.max(0, targetBuffer.messages.length - 1)
|
||||
targetBuffer.messages.splice(insertIdx, 0, systemMsg)
|
||||
targetBuffer.messages.splice(insertIdx, 0, summaryMsg)
|
||||
aiMessageIndex++
|
||||
}
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user