Files
command-code-openai-bridge/src/responses.ts
T
2026-08-12 16:23:52 +08:00

1961 lines
63 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { randomUUID } from "node:crypto";
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
import path from "node:path";
import type { ServerResponse } from "node:http";
import { Ajv, type ErrorObject, type ValidateFunction } from "ajv";
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z, ZodError } from "zod";
import {
attachmentPromptBlock,
AttachmentInputError,
decodeImageDataUrl,
decodeTextFileData,
materializeAttachments,
validateAttachmentBatch,
type AttachmentReference,
type MaterializedAttachments,
type PendingAttachment,
} from "./attachments.js";
import {
CommandCodeError,
runCommandCode,
type CommandResult,
type InstallationStatus,
} from "./command-code.js";
import type { BridgeConfig } from "./config.js";
import { FinalTurnAccumulator } from "./final-turn.js";
import { openAIError, type CommandUsage } from "./openai.js";
import type { RequestCoordinator } from "./request-coordinator.js";
import {
parseFunctionToolDecision,
parseFunctionToolArguments,
ToolDecisionError,
validateFunctionToolArguments,
validateFunctionToolSchemas,
type FunctionToolCall,
type FunctionToolChoice,
type FunctionToolDecisionRequest,
type FunctionToolDefinition,
} from "./tool-calling.js";
const responseIdPattern = /^resp_[A-Za-z0-9_-]{1,128}$/;
const responseIdSchema = z.string().regex(responseIdPattern, "Invalid response ID");
const roleSchema = z.enum(["system", "developer", "user", "assistant"]);
const functionNameSchema = z.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
const responseItemIdSchema = z.string().min(1).max(128);
const inputTextPartSchema = z.object({
type: z.literal("input_text"),
text: z.string(),
}).strict();
const outputTextPartSchema = z.object({
type: z.literal("output_text"),
text: z.string(),
}).strict();
const inputImagePartSchema = z.object({
type: z.literal("input_image"),
image_url: z.string().min(1).optional(),
file_id: z.string().min(1).optional(),
detail: z.enum(["auto", "low", "high"]).optional(),
}).strict().superRefine((part, context) => {
if ((part.image_url === undefined) === (part.file_id === undefined)) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "input_image requires exactly one of image_url or file_id",
});
}
});
const inputFilePartSchema = z.object({
type: z.literal("input_file"),
file_data: z.string().min(1).optional(),
file_url: z.string().min(1).optional(),
file_id: z.string().min(1).optional(),
filename: z.string().min(1).max(255).optional(),
}).strict().superRefine((part, context) => {
const sourceCount = [part.file_data, part.file_url, part.file_id]
.filter((source) => source !== undefined).length;
if (sourceCount !== 1) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "input_file requires exactly one of file_data, file_url, or file_id",
});
}
if (part.file_data !== undefined && part.filename === undefined) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["filename"],
message: "filename is required with file_data",
});
}
});
const inputMessageSchema = z.object({
type: z.literal("message").optional(),
role: roleSchema,
content: z.union([
z.string(),
z.array(z.union([
inputTextPartSchema,
outputTextPartSchema,
inputImagePartSchema,
inputFilePartSchema,
])).min(1),
]),
}).strict();
const inputFunctionCallSchema = z.object({
id: responseItemIdSchema.optional(),
type: z.literal("function_call"),
call_id: responseItemIdSchema,
name: functionNameSchema,
arguments: z.string(),
status: z.enum(["in_progress", "completed", "incomplete"]).optional(),
}).strict();
const inputFunctionCallOutputSchema = z.object({
id: responseItemIdSchema.optional(),
type: z.literal("function_call_output"),
call_id: responseItemIdSchema,
output: z.string(),
status: z.enum(["in_progress", "completed", "incomplete"]).optional(),
}).strict();
const responseInputItemSchema = z.union([
inputMessageSchema,
inputFunctionCallSchema,
inputFunctionCallOutputSchema,
]);
const responseFunctionToolSchema = z.object({
type: z.literal("function"),
name: functionNameSchema,
description: z.string().nullable().optional(),
parameters: z.union([z.record(z.unknown()), z.null()]).optional(),
strict: z.boolean().nullable().optional(),
}).strict();
const responseToolChoiceSchema = z.union([
z.enum(["none", "auto", "required"]),
z.object({ type: z.literal("function"), name: functionNameSchema }).strict(),
]);
const textFormatSchema = z.object({
type: z.literal("text"),
}).strict();
const jsonObjectFormatSchema = z.object({
type: z.literal("json_object"),
}).strict();
const jsonSchemaFormatSchema = z.object({
type: z.literal("json_schema"),
name: z.string().min(1).max(64),
strict: z.boolean().optional().default(true),
schema: z.union([z.record(z.unknown()), z.boolean()]),
}).strict();
const responseTextFormatSchema = z.discriminatedUnion("type", [
textFormatSchema,
jsonObjectFormatSchema,
jsonSchemaFormatSchema,
]);
const metadataSchema = z.record(z.string().max(512)).superRefine((metadata, context) => {
if (Object.keys(metadata).length > 16) {
context.addIssue({ code: z.ZodIssueCode.custom, message: "Metadata supports at most 16 entries" });
}
for (const key of Object.keys(metadata)) {
if (key.length > 64) {
context.addIssue({ code: z.ZodIssueCode.custom, message: "Metadata keys support at most 64 characters" });
break;
}
}
});
export const responseRequestSchema = z.object({
model: z.string().min(1),
input: z.union([z.string(), z.array(responseInputItemSchema).min(1)]),
instructions: z.string().nullable().optional().default(null),
stream: z.boolean().optional().default(false),
store: z.boolean().optional().default(true),
previous_response_id: responseIdSchema.nullable().optional().default(null),
metadata: metadataSchema.optional().default({}),
reasoning: z.object({
effort: z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]),
}).strict().optional(),
text: z.object({
format: responseTextFormatSchema,
}).strict().optional().default({ format: { type: "text" } }),
tools: z.array(responseFunctionToolSchema).min(1).max(128).optional(),
tool_choice: responseToolChoiceSchema.optional(),
parallel_tool_calls: z.boolean().optional(),
}).strict().superRefine((request, context) => {
const toolNames = new Set<string>();
for (const [index, tool] of (request.tools ?? []).entries()) {
if (toolNames.has(tool.name)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["tools", index, "name"],
message: `Duplicate tool name '${tool.name}'`,
});
}
toolNames.add(tool.name);
}
if (request.tool_choice !== undefined && request.tools === undefined) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["tool_choice"],
message: "tool_choice requires tools",
});
}
if (typeof request.tool_choice === "object" && !toolNames.has(request.tool_choice.name)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["tool_choice", "name"],
message: `Unknown forced tool '${request.tool_choice.name}'`,
});
}
});
type ResponseRequest = z.infer<typeof responseRequestSchema>;
export type ResponseTextFormat = z.infer<typeof responseTextFormatSchema>;
type ResponseRole = z.infer<typeof roleSchema>;
type ResponseStatus = "in_progress" | "completed" | "incomplete" | "failed" | "cancelled";
interface ResponseInputPart {
type: "input_text" | "output_text";
text: string;
}
interface ResponseInputMessage {
id: string;
type: "message";
role: ResponseRole;
content: ResponseInputPart[];
}
interface ResponseInputFunctionCall {
id: string;
type: "function_call";
call_id: string;
name: string;
arguments: string;
status: "in_progress" | "completed" | "incomplete";
}
interface ResponseInputFunctionCallOutput {
id: string;
type: "function_call_output";
call_id: string;
output: string;
status: "in_progress" | "completed" | "incomplete";
}
type ResponseInputItem = ResponseInputMessage | ResponseInputFunctionCall | ResponseInputFunctionCallOutput;
interface ResponseOutputText {
type: "output_text";
text: string;
annotations: [];
}
interface ResponseOutputMessage {
id: string;
type: "message";
role: "assistant";
status: "completed" | "incomplete";
content: ResponseOutputText[];
}
interface ResponseOutputFunctionCall {
id: string;
type: "function_call";
call_id: string;
name: string;
arguments: string;
status: "completed" | "incomplete";
}
interface ResponseReasoningSummaryText {
type: "summary_text";
text: string;
}
interface ResponseOutputReasoning {
id: string;
type: "reasoning";
summary: ResponseReasoningSummaryText[];
status: "in_progress" | "completed" | "incomplete";
content?: Array<{ type: "reasoning_text"; text: string }>;
}
type ResponseOutputItem = ResponseOutputMessage | ResponseOutputFunctionCall | ResponseOutputReasoning;
interface ResponseUsage {
input_tokens: number;
output_tokens: number;
total_tokens: number;
}
interface ResponseError {
code: string;
message: string;
}
interface LocalResponse {
id: string;
object: "response";
created_at: number;
completed_at: number | null;
status: ResponseStatus;
error: ResponseError | null;
incomplete_details: { reason: string } | null;
instructions: string | null;
model: string;
output: ResponseOutputItem[];
output_text: string;
previous_response_id: string | null;
reasoning: { effort: string | null; summary: null };
store: boolean;
metadata: Record<string, string>;
text: { format: ResponseTextFormat };
tools: ResponseFunctionTool[];
tool_choice: ResponseToolChoice;
parallel_tool_calls: boolean;
usage: ResponseUsage | null;
}
interface ResponseFunctionTool {
type: "function";
name: string;
description?: string | null;
parameters: Record<string, unknown>;
strict: boolean | null;
}
type ResponseToolChoice = "none" | "auto" | "required" | { type: "function"; name: string };
interface StoredResponse {
response: LocalResponse;
input_items: ResponseInputItem[];
}
export interface StructuredValidation {
validate: (text: string) => { valid: boolean; errors: string[] };
}
interface AttemptResult {
result: CommandResult;
finalChunks: string[];
reasoningItems: ResponseOutputReasoning[];
}
interface ExecutionResult {
finalText: string;
chunks: string[];
reasoningItems: ResponseOutputReasoning[];
usage?: CommandUsage;
validationErrors?: string[];
toolCalls?: FunctionToolCall[];
}
class ResponseApiError extends Error {
constructor(
message: string,
readonly statusCode: number,
readonly code: string,
readonly type = "invalid_request_error",
readonly param: string | null = null,
) {
super(message);
this.name = "ResponseApiError";
}
}
class ResponseStoreError extends Error {
constructor(message: string, readonly cause?: unknown) {
super(message);
this.name = "ResponseStoreError";
}
}
class ResponseStore {
readonly directory: string;
constructor(directory: string) {
this.directory = path.resolve(directory);
}
async initialize(): Promise<void> {
await mkdir(this.directory, { recursive: true });
}
private filePath(id: string): string {
if (!responseIdPattern.test(id)) {
throw new ResponseApiError("Invalid response_id", 400, "invalid_response_id", "invalid_request_error", "response_id");
}
const resolved = path.resolve(this.directory, `${id}.json`);
if (path.dirname(resolved) !== this.directory) {
throw new ResponseApiError("Invalid response_id", 400, "invalid_response_id", "invalid_request_error", "response_id");
}
return resolved;
}
async load(id: string): Promise<StoredResponse | undefined> {
const filePath = this.filePath(id);
let source: string;
try {
source = await readFile(filePath, "utf8");
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
throw new ResponseStoreError(`Unable to read stored response '${id}'`, error);
}
try {
const value: unknown = JSON.parse(source);
if (!isRecord(value) || !isRecord(value.response) || value.response.id !== id || !Array.isArray(value.input_items)) {
throw new Error("Stored response shape is invalid");
}
return value as unknown as StoredResponse;
} catch (error) {
throw new ResponseStoreError(`Stored response '${id}' is corrupt`, error);
}
}
async loadChain(id: string): Promise<StoredResponse[]> {
const chain: StoredResponse[] = [];
const seen = new Set<string>();
let currentId: string | null = id;
while (currentId) {
if (seen.has(currentId) || seen.size >= 1000) {
throw new ResponseStoreError("Stored response chain contains a cycle or is too deep");
}
seen.add(currentId);
const stored = await this.load(currentId);
if (!stored) {
throw new ResponseApiError(
`Response '${currentId}' was not found`,
404,
"response_not_found",
"invalid_request_error",
"previous_response_id",
);
}
chain.unshift(stored);
currentId = stored.response.previous_response_id;
}
return chain;
}
async save(stored: StoredResponse): Promise<void> {
const target = this.filePath(stored.response.id);
const temporary = path.resolve(this.directory, `.${stored.response.id}.${randomHexId()}.tmp`);
if (path.dirname(temporary) !== this.directory) throw new ResponseStoreError("Invalid temporary response path");
try {
await writeFile(temporary, `${JSON.stringify(stored, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
await rename(temporary, target);
} catch (error) {
try { await unlink(temporary); } catch { /* temporary file may not exist */ }
throw new ResponseStoreError(`Unable to store response '${stored.response.id}'`, error);
}
}
async delete(id: string): Promise<boolean> {
const target = this.filePath(id);
try {
await unlink(target);
return true;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
throw new ResponseStoreError(`Unable to delete response '${id}'`, error);
}
}
}
class ResponseReasoningAccumulator {
private readonly completed: ResponseOutputReasoning[] = [];
private current: { id: string; outputIndex: number; text: string } | undefined;
constructor(private readonly writer?: ResponsesSseWriter) {}
event(event: Record<string, unknown>): void {
if (event.type === "thinking_start") {
this.close();
return;
}
if (event.type === "thinking_delta" && typeof event.delta === "string") {
this.add(event.delta);
return;
}
if (
event.type === "thinking_end"
|| event.type === "turn_start"
|| event.type === "text_delta"
|| event.type === "message_end"
|| event.type === "turn_end"
) {
this.close();
}
}
finish(): ResponseOutputReasoning[] {
this.close();
return [...this.completed];
}
private add(delta: string): void {
if (delta === "") return;
if (!this.current) {
this.current = {
id: newReasoningItemId(),
outputIndex: this.completed.length,
text: "",
};
this.writer?.beginReasoning(this.current.id, this.current.outputIndex);
}
this.current.text += delta;
this.writer?.addReasoningDelta(this.current.id, this.current.outputIndex, delta);
}
private close(): void {
if (!this.current) return;
const item: ResponseOutputReasoning = {
id: this.current.id,
type: "reasoning",
summary: [{ type: "summary_text", text: this.current.text }],
status: "completed",
};
this.completed.push(item);
this.writer?.finishReasoning(item, this.current.outputIndex);
this.current = undefined;
}
}
class ResponsesSseWriter {
private sequenceNumber = 0;
private outputStarted = false;
private outputText = "";
private textOutputIndex: number | undefined;
private readonly emittedReasoningIds = new Set<string>();
private readonly completedReasoning = new Map<string, ResponseOutputReasoning>();
constructor(
private readonly response: ServerResponse,
private readonly messageId: string,
) {}
event(type: string, payload: Record<string, unknown>): void {
if (this.response.destroyed || this.response.writableEnded) return;
const event = { type, ...payload, sequence_number: this.sequenceNumber++ };
this.response.write(`event: ${type}\ndata: ${JSON.stringify(event)}\n\n`);
}
begin(initial: LocalResponse): void {
this.event("response.created", { response: initial });
this.event("response.in_progress", { response: initial });
}
beginReasoning(itemId: string, outputIndex: number): void {
if (this.emittedReasoningIds.has(itemId)) return;
this.emittedReasoningIds.add(itemId);
this.event("response.output_item.added", {
output_index: outputIndex,
item: {
id: itemId,
type: "reasoning",
summary: [],
status: "in_progress",
},
});
this.event("response.reasoning_summary_part.added", {
item_id: itemId,
output_index: outputIndex,
summary_index: 0,
part: { type: "summary_text", text: "" },
});
}
addReasoningDelta(itemId: string, outputIndex: number, delta: string): void {
if (delta === "" || !this.emittedReasoningIds.has(itemId)) return;
this.event("response.reasoning_summary_text.delta", {
item_id: itemId,
output_index: outputIndex,
summary_index: 0,
delta,
});
}
finishReasoning(item: ResponseOutputReasoning, outputIndex: number): void {
if (this.completedReasoning.has(item.id)) return;
const part = item.summary[0];
if (!part) return;
this.completedReasoning.set(item.id, item);
this.event("response.reasoning_summary_text.done", {
item_id: item.id,
output_index: outputIndex,
summary_index: 0,
text: part.text,
});
this.event("response.reasoning_summary_part.done", {
item_id: item.id,
output_index: outputIndex,
summary_index: 0,
part,
});
this.event("response.output_item.done", { output_index: outputIndex, item });
}
addReasoningItems(items: ResponseOutputReasoning[]): void {
for (const [outputIndex, item] of items.entries()) {
if (this.emittedReasoningIds.has(item.id)) continue;
this.beginReasoning(item.id, outputIndex);
const part = item.summary[0];
if (part?.text) this.addReasoningDelta(item.id, outputIndex, part.text);
this.finishReasoning(item, outputIndex);
}
}
addText(chunks: string[]): void {
if (!this.outputStarted) {
this.outputStarted = true;
this.textOutputIndex = this.completedReasoning.size;
this.event("response.output_item.added", {
output_index: this.textOutputIndex,
item: {
id: this.messageId,
type: "message",
role: "assistant",
status: "in_progress",
content: [],
},
});
this.event("response.content_part.added", {
item_id: this.messageId,
output_index: this.textOutputIndex,
content_index: 0,
part: { type: "output_text", text: "", annotations: [] },
});
}
for (const delta of chunks) {
if (delta === "") continue;
this.outputText += delta;
this.event("response.output_text.delta", {
item_id: this.messageId,
output_index: this.textOutputIndex,
content_index: 0,
delta,
});
}
}
finish(response: LocalResponse, terminalEvent: "response.completed" | "response.incomplete"): void {
if (response.output.some((item) => item.type === "function_call")) {
this.addToolCalls(response);
this.event(terminalEvent, { response });
return;
}
const outputIndex = response.output.findIndex((item) => (
item.type === "message" && item.id === this.messageId
));
if (outputIndex < 0) {
this.event(terminalEvent, { response });
return;
}
this.ensureFinalText(response.output_text);
if (this.outputStarted) {
const outputMessage = response.output[outputIndex];
if (!outputMessage || outputMessage.type !== "message") {
throw new Error("Response output message is missing");
}
if (this.textOutputIndex !== outputIndex) {
throw new Error("Response output message index does not match the streamed item index");
}
const part = outputMessage.content[0];
if (!part) throw new Error("Response output text part is missing");
this.event("response.output_text.done", {
item_id: this.messageId,
output_index: outputIndex,
content_index: 0,
text: response.output_text,
});
this.event("response.content_part.done", {
item_id: this.messageId,
output_index: outputIndex,
content_index: 0,
part,
});
this.event("response.output_item.done", { output_index: outputIndex, item: outputMessage });
}
this.event(terminalEvent, { response });
}
private addToolCalls(response: LocalResponse): void {
for (const [outputIndex, item] of response.output.entries()) {
if (item.type !== "function_call") continue;
const addedItem = { ...item, arguments: "", status: "in_progress" as const };
this.event("response.output_item.added", { output_index: outputIndex, item: addedItem });
if (item.arguments !== "") {
this.event("response.function_call_arguments.delta", {
item_id: item.id,
output_index: outputIndex,
delta: item.arguments,
});
}
this.event("response.function_call_arguments.done", {
item_id: item.id,
output_index: outputIndex,
arguments: item.arguments,
});
this.event("response.output_item.done", { output_index: outputIndex, item });
}
}
failed(response: LocalResponse): void {
this.event("response.failed", { response });
}
error(code: string, message: string, param: string | null = null): void {
this.event("error", { code, message, param });
}
end(): void {
if (!this.response.destroyed && !this.response.writableEnded) this.response.end();
}
private ensureFinalText(finalText: string): void {
if (!this.outputStarted) {
this.addText([finalText]);
return;
}
if (this.outputText === finalText) return;
if (finalText.startsWith(this.outputText)) {
this.addText([finalText.slice(this.outputText.length)]);
return;
}
throw new Error("Command Code finalText does not match the final structured turn text");
}
}
interface ResponseRouteDependencies {
config: BridgeConfig;
installation: InstallationStatus;
coordinator: RequestCoordinator;
}
export async function registerResponseRoutes(
app: FastifyInstance,
dependencies: ResponseRouteDependencies,
): Promise<void> {
const { config, installation, coordinator } = dependencies;
const store = new ResponseStore(config.resolvedResponseStoreDirectory);
await store.initialize();
app.get("/v1/responses/:response_id", async (request, reply) => {
try {
emptyQuerySchema.parse(request.query);
const id = responseIdFromRequest(request);
const stored = await store.load(id);
if (!stored) throw responseNotFound(id);
return reply.send(stored.response);
} catch (error) {
return sendRouteError(reply, error);
}
});
app.delete("/v1/responses/:response_id", async (request, reply) => {
try {
emptyQuerySchema.parse(request.query);
const id = responseIdFromRequest(request);
if (!await store.delete(id)) throw responseNotFound(id);
return reply.send({ id, object: "response", deleted: true });
} catch (error) {
return sendRouteError(reply, error);
}
});
app.get("/v1/responses/:response_id/input_items", async (request, reply) => {
try {
const id = responseIdFromRequest(request);
const stored = await store.load(id);
if (!stored) throw responseNotFound(id);
const query = inputItemsQuerySchema.parse(request.query);
const ordered = query.order === "asc"
? [...stored.input_items]
: [...stored.input_items].reverse();
const afterIndex = query.after ? ordered.findIndex((item) => item.id === query.after) : -1;
const available = afterIndex >= 0 ? ordered.slice(afterIndex + 1) : ordered;
const data = available.slice(0, query.limit);
return reply.send({
object: "list",
data,
first_id: data[0]?.id ?? null,
last_id: data.at(-1)?.id ?? null,
has_more: available.length > data.length,
});
} catch (error) {
return sendRouteError(reply, error);
}
});
app.post("/v1/responses", async (request, reply) => {
if (!installation.installed) {
return reply.status(503).send(openAIError(
"Command Code CLI is not installed",
"command_code_not_installed",
"server_error",
));
}
if (!installation.authenticated) {
return reply.status(503).send(openAIError(
"Command Code is not authenticated; run command-code login",
"command_code_not_authenticated",
"server_error",
));
}
let parsed: ResponseRequest;
let validation: StructuredValidation | undefined;
let pendingAttachments: PendingAttachment[];
try {
parsed = parseResponseRequest(request.body);
validation = createStructuredValidation(parsed.text.format);
validateResponseToolRequest(parsed);
pendingAttachments = responseInputAttachments(parsed);
} catch (error) {
return sendRouteError(reply, error);
}
const model = config.models[parsed.model];
if (!model) {
return reply.status(404).send(openAIError(
`Model '${parsed.model}' is not configured`,
"model_not_found",
"invalid_request_error",
"model",
));
}
const firstImage = pendingAttachments.find((attachment) => attachment.kind === "image");
if (firstImage && !model.supports_image_input) {
return sendRouteError(reply, new AttachmentInputError(
`Model '${parsed.model}' is not configured for image input`,
"unsupported_parameter",
firstImage.param,
));
}
const lease = coordinator.begin();
if (!lease) return sendBusy(reply);
const { controller: abortController } = lease;
let responseCompleted = false;
let acquired = false;
let materialized: MaterializedAttachments | undefined;
const cancelOnDisconnect = () => {
if (!responseCompleted) coordinator.cancel(abortController, "client disconnected");
};
request.raw.once("aborted", cancelOnDisconnect);
reply.raw.once("close", cancelOnDisconnect);
const inputItems = normalizeInput(parsed.input);
const responseId = newResponseId();
const messageId = newMessageId();
const createdAt = Math.floor(Date.now() / 1000);
const effectiveEffort = parsed.reasoning?.effort ?? model.effort;
const initialResponse = createResponse({
id: responseId,
messageId,
createdAt,
request: parsed,
effort: effectiveEffort,
status: "in_progress",
});
let writer: ResponsesSseWriter | undefined;
try {
if (parsed.stream) {
writer = startSse(reply, messageId);
writer.begin(initialResponse);
}
if (request.raw.aborted || reply.raw.destroyed) cancelOnDisconnect();
acquired = await lease.ready;
if (!acquired) {
responseCompleted = true;
return reply;
}
materialized = await materializeAttachments(
config.resolvedWorkingDirectory,
pendingAttachments,
);
const chain = parsed.previous_response_id
? await store.loadChain(parsed.previous_response_id)
: [];
validateResponseFunctionHistory(parsed, chain, inputItems);
const prompt = buildResponsesPrompt(parsed, chain, inputItems, materialized.references);
const deadline = Date.now() + config.timeout_seconds * 1000;
const toolRequest = responseFunctionToolDecisionRequest(parsed);
const execution = await executeResponse({
config,
cliModel: model.cli_model,
effort: effectiveEffort,
prompt,
format: parsed.text.format,
...(toolRequest ? { toolRequest } : {}),
signal: abortController.signal,
deadline,
...(validation ? { validation } : {}),
...(writer ? { writer } : {}),
});
const terminalResponse = createResponse({
id: responseId,
messageId,
createdAt,
request: parsed,
effort: effectiveEffort,
status: execution.validationErrors ? "incomplete" : "completed",
finalText: execution.finalText,
reasoningItems: execution.reasoningItems,
...(execution.toolCalls ? { toolCalls: execution.toolCalls } : {}),
...(execution.usage ? { usage: execution.usage } : {}),
...(execution.validationErrors ? { incompleteReason: "structured_output_validation_failed" } : {}),
});
if (parsed.store) await store.save({ response: terminalResponse, input_items: inputItems });
responseCompleted = true;
if (writer) {
writer.addReasoningItems(execution.reasoningItems);
if (validation) writer.addText(execution.chunks);
writer.finish(
terminalResponse,
terminalResponse.status === "completed" ? "response.completed" : "response.incomplete",
);
writer.end();
return reply;
}
return reply.send(terminalResponse);
} catch (error) {
responseCompleted = true;
const terminal = terminalResponseForError({
error,
id: responseId,
messageId,
createdAt,
request: parsed,
effort: effectiveEffort,
});
try {
if (parsed.store && terminal.response) {
await store.save({ response: terminal.response, input_items: inputItems });
}
} catch (storeError) {
process.stderr.write(`${formatUnknownError(storeError)}\n`);
}
if (writer) {
if (terminal.response?.status === "incomplete") {
writer.finish(terminal.response, "response.incomplete");
} else if (terminal.response?.status === "failed") {
writer.failed(terminal.response);
} else {
writer.error(terminal.code, terminal.message, terminal.param);
}
writer.end();
return reply;
}
if (terminal.response) return reply.status(terminal.statusCode).send(terminal.response);
return reply.status(terminal.statusCode).send(openAIError(
terminal.message,
terminal.code,
terminal.type,
terminal.param,
));
} finally {
request.raw.off("aborted", cancelOnDisconnect);
reply.raw.off("close", cancelOnDisconnect);
try {
await materialized?.cleanup();
} catch {
process.stderr.write("警告:无法清理本次请求的临时附件目录。\n");
}
if (!acquired) coordinator.cancel(abortController, "request ended before execution");
coordinator.finish(abortController);
}
});
}
const inputItemsQuerySchema = z.object({
after: z.string().optional(),
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
order: z.enum(["asc", "desc"]).optional().default("desc"),
}).strict();
const emptyQuerySchema = z.object({}).strict();
function parseResponseRequest(body: unknown): ResponseRequest {
const unsupported = findUnsupportedParameter(body);
if (unsupported) {
throw new ResponseApiError(
`Unsupported parameter: '${unsupported}'`,
400,
"unsupported_parameter",
"invalid_request_error",
unsupported,
);
}
try {
return responseRequestSchema.parse(body);
} catch (error) {
if (!(error instanceof ZodError)) throw error;
const unknown = error.issues.find((issue) => issue.code === "unrecognized_keys");
if (unknown?.code === "unrecognized_keys") {
const key = unknown.keys[0] ?? "unknown";
const param = [...unknown.path, key].join(".");
throw new ResponseApiError(
`Unsupported parameter: '${param}'`,
400,
"unsupported_parameter",
"invalid_request_error",
param,
);
}
const message = error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
throw new ResponseApiError(message, 400, "invalid_request");
}
}
function findUnsupportedParameter(body: unknown): string | undefined {
if (!isRecord(body)) return undefined;
const supported = new Set([
"model", "input", "instructions", "stream", "store",
"previous_response_id", "metadata", "reasoning", "text",
"tools", "tool_choice", "parallel_tool_calls",
]);
const unknownTopLevel = Object.keys(body).find((key) => !supported.has(key));
if (unknownTopLevel) return unknownTopLevel;
if (isRecord(body.text) && isRecord(body.text.format)) {
const formatType = body.text.format.type;
if (formatType !== "text" && formatType !== "json_object" && formatType !== "json_schema") {
return "text.format.type";
}
}
if (Array.isArray(body.tools)) {
for (const [toolIndex, tool] of body.tools.entries()) {
if (!isRecord(tool)) continue;
if (tool.type !== "function") return `tools.${toolIndex}.type`;
}
}
if (isRecord(body.tool_choice) && body.tool_choice.type !== "function") {
return "tool_choice.type";
}
if (!Array.isArray(body.input)) return undefined;
for (const [itemIndex, item] of body.input.entries()) {
if (!isRecord(item)) continue;
if (
item.type !== undefined
&& item.type !== "message"
&& item.type !== "function_call"
&& item.type !== "function_call_output"
) return `input.${itemIndex}.type`;
if (item.type === "function_call" || item.type === "function_call_output") continue;
if (!Array.isArray(item.content)) continue;
for (const [partIndex, part] of item.content.entries()) {
if (!isRecord(part)) continue;
if (
part.type !== "input_text"
&& part.type !== "output_text"
&& part.type !== "input_image"
&& part.type !== "input_file"
) {
return `input.${itemIndex}.content.${partIndex}.type`;
}
}
}
return undefined;
}
function responseInputAttachments(request: ResponseRequest): PendingAttachment[] {
if (typeof request.input === "string") return [];
const attachments: PendingAttachment[] = [];
for (const [itemIndex, item] of request.input.entries()) {
if (item.type === "function_call" || item.type === "function_call_output") continue;
if (typeof item.content === "string") continue;
for (const [partIndex, part] of item.content.entries()) {
const baseParam = `input.${itemIndex}.content.${partIndex}`;
if (part.type === "input_image") {
if (part.file_id !== undefined) {
throw new AttachmentInputError(
"file_id image inputs require an OpenAI Files service, which this bridge does not implement",
"unsupported_parameter",
`${baseParam}.file_id`,
);
}
if (part.detail !== undefined && part.detail !== "auto") {
throw new AttachmentInputError(
"Command Code does not expose image detail controls; omit detail or use 'auto'",
"unsupported_parameter",
`${baseParam}.detail`,
);
}
attachments.push(decodeImageDataUrl(
part.image_url!,
`${baseParam}.image_url`,
));
continue;
}
if (part.type !== "input_file") continue;
if (part.file_url !== undefined) {
throw new AttachmentInputError(
"Remote file URLs are not fetched; provide UTF-8 text through file_data",
"unsupported_parameter",
`${baseParam}.file_url`,
);
}
if (part.file_id !== undefined) {
throw new AttachmentInputError(
"file_id inputs require an OpenAI Files service, which this bridge does not implement",
"unsupported_parameter",
`${baseParam}.file_id`,
);
}
attachments.push(decodeTextFileData(
part.file_data!,
part.filename!,
`${baseParam}.file_data`,
));
}
}
validateAttachmentBatch(attachments);
return attachments;
}
function normalizeInput(input: ResponseRequest["input"]): ResponseInputItem[] {
if (typeof input === "string") {
return [{
id: newMessageId(),
type: "message",
role: "user",
content: [{ type: "input_text", text: input }],
}];
}
return input.map((item) => {
if (item.type === "function_call") {
return {
id: item.id ?? newFunctionCallItemId(),
type: "function_call",
call_id: item.call_id,
name: item.name,
arguments: item.arguments,
status: item.status ?? "completed",
};
}
if (item.type === "function_call_output") {
return {
id: item.id ?? newFunctionCallOutputItemId(),
type: "function_call_output",
call_id: item.call_id,
output: item.output,
status: item.status ?? "completed",
};
}
return {
id: newMessageId(),
type: "message",
role: item.role,
content: typeof item.content === "string"
? [{ type: item.role === "assistant" ? "output_text" : "input_text", text: item.content }]
: item.content.flatMap((part) => (
part.type === "input_text" || part.type === "output_text"
? [{ type: part.type, text: part.text }]
: []
)),
};
});
}
function buildResponsesPrompt(
request: ResponseRequest,
chain: StoredResponse[],
currentInput: ResponseInputItem[],
attachmentReferences: ReadonlyMap<string, AttachmentReference>,
): string {
const history: Array<ResponseInputItem | ResponseOutputItem> = [];
for (const stored of chain) {
history.push(...stored.input_items, ...stored.response.output);
}
history.push(...currentInput);
const envelope = {
protocol: usesResponseToolCalling(request, history)
? "openai-responses-tools-history-v1"
: "openai-responses-text-history-v1",
instructions: request.instructions,
input: history,
tools: normalizeResponseTools(request.tools),
tool_choice: effectiveResponseToolChoice(request),
parallel_tool_calls: request.parallel_tool_calls ?? true,
};
const attachments = attachmentPromptBlock(attachmentReferences);
if (usesResponseToolCalling(request, history)) {
const toolBoundary = attachments
? "除读取下方 API 附件临时路径所必需的 read_file 外,不要使用 Command Code 自身工具替代外部 tools;附件读取只是在消费用户输入。"
: "不要使用 Command Code 自身的文件、终端、搜索、网络或其他工具替代外部 tools;只依据 input、tools 和 function_call_output 作出决定。";
return [
"下面 JSON 对象是外部客户端提交的一次 OpenAI Responses 工具调用任务,input 包含完整历史。",
"你只负责决定当前这一轮应该调用外部工具还是返回最终回答。外部工具由客户端执行,你不能模拟工具结果。",
toolBoundary,
"严格执行 input 中的 system、developer、user 指令和当前 instructions,并结合 message、function_call、function_call_output 及其他已有 output item 理解完整历史。",
"历史 reasoning item 只包含此前公开的 summary 文本记录,不是可恢复的内部推理状态;仅把 summary 当作普通历史上下文。",
"只返回以下两个 JSON 对象之一,禁止 Markdown 代码围栏、前后说明和额外字段:",
"最终回答:{\"type\":\"final\",\"content\":\"面向用户的完整回答\"}",
"调用工具:{\"type\":\"tool_calls\",\"calls\":[{\"name\":\"工具名称\",\"arguments\":{}}]}",
"调用工具时,name 必须与 tools 中的名称完全一致,arguments 必须是符合该工具 parameters JSON Schema 的对象。",
"tool_choice 为 none 时必须返回 final;为 required 或指定函数时必须返回 tool_calls。parallel_tool_calls 为 false 时 calls 只能有一项。",
"需要工具提供信息或执行动作时返回 tool_calls 并立即结束;已有 function_call_output 且信息足够时返回 final。工具调用轮次禁止生成面向用户的正文。",
formatDirective(request.text.format),
...(attachments ? [attachments] : []),
"JSON 数据开始:",
JSON.stringify(envelope),
].join("\n\n");
}
return [
"下面 JSON 对象是外部客户端提交的完整 Responses 文本任务和本地重建的响应链。",
"按 input 中的 system、developer、user、assistant 角色与顺序理解上下文;当前 instructions 是本次请求的高优先级开发者指令。",
"历史 reasoning item 只包含此前公开的 summary 文本记录,不是可恢复的内部推理状态;仅把 summary 当作普通历史上下文。",
"严格执行最后一个用户任务。不要复述 JSON,不要输出角色标签,不要暴露内部思考。",
formatDirective(request.text.format),
...(attachments ? [attachments] : []),
"JSON 数据开始:",
JSON.stringify(envelope),
].join("\n\n");
}
export function buildRepairPrompt(
originalPrompt: string,
format: ResponseTextFormat,
invalidOutput: string,
validationErrors: string[],
): string {
return [
"这是同一请求的唯一一次结构化输出修复步骤。",
"保留原始答案的语义,只修复 JSON 语法和 Schema 违规;只返回修复后的 JSON,禁止代码围栏和解释。",
formatDirective(format),
"校验错误:",
JSON.stringify(validationErrors),
"不合格输出:",
JSON.stringify(invalidOutput),
"原始任务:",
originalPrompt,
].join("\n\n");
}
function formatDirective(format: ResponseTextFormat): string {
if (format.type === "text") return "内部输出格式要求:返回纯文本。";
if (format.type === "json_object") {
return "内部输出格式要求:只返回一个合法 JSON 对象;禁止 Markdown 代码围栏、前后说明和非 JSON 文本。";
}
return [
`内部输出格式要求:只返回一个符合 JSON Schema '${format.name}' 的合法 JSON 值;禁止 Markdown 代码围栏、前后说明和非 JSON 文本。`,
`JSON Schema${JSON.stringify(format.schema)}`,
].join("\n");
}
async function executeResponse(options: {
config: BridgeConfig;
cliModel: string;
effort: string;
prompt: string;
format: ResponseTextFormat;
toolRequest?: FunctionToolDecisionRequest;
validation?: StructuredValidation;
signal: AbortSignal;
deadline: number;
writer?: ResponsesSseWriter;
}): Promise<ExecutionResult> {
const constrained = options.validation !== undefined || options.toolRequest !== undefined;
const first = await executeAttempt(options, options.prompt, constrained ? undefined : options.writer);
if (options.toolRequest) {
return executeToolDecision({ ...options, toolRequest: options.toolRequest }, first);
}
if (!options.validation) {
return {
finalText: first.result.finalText,
chunks: first.finalChunks,
reasoningItems: first.reasoningItems,
...(first.result.usage ? { usage: first.result.usage } : {}),
};
}
const firstValidation = options.validation.validate(first.result.finalText);
if (firstValidation.valid) {
return {
finalText: first.result.finalText,
chunks: first.finalChunks,
reasoningItems: first.reasoningItems,
...(first.result.usage ? { usage: first.result.usage } : {}),
};
}
const repairPrompt = buildRepairPrompt(
options.prompt,
options.format,
first.result.finalText,
firstValidation.errors,
);
const second = await executeAttempt(options, repairPrompt);
const combinedUsage = addUsage(first.result.usage, second.result.usage);
const secondValidation = options.validation.validate(second.result.finalText);
return {
finalText: second.result.finalText,
chunks: second.finalChunks,
reasoningItems: second.reasoningItems,
...(combinedUsage ? { usage: combinedUsage } : {}),
...(!secondValidation.valid ? { validationErrors: secondValidation.errors } : {}),
};
}
async function executeToolDecision(
options: {
config: BridgeConfig;
cliModel: string;
effort: string;
prompt: string;
format: ResponseTextFormat;
toolRequest: FunctionToolDecisionRequest;
validation?: StructuredValidation;
signal: AbortSignal;
deadline: number;
},
first: AttemptResult,
): Promise<ExecutionResult> {
try {
return executionFromToolDecision(
parseFunctionToolDecision(first.result.finalText, options.toolRequest),
first,
);
} catch (error) {
if (!(error instanceof ToolDecisionError)) throw error;
process.stderr.write(`\nResponses 工具调用决策校验失败,正在修复:${error.errors.join("; ")}\n`);
const repairPrompt = buildToolDecisionRepairPrompt(
options.prompt,
first.result.finalText,
error.errors,
);
const second = await executeAttempt(options, repairPrompt);
let decision;
try {
decision = parseFunctionToolDecision(second.result.finalText, options.toolRequest);
} catch (repairError) {
if (repairError instanceof ToolDecisionError) {
throw new ResponseApiError(
`Invalid tool decision after repair: ${repairError.errors.join("; ")}`,
502,
"invalid_tool_decision",
"server_error",
);
}
throw repairError;
}
const result = executionFromToolDecision(decision, second);
const usage = addUsage(first.result.usage, second.result.usage);
return { ...result, ...(usage ? { usage } : {}) };
}
}
function executionFromToolDecision(
decision: ReturnType<typeof parseFunctionToolDecision>,
attempt: AttemptResult,
): ExecutionResult {
if (decision.type === "tool_calls") {
return {
finalText: "",
chunks: [],
reasoningItems: attempt.reasoningItems,
toolCalls: decision.toolCalls,
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
};
}
return {
finalText: decision.content,
chunks: [decision.content],
reasoningItems: attempt.reasoningItems,
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
};
}
function buildToolDecisionRepairPrompt(
originalPrompt: string,
invalidOutput: string,
errors: string[],
): string {
return [
"上一次输出不符合 Responses 外部工具调用传输协议。保持原来的决策意图,只修复 JSON 结构、工具名称或参数。",
"只返回原任务要求的 final 或 tool_calls JSON 对象,禁止 Markdown 代码围栏、前后说明和额外字段。",
"校验错误:",
errors.join("\n"),
"上一次输出:",
invalidOutput,
"原始任务:",
originalPrompt,
].join("\n\n");
}
async function executeAttempt(
options: {
config: BridgeConfig;
cliModel: string;
effort: string;
signal: AbortSignal;
deadline: number;
},
prompt: string,
writer?: ResponsesSseWriter,
): Promise<AttemptResult> {
const accumulator = new FinalTurnAccumulator();
const reasoning = new ResponseReasoningAccumulator(writer);
let result: CommandResult;
try {
result = await runCommandCode(
options.config,
options.cliModel,
options.effort,
prompt,
options.signal,
{
timeoutMs: options.deadline - Date.now(),
onEvent: (event) => {
reasoning.event(event);
const chunks = accumulator.event(event);
if (chunks && writer) writer.addText(chunks);
},
},
);
} catch (error) {
reasoning.finish();
throw error;
}
return {
result,
finalChunks: accumulator.finalChunks ?? [result.finalText],
reasoningItems: reasoning.finish(),
};
}
export function createStructuredValidation(format: ResponseTextFormat): StructuredValidation | undefined {
if (format.type === "text") return undefined;
if (format.type === "json_object") {
return {
validate: (text) => {
try {
const value: unknown = JSON.parse(text);
const valid = isRecord(value);
return valid
? { valid: true, errors: [] }
: { valid: false, errors: ["Output must be a JSON object"] };
} catch (error) {
return { valid: false, errors: [`Invalid JSON: ${formatUnknownError(error)}`] };
}
},
};
}
let validator: ValidateFunction;
try {
const ajv = new Ajv({ allErrors: true, strict: false });
validator = ajv.compile(format.schema);
} catch (error) {
throw new ResponseApiError(
`Invalid JSON Schema: ${formatUnknownError(error)}`,
400,
"invalid_json_schema",
"invalid_request_error",
"text.format.schema",
);
}
return {
validate: (text) => {
let value: unknown;
try {
value = JSON.parse(text);
} catch (error) {
return { valid: false, errors: [`Invalid JSON: ${formatUnknownError(error)}`] };
}
const valid = validator(value);
return {
valid,
errors: valid ? [] : formatAjvErrors(validator.errors),
};
},
};
}
function createResponse(options: {
id: string;
messageId: string;
createdAt: number;
request: ResponseRequest;
effort: string;
status: ResponseStatus;
finalText?: string;
reasoningItems?: ResponseOutputReasoning[];
toolCalls?: FunctionToolCall[];
usage?: CommandUsage;
error?: ResponseError;
incompleteReason?: string;
}): LocalResponse {
const finalText = options.finalText ?? "";
const terminal = options.status !== "in_progress";
const outputStatus = options.status === "completed" ? "completed" : "incomplete";
const generatedOutput: ResponseOutputItem[] = options.toolCalls
? options.toolCalls.map((call) => ({
id: newFunctionCallItemId(),
type: "function_call",
call_id: call.callId,
name: call.name,
arguments: call.arguments,
status: outputStatus,
}))
: options.finalText !== undefined
? [{
id: options.messageId,
type: "message",
role: "assistant",
status: outputStatus,
content: [{ type: "output_text", text: finalText, annotations: [] }],
}]
: [];
const output: ResponseOutputItem[] = [
...(options.reasoningItems ?? []),
...generatedOutput,
];
return {
id: options.id,
object: "response",
created_at: options.createdAt,
completed_at: terminal ? Math.floor(Date.now() / 1000) : null,
status: options.status,
error: options.error ?? null,
incomplete_details: options.incompleteReason ? { reason: options.incompleteReason } : null,
instructions: options.request.instructions,
model: options.request.model,
output,
output_text: finalText,
previous_response_id: options.request.previous_response_id,
reasoning: { effort: options.effort, summary: null },
store: options.request.store,
metadata: options.request.metadata,
text: { format: options.request.text.format },
tools: normalizeResponseTools(options.request.tools),
tool_choice: effectiveResponseToolChoice(options.request),
parallel_tool_calls: options.request.parallel_tool_calls ?? true,
usage: toResponseUsage(options.usage),
};
}
function terminalResponseForError(options: {
error: unknown;
id: string;
messageId: string;
createdAt: number;
request: ResponseRequest;
effort: string;
}): {
response?: LocalResponse;
statusCode: number;
code: string;
message: string;
type: string;
param: string | null;
} {
const { error } = options;
if (error instanceof ResponseApiError) {
return {
statusCode: error.statusCode,
code: error.code,
message: error.message,
type: error.type,
param: error.param,
};
}
if (error instanceof CommandCodeError) {
if (error.kind === "max_turns" || error.kind === "timeout") {
const reason = error.kind === "max_turns" ? "max_turns" : "timeout";
return {
response: createResponse({
id: options.id,
messageId: options.messageId,
createdAt: options.createdAt,
request: options.request,
effort: options.effort,
status: "incomplete",
incompleteReason: reason,
}),
statusCode: 200,
code: reason,
message: error.message,
type: "server_error",
param: null,
};
}
if (error.kind === "cancelled") {
return {
response: createResponse({
id: options.id,
messageId: options.messageId,
createdAt: options.createdAt,
request: options.request,
effort: options.effort,
status: "cancelled",
error: { code: "cancelled", message: "Command Code request cancelled" },
}),
statusCode: 499,
code: "cancelled",
message: "Command Code request cancelled",
type: "server_error",
param: null,
};
}
const mapped = commandErrorMapping(error.exitCode);
return {
response: createResponse({
id: options.id,
messageId: options.messageId,
createdAt: options.createdAt,
request: options.request,
effort: options.effort,
status: "failed",
error: { code: mapped.code, message: mapped.message },
}),
statusCode: mapped.statusCode,
code: mapped.code,
message: mapped.message,
type: "server_error",
param: null,
};
}
process.stderr.write(`${formatUnknownError(error)}\n`);
return {
statusCode: 500,
code: "bridge_error",
message: "Internal bridge error",
type: "server_error",
param: null,
};
}
function commandErrorMapping(exitCode?: number | null): { statusCode: number; code: string; message: string } {
switch (exitCode) {
case 3:
return { statusCode: 503, code: "command_code_not_authenticated", message: "Command Code is not authenticated" };
case 5:
return { statusCode: 429, code: "rate_limit_exceeded", message: "Command Code rate limit exceeded" };
case 10:
return { statusCode: 402, code: "insufficient_credits", message: "Command Code credits are insufficient" };
default:
return { statusCode: 502, code: "command_code_error", message: "Command Code request failed" };
}
}
function startSse(reply: FastifyReply, messageId: string): ResponsesSseWriter {
reply.hijack();
reply.raw.writeHead(200, {
"Content-Type": "text/event-stream; charset=utf-8",
"Cache-Control": "no-cache, no-transform",
Connection: "keep-alive",
"X-Accel-Buffering": "no",
});
reply.raw.flushHeaders();
return new ResponsesSseWriter(reply.raw, messageId);
}
function sendRouteError(reply: FastifyReply, error: unknown) {
if (error instanceof AttachmentInputError) {
return reply.status(400).send(openAIError(
error.message,
error.code,
"invalid_request_error",
error.param,
));
}
if (error instanceof ResponseApiError) {
return reply.status(error.statusCode).send(openAIError(error.message, error.code, error.type, error.param));
}
if (error instanceof ZodError) {
const unknown = error.issues.find((issue) => issue.code === "unrecognized_keys");
if (unknown?.code === "unrecognized_keys") {
const key = unknown.keys[0] ?? "unknown";
const param = [...unknown.path, key].join(".");
return reply.status(400).send(openAIError(
`Unsupported parameter: '${param}'`,
"unsupported_parameter",
"invalid_request_error",
param,
));
}
const message = error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
return reply.status(400).send(openAIError(message, "invalid_request"));
}
process.stderr.write(`${formatUnknownError(error)}\n`);
return reply.status(500).send(openAIError("Internal bridge error", "bridge_error", "server_error"));
}
function sendBusy(reply: FastifyReply) {
return reply.status(429).send(openAIError(
"Command Code request queue is full",
"queue_full",
"server_error",
));
}
function responseIdFromRequest(request: FastifyRequest): string {
const params = request.params as { response_id?: unknown };
const result = responseIdSchema.safeParse(params.response_id);
if (!result.success) {
throw new ResponseApiError("Invalid response_id", 400, "invalid_response_id", "invalid_request_error", "response_id");
}
return result.data;
}
function responseNotFound(id: string): ResponseApiError {
return new ResponseApiError(
`Response '${id}' was not found`,
404,
"response_not_found",
"invalid_request_error",
"response_id",
);
}
function validateResponseToolRequest(request: ResponseRequest): void {
const errors = validateFunctionToolSchemas(responseFunctionTools(request));
if (errors.length > 0) {
throw new ResponseApiError(
errors.join("; "),
400,
"invalid_tool_schema",
"invalid_request_error",
"tools",
);
}
}
function validateResponseFunctionHistory(
request: ResponseRequest,
chain: StoredResponse[],
currentInput: ResponseInputItem[],
): void {
const tools = new Map(responseFunctionTools(request).map((tool) => [tool.name, tool]));
const callIds = new Set<string>();
const outputCallIds = new Set<string>();
const items: Array<{ item: ResponseInputItem | ResponseOutputItem; param: string }> = [];
for (const stored of chain) {
for (const item of stored.input_items) items.push({ item, param: "previous_response_id" });
for (const item of stored.response.output) items.push({ item, param: "previous_response_id" });
}
for (const [index, item] of currentInput.entries()) items.push({ item, param: `input.${index}` });
for (const { item, param } of items) {
if (item.type === "function_call") {
if (callIds.has(item.call_id)) {
throw new ResponseApiError(
`Duplicate function call_id '${item.call_id}'`,
400,
"invalid_function_call",
"invalid_request_error",
`${param}.call_id`,
);
}
callIds.add(item.call_id);
const tool = tools.get(item.name);
if (!tool) {
throw new ResponseApiError(
`Function call references unknown tool '${item.name}'`,
400,
"invalid_tool_name",
"invalid_request_error",
`${param}.name`,
);
}
let args: Record<string, unknown>;
try {
args = parseFunctionToolArguments(item.arguments, `${param}.arguments`);
} catch (error) {
if (error instanceof ToolDecisionError) {
throw new ResponseApiError(
error.errors.join("; "),
400,
"invalid_tool_arguments",
"invalid_request_error",
`${param}.arguments`,
);
}
throw error;
}
const errors = validateFunctionToolArguments(tool, item.name, args);
if (errors.length > 0) {
throw new ResponseApiError(
errors.join("; "),
400,
"invalid_tool_arguments",
"invalid_request_error",
`${param}.arguments`,
);
}
continue;
}
if (item.type === "function_call_output") {
if (!callIds.has(item.call_id)) {
throw new ResponseApiError(
`No preceding function_call found for call_id '${item.call_id}'`,
400,
"invalid_function_call_output",
"invalid_request_error",
`${param}.call_id`,
);
}
if (outputCallIds.has(item.call_id)) {
throw new ResponseApiError(
`Duplicate function_call_output for call_id '${item.call_id}'`,
400,
"invalid_function_call_output",
"invalid_request_error",
`${param}.call_id`,
);
}
outputCallIds.add(item.call_id);
}
}
}
function responseFunctionTools(request: ResponseRequest): FunctionToolDefinition[] {
return normalizeResponseTools(request.tools).map((tool) => ({
name: tool.name,
...(tool.description !== undefined ? { description: tool.description } : {}),
parameters: tool.parameters,
strict: tool.strict,
}));
}
function responseFunctionToolDecisionRequest(
request: ResponseRequest,
): FunctionToolDecisionRequest | undefined {
if (!request.tools) return undefined;
const toolChoice = effectiveResponseToolChoice(request);
return {
tools: responseFunctionTools(request),
toolChoice: typeof toolChoice === "string" ? toolChoice : { name: toolChoice.name },
parallelToolCalls: request.parallel_tool_calls ?? true,
};
}
function normalizeResponseTools(tools: ResponseRequest["tools"]): ResponseFunctionTool[] {
return (tools ?? []).map((tool) => ({
type: "function",
name: tool.name,
...(tool.description !== undefined ? { description: tool.description } : {}),
parameters: tool.parameters ?? { type: "object", properties: {} },
strict: tool.strict ?? null,
}));
}
function effectiveResponseToolChoice(request: ResponseRequest): ResponseToolChoice {
return request.tool_choice ?? (request.tools ? "auto" : "none");
}
function usesResponseToolCalling(
request: ResponseRequest,
history: Array<ResponseInputItem | ResponseOutputItem>,
): boolean {
return request.tools !== undefined || history.some((item) => (
item.type === "function_call" || item.type === "function_call_output"
));
}
function toResponseUsage(usage?: CommandUsage): ResponseUsage | null {
if (!usage || (usage.inputTokens === undefined && usage.outputTokens === undefined)) return null;
const inputTokens = usage.inputTokens ?? 0;
const outputTokens = usage.outputTokens ?? 0;
return {
input_tokens: inputTokens,
output_tokens: outputTokens,
total_tokens: inputTokens + outputTokens,
};
}
export function addUsage(left?: CommandUsage, right?: CommandUsage): CommandUsage | undefined {
if (!left && !right) return undefined;
return {
inputTokens: (left?.inputTokens ?? 0) + (right?.inputTokens ?? 0),
outputTokens: (left?.outputTokens ?? 0) + (right?.outputTokens ?? 0),
};
}
function formatAjvErrors(errors: ErrorObject[] | null | undefined): string[] {
if (!errors || errors.length === 0) return ["JSON Schema validation failed"];
return errors.map((error) => `${error.instancePath || "/"}: ${error.message ?? error.keyword}`);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function newResponseId(): string {
return `resp_local_${randomHexId()}`;
}
function newMessageId(): string {
return `msg_local_${randomHexId()}`;
}
function newFunctionCallItemId(): string {
return `fc_local_${randomHexId()}`;
}
function newFunctionCallOutputItemId(): string {
return `fco_local_${randomHexId()}`;
}
function newReasoningItemId(): string {
return `rs_local_${randomHexId()}`;
}
function randomHexId(): string {
return randomUUID().replaceAll("-", "");
}
function formatUnknownError(error: unknown): string {
return error instanceof Error ? (error.stack ?? error.message) : String(error);
}