@ai-sdk/openai-compatible 3.0.81 → 3.0.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { JSONValue, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4FinishReason, LanguageModelV4Prompt, LanguageModelV4Usage, SharedV4ProviderMetadata, SharedV4Warning } from "@ai-sdk/provider";
2
- import { FetchFunction } from "@ai-sdk/provider-utils";
2
+ import { FetchFunction, Resolvable } from "@ai-sdk/provider-utils";
3
3
  import { ZodType, z } from "zod/v4";
4
4
  //#region src/chat/openai-compatible-api-types.d.ts
5
5
  type OpenAICompatibleChatPrompt = Array<OpenAICompatibleMessage>;
@@ -181,7 +181,7 @@ type MetadataExtractor = {
181
181
  //#region src/chat/openai-compatible-chat-language-model.d.ts
182
182
  type OpenAICompatibleChatConfig = {
183
183
  provider: string;
184
- headers?: () => Record<string, string | undefined>;
184
+ headers?: Resolvable<Record<string, string | undefined>>;
185
185
  url: (options: {
186
186
  modelId: string;
187
187
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat/openai-compatible-api-types.ts","../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts","../../src/openai-compatible-error.ts","../../src/chat/openai-compatible-metadata-extractor.ts","../../src/chat/openai-compatible-chat-language-model.ts"],"mappings":";;;;KAEY,6BAA6B,MAAM;KAEnC,0BACR,gCACA,8BACA,mCACA;KAIC,WAAW,aAAa,eAE3B,YAAY,cAAc,IAAI;UAGf,sCAAsC;EACrD;EACA;;UAGe,oCAAoC,WAAW;EAC9D;EACA,kBAAkB,MAAM;;KAGd,8BACR,kCACA,mCACA,mCACA,wCACA;UAEa,wCAAwC;EACvD;EACA;;UAGe,yCAAyC;EACxD;EACA;IAAa;;;UAIE,yCAAyC;EACxD;EACA;IAAa;;;UAIE,8CAA8C;EAC7D;EACA;IAAe;IAAc;;;UAGd,wCAAwC;EACvD;EACA;IAAQ;IAAkB;;IAAwB;;;UAGnC,yCAAyC,WAAW;EACnE;EACA;EACA;EACA,aAAa,MAAM;;UAGJ,wCAAwC;EACvD;EACA;EACA;IACE;IACA;;;;;;EAMF;IACE;MACE;;;;UAKW,oCAAoC;EACnD;EACA,kBAAkB,MAAM;EACxB;;;;wBCmGc,sCACd,QAAQ,yBAEN,UACA,oBACA;EAEA;EACA;EACA;IAED;;;wBCpMa,iCACd;EAEM;EACA;EACA;IACE;;EAEF;IACE;;uBAKP;;;wBCfa,gCACd,0CACC;;;wBCFa,sBACd,IACA,OACA;EAEA;EACA;EACA;+BACD;;;wBCLe,eACd,OACA;EAEA,OAAO;EACP,aAAa;;EAEb,mBAEI;IACE;IACA;MACE;MACA;MACA;MACA;;;EAGR;IACM;IAAkB;MAAY;;;EAKpC,cAAc;;;;KCXJ,uBAAuB;EACjC,aAAa,QAAQ;EACrB,iBAAiB,OAAO;EACxB,eAAe,UAAU,UAAU,QAAQ;;;;;;;;;KCdjC;;;;;;;;;EASV,oBACE;IAEA;QACI,QAAQ;;;;;;;;EASd;;;;;;;IAOE,aAAa;;;;;;;;IASb,iBAAiB;;;;;KCkBT;EACV;EACA,gBAAgB;EAChB,MAAM;IAAW;IAAiB;;EAClC,QAAQ;EACR;EACA,iBAAiB;EACjB,oBAAoB;;;;EAKpB;;;;EAKA;;;;EAKA,sBAAsB;;;;;;;EAQtB,wBACE,MAAM,qBACN,WAAW,sBACR;;;;;;;EAQL,gBAAgB;IACd,WAAW,QACT;IAGF,UAAU;;;;;;EAOZ,gBACE,OAAO,EAAE,MAAM,kBAAkB,uCAC9B;;cAqrBD,wCAAgC,EAAA,YAAA,EAAA,YAAA,EAAA;;;;;;;;;;;;GAmBxB,EAAA,KAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat/openai-compatible-api-types.ts","../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts","../../src/openai-compatible-error.ts","../../src/chat/openai-compatible-metadata-extractor.ts","../../src/chat/openai-compatible-chat-language-model.ts"],"mappings":";;;;KAEY,6BAA6B,MAAM;KAEnC,0BACR,gCACA,8BACA,mCACA;KAIC,WAAW,aAAa,eAE3B,YAAY,cAAc,IAAI;UAGf,sCAAsC;EACrD;EACA;;UAGe,oCAAoC,WAAW;EAC9D;EACA,kBAAkB,MAAM;;KAGd,8BACR,kCACA,mCACA,mCACA,wCACA;UAEa,wCAAwC;EACvD;EACA;;UAGe,yCAAyC;EACxD;EACA;IAAa;;;UAIE,yCAAyC;EACxD;EACA;IAAa;;;UAIE,8CAA8C;EAC7D;EACA;IAAe;IAAc;;;UAGd,wCAAwC;EACvD;EACA;IAAQ;IAAkB;;IAAwB;;;UAGnC,yCAAyC,WAAW;EACnE;EACA;EACA;EACA,aAAa,MAAM;;UAGJ,wCAAwC;EACvD;EACA;EACA;IACE;IACA;;;;;;EAMF;IACE;MACE;;;;UAKW,oCAAoC;EACnD;EACA,kBAAkB,MAAM;EACxB;;;;wBCiHc,sCACd,QAAQ,yBAEN,UACA,oBACA;EAEA;EACA;EACA;IAED;;;wBClNa,iCACd;EAEM;EACA;EACA;IACE;;EAEF;IACE;;uBAKP;;;wBCfa,gCACd,0CACC;;;wBCFa,sBACd,IACA,OACA;EAEA;EACA;EACA;+BACD;;;wBCLe,eACd,OACA;EAEA,OAAO;EACP,aAAa;;EAEb,mBAEI;IACE;IACA;MACE;MACA;MACA;MACA;;;EAGR;IACM;IAAkB;MAAY;;;EAKpC,cAAc;;;;KCXJ,uBAAuB;EACjC,aAAa,QAAQ;EACrB,iBAAiB,OAAO;EACxB,eAAe,UAAU,UAAU,QAAQ;;;;;;;;;KCdjC;;;;;;;;;EASV,oBACE;IAEA;QACI,QAAQ;;;;;;;;EASd;;;;;;;IAOE,aAAa;;;;;;;;IASb,iBAAiB;;;;;KCoBT;EACV;EACA,UAAU,WAAW;EACrB,MAAM;IAAW;IAAiB;;EAClC,QAAQ;EACR;EACA,iBAAiB;EACjB,oBAAoB;;;;EAKpB;;;;EAKA;;;;EAKA,sBAAsB;;;;;;;EAQtB,wBACE,MAAM,qBACN,WAAW,sBACR;;;;;;;EAQL,gBAAgB;IACd,WAAW,QACT;IAGF,UAAU;;;;;;EAOZ,gBACE,OAAO,EAAE,MAAM,kBAAkB,uCAC9B;;cAmsBD,wCAAgC,EAAA,YAAA,EAAA,YAAA,EAAA;;;;;;;;;;;;GAmBxB,EAAA,KAAA"}
@@ -1,5 +1,5 @@
1
1
  import { UnsupportedFunctionalityError } from "@ai-sdk/provider";
2
- import { convertBase64ToUint8Array, convertToBase64, createLanguageModelResponseMetadata, createNullLanguageModelUsage, getTopLevelMediaType, resolveFullMediaType, resolveProviderReference } from "@ai-sdk/provider-utils";
2
+ import { convertBase64ToUint8Array, convertToBase64, createLanguageModelResponseMetadata, createNullLanguageModelUsage, getTopLevelMediaType, resolveFullMediaType, resolveProviderReference, secureJsonParse, stringifyWithBigIntAndCycles } from "@ai-sdk/provider-utils";
3
3
  //#region src/chat/convert-to-openai-compatible-chat-messages.ts
4
4
  function getOpenAIMetadata(message) {
5
5
  return message?.providerOptions?.openaiCompatible ?? {};
@@ -84,6 +84,15 @@ function convertToolContentPart(part, provider) {
84
84
  if (part.type === "custom") throw new UnsupportedFunctionalityError({ functionality: "custom tool content parts" });
85
85
  return convertToOpenAICompatibleContentPart(part, provider);
86
86
  }
87
+ function serializeToolCallArguments(input) {
88
+ if (typeof input === "string") try {
89
+ secureJsonParse(input);
90
+ return input;
91
+ } catch {
92
+ return JSON.stringify(input);
93
+ }
94
+ return stringifyWithBigIntAndCycles(input);
95
+ }
87
96
  function convertToOpenAICompatibleChatMessages(prompt, { provider = "openaiCompatible", providerOptionsKey = "google", supportsMultiPartToolContent = false } = {}) {
88
97
  const messages = [];
89
98
  for (const { role, content, ...message } of prompt) {
@@ -131,7 +140,7 @@ function convertToOpenAICompatibleChatMessages(prompt, { provider = "openaiCompa
131
140
  type: "function",
132
141
  function: {
133
142
  name: part.toolName,
134
- arguments: JSON.stringify(part.input)
143
+ arguments: serializeToolCallArguments(part.input)
135
144
  },
136
145
  ...partMetadata,
137
146
  ...thoughtSignature ? { extra_content: { google: { thought_signature: String(thoughtSignature) } } } : {}
@@ -164,9 +173,9 @@ function convertToOpenAICompatibleChatMessages(prompt, { provider = "openaiCompa
164
173
  break;
165
174
  case "json":
166
175
  case "error-json":
167
- contentValue = JSON.stringify(output.value);
176
+ contentValue = stringifyWithBigIntAndCycles(output.value);
168
177
  break;
169
- case "content": contentValue = supportsMultiPartToolContent ? output.value.map((part) => convertToolContentPart(part, provider)) : JSON.stringify(output.value);
178
+ case "content": contentValue = supportsMultiPartToolContent ? output.value.map((part) => convertToolContentPart(part, provider)) : stringifyWithBigIntAndCycles(output.value);
170
179
  }
171
180
  const toolResponseMetadata = getOpenAIMetadata(toolResponse);
172
181
  messages.push({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_exhaustiveCheck","_exhaustiveCheck"],"sources":["../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts"],"sourcesContent":["import {\n UnsupportedFunctionalityError,\n type LanguageModelV4FilePart,\n type LanguageModelV4Prompt,\n type LanguageModelV4TextPart,\n type LanguageModelV4ToolResultOutput,\n type SharedV4ProviderMetadata,\n} from '@ai-sdk/provider';\nimport type {\n OpenAICompatibleChatPrompt,\n OpenAICompatibleContentPart,\n} from './openai-compatible-api-types';\nimport {\n convertBase64ToUint8Array,\n convertToBase64,\n getTopLevelMediaType,\n resolveFullMediaType,\n resolveProviderReference,\n} from '@ai-sdk/provider-utils';\n\nfunction getOpenAIMetadata(message: {\n providerOptions?: SharedV4ProviderMetadata;\n}) {\n return message?.providerOptions?.openaiCompatible ?? {};\n}\n\nfunction getAudioFormat(mediaType: string): 'wav' | 'mp3' | null {\n switch (mediaType) {\n case 'audio/wav':\n return 'wav';\n case 'audio/mp3':\n case 'audio/mpeg':\n return 'mp3';\n default:\n return null;\n }\n}\n\nfunction convertToOpenAICompatibleContentPart(\n part: LanguageModelV4TextPart | LanguageModelV4FilePart,\n provider: string,\n): OpenAICompatibleContentPart {\n const partMetadata = getOpenAIMetadata(part);\n\n switch (part.type) {\n case 'text': {\n return { type: 'text', text: part.text, ...partMetadata };\n }\n case 'file': {\n switch (part.data.type) {\n case 'reference': {\n return {\n type: 'file',\n file: {\n file_id: resolveProviderReference({\n reference: part.data.reference,\n provider,\n }),\n },\n ...partMetadata,\n };\n }\n case 'text': {\n throw new UnsupportedFunctionalityError({\n functionality: 'text file parts',\n });\n }\n case 'url':\n case 'data': {\n const topLevel = getTopLevelMediaType(part.mediaType);\n\n if (topLevel === 'image') {\n return {\n type: 'image_url',\n image_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'video') {\n return {\n type: 'video_url',\n video_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'audio') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'audio file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n const format = getAudioFormat(fullMediaType);\n if (format === null) {\n throw new UnsupportedFunctionalityError({\n functionality: `audio media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'input_audio',\n input_audio: {\n data: convertToBase64(part.data.data),\n format,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'application') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'PDF file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n if (fullMediaType !== 'application/pdf') {\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'file',\n file: {\n filename: part.filename ?? 'document.pdf',\n file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'text') {\n const textContent =\n part.data.type === 'url'\n ? part.data.url.toString()\n : typeof part.data.data === 'string'\n ? new TextDecoder().decode(\n convertBase64ToUint8Array(part.data.data),\n )\n : new TextDecoder().decode(part.data.data);\n\n return {\n type: 'text',\n text: textContent,\n ...partMetadata,\n };\n }\n\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${part.mediaType}`,\n });\n }\n }\n }\n }\n}\n\nfunction convertToolContentPart(\n part: Extract<\n LanguageModelV4ToolResultOutput,\n { type: 'content' }\n >['value'][number],\n provider: string,\n): OpenAICompatibleContentPart {\n if (part.type === 'custom') {\n throw new UnsupportedFunctionalityError({\n functionality: 'custom tool content parts',\n });\n }\n\n return convertToOpenAICompatibleContentPart(part, provider);\n}\n\nexport function convertToOpenAICompatibleChatMessages(\n prompt: LanguageModelV4Prompt,\n {\n provider = 'openaiCompatible',\n providerOptionsKey = 'google',\n supportsMultiPartToolContent = false,\n }: {\n provider?: string;\n providerOptionsKey?: string;\n supportsMultiPartToolContent?: boolean;\n } = {},\n): OpenAICompatibleChatPrompt {\n const messages: OpenAICompatibleChatPrompt = [];\n for (const { role, content, ...message } of prompt) {\n const metadata = getOpenAIMetadata({ ...message });\n switch (role) {\n case 'system': {\n messages.push({ role: 'system', content, ...metadata });\n break;\n }\n\n case 'user': {\n if (content.length === 1 && content[0].type === 'text') {\n messages.push({\n role: 'user',\n content: content[0].text,\n ...getOpenAIMetadata(content[0]),\n });\n break;\n }\n\n messages.push({\n role: 'user',\n content: content.map(part =>\n convertToOpenAICompatibleContentPart(part, provider),\n ),\n ...metadata,\n });\n\n break;\n }\n\n case 'assistant': {\n let text = '';\n let reasoning = '';\n const toolCalls: Array<{\n id: string;\n type: 'function';\n function: { name: string; arguments: string };\n extra_content?: {\n google?: {\n thought_signature?: string;\n };\n };\n }> = [];\n\n for (const part of content) {\n const partMetadata = getOpenAIMetadata(part);\n switch (part.type) {\n case 'text': {\n text += part.text;\n break;\n }\n case 'reasoning': {\n reasoning += part.text;\n break;\n }\n case 'tool-call': {\n // TODO: thoughtSignature should be abstracted once we add support for other providers\n const thoughtSignature =\n part.providerOptions?.[providerOptionsKey]?.thoughtSignature ??\n part.providerOptions?.google?.thoughtSignature;\n toolCalls.push({\n id: part.toolCallId,\n type: 'function',\n function: {\n name: part.toolName,\n arguments: JSON.stringify(part.input),\n },\n ...partMetadata,\n // Include extra_content for Google Gemini thought signatures\n ...(thoughtSignature\n ? {\n extra_content: {\n google: {\n thought_signature: String(thoughtSignature),\n },\n },\n }\n : {}),\n });\n break;\n }\n }\n }\n\n messages.push({\n role: 'assistant',\n content: toolCalls.length > 0 ? text || null : text,\n ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n ...metadata,\n });\n\n break;\n }\n\n case 'tool': {\n for (const toolResponse of content) {\n if (toolResponse.type === 'tool-approval-response') {\n continue;\n }\n\n const output = toolResponse.output;\n\n let contentValue: string | Array<OpenAICompatibleContentPart>;\n switch (output.type) {\n case 'text':\n case 'error-text':\n contentValue = output.value;\n break;\n case 'execution-denied':\n contentValue = output.reason ?? 'Tool call execution denied.';\n break;\n case 'json':\n case 'error-json':\n contentValue = JSON.stringify(output.value);\n break;\n case 'content':\n contentValue = supportsMultiPartToolContent\n ? output.value.map(part =>\n convertToolContentPart(part, provider),\n )\n : JSON.stringify(output.value);\n break;\n }\n\n const toolResponseMetadata = getOpenAIMetadata(toolResponse);\n messages.push({\n role: 'tool',\n tool_call_id: toolResponse.toolCallId,\n content: contentValue,\n ...toolResponseMetadata,\n });\n }\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unsupported role: ${_exhaustiveCheck}`);\n }\n }\n }\n\n return messages;\n}\n","import type { LanguageModelV4Usage } from '@ai-sdk/provider';\nimport { createNullLanguageModelUsage } from '@ai-sdk/provider-utils';\n\nexport function convertOpenAICompatibleChatUsage(\n usage:\n | {\n prompt_tokens?: number | null;\n completion_tokens?: number | null;\n prompt_tokens_details?: {\n cached_tokens?: number | null;\n } | null;\n completion_tokens_details?: {\n reasoning_tokens?: number | null;\n } | null;\n }\n | undefined\n | null,\n): LanguageModelV4Usage {\n if (usage == null) {\n return createNullLanguageModelUsage();\n }\n\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const reasoningTokens =\n usage.completion_tokens_details?.reasoning_tokens ?? 0;\n\n return {\n inputTokens: {\n total: promptTokens,\n noCache: promptTokens - cacheReadTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: completionTokens,\n text: Math.max(0, completionTokens - reasoningTokens),\n reasoning: reasoningTokens,\n },\n raw: usage,\n };\n}\n","import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';\n\nexport function mapOpenAICompatibleFinishReason(\n finishReason: string | null | undefined,\n): LanguageModelV4FinishReason['unified'] {\n switch (finishReason) {\n case 'stop':\n return 'stop';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content-filter';\n case 'function_call':\n case 'tool_calls':\n return 'tool-calls';\n default:\n return 'other';\n }\n}\n","import { createLanguageModelResponseMetadata } from '@ai-sdk/provider-utils';\n\nexport function getResponseMetadata({\n id,\n model,\n created,\n}: {\n id?: string | null;\n model?: string | null;\n created?: number | null;\n}) {\n return createLanguageModelResponseMetadata({\n id,\n model,\n // Some OpenAI-compatible providers use 0 as a placeholder timestamp.\n created: created || undefined,\n });\n}\n","import {\n UnsupportedFunctionalityError,\n type LanguageModelV4CallOptions,\n type SharedV4Warning,\n} from '@ai-sdk/provider';\nexport function prepareTools({\n tools,\n toolChoice,\n}: {\n tools: LanguageModelV4CallOptions['tools'];\n toolChoice?: LanguageModelV4CallOptions['toolChoice'];\n}): {\n tools:\n | undefined\n | Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }>;\n toolChoice:\n | { type: 'function'; function: { name: string } }\n | 'auto'\n | 'none'\n | 'required'\n | undefined;\n toolWarnings: SharedV4Warning[];\n} {\n // when the tools array is empty, change it to undefined to prevent errors:\n tools = tools?.length ? tools : undefined;\n\n const toolWarnings: SharedV4Warning[] = [];\n\n if (tools == null) {\n return { tools: undefined, toolChoice: undefined, toolWarnings };\n }\n\n const openaiCompatTools: Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }> = [];\n\n for (const tool of tools) {\n if (tool.type === 'provider') {\n toolWarnings.push({\n type: 'unsupported',\n feature: `provider-defined tool ${tool.id}`,\n });\n } else {\n openaiCompatTools.push({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n ...(tool.strict != null ? { strict: tool.strict } : {}),\n },\n });\n }\n }\n\n if (toolChoice == null) {\n return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };\n }\n\n const type = toolChoice.type;\n\n switch (type) {\n case 'auto':\n case 'none':\n case 'required':\n return { tools: openaiCompatTools, toolChoice: type, toolWarnings };\n case 'tool':\n return {\n tools: openaiCompatTools,\n toolChoice: {\n type: 'function',\n function: { name: toolChoice.toolName },\n },\n toolWarnings,\n };\n default: {\n const _exhaustiveCheck: never = type;\n throw new UnsupportedFunctionalityError({\n functionality: `tool choice type: ${_exhaustiveCheck}`,\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["_exhaustiveCheck","_exhaustiveCheck"],"sources":["../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts"],"sourcesContent":["import {\n UnsupportedFunctionalityError,\n type LanguageModelV4FilePart,\n type LanguageModelV4Prompt,\n type LanguageModelV4TextPart,\n type LanguageModelV4ToolResultOutput,\n type SharedV4ProviderMetadata,\n} from '@ai-sdk/provider';\nimport type {\n OpenAICompatibleChatPrompt,\n OpenAICompatibleContentPart,\n} from './openai-compatible-api-types';\nimport {\n secureJsonParse,\n stringifyWithBigIntAndCycles,\n convertBase64ToUint8Array,\n convertToBase64,\n getTopLevelMediaType,\n resolveFullMediaType,\n resolveProviderReference,\n} from '@ai-sdk/provider-utils';\n\nfunction getOpenAIMetadata(message: {\n providerOptions?: SharedV4ProviderMetadata;\n}) {\n return message?.providerOptions?.openaiCompatible ?? {};\n}\n\nfunction getAudioFormat(mediaType: string): 'wav' | 'mp3' | null {\n switch (mediaType) {\n case 'audio/wav':\n return 'wav';\n case 'audio/mp3':\n case 'audio/mpeg':\n return 'mp3';\n default:\n return null;\n }\n}\n\nfunction convertToOpenAICompatibleContentPart(\n part: LanguageModelV4TextPart | LanguageModelV4FilePart,\n provider: string,\n): OpenAICompatibleContentPart {\n const partMetadata = getOpenAIMetadata(part);\n\n switch (part.type) {\n case 'text': {\n return { type: 'text', text: part.text, ...partMetadata };\n }\n case 'file': {\n switch (part.data.type) {\n case 'reference': {\n return {\n type: 'file',\n file: {\n file_id: resolveProviderReference({\n reference: part.data.reference,\n provider,\n }),\n },\n ...partMetadata,\n };\n }\n case 'text': {\n throw new UnsupportedFunctionalityError({\n functionality: 'text file parts',\n });\n }\n case 'url':\n case 'data': {\n const topLevel = getTopLevelMediaType(part.mediaType);\n\n if (topLevel === 'image') {\n return {\n type: 'image_url',\n image_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'video') {\n return {\n type: 'video_url',\n video_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'audio') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'audio file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n const format = getAudioFormat(fullMediaType);\n if (format === null) {\n throw new UnsupportedFunctionalityError({\n functionality: `audio media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'input_audio',\n input_audio: {\n data: convertToBase64(part.data.data),\n format,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'application') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'PDF file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n if (fullMediaType !== 'application/pdf') {\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'file',\n file: {\n filename: part.filename ?? 'document.pdf',\n file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'text') {\n const textContent =\n part.data.type === 'url'\n ? part.data.url.toString()\n : typeof part.data.data === 'string'\n ? new TextDecoder().decode(\n convertBase64ToUint8Array(part.data.data),\n )\n : new TextDecoder().decode(part.data.data);\n\n return {\n type: 'text',\n text: textContent,\n ...partMetadata,\n };\n }\n\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${part.mediaType}`,\n });\n }\n }\n }\n }\n}\n\nfunction convertToolContentPart(\n part: Extract<\n LanguageModelV4ToolResultOutput,\n { type: 'content' }\n >['value'][number],\n provider: string,\n): OpenAICompatibleContentPart {\n if (part.type === 'custom') {\n throw new UnsupportedFunctionalityError({\n functionality: 'custom tool content parts',\n });\n }\n\n return convertToOpenAICompatibleContentPart(part, provider);\n}\n\nfunction serializeToolCallArguments(input: unknown): string {\n if (typeof input === 'string') {\n try {\n secureJsonParse(input);\n return input;\n } catch {\n return JSON.stringify(input);\n }\n }\n return stringifyWithBigIntAndCycles(input);\n}\n\nexport function convertToOpenAICompatibleChatMessages(\n prompt: LanguageModelV4Prompt,\n {\n provider = 'openaiCompatible',\n providerOptionsKey = 'google',\n supportsMultiPartToolContent = false,\n }: {\n provider?: string;\n providerOptionsKey?: string;\n supportsMultiPartToolContent?: boolean;\n } = {},\n): OpenAICompatibleChatPrompt {\n const messages: OpenAICompatibleChatPrompt = [];\n for (const { role, content, ...message } of prompt) {\n const metadata = getOpenAIMetadata({ ...message });\n switch (role) {\n case 'system': {\n messages.push({ role: 'system', content, ...metadata });\n break;\n }\n\n case 'user': {\n if (content.length === 1 && content[0].type === 'text') {\n messages.push({\n role: 'user',\n content: content[0].text,\n ...getOpenAIMetadata(content[0]),\n });\n break;\n }\n\n messages.push({\n role: 'user',\n content: content.map(part =>\n convertToOpenAICompatibleContentPart(part, provider),\n ),\n ...metadata,\n });\n\n break;\n }\n\n case 'assistant': {\n let text = '';\n let reasoning = '';\n const toolCalls: Array<{\n id: string;\n type: 'function';\n function: { name: string; arguments: string };\n extra_content?: {\n google?: {\n thought_signature?: string;\n };\n };\n }> = [];\n\n for (const part of content) {\n const partMetadata = getOpenAIMetadata(part);\n switch (part.type) {\n case 'text': {\n text += part.text;\n break;\n }\n case 'reasoning': {\n reasoning += part.text;\n break;\n }\n case 'tool-call': {\n // TODO: thoughtSignature should be abstracted once we add support for other providers\n const thoughtSignature =\n part.providerOptions?.[providerOptionsKey]?.thoughtSignature ??\n part.providerOptions?.google?.thoughtSignature;\n toolCalls.push({\n id: part.toolCallId,\n type: 'function',\n function: {\n name: part.toolName,\n arguments: serializeToolCallArguments(part.input),\n },\n ...partMetadata,\n // Include extra_content for Google Gemini thought signatures\n ...(thoughtSignature\n ? {\n extra_content: {\n google: {\n thought_signature: String(thoughtSignature),\n },\n },\n }\n : {}),\n });\n break;\n }\n }\n }\n\n messages.push({\n role: 'assistant',\n content: toolCalls.length > 0 ? text || null : text,\n ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n ...metadata,\n });\n\n break;\n }\n\n case 'tool': {\n for (const toolResponse of content) {\n if (toolResponse.type === 'tool-approval-response') {\n continue;\n }\n\n const output = toolResponse.output;\n\n let contentValue: string | Array<OpenAICompatibleContentPart>;\n switch (output.type) {\n case 'text':\n case 'error-text':\n contentValue = output.value;\n break;\n case 'execution-denied':\n contentValue = output.reason ?? 'Tool call execution denied.';\n break;\n case 'json':\n case 'error-json':\n contentValue = stringifyWithBigIntAndCycles(output.value);\n break;\n case 'content':\n contentValue = supportsMultiPartToolContent\n ? output.value.map(part =>\n convertToolContentPart(part, provider),\n )\n : stringifyWithBigIntAndCycles(output.value);\n break;\n }\n\n const toolResponseMetadata = getOpenAIMetadata(toolResponse);\n messages.push({\n role: 'tool',\n tool_call_id: toolResponse.toolCallId,\n content: contentValue,\n ...toolResponseMetadata,\n });\n }\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unsupported role: ${_exhaustiveCheck}`);\n }\n }\n }\n\n return messages;\n}\n","import type { LanguageModelV4Usage } from '@ai-sdk/provider';\nimport { createNullLanguageModelUsage } from '@ai-sdk/provider-utils';\n\nexport function convertOpenAICompatibleChatUsage(\n usage:\n | {\n prompt_tokens?: number | null;\n completion_tokens?: number | null;\n prompt_tokens_details?: {\n cached_tokens?: number | null;\n } | null;\n completion_tokens_details?: {\n reasoning_tokens?: number | null;\n } | null;\n }\n | undefined\n | null,\n): LanguageModelV4Usage {\n if (usage == null) {\n return createNullLanguageModelUsage();\n }\n\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const reasoningTokens =\n usage.completion_tokens_details?.reasoning_tokens ?? 0;\n\n return {\n inputTokens: {\n total: promptTokens,\n noCache: promptTokens - cacheReadTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: completionTokens,\n text: Math.max(0, completionTokens - reasoningTokens),\n reasoning: reasoningTokens,\n },\n raw: usage,\n };\n}\n","import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';\n\nexport function mapOpenAICompatibleFinishReason(\n finishReason: string | null | undefined,\n): LanguageModelV4FinishReason['unified'] {\n switch (finishReason) {\n case 'stop':\n return 'stop';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content-filter';\n case 'function_call':\n case 'tool_calls':\n return 'tool-calls';\n default:\n return 'other';\n }\n}\n","import { createLanguageModelResponseMetadata } from '@ai-sdk/provider-utils';\n\nexport function getResponseMetadata({\n id,\n model,\n created,\n}: {\n id?: string | null;\n model?: string | null;\n created?: number | null;\n}) {\n return createLanguageModelResponseMetadata({\n id,\n model,\n // Some OpenAI-compatible providers use 0 as a placeholder timestamp.\n created: created || undefined,\n });\n}\n","import {\n UnsupportedFunctionalityError,\n type LanguageModelV4CallOptions,\n type SharedV4Warning,\n} from '@ai-sdk/provider';\nexport function prepareTools({\n tools,\n toolChoice,\n}: {\n tools: LanguageModelV4CallOptions['tools'];\n toolChoice?: LanguageModelV4CallOptions['toolChoice'];\n}): {\n tools:\n | undefined\n | Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }>;\n toolChoice:\n | { type: 'function'; function: { name: string } }\n | 'auto'\n | 'none'\n | 'required'\n | undefined;\n toolWarnings: SharedV4Warning[];\n} {\n // when the tools array is empty, change it to undefined to prevent errors:\n tools = tools?.length ? tools : undefined;\n\n const toolWarnings: SharedV4Warning[] = [];\n\n if (tools == null) {\n return { tools: undefined, toolChoice: undefined, toolWarnings };\n }\n\n const openaiCompatTools: Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }> = [];\n\n for (const tool of tools) {\n if (tool.type === 'provider') {\n toolWarnings.push({\n type: 'unsupported',\n feature: `provider-defined tool ${tool.id}`,\n });\n } else {\n openaiCompatTools.push({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n ...(tool.strict != null ? { strict: tool.strict } : {}),\n },\n });\n }\n }\n\n if (toolChoice == null) {\n return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };\n }\n\n const type = toolChoice.type;\n\n switch (type) {\n case 'auto':\n case 'none':\n case 'required':\n return { tools: openaiCompatTools, toolChoice: type, toolWarnings };\n case 'tool':\n return {\n tools: openaiCompatTools,\n toolChoice: {\n type: 'function',\n function: { name: toolChoice.toolName },\n },\n toolWarnings,\n };\n default: {\n const _exhaustiveCheck: never = type;\n throw new UnsupportedFunctionalityError({\n functionality: `tool choice type: ${_exhaustiveCheck}`,\n });\n }\n }\n}\n"],"mappings":"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"}
package/docs/index.mdx CHANGED
@@ -50,12 +50,15 @@ You can use the following optional settings to customize the provider instance:
50
50
 
51
51
  Set the URL prefix for API calls.
52
52
 
53
- - **apiKey** _string_
53
+ - **apiKey** _string | PromiseLike&lt;string&gt; | (() =&gt; string | PromiseLike&lt;string&gt;)_
54
54
 
55
55
  API key for authenticating requests. If specified, adds an `Authorization`
56
56
  header to request headers with the value `Bearer <apiKey>`. This will be added
57
57
  before any headers potentially specified in the `headers` option.
58
58
 
59
+ A function is called before each request and may return a promise. Use it to
60
+ supply refreshed credentials; a promise value resolves to the same key each time.
61
+
59
62
  - **headers** _Record&lt;string,string&gt;_
60
63
 
61
64
  Optional custom headers to include in requests. These will be added to request headers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai-compatible",
3
- "version": "3.0.81",
3
+ "version": "3.0.83",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.34",
39
- "@ai-sdk/provider-utils": "5.0.71"
38
+ "@ai-sdk/provider": "4.0.35",
39
+ "@ai-sdk/provider-utils": "5.0.73"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -11,6 +11,8 @@ import type {
11
11
  OpenAICompatibleContentPart,
12
12
  } from './openai-compatible-api-types';
13
13
  import {
14
+ secureJsonParse,
15
+ stringifyWithBigIntAndCycles,
14
16
  convertBase64ToUint8Array,
15
17
  convertToBase64,
16
18
  getTopLevelMediaType,
@@ -186,6 +188,18 @@ function convertToolContentPart(
186
188
  return convertToOpenAICompatibleContentPart(part, provider);
187
189
  }
188
190
 
191
+ function serializeToolCallArguments(input: unknown): string {
192
+ if (typeof input === 'string') {
193
+ try {
194
+ secureJsonParse(input);
195
+ return input;
196
+ } catch {
197
+ return JSON.stringify(input);
198
+ }
199
+ }
200
+ return stringifyWithBigIntAndCycles(input);
201
+ }
202
+
189
203
  export function convertToOpenAICompatibleChatMessages(
190
204
  prompt: LanguageModelV4Prompt,
191
205
  {
@@ -263,7 +277,7 @@ export function convertToOpenAICompatibleChatMessages(
263
277
  type: 'function',
264
278
  function: {
265
279
  name: part.toolName,
266
- arguments: JSON.stringify(part.input),
280
+ arguments: serializeToolCallArguments(part.input),
267
281
  },
268
282
  ...partMetadata,
269
283
  // Include extra_content for Google Gemini thought signatures
@@ -312,14 +326,14 @@ export function convertToOpenAICompatibleChatMessages(
312
326
  break;
313
327
  case 'json':
314
328
  case 'error-json':
315
- contentValue = JSON.stringify(output.value);
329
+ contentValue = stringifyWithBigIntAndCycles(output.value);
316
330
  break;
317
331
  case 'content':
318
332
  contentValue = supportsMultiPartToolContent
319
333
  ? output.value.map(part =>
320
334
  convertToolContentPart(part, provider),
321
335
  )
322
- : JSON.stringify(output.value);
336
+ : stringifyWithBigIntAndCycles(output.value);
323
337
  break;
324
338
  }
325
339
 
@@ -11,8 +11,11 @@ export const openaiCompatibleLanguageModelChatOptions = z.object({
11
11
 
12
12
  /**
13
13
  * Reasoning effort for reasoning models. Defaults to `medium`.
14
+ *
15
+ * Set to `null` to send `"reasoning_effort": null`, for providers that treat
16
+ * an explicit `null` differently from an omitted field.
14
17
  */
15
- reasoningEffort: z.string().optional(),
18
+ reasoningEffort: z.string().nullish(),
16
19
 
17
20
  /**
18
21
  * Controls the verbosity of the generated text. Defaults to `medium`.
@@ -13,6 +13,8 @@ import {
13
13
  type SharedV4Warning,
14
14
  } from '@ai-sdk/provider';
15
15
  import {
16
+ type Resolvable,
17
+ resolve,
16
18
  lazySchema,
17
19
  zodSchema,
18
20
  combineHeaders,
@@ -63,7 +65,7 @@ type OpenAICompatibleStreamingToolCallDelta = StreamingToolCallDelta & {
63
65
 
64
66
  export type OpenAICompatibleChatConfig = {
65
67
  provider: string;
66
- headers?: () => Record<string, string | undefined>;
68
+ headers?: Resolvable<Record<string, string | undefined>>;
67
69
  url: (options: { modelId: string; path: string }) => string;
68
70
  fetch?: FetchFunction;
69
71
  includeUsage?: boolean;
@@ -290,6 +292,15 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
290
292
  providerOptions,
291
293
  );
292
294
 
295
+ const reasoningEffort =
296
+ compatibleOptions.reasoningEffort !== undefined
297
+ ? compatibleOptions.reasoningEffort
298
+ : isCustomReasoning(reasoning)
299
+ ? this.config.mapReasoning != null
300
+ ? this.config.mapReasoning({ reasoning, warnings })
301
+ : reasoning
302
+ : undefined;
303
+
293
304
  return {
294
305
  metadataKey,
295
306
  args: {
@@ -335,13 +346,12 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
335
346
  ),
336
347
  ),
337
348
 
338
- reasoning_effort:
339
- compatibleOptions.reasoningEffort ??
340
- (isCustomReasoning(reasoning)
341
- ? this.config.mapReasoning != null
342
- ? this.config.mapReasoning({ reasoning, warnings })
343
- : reasoning
344
- : undefined),
349
+ // only write reasoning_effort when it has a value: an explicit `null` must be
350
+ // sent as `null`, and an unset option must not overwrite a raw
351
+ // `reasoning_effort` passed through provider options:
352
+ ...(reasoningEffort !== undefined
353
+ ? { reasoning_effort: reasoningEffort }
354
+ : {}),
345
355
  verbosity: compatibleOptions.textVerbosity,
346
356
 
347
357
  // messages:
@@ -377,7 +387,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
377
387
  path: '/chat/completions',
378
388
  modelId: this.modelId,
379
389
  }),
380
- headers: combineHeaders(this.config.headers?.(), options.headers),
390
+ headers: combineHeaders(
391
+ await resolve(this.config.headers),
392
+ options.headers,
393
+ ),
381
394
  body: transformedBody,
382
395
  failedResponseHandler: this.failedResponseHandler,
383
396
  successfulResponseHandler: createJsonResponseHandler(
@@ -494,7 +507,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
494
507
  path: '/chat/completions',
495
508
  modelId: this.modelId,
496
509
  }),
497
- headers: combineHeaders(this.config.headers?.(), options.headers),
510
+ headers: combineHeaders(
511
+ await resolve(this.config.headers),
512
+ options.headers,
513
+ ),
498
514
  body,
499
515
  failedResponseHandler: this.failedResponseHandler,
500
516
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -10,6 +10,8 @@ import type {
10
10
  SharedV4Warning,
11
11
  } from '@ai-sdk/provider';
12
12
  import {
13
+ type Resolvable,
14
+ resolve,
13
15
  lazySchema,
14
16
  zodSchema,
15
17
  combineHeaders,
@@ -45,7 +47,7 @@ import {
45
47
  type OpenAICompatibleCompletionConfig = {
46
48
  provider: string;
47
49
  includeUsage?: boolean;
48
- headers?: () => Record<string, string | undefined>;
50
+ headers?: Resolvable<Record<string, string | undefined>>;
49
51
  url: (options: { modelId: string; path: string }) => string;
50
52
  fetch?: FetchFunction;
51
53
  errorStructure?: ProviderErrorStructure<any>;
@@ -217,7 +219,10 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
217
219
  path: '/completions',
218
220
  modelId: this.modelId,
219
221
  }),
220
- headers: combineHeaders(this.config.headers?.(), options.headers),
222
+ headers: combineHeaders(
223
+ await resolve(this.config.headers),
224
+ options.headers,
225
+ ),
221
226
  body: args,
222
227
  failedResponseHandler: this.failedResponseHandler,
223
228
  successfulResponseHandler: createJsonResponseHandler(
@@ -272,7 +277,10 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
272
277
  path: '/completions',
273
278
  modelId: this.modelId,
274
279
  }),
275
- headers: combineHeaders(this.config.headers?.(), options.headers),
280
+ headers: combineHeaders(
281
+ await resolve(this.config.headers),
282
+ options.headers,
283
+ ),
276
284
  body,
277
285
  failedResponseHandler: this.failedResponseHandler,
278
286
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -4,6 +4,8 @@ import {
4
4
  type SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
7
+ type Resolvable,
8
+ resolve,
7
9
  lazySchema,
8
10
  zodSchema,
9
11
  combineHeaders,
@@ -40,7 +42,7 @@ type OpenAICompatibleEmbeddingConfig = {
40
42
 
41
43
  provider: string;
42
44
  url: (options: { modelId: string; path: string }) => string;
43
- headers?: () => Record<string, string | undefined>;
45
+ headers?: Resolvable<Record<string, string | undefined>>;
44
46
  fetch?: FetchFunction;
45
47
  errorStructure?: ProviderErrorStructure<any>;
46
48
  };
@@ -154,7 +156,7 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
154
156
  path: '/embeddings',
155
157
  modelId: this.modelId,
156
158
  }),
157
- headers: combineHeaders(this.config.headers?.(), headers),
159
+ headers: combineHeaders(await resolve(this.config.headers), headers),
158
160
  body: {
159
161
  model: this.modelId,
160
162
  input: values,
@@ -11,6 +11,8 @@ import {
11
11
  warnIfDeprecatedProviderOptionsKey,
12
12
  } from '../utils/to-camel-case';
13
13
  import {
14
+ type Resolvable,
15
+ resolve,
14
16
  lazySchema,
15
17
  zodSchema,
16
18
  type InferSchema,
@@ -39,7 +41,7 @@ import type { OpenAICompatibleImageModelId } from './openai-compatible-image-set
39
41
 
40
42
  export type OpenAICompatibleImageModelConfig = {
41
43
  provider: string;
42
- headers?: () => Record<string, string | undefined>;
44
+ headers?: Resolvable<Record<string, string | undefined>>;
43
45
  url: (options: { modelId: string; path: string }) => string;
44
46
  fetch?: FetchFunction;
45
47
  errorStructure?: ProviderErrorStructure<any>;
@@ -136,7 +138,10 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
136
138
  path: isEditing ? '/images/edits' : '/images/generations',
137
139
  modelId: this.modelId,
138
140
  });
139
- const requestHeaders = combineHeaders(this.config.headers?.(), headers);
141
+ const requestHeaders = combineHeaders(
142
+ await resolve(this.config.headers),
143
+ headers,
144
+ );
140
145
 
141
146
  // Image editing mode - use form data and /images/edits endpoint
142
147
  if (isEditing) {
@@ -165,7 +170,7 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
165
170
  return {
166
171
  images: await this.getImages(response.data, {
167
172
  url,
168
- headers: requestHeaders,
173
+ headers,
169
174
  abortSignal,
170
175
  }),
171
176
  warnings,
@@ -202,7 +207,7 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
202
207
  return {
203
208
  images: await this.getImages(response.data, {
204
209
  url,
205
- headers: requestHeaders,
210
+ headers,
206
211
  abortSignal,
207
212
  }),
208
213
  warnings,
@@ -219,7 +224,7 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
219
224
  data: InferSchema<typeof openaiCompatibleImageResponseSchema>['data'],
220
225
  options: {
221
226
  url: string;
222
- headers: Record<string, string | undefined>;
227
+ headers: Record<string, string | undefined> | undefined;
223
228
  abortSignal: AbortSignal | undefined;
224
229
  },
225
230
  ): Promise<string[] | Uint8Array[]> {
@@ -246,7 +251,10 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
246
251
  validateUrl: true,
247
252
  trustedOrigin: options.url,
248
253
  credentialedOrigin: options.url,
249
- headers: options.headers,
254
+ headers: combineHeaders(
255
+ await resolve(this.config.headers),
256
+ options.headers,
257
+ ),
250
258
  abortSignal: options.abortSignal,
251
259
  fetch: this.config.fetch,
252
260
  successfulResponseHandler: createBinaryResponseHandler(),
@@ -5,6 +5,8 @@ import type {
5
5
  ProviderV4,
6
6
  } from '@ai-sdk/provider';
7
7
  import {
8
+ resolveWith,
9
+ type Resolvable,
8
10
  withoutTrailingSlash,
9
11
  withUserAgentSuffix,
10
12
  type FetchFunction,
@@ -61,8 +63,9 @@ export interface OpenAICompatibleProviderSettings {
61
63
  * API key for authenticating requests. If specified, adds an `Authorization`
62
64
  * header to request headers with the value `Bearer <apiKey>`. This will be added
63
65
  * before any headers potentially specified in the `headers` option.
66
+ * A function is called before each request and may return a promise.
64
67
  */
65
- apiKey?: string;
68
+ apiKey?: Resolvable<string>;
66
69
 
67
70
  /**
68
71
  * Optional custom headers to include in requests. These will be added to request headers
@@ -148,17 +151,20 @@ export function createOpenAICompatible<
148
151
  interface CommonModelConfig {
149
152
  provider: string;
150
153
  url: ({ path }: { path: string }) => string;
151
- headers: () => Record<string, string>;
154
+ headers: Resolvable<Record<string, string>>;
152
155
  fetch?: FetchFunction;
153
156
  }
154
157
 
155
- const headers = {
156
- ...(options.apiKey && { Authorization: `Bearer ${options.apiKey}` }),
157
- ...options.headers,
158
- };
158
+ const configuredApiKey = options.apiKey;
159
+ const headers = { ...options.headers };
159
160
 
160
161
  const getHeaders = () =>
161
- withUserAgentSuffix(headers, `ai-sdk-openai-compatible/${VERSION}`);
162
+ resolveWith(configuredApiKey, apiKey =>
163
+ withUserAgentSuffix(
164
+ { ...(apiKey && { Authorization: `Bearer ${apiKey}` }), ...headers },
165
+ `ai-sdk-openai-compatible/${VERSION}`,
166
+ ),
167
+ );
162
168
 
163
169
  const getCommonModelConfig = (modelType: string): CommonModelConfig => ({
164
170
  provider: `${providerName}.${modelType}`,