UNPKG

@ai-sdk/open-responses

Version:

The **[Open Responses provider](https://ai-sdk.dev/providers/ai-sdk-providers/open-responses)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for [Open Responses](https://www.openresponses.org/) compatible APIs.

652 lines (644 loc) 22.6 kB
// src/version.ts var VERSION = true ? "1.0.16" : "0.0.0-test"; // src/open-responses-provider.ts import { NoSuchModelError } from "@ai-sdk/provider"; import { generateId, withUserAgentSuffix } from "@ai-sdk/provider-utils"; // src/responses/open-responses-language-model.ts import { combineHeaders, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonResponseHandler, jsonSchema, parseProviderOptions, postJsonToApi } from "@ai-sdk/provider-utils"; import { z as z3 } from "zod/v4"; // src/responses/convert-to-open-responses-input.ts import { convertToBase64 } from "@ai-sdk/provider-utils"; async function convertToOpenResponsesInput({ prompt }) { var _a, _b, _c; const input = []; const warnings = []; const systemMessages = []; for (const { role, content } of prompt) { switch (role) { case "system": { systemMessages.push(content); break; } case "user": { const userContent = []; for (const part of content) { switch (part.type) { case "text": { userContent.push({ type: "input_text", text: part.text }); break; } case "file": { const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType; if (part.mediaType.startsWith("image/")) { userContent.push({ type: "input_image", ...part.data instanceof URL ? { image_url: part.data.toString() } : { image_url: `data:${mediaType};base64,${convertToBase64(part.data)}` } }); } else if (part.data instanceof URL) { userContent.push({ type: "input_file", file_url: part.data.toString() }); } else { userContent.push({ type: "input_file", filename: (_a = part.filename) != null ? _a : "data", file_data: `data:${mediaType};base64,${convertToBase64(part.data)}` }); } break; } } } input.push({ type: "message", role: "user", content: userContent }); break; } case "assistant": { const assistantContent = []; const toolCalls = []; for (const part of content) { switch (part.type) { case "text": { assistantContent.push({ type: "output_text", text: part.text }); break; } case "tool-call": { const argumentsValue = typeof part.input === "string" ? part.input : JSON.stringify(part.input); toolCalls.push({ type: "function_call", call_id: part.toolCallId, name: part.toolName, arguments: argumentsValue }); break; } } } if (assistantContent.length > 0) { input.push({ type: "message", role: "assistant", content: assistantContent }); } for (const toolCall of toolCalls) { input.push(toolCall); } break; } case "tool": { for (const part of content) { if (part.type === "tool-result") { const output = part.output; let contentValue; switch (output.type) { case "text": case "error-text": contentValue = output.value; break; case "execution-denied": contentValue = (_b = output.reason) != null ? _b : "Tool execution denied."; break; case "json": case "error-json": contentValue = JSON.stringify(output.value); break; case "content": { const contentParts = []; for (const item of output.value) { switch (item.type) { case "text": { contentParts.push({ type: "input_text", text: item.text }); break; } case "image-data": { contentParts.push({ type: "input_image", image_url: `data:${item.mediaType};base64,${item.data}` }); break; } case "image-url": { contentParts.push({ type: "input_image", image_url: item.url }); break; } case "file-data": { contentParts.push({ type: "input_file", filename: (_c = item.filename) != null ? _c : "data", file_data: `data:${item.mediaType};base64,${item.data}` }); break; } default: { warnings.push({ type: "other", message: `unsupported tool content part type: ${item.type}` }); break; } } } contentValue = contentParts; break; } } input.push({ type: "function_call_output", call_id: part.toolCallId, output: contentValue }); } } break; } } } return { input, instructions: systemMessages.length > 0 ? systemMessages.join("\n") : void 0, warnings }; } // src/responses/open-responses-api.ts import { lazySchema, zodSchema } from "@ai-sdk/provider-utils"; import { z } from "zod/v4"; var openResponsesErrorSchema = lazySchema( () => zodSchema( z.object({ error: z.object({ message: z.string(), type: z.string(), param: z.string(), code: z.string() }) }) ) ); // src/responses/map-open-responses-finish-reason.ts function mapOpenResponsesFinishReason({ finishReason, hasToolCalls }) { switch (finishReason) { case void 0: case null: return hasToolCalls ? "tool-calls" : "stop"; case "max_output_tokens": return "length"; case "content_filter": return "content-filter"; default: return hasToolCalls ? "tool-calls" : "other"; } } // src/responses/open-responses-options.ts import { lazySchema as lazySchema2, zodSchema as zodSchema2 } from "@ai-sdk/provider-utils"; import { z as z2 } from "zod/v4"; var openResponsesOptionsSchema = lazySchema2( () => zodSchema2( z2.object({ reasoningEffort: z2.enum(["none", "low", "medium", "high", "xhigh"]).nullish(), /** * Controls reasoning summary output from the model. * Valid values: 'concise', 'detailed', 'auto'. */ reasoningSummary: z2.enum(["concise", "detailed", "auto"]).nullish() }) ) ); // src/responses/open-responses-language-model.ts var OpenResponsesLanguageModel = class { constructor(modelId, config) { this.specificationVersion = "v3"; this.supportedUrls = { "image/*": [/^https?:\/\/.*$/] }; this.modelId = modelId; this.config = config; } get provider() { return this.config.provider; } async getArgs({ maxOutputTokens, temperature, stopSequences, topP, topK, presencePenalty, frequencyPenalty, seed, prompt, providerOptions, tools, toolChoice, responseFormat }) { var _a; const warnings = []; if (stopSequences != null) { warnings.push({ type: "unsupported", feature: "stopSequences" }); } if (topK != null) { warnings.push({ type: "unsupported", feature: "topK" }); } if (seed != null) { warnings.push({ type: "unsupported", feature: "seed" }); } const { input, instructions, warnings: inputWarnings } = await convertToOpenResponsesInput({ prompt }); warnings.push(...inputWarnings); const functionTools = tools == null ? void 0 : tools.filter((tool) => tool.type === "function").map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.inputSchema, ...tool.strict != null ? { strict: tool.strict } : {} })); const convertedToolChoice = toolChoice == null ? void 0 : toolChoice.type === "tool" ? { type: "function", name: toolChoice.toolName } : toolChoice.type; const textFormat = (responseFormat == null ? void 0 : responseFormat.type) === "json" ? { type: "json_schema", ...responseFormat.schema != null ? { name: (_a = responseFormat.name) != null ? _a : "response", description: responseFormat.description, schema: responseFormat.schema, strict: true } : {} } : void 0; const openResponsesOptions = await parseProviderOptions({ provider: this.config.providerOptionsName, providerOptions, schema: openResponsesOptionsSchema }); return { body: { model: this.modelId, input, instructions, max_output_tokens: maxOutputTokens, temperature, top_p: topP, presence_penalty: presencePenalty, frequency_penalty: frequencyPenalty, reasoning: (openResponsesOptions == null ? void 0 : openResponsesOptions.reasoningEffort) != null || (openResponsesOptions == null ? void 0 : openResponsesOptions.reasoningSummary) != null ? { ...(openResponsesOptions == null ? void 0 : openResponsesOptions.reasoningEffort) != null && { effort: openResponsesOptions.reasoningEffort }, ...(openResponsesOptions == null ? void 0 : openResponsesOptions.reasoningSummary) != null && { summary: openResponsesOptions.reasoningSummary } } : void 0, tools: (functionTools == null ? void 0 : functionTools.length) ? functionTools : void 0, tool_choice: convertedToolChoice, ...textFormat != null && { text: { format: textFormat } } }, warnings }; } async doGenerate(options) { var _a, _b, _c, _d, _e, _f; const { body, warnings } = await this.getArgs(options); const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url, headers: combineHeaders(this.config.headers(), options.headers), body, failedResponseHandler: createJsonErrorResponseHandler({ errorSchema: openResponsesErrorSchema, errorToMessage: (error) => error.error.message }), successfulResponseHandler: createJsonResponseHandler( // do not validate the response body, only apply types to the response body jsonSchema(() => { throw new Error("json schema not implemented"); }) ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const content = []; let hasToolCalls = false; for (const part of response.output) { switch (part.type) { // TODO AI SDK 7 adjust reasoning in the specification to better support the reasoning structure from open responses. case "reasoning": { for (const contentPart of (_a = part.content) != null ? _a : []) { content.push({ type: "reasoning", text: contentPart.text }); } break; } case "message": { for (const contentPart of part.content) { content.push({ type: "text", text: contentPart.text }); } break; } case "function_call": { hasToolCalls = true; content.push({ type: "tool-call", toolCallId: part.call_id, toolName: part.name, input: part.arguments }); break; } } } const usage = response.usage; const inputTokens = usage == null ? void 0 : usage.input_tokens; const cachedInputTokens = (_b = usage == null ? void 0 : usage.input_tokens_details) == null ? void 0 : _b.cached_tokens; const outputTokens = usage == null ? void 0 : usage.output_tokens; const reasoningTokens = (_c = usage == null ? void 0 : usage.output_tokens_details) == null ? void 0 : _c.reasoning_tokens; return { content, finishReason: { unified: mapOpenResponsesFinishReason({ finishReason: (_d = response.incomplete_details) == null ? void 0 : _d.reason, hasToolCalls }), raw: (_f = (_e = response.incomplete_details) == null ? void 0 : _e.reason) != null ? _f : void 0 }, usage: { inputTokens: { total: inputTokens, noCache: (inputTokens != null ? inputTokens : 0) - (cachedInputTokens != null ? cachedInputTokens : 0), cacheRead: cachedInputTokens, cacheWrite: void 0 }, outputTokens: { total: outputTokens, text: (outputTokens != null ? outputTokens : 0) - (reasoningTokens != null ? reasoningTokens : 0), reasoning: reasoningTokens }, raw: response.usage }, request: { body }, response: { id: response.id, timestamp: new Date(response.created_at * 1e3), modelId: response.model, headers: responseHeaders, body: rawResponse }, providerMetadata: void 0, warnings }; } async doStream(options) { const { body, warnings } = await this.getArgs(options); const { responseHeaders, value: response } = await postJsonToApi({ url: this.config.url, headers: combineHeaders(this.config.headers(), options.headers), body: { ...body, stream: true }, failedResponseHandler: createJsonErrorResponseHandler({ errorSchema: openResponsesErrorSchema, errorToMessage: (error) => error.error.message }), // TODO consider validation successfulResponseHandler: createEventSourceResponseHandler(z3.any()), abortSignal: options.abortSignal, fetch: this.config.fetch }); const usage = { inputTokens: { total: void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: void 0, text: void 0, reasoning: void 0 } }; const updateUsage = (responseUsage) => { var _a, _b; if (!responseUsage) { return; } const inputTokens = responseUsage.input_tokens; const cachedInputTokens = (_a = responseUsage.input_tokens_details) == null ? void 0 : _a.cached_tokens; const outputTokens = responseUsage.output_tokens; const reasoningTokens = (_b = responseUsage.output_tokens_details) == null ? void 0 : _b.reasoning_tokens; usage.inputTokens = { total: inputTokens, noCache: (inputTokens != null ? inputTokens : 0) - (cachedInputTokens != null ? cachedInputTokens : 0), cacheRead: cachedInputTokens, cacheWrite: void 0 }; usage.outputTokens = { total: outputTokens, text: (outputTokens != null ? outputTokens : 0) - (reasoningTokens != null ? reasoningTokens : 0), reasoning: reasoningTokens }; usage.raw = responseUsage; }; let isActiveReasoning = false; let hasToolCalls = false; let finishReason = { unified: "other", raw: void 0 }; const toolCallsByItemId = {}; return { stream: response.pipeThrough( new TransformStream({ start(controller) { controller.enqueue({ type: "stream-start", warnings }); }, transform(parseResult, controller) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; if (options.includeRawChunks) { controller.enqueue({ type: "raw", rawValue: parseResult.rawValue }); } if (!parseResult.success) { controller.enqueue({ type: "error", error: parseResult.error }); return; } const chunk = parseResult.value; if (chunk.type === "response.output_item.added" && chunk.item.type === "function_call") { toolCallsByItemId[chunk.item.id] = { toolName: chunk.item.name, toolCallId: chunk.item.call_id, arguments: chunk.item.arguments }; } else if (chunk.type === "response.function_call_arguments.delta") { const functionCallChunk = chunk; const toolCall = (_a = toolCallsByItemId[functionCallChunk.item_id]) != null ? _a : toolCallsByItemId[functionCallChunk.item_id] = {}; toolCall.arguments = ((_b = toolCall.arguments) != null ? _b : "") + functionCallChunk.delta; } else if (chunk.type === "response.function_call_arguments.done") { const functionCallChunk = chunk; const toolCall = (_c = toolCallsByItemId[functionCallChunk.item_id]) != null ? _c : toolCallsByItemId[functionCallChunk.item_id] = {}; toolCall.arguments = functionCallChunk.arguments; } else if (chunk.type === "response.output_item.done" && chunk.item.type === "function_call") { const toolCall = toolCallsByItemId[chunk.item.id]; const toolName = (_d = toolCall == null ? void 0 : toolCall.toolName) != null ? _d : chunk.item.name; const toolCallId = (_e = toolCall == null ? void 0 : toolCall.toolCallId) != null ? _e : chunk.item.call_id; const input = (_g = (_f = toolCall == null ? void 0 : toolCall.arguments) != null ? _f : chunk.item.arguments) != null ? _g : ""; controller.enqueue({ type: "tool-call", toolCallId, toolName, input }); hasToolCalls = true; delete toolCallsByItemId[chunk.item.id]; } else if (chunk.type === "response.output_item.added" && chunk.item.type === "reasoning") { controller.enqueue({ type: "reasoning-start", id: chunk.item.id }); isActiveReasoning = true; } else if (chunk.type === "response.reasoning_text.delta") { const reasoningChunk = chunk; controller.enqueue({ type: "reasoning-delta", id: reasoningChunk.item_id, delta: reasoningChunk.delta }); } else if (chunk.type === "response.output_item.done" && chunk.item.type === "reasoning") { controller.enqueue({ type: "reasoning-end", id: chunk.item.id }); isActiveReasoning = false; } else if (chunk.type === "response.output_item.added" && chunk.item.type === "message") { controller.enqueue({ type: "text-start", id: chunk.item.id }); } else if (chunk.type === "response.output_text.delta") { controller.enqueue({ type: "text-delta", id: chunk.item_id, delta: chunk.delta }); } else if (chunk.type === "response.output_item.done" && chunk.item.type === "message") { controller.enqueue({ type: "text-end", id: chunk.item.id }); } else if (chunk.type === "response.completed" || chunk.type === "response.incomplete") { const reason = (_h = chunk.response.incomplete_details) == null ? void 0 : _h.reason; finishReason = { unified: mapOpenResponsesFinishReason({ finishReason: reason, hasToolCalls }), raw: reason != null ? reason : void 0 }; updateUsage(chunk.response.usage); } else if (chunk.type === "response.failed") { finishReason = { unified: "error", raw: (_j = (_i = chunk.response.error) == null ? void 0 : _i.code) != null ? _j : chunk.response.status }; updateUsage(chunk.response.usage); } }, flush(controller) { if (isActiveReasoning) { controller.enqueue({ type: "reasoning-end", id: "reasoning-0" }); } controller.enqueue({ type: "finish", finishReason, usage, providerMetadata: void 0 }); } }) ), request: { body }, response: { headers: responseHeaders } }; } }; // src/open-responses-provider.ts function createOpenResponses(options) { const providerName = options.name; const getHeaders = () => withUserAgentSuffix( { ...options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}, ...options.headers }, `ai-sdk/open-responses/${VERSION}` ); const createResponsesModel = (modelId) => { return new OpenResponsesLanguageModel(modelId, { provider: `${providerName}.responses`, providerOptionsName: providerName, headers: getHeaders, url: options.url, fetch: options.fetch, generateId: () => generateId() }); }; const createLanguageModel = (modelId) => { if (new.target) { throw new Error( "The OpenAI model function cannot be called with the new keyword." ); } return createResponsesModel(modelId); }; const provider = function(modelId) { return createLanguageModel(modelId); }; provider.specificationVersion = "v3"; provider.languageModel = createLanguageModel; provider.embeddingModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "embeddingModel" }); }; provider.imageModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "imageModel" }); }; return provider; } export { VERSION, createOpenResponses }; //# sourceMappingURL=index.mjs.map