Spaces:
Runtime error
Runtime error
| // Test tool-calling support across all configured models. | |
| import { readFileSync } from "node:fs" | |
| import { streamText, tool } from "ai" | |
| import { createGoogleGenerativeAI } from "@ai-sdk/google" | |
| import { createGroq } from "@ai-sdk/groq" | |
| import { createOpenAI } from "@ai-sdk/openai" | |
| import { z } from "zod" | |
| // Tiny inline .env.local loader (avoids extra dep) | |
| try { | |
| const env = readFileSync(".env.local", "utf8") | |
| for (const line of env.split("\n")) { | |
| const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*"?([^"\r\n]*)"?\s*$/) | |
| if (m && !process.env[m[1]]) process.env[m[1]] = m[2] | |
| } | |
| } catch {} | |
| const PROVIDERS = { | |
| google: { | |
| models: [ | |
| "gemini-2.5-flash-lite", | |
| "gemini-2.5-flash", | |
| "gemini-2.5-pro", | |
| "gemini-2.0-flash", | |
| "gemini-2.0-flash-lite", | |
| ], | |
| }, | |
| groq: { | |
| models: [ | |
| "llama-3.3-70b-versatile", | |
| "llama-3.1-8b-instant", | |
| "openai/gpt-oss-120b", | |
| "openai/gpt-oss-20b", | |
| "qwen/qwen3-32b", | |
| "meta-llama/llama-4-scout-17b-16e-instruct", | |
| ], | |
| }, | |
| github: { | |
| models: [ | |
| "gpt-4o-mini", | |
| "gpt-4o", | |
| "Meta-Llama-3.1-8B-Instruct", | |
| "Meta-Llama-3.1-405B-Instruct", | |
| ], | |
| }, | |
| } | |
| const tools = { | |
| calculator: tool({ | |
| description: "Evaluate a math expression. Use for arithmetic.", | |
| parameters: z.object({ expression: z.string() }), | |
| execute: async ({ expression }) => { | |
| const r = Function(`"use strict";return (${expression})`)() | |
| return { result: r } | |
| }, | |
| }), | |
| currentTime: tool({ | |
| description: "Get current ISO date-time.", | |
| parameters: z.object({}), | |
| execute: async () => ({ now: new Date().toISOString() }), | |
| }), | |
| } | |
| function build(provider, modelId) { | |
| if (provider === "google") | |
| return createGoogleGenerativeAI({ apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY })(modelId) | |
| if (provider === "groq") return createGroq({ apiKey: process.env.GROQ_API_KEY })(modelId) | |
| if (provider === "github") | |
| return createOpenAI({ | |
| apiKey: process.env.GITHUB_TOKEN, | |
| baseURL: "https://models.inference.ai.azure.com", | |
| })(modelId) | |
| } | |
| async function testOne(provider, modelId) { | |
| const t0 = Date.now() | |
| try { | |
| const result = streamText({ | |
| model: build(provider, modelId), | |
| tools, | |
| maxSteps: 3, | |
| system: | |
| "Khi user hỏi tính toán PHẢI gọi tool calculator. " + | |
| "Khi user hỏi giờ PHẢI gọi tool currentTime. Không tự tính.", | |
| messages: [{ role: "user", content: "Tính giúp: 137 * 29 + 8 = ?" }], | |
| }) | |
| let toolCalled = false | |
| let toolName = null | |
| let text = "" | |
| let chunks = 0 | |
| let firstChunkMs = null | |
| for await (const part of result.fullStream) { | |
| chunks++ | |
| if (firstChunkMs === null) firstChunkMs = Date.now() - t0 | |
| if (part.type === "tool-call") { | |
| toolCalled = true | |
| toolName = part.toolName | |
| } else if (part.type === "text-delta") { | |
| text += part.textDelta | |
| } else if (part.type === "error") { | |
| throw part.error | |
| } | |
| } | |
| return { | |
| ok: true, | |
| toolCalled, | |
| toolName, | |
| streaming: chunks > 3, | |
| chunks, | |
| ttfbMs: firstChunkMs, | |
| totalMs: Date.now() - t0, | |
| textPreview: text.slice(0, 80).replace(/\s+/g, " "), | |
| } | |
| } catch (e) { | |
| return { ok: false, error: (e?.message || String(e)).slice(0, 240), totalMs: Date.now() - t0 } | |
| } | |
| } | |
| const results = [] | |
| for (const [provider, info] of Object.entries(PROVIDERS)) { | |
| for (const m of info.models) { | |
| process.stdout.write(`Testing ${provider}/${m} ... `) | |
| const r = await testOne(provider, m) | |
| const status = r.ok | |
| ? r.toolCalled | |
| ? `OK tool=${r.toolName} stream=${r.streaming ? "y" : "n"} chunks=${r.chunks} ttfb=${r.ttfbMs}ms total=${r.totalMs}ms` | |
| : `NO-TOOL stream=${r.streaming ? "y" : "n"} total=${r.totalMs}ms text="${r.textPreview}"` | |
| : `ERR ${r.error}` | |
| console.log(status) | |
| results.push({ provider, model: m, ...r }) | |
| await new Promise((r) => setTimeout(r, 800)) | |
| } | |
| } | |
| console.log("\n=== SUMMARY ===") | |
| for (const r of results) { | |
| const tool = r.ok ? (r.toolCalled ? "OK " : "NO ") : "ERR" | |
| const stream = r.ok ? (r.streaming ? "y" : "n") : "-" | |
| console.log(`${tool} stream=${stream} ${r.provider.padEnd(8)} ${r.model}`) | |
| } | |
| const broken = results.filter((r) => !r.ok) | |
| const noTool = results.filter((r) => r.ok && !r.toolCalled) | |
| console.log(`\n${results.length} tested · ${broken.length} broken · ${noTool.length} no tool-call`) | |
| if (broken.length) { | |
| console.log("\nBROKEN:") | |
| for (const b of broken) console.log(` ${b.provider}/${b.model}: ${b.error}`) | |
| } | |