Files
UHD_Media_Tool/spikes/pi/provider-config.ts
T

70 lines
4.8 KiB
TypeScript

// 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<void>((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<any>[]) {
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();