@ai-sdk/openai 4.0.65 → 4.0.67

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.
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod/v4';
2
+
3
+ export type OpenAIRealtimeModelLiveId = 'gpt-live-1' | (string & {});
4
+
5
+ const serverEventSelectorSchema = z
6
+ .strictObject({
7
+ type: z.string(),
8
+ responseEvent: z.string().optional(),
9
+ })
10
+ .refine(
11
+ selector =>
12
+ (selector.type === 'response.event') ===
13
+ (selector.responseEvent !== undefined),
14
+ 'responseEvent is required for response.event and forbidden for other event types.',
15
+ );
16
+
17
+ export const openaiRealtimeModelLiveOptionsSchema = z.strictObject({
18
+ client: z
19
+ .strictObject({
20
+ dataChannel: z.strictObject({
21
+ allowedClientEvents: z
22
+ .union([z.literal('all'), z.array(z.string())])
23
+ .optional(),
24
+ allowedServerEvents: z
25
+ .union([z.literal('all'), z.array(serverEventSelectorSchema)])
26
+ .optional(),
27
+ }),
28
+ })
29
+ .optional(),
30
+ delegation: z
31
+ .strictObject({ type: z.literal('client') })
32
+ .nullable()
33
+ .optional(),
34
+ input: z
35
+ .array(
36
+ z.discriminatedUnion('role', [
37
+ z.strictObject({
38
+ type: z.literal('message'),
39
+ role: z.enum(['developer', 'user']),
40
+ content: z.tuple([
41
+ z.strictObject({ type: z.literal('input_text'), text: z.string() }),
42
+ ]),
43
+ }),
44
+ z.strictObject({
45
+ type: z.literal('message'),
46
+ role: z.literal('assistant'),
47
+ content: z.tuple([
48
+ z.strictObject({
49
+ type: z.enum(['text', 'output_text']),
50
+ text: z.string(),
51
+ }),
52
+ ]),
53
+ }),
54
+ ]),
55
+ )
56
+ .max(128)
57
+ .optional(),
58
+ store: z.boolean().optional(),
59
+ voice: z.strictObject({ id: z.string().min(1) }).optional(),
60
+ });
61
+
62
+ /** Experimental Live options under sessionConfig.providerOptions.openai. */
63
+ export type OpenAIRealtimeModelLiveOptions = z.infer<
64
+ typeof openaiRealtimeModelLiveOptionsSchema
65
+ >;
@@ -0,0 +1,107 @@
1
+ import {
2
+ type Experimental_RealtimeModelV4 as RealtimeModelV4,
3
+ type Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
4
+ type Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
5
+ } from '@ai-sdk/provider';
6
+ import {
7
+ createJsonResponseHandler,
8
+ postJsonToApi,
9
+ } from '@ai-sdk/provider-utils';
10
+ import { z } from 'zod/v4';
11
+ import { openaiFailedResponseHandler } from '../openai-error';
12
+ import {
13
+ createOpenAILiveServerEventParser,
14
+ parseOpenAILiveServerEvent,
15
+ serializeOpenAILiveClientEvent,
16
+ } from './openai-live-event-mapper';
17
+ import type { OpenAIRealtimeModelLiveId } from './openai-realtime-model-live-options';
18
+ import { buildOpenAILiveSessionConfig } from './openai-live-session-config';
19
+ import type { OpenAIRealtimeModelConfig } from '../realtime/openai-realtime-model';
20
+
21
+ export type OpenAIRealtimeModelLiveConfig = OpenAIRealtimeModelConfig;
22
+
23
+ const webRTCSessionSchema = z.object({
24
+ session: z.object({ id: z.string().min(1) }),
25
+ transport: z.object({ type: z.literal('webrtc'), sdp: z.string().min(1) }),
26
+ });
27
+
28
+ export class OpenAIRealtimeModelLive implements RealtimeModelV4 {
29
+ readonly specificationVersion = 'v4' as const;
30
+ readonly capabilities = {
31
+ conversation: 'continuous',
32
+ transports: ['websocket', 'webrtc'],
33
+ connections: ['server-websocket', 'webrtc'],
34
+ startup: 'session-start',
35
+ finalization: 'session-close',
36
+ } as const;
37
+
38
+ constructor(
39
+ readonly modelId: OpenAIRealtimeModelLiveId,
40
+ private readonly config: OpenAIRealtimeModelLiveConfig,
41
+ ) {}
42
+
43
+ get provider(): string {
44
+ return this.config.provider;
45
+ }
46
+
47
+ getWebRTCConfig(): { dataChannelLabel: string } {
48
+ return { dataChannelLabel: 'oai-events' };
49
+ }
50
+
51
+ getServerWebSocketConfig(): { url: string; headers: Record<string, string> } {
52
+ const url = new URL(`${this.config.baseURL}/live/sessions`);
53
+ url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:';
54
+ const headers: Record<string, string> = {};
55
+ for (const [key, value] of Object.entries(this.config.headers())) {
56
+ if (value !== undefined) headers[key] = value;
57
+ }
58
+ return { url: url.toString(), headers };
59
+ }
60
+
61
+ async doCreateWebRTCSession({
62
+ sdp,
63
+ sessionConfig = {},
64
+ abortSignal,
65
+ }: {
66
+ sdp: string;
67
+ sessionConfig?: RealtimeModelV4SessionConfig;
68
+ abortSignal?: AbortSignal;
69
+ }): Promise<{ sessionId: string; sdp: string }> {
70
+ const session = buildOpenAILiveSessionConfig(
71
+ sessionConfig,
72
+ this.modelId,
73
+ 'webrtc',
74
+ );
75
+ const { value } = await postJsonToApi({
76
+ url: `${this.config.baseURL}/live/sessions`,
77
+ headers: this.config.headers(),
78
+ body: {
79
+ session,
80
+ transport: { type: 'webrtc', sdp: z.string().min(1).parse(sdp) },
81
+ },
82
+ failedResponseHandler: openaiFailedResponseHandler,
83
+ successfulResponseHandler: createJsonResponseHandler(webRTCSessionSchema),
84
+ abortSignal,
85
+ fetch: this.config.fetch,
86
+ });
87
+ return { sessionId: value.session.id, sdp: value.transport.sdp };
88
+ }
89
+
90
+ parseServerEvent(raw: unknown) {
91
+ return parseOpenAILiveServerEvent(raw);
92
+ }
93
+
94
+ createServerEventParser() {
95
+ return createOpenAILiveServerEventParser();
96
+ }
97
+
98
+ serializeClientEvent(event: RealtimeModelV4ClientEvent): unknown {
99
+ return serializeOpenAILiveClientEvent(event, this.modelId);
100
+ }
101
+
102
+ buildSessionConfig(
103
+ config: RealtimeModelV4SessionConfig,
104
+ ): Record<string, unknown> {
105
+ return buildOpenAILiveSessionConfig(config, this.modelId);
106
+ }
107
+ }
@@ -0,0 +1,161 @@
1
+ import {
2
+ UnsupportedFunctionalityError,
3
+ type JSONSchema7,
4
+ type JSONSchema7Definition,
5
+ type SharedV4Warning,
6
+ } from '@ai-sdk/provider';
7
+
8
+ /**
9
+ * Normalizes JSON Schema for OpenAI structured outputs.
10
+ *
11
+ * OpenAI does not support the JSON Schema `propertyNames` keyword. Property
12
+ * names in JSON objects are always strings, so string-based constraints can be
13
+ * left to client-side validation after removing the keyword. This
14
+ * compatibility layer does not rewrite non-string property name schemas.
15
+ */
16
+ export function normalizeOpenAIJsonSchema(schema: JSONSchema7): {
17
+ schema: JSONSchema7;
18
+ warnings: SharedV4Warning[];
19
+ } {
20
+ let removedPropertyNames = false;
21
+
22
+ const normalizedSchema = normalizeSchema(schema);
23
+
24
+ return {
25
+ schema: normalizedSchema,
26
+ warnings: removedPropertyNames
27
+ ? [
28
+ {
29
+ type: 'compatibility',
30
+ feature: 'JSON Schema propertyNames',
31
+ details:
32
+ 'OpenAI does not support JSON Schema propertyNames. It was removed before sending the schema, so OpenAI will not enforce property-name constraints.',
33
+ },
34
+ ]
35
+ : [],
36
+ };
37
+
38
+ function normalizeSchema(schema: JSONSchema7): JSONSchema7 {
39
+ const propertyNames = schema.propertyNames;
40
+
41
+ if (propertyNames != null) {
42
+ if (
43
+ typeof propertyNames === 'boolean' ||
44
+ propertyNames.type !== 'string'
45
+ ) {
46
+ throw new UnsupportedFunctionalityError({
47
+ functionality:
48
+ 'JSON Schema propertyNames that does not use a string schema',
49
+ });
50
+ }
51
+
52
+ removedPropertyNames = true;
53
+ }
54
+
55
+ const normalizedSchema = { ...schema };
56
+ delete normalizedSchema.propertyNames;
57
+
58
+ if (normalizedSchema.properties != null) {
59
+ normalizedSchema.properties = normalizeSchemaRecord(
60
+ normalizedSchema.properties,
61
+ );
62
+ }
63
+
64
+ if (normalizedSchema.patternProperties != null) {
65
+ normalizedSchema.patternProperties = normalizeSchemaRecord(
66
+ normalizedSchema.patternProperties,
67
+ );
68
+ }
69
+
70
+ if (normalizedSchema.additionalProperties != null) {
71
+ normalizedSchema.additionalProperties = normalizeDefinition(
72
+ normalizedSchema.additionalProperties,
73
+ );
74
+ }
75
+
76
+ if (normalizedSchema.additionalItems != null) {
77
+ normalizedSchema.additionalItems = normalizeDefinition(
78
+ normalizedSchema.additionalItems,
79
+ );
80
+ }
81
+
82
+ if (normalizedSchema.items != null) {
83
+ normalizedSchema.items = Array.isArray(normalizedSchema.items)
84
+ ? normalizedSchema.items.map(normalizeDefinition)
85
+ : normalizeDefinition(normalizedSchema.items);
86
+ }
87
+
88
+ if (normalizedSchema.contains != null) {
89
+ normalizedSchema.contains = normalizeDefinition(
90
+ normalizedSchema.contains,
91
+ );
92
+ }
93
+
94
+ if (normalizedSchema.not != null) {
95
+ normalizedSchema.not = normalizeDefinition(normalizedSchema.not);
96
+ }
97
+
98
+ if (normalizedSchema.allOf != null) {
99
+ normalizedSchema.allOf = normalizedSchema.allOf.map(normalizeDefinition);
100
+ }
101
+
102
+ if (normalizedSchema.anyOf != null) {
103
+ normalizedSchema.anyOf = normalizedSchema.anyOf.map(normalizeDefinition);
104
+ }
105
+
106
+ if (normalizedSchema.oneOf != null) {
107
+ normalizedSchema.oneOf = normalizedSchema.oneOf.map(normalizeDefinition);
108
+ }
109
+
110
+ if (normalizedSchema.definitions != null) {
111
+ normalizedSchema.definitions = normalizeSchemaRecord(
112
+ normalizedSchema.definitions,
113
+ );
114
+ }
115
+
116
+ if (normalizedSchema.$defs != null) {
117
+ normalizedSchema.$defs = normalizeSchemaRecord(normalizedSchema.$defs);
118
+ }
119
+
120
+ if (normalizedSchema.dependencies != null) {
121
+ normalizedSchema.dependencies = Object.fromEntries(
122
+ Object.entries(normalizedSchema.dependencies).map(
123
+ ([key, dependency]) => [
124
+ key,
125
+ Array.isArray(dependency)
126
+ ? dependency
127
+ : normalizeDefinition(dependency),
128
+ ],
129
+ ),
130
+ );
131
+ }
132
+
133
+ for (const keyword of ['if', 'then', 'else'] as const) {
134
+ const conditionalSchema = normalizedSchema[keyword];
135
+ if (conditionalSchema != null) {
136
+ normalizedSchema[keyword] = normalizeDefinition(conditionalSchema);
137
+ }
138
+ }
139
+
140
+ return normalizedSchema;
141
+ }
142
+
143
+ function normalizeSchemaRecord(
144
+ schemas: Record<string, JSONSchema7Definition>,
145
+ ): Record<string, JSONSchema7Definition> {
146
+ return Object.fromEntries(
147
+ Object.entries(schemas).map(([key, schema]) => [
148
+ key,
149
+ normalizeDefinition(schema),
150
+ ]),
151
+ );
152
+ }
153
+
154
+ function normalizeDefinition(
155
+ definition: JSONSchema7Definition,
156
+ ): JSONSchema7Definition {
157
+ return typeof definition === 'boolean'
158
+ ? definition
159
+ : normalizeSchema(definition);
160
+ }
161
+ }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  InvalidArgumentError,
3
3
  InvalidResponseDataError,
