85 lines
3 KiB
TypeScript
85 lines
3 KiB
TypeScript
/**
|
|
* Probe: does the configured MiniMax endpoint accept (a) image blocks and
|
|
* (b) tool calls, and what content-block types come back?
|
|
*
|
|
* Read-only against PB (reads ai_settings); makes two small LLM calls.
|
|
*
|
|
* Run with: npx tsx scripts/smoke/giellie-vision-probe.ts
|
|
* Requires AI enabled + key set at /settings/ai.
|
|
*/
|
|
import "./_env"; // MUST be first
|
|
import Anthropic from "@anthropic-ai/sdk";
|
|
import { createCanvas } from "@napi-rs/canvas";
|
|
import { getActiveAiConfig } from "../../src/lib/server/ai-settings-service";
|
|
|
|
async function main() {
|
|
const cfg = await getActiveAiConfig();
|
|
const client = new Anthropic({
|
|
apiKey: cfg.apiKey,
|
|
...(cfg.baseUrl ? { baseURL: cfg.baseUrl } : {}),
|
|
});
|
|
console.log(`[probe] model=${cfg.modelId} baseUrl=${cfg.baseUrl || "(default)"}`);
|
|
|
|
// (a) Vision: render "PRICE 4471" to a PNG and ask for the number.
|
|
const canvas = createCanvas(400, 120);
|
|
const ctx = canvas.getContext("2d");
|
|
ctx.fillStyle = "#fff";
|
|
ctx.fillRect(0, 0, 400, 120);
|
|
ctx.fillStyle = "#000";
|
|
ctx.font = "48px sans-serif";
|
|
ctx.fillText("PRICE 4471", 20, 75);
|
|
const png = canvas.toBuffer("image/png").toString("base64");
|
|
|
|
try {
|
|
const res = await client.messages.create({
|
|
model: cfg.modelId,
|
|
max_tokens: 1024,
|
|
messages: [
|
|
{
|
|
role: "user",
|
|
content: [
|
|
{ type: "image", source: { type: "base64", media_type: "image/png", data: png } },
|
|
{ type: "text", text: "What number is written in the image? Reply with just the number." },
|
|
],
|
|
},
|
|
],
|
|
});
|
|
const text = res.content.map((b) => (b.type === "text" ? b.text : "")).join("");
|
|
const types = res.content.map((b) => b.type).join(",");
|
|
console.log(`[probe] VISION block types=[${types}] answer=${JSON.stringify(text.trim())}`);
|
|
console.log(text.includes("4471") ? "[probe] VISION: PASS" : "[probe] VISION: FAIL (answer wrong)");
|
|
} catch (e) {
|
|
console.log("[probe] VISION: FAIL (request rejected):", e instanceof Error ? e.message : e);
|
|
}
|
|
|
|
// (b) Tool calling.
|
|
try {
|
|
const res = await client.messages.create({
|
|
model: cfg.modelId,
|
|
max_tokens: 1024,
|
|
tools: [
|
|
{
|
|
name: "orders__get",
|
|
description: "Get an order by order number.",
|
|
input_schema: {
|
|
type: "object",
|
|
properties: { order_number: { type: "string" } },
|
|
required: ["order_number"],
|
|
},
|
|
},
|
|
],
|
|
messages: [{ role: "user", content: "What is the status of order 12345? Use the tool." }],
|
|
});
|
|
const types = res.content.map((b) => b.type).join(",");
|
|
const tu = res.content.find((b) => b.type === "tool_use");
|
|
console.log(`[probe] TOOLS block types=[${types}] stop_reason=${res.stop_reason}`);
|
|
console.log(tu ? `[probe] TOOLS: PASS input=${JSON.stringify((tu as { input: unknown }).input)}` : "[probe] TOOLS: FAIL (no tool_use)");
|
|
} catch (e) {
|
|
console.log("[probe] TOOLS: FAIL (request rejected):", e instanceof Error ? e.message : e);
|
|
}
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|