102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import type {
|
|
ChatEvent,
|
|
InterruptEvent,
|
|
MessageChunkEvent,
|
|
ToolCallChunksEvent,
|
|
ToolCallResultEvent,
|
|
ToolCallsEvent,
|
|
} from "../api";
|
|
import { deepClone } from "../utils/deep-clone";
|
|
|
|
import type { Message } from "./types";
|
|
|
|
export function mergeMessage(message: Message, event: ChatEvent) {
|
|
if (event.type === "message_chunk") {
|
|
mergeTextMessage(message, event);
|
|
} else if (event.type === "tool_calls" || event.type === "tool_call_chunks") {
|
|
mergeToolCallMessage(message, event);
|
|
} else if (event.type === "tool_call_result") {
|
|
mergeToolCallResultMessage(message, event);
|
|
} else if (event.type === "interrupt") {
|
|
mergeInterruptMessage(message, event);
|
|
}
|
|
if (event.data.finish_reason) {
|
|
message.finishReason = event.data.finish_reason;
|
|
message.isStreaming = false;
|
|
if (message.toolCalls) {
|
|
message.toolCalls.forEach((toolCall) => {
|
|
if (toolCall.argsChunks?.length) {
|
|
toolCall.args = JSON.parse(toolCall.argsChunks.join(""));
|
|
delete toolCall.argsChunks;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return deepClone(message);
|
|
}
|
|
|
|
function mergeTextMessage(message: Message, event: MessageChunkEvent) {
|
|
if (event.data.content) {
|
|
message.content += event.data.content;
|
|
message.contentChunks.push(event.data.content);
|
|
}
|
|
if (event.data.reasoning_content) {
|
|
message.reasoningContent = (message.reasoningContent ?? "") + event.data.reasoning_content;
|
|
message.reasoningContentChunks = message.reasoningContentChunks ?? [];
|
|
message.reasoningContentChunks.push(event.data.reasoning_content);
|
|
}
|
|
}
|
|
|
|
function mergeToolCallMessage(
|
|
message: Message,
|
|
event: ToolCallsEvent | ToolCallChunksEvent,
|
|
) {
|
|
if (event.type === "tool_calls" && event.data.tool_calls[0]?.name) {
|
|
message.toolCalls = event.data.tool_calls.map((raw) => ({
|
|
id: raw.id,
|
|
name: raw.name,
|
|
args: raw.args,
|
|
result: undefined,
|
|
}));
|
|
}
|
|
|
|
message.toolCalls ??= [];
|
|
for (const chunk of event.data.tool_call_chunks) {
|
|
if (chunk.id) {
|
|
const toolCall = message.toolCalls.find(
|
|
(toolCall) => toolCall.id === chunk.id,
|
|
);
|
|
if (toolCall) {
|
|
toolCall.argsChunks = [chunk.args];
|
|
}
|
|
} else {
|
|
const streamingToolCall = message.toolCalls.find(
|
|
(toolCall) => toolCall.argsChunks?.length,
|
|
);
|
|
if (streamingToolCall) {
|
|
streamingToolCall.argsChunks!.push(chunk.args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function mergeToolCallResultMessage(
|
|
message: Message,
|
|
event: ToolCallResultEvent,
|
|
) {
|
|
const toolCall = message.toolCalls?.find(
|
|
(toolCall) => toolCall.id === event.data.tool_call_id,
|
|
);
|
|
if (toolCall) {
|
|
toolCall.result = event.data.content;
|
|
}
|
|
}
|
|
|
|
function mergeInterruptMessage(message: Message, event: InterruptEvent) {
|
|
message.isStreaming = false;
|
|
message.options = event.data.options;
|
|
}
|