refactor: 标题生成重构、UI样式优化、测试增强
- 将标题生成逻辑提取为独立函数,提前到Agent调用前非阻塞执行 - 修复模型/供应商不存在时的HTTP状态码 500→400 - ChatPanel: 分离模型选择useEffect、CSS类替代内联样式、按钮样式统一 - use-conversations: fetchConversations/fetchMessages改用handleResponse去重 - 聊天面板滚动优化(scroll-behavior: smooth, overflow-anchor: auto) - 测试: mock支持onFinish回调,新增首次消息标题生成测试 - 移除未使用的SendMessageRequest接口
This commit is contained in:
@@ -13,19 +13,33 @@ import { createMigratedMemoryTestDatabase } from "../../helpers";
|
||||
const MODE: RuntimeMode = "test";
|
||||
|
||||
void mock.module("ai", () => ({
|
||||
createAgentUIStreamResponse: () =>
|
||||
Promise.resolve(
|
||||
createAgentUIStreamResponse: (opts: {
|
||||
agent: unknown;
|
||||
messages: unknown[];
|
||||
onFinish:
|
||||
| ((event: { finishReason?: string; responseMessage: { parts?: Array<{ text: string; type: string }> } }) => void)
|
||||
| undefined;
|
||||
}) => {
|
||||
if (opts.onFinish) {
|
||||
opts.onFinish({
|
||||
responseMessage: {
|
||||
parts: [{ text: "test reply from AI", type: "text" }],
|
||||
},
|
||||
});
|
||||
}
|
||||
return Promise.resolve(
|
||||
new Response(
|
||||
'data: {"type":"start-step"}\n\ndata: {"type":"text-start","id":"txt-1"}\n\ndata: {"type":"text-delta","id":"txt-1","delta":"test reply from AI"}\n\ndata: {"type":"text-end","id":"txt-1"}\n\ndata: {"type":"finish-step"}\n\ndata: {"type":"finish"}\n\n',
|
||||
{
|
||||
headers: { "Content-Type": "text/event-stream" },
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
createProviderRegistry: () => ({
|
||||
languageModel: () => ({}),
|
||||
}),
|
||||
generateText: () => Promise.resolve({ text: "", usage: {} }),
|
||||
generateText: () => Promise.resolve({ text: "AI总结标题", usage: {} }),
|
||||
stepCountIs: () => () => true,
|
||||
tool: () => ({ execute: async () => await Promise.resolve({}) }),
|
||||
ToolLoopAgent: function M() {
|
||||
@@ -593,8 +607,56 @@ describe("聊天 API 路由", () => {
|
||||
db,
|
||||
);
|
||||
const msgBody = (await msgRes.json()) as { items: Message[] };
|
||||
expect(msgBody.items.length).toBeGreaterThanOrEqual(1);
|
||||
expect(msgBody.items.length).toBeGreaterThanOrEqual(2);
|
||||
expect(msgBody.items.some((m) => m.role === "user")).toBe(true);
|
||||
expect(msgBody.items.some((m) => m.role === "assistant")).toBe(true);
|
||||
handle.close();
|
||||
} finally {
|
||||
handle.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("首次发送消息时触发标题生成", async () => {
|
||||
const handle = createMigratedMemoryTestDatabase("chat-send-title");
|
||||
try {
|
||||
const db = handle.db;
|
||||
const projectId = seedProject(db);
|
||||
const providerId = seedProvider(db);
|
||||
const modelId = seedModel(db, providerId);
|
||||
|
||||
const createRes = await createConversationViaHandler(
|
||||
new Request(`http://localhost/api/projects/${projectId}/conversations`, {
|
||||
body: JSON.stringify({}),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "POST",
|
||||
}),
|
||||
db,
|
||||
);
|
||||
const created = ((await createRes.json()) as { conversation: Conversation }).conversation;
|
||||
expect(created.title).toBe("新会话");
|
||||
|
||||
await sendChatViaHandler(
|
||||
new Request(`http://localhost/api/projects/${projectId}/chat`, {
|
||||
body: JSON.stringify({
|
||||
conversationId: created.id,
|
||||
messages: [{ parts: [{ text: "请帮我分析一下这个项目的性能瓶颈", type: "text" }], role: "user" }],
|
||||
modelDbId: modelId,
|
||||
}),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "POST",
|
||||
}),
|
||||
db,
|
||||
);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
|
||||
const getRes = await getConversationViaHandler(
|
||||
new Request(`http://localhost/api/projects/${projectId}/conversations/${created.id}`),
|
||||
db,
|
||||
);
|
||||
const body = (await getRes.json()) as { conversation: Conversation };
|
||||
expect(body.conversation.title).not.toBe("新会话");
|
||||
expect(body.conversation.title).toBe("AI总结标题");
|
||||
handle.close();
|
||||
} finally {
|
||||
handle.cleanup();
|
||||
|
||||
Reference in New Issue
Block a user