// Probe 03: streamed function-call round trip on each model (OpenAI SDK). import { client, save, short } from "./lib.ts"; import { MODELS, byId } from "./models.ts"; const tools: any = [ { type: "function", function: { name: "get_weather", description: "Get the current weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] }, }, }, ]; const results: any = {}; for (const model of MODELS) { const out: any = { chunks: [] as any[] }; results[model] = out; try { const messages: any[] = [{ role: "user", content: "What's the weather in Montreal? Use the tool." }]; const stream = await client.chat.completions.create({ model, messages, tools, tool_choice: "auto", stream: true, max_tokens: 200, ...(byId.get(model)?.reasoning ? { reasoning: { effort: "low" } } : {}), } as any); const acc: Record = {}; let deltaWithTools = 0; let finish: string | undefined; let usage: any; let reasoningDetails: any[] = []; for await (const chunk of stream) { const c: any = chunk.choices?.[0]; if (c?.delta?.tool_calls) { deltaWithTools++; if (out.chunks.length < 4) out.chunks.push(chunk); for (const tc of c.delta.tool_calls) { const i = tc.index ?? 0; acc[i] ??= { id: tc.id, type: "function", function: { name: "", arguments: "" } }; if (tc.id) acc[i].id = tc.id; if (tc.function?.name) acc[i].function.name += tc.function.name; if (tc.function?.arguments) acc[i].function.arguments += tc.function.arguments; } } if (c?.delta?.reasoning_details) reasoningDetails.push(...c.delta.reasoning_details); if (c?.finish_reason) finish = c.finish_reason; if ((chunk as any).usage) usage = (chunk as any).usage; } const toolCalls = Object.values(acc); out.round1 = { finish, deltaWithTools, toolCalls, usage, reasoningDetailsTypes: [...new Set(reasoningDetails.map((d) => d.type))] }; console.log(`\n[${model}] round1 finish=${finish} toolDeltaChunks=${deltaWithTools} calls=${JSON.stringify(toolCalls)} rt=${usage?.completion_tokens_details?.reasoning_tokens} cost=${usage?.cost}`); if (!toolCalls.length) continue; const assistantMsg: any = { role: "assistant", content: null, tool_calls: toolCalls }; if (reasoningDetails.length) assistantMsg.reasoning_details = reasoningDetails; // docs: pass back unmodified messages.push(assistantMsg); for (const tc of toolCalls) { messages.push({ role: "tool", tool_call_id: tc.id, content: JSON.stringify({ city: "Montreal", temp_c: 21, sky: "sunny" }) }); } const r2: any = await client.chat.completions.create({ model, messages, tools, max_tokens: 100, ...(byId.get(model)?.reasoning ? { reasoning: { effort: "low" } } : {}) } as any); out.round2 = { finish: r2.choices[0].finish_reason, content: r2.choices[0].message.content, usage: r2.usage, provider: r2.provider }; console.log(` round2 finish=${r2.choices[0].finish_reason} provider=${r2.provider} content=${short(r2.choices[0].message.content, 120)}`); } catch (e: any) { out.error = { status: e.status, message: e.message, body: e.error }; console.log(` ERROR ${e.status} ${short(e.error ?? e.message, 300)}`); } } save("03-tools.json", results);