UNPKG

@openrouter/ai-sdk-provider

Version:

The [OpenRouter](https://openrouter.ai/) provider for the [Vercel AI SDK](https://sdk.vercel.ai/docs) gives access to over 300 large language model on the OpenRouter chat and completion APIs.

1,371 lines (1,353 loc) 50.9 kB
"use strict"; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { OpenRouter: () => OpenRouter, createOpenRouter: () => createOpenRouter, openrouter: () => openrouter }); module.exports = __toCommonJS(index_exports); // src/openrouter-facade.ts var import_provider_utils5 = require("@ai-sdk/provider-utils"); // src/schemas/reasoning-details.ts var import_zod = require("zod"); var ReasoningDetailSummarySchema = import_zod.z.object({ type: import_zod.z.literal("reasoning.summary" /* Summary */), summary: import_zod.z.string() }); var ReasoningDetailEncryptedSchema = import_zod.z.object({ type: import_zod.z.literal("reasoning.encrypted" /* Encrypted */), data: import_zod.z.string() }); var ReasoningDetailTextSchema = import_zod.z.object({ type: import_zod.z.literal("reasoning.text" /* Text */), text: import_zod.z.string().nullish(), signature: import_zod.z.string().nullish() }); var ReasoningDetailUnionSchema = import_zod.z.union([ ReasoningDetailSummarySchema, ReasoningDetailEncryptedSchema, ReasoningDetailTextSchema ]); var ReasoningDetailsWithUnknownSchema = import_zod.z.union([ ReasoningDetailUnionSchema, import_zod.z.unknown().transform(() => null) ]); var ReasoningDetailArraySchema = import_zod.z.array(ReasoningDetailsWithUnknownSchema).transform((d) => d.filter((d2) => !!d2)); // src/openrouter-chat-language-model.ts var import_provider = require("@ai-sdk/provider"); var import_provider_utils3 = require("@ai-sdk/provider-utils"); var import_zod3 = require("zod"); // src/convert-to-openrouter-chat-messages.ts var import_provider_utils = require("@ai-sdk/provider-utils"); function getCacheControl(providerMetadata) { var _a, _b, _c; const anthropic = providerMetadata == null ? void 0 : providerMetadata.anthropic; const openrouter2 = providerMetadata == null ? void 0 : providerMetadata.openrouter; return (_c = (_b = (_a = openrouter2 == null ? void 0 : openrouter2.cacheControl) != null ? _a : openrouter2 == null ? void 0 : openrouter2.cache_control) != null ? _b : anthropic == null ? void 0 : anthropic.cacheControl) != null ? _c : anthropic == null ? void 0 : anthropic.cache_control; } function convertToOpenRouterChatMessages(prompt) { var _a, _b, _c; const messages = []; for (const { role, content, providerMetadata } of prompt) { switch (role) { case "system": { messages.push({ role: "system", content, cache_control: getCacheControl(providerMetadata) }); break; } case "user": { if (content.length === 1 && ((_a = content[0]) == null ? void 0 : _a.type) === "text") { messages.push({ role: "user", content: content[0].text, cache_control: (_b = getCacheControl(providerMetadata)) != null ? _b : getCacheControl(content[0].providerMetadata) }); break; } const messageCacheControl = getCacheControl(providerMetadata); const contentParts = content.map( (part) => { var _a2, _b2, _c2, _d; const cacheControl = (_a2 = getCacheControl(part.providerMetadata)) != null ? _a2 : messageCacheControl; switch (part.type) { case "text": return { type: "text", text: part.text, // For text parts, only use part-specific cache control cache_control: cacheControl }; case "image": return { type: "image_url", image_url: { url: part.image instanceof URL ? part.image.toString() : `data:${(_b2 = part.mimeType) != null ? _b2 : "image/jpeg"};base64,${(0, import_provider_utils.convertUint8ArrayToBase64)( part.image )}` }, // For image parts, use part-specific or message-level cache control cache_control: cacheControl }; case "file": return { type: "file", file: { filename: String( (_d = (_c2 = part.providerMetadata) == null ? void 0 : _c2.openrouter) == null ? void 0 : _d.filename ), file_data: part.data instanceof Uint8Array ? `data:${part.mimeType};base64,${(0, import_provider_utils.convertUint8ArrayToBase64)(part.data)}` : `data:${part.mimeType};base64,${part.data}` }, cache_control: cacheControl }; default: { const _exhaustiveCheck = part; throw new Error( `Unsupported content part type: ${_exhaustiveCheck}` ); } } } ); messages.push({ role: "user", content: contentParts }); break; } case "assistant": { let text = ""; let reasoning = ""; const reasoningDetails = []; const toolCalls = []; for (const part of content) { switch (part.type) { case "text": { text += part.text; break; } case "tool-call": { toolCalls.push({ id: part.toolCallId, type: "function", function: { name: part.toolName, arguments: JSON.stringify(part.args) } }); break; } case "reasoning": { reasoning += part.text; reasoningDetails.push({ type: "reasoning.text" /* Text */, text: part.text, signature: part.signature }); break; } case "redacted-reasoning": { reasoningDetails.push({ type: "reasoning.encrypted" /* Encrypted */, data: part.data }); break; } case "file": break; default: { const _exhaustiveCheck = part; throw new Error(`Unsupported part: ${_exhaustiveCheck}`); } } } messages.push({ role: "assistant", content: text, tool_calls: toolCalls.length > 0 ? toolCalls : void 0, reasoning: reasoning || void 0, reasoning_details: reasoningDetails.length > 0 ? reasoningDetails : void 0, cache_control: getCacheControl(providerMetadata) }); break; } case "tool": { for (const toolResponse of content) { messages.push({ role: "tool", tool_call_id: toolResponse.toolCallId, content: JSON.stringify(toolResponse.result), cache_control: (_c = getCacheControl(providerMetadata)) != null ? _c : getCacheControl(toolResponse.providerMetadata) }); } break; } default: { const _exhaustiveCheck = role; throw new Error(`Unsupported role: ${_exhaustiveCheck}`); } } } return messages; } // src/map-openrouter-chat-logprobs.ts function mapOpenRouterChatLogProbsOutput(logprobs) { var _a, _b; return (_b = (_a = logprobs == null ? void 0 : logprobs.content) == null ? void 0 : _a.map(({ token, logprob, top_logprobs }) => ({ token, logprob, topLogprobs: top_logprobs ? top_logprobs.map(({ token: token2, logprob: logprob2 }) => ({ token: token2, logprob: logprob2 })) : [] }))) != null ? _b : void 0; } // src/map-openrouter-finish-reason.ts function mapOpenRouterFinishReason(finishReason) { switch (finishReason) { case "stop": return "stop"; case "length": return "length"; case "content_filter": return "content-filter"; case "function_call": case "tool_calls": return "tool-calls"; default: return "unknown"; } } // src/openrouter-error.ts var import_provider_utils2 = require("@ai-sdk/provider-utils"); var import_zod2 = require("zod"); var OpenRouterErrorResponseSchema = import_zod2.z.object({ error: import_zod2.z.object({ code: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).nullable(), message: import_zod2.z.string(), type: import_zod2.z.string().nullable(), param: import_zod2.z.any().nullable() }) }); var openrouterFailedResponseHandler = (0, import_provider_utils2.createJsonErrorResponseHandler)({ errorSchema: OpenRouterErrorResponseSchema, errorToMessage: (data) => data.error.message }); // src/openrouter-chat-language-model.ts function isFunctionTool(tool) { return "parameters" in tool; } var OpenRouterChatLanguageModel = class { constructor(modelId, settings, config) { this.specificationVersion = "v1"; this.defaultObjectGenerationMode = "tool"; this.modelId = modelId; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } getArgs({ mode, prompt, maxTokens, temperature, topP, frequencyPenalty, presencePenalty, seed, stopSequences, responseFormat, topK, providerMetadata }) { var _a; const type = mode.type; const extraCallingBody = (_a = providerMetadata == null ? void 0 : providerMetadata.openrouter) != null ? _a : {}; const baseArgs = __spreadValues(__spreadValues(__spreadValues({ // model id: model: this.modelId, models: this.settings.models, // model specific settings: logit_bias: this.settings.logitBias, logprobs: this.settings.logprobs === true || typeof this.settings.logprobs === "number" ? true : void 0, top_logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0, user: this.settings.user, parallel_tool_calls: this.settings.parallelToolCalls, // standardized settings: max_tokens: maxTokens, temperature, top_p: topP, frequency_penalty: frequencyPenalty, presence_penalty: presencePenalty, seed, stop: stopSequences, response_format: responseFormat, top_k: topK, // messages: messages: convertToOpenRouterChatMessages(prompt), // OpenRouter specific settings: include_reasoning: this.settings.includeReasoning, reasoning: this.settings.reasoning, usage: this.settings.usage }, this.config.extraBody), this.settings.extraBody), extraCallingBody); switch (type) { case "regular": { return __spreadValues(__spreadValues({}, baseArgs), prepareToolsAndToolChoice(mode)); } case "object-json": { return __spreadProps(__spreadValues({}, baseArgs), { response_format: { type: "json_object" } }); } case "object-tool": { return __spreadProps(__spreadValues({}, baseArgs), { tool_choice: { type: "function", function: { name: mode.tool.name } }, tools: [ { type: "function", function: { name: mode.tool.name, description: mode.tool.description, parameters: mode.tool.parameters } } ] }); } // Handle all non-text types with a single default case default: { const _exhaustiveCheck = type; throw new import_provider.UnsupportedFunctionalityError({ functionality: `${_exhaustiveCheck} mode` }); } } } async doGenerate(options) { var _b, _c, _d, _e, _f, _g, _h, _i, _j; const args = this.getArgs(options); const { responseHeaders, value: response } = await (0, import_provider_utils3.postJsonToApi)({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: (0, import_provider_utils3.combineHeaders)(this.config.headers(), options.headers), body: args, failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils3.createJsonResponseHandler)( OpenRouterNonStreamChatCompletionResponseSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const _a = args, { messages: rawPrompt } = _a, rawSettings = __objRest(_a, ["messages"]); const choice = response.choices[0]; if (!choice) { throw new Error("No choice in response"); } const usageInfo = response.usage ? { promptTokens: (_b = response.usage.prompt_tokens) != null ? _b : 0, completionTokens: (_c = response.usage.completion_tokens) != null ? _c : 0 } : { promptTokens: 0, completionTokens: 0 }; const providerMetadata = {}; if (response.usage && ((_d = this.settings.usage) == null ? void 0 : _d.include)) { providerMetadata.openrouter = { usage: { promptTokens: response.usage.prompt_tokens, promptTokensDetails: response.usage.prompt_tokens_details ? { cachedTokens: (_e = response.usage.prompt_tokens_details.cached_tokens) != null ? _e : 0 } : void 0, completionTokens: response.usage.completion_tokens, completionTokensDetails: response.usage.completion_tokens_details ? { reasoningTokens: (_f = response.usage.completion_tokens_details.reasoning_tokens) != null ? _f : 0 } : void 0, cost: response.usage.cost, totalTokens: (_g = response.usage.total_tokens) != null ? _g : 0 } }; } const hasProviderMetadata = Object.keys(providerMetadata).length > 0; const reasoningDetails = (_h = choice.message.reasoning_details) != null ? _h : []; const reasoning = reasoningDetails.length > 0 ? reasoningDetails.map((detail) => { var _a2; switch (detail.type) { case "reasoning.text" /* Text */: { if (detail.text) { return { type: "text", text: detail.text, signature: (_a2 = detail.signature) != null ? _a2 : void 0 }; } break; } case "reasoning.summary" /* Summary */: { if (detail.summary) { return { type: "text", text: detail.summary }; } break; } case "reasoning.encrypted" /* Encrypted */: { if (detail.data) { return { type: "redacted", data: detail.data }; } break; } default: { detail; } } return null; }).filter((p) => p !== null) : choice.message.reasoning ? [ { type: "text", text: choice.message.reasoning } ] : []; return __spreadValues({ response: { id: response.id, modelId: response.model }, text: (_i = choice.message.content) != null ? _i : void 0, reasoning, toolCalls: (_j = choice.message.tool_calls) == null ? void 0 : _j.map((toolCall) => { var _a2; return { toolCallType: "function", toolCallId: (_a2 = toolCall.id) != null ? _a2 : (0, import_provider_utils3.generateId)(), toolName: toolCall.function.name, args: toolCall.function.arguments }; }), finishReason: mapOpenRouterFinishReason(choice.finish_reason), usage: usageInfo, rawCall: { rawPrompt, rawSettings }, rawResponse: { headers: responseHeaders }, warnings: [], logprobs: mapOpenRouterChatLogProbsOutput(choice.logprobs) }, hasProviderMetadata ? { providerMetadata } : {}); } async doStream(options) { var _a, _c; const args = this.getArgs(options); const { responseHeaders, value: response } = await (0, import_provider_utils3.postJsonToApi)({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: (0, import_provider_utils3.combineHeaders)(this.config.headers(), options.headers), body: __spreadProps(__spreadValues({}, args), { stream: true, // only include stream_options when in strict compatibility mode: stream_options: this.config.compatibility === "strict" ? __spreadValues({ include_usage: true }, ((_a = this.settings.usage) == null ? void 0 : _a.include) ? { include_usage: true } : {}) : void 0 }), failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils3.createEventSourceResponseHandler)( OpenRouterStreamChatCompletionChunkSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const _b = args, { messages: rawPrompt } = _b, rawSettings = __objRest(_b, ["messages"]); const toolCalls = []; let finishReason = "other"; let usage = { promptTokens: Number.NaN, completionTokens: Number.NaN }; let logprobs; const openrouterUsage = {}; const shouldIncludeUsageAccounting = !!((_c = this.settings.usage) == null ? void 0 : _c.include); return { stream: response.pipeThrough( new TransformStream({ transform(chunk, controller) { var _a2, _b2, _c2, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n; if (!chunk.success) { finishReason = "error"; controller.enqueue({ type: "error", error: chunk.error }); return; } const value = chunk.value; if ("error" in value) { finishReason = "error"; controller.enqueue({ type: "error", error: value.error }); return; } if (value.id) { controller.enqueue({ type: "response-metadata", id: value.id }); } if (value.model) { controller.enqueue({ type: "response-metadata", modelId: value.model }); } if (value.usage != null) { usage = { promptTokens: value.usage.prompt_tokens, completionTokens: value.usage.completion_tokens }; openrouterUsage.promptTokens = value.usage.prompt_tokens; if (value.usage.prompt_tokens_details) { openrouterUsage.promptTokensDetails = { cachedTokens: (_a2 = value.usage.prompt_tokens_details.cached_tokens) != null ? _a2 : 0 }; } openrouterUsage.completionTokens = value.usage.completion_tokens; if (value.usage.completion_tokens_details) { openrouterUsage.completionTokensDetails = { reasoningTokens: (_b2 = value.usage.completion_tokens_details.reasoning_tokens) != null ? _b2 : 0 }; } openrouterUsage.cost = value.usage.cost; openrouterUsage.totalTokens = value.usage.total_tokens; } const choice = value.choices[0]; if ((choice == null ? void 0 : choice.finish_reason) != null) { finishReason = mapOpenRouterFinishReason(choice.finish_reason); } if ((choice == null ? void 0 : choice.delta) == null) { return; } const delta = choice.delta; if (delta.content != null) { controller.enqueue({ type: "text-delta", textDelta: delta.content }); } if (delta.reasoning != null) { controller.enqueue({ type: "reasoning", textDelta: delta.reasoning }); } if (delta.reasoning_details && delta.reasoning_details.length > 0) { for (const detail of delta.reasoning_details) { switch (detail.type) { case "reasoning.text" /* Text */: { if (detail.text) { controller.enqueue({ type: "reasoning", textDelta: detail.text }); } if (detail.signature) { controller.enqueue({ type: "reasoning-signature", signature: detail.signature }); } break; } case "reasoning.encrypted" /* Encrypted */: { if (detail.data) { controller.enqueue({ type: "redacted-reasoning", data: detail.data }); } break; } case "reasoning.summary" /* Summary */: { if (detail.summary) { controller.enqueue({ type: "reasoning", textDelta: detail.summary }); } break; } default: { detail; break; } } } } const mappedLogprobs = mapOpenRouterChatLogProbsOutput( choice == null ? void 0 : choice.logprobs ); if (mappedLogprobs == null ? void 0 : mappedLogprobs.length) { if (logprobs === void 0) { logprobs = []; } logprobs.push(...mappedLogprobs); } if (delta.tool_calls != null) { for (const toolCallDelta of delta.tool_calls) { const index = toolCallDelta.index; if (toolCalls[index] == null) { if (toolCallDelta.type !== "function") { throw new import_provider.InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function' type.` }); } if (toolCallDelta.id == null) { throw new import_provider.InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'id' to be a string.` }); } if (((_c2 = toolCallDelta.function) == null ? void 0 : _c2.name) == null) { throw new import_provider.InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function.name' to be a string.` }); } toolCalls[index] = { id: toolCallDelta.id, type: "function", function: { name: toolCallDelta.function.name, arguments: (_d = toolCallDelta.function.arguments) != null ? _d : "" }, sent: false }; const toolCall2 = toolCalls[index]; if (toolCall2 == null) { throw new Error("Tool call is missing"); } if (((_e = toolCall2.function) == null ? void 0 : _e.name) != null && ((_f = toolCall2.function) == null ? void 0 : _f.arguments) != null && (0, import_provider_utils3.isParsableJson)(toolCall2.function.arguments)) { controller.enqueue({ type: "tool-call-delta", toolCallType: "function", toolCallId: toolCall2.id, toolName: toolCall2.function.name, argsTextDelta: toolCall2.function.arguments }); controller.enqueue({ type: "tool-call", toolCallType: "function", toolCallId: (_g = toolCall2.id) != null ? _g : (0, import_provider_utils3.generateId)(), toolName: toolCall2.function.name, args: toolCall2.function.arguments }); toolCall2.sent = true; } continue; } const toolCall = toolCalls[index]; if (toolCall == null) { throw new Error("Tool call is missing"); } if (((_h = toolCallDelta.function) == null ? void 0 : _h.arguments) != null) { toolCall.function.arguments += (_j = (_i = toolCallDelta.function) == null ? void 0 : _i.arguments) != null ? _j : ""; } controller.enqueue({ type: "tool-call-delta", toolCallType: "function", toolCallId: toolCall.id, toolName: toolCall.function.name, argsTextDelta: (_k = toolCallDelta.function.arguments) != null ? _k : "" }); if (((_l = toolCall.function) == null ? void 0 : _l.name) != null && ((_m = toolCall.function) == null ? void 0 : _m.arguments) != null && (0, import_provider_utils3.isParsableJson)(toolCall.function.arguments)) { controller.enqueue({ type: "tool-call", toolCallType: "function", toolCallId: (_n = toolCall.id) != null ? _n : (0, import_provider_utils3.generateId)(), toolName: toolCall.function.name, args: toolCall.function.arguments }); toolCall.sent = true; } } } }, flush(controller) { var _a2; if (finishReason === "tool-calls") { for (const toolCall of toolCalls) { if (!toolCall.sent) { controller.enqueue({ type: "tool-call", toolCallType: "function", toolCallId: (_a2 = toolCall.id) != null ? _a2 : (0, import_provider_utils3.generateId)(), toolName: toolCall.function.name, // Coerce invalid arguments to an empty JSON object args: (0, import_provider_utils3.isParsableJson)(toolCall.function.arguments) ? toolCall.function.arguments : "{}" }); toolCall.sent = true; } } } const providerMetadata = {}; if (shouldIncludeUsageAccounting && (openrouterUsage.totalTokens !== void 0 || openrouterUsage.cost !== void 0 || openrouterUsage.promptTokensDetails !== void 0 || openrouterUsage.completionTokensDetails !== void 0)) { providerMetadata.openrouter = { usage: openrouterUsage }; } const hasProviderMetadata = Object.keys(providerMetadata).length > 0 && shouldIncludeUsageAccounting; controller.enqueue(__spreadValues({ type: "finish", finishReason, logprobs, usage }, hasProviderMetadata ? { providerMetadata } : {})); } }) ), rawCall: { rawPrompt, rawSettings }, rawResponse: { headers: responseHeaders }, warnings: [] }; } }; var OpenRouterChatCompletionBaseResponseSchema = import_zod3.z.object({ id: import_zod3.z.string().optional(), model: import_zod3.z.string().optional(), usage: import_zod3.z.object({ prompt_tokens: import_zod3.z.number(), prompt_tokens_details: import_zod3.z.object({ cached_tokens: import_zod3.z.number() }).nullish(), completion_tokens: import_zod3.z.number(), completion_tokens_details: import_zod3.z.object({ reasoning_tokens: import_zod3.z.number() }).nullish(), total_tokens: import_zod3.z.number(), cost: import_zod3.z.number().optional() }).nullish() }); var OpenRouterNonStreamChatCompletionResponseSchema = OpenRouterChatCompletionBaseResponseSchema.extend({ choices: import_zod3.z.array( import_zod3.z.object({ message: import_zod3.z.object({ role: import_zod3.z.literal("assistant"), content: import_zod3.z.string().nullable().optional(), reasoning: import_zod3.z.string().nullable().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), tool_calls: import_zod3.z.array( import_zod3.z.object({ id: import_zod3.z.string().optional().nullable(), type: import_zod3.z.literal("function"), function: import_zod3.z.object({ name: import_zod3.z.string(), arguments: import_zod3.z.string() }) }) ).optional() }), index: import_zod3.z.number(), logprobs: import_zod3.z.object({ content: import_zod3.z.array( import_zod3.z.object({ token: import_zod3.z.string(), logprob: import_zod3.z.number(), top_logprobs: import_zod3.z.array( import_zod3.z.object({ token: import_zod3.z.string(), logprob: import_zod3.z.number() }) ) }) ).nullable() }).nullable().optional(), finish_reason: import_zod3.z.string().optional().nullable() }) ) }); var OpenRouterStreamChatCompletionChunkSchema = import_zod3.z.union([ OpenRouterChatCompletionBaseResponseSchema.extend({ choices: import_zod3.z.array( import_zod3.z.object({ delta: import_zod3.z.object({ role: import_zod3.z.enum(["assistant"]).optional(), content: import_zod3.z.string().nullish(), reasoning: import_zod3.z.string().nullish().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), tool_calls: import_zod3.z.array( import_zod3.z.object({ index: import_zod3.z.number(), id: import_zod3.z.string().nullish(), type: import_zod3.z.literal("function").optional(), function: import_zod3.z.object({ name: import_zod3.z.string().nullish(), arguments: import_zod3.z.string().nullish() }) }) ).nullish() }).nullish(), logprobs: import_zod3.z.object({ content: import_zod3.z.array( import_zod3.z.object({ token: import_zod3.z.string(), logprob: import_zod3.z.number(), top_logprobs: import_zod3.z.array( import_zod3.z.object({ token: import_zod3.z.string(), logprob: import_zod3.z.number() }) ) }) ).nullable() }).nullish(), finish_reason: import_zod3.z.string().nullable().optional(), index: import_zod3.z.number() }) ) }), OpenRouterErrorResponseSchema ]); function prepareToolsAndToolChoice(mode) { var _a; const tools = ((_a = mode.tools) == null ? void 0 : _a.length) ? mode.tools : void 0; if (tools == null) { return { tools: void 0, tool_choice: void 0 }; } const mappedTools = tools.map((tool) => { if (isFunctionTool(tool)) { return { type: "function", function: { name: tool.name, description: tool.description, parameters: tool.parameters } }; } return { type: "function", function: { name: tool.name } }; }); const toolChoice = mode.toolChoice; if (toolChoice == null) { return { tools: mappedTools, tool_choice: void 0 }; } const type = toolChoice.type; switch (type) { case "auto": case "none": case "required": return { tools: mappedTools, tool_choice: type }; case "tool": return { tools: mappedTools, tool_choice: { type: "function", function: { name: toolChoice.toolName } } }; default: { const _exhaustiveCheck = type; throw new Error(`Unsupported tool choice type: ${_exhaustiveCheck}`); } } } // src/openrouter-completion-language-model.ts var import_provider3 = require("@ai-sdk/provider"); var import_provider_utils4 = require("@ai-sdk/provider-utils"); var import_zod4 = require("zod"); // src/convert-to-openrouter-completion-prompt.ts var import_provider2 = require("@ai-sdk/provider"); function convertToOpenRouterCompletionPrompt({ prompt, inputFormat, user = "user", assistant = "assistant" }) { if (inputFormat === "prompt" && prompt.length === 1 && prompt[0] && prompt[0].role === "user" && prompt[0].content.length === 1 && prompt[0].content[0] && prompt[0].content[0].type === "text") { return { prompt: prompt[0].content[0].text }; } let text = ""; if (prompt[0] && prompt[0].role === "system") { text += `${prompt[0].content} `; prompt = prompt.slice(1); } for (const { role, content } of prompt) { switch (role) { case "system": { throw new import_provider2.InvalidPromptError({ message: "Unexpected system message in prompt: ${content}", prompt }); } case "user": { const userMessage = content.map((part) => { switch (part.type) { case "text": { return part.text; } case "image": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "images" }); } case "file": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "file attachments" }); } default: { const _exhaustiveCheck = part; throw new Error( `Unsupported content type: ${_exhaustiveCheck}` ); } } }).join(""); text += `${user}: ${userMessage} `; break; } case "assistant": { const assistantMessage = content.map((part) => { switch (part.type) { case "text": { return part.text; } case "tool-call": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "tool-call messages" }); } case "reasoning": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "reasoning messages" }); } case "redacted-reasoning": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "redacted reasoning messages" }); } case "file": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "file attachments" }); } default: { const _exhaustiveCheck = part; throw new Error( `Unsupported content type: ${_exhaustiveCheck}` ); } } }).join(""); text += `${assistant}: ${assistantMessage} `; break; } case "tool": { throw new import_provider2.UnsupportedFunctionalityError({ functionality: "tool messages" }); } default: { const _exhaustiveCheck = role; throw new Error(`Unsupported role: ${_exhaustiveCheck}`); } } } text += `${assistant}: `; return { prompt: text }; } // src/map-openrouter-completion-logprobs.ts function mapOpenRouterCompletionLogProbs(logprobs) { return logprobs == null ? void 0 : logprobs.tokens.map((token, index) => { var _a, _b; return { token, logprob: (_a = logprobs.token_logprobs[index]) != null ? _a : 0, topLogprobs: logprobs.top_logprobs ? Object.entries((_b = logprobs.top_logprobs[index]) != null ? _b : {}).map( ([token2, logprob]) => ({ token: token2, logprob }) ) : [] }; }); } // src/openrouter-completion-language-model.ts var OpenRouterCompletionLanguageModel = class { constructor(modelId, settings, config) { this.specificationVersion = "v1"; this.defaultObjectGenerationMode = void 0; this.modelId = modelId; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } getArgs({ mode, inputFormat, prompt, maxTokens, temperature, topP, frequencyPenalty, presencePenalty, seed, responseFormat, topK, stopSequences, providerMetadata }) { var _a, _b; const type = mode.type; const extraCallingBody = (_a = providerMetadata == null ? void 0 : providerMetadata.openrouter) != null ? _a : {}; const { prompt: completionPrompt } = convertToOpenRouterCompletionPrompt({ prompt, inputFormat }); const baseArgs = __spreadValues(__spreadValues(__spreadValues({ // model id: model: this.modelId, models: this.settings.models, // model specific settings: logit_bias: this.settings.logitBias, logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0, suffix: this.settings.suffix, user: this.settings.user, // standardized settings: max_tokens: maxTokens, temperature, top_p: topP, frequency_penalty: frequencyPenalty, presence_penalty: presencePenalty, seed, stop: stopSequences, response_format: responseFormat, top_k: topK, // prompt: prompt: completionPrompt, // OpenRouter specific settings: include_reasoning: this.settings.includeReasoning, reasoning: this.settings.reasoning }, this.config.extraBody), this.settings.extraBody), extraCallingBody); switch (type) { case "regular": { if ((_b = mode.tools) == null ? void 0 : _b.length) { throw new import_provider3.UnsupportedFunctionalityError({ functionality: "tools" }); } if (mode.toolChoice) { throw new import_provider3.UnsupportedFunctionalityError({ functionality: "toolChoice" }); } return baseArgs; } case "object-json": { throw new import_provider3.UnsupportedFunctionalityError({ functionality: "object-json mode" }); } case "object-tool": { throw new import_provider3.UnsupportedFunctionalityError({ functionality: "object-tool mode" }); } // Handle all non-text types with a single default case default: { const _exhaustiveCheck = type; throw new import_provider3.UnsupportedFunctionalityError({ functionality: `${_exhaustiveCheck} mode` }); } } } async doGenerate(options) { var _b, _c, _d, _e, _f; const args = this.getArgs(options); const { responseHeaders, value: response } = await (0, import_provider_utils4.postJsonToApi)({ url: this.config.url({ path: "/completions", modelId: this.modelId }), headers: (0, import_provider_utils4.combineHeaders)(this.config.headers(), options.headers), body: args, failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils4.createJsonResponseHandler)( OpenRouterCompletionChunkSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const _a = args, { prompt: rawPrompt } = _a, rawSettings = __objRest(_a, ["prompt"]); if ("error" in response) { throw new Error(`${response.error.message}`); } const choice = response.choices[0]; if (!choice) { throw new Error("No choice in OpenRouter completion response"); } return { response: { id: response.id, modelId: response.model }, text: (_b = choice.text) != null ? _b : "", reasoning: choice.reasoning || void 0, usage: { promptTokens: (_d = (_c = response.usage) == null ? void 0 : _c.prompt_tokens) != null ? _d : 0, completionTokens: (_f = (_e = response.usage) == null ? void 0 : _e.completion_tokens) != null ? _f : 0 }, finishReason: mapOpenRouterFinishReason(choice.finish_reason), logprobs: mapOpenRouterCompletionLogProbs(choice.logprobs), rawCall: { rawPrompt, rawSettings }, rawResponse: { headers: responseHeaders }, warnings: [] }; } async doStream(options) { const args = this.getArgs(options); const { responseHeaders, value: response } = await (0, import_provider_utils4.postJsonToApi)({ url: this.config.url({ path: "/completions", modelId: this.modelId }), headers: (0, import_provider_utils4.combineHeaders)(this.config.headers(), options.headers), body: __spreadProps(__spreadValues({}, this.getArgs(options)), { stream: true, // only include stream_options when in strict compatibility mode: stream_options: this.config.compatibility === "strict" ? { include_usage: true } : void 0 }), failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils4.createEventSourceResponseHandler)( OpenRouterCompletionChunkSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const _a = args, { prompt: rawPrompt } = _a, rawSettings = __objRest(_a, ["prompt"]); let finishReason = "other"; let usage = { promptTokens: Number.NaN, completionTokens: Number.NaN }; let logprobs; return { stream: response.pipeThrough( new TransformStream({ transform(chunk, controller) { if (!chunk.success) { finishReason = "error"; controller.enqueue({ type: "error", error: chunk.error }); return; } const value = chunk.value; if ("error" in value) { finishReason = "error"; controller.enqueue({ type: "error", error: value.error }); return; } if (value.usage != null) { usage = { promptTokens: value.usage.prompt_tokens, completionTokens: value.usage.completion_tokens }; } const choice = value.choices[0]; if ((choice == null ? void 0 : choice.finish_reason) != null) { finishReason = mapOpenRouterFinishReason(choice.finish_reason); } if ((choice == null ? void 0 : choice.text) != null) { controller.enqueue({ type: "text-delta", textDelta: choice.text }); } const mappedLogprobs = mapOpenRouterCompletionLogProbs( choice == null ? void 0 : choice.logprobs ); if (mappedLogprobs == null ? void 0 : mappedLogprobs.length) { if (logprobs === void 0) { logprobs = []; } logprobs.push(...mappedLogprobs); } }, flush(controller) { controller.enqueue({ type: "finish", finishReason, logprobs, usage }); } }) ), rawCall: { rawPrompt, rawSettings }, rawResponse: { headers: responseHeaders }, warnings: [] }; } }; var OpenRouterCompletionChunkSchema = import_zod4.z.union([ import_zod4.z.object({ id: import_zod4.z.string().optional(), model: import_zod4.z.string().optional(), choices: import_zod4.z.array( import_zod4.z.object({ text: import_zod4.z.string(), reasoning: import_zod4.z.string().nullish().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), finish_reason: import_zod4.z.string().nullish(), index: import_zod4.z.number(), logprobs: import_zod4.z.object({ tokens: import_zod4.z.array(import_zod4.z.string()), token_logprobs: import_zod4.z.array(import_zod4.z.number()), top_logprobs: import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.number())).nullable() }).nullable().optional() }) ), usage: import_zod4.z.object({ prompt_tokens: import_zod4.z.number(), completion_tokens: import_zod4.z.number() }).optional().nullable() }), OpenRouterErrorResponseSchema ]); // src/openrouter-facade.ts var OpenRouter = class { /** * Creates a new OpenRouter provider instance. */ constructor(options = {}) { var _a, _b; this.baseURL = (_b = (0, import_provider_utils5.withoutTrailingSlash)((_a = options.baseURL) != null ? _a : options.baseUrl)) != null ? _b : "https://openrouter.ai/api/v1"; this.apiKey = options.apiKey; this.headers = options.headers; } get baseConfig() { return { baseURL: this.baseURL, headers: () => __spreadValues({ Authorization: `Bearer ${(0, import_provider_utils5.loadApiKey)({ apiKey: this.apiKey, environmentVariableName: "OPENROUTER_API_KEY", description: "OpenRouter" })}` }, this.headers) }; } chat(modelId, settings = {}) { return new OpenRouterChatLanguageModel(modelId, settings, __spreadProps(__spreadValues({ provider: "openrouter.chat" }, this.baseConfig), { compatibility: "strict", url: ({ path }) => `${this.baseURL}${path}` })); } completion(modelId, settings = {}) { return new OpenRouterCompletionLanguageModel(modelId, settings, __spreadProps(__spreadValues({ provider: "openrouter.completion" }, this.baseConfig), { compatibility: "strict", url: ({ path }) => `${this.baseURL}${path}` })); } }; // src/openrouter-provider.ts var import_provider_utils6 = require("@ai-sdk/provider-utils"); function createOpenRouter(options = {}) { var _a, _b, _c; const baseURL = (_b = (0, import_provider_utils6.withoutTrailingSlash)((_a = options.baseURL) != null ? _a : options.baseUrl)) != null ? _b : "https://openrouter.ai/api/v1"; const compatibility = (_c = options.compatibility) != null ? _c : "compatible"; const getHeaders = () => __spreadValues({ Authorization: `Bearer ${(0, import_provider_utils6.loadApiKey)({ apiKey: options.apiKey, environmentVariableName: "OPENROUTER_API_KEY", description: "OpenRouter" })}` }, options.headers); const createChatModel = (modelId, settings = {}) => new OpenRouterChatLanguageModel(modelId, settings, { provider: "openrouter.chat", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, compatibility, fetch: options.fetch, extraBody: options.extraBody }); const createCompletionModel = (modelId, settings = {}) => new OpenRouterCompletionLanguageModel(modelId, settings, { provider: "openrouter.completion", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, compatibility, fetch: options.fetch, extraBody: options.extraBody }); const