@ai-sdk/openai 4.0.106 → 4.0.107

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.
@@ -3543,6 +3543,32 @@ const result = await transcribe({
3543
3543
  });
3544
3544
  ```
3545
3545
 
3546
+ For `gpt-transcribe`, use `languages` for expected languages and `keywords` for
3547
+ names, acronyms, and domain vocabulary. These hints are sent as repeated
3548
+ `languages[]` and `keywords[]` multipart fields:
3549
+
3550
+ ```ts
3551
+ import { transcribe } from 'ai';
3552
+ import { openai, type OpenAITranscriptionModelOptions } from '@ai-sdk/openai';
3553
+
3554
+ const result = await transcribe({
3555
+ model: openai.transcription('gpt-transcribe'),
3556
+ audio: new Uint8Array([1, 2, 3, 4]),
3557
+ providerOptions: {
3558
+ openai: {
3559
+ languages: ['en', 'fr'],
3560
+ keywords: ['premium plan', 'AC-42'],
3561
+ prompt: 'A customer support call.',
3562
+ } satisfies OpenAITranscriptionModelOptions,
3563
+ },
3564
+ });
3565
+ ```
3566
+
3567
+ Use `languages` instead of `language` with `gpt-transcribe`; do not send both.
3568
+ The API validates model support and language codes. These two options currently
3569
+ apply to REST transcription; the provider reports unsupported-option warnings
3570
+ if they are supplied to `experimental_streamTranscribe`.
3571
+
3546
3572
  To get word-level timestamps, specify the granularity:
3547
3573
 
3548
3574
  ```ts highlight="8-9"
@@ -3592,6 +3618,16 @@ The following provider options are available:
3592
3618
  For streaming with `gpt-transcribe` and `gpt-live-transcribe`, it is sent as a single-entry `languages` list.
3593
3619
  Optional.
3594
3620
 
3621
+ - **languages** _string[]_
3622
+ Expected languages for REST transcription, such as `['en', 'fr']` for `gpt-transcribe`.
3623
+ Use this instead of `language`; do not send both. The API validates model support.
3624
+ Optional.
3625
+
3626
+ - **keywords** _string[]_
3627
+ Vocabulary hints for REST transcription, such as product names, acronyms, or technical terms.
3628
+ Supported by `gpt-transcribe`; the API validates support for other models.
3629
+ Optional.
3630
+
3595
3631
  - **prompt** _string_
3596
3632
  An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
3597
3633
  Optional.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.106",
3
+ "version": "4.0.107",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ai-sdk/provider": "4.0.35",
39
- "@ai-sdk/provider-utils": "5.0.72"
39
+ "@ai-sdk/provider-utils": "5.0.73"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -6,6 +6,8 @@ import {
6
6
  } from '@ai-sdk/provider';
7
7
  import type { OpenAIChatPrompt } from './openai-chat-prompt';
