@ai-sdk/openai 4.0.108 → 4.0.109

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.
@@ -162,6 +162,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
162
162
  audio,
163
163
  mediaType,
164
164
  providerOptions,
165
+ timestampGranularities: requestedTimestampGranularities,
165
166
  }: OpenAITranscriptionCallOptions) {
166
167
  const warnings: SharedV4Warning[] = [];
167
168
 
@@ -194,12 +195,42 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
194
195
  const responseFormat =
195
196
  openAIOptions?.responseFormat ?? requestConfig.responseFormat;
196
197
 
197
- // Timestamps are only returned with verbose_json, so only default to
198
- // segments when that format is used.
198
+ // Only verbose_json accepts timestamp_granularities. Diarized JSON
199
+ // always returns segment timings without a timestamp control.
199
200
  const timestampGranularities =
200
201
  openAIOptions?.timestampGranularities ??
202
+ requestedTimestampGranularities ??
201
203
  (responseFormat === 'verbose_json' ? ['segment'] : undefined);
202
204
 
205
+ for (const granularity of requestedTimestampGranularities ?? []) {
206
+ if (
207
+ openAIOptions?.timestampGranularities != null &&
208
+ !openAIOptions.timestampGranularities.includes(granularity)
209
+ ) {
210
+ warnings.push({
211
+ type: 'unsupported',
212
+ feature: 'timestampGranularities',
213
+ details: `The "${granularity}" timestamp granularity is excluded by providerOptions.openai.timestampGranularities.`,
214
+ });
215
+ }
216
+ }
217
+
218
+ for (const granularity of timestampGranularities ?? []) {
219
+ if (
220
+ responseFormat !== 'verbose_json' &&
221
+ !(responseFormat === 'diarized_json' && granularity === 'segment')
222
+ ) {
223
+ warnings.push({
224
+ type: 'unsupported',
225
+ feature:
226
+ openAIOptions?.timestampGranularities != null
227
+ ? 'providerOptions.openai.timestampGranularities'
228
+ : 'timestampGranularities',
229
+ details: `OpenAI transcription with the ${responseFormat ?? 'default JSON'} response format does not support ${granularity} timestamps.`,
230
+ });
231
+ }
232
+ }
233
+
203
234
  const chunkingStrategy =
204
235
  openAIOptions?.chunkingStrategy ?? requestConfig.chunkingStrategy;
205
236
 
@@ -211,7 +242,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
211
242
  keywords: openAIOptions?.keywords,
212
243
  prompt: openAIOptions?.prompt,
213
244
  temperature: openAIOptions?.temperature,
214
- timestamp_granularities: timestampGranularities,
245
+ timestamp_granularities:
246
+ responseFormat === 'verbose_json' ? timestampGranularities : undefined,
215
247
  };
216
248
 
217
249
  for (const [key, value] of Object.entries(transcriptionModelOptions)) {
@@ -314,6 +346,12 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
314
346
  : [],
315
347
  );
316
348
 
349
+ const words = response.words?.map(word => ({
350
+ text: word.word,
351
+ startSecond: word.start,
352
+ endSecond: word.end,
353
+ }));
354
+
317
355
  return {
318
356
  text: response.text,
319
357
  segments:
@@ -322,12 +360,9 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
322
360
  startSecond: segment.start,
323
361
  endSecond: segment.end,
324
362
  })) ??
325
- response.words?.map(word => ({
326
- text: word.word,
327
- startSecond: word.start,
328
- endSecond: word.end,
329
- })) ??
363
+ words ??
330
364
  [],
365
+ ...(words != null && words.length > 0 ? { words } : {}),
331
366
  language,
332
367
  // Duration-billed json responses report the duration only in `usage`:
333
368
  durationInSeconds: