* Stand up an in-process upstream that speaks the real CAPI shapes the * runtime needs: model catalog, policy, `/responses` SSE for HTTP * inference, and a WebSocket endpoint at `/responses` that answers each * inbound `response.create` with the ordered `/responses` events the * reducer expects.
()
| 29 | * server directly; the handler does, on the runtime's behalf. |
| 30 | */ |
| 31 | async function startFakeUpstream(): Promise<{ |
| 32 | url: string; |
| 33 | server: HttpServer; |
| 34 | wsRequestCount: () => number; |
| 35 | close: () => Promise<void>; |
| 36 | }> { |
| 37 | let wsRequests = 0; |
| 38 | |
| 39 | const httpServer = createServer((req, res) => { |
| 40 | const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`); |
| 41 | if (url.pathname === "/models" && req.method === "GET") { |
| 42 | sendJson(res, 200, { |
| 43 | data: [ |
| 44 | { |
| 45 | id: "claude-sonnet-4.5", |
| 46 | name: "Claude Sonnet 4.5", |
| 47 | object: "model", |
| 48 | vendor: "Anthropic", |
| 49 | version: "1", |
| 50 | preview: false, |
| 51 | model_picker_enabled: true, |
| 52 | supported_endpoints: ["/responses", "ws:/responses"], |
| 53 | capabilities: { |
| 54 | type: "chat", |
| 55 | family: "claude-sonnet-4.5", |
| 56 | tokenizer: "o200k_base", |
| 57 | limits: { |
| 58 | max_context_window_tokens: 200000, |
| 59 | max_output_tokens: 8192, |
| 60 | }, |
| 61 | supports: { |
| 62 | streaming: true, |
| 63 | tool_calls: true, |
| 64 | parallel_tool_calls: true, |
| 65 | vision: true, |
| 66 | }, |
| 67 | }, |
| 68 | }, |
| 69 | ], |
| 70 | }); |
| 71 | return; |
| 72 | } |
| 73 | if (url.pathname.endsWith("/models/session")) { |
| 74 | sendJson(res, 200, {}); |
| 75 | return; |
| 76 | } |
| 77 | if (url.pathname.includes("/policy")) { |
| 78 | sendJson(res, 200, { state: "enabled" }); |
| 79 | return; |
| 80 | } |
| 81 | if (url.pathname.endsWith("/responses") && req.method === "POST") { |
| 82 | // Single-shot HTTP inference (e.g. title generation). SSE |
| 83 | // events the `responses-client.ts` reducer accepts. |
| 84 | drainBody(req) |
| 85 | .then(() => { |
| 86 | res.writeHead(200, { |
| 87 | "content-type": "text/event-stream", |
| 88 | "cache-control": "no-cache", |
no test coverage detected
searching dependent graphs…