diff --git a/src/chatbox.tsx b/src/chatbox.tsx index d57279c..6893630 100644 --- a/src/chatbox.tsx +++ b/src/chatbox.tsx @@ -15,6 +15,7 @@ import ChatGPT, { FetchResponse, Message as MessageType, MessageDetail, + ToolCall, } from "./chatgpt"; import Message from "./message"; import models from "./models"; @@ -71,12 +72,48 @@ export default function ChatBOX(props: { let responseTokenCount = 0; chatStore.streamMode = true; const allChunkMessage: string[] = []; + const allChunkTool: ToolCall[] = []; setShowGenerating(true); for await (const i of client.processStreamResponse(response)) { chatStore.responseModelName = i.model; responseTokenCount += 1; allChunkMessage.push(i.choices[0].delta.content ?? ""); - setGeneratingMessage(allChunkMessage.join("")); + const tool_calls = i.choices[0].delta.tool_calls; + if (tool_calls) { + for (const tool_call of tool_calls) { + // init + if (tool_call.id) { + allChunkTool.push({ + id: tool_call.id, + type: tool_call.type, + index: tool_call.index, + function: { + name: tool_call.function.name, + arguments: "", + }, + }); + continue; + } + + // update tool call arguments + const tool = allChunkTool.find( + (tool) => tool.index === tool_call.index + ); + + if (!tool) { + console.log("tool (by index) not found", tool_call.index); + continue; + } + + tool.function.arguments += tool_call.function.arguments; + } + } + setGeneratingMessage( + allChunkMessage.join("") + + allChunkTool.map((tool) => { + return `Tool Call ID: ${tool.id}\nType: ${tool.type}\nFunction: ${tool.function.name}\nArguments: ${tool.function.arguments}`; + }) + ); } setShowGenerating(false); const content = allChunkMessage.join(""); @@ -103,6 +140,7 @@ export default function ChatBOX(props: { chatStore.history.push({ role: "assistant", content, + tool_calls: allChunkTool, hide: false, token: responseTokenCount, example: false, diff --git a/src/chatgpt.ts b/src/chatgpt.ts index 9affb8d..6704c46 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -8,17 +8,43 @@ export interface MessageDetail { text?: string; image_url?: ImageURL; } +export interface ToolCall { + index: number; + id?: string; + type: string; + function: { + name: string; + arguments: string; + }; +} export interface Message { role: "system" | "user" | "assistant" | "tool"; content: string | MessageDetail[]; name?: "example_user" | "example_assistant"; - tool_calls?: { - id: string; - type: string; - function: any; - }[]; + tool_calls?: ToolCall[]; tool_call_id?: string; } + +interface Delta { + role?: string; + content?: string; + tool_calls?: ToolCall[]; +} + +interface Choices { + index: number; + delta: Delta; + finish_reason: string | null; +} + +export interface StreamingResponseChunk { + id: string; + object: string; + created: number; + model: string; + system_fingerprint: string; + choices: Choices[]; +} export const getMessageText = (message: Message): string => { if (typeof message.content === "string") { return message.content; @@ -252,7 +278,7 @@ class Chat { console.log("line", line); try { const jsonStr = line.slice("data:".length).trim(); - const json = JSON.parse(jsonStr); + const json = JSON.parse(jsonStr) as StreamingResponseChunk; yield json; } catch (e) { console.log(`Chunk parse error at: ${line}`); diff --git a/src/message.tsx b/src/message.tsx index b692b03..b7f8f1e 100644 --- a/src/message.tsx +++ b/src/message.tsx @@ -299,7 +299,8 @@ export default function Message(props: Props) {
Tool Call ID: {tool_call?.id}

Type: {tool_call?.type}

-

Function: {JSON.stringify(tool_call?.function)}

+

Function: {tool_call.function.name}

+

Arguments: {tool_call.function.arguments}

))}