4
+ UnsupportedFunctionalityError,
4
5
  type Experimental_BatchV4 as BatchV4,
5
6
  type Experimental_BatchV4CancelResult as BatchV4CancelResult,
6
7
  type Experimental_BatchV4StartResult as BatchV4StartResult,
@@ -89,6 +90,20 @@ type OpenAIBatchResultConversion =
89
90
  | { success: true; result: LanguageModelV4GenerateResult }
90
91
  | { success: false; error: BatchV4Error };
91
92
 
93
+ function assertTextBatchRequests(
94
+ requests: BatchV4StartOptions['requests'],
95
+ ): asserts requests is ReadonlyArray<OpenAIBatchRequest> {
96
+ for (const request of requests) {
97
+ const requestType = request.type;
98
+ if (requestType !== 'text') {
99
+ throw new UnsupportedFunctionalityError({
100
+ functionality: `batch request type: ${requestType}`,
101
+ message: `The OpenAI Batch API does not support batch requests with type "${requestType}".`,
102
+ });
103
+ }
104
+ }
105
+ }
106
+
92
107
  const openaiBatchResponseZodSchema = () =>
93
108
  z.object({
94
109
  id: z.string(),
@@ -174,6 +189,7 @@ export class OpenAIBatch implements BatchV4<OpenAIBatchModelIds> {
174
189
  async doStartBatch(
175
190
  options: BatchV4StartOptions<OpenAIBatchModelIds>,
176
191
  ): Promise<BatchV4StartResult> {
192
+ assertTextBatchRequests(options.requests);
177
193
  validateSingleModel(options.requests);
178
194
 
179
195
  const fileParts: string[] = [];
@@ -24,6 +24,13 @@ export type OpenAIConfig = {
24
24
  * @see https://github.com/vercel/ai/issues/20180
25
25
  */
26
26
  explicitMessageItemType?: boolean;
27
+ /**
28
+ * Whether the provider supports the
29
+ * `web_search_call.action.sources` Responses API include value.
30
+ *
31
+ * Defaults to `true`.
32
+ */
33
+ supportsWebSearchSourcesInclude?: boolean;
27
34
  /**
28
35
  * This is soft-deprecated. Use provider references (e.g. `{ openai: 'file-abc123' }`)
29
36
  * in file part data instead. File ID prefixes used to identify file IDs
@@ -5,8 +5,6 @@ import type {
5
5
  ImageModelV4,
6
6
  LanguageModelV4,
7
7
  ProviderV4,
8
- Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
9
- Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
10
8
  SpeechModelV4,
11
9
  SkillsV4,
12
10
  TranscriptionModelV4,
@@ -33,7 +31,10 @@ import type { OpenAIImageModelId } from './image/openai-image-model-options';
33
31
  import { openaiTools } from './openai-tools';
34
32
  import { OpenAIBatch } from './openai-batch';
35
33
  import { OpenAIResponsesLanguageModel } from './responses/openai-responses-language-model';
36
- import { OpenAIRealtimeModel } from './realtime/openai-realtime-model';
34
+ import {
35
+ createOpenAIRealtimeFactory,
36
+ type OpenAIRealtimeFactory,
37
+ } from './realtime/openai-realtime-factory';
37
38
  import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
38
39
  import { OpenAISpeechModel } from './speech/openai-speech-model';
39
40
  import type { OpenAISpeechModelId } from './speech/openai-speech-model-options';
@@ -125,7 +126,7 @@ export interface OpenAIProvider extends ProviderV4 {
125
126
  * Creates an experimental realtime model for bidirectional audio/text
126
127
  * communication over WebSocket.
127
128
  */
128
- experimental_realtime: RealtimeFactoryV4;
129
+ experimental_realtime: OpenAIRealtimeFactory;
129
130
 
130
131
  /**
131
132
  * Returns a FilesV4 interface for uploading files to OpenAI.
@@ -337,33 +338,6 @@ export function createOpenAI(
337
338
  },
338
339
  });
339
340
 
340
- const createRealtimeModel = (modelId: string) =>
341
- new OpenAIRealtimeModel(modelId, {
342
- provider: `${providerName}.realtime`,
343
- baseURL,
344
- headers: getHeaders,
345
- fetch: options.fetch,
346
- });
347
-
348
- const experimentalRealtimeFactory = Object.assign(
349
- (modelId: string) => createRealtimeModel(modelId),
350
- {
351
- getToken: async (tokenOptions: RealtimeFactoryV4GetTokenOptions) => {
352
- const model = createRealtimeModel(tokenOptions.model);
353
- const secret = await model.doCreateClientSecret({
354
- sessionConfig: tokenOptions.sessionConfig,
355
- expiresAfterSeconds: tokenOptions.expiresAfterSeconds,
356
- });
357
-
358
- return {
359
- token: secret.token,
360
- url: secret.url,
361
- expiresAt: secret.expiresAt,
362
- };
363
- },
364
- },
365
- ) as RealtimeFactoryV4;
366
-
367
341
  const provider = function (modelId: OpenAIResponsesModelId) {
368
342
  return createLanguageModel(modelId);
369
343
  };
@@ -393,7 +367,12 @@ export function createOpenAI(
393
367
  provider.skills = createSkills;
394
368
  provider.experimental_batch = createBatch;
395
369
 
396
- provider.experimental_realtime = experimentalRealtimeFactory;
370
+ provider.experimental_realtime = createOpenAIRealtimeFactory({
371
+ provider: providerName,
372
+ baseURL,
373
+ headers: getHeaders,
374
+ fetch: options.fetch,
375
+ });
397
376
 
398
377
  provider.tools = openaiTools;
399
378
 
@@ -9,7 +9,11 @@ type OpenAIRealtimeWireEvent = {
9
9
  session?: { id?: string };
10
10
  item?: { id?: string } & Record<string, unknown>;
11
11
  response?: { id?: string; status?: string };
12
- error?: { message?: string; code?: string };
12
+ error?: {
13
+ message?: string | null;
14
+ code?: string | null;
15
+ event_id?: string | null;
16
+ } | null;
13
17
  item_id: string;
14
18
  previous_item_id?: string;
15
19
  response_id: string;
@@ -217,6 +221,7 @@ export function parseOpenAIRealtimeServerEvent(
217
221
  type: 'error',
218
222
  message: event.error?.message ?? event.message ?? 'Unknown error',
219
223
  code: event.error?.code ?? event.code,
224
+ clientEventId: event.error?.event_id ?? undefined,
220
225
  raw,
221
226
  };
222
227
 
@@ -238,12 +243,14 @@ export function serializeOpenAIRealtimeClientEvent(
238
243
  return {
239
244
  type: 'session.update',
240
245
  session: buildOpenAISessionConfig(event.config, modelId),
246
+ ...(event.eventId != null ? { event_id: event.eventId } : {}),
241
247
  };
242
248
 
243
249
  case 'input-audio-append':
244
250
  return {
245
251
  type: 'input_audio_buffer.append',
246
252
  audio: event.audio,
253
+ ...(event.eventId != null ? { event_id: event.eventId } : {}),
247
254
  };
248
255
 
249
256
  case 'input-audio-commit':
@@ -0,0 +1,88 @@
1
+ import {
2
+ InvalidArgumentError,
3
+ UnsupportedFunctionalityError,
4
+ type Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
5
+ type Experimental_RealtimeModelV4 as RealtimeModelV4,
6
+ } from '@ai-sdk/provider';
7
+ import { OpenAIRealtimeModelLive } from '../live/openai-realtime-model-live';
8
+ import {
9
+ OpenAIRealtimeModel,
10
+ type OpenAIRealtimeModelConfig,
11
+ } from './openai-realtime-model';
12
+
13
+ const knownLiveModelIds = ['gpt-live-1'] as const;
14
+
15
+ export type OpenAIRealtimeOptions = {
16
+ /** Overrides model ID routing, including for early-access models. */
17
+ api?: 'live' | 'realtime';
18
+ };
19
+
20
+ export interface OpenAIRealtimeFactory extends RealtimeFactoryV4 {
21
+ (modelId: string, options: { api: 'live' }): OpenAIRealtimeModelLive;
22
+ (modelId: string, options: { api: 'realtime' }): OpenAIRealtimeModel;
23
+ (
24
+ modelId: (typeof knownLiveModelIds)[number],
25
+ options?: { api?: undefined },
26
+ ): OpenAIRealtimeModelLive;
27
+ (modelId: string, options?: OpenAIRealtimeOptions): RealtimeModelV4;
28
+
29
+ getToken(
30
+ options: Parameters<RealtimeFactoryV4['getToken']>[0] &
31
+ OpenAIRealtimeOptions,
32
+ ): ReturnType<RealtimeFactoryV4['getToken']>;
33
+ }
34
+
35
+ function resolveRealtimeApi(
36
+ modelId: string,
37
+ { api }: OpenAIRealtimeOptions = {},
38
+ ): 'live' | 'realtime' {
39
+ if (api !== undefined) {
40
+ if (api !== 'live' && api !== 'realtime') {
41
+ throw new InvalidArgumentError({
42
+ argument: 'api',
43
+ message: 'OpenAI realtime api must be "live" or "realtime".',
44
+ });
45
+ }
46
+ return api;
47
+ }
48
+
49
+ return knownLiveModelIds.some(knownModelId => knownModelId === modelId)
50
+ ? 'live'
51
+ : 'realtime';
52
+ }
53
+
54
+ export function createOpenAIRealtimeFactory(
55
+ config: OpenAIRealtimeModelConfig,
56
+ ): OpenAIRealtimeFactory {
57
+ const createModel = (modelId: string, options?: OpenAIRealtimeOptions) => {
58
+ const api = resolveRealtimeApi(modelId, options);
59
+ const modelConfig = { ...config, provider: `${config.provider}.${api}` };
60
+ return api === 'live'
61
+ ? new OpenAIRealtimeModelLive(modelId, modelConfig)
62
+ : new OpenAIRealtimeModel(modelId, modelConfig);
63
+ };
64
+
65
+ return Object.assign(createModel, {
66
+ getToken: async (
67
+ options: Parameters<OpenAIRealtimeFactory['getToken']>[0],
68
+ ) => {
69
+ const model = createModel(options.model, options);
70
+ if (model instanceof OpenAIRealtimeModelLive) {
71
+ throw new UnsupportedFunctionalityError({
72
+ functionality:
73
+ 'Short-lived OpenAI credentials for the Live API. Use server WebSocket setup via getServerWebSocketConfig() with a server-side API key instead.',
74
+ });
75
+ }
76
+
77
+ const secret = await model.doCreateClientSecret({
78
+ sessionConfig: options.sessionConfig,
79
+ expiresAfterSeconds: options.expiresAfterSeconds,
80
+ });
81
+ return {
82
+ token: secret.token,
83
+ url: secret.url,
84
+ expiresAt: secret.expiresAt,
85
+ };
86
+ },
87
+ }) as OpenAIRealtimeFactory;
88
+ }
@@ -179,6 +179,15 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
179
179
  )
180
180
  .nullish(),
181
181
 
182
+ /**
183
+ * Whether to automatically include web search action sources in the
184
+ * response. Disable this for OpenAI-compatible providers that do not
185
+ * support the `web_search_call.action.sources` include value.
186
+ *
187
+ * Defaults to `true`.
188
+ */
189
+ includeWebSearchSources: z.boolean().optional(),
190
+
182
191
  /**
183
192
  * Instructions for the model.
184
193
  * They can be used to change the system or developer message when continuing a conversation using the `previousResponseId` option.
@@ -70,6 +70,7 @@ import {
70
70
  isUndeclaredParallelToolCall,
71
71
  } from './expand-parallel-tool-call';
72
72
  import { mapOpenAIResponseFinishReason } from './map-openai-responses-finish-reason';
73
+ import { normalizeOpenAIJsonSchema } from '../normalize-openai-json-schema';
73
74
  import {
74
75
  openaiResponsesChunkSchema,
75
76
  openaiResponsesResponseSchema,
@@ -444,6 +445,14 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
444
445
  }
445
446
 
446
447
  const strictJsonSchema = openaiOptions?.strictJsonSchema ?? true;
448
+ const normalizedResponseFormatSchema =
449
+ responseFormat?.type === 'json' && responseFormat.schema != null
450
+ ? normalizeOpenAIJsonSchema(responseFormat.schema)
451
+ : undefined;
452
+
453
+ if (normalizedResponseFormatSchema != null) {
454
+ warnings.push(...normalizedResponseFormatSchema.warnings);
455
+ }
447
456
 
448
457
  let include: OpenAIResponsesIncludeOptions = openaiOptions?.include;
449
458
 
@@ -486,7 +495,11 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
486
495
  ) as LanguageModelV4ProviderTool | undefined
487
496
  )?.name;
488
497
 
489
- if (webSearchToolName) {
498
+ if (
499
+ webSearchToolName &&
500
+ config.supportsWebSearchSourcesInclude !== false &&
501
+ openaiOptions?.includeWebSearchSources !== false
502
+ ) {
490
503
  addInclude('web_search_call.action.sources');
491
504
  }
492
505
 
@@ -513,13 +526,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
513
526
  text: {
514
527
  ...(responseFormat?.type === 'json' && {
515
528
  format:
516
- responseFormat.schema != null
529
+ normalizedResponseFormatSchema != null
517
530
  ? {
518
531
  type: 'json_schema',
519
532
  strict: strictJsonSchema,
520
533
  name: responseFormat.name ?? 'response',
521
534
  description: responseFormat.description,
522
- schema: responseFormat.schema,
535
+ schema: normalizedResponseFormatSchema.schema,
523
536
  }
524
537
  : { type: 'json_object' },
525
538
  }),