import OpenAI from "openai"; import type { AgentMessage, ModelClient, ModelTurn, ToolCall, ToolDefinition } from "../types.js"; import type { EventBus } from "../events.js"; export class OpenAIResponsesClient implements ModelClient { private readonly client = new OpenAI(); constructor( private readonly model = "gpt-5", private readonly instructions?: string, private readonly events?: EventBus ) {} async respond(input: AgentMessage[], tools: ToolDefinition[]): Promise { await this.events?.emit("model.request", { model: this.model, messages: input.length, tools: tools.map((tool) => tool.name) }); const response = await this.client.responses.create({ model: this.model, instructions: this.instructions, input, tools: tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.parameters, strict: true })) }); const toolCalls: ToolCall[] = response.output.flatMap((item) => item.type === "function_call" ? [ { id: item.call_id, name: item.name, arguments: item.arguments } ] : [] ); const outputText = response.output_text ?? ""; await this.events?.emit("model.response", { outputItems: response.output.length, toolCalls: toolCalls.map((toolCall) => toolCall.name), hasText: outputText.length > 0 }); return { output: sanitizeOutputItems(response.output), outputText, toolCalls }; } async respondStream( input: AgentMessage[], tools: ToolDefinition[], onTextDelta: (delta: string) => void ): Promise { await this.events?.emit("model.request", { model: this.model, messages: input.length, tools: tools.map((tool) => tool.name) }); const stream = this.client.responses.stream({ model: this.model, instructions: this.instructions, input, tools: tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.parameters, strict: true })) }); for await (const event of stream) { if (event.type === "response.output_text.delta") { onTextDelta(event.delta); } } const response = await stream.finalResponse(); const toolCalls: ToolCall[] = response.output.flatMap((item) => item.type === "function_call" ? [ { id: item.call_id, name: item.name, arguments: item.arguments } ] : [] ); const outputText = response.output_text ?? ""; await this.events?.emit("model.response", { outputItems: response.output.length, toolCalls: toolCalls.map((toolCall) => toolCall.name), hasText: outputText.length > 0 }); return { output: sanitizeOutputItems(response.output), outputText, toolCalls }; } } function sanitizeOutputItems(items: unknown[]): unknown[] { return JSON.parse( JSON.stringify(items, (key, value) => { if (key === "parsed_arguments") { return undefined; } return value; }) ) as unknown[]; }