70 lines
4.8 KiB
TypeScript
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();
|