import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import OpenAI from "openai"; function jsonResponse(value, status = 200, extraHeaders = {}) { return new Response(JSON.stringify(value), { status, headers: { "Content-Type": "application/json", "X-Request-ID": "req_js_contract", ...extraHeaders, }, }); } function inputItems(input) { if (typeof input === "string") { return [ { id: "msg_input_contract", type: "message", role: "user", content: [{ type: "input_text", text: input }], status: "completed", }, ]; } return input.map((item, index) => ({ id: item.id ?? `item_contract_${index}`, status: item.status ?? "completed", ...item, })); } function completedResponse({ id, model, previousResponseId, input, output }) { return { id, object: "response", created_at: 1788739200, completed_at: 1788739201, status: "completed", error: null, incomplete_details: null, instructions: null, model, output, parallel_tool_calls: false, previous_response_id: previousResponseId, store: true, tool_choice: "auto", tools: [], metadata: {}, usage: null, _input: inputItems(input), }; } function publicResponse(response) { const value = { ...response }; delete value._input; return value; } function sseResponse(response) { const initial = { ...publicResponse(response), status: "in_progress", completed_at: null, output: [], }; const item = response.output[0]; const events = []; const append = (event) => { events.push({ ...event, sequence_number: events.length }); }; append({ type: "response.created", response: initial }); append({ type: "response.in_progress", response: initial }); if (item.type === "message") { const part = item.content[0]; append({ type: "response.output_item.added", output_index: 0, item: { ...item, status: "in_progress", content: [] }, }); append({ type: "response.content_part.added", item_id: item.id, output_index: 0, content_index: 0, part: { type: "output_text", text: "", annotations: [] }, }); append({ type: "response.output_text.delta", item_id: item.id, output_index: 0, content_index: 0, delta: part.text, logprobs: [], }); append({ type: "response.output_text.done", item_id: item.id, output_index: 0, content_index: 0, text: part.text, logprobs: [], }); append({ type: "response.content_part.done", item_id: item.id, output_index: 0, content_index: 0, part, }); } else { append({ type: "response.output_item.added", output_index: 0, item: { ...item, status: "in_progress", arguments: "" }, }); append({ type: "response.function_call_arguments.delta", item_id: item.id, output_index: 0, delta: item.arguments, }); append({ type: "response.function_call_arguments.done", item_id: item.id, output_index: 0, name: item.name, arguments: item.arguments, }); } append({ type: "response.output_item.done", output_index: 0, item }); append({ type: "response.completed", response: publicResponse(response) }); const body = events .map((event) => `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`) .join(""); return new Response(body, { status: 200, headers: { "Content-Type": "text/event-stream", "X-Request-ID": "req_js_contract", }, }); } function createContractFetch() { const responses = new Map(); const idempotency = new Map(); let sequence = 0; const fetch = async (input, init) => { const request = input instanceof Request ? input : new Request(input, init); assert.match(request.headers.get("authorization") ?? "", /^Bearer /); const url = new URL(request.url); if (request.method === "GET" && url.pathname === "/v1/models") { return jsonResponse({ object: "list", data: [ { id: "assistant:asst_js", object: "model", created: 1788739200, owned_by: "interactive-media", name: "JS contract assistant", capabilities: ["responses", "realtime"], assistant_id: "asst_js", assistant_type: "prompt", runtime_mode: "pipeline", }, ], }); } if (request.method === "POST" && url.pathname === "/v1/responses") { const body = await request.clone().json(); const idempotencyKey = request.headers.get("idempotency-key"); const fingerprint = JSON.stringify(body); if (idempotencyKey && idempotency.has(idempotencyKey)) { const stored = idempotency.get(idempotencyKey); if (stored.fingerprint !== fingerprint) { return jsonResponse( { error: { type: "invalid_request_error", code: "idempotency_key_conflict", message: "Idempotency-Key was used with a different request", param: "Idempotency-Key", }, }, 409, ); } return jsonResponse(publicResponse(stored.response), 200, { "X-Idempotent-Replayed": "true", }); } const parent = body.previous_response_id ? responses.get(body.previous_response_id) : undefined; const model = body.model ?? parent?.model; sequence += 1; const responseId = `resp_js_${sequence}`; const isToolCall = body.input === "调用客户端工具"; const output = isToolCall ? [ { id: "fc_js_contract", type: "function_call", status: "completed", call_id: "call_js_contract", name: "show_message", arguments: '{"message":"请确认"}', }, ] : [ { id: `msg_js_${sequence}`, type: "message", status: "completed", role: "assistant", content: [ { type: "output_text", text: `第 ${sequence} 轮回复`, annotations: [], }, ], }, ]; const response = completedResponse({ id: responseId, model, previousResponseId: body.previous_response_id ?? null, input: body.input, output, }); responses.set(responseId, response); if (idempotencyKey) { idempotency.set(idempotencyKey, { fingerprint, response }); } return body.stream ? sseResponse(response) : jsonResponse(publicResponse(response)); } const inputItemsMatch = url.pathname.match( /^\/v1\/responses\/([^/]+)\/input_items$/, ); if (request.method === "GET" && inputItemsMatch) { const response = responses.get(inputItemsMatch[1]); if (!response) return jsonResponse({ error: { message: "Not found" } }, 404); const data = response._input; return jsonResponse({ object: "list", data, first_id: data[0]?.id ?? null, last_id: data.at(-1)?.id ?? null, has_more: false, }); } const responseMatch = url.pathname.match(/^\/v1\/responses\/([^/]+)$/); if (responseMatch && request.method === "GET") { const response = responses.get(responseMatch[1]); return response ? jsonResponse(publicResponse(response)) : jsonResponse( { error: { type: "invalid_request_error", code: "response_not_found", message: "Response not found", param: null, }, }, 404, ); } if (responseMatch && request.method === "DELETE") { const deleted = responses.delete(responseMatch[1]); return deleted ? jsonResponse({ id: responseMatch[1], object: "response", deleted: true }) : jsonResponse({ error: { message: "Not found" } }, 404); } return jsonResponse({ error: { message: "Unexpected contract request" } }, 500); }; return { fetch, responses }; } test("OpenAPI snapshot exposes the public Responses surface", () => { const openapiPath = new URL("../../backend/contracts/openapi.json", import.meta.url); const openapi = JSON.parse(readFileSync(openapiPath, "utf8")); assert.ok(openapi.paths["/v1/responses"].post); assert.ok(openapi.paths["/v1/responses/{response_id}/input_items"].get); assert.ok(openapi.paths["/v1/responses/{response_id}"].delete); assert.ok(openapi.paths["/v1/models"].get); }); test("official JavaScript SDK completes three stateful turns and discovery", async () => { const contract = createContractFetch(); const client = new OpenAI({ apiKey: "sk-rt-js-contract", baseURL: "https://contract.test/v1", fetch: contract.fetch, maxRetries: 0, }); const models = await client.models.list(); assert.equal(models.data[0].id, "assistant:asst_js"); const first = await client.responses.create({ model: "assistant:asst_js", input: "第一轮", }); const second = await client.responses.create({ previous_response_id: first.id, input: "第二轮", }); const third = await client.responses.create({ previous_response_id: second.id, input: "第三轮", }); assert.equal(third.previous_response_id, second.id); assert.equal(third.model, "assistant:asst_js"); assert.equal(third.output_text, "第 3 轮回复"); const page = await client.responses.inputItems.list(third.id, { order: "asc" }); assert.equal(page.data[0].type, "message"); const retrieved = await client.responses.retrieve(third.id); assert.equal(retrieved.id, third.id); const deleted = await client.responses.delete(first.id); assert.equal(deleted.deleted, true); }); test("official JavaScript SDK parses SSE and Client Tool continuation", async () => { const contract = createContractFetch(); const client = new OpenAI({ apiKey: "sk-rt-js-contract", baseURL: "https://contract.test/v1", fetch: contract.fetch, maxRetries: 0, }); const stream = await client.responses.create({ model: "assistant:asst_js", input: "流式输出", stream: true, }); const events = []; for await (const event of stream) events.push(event); assert.deepEqual( events.map((event) => event.sequence_number), Array.from({ length: events.length }, (_, index) => index), ); assert.equal(events.at(-1).type, "response.completed"); const streamed = await client.responses.retrieve( events.at(-1).response.id, ); assert.equal( streamed.output_text, events.at(-1).response.output[0].content[0].text, ); const toolStream = await client.responses.create({ model: "assistant:asst_js", input: "调用客户端工具", stream: true, }); const toolEvents = []; for await (const event of toolStream) toolEvents.push(event); assert.ok( toolEvents.some( (event) => event.type === "response.function_call_arguments.done", ), ); const tool = toolEvents.at(-1).response; const continued = await client.responses.create({ previous_response_id: tool.id, input: [ { type: "function_call_output", call_id: tool.output[0].call_id, output: '{"status":"ok"}', }, ], }); assert.equal(continued.status, "completed"); }); test("official JavaScript SDK preserves idempotency semantics", async () => { const contract = createContractFetch(); const client = new OpenAI({ apiKey: "sk-rt-js-contract", baseURL: "https://contract.test/v1", fetch: contract.fetch, maxRetries: 0, }); const options = { headers: { "Idempotency-Key": "js-contract-key" } }; const request = { model: "assistant:asst_js", input: "幂等请求" }; const first = await client.responses.create(request, options); const replay = await client.responses.create(request, options); assert.equal(replay.id, first.id); await assert.rejects( client.responses.create( { model: "assistant:asst_js", input: "不同请求" }, options, ), (error) => error.status === 409, ); });