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,393 lines (1,376 loc) 48.2 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; 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; }; // src/openrouter-facade.ts import { loadApiKey, withoutTrailingSlash } from "@ai-sdk/provider-utils"; // src/schemas/reasoning-details.ts import { z } from "zod"; var ReasoningDetailSummarySchema = z.object({ type: z.literal("reasoning.summary" /* Summary */), summary: z.string() }); var ReasoningDetailEncryptedSchema = z.object({ type: z.literal("reasoning.encrypted" /* Encrypted */), data: z.string() }); var ReasoningDetailTextSchema = z.object({ type: z.literal("reasoning.text" /* Text */), text: z.string().nullish(), signature: z.string().nullish() }); var ReasoningDetailUnionSchema = z.union([ ReasoningDetailSummarySchema, ReasoningDetailEncryptedSchema, ReasoningDetailTextSchema ]); var ReasoningDetailsWithUnknownSchema = z.union([ ReasoningDetailUnionSchema, z.unknown().transform(() => null) ]); var ReasoningDetailArraySchema = z.array(ReasoningDetailsWithUnknownSchema).transform((d) => d.filter((d2) => !!d2)); // src/openrouter-chat-language-model.ts import { InvalidResponseDataError, UnsupportedFunctionalityError } from "@ai-sdk/provider"; import { combineHeaders, createEventSourceResponseHandler, createJsonResponseHandler, generateId, isParsableJson, postJsonToApi } from "@ai-sdk/provider-utils"; import { z as z3 } from "zod"; // src/convert-to-openrouter-chat-messages.ts import { convertUint8ArrayToBase64 } from "@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,${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,${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 import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils"; import { z as z2 } from "zod"; var OpenRouterErrorResponseSchema = z2.object({ error: z2.object({ code: z2.union([z2.string(), z2.number()]).nullable(), message: z2.string(), type: z2.string().nullable(), param: z2.any().nullable() }) }); var openrouterFailedResponseHandler = 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 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 postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: combineHeaders(this.config.headers(), options.headers), body: args, failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: 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 : 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 postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: 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: 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 InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function' type.` }); } if (toolCallDelta.id == null) { throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'id' to be a string.` }); } if (((_c2 = toolCallDelta.function) == null ? void 0 : _c2.name) == null) { throw new 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 && 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 : 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 && isParsableJson(toolCall.function.arguments)) { controller.enqueue({ type: "tool-call", toolCallType: "function", toolCallId: (_n = toolCall.id) != null ? _n : 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 : generateId(), toolName: toolCall.function.name, // Coerce invalid arguments to an empty JSON object args: 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 = z3.object({ id: z3.string().optional(), model: z3.string().optional(), usage: z3.object({ prompt_tokens: z3.number(), prompt_tokens_details: z3.object({ cached_tokens: z3.number() }).nullish(), completion_tokens: z3.number(), completion_tokens_details: z3.object({ reasoning_tokens: z3.number() }).nullish(), total_tokens: z3.number(), cost: z3.number().optional() }).nullish() }); var OpenRouterNonStreamChatCompletionResponseSchema = OpenRouterChatCompletionBaseResponseSchema.extend({ choices: z3.array( z3.object({ message: z3.object({ role: z3.literal("assistant"), content: z3.string().nullable().optional(), reasoning: z3.string().nullable().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), tool_calls: z3.array( z3.object({ id: z3.string().optional().nullable(), type: z3.literal("function"), function: z3.object({ name: z3.string(), arguments: z3.string() }) }) ).optional() }), index: z3.number(), logprobs: z3.object({ content: z3.array( z3.object({ token: z3.string(), logprob: z3.number(), top_logprobs: z3.array( z3.object({ token: z3.string(), logprob: z3.number() }) ) }) ).nullable() }).nullable().optional(), finish_reason: z3.string().optional().nullable() }) ) }); var OpenRouterStreamChatCompletionChunkSchema = z3.union([ OpenRouterChatCompletionBaseResponseSchema.extend({ choices: z3.array( z3.object({ delta: z3.object({ role: z3.enum(["assistant"]).optional(), content: z3.string().nullish(), reasoning: z3.string().nullish().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), tool_calls: z3.array( z3.object({ index: z3.number(), id: z3.string().nullish(), type: z3.literal("function").optional(), function: z3.object({ name: z3.string().nullish(), arguments: z3.string().nullish() }) }) ).nullish() }).nullish(), logprobs: z3.object({ content: z3.array( z3.object({ token: z3.string(), logprob: z3.number(), top_logprobs: z3.array( z3.object({ token: z3.string(), logprob: z3.number() }) ) }) ).nullable() }).nullish(), finish_reason: z3.string().nullable().optional(), index: z3.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 import { UnsupportedFunctionalityError as UnsupportedFunctionalityError3 } from "@ai-sdk/provider"; import { combineHeaders as combineHeaders2, createEventSourceResponseHandler as createEventSourceResponseHandler2, createJsonResponseHandler as createJsonResponseHandler2, postJsonToApi as postJsonToApi2 } from "@ai-sdk/provider-utils"; import { z as z4 } from "zod"; // src/convert-to-openrouter-completion-prompt.ts import { InvalidPromptError, UnsupportedFunctionalityError as UnsupportedFunctionalityError2 } from "@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 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 UnsupportedFunctionalityError2({ functionality: "images" }); } case "file": { throw new UnsupportedFunctionalityError2({ 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 UnsupportedFunctionalityError2({ functionality: "tool-call messages" }); } case "reasoning": { throw new UnsupportedFunctionalityError2({ functionality: "reasoning messages" }); } case "redacted-reasoning": { throw new UnsupportedFunctionalityError2({ functionality: "redacted reasoning messages" }); } case "file": { throw new UnsupportedFunctionalityError2({ functionality: "file attachments" }); } default: { const _exhaustiveCheck = part; throw new Error( `Unsupported content type: ${_exhaustiveCheck}` ); } } }).join(""); text += `${assistant}: ${assistantMessage} `; break; } case "tool": { throw new UnsupportedFunctionalityError2({ 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 UnsupportedFunctionalityError3({ functionality: "tools" }); } if (mode.toolChoice) { throw new UnsupportedFunctionalityError3({ functionality: "toolChoice" }); } return baseArgs; } case "object-json": { throw new UnsupportedFunctionalityError3({ functionality: "object-json mode" }); } case "object-tool": { throw new UnsupportedFunctionalityError3({ functionality: "object-tool mode" }); } // Handle all non-text types with a single default case default: { const _exhaustiveCheck = type; throw new UnsupportedFunctionalityError3({ functionality: `${_exhaustiveCheck} mode` }); } } } async doGenerate(options) { var _b, _c, _d, _e, _f; const args = this.getArgs(options); const { responseHeaders, value: response } = await postJsonToApi2({ url: this.config.url({ path: "/completions", modelId: this.modelId }), headers: combineHeaders2(this.config.headers(), options.headers), body: args, failedResponseHandler: openrouterFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler2( 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 postJsonToApi2({ url: this.config.url({ path: "/completions", modelId: this.modelId }), headers: combineHeaders2(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: createEventSourceResponseHandler2( 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 = z4.union([ z4.object({ id: z4.string().optional(), model: z4.string().optional(), choices: z4.array( z4.object({ text: z4.string(), reasoning: z4.string().nullish().optional(), reasoning_details: ReasoningDetailArraySchema.nullish(), finish_reason: z4.string().nullish(), index: z4.number(), logprobs: z4.object({ tokens: z4.array(z4.string()), token_logprobs: z4.array(z4.number()), top_logprobs: z4.array(z4.record(z4.string(), z4.number())).nullable() }).nullable().optional() }) ), usage: z4.object({ prompt_tokens: z4.number(), completion_tokens: z4.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 = 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 ${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 import { loadApiKey as loadApiKey2, withoutTrailingSlash as withoutTrailingSlash2 } from "@ai-sdk/provider-utils"; function createOpenRouter(options = {}) { var _a, _b, _c; const baseURL = (_b = withoutTrailingSlash2((_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 ${loadApiKey2({ 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 createLanguageModel = (modelId, settings) => { if (new.target) { throw new Error( "The OpenRouter model function cannot be called with the new keyword." ); } if (modelId === "openai/gpt-3.5-turbo-instruct") { return createCompletionModel( modelId, settings ); } return createChatModel(modelId, settings); }; const provider = (modelId, settings) => createLanguageModel(modelId, settings); provider.languageModel = createLanguageModel; provider.chat = createChatModel; provider.completion = createCompletionModel; return provider; } var openrouter = createOpenRouter({ compatibility: "strict" // strict for OpenRouter API }); export { OpenRouter, createOpenRouter, openrouter }; //# sourceMappingURL=index.mjs.map