// Real provider types (Anthropic Messages + OpenAI-compatible Chat Completions) pointed at a LOCAL capture server. // No external network: the capture server records method/path/headers/body and returns HTTP 400. import http from "node:http"; import { createModels, createProvider, getSupportedThinkingLevels, type Model } from "@earendil-works/pi-ai"; import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy"; import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy"; import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic"; import { AgentHarness, BACKGROUND_CONTEXT, MemorySessionRepo } from "@earendil-works/pi-agent-core"; import { inventoryQuery } from "./common.ts"; const captured: any[] = []; const server = http.createServer((req, res) => { let body = ""; req.on("data", (c) => (body += c)); req.on("end", () => { captured.push({ method: req.method, url: req.url, auth: req.headers["authorization"] ?? req.headers["x-api-key"], body: JSON.parse(body || "{}") }); res.writeHead(400, { "content-type": "application/json" }); res.end(JSON.stringify({ error: { type: "invalid_request_error", message: "capture server: refusing on purpose" } })); }); }); await new Promise((r) => server.listen(0, "127.0.0.1", r)); const base = `http://127.0.0.1:${(server.address() as any).port}`; // Secret comes from our own secret store; resolve() is called at request time. No env var lookup. const secret = (name: string) => ({ name, resolve: async () => ({ auth: { apiKey: `sk-test-${name}` } }) }); const anthropicCompatible: Model<"anthropic-messages"> = { id: "claude-sonnet-4-5", name: "Anthropic-compatible upstream", api: "anthropic-messages", provider: "upstream-anthropic", baseUrl: base, reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 200000, maxTokens: 8192, }; const openaiCompatible: Model<"openai-completions"> = { id: "qwen3-235b", name: "OpenAI-compatible upstream", api: "openai-completions", provider: "upstream-openai", baseUrl: `${base}/v1`, reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192, compat: { supportsDeveloperRole: false, thinkingFormat: "qwen" }, }; const models = createModels(); models.setProvider(createProvider({ id: "upstream-anthropic", auth: { apiKey: secret("anthropic") }, models: [anthropicCompatible], api: anthropicMessagesApi() })); models.setProvider(createProvider({ id: "upstream-openai", auth: { apiKey: secret("openai") }, models: [openaiCompatible], api: openAICompletionsApi() })); // Built-in provider catalog is available too (no request made): const builtin = anthropicProvider(); const cat = createModels(); cat.setProvider(builtin); console.log("builtin anthropic models (first 5):", cat.getModels("anthropic").slice(0, 5).map((m) => m.id).join(", ")); console.log("builtin anthropic auth configured without env?", JSON.stringify(await cat.checkAuth("anthropic"))); console.log("supported thinking levels, custom anthropic model:", getSupportedThinkingLevels(anthropicCompatible).join(",")); console.log("supported thinking levels, custom openai model:", getSupportedThinkingLevels(openaiCompatible).join(",")); for (const model of [anthropicCompatible, openaiCompatible] as Model[]) { const repo = new MemorySessionRepo(); const session = await repo.create({}, BACKGROUND_CONTEXT); const { harness } = await AgentHarness.create({ session, models, model, systemPrompt: "媒体库助手", tools: [inventoryQuery], toolContext: { actorId: "u", grants: [] }, thinkingLevel: "high", compaction: { enabled: true, reserveTokens: 8000, keepRecentTokens: 12000 }, retry: { enabled: false, maxRetries: 0, baseDelayMs: 0 }, streamOptions: { timeoutMs: 5000, maxRetries: 0, headers: { "x-umt-run": "spike" } }, }, BACKGROUND_CONTEXT); harness.hooks.on("before_request", (ev) => { console.log(`[before_request] ${ev.model.provider}/${ev.model.id} step=${ev.step} attempt=${ev.attempt}`); return undefined; }); const lane = await harness.lane("main", BACKGROUND_CONTEXT); const r = await lane.prompt("库里有 Dune 吗", undefined, BACKGROUND_CONTEXT); console.log(`[${model.provider}] run:`, JSON.stringify(r.ok ? { status: r.value.status, error: (r.value as any).error } : r.error).slice(0, 300)); await harness.close(BACKGROUND_CONTEXT); } for (const c of captured) { const b = c.body; console.log(`CAPTURED ${c.method} ${c.url} auth=${c.auth}`); console.log(` model=${b.model} max_tokens=${b.max_tokens ?? b.max_completion_tokens} thinking=${JSON.stringify(b.thinking ?? b.enable_thinking ?? b.reasoning_effort)} tools=${JSON.stringify((b.tools ?? []).map((t: any) => t.name ?? t.function?.name))} stream=${b.stream}`); } server.close();