添加工具调用解析和聊天工具操作处理

This commit is contained in:
2026-04-03 11:49:05 +08:00
parent a1c8041b11
commit d610a09c14
11 changed files with 1269 additions and 18 deletions
@@ -1,4 +1,7 @@
import { parseAssistantMessageSections } from "./chatMessageSections";
import {
parseAssistantMessageSections,
parseContentWithToolCalls,
} from "./chatMessageSections";
describe("parseAssistantMessageSections", () => {
it("returns plain assistant content when there is no thought block", () => {
@@ -41,3 +44,88 @@ describe("parseAssistantMessageSections", () => {
});
});
});
describe("parseContentWithToolCalls", () => {
it("returns a single text segment when there are no tool calls", () => {
const result = parseContentWithToolCalls("普通文本回答");
expect(result.segments).toEqual([
{ type: "text", content: "普通文本回答" },
]);
expect(result.toolCalls).toHaveLength(0);
});
it("parses a complete tool_call block", () => {
const content =
'分析完成。\n<tool_call>{"tool":"locate_nodes","params":{"ids":["J1","J2"]}}</tool_call>\n以上是结果。';
const result = parseContentWithToolCalls(content);
expect(result.toolCalls).toHaveLength(1);
expect(result.toolCalls[0].tool).toBe("locate_nodes");
expect(result.toolCalls[0].params).toEqual({ ids: ["J1", "J2"] });
expect(result.segments).toHaveLength(3);
expect(result.segments[0]).toEqual({
type: "text",
content: "分析完成。",
});
expect(result.segments[1]).toMatchObject({
type: "tool_call",
toolCall: { tool: "locate_nodes" },
});
expect(result.segments[2]).toEqual({
type: "text",
content: "以上是结果。",
});
});
it("parses multiple tool_call blocks", () => {
const content =
'文本1\n<tool_call>{"tool":"locate_pipes","params":{"ids":["P1"]}}</tool_call>\n文本2\n<tool_call>{"tool":"chart","params":{"title":"图"}}</tool_call>';
const result = parseContentWithToolCalls(content);
expect(result.toolCalls).toHaveLength(2);
expect(result.toolCalls[0].tool).toBe("locate_pipes");
expect(result.toolCalls[1].tool).toBe("chart");
expect(result.segments).toHaveLength(4);
});
it("detects an unclosed tool_call tag as pending (streaming)", () => {
const content = '正在分析...\n<tool_call>{"tool":"locate_no';
const result = parseContentWithToolCalls(content);
expect(result.segments).toHaveLength(2);
expect(result.segments[0]).toEqual({
type: "text",
content: "正在分析...",
});
expect(result.segments[1]).toEqual({ type: "tool_call_pending" });
expect(result.toolCalls).toHaveLength(0);
});
it("strips partial opening tags during streaming", () => {
const content = "正在分析...\n<tool_c";
const result = parseContentWithToolCalls(content);
expect(result.segments).toHaveLength(1);
expect(result.segments[0]).toEqual({
type: "text",
content: "正在分析...",
});
});
it("handles malformed JSON gracefully", () => {
const content =
'前文\n<tool_call>{invalid json}</tool_call>\n后文';
const result = parseContentWithToolCalls(content);
// Malformed tool call is treated as text
expect(result.toolCalls).toHaveLength(0);
expect(result.segments.length).toBeGreaterThanOrEqual(2);
});
it("returns empty segments for empty content", () => {
const result = parseContentWithToolCalls("");
expect(result.segments).toHaveLength(0);
expect(result.toolCalls).toHaveLength(0);
});
});