@ai-sdk/openai
Version:
The **[OpenAI provider](https://ai-sdk.dev/providers/ai-sdk-providers/openai)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the OpenAI chat and completion APIs and embedding model support for the OpenAI embeddings API.
1,420 lines (1,402 loc) • 284 kB
JavaScript
// src/chat/openai-chat-language-model.ts
import {
StreamingToolCallTracker,
combineHeaders,
createEventSourceResponseHandler,
createJsonResponseHandler,
generateId,
isCustomReasoning,
parseProviderOptions,
postJsonToApi,
serializeModelOptions,
WORKFLOW_DESERIALIZE,
WORKFLOW_SERIALIZE
} from "@ai-sdk/provider-utils";
// src/openai-error.ts
import { z } from "zod/v4";
import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils";
var openaiErrorDataSchema = z.object({
error: z.object({
message: z.string(),
// The additional information below is handled loosely to support
// OpenAI-compatible providers that have slightly different error
// responses:
type: z.string().nullish(),
param: z.any().nullish(),
code: z.union([z.string(), z.number()]).nullish()
})
});
var openaiFailedResponseHandler = createJsonErrorResponseHandler({
errorSchema: openaiErrorDataSchema,
errorToMessage: (data) => data.error.message
});
// src/openai-language-model-capabilities.ts
function getOpenAILanguageModelCapabilities(modelId) {
const supportsFlexProcessing = modelId.startsWith("o3") || modelId.startsWith("o4-mini") || modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat");
const supportsPriorityProcessing = modelId.startsWith("gpt-4") || modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-nano") && !modelId.startsWith("gpt-5-chat") && !modelId.startsWith("gpt-5.4-nano") || modelId.startsWith("o3") || modelId.startsWith("o4-mini");
const isReasoningModel = modelId.startsWith("o1") || modelId.startsWith("o3") || modelId.startsWith("o4-mini") || modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat");
const supportsNonReasoningParameters = modelId.startsWith("gpt-5.1") || modelId.startsWith("gpt-5.2") || modelId.startsWith("gpt-5.3") || modelId.startsWith("gpt-5.4") || modelId.startsWith("gpt-5.5") || modelId.startsWith("gpt-5.6");
const systemMessageMode = isReasoningModel ? "developer" : "system";
return {
supportsFlexProcessing,
supportsPriorityProcessing,
isReasoningModel,
systemMessageMode,
supportsNonReasoningParameters
};
}
// src/openai-stream-error.ts
import { APICallError } from "@ai-sdk/provider";
async function throwIfOpenAIStreamErrorBeforeOutput({
stream,
getError,
isOutputChunk,
url,
requestBodyValues,
responseHeaders
}) {
const [streamForEarlyError, streamForConsumer] = stream.tee();
const reader = streamForEarlyError.getReader();
try {
while (true) {
const result = await reader.read();
if (result.done) {
return streamForConsumer;
}
const chunk = result.value;
if (!chunk.success) {
return streamForConsumer;
}
const errorFrame = getError(chunk.value);
if (errorFrame != null) {
streamForConsumer.cancel().catch(() => {
});
throw createOpenAIStreamError({
frame: errorFrame,
url,
requestBodyValues,
responseHeaders
});
}
if (isOutputChunk(chunk.value)) {
return streamForConsumer;
}
}
} finally {
reader.cancel().catch(() => {
});
reader.releaseLock();
}
}
function createOpenAIStreamError({
frame,
url,
requestBodyValues,
responseHeaders
}) {
var _a;
const streamError = parseStreamError(frame);
return new APICallError({
message: (_a = streamError == null ? void 0 : streamError.message) != null ? _a : "OpenAI stream failed before any output was generated",
url,
requestBodyValues,
statusCode: streamError == null ? 500 : getStatusCode(streamError),
responseHeaders,
responseBody: JSON.stringify(frame),
data: frame
});
}
function parseStreamError(frame) {
var _a;
const value = asRecord(frame);
if (value == null) {
return void 0;
}
if (value.type === "response.failed") {
const response = asRecord(value.response);
const responseError = asRecord(response == null ? void 0 : response.error);
return typeof (responseError == null ? void 0 : responseError.message) === "string" ? {
message: responseError.message,
code: getStringOrNumber(responseError.code),
type: "response.failed",
frame
} : void 0;
}
const error = (_a = asRecord(value.error)) != null ? _a : value;
return typeof error.message === "string" && (asRecord(value.error) != null || typeof error.type === "string" || "code" in error || "param" in error) ? {
message: error.message,
code: getStringOrNumber(error.code),
type: typeof error.type === "string" ? error.type : void 0,
frame
} : void 0;
}
function getStatusCode(error) {
if (typeof error.code === "number" && isHttpErrorStatusCode(error.code)) {
return error.code;
}
if (typeof error.code === "string" && /^\d{3}$/.test(error.code)) {
const numericCode = Number(error.code);
if (isHttpErrorStatusCode(numericCode)) {
return numericCode;
}
}
const discriminator = [error.code, error.type].filter((value) => typeof value === "string" || typeof value === "number").join(" ").toLowerCase();
if (["insufficient_quota", "rate_limit"].some(
(term) => discriminator.includes(term)
)) {
return 429;
}
if (discriminator.includes("authentication")) return 401;
if (discriminator.includes("permission")) return 403;
if (discriminator.includes("not_found")) return 404;
if (["invalid", "bad_request", "context_length"].some(
(term) => discriminator.includes(term)
)) {
return 400;
}
if (discriminator.includes("overload")) return 503;
if (discriminator.includes("timeout")) return 504;
return 500;
}
function asRecord(value) {
return typeof value === "object" && value != null ? value : void 0;
}
function getStringOrNumber(value) {
return typeof value === "string" || typeof value === "number" ? value : void 0;
}
function isHttpErrorStatusCode(value) {
return Number.isInteger(value) && value >= 400 && value <= 599;
}
// src/chat/convert-openai-chat-usage.ts
function convertOpenAIChatUsage(usage) {
var _a, _b, _c, _d, _e, _f, _g, _h;
if (usage == null) {
return {
inputTokens: {
total: void 0,
noCache: void 0,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: void 0,
text: void 0,
reasoning: void 0
},
raw: void 0
};
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
const cachedTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
const cacheWriteTokens = (_f = (_e = usage.prompt_tokens_details) == null ? void 0 : _e.cache_write_tokens) != null ? _f : void 0;
const reasoningTokens = (_h = (_g = usage.completion_tokens_details) == null ? void 0 : _g.reasoning_tokens) != null ? _h : 0;
return {
inputTokens: {
total: promptTokens,
noCache: promptTokens - cachedTokens - (cacheWriteTokens != null ? cacheWriteTokens : 0),
cacheRead: cachedTokens,
cacheWrite: cacheWriteTokens
},
outputTokens: {
total: completionTokens,
text: completionTokens - reasoningTokens,
reasoning: reasoningTokens
},
raw: usage
};
}
// src/chat/convert-to-openai-chat-messages.ts
import {
UnsupportedFunctionalityError
} from "@ai-sdk/provider";
import {
convertToBase64,
getTopLevelMediaType,
resolveFullMediaType,
resolveProviderReference
} from "@ai-sdk/provider-utils";
function serializeToolCallArguments(input) {
return JSON.stringify(input === void 0 ? {} : input);
}
function getPromptCacheBreakpoint(providerOptions) {
var _a;
return (_a = providerOptions == null ? void 0 : providerOptions.openai) == null ? void 0 : _a.promptCacheBreakpoint;
}
function convertToOpenAIChatMessages({
prompt,
systemMessageMode = "system"
}) {
var _a, _b;
const messages = [];
const warnings = [];
for (const { role, content, providerOptions } of prompt) {
switch (role) {
case "system": {
switch (systemMessageMode) {
case "system": {
const promptCacheBreakpoint = getPromptCacheBreakpoint(providerOptions);
messages.push({
role: "system",
content: promptCacheBreakpoint == null ? content : [
{
type: "text",
text: content,
prompt_cache_breakpoint: promptCacheBreakpoint
}
]
});
break;
}
case "developer": {
const promptCacheBreakpoint = getPromptCacheBreakpoint(providerOptions);
messages.push({
role: "developer",
content: promptCacheBreakpoint == null ? content : [
{
type: "text",
text: content,
prompt_cache_breakpoint: promptCacheBreakpoint
}
]
});
break;
}
case "remove": {
warnings.push({
type: "other",
message: "system messages are removed for this model"
});
break;
}
default: {
const _exhaustiveCheck = systemMessageMode;
throw new Error(
`Unsupported system message mode: ${_exhaustiveCheck}`
);
}
}
break;
}
case "user": {
if (content.length === 1 && content[0].type === "text" && getPromptCacheBreakpoint(content[0].providerOptions) == null) {
messages.push({ role: "user", content: content[0].text });
break;
}
messages.push({
role: "user",
content: content.map((part, index) => {
var _a2, _b2, _c;
switch (part.type) {
case "text": {
const promptCacheBreakpoint = getPromptCacheBreakpoint(
part.providerOptions
);
return {
type: "text",
text: part.text,
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
}
case "file": {
const promptCacheBreakpoint = getPromptCacheBreakpoint(
part.providerOptions
);
switch (part.data.type) {
case "reference": {
return {
type: "file",
file: {
file_id: resolveProviderReference({
reference: part.data.reference,
provider: "openai"
})
},
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
}
case "text": {
throw new UnsupportedFunctionalityError({
functionality: "text file parts"
});
}
case "url":
case "data": {
const topLevel = getTopLevelMediaType(part.mediaType);
if (topLevel === "image") {
return {
type: "image_url",
image_url: {
url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
detail: (_b2 = (_a2 = part.providerOptions) == null ? void 0 : _a2.openai) == null ? void 0 : _b2.imageDetail
},
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
} else if (topLevel === "audio") {
if (part.data.type === "url") {
throw new UnsupportedFunctionalityError({
functionality: "audio file parts with URLs"
});
}
const fullMediaType = resolveFullMediaType({ part });
switch (fullMediaType) {
case "audio/wav": {
return {
type: "input_audio",
input_audio: {
data: convertToBase64(part.data.data),
format: "wav"
},
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
}
case "audio/mp3":
case "audio/mpeg": {
return {
type: "input_audio",
input_audio: {
data: convertToBase64(part.data.data),
format: "mp3"
},
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
}
default: {
throw new UnsupportedFunctionalityError({
functionality: `audio content parts with media type ${fullMediaType}`
});
}
}
}
{
const fullMediaType = resolveFullMediaType({ part });
if (fullMediaType !== "application/pdf") {
throw new UnsupportedFunctionalityError({
functionality: `file part media type ${fullMediaType}`
});
}
if (part.data.type === "url") {
throw new UnsupportedFunctionalityError({
functionality: "PDF file parts with URLs"
});
}
return {
type: "file",
file: {
filename: (_c = part.filename) != null ? _c : `part-${index}.pdf`,
file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
},
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
};
}
}
}
}
}
})
});
break;
}
case "assistant": {
let text = "";
const textParts = [];
let hasPromptCacheBreakpoint = false;
const toolCalls = [];
for (const part of content) {
switch (part.type) {
case "text": {
const promptCacheBreakpoint = getPromptCacheBreakpoint(
part.providerOptions
);
text += part.text;
textParts.push({
type: "text",
text: part.text,
...promptCacheBreakpoint != null && {
prompt_cache_breakpoint: promptCacheBreakpoint
}
});
hasPromptCacheBreakpoint || (hasPromptCacheBreakpoint = promptCacheBreakpoint != null);
break;
}
case "tool-call": {
toolCalls.push({
id: part.toolCallId,
type: "function",
function: {
name: part.toolName,
arguments: serializeToolCallArguments(part.input)
}
});
break;
}
}
}
messages.push({
role: "assistant",
content: hasPromptCacheBreakpoint ? textParts : toolCalls.length > 0 ? text || null : text,
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
});
break;
}
case "tool": {
for (const toolResponse of content) {
if (toolResponse.type === "tool-approval-response") {
continue;
}
const output = toolResponse.output;
const promptCacheBreakpoint = (_a = output.type === "content" ? output.value.map((part) => getPromptCacheBreakpoint(part.providerOptions)).find((breakpoint) => breakpoint != null) : getPromptCacheBreakpoint(output.providerOptions)) != null ? _a : getPromptCacheBreakpoint(toolResponse.providerOptions);
let contentValue;
switch (output.type) {
case "text":
case "error-text":
contentValue = output.value;
break;
case "execution-denied":
contentValue = (_b = output.reason) != null ? _b : "Tool call execution denied.";
break;
case "content":
case "json":
case "error-json":
contentValue = JSON.stringify(output.value);
break;
}
messages.push({
role: "tool",
tool_call_id: toolResponse.toolCallId,
content: promptCacheBreakpoint == null ? contentValue : [
{
type: "text",
text: contentValue,
prompt_cache_breakpoint: promptCacheBreakpoint
}
]
});
}
break;
}
default: {
const _exhaustiveCheck = role;
throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
}
}
}
return { messages, warnings };
}
// src/chat/get-response-metadata.ts
function getResponseMetadata({
id,
model,
created
}) {
return {
id: id != null ? id : void 0,
modelId: model != null ? model : void 0,
timestamp: created ? new Date(created * 1e3) : void 0
};
}
// src/chat/map-openai-finish-reason.ts
function mapOpenAIFinishReason(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 "other";
}
}
// src/chat/openai-chat-api.ts
import {
lazySchema,
zodSchema
} from "@ai-sdk/provider-utils";
import { z as z2 } from "zod/v4";
var openaiChatResponseSchema = lazySchema(
() => zodSchema(
z2.object({
id: z2.string().nullish(),
created: z2.number().nullish(),
model: z2.string().nullish(),
choices: z2.array(
z2.object({
message: z2.object({
role: z2.literal("assistant").nullish(),
content: z2.string().nullish(),
tool_calls: z2.array(
z2.object({
id: z2.string().nullish(),
type: z2.literal("function"),
function: z2.object({
name: z2.string(),
arguments: z2.string()
})
})
).nullish(),
annotations: z2.array(
z2.object({
type: z2.literal("url_citation"),
url_citation: z2.object({
start_index: z2.number(),
end_index: z2.number(),
url: z2.string(),
title: z2.string()
})
})
).nullish()
}),
index: z2.number(),
logprobs: z2.object({
content: z2.array(
z2.object({
token: z2.string(),
logprob: z2.number(),
top_logprobs: z2.array(
z2.object({
token: z2.string(),
logprob: z2.number()
})
)
})
).nullish()
}).nullish(),
finish_reason: z2.string().nullish()
})
),
usage: z2.object({
prompt_tokens: z2.number().nullish(),
completion_tokens: z2.number().nullish(),
total_tokens: z2.number().nullish(),
prompt_tokens_details: z2.object({
cached_tokens: z2.number().nullish(),
cache_write_tokens: z2.number().nullish()
}).nullish(),
completion_tokens_details: z2.object({
reasoning_tokens: z2.number().nullish(),
accepted_prediction_tokens: z2.number().nullish(),
rejected_prediction_tokens: z2.number().nullish()
}).nullish()
}).nullish()
})
)
);
var openaiChatChunkSchema = lazySchema(
() => zodSchema(
z2.union([
z2.object({
id: z2.string().nullish(),
created: z2.number().nullish(),
model: z2.string().nullish(),
choices: z2.array(
z2.object({
delta: z2.object({
role: z2.enum(["assistant"]).nullish(),
content: z2.string().nullish(),
tool_calls: z2.array(
z2.object({
index: z2.number(),
id: z2.string().nullish(),
type: z2.literal("function").nullish(),
function: z2.object({
name: z2.string().nullish(),
arguments: z2.string().nullish()
})
})
).nullish(),
annotations: z2.array(
z2.object({
type: z2.literal("url_citation"),
url_citation: z2.object({
start_index: z2.number(),
end_index: z2.number(),
url: z2.string(),
title: z2.string()
})
})
).nullish()
}).nullish(),
logprobs: z2.object({
content: z2.array(
z2.object({
token: z2.string(),
logprob: z2.number(),
top_logprobs: z2.array(
z2.object({
token: z2.string(),
logprob: z2.number()
})
)
})
).nullish()
}).nullish(),
finish_reason: z2.string().nullish(),
index: z2.number()
})
),
usage: z2.object({
prompt_tokens: z2.number().nullish(),
completion_tokens: z2.number().nullish(),
total_tokens: z2.number().nullish(),
prompt_tokens_details: z2.object({
cached_tokens: z2.number().nullish(),
cache_write_tokens: z2.number().nullish()
}).nullish(),
completion_tokens_details: z2.object({
reasoning_tokens: z2.number().nullish(),
accepted_prediction_tokens: z2.number().nullish(),
rejected_prediction_tokens: z2.number().nullish()
}).nullish()
}).nullish()
}),
openaiErrorDataSchema
])
)
);
// src/chat/openai-chat-language-model-options.ts
import {
lazySchema as lazySchema2,
zodSchema as zodSchema2
} from "@ai-sdk/provider-utils";
import { z as z3 } from "zod/v4";
var openaiLanguageModelChatOptions = lazySchema2(
() => zodSchema2(
z3.object({
/**
* Modify the likelihood of specified tokens appearing in the completion.
*
* Accepts a JSON object that maps tokens (specified by their token ID in
* the GPT tokenizer) to an associated bias value from -100 to 100.
*/
logitBias: z3.record(z3.coerce.number(), z3.number()).optional(),
/**
* Return the log probabilities of the tokens.
*
* Setting to true will return the log probabilities of the tokens that
* were generated.
*
* Setting to a number will return the log probabilities of the top n
* tokens that were generated.
*/
logprobs: z3.union([z3.boolean(), z3.number()]).optional(),
/**
* Whether to enable parallel function calling during tool use. Default to true.
*/
parallelToolCalls: z3.boolean().optional(),
/**
* A unique identifier representing your end-user, which can help OpenAI to
* monitor and detect abuse.
*/
user: z3.string().optional(),
/**
* Reasoning effort for reasoning models. Defaults to `medium`.
*/
reasoningEffort: z3.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional(),
/**
* Maximum number of completion tokens to generate. Useful for reasoning models.
*/
maxCompletionTokens: z3.number().optional(),
/**
* Whether to enable persistence in responses API.
*/
store: z3.boolean().optional(),
/**
* Metadata to associate with the request.
*/
metadata: z3.record(z3.string().max(64), z3.string().max(512)).optional(),
/**
* Parameters for prediction mode.
*/
prediction: z3.record(z3.string(), z3.any()).optional(),
/**
* Service tier for the request.
* - 'auto': Default service tier. The request will be processed with the service tier configured in the
* Project settings. Unless otherwise configured, the Project will use 'default'.
* - 'flex': 50% cheaper processing at the cost of increased latency. Only available for o3 and o4-mini models.
* - 'priority': Higher-speed processing with predictably low latency at premium cost. Available for Enterprise customers.
* - 'default': The request will be processed with the standard pricing and performance for the selected model.
*
* @default 'auto'
*/
serviceTier: z3.enum(["auto", "flex", "priority", "default"]).optional(),
/**
* Whether to use strict JSON schema validation.
*
* @default true
*/
strictJsonSchema: z3.boolean().optional(),
/**
* Controls the verbosity of the model's responses.
* Lower values will result in more concise responses, while higher values will result in more verbose responses.
*/
textVerbosity: z3.enum(["low", "medium", "high"]).optional(),
/**
* A cache key for prompt caching. Allows manual control over prompt caching behavior.
* Useful for improving cache hit rates and working around automatic caching issues.
*/
promptCacheKey: z3.string().optional(),
/**
* Prompt cache behavior for GPT-5.6 and later models.
* `mode` controls whether OpenAI also places an implicit breakpoint.
* `ttl` sets the minimum cache lifetime and currently only supports 30 minutes.
*/
promptCacheOptions: z3.object({
mode: z3.enum(["implicit", "explicit"]).optional(),
ttl: z3.literal("30m").optional()
}).optional(),
/**
* The retention policy for the prompt cache.
* - 'in_memory': Default. Standard prompt caching behavior.
* - '24h': Extended prompt caching that keeps cached prefixes active for up to 24 hours.
* Available for models before GPT-5.6 that support extended caching.
*
* @deprecated For GPT-5.6 and later models, use `promptCacheOptions.ttl`.
*
* @default 'in_memory'
*/
promptCacheRetention: z3.enum(["in_memory", "24h"]).optional(),
/**
* A stable identifier used to help detect users of your application
* that may be violating OpenAI's usage policies. The IDs should be a
* string that uniquely identifies each user. We recommend hashing their
* username or email address, in order to avoid sending us any identifying
* information.
*/
safetyIdentifier: z3.string().optional(),
/**
* Override the system message mode for this model.
* - 'system': Use the 'system' role for system messages (default for most models)
* - 'developer': Use the 'developer' role for system messages (used by reasoning models)
* - 'remove': Remove system messages entirely
*
* If not specified, the mode is automatically determined based on the model.
*/
systemMessageMode: z3.enum(["system", "developer", "remove"]).optional(),
/**
* Force treating this model as a reasoning model.
*
* This is useful for "stealth" reasoning models (e.g. via a custom baseURL)
* where the model ID is not recognized by the SDK's allowlist.
*
* When enabled, the SDK applies reasoning-model parameter compatibility rules
* and defaults `systemMessageMode` to `developer` unless overridden.
*/
forceReasoning: z3.boolean().optional()
})
)
);
// src/chat/openai-chat-prepare-tools.ts
import {
UnsupportedFunctionalityError as UnsupportedFunctionalityError2
} from "@ai-sdk/provider";
function prepareChatTools({
tools,
toolChoice
}) {
tools = (tools == null ? void 0 : tools.length) ? tools : void 0;
const toolWarnings = [];
if (tools == null) {
return { tools: void 0, toolChoice: void 0, toolWarnings };
}
const openaiTools = [];
for (const tool of tools) {
switch (tool.type) {
case "function":
openaiTools.push({
type: "function",
function: {
name: tool.name,
description: tool.description,
parameters: tool.inputSchema,
...tool.strict != null ? { strict: tool.strict } : {}
}
});
break;
default:
toolWarnings.push({
type: "unsupported",
feature: `tool type: ${tool.type}`
});
break;
}
}
if (toolChoice == null) {
return { tools: openaiTools, toolChoice: void 0, toolWarnings };
}
const type = toolChoice.type;
switch (type) {
case "auto":
case "none":
case "required":
return { tools: openaiTools, toolChoice: type, toolWarnings };
case "tool":
return {
tools: openaiTools,
toolChoice: {
type: "function",
function: {
name: toolChoice.toolName
}
},
toolWarnings
};
default: {
const _exhaustiveCheck = type;
throw new UnsupportedFunctionalityError2({
functionality: `tool choice type: ${_exhaustiveCheck}`
});
}
}
}
// src/chat/openai-chat-language-model.ts
var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
constructor(modelId, config) {
this.specificationVersion = "v4";
this.supportedUrls = {
"image/*": [/^https?:\/\/.*$/]
};
this.modelId = modelId;
this.config = config;
}
static [WORKFLOW_SERIALIZE](model) {
return serializeModelOptions({
modelId: model.modelId,
config: model.config
});
}
static [WORKFLOW_DESERIALIZE](options) {
return new _OpenAIChatLanguageModel(options.modelId, options.config);
}
get provider() {
return this.config.provider;
}
async getArgs({
prompt,
maxOutputTokens,
temperature,
topP,
topK,
frequencyPenalty,
presencePenalty,
stopSequences,
responseFormat,
seed,
tools,
toolChoice,
reasoning,
providerOptions
}) {
var _a, _b, _c, _d, _e, _f;
const warnings = [];
const openaiOptions = (_a = await parseProviderOptions({
provider: "openai",
providerOptions,
schema: openaiLanguageModelChatOptions
})) != null ? _a : {};
const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
const resolvedReasoningEffort = (_b = openaiOptions.reasoningEffort) != null ? _b : isCustomReasoning(reasoning) ? reasoning : void 0;
const isReasoningModel = (_c = openaiOptions.forceReasoning) != null ? _c : modelCapabilities.isReasoningModel;
if (topK != null) {
warnings.push({ type: "unsupported", feature: "topK" });
}
const { messages, warnings: messageWarnings } = convertToOpenAIChatMessages(
{
prompt,
systemMessageMode: (_d = openaiOptions.systemMessageMode) != null ? _d : isReasoningModel ? "developer" : modelCapabilities.systemMessageMode
}
);
warnings.push(...messageWarnings);
const strictJsonSchema = (_e = openaiOptions.strictJsonSchema) != null ? _e : true;
const baseArgs = {
// model id:
model: this.modelId,
// model specific settings:
logit_bias: openaiOptions.logitBias,
logprobs: openaiOptions.logprobs === true || typeof openaiOptions.logprobs === "number" ? true : void 0,
top_logprobs: typeof openaiOptions.logprobs === "number" ? openaiOptions.logprobs : typeof openaiOptions.logprobs === "boolean" ? openaiOptions.logprobs ? 0 : void 0 : void 0,
user: openaiOptions.user,
parallel_tool_calls: openaiOptions.parallelToolCalls,
// standardized settings:
max_tokens: maxOutputTokens,
temperature,
top_p: topP,
frequency_penalty: frequencyPenalty,
presence_penalty: presencePenalty,
response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? responseFormat.schema != null ? {
type: "json_schema",
json_schema: {
schema: responseFormat.schema,
strict: strictJsonSchema,
name: (_f = responseFormat.name) != null ? _f : "response",
description: responseFormat.description
}
} : { type: "json_object" } : void 0,
stop: stopSequences,
seed,
verbosity: openaiOptions.textVerbosity,
// openai specific settings:
// TODO AI SDK 6: remove, we auto-map maxOutputTokens now
max_completion_tokens: openaiOptions.maxCompletionTokens,
store: openaiOptions.store,
metadata: openaiOptions.metadata,
prediction: openaiOptions.prediction,
reasoning_effort: resolvedReasoningEffort,
service_tier: openaiOptions.serviceTier,
prompt_cache_key: openaiOptions.promptCacheKey,
prompt_cache_options: openaiOptions.promptCacheOptions,
prompt_cache_retention: openaiOptions.promptCacheRetention,
safety_identifier: openaiOptions.safetyIdentifier,
// messages:
messages
};
if (isReasoningModel) {
if (resolvedReasoningEffort !== "none" || !modelCapabilities.supportsNonReasoningParameters) {
if (baseArgs.temperature != null) {
baseArgs.temperature = void 0;
warnings.push({
type: "unsupported",
feature: "temperature",
details: "temperature is not supported for reasoning models"
});
}
if (baseArgs.top_p != null) {
baseArgs.top_p = void 0;
warnings.push({
type: "unsupported",
feature: "topP",
details: "topP is not supported for reasoning models"
});
}
if (baseArgs.logprobs != null) {
baseArgs.logprobs = void 0;
warnings.push({
type: "other",
message: "logprobs is not supported for reasoning models"
});
}
}
if (baseArgs.frequency_penalty != null) {
baseArgs.frequency_penalty = void 0;
warnings.push({
type: "unsupported",
feature: "frequencyPenalty",
details: "frequencyPenalty is not supported for reasoning models"
});
}
if (baseArgs.presence_penalty != null) {
baseArgs.presence_penalty = void 0;
warnings.push({
type: "unsupported",
feature: "presencePenalty",
details: "presencePenalty is not supported for reasoning models"
});
}
if (baseArgs.logit_bias != null) {
baseArgs.logit_bias = void 0;
warnings.push({
type: "other",
message: "logitBias is not supported for reasoning models"
});
}
if (baseArgs.top_logprobs != null) {
baseArgs.top_logprobs = void 0;
warnings.push({
type: "other",
message: "topLogprobs is not supported for reasoning models"
});
}
if (baseArgs.max_tokens != null) {
if (baseArgs.max_completion_tokens == null) {
baseArgs.max_completion_tokens = baseArgs.max_tokens;
}
baseArgs.max_tokens = void 0;
}
} else if (this.modelId.startsWith("gpt-4o-search-preview") || this.modelId.startsWith("gpt-4o-mini-search-preview")) {
if (baseArgs.temperature != null) {
baseArgs.temperature = void 0;
warnings.push({
type: "unsupported",
feature: "temperature",
details: "temperature is not supported for the search preview models and has been removed."
});
}
}
if (openaiOptions.serviceTier === "flex" && !modelCapabilities.supportsFlexProcessing) {
warnings.push({
type: "unsupported",
feature: "serviceTier",
details: "flex processing is only available for o3, o4-mini, and gpt-5 models"
});
baseArgs.service_tier = void 0;
}
if (openaiOptions.serviceTier === "priority" && !modelCapabilities.supportsPriorityProcessing) {
warnings.push({
type: "unsupported",
feature: "serviceTier",
details: "priority processing is only available for supported models (gpt-4, gpt-5, gpt-5-mini, o3, o4-mini) and requires Enterprise access. gpt-5-nano is not supported"
});
baseArgs.service_tier = void 0;
}
const {
tools: openaiTools,
toolChoice: openaiToolChoice,
toolWarnings
} = prepareChatTools({
tools,
toolChoice
});
return {
args: {
...baseArgs,
tools: openaiTools,
tool_choice: openaiToolChoice
},
warnings: [...warnings, ...toolWarnings]
};
}
async doGenerate(options) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const { args: body, warnings } = await this.getArgs(options);
const {
responseHeaders,
value: response,
rawValue: rawResponse
} = await postJsonToApi({
url: this.config.url({
path: "/chat/completions",
modelId: this.modelId
}),
headers: combineHeaders((_b = (_a = this.config).headers) == null ? void 0 : _b.call(_a), options.headers),
body,
failedResponseHandler: openaiFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler(
openaiChatResponseSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const choice = response.choices[0];
const content = [];
const text = choice.message.content;
if (text != null && text.length > 0) {
content.push({ type: "text", text });
}
for (const toolCall of (_c = choice.message.tool_calls) != null ? _c : []) {
content.push({
type: "tool-call",
toolCallId: (_d = toolCall.id) != null ? _d : generateId(),
toolName: toolCall.function.name,
input: toolCall.function.arguments
});
}
for (const annotation of (_e = choice.message.annotations) != null ? _e : []) {
content.push({
type: "source",
sourceType: "url",
id: generateId(),
url: annotation.url_citation.url,
title: annotation.url_citation.title
});
}
const completionTokenDetails = (_f = response.usage) == null ? void 0 : _f.completion_tokens_details;
const providerMetadata = { openai: {} };
if ((completionTokenDetails == null ? void 0 : completionTokenDetails.accepted_prediction_tokens) != null) {
providerMetadata.openai.acceptedPredictionTokens = completionTokenDetails == null ? void 0 : completionTokenDetails.accepted_prediction_tokens;
}
if ((completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens) != null) {
providerMetadata.openai.rejectedPredictionTokens = completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens;
}
if (((_g = choice.logprobs) == null ? void 0 : _g.content) != null) {
providerMetadata.openai.logprobs = choice.logprobs.content;
}
return {
content,
finishReason: {
unified: mapOpenAIFinishReason(choice.finish_reason),
raw: (_h = choice.finish_reason) != null ? _h : void 0
},
usage: convertOpenAIChatUsage(response.usage),
request: { body },
response: {
...getResponseMetadata(response),
headers: responseHeaders,
body: rawResponse
},
warnings,
providerMetadata
};
}
async doStream(options) {
var _a, _b;
const { args, warnings } = await this.getArgs(options);
const body = {
...args,
stream: true,
stream_options: {
include_usage: true
}
};
const url = this.config.url({
path: "/chat/completions",
modelId: this.modelId
});
const { responseHeaders, value: response } = await postJsonToApi({
url,
headers: combineHeaders((_b = (_a = this.config).headers) == null ? void 0 : _b.call(_a), options.headers),
body,
failedResponseHandler: openaiFailedResponseHandler,
successfulResponseHandler: createEventSourceResponseHandler(
openaiChatChunkSchema
),
abortSignal: options.abortSignal,
fetch: this.config.fetch
});
const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
stream: response,
getError: (chunk) => "error" in chunk ? chunk.error : void 0,
isOutputChunk: isOpenAIChatOutputChunk,
url,
requestBodyValues: body,
responseHeaders
});
let toolCallTracker;
let finishReason = {
unified: "other",
raw: void 0
};
let usage = void 0;
let metadataExtracted = false;
let isActiveText = false;
const providerMetadata = { openai: {} };
const result = {
stream: checkedResponse.pipeThrough(
new TransformStream({
start(controller) {
toolCallTracker = new StreamingToolCallTracker(controller, {
generateId,
typeValidation: "if-present"
});
controller.enqueue({ type: "stream-start", warnings });
},
transform(chunk, controller) {
var _a2, _b2, _c, _d, _e;
if (options.includeRawChunks) {
controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
}
if (!chunk.success) {
finishReason = { unified: "error", raw: void 0 };
controller.enqueue({ type: "error", error: chunk.error });
return;
}
const value = chunk.value;
if ("error" in value) {
finishReason = { unified: "error", raw: void 0 };
controller.enqueue({ type: "error", error: value.error });
return;
}
if (!metadataExtracted) {
const metadata = getResponseMetadata(value);
if (Object.values(metadata).some(Boolean)) {
metadataExtracted = true;
controller.enqueue({
type: "response-metadata",
...getResponseMetadata(value)
});
}
}
if (value.usage != null) {
usage = value.usage;
if (((_a2 = value.usage.completion_tokens_details) == null ? void 0 : _a2.accepted_prediction_tokens) != null) {
providerMetadata.openai.acceptedPredictionTokens = (_b2 = value.usage.completion_tokens_details) == null ? void 0 : _b2.accepted_prediction_tokens;
}
if (((_c = value.usage.completion_tokens_details) == null ? void 0 : _c.rejected_prediction_tokens) != null) {
providerMetadata.openai.rejectedPredictionTokens = (_d = value.usage.completion_tokens_details) == null ? void 0 : _d.rejected_prediction_tokens;
}
}
const choice = value.choices[0];
if ((choice == null ? void 0 : choice.finish_reason) != null) {
finishReason = {
unified: mapOpenAIFinishReason(choice.finish_reason),
raw: choice.finish_reason
};
}
if (((_e = choice == null ? void 0 : choice.logprobs) == null ? void 0 : _e.content) != null) {
providerMetadata.openai.logprobs = choice.logprobs.content;
}
if ((choice == null ? void 0 : choice.delta) == null) {
return;
}
const delta = choice.delta;
if (delta.content != null) {
if (!isActiveText) {
controller.enqueue({ type: "text-start", id: "0" });
isActiveText = true;
}
controller.enqueue({
type: "text-delta",
id: "0",
delta: delta.content
});
}
if (delta.tool_calls != null) {
for (const toolCallDelta of delta.tool_calls) {
toolCallTracker.processDelta(toolCallDelta);
}
}
if (delta.annotations != null) {
for (const annotation of delta.annotations) {
controller.enqueue({
type: "source",
sourceType: "url",
id: generateId(),
url: annotation.url_citation.url,
title: annotation.url_citation.title
});
}
}
},
flush(controller) {
if (isActiveText) {
controller.enqueue({ type: "text-end", id: "0" });
}
toolCallTracker.flush();
controller.enqueue({
type: "finish",
finishReason,
usage: convertOpenAIChatUsage(usage),
...providerMetadata != null ? { providerMetadata } : {}
});
}
})
),
request: { body },
response: { headers: responseHeaders }
};
return result;
}
};
function isOpenAIChatOutputChunk(chunk) {
if ("error" in chunk) {
return false;
}
return chunk.choices.some((choice) => {
const delta = choice.delta;
return (delta == null ? void 0 : delta.content) != null && delta.content.length > 0 || (delta == null ? void 0 : delta.tool_calls) != null && delta.tool_calls.length > 0 || (delta == null ? void 0 : delta.annotations) != null && delta.annotations.length > 0;
});
}
// src/completion/openai-completion-language-model.ts
import {
combineHeaders as combineHeaders2,
createEventSourceResponseHandler as createEventSourceResponseHandler2,
createJsonResponseHandler as createJsonResponseHandler2,
parseProviderOptions as parseProviderOptions2,
postJsonToApi as postJsonToApi2,
serializeModelOptions as serializeModelOptions2,
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2,
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2
} from "@ai-sdk/provider-utils";
// src/completion/convert-openai-completion-usage.ts
function convertOpenAICompletionUsage(usage) {
var _a, _b, _c, _d;
if (usage == null) {
return {
inputTokens: {
total: void 0,
noCache: void 0,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: void 0,
text: void 0,
reasoning: void 0
},
raw: void 0
};
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
return {
inputTokens: {
total: (_c = usage.prompt_tokens) != null ? _c : void 0,
noCache: promptTokens,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: (_d = usage.completion_tokens) != null ? _d : void 0,
text: completionTokens,
reasoning: void 0
},
raw: usage
};
}
// src/completion/convert-to-openai-completion-prompt.ts
import {
InvalidPromptError,
UnsupportedFunctionalityError as UnsupportedFunctionalityError3
} from "@ai-sdk/provider";
function convertToOpenAICompletionPrompt({
prompt,
user = "user",
assistant = "assistant"
}) {
let text = "";
if (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;
}
}
}).filter(Boolean).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 UnsupportedFunctionalityError3({
functionality: "tool-call messages"
});
}
}
}).join("");
text += `${assistant}:
${assistantMessage}
`;