refactor(storage): 统一消息存储到 Core 层

问题:Server 端只存储最终文本响应,工具调用的中间消息丢失。

解决方案:
- Agent.chat() 返回 ChatResult,包含完整消息链
- Server SessionManager 简化为只管理会话元数据
- 消息 API 改为从 Core Storage 读取
- 移除 Server 端的消息存储和 addMessage 方法

影响范围:
- core: Agent.chat() 返回类型变更
- server: SessionManager 接口变更,移除消息存储
- server: GET /sessions/:id/messages 从 Core 读取
- server: 移除 POST /sessions/:id/messages 端点
This commit is contained in:
2025-12-15 10:04:22 +08:00
parent a657af9bb7
commit 6342a46e59
14 changed files with 273 additions and 503 deletions
+40 -27
View File
@@ -39,12 +39,20 @@ export interface CompressionResult {
summaryTokens?: number;
}
/**
* Chat 返回结果
*/
interface ChatResult {
text: string;
messages: unknown[];
}
/**
* Agent 实例接口
*/
interface AgentInstance {
setRegistry(registry: unknown): void;
chat(message: string, onStream?: (chunk: string) => void): Promise<string>;
chat(message: string, onStream?: (chunk: string) => void): Promise<ChatResult>;
getToolCount(): { core: number; discovered: number; total: number };
getContextUsageFormatted(): string;
getContextUsage(): TokenUsage;
@@ -52,6 +60,7 @@ interface AgentInstance {
getCompressionManager(): {
shouldCompress(messages: unknown[]): boolean;
};
getHistory(): unknown[];
}
/**
@@ -256,23 +265,17 @@ export async function processMessage(sessionId: string, content: string): Promis
}
// Core 模块不可用,返回占位响应
const errorContent = 'Agent core module not available. Please build @ai-assistant/core first.';
broadcastToSession(sessionId, {
type: 'chunk',
sessionId,
payload: {
content: 'Agent core module not available. Please build @ai-assistant/core first.',
},
});
const assistantMessage = await sessionManager.addMessage(sessionId, {
role: 'assistant',
content: 'Agent core module not available. Please build @ai-assistant/core first.',
payload: { content: errorContent },
});
broadcastToSession(sessionId, {
type: 'done',
sessionId,
payload: assistantMessage,
payload: { text: errorContent, hasToolCalls: false, messageCount: 0 },
});
sessionManager.updateStatus(sessionId, 'idle' as SessionStatus);
@@ -282,7 +285,7 @@ export async function processMessage(sessionId: string, content: string): Promis
try {
// 调用 Agent 的 chat 方法,使用流式回调
const response = await agent.chat(content, (chunk: string) => {
const result = await agent.chat(content, (chunk: string) => {
// 推送流式内容
broadcastToSession(sessionId, {
type: 'chunk',
@@ -299,31 +302,41 @@ export async function processMessage(sessionId: string, content: string): Promis
}
});
// 保存助手消息
const assistantMessage = await sessionManager.addMessage(sessionId, {
role: 'assistant',
content: response,
// 消息已由 Core Agent 自动持久化,这里只更新 Server 端的会话计数
const session = sessionManager.get(sessionId);
if (session) {
// 从 Agent 获取实际消息数
const history = agent.getHistory();
session.messageCount = history.length;
session.updatedAt = new Date().toISOString();
}
// 检查是否有工具调用
const hasToolCalls = result.messages.some((m: unknown) => {
const msg = m as { content?: unknown };
return Array.isArray(msg.content) && msg.content.some((c: unknown) => {
const block = c as { type?: string };
return block.type === 'tool-call';
});
});
// 发送完成消息
broadcastToSession(sessionId, {
type: 'done',
sessionId,
payload: assistantMessage,
payload: {
text: result.text,
hasToolCalls,
messageCount: result.messages.length,
},
});
// 检查是否需要生成会话标题(首次对话完成后)
const session = sessionManager.get(sessionId);
const messages = sessionManager.getMessages(sessionId);
if (session && !session.name && messages.length === 2) {
// 首条用户消息 + 首条 AI 回复 = 2 条消息
const userMessage = messages.find(m => m.role === 'user');
if (userMessage) {
// 异步生成标题,不阻塞响应
generateSessionTitle(sessionId, userMessage.content, response).catch(err => {
console.error('[Agent] Failed to generate session title:', err);
});
}
if (session && !session.name) {
// 异步生成标题,不阻塞响应
generateSessionTitle(sessionId, content, result.text).catch(err => {
console.error('[Agent] Failed to generate session title:', err);
});
}
emitStatusEvent(sessionId, 'idle');