8
8
  import {
9
+ secureJsonParse,
10
+ stringifyWithBigIntAndCycles,
9
11
  convertToBase64,
10
12
  getTopLevelMediaType,
11
13
  resolveFullMediaType,
@@ -13,7 +15,22 @@ import {
13
15
  } from '@ai-sdk/provider-utils';
14
16
 
15
17
  function serializeToolCallArguments(input: unknown): string {
16
- return JSON.stringify(
18
+ if (typeof input === 'string') {
19
+ try {
20
+ const parsed = secureJsonParse(input);
21
+ if (
22
+ typeof parsed !== 'object' ||
23
+ parsed === null ||
24
+ Array.isArray(parsed)
25
+ ) {
26
+ return '{}';
27
+ }
28
+ return input;
29
+ } catch {
30
+ return '{}';
31
+ }
32
+ }
33
+ return stringifyWithBigIntAndCycles(
17
34
  typeof input === 'object' && input !== null && !Array.isArray(input)
18
35
  ? input
19
36
  : {},
@@ -326,14 +343,16 @@ export function convertToOpenAIChatMessages({
326
343
  break;
327
344
  case 'error-text':
328
345
  case 'error-json':
329
- contentValue = JSON.stringify({ error: output.value });
346
+ contentValue = stringifyWithBigIntAndCycles({
347
+ error: output.value,
348
+ });
330
349
  break;
331
350
  case 'execution-denied':
332
351
  contentValue = output.reason ?? 'Tool call execution denied.';
333
352
  break;
334
353
  case 'content':
335
354
  case 'json':
336
- contentValue = JSON.stringify(output.value);
355
+ contentValue = stringifyWithBigIntAndCycles(output.value);
337
356
  break;
338
357
  }
339
358
 
@@ -8,6 +8,8 @@ import {
8
8
  type SharedV4Warning,
9
9
  } from '@ai-sdk/provider';
10
10
  import {
11
+ secureJsonParse,
12
+ stringifyWithBigIntAndCycles,
11
13
  lazySchema,
12
14
  zodSchema,
13
15
  type InferSchema,
@@ -62,7 +64,15 @@ import {
62
64
  } from './expand-parallel-tool-call';
63
65
 
64
66
  function serializeToolCallArguments(input: unknown): string {
65
- return JSON.stringify(input === undefined ? {} : input);
67
+ if (typeof input === 'string') {
68
+ try {
69
+ secureJsonParse(input);
70
+ return input;
71
+ } catch {
72
+ return stringifyWithBigIntAndCycles(input);
73
+ }
74
+ }
75
+ return stringifyWithBigIntAndCycles(input === undefined ? {} : input);
66
76
  }
67
77
 
68
78
  function mapToolCaller(
@@ -172,19 +182,23 @@ async function convertFunctionToolResultOutput({
172
182
  switch (output.type) {
173
183
  case 'text':
174
184
  return convertScalarOutput(
175
- hasOutputSchema ? JSON.stringify(output.value) : output.value,
185
+ hasOutputSchema
186
+ ? stringifyWithBigIntAndCycles(output.value)
187
+ : output.value,
176
188
  );
177
189
  case 'error-text':
178
190
  case 'error-json':
179
- return convertScalarOutput(JSON.stringify({ error: output.value }));
191
+ return convertScalarOutput(
192
+ stringifyWithBigIntAndCycles({ error: output.value }),
193
+ );
180
194
  case 'execution-denied': {
181
195
  const reason = output.reason ?? 'Tool call execution denied.';
182
196
  return convertScalarOutput(
183
- hasOutputSchema ? JSON.stringify(reason) : reason,
197
+ hasOutputSchema ? stringifyWithBigIntAndCycles(reason) : reason,
184
198
  );
185
199
  }
186
200
  case 'json':
187
- return convertScalarOutput(JSON.stringify(output.value));
201
+ return convertScalarOutput(stringifyWithBigIntAndCycles(output.value));
188
202
  case 'content':
189
203
  return output.value
190
204
  .map(item => {
@@ -1074,7 +1088,7 @@ export async function convertToOpenAIResponsesInput({
1074
1088
  input:
1075
1089
  typeof part.input === 'string'
1076
1090
  ? part.input
1077
- : JSON.stringify(part.input),
1091
+ : stringifyWithBigIntAndCycles(part.input),
1078
1092
  ...(isAsync != null && { async: isAsync }),
1079
1093
  id,
1080
1094
  });
@@ -1311,7 +1325,7 @@ export async function convertToOpenAIResponsesInput({
1311
1325
  } else if (reasoningMessage !== undefined) {
1312
1326
  warnings.push({
1313
1327
  type: 'other',
1314
- message: `Cannot append empty reasoning part to existing reasoning sequence. Skipping reasoning part: ${JSON.stringify(part)}.`,
1328
+ message: `Cannot append empty reasoning part to existing reasoning sequence. Skipping reasoning part: ${stringifyWithBigIntAndCycles(part)}.`,
1315
1329
  });
1316
1330
  }
1317
1331
 
@@ -1362,7 +1376,7 @@ export async function convertToOpenAIResponsesInput({
1362
1376
  } else {
1363
1377
  warnings.push({
1364
1378
  type: 'other',
1365
- message: `Non-OpenAI reasoning parts are not supported. Skipping reasoning part: ${JSON.stringify(part)}.`,
1379
+ message: `Non-OpenAI reasoning parts are not supported. Skipping reasoning part: ${stringifyWithBigIntAndCycles(part)}.`,
1366
1380
  });
1367
1381
  }
1368
1382
  }
@@ -1480,7 +1494,9 @@ export async function convertToOpenAIResponsesInput({
1480
1494
  }),
1481
1495
  );
1482
1496
  const serializedToolOutputs = toolOutputs.map(({ output }) =>
1483
- typeof output === 'string' ? output : JSON.stringify(output),
1497
+ typeof output === 'string'
1498
+ ? output
1499
+ : stringifyWithBigIntAndCycles(output),
1484
1500
  );
1485
1501
  const hasPromptCacheBreakpoint = toolOutputs.some(
1486
1502
  ({ promptCacheBreakpoint }) => promptCacheBreakpoint != null,
@@ -1663,7 +1679,7 @@ export async function convertToOpenAIResponsesInput({
1663
1679
  case 'error-text':
1664
1680
  case 'error-json':
1665
1681
  outputValue = convertScalarOutput(
1666
- JSON.stringify({ error: output.value }),
1682
+ stringifyWithBigIntAndCycles({ error: output.value }),
1667
1683
  );
1668
1684
  break;
1669
1685
  case 'execution-denied':
@@ -1672,7 +1688,9 @@ export async function convertToOpenAIResponsesInput({
1672
1688
  );
1673
1689
  break;
1674
1690
  case 'json':
1675
- outputValue = convertScalarOutput(JSON.stringify(output.value));
1691
+ outputValue = convertScalarOutput(
1692
+ stringifyWithBigIntAndCycles(output.value),
1693
+ );
1676
1694
  break;
1677
1695
  case 'content':
1678
1696
  outputValue = output.value
@@ -32,6 +32,18 @@ export const openAITranscriptionModelOptions = lazySchema(() =>
32
32
  */
33
33
  language: z.string().optional(),
34
34
 
35
+ /**
36
+ * Expected languages for REST transcription, e.g. with gpt-transcribe.
37
+ * Use this instead of language. The API validates model support.
38
+ */
39
+ languages: z.array(z.string()).optional(),
40
+
41
+ /**
42
+ * Vocabulary hints for REST transcription, e.g. names or technical terms.
43
+ * The API validates model support.
44
+ */
45
+ keywords: z.array(z.string()).optional(),
46
+
35
47
  /**
36
48
  * An optional text to guide the model's style or continue a previous audio segment.
37
49
  */
@@ -207,6 +207,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
207
207
  response_format: responseFormat,
208
208
  include: openAIOptions?.include,
209
209
  language: openAIOptions?.language,
210
+ languages: openAIOptions?.languages,
211
+ keywords: openAIOptions?.keywords,
210
212
  prompt: openAIOptions?.prompt,
211
213
  temperature: openAIOptions?.temperature,
212
214
  timestamp_granularities: timestampGranularities,
@@ -384,6 +386,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
384
386
  const rawOpenAIOptions = options.providerOptions?.openai ?? {};
385
387
  for (const option of [
386
388
  'include',
389
+ 'languages',
390
+ 'keywords',
387
391
  'prompt',
388
392
  'temperature',
389
393
  'timestampGranularities',