import { CHAT_MODELS, post, rawSSE, save, short } from "./lib.ts"; const tools = [ { type: "function", function: { name: "get_weather", description: "Get the current weather for a city", parameters: { type: "object", properties: { city: { type: "string", description: "City name" } }, required: ["city"] }, }, }, ]; const results: Record = {}; for (const model of CHAT_MODELS) { console.log(`\n=== ${model}`); const messages: any[] = [{ role: "user", content: "What's the weather in Montreal right now? Use the tool." }]; const s = await rawSSE("/chat/completions", { model, messages, tools, tool_choice: "auto", max_tokens: 200, stream: true }); const toolDeltas: any[] = []; let finish: any; let usage: any; for (const e of s.events) { if (e.data === "[DONE]") continue; const ch = e.data?.choices?.[0]; if (ch?.delta?.tool_calls) toolDeltas.push(ch.delta); if (ch?.finish_reason) finish = ch.finish_reason; if (e.data?.usage) usage = e.data.usage; } console.log(`stream ${s.status} events=${s.events.length} finish=${finish} toolDeltaChunks=${toolDeltas.length}`); console.log("tool deltas", short(toolDeltas, 900)); console.log("usage", JSON.stringify(usage)); // assemble const calls: Record = {}; for (const d of toolDeltas) for (const tc of d.tool_calls) { const idx = tc.index ?? 0; calls[idx] ??= { id: tc.id, type: tc.type, function: { name: "", arguments: "" } }; if (tc.id) calls[idx].id = tc.id; if (tc.function?.name) calls[idx].function.name += tc.function.name; if (tc.function?.arguments) calls[idx].function.arguments += tc.function.arguments; } const toolCalls = Object.values(calls); console.log("assembled", JSON.stringify(toolCalls)); let round2: any = null; if (toolCalls.length) { messages.push({ role: "assistant", content: "", tool_calls: toolCalls }); for (const tc of toolCalls) messages.push({ role: "tool", tool_call_id: tc.id, name: tc.function.name, content: JSON.stringify({ temp_c: -3, sky: "snow" }) }); round2 = await post("/chat/completions", { model, messages, tools, max_tokens: 100 }); console.log(`round2 ${round2.status} ${short(round2.body?.choices?.[0]?.message?.content ?? round2.body, 200)}`); } results[model] = { stream: { status: s.status, events: s.events, toolDeltas }, assembled: toolCalls, round2 }; } // tool_call_id format constraint: try a long OpenAI-style id in a tool message const bad = await post("/chat/completions", { model: "mistral-small-latest", max_tokens: 50, messages: [ { role: "user", content: "Weather in Paris?" }, { role: "assistant", content: "", tool_calls: [{ id: "call_abc123def456ghi789", type: "function", function: { name: "get_weather", arguments: '{"city":"Paris"}' } }] }, { role: "tool", tool_call_id: "call_abc123def456ghi789", name: "get_weather", content: '{"temp_c": 12}' }, ], tools, }); console.log("\nlong tool_call_id round trip", bad.status, short(bad.body, 400)); results.longToolCallId = bad; save("03-tools-stream.json", results);