@ai-sdk/openai 4.0.83 → 4.0.85

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.
@@ -36,6 +36,15 @@ const openai = createOpenAI({
36
36
 
37
37
  You can use the following optional settings to customize the OpenAI provider instance:
38
38
 
39
+ - **batchResultDownloads** _object_
40
+
41
+ Settings for downloading JSON Lines batch results.
42
+ - **maxLineBytes** _number_
43
+
44
+ Maximum UTF-8 bytes per row, excluding the LF delimiter. Defaults to 64 MiB
45
+ (67,108,864 bytes). Must be a positive safe integer. Oversized rows throw a
46
+ `DownloadError` and cancel the download.
47
+
39
48
  - **baseURL** _string_
40
49
 
41
50
  Use a different URL prefix for API calls, e.g. to use proxy servers.
@@ -3469,18 +3478,18 @@ const result = await generateSpeech({
3469
3478
  | `tts-1-hd` | <Check /> |
3470
3479
  | `gpt-4o-mini-tts` | <Check /> |
3471
3480
 
3472
- ## Evaluation Models
3481
+ ## Decision Models
3473
3482
 
3474
- Create an experimental evaluation model with `openai.evaluationModel(modelId)`.
3475
- It uses the Responses API with structured output to answer Choice, Score, and
3476
- Boolean questions. Choose a model that supports structured output, such as `gpt-6-luna`.
3483
+ Create an experimental decision model with `openai.decisionModel(modelId)`.
3484
+ It uses the native [Decisions API](https://developers.openai.com/api/docs/guides/decisions)
3485
+ to answer Choice, Score, and Boolean questions with `gpt-6-luna`.
3477
3486
 
3478
3487
  ```ts
3479
3488
  import { openai } from '@ai-sdk/openai';
3480
- import { experimental_evaluate } from 'ai';
3489
+ import { experimental_decide } from 'ai';
3481
3490
 
3482
- const { answers } = await experimental_evaluate({
3483
- model: openai.evaluationModel('gpt-6-luna'),
3491
+ const { answers } = await experimental_decide({
3492
+ model: openai.decisionModel('gpt-6-luna'),
3484
3493
  state: 'I was charged twice.',
3485
3494
  questions: {
3486
3495
  requestsRefund: {
@@ -3496,20 +3505,40 @@ const { answers } = await experimental_evaluate({
3496
3505
  });
3497
3506
  ```
3498
3507
 
3499
- Scores are finite fractional positions on the ordered rubric. Choice labels are
3500
- returned exactly as supplied. These are model judgments: no probability
3501
- distributions are returned for Choice or Score. Boolean answers contain a prompted
3502
- estimate of P(true), validated to be finite and in `[0, 1]`. These estimates are
3503
- not guaranteed to be calibrated. Apply thresholds in application code, for example
3508
+ Scores are probability-weighted fractional positions on the ordered rubric, starting
3509
+ at zero. Choice labels are returned exactly as supplied. Choice and Score answers
3510
+ include probability distributions. Boolean questions use the API's predicate
3511
+ primitive and return P(true). Apply thresholds in application code, for example
3504
3512
  `answers.requestsRefund.probability >= 0.5`.
3505
3513
 
3514
+ Structured state, instructions, and criterion descriptions are serialized as JSON
3515
+ text. Score levels receive zero-based index labels. Non-null Boolean criteria are
3516
+ appended to predicate instructions under `Criteria for true:` and `Criteria for false:`.
3517
+ String criteria are passed through; structured criteria are serialized as JSON text.
3518
+
3506
3519
  The factory respects `createOpenAI` settings, including custom fetch and base URL.
3507
- Pass Responses options through `providerOptions.openai`. Usage, warnings,
3508
- response information, and provider metadata are preserved. Refusals, truncated
3509
- output, or invalid answers fail the entire call. See [Evaluation](/docs/ai-sdk-core/evaluation).
3520
+ Set `providerOptions.openai.safetyIdentifier` to an opaque end-user identifier for
3521
+ safety monitoring (up to 128 characters). For example:
3522
+
3523
+ ```ts
3524
+ providerOptions: {
3525
+ openai: { safetyIdentifier: 'user-123' } satisfies OpenAIDecisionModelOptions,
3526
+ }
3527
+ ```
3528
+
3529
+ Import `OpenAIDecisionModelOptions` from `@ai-sdk/openai` when using the type above.
3530
+ Other OpenAI options are unsupported and produce warnings. Native confidence
3531
+ values are available in `providerMetadata.openai.confidence`, keyed by question ID.
3532
+ Input and output token counts are available in `usage`. The full native token usage
3533
+ (including cache, reasoning, and total counts) is preserved in
3534
+ `providerMetadata.openai.usage`. Response headers, the returned model ID, and the
3535
+ raw response body are preserved. If OpenAI refuses a question, the entire call
3536
+ throws `InvalidResponseDataError` with the question name and the raw response in
3537
+ `error.data`. Invalid answers also fail the entire decision.
3538
+ See [Decisions](/docs/ai-sdk-core/decisions).
3510
3539
 
3511
- Evaluation models can also be accessed through `customProvider` aliases or
3512
- `createProviderRegistry().evaluationModel('provider:model')`. Direct string IDs
3540
+ Decision models can also be accessed through `customProvider` aliases or
3541
+ `createProviderRegistry().decisionModel('provider:model')`. Direct string IDs
3513
3542
  use Gateway by default, or an explicitly configured default provider with an
3514
- `evaluationModel` method. See
3515
- [model aliases and registries](/docs/ai-sdk-core/evaluation#model-aliases-and-registries).
3543
+ `decisionModel` method. See
3544
+ [model aliases and registries](/docs/ai-sdk-core/decisions#model-aliases-and-registries).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.83",
3
+ "version": "4.0.85",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,15 +35,15 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.21",
39
- "@ai-sdk/provider-utils": "5.0.53"
38
+ "@ai-sdk/provider": "4.0.23",
39
+ "@ai-sdk/provider-utils": "5.0.55"
40
40
  },
41
41
  "devDependencies": {
42
- "@ai-sdk/test-server": "2.0.3",
42
+ "@ai-sdk/test-server": "2.0.4",
43
43
  "@types/node": "22.19.19",
44
44
  "@vercel/ai-tsconfig": "0.0.0",
45
45
  "tsdown": "^0.23.0",
46
- "typescript": "5.8.3",
46
+ "typescript": "6.0.3",
47
47
  "zod": "3.25.76"
48
48
  },
49
49
  "peerDependencies": {
@@ -146,6 +146,17 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
146
146
  warnings.push({ type: 'unsupported', feature: 'topK' });
147
147
  }
148
148
 
149
+ // reasoningSummary is a Responses API option that the chat options schema strips,
150
+ // so check the raw provider options to avoid silently ignoring it:
151
+ if (providerOptions?.openai?.reasoningSummary != null) {
152
+ warnings.push({
153
+ type: 'unsupported',
154
+ feature: 'reasoningSummary',
155
+ details:
156
+ 'reasoningSummary is only supported by the Responses API, not the Chat Completions API',
157
+ });
158
+ }
159
+
149
160
  const { messages, warnings: messageWarnings } = convertToOpenAIChatMessages(
150
161
  {
151
162
  prompt,
@@ -148,7 +148,7 @@ export class OpenAIFiles implements FilesV4 {
148
148
  } else {
149
149
  const fileBytes = convertInlineFileDataToUint8Array(data);
150
150
 
151
- const blob = new Blob([fileBytes], {
151
+ const blob = new Blob([fileBytes as Uint8Array<ArrayBuffer>], {
152
152
  type: mediaType,
153
153
  });
154
154
 
package/src/index.ts CHANGED
@@ -60,3 +60,5 @@ export type {
60
60
  OpenaiResponsesSourceDocumentProviderMetadata,
61
61
  } from './responses/openai-responses-provider-metadata';
62
62
  export { VERSION } from './version';
63
+ export type { OpenAIDecisionModelId } from './openai-decision-model';
64
+ export type { OpenAIDecisionModelOptions } from './openai-decision-model-options';
@@ -181,6 +181,7 @@ export class OpenAIBatch implements BatchV4<OpenAIBatchModelIds> {
181
181
  private readonly options: {
182
182
  provider: string;
183
183
  config: OpenAIConfig;
184
+ maxLineBytes?: number;
184
185
  },
185
186
  ) {
186
187
  this.provider = options.provider;
@@ -466,6 +467,7 @@ export class OpenAIBatch implements BatchV4<OpenAIBatchModelIds> {
466
467
  failedResponseHandler: openaiFailedResponseHandler,
467
468
  successfulResponseHandler: createJsonLinesResponseHandler(
468
469
  openaiBatchResultLineSchema,
470
+ { maxLineBytes: this.options.maxLineBytes },
469
471
  ),
470
472
  abortSignal: options.abortSignal,
471
473
  fetch: this.options.config.fetch,
@@ -0,0 +1,19 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+
8
+ export const openaiDecisionModelOptions = lazySchema(() =>
9
+ zodSchema(
10
+ z.object({
11
+ /** An opaque end-user identifier for safety monitoring, up to 128 characters. */
12
+ safetyIdentifier: z.string().max(128).optional(),
13
+ }),
14
+ ),
15
+ );
16
+
17
+ export type OpenAIDecisionModelOptions = InferSchema<
18
+ typeof openaiDecisionModelOptions
19
+ >;
@@ -0,0 +1,285 @@
1
+ import {
2
+ InvalidResponseDataError,
3
+ type Experimental_DecisionModelV4 as DecisionModelV4,
4
+ type Experimental_DecisionModelV4Answer as DecisionModelV4Answer,
5
+ type Experimental_DecisionModelV4Input as DecisionModelV4Input,
6
+ } from '@ai-sdk/provider';
7
+ import {
8
+ combineHeaders,
9
+ createJsonResponseHandler,
10
+ postJsonToApi,
11
+ parseProviderOptions,
12
+ serializeModelOptions,
13
+ WORKFLOW_DESERIALIZE,
14
+ WORKFLOW_SERIALIZE,
15
+ } from '@ai-sdk/provider-utils';
16
+ import { z } from 'zod/v4';
17
+ import {
18
+ prepareOpenAIConfigForWorkflowDeserialize,
19
+ type OpenAIConfig,
20
+ } from './openai-config';
21
+ import { openaiFailedResponseHandler } from './openai-error';
22
+ import { openaiDecisionModelOptions } from './openai-decision-model-options';
23
+
24
+ export type OpenAIDecisionModelId = 'gpt-6-luna' | (string & {});
25
+
26
+ const probability = z.number().min(0).max(1);
27
+ const responseSchema = z.object({
28
+ model: z.string().nullish(),
29
+ usage: z
30
+ .object({
31
+ input_tokens: z.number().nullish(),
32
+ input_tokens_details: z
33
+ .object({
34
+ cached_tokens: z.number().nullish(),
35
+ cache_write_tokens: z.number().nullish(),
36
+ })
37
+ .nullish(),
38
+ output_tokens: z.number().nullish(),
39
+ output_tokens_details: z
40
+ .object({
41
+ reasoning_tokens: z.number().nullish(),
42
+ })
43
+ .nullish(),
44
+ total_tokens: z.number().nullish(),
45
+ })
46
+ .nullish(),
47
+ answers: z.array(
48
+ z.discriminatedUnion('type', [
49
+ z.object({ type: z.literal('refusal'), name: z.string().nullable() }),
50
+ z.object({
51
+ type: z.literal('predicate'),
52
+ name: z.string(),
53
+ probability,
54
+ }),
55
+ z.object({
56
+ type: z.literal('choice'),
57
+ name: z.string(),
58
+ choice: z.string(),
59
+ confidence: probability.nullish(),
60
+ probabilities: z.array(z.object({ value: z.string(), probability })),
61
+ }),
62
+ z.object({
63
+ type: z.literal('score'),
64
+ name: z.string(),
65
+ score: z.number(),
66
+ confidence: probability.nullish(),
67
+ probabilities: z.array(
68
+ z.object({ value: z.number().int().nonnegative(), probability }),
69
+ ),
70
+ }),
71
+ ]),
72
+ ),
73
+ });
74
+
75
+ function toText(input: DecisionModelV4Input): string {
76
+ return typeof input === 'string' ? input : JSON.stringify(input);
77
+ }
78
+
79
+ export class DecisionOpenAIModel implements DecisionModelV4 {
80
+ readonly specificationVersion = 'v4';
81
+ readonly supportedQuestionTypes = ['choice', 'score', 'boolean'] as const;
82
+
83
+ constructor(
84
+ readonly modelId: OpenAIDecisionModelId,
85
+ private readonly config: OpenAIConfig,
86
+ ) {}
87
+
88
+ get provider() {
89
+ return this.config.provider;
90
+ }
91
+
92
+ static [WORKFLOW_SERIALIZE](model: DecisionOpenAIModel) {
93
+ return serializeModelOptions({
94
+ modelId: model.modelId,
95
+ config: model.config,
96
+ });
97
+ }
98
+
99
+ static [WORKFLOW_DESERIALIZE](options: {
100
+ modelId: OpenAIDecisionModelId;
101
+ config: Parameters<typeof prepareOpenAIConfigForWorkflowDeserialize>[0];
102
+ }) {
103
+ return new DecisionOpenAIModel(
104
+ options.modelId,
105
+ prepareOpenAIConfigForWorkflowDeserialize(options.config),
106
+ );
107
+ }
108
+
109
+ /** @deprecated Use `doDecide` instead. */
110
+ doEvaluate(options: Parameters<DecisionModelV4['doDecide']>[0]) {
111
+ return this.doDecide(options);
112
+ }
113
+
114
+ async doDecide({
115
+ state,
116
+ questions,
117
+ headers,
118
+ abortSignal,
119
+ providerOptions,
120
+ }: Parameters<DecisionModelV4['doDecide']>[0]): Promise<
121
+ Awaited<ReturnType<DecisionModelV4['doDecide']>>
122
+ > {
123
+ const openaiOptions = await parseProviderOptions({
124
+ provider: 'openai',
125
+ providerOptions,
126
+ schema: openaiDecisionModelOptions,
127
+ });
128
+ const {
129
+ value: response,
130
+ rawValue,
131
+ responseHeaders,
132
+ } = await postJsonToApi({
133
+ url: this.config.url({ path: '/decisions', modelId: this.modelId }),
134
+ headers: combineHeaders(this.config.headers?.(), headers),
135
+ body: {
136
+ model: this.modelId,
137
+ safety_identifier: openaiOptions?.safetyIdentifier,
138
+ input: toText(state),
139
+ questions: Object.entries(questions).map(([name, question]) => {
140
+ const instructions = toText(question.instructions);
141
+ switch (question.type) {
142
+ case 'boolean':
143
+ return {
144
+ type: 'predicate',
145
+ name,
146
+ instructions: [
147
+ instructions,
148
+ question.criteria?.true == null
149
+ ? undefined
150
+ : `Criteria for true:\n${toText(question.criteria.true)}`,
151
+ question.criteria?.false == null
152
+ ? undefined
153
+ : `Criteria for false:\n${toText(question.criteria.false)}`,
154
+ ]
155
+ .filter(part => part !== undefined)
156
+ .join('\n\n'),
157
+ };
158
+ case 'choice':
159
+ return {
160
+ type: 'choice',
161
+ name,
162
+ instructions,
163
+ choices: Object.entries(question.criteria).map(
164
+ ([value, description]) => ({
165
+ value,
166
+ ...(description == null
167
+ ? {}
168
+ : { description: toText(description) }),
169
+ }),
170
+ ),
171
+ };
172
+ case 'score':
173
+ return {
174
+ type: 'score',
175
+ name,
176
+ instructions,
177
+ levels: question.criteria.map((description, index) => ({
178
+ // Score criteria have no separate labels; indices identify each level.
179
+ label: String(index),
180
+ ...(description == null
181
+ ? {}
182
+ : { description: toText(description) }),
183
+ })),
184
+ };
185
+ }
186
+ }),
187
+ },
188
+ abortSignal,
189
+ fetch: this.config.fetch,
190
+ failedResponseHandler: openaiFailedResponseHandler,
191
+ successfulResponseHandler: createJsonResponseHandler(responseSchema),
192
+ });
193
+
194
+ const answers = Object.fromEntries(
195
+ response.answers.map((answer): [string, DecisionModelV4Answer] => {
196
+ if (answer.type === 'refusal') {
197
+ throw new InvalidResponseDataError({
198
+ data: rawValue,
199
+ message:
200
+ answer.name === null
201
+ ? 'OpenAI Decisions refused an unnamed question.'
202
+ : `OpenAI Decisions refused question ${JSON.stringify(answer.name)}.`,
203
+ });
204
+ }
205
+ if (answer.type === 'predicate') {
206
+ return [
207
+ answer.name,
208
+ { type: 'boolean', probability: answer.probability },
209
+ ];
210
+ }
211
+ const probabilities = Object.fromEntries(
212
+ answer.probabilities.map(({ value, probability }) => [
213
+ value,
214
+ probability,
215
+ ]),
216
+ );
217
+ if (Object.keys(probabilities).length !== answer.probabilities.length) {
218
+ throw new InvalidResponseDataError({
219
+ data: rawValue,
220
+ message: 'Decisions returned duplicate probability values.',
221
+ });
222
+ }
223
+ return [
224
+ answer.name,
225
+ answer.type === 'choice'
226
+ ? { type: 'choice', choice: answer.choice, probabilities }
227
+ : { type: 'score', score: answer.score, probabilities },
228
+ ];
229
+ }),
230
+ );
231
+
232
+ const names = response.answers.map(answer => answer.name);
233
+ if (
234
+ names.length !== Object.keys(questions).length ||
235
+ new Set(names).size !== names.length ||
236
+ names.some(
237
+ name =>
238
+ typeof name !== 'string' ||
239
+ !Object.prototype.hasOwnProperty.call(questions, name),
240
+ )
241
+ ) {
242
+ throw new InvalidResponseDataError({
243
+ data: rawValue,
244
+ message: 'Decisions must return exactly one answer for every question.',
245
+ });
246
+ }
247
+
248
+ return {
249
+ answers,
250
+ usage:
251
+ response.usage == null
252
+ ? undefined
253
+ : {
254
+ inputTokens: response.usage.input_tokens ?? undefined,
255
+ outputTokens: response.usage.output_tokens ?? undefined,
256
+ },
257
+ // The Decisions API reports probabilities and scores to two decimal places.
258
+ rounding: { probabilityDecimals: 2, scoreDecimals: 2 },
259
+ warnings: Object.keys(providerOptions?.openai ?? {})
260
+ .filter(option => option !== 'safetyIdentifier')
261
+ .map(option => ({
262
+ type: 'unsupported',
263
+ feature: `providerOptions.openai.${option}`,
264
+ })),
265
+ providerMetadata: {
266
+ openai: {
267
+ ...(response.usage == null ? {} : { usage: response.usage }),
268
+ confidence: Object.fromEntries(
269
+ response.answers.flatMap(answer =>
270
+ (answer.type === 'choice' || answer.type === 'score') &&
271
+ answer.confidence != null
272
+ ? [[answer.name, answer.confidence]]
273
+ : [],
274
+ ),
275
+ ),
276
+ },
277
+ },
278
+ response: {
279
+ modelId: response.model ?? this.modelId,
280
+ headers: responseHeaders,
281
+ body: rawValue,
282
+ },
283
+ };
284
+ }
285
+ }
@@ -1,6 +1,6 @@
1
1
  import type {
2
2
  Experimental_BatchV4 as BatchV4,
3
- Experimental_EvaluationModelV4 as EvaluationModelV4,
3
+ Experimental_DecisionModelV4 as DecisionModelV4,
4
4
  EmbeddingModelV4,
5
5
  FilesV4,
6
6
  ImageModelV4,
@@ -20,7 +20,10 @@ import {
20
20
  type FetchFunction,
21
21
  type WebSocketConstructor,
22
22
  } from '@ai-sdk/provider-utils';
23
- import { Experimental_EvaluationLanguageModel as EvaluationLanguageModel } from '@ai-sdk/provider-utils/experimental-evaluation';
23
+ import {
24
+ DecisionOpenAIModel,
25
+ type OpenAIDecisionModelId,
26
+ } from './openai-decision-model';
24
27
  import { OpenAIChatLanguageModel } from './chat/openai-chat-language-model';
25
28
  import type { OpenAIChatModelId } from './chat/openai-chat-language-model-options';
26
29
  import { OpenAICompletionLanguageModel } from './completion/openai-completion-language-model';
@@ -50,8 +53,12 @@ import { VERSION } from './version';
50
53
  export interface OpenAIProvider extends ProviderV4 {
51
54
  (modelId: OpenAIResponsesModelId): LanguageModelV4;
52
55
 
53
- /** Creates an experimental Choice/Score/Boolean evaluation model using the Responses API. */
54
- evaluationModel(modelId: OpenAIResponsesModelId): EvaluationModelV4;
56
+ /** Creates an experimental Choice/Score/Boolean decision model using the Decisions API. */
57
+ decisionModel(modelId: OpenAIDecisionModelId): DecisionModelV4;
58
+ /** @deprecated Use `decisionModel` instead. */
59
+ evaluationModel(modelId: OpenAIDecisionModelId): DecisionModelV4 & {
60
+ doEvaluate: DecisionModelV4['doDecide'];
61
+ };
55
62
 
56
63
  /**
57
64
  * Creates an OpenAI model for text generation.
@@ -155,6 +162,12 @@ export interface OpenAIProvider extends ProviderV4 {
155
162
  }
156
163
 
157
164
  export interface OpenAIProviderSettings {
165
+ /** Settings for downloading JSON Lines batch results. */
166
+ batchResultDownloads?: {
167
+ /** Maximum UTF-8 bytes per row, excluding LF. Defaults to 64 MiB. */
168
+ maxLineBytes?: number;
169
+ };
170
+
158
171
  /**
159
172
  * Base URL for the OpenAI API calls.
160
173
  */
@@ -332,6 +345,7 @@ export function createOpenAI(
332
345
  const createBatch = () =>
333
346
  new OpenAIBatch({
334
347
  provider: `${providerName}.batch`,
348
+ maxLineBytes: options.batchResultDownloads?.maxLineBytes,
335
349
  config: {
336
350
  provider: `${providerName}.responses`,
337
351
  baseURL,
@@ -352,11 +366,16 @@ export function createOpenAI(
352
366
  provider.chat = createChatModel;
353
367
  provider.completion = createCompletionModel;
354
368
  provider.responses = createResponsesModel;
355
- provider.evaluationModel = (modelId: OpenAIResponsesModelId) =>
356
- new EvaluationLanguageModel({
357
- model: createResponsesModel(modelId),
358
- provider: `${providerName}.evaluation`,
369
+ provider.decisionModel = (modelId: OpenAIDecisionModelId) =>
370
+ new DecisionOpenAIModel(modelId, {
371
+ baseURL,
372
+ url: ({ path }) => `${baseURL}${path}`,
373
+ headers: getHeaders,
374
+ fetch: options.fetch,
375
+ provider: `${providerName}.decision`,
359
376
  });
377
+ provider.evaluationModel =
378
+ provider.decisionModel as OpenAIProvider['evaluationModel'];
360
379
  provider.embedding = createEmbeddingModel;
361
380
  provider.embeddingModel = createEmbeddingModel;
362
381
  provider.textEmbedding = createEmbeddingModel;
@@ -41,7 +41,11 @@ export class OpenAISkills implements SkillsV4 {
41
41
 
42
42
  for (const file of params.files) {
43
43
  const content = convertInlineFileDataToUint8Array(file.data);
44
- formData.append('files[]', new Blob([content]), file.path);
44
+ formData.append(
45
+ 'files[]',
46
+ new Blob([content as Uint8Array<ArrayBuffer>]),
47
+ file.path,
48
+ );
45
49
  }
46
50
 
47
51
  const { value: response } = await postFormDataToApi({
@@ -180,7 +180,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
180
180
  const formData = new FormData();
181
181
  const blob =
182
182
  audio instanceof Uint8Array
183
- ? new Blob([audio])
183
+ ? new Blob([audio as Uint8Array<ArrayBuffer>])
184
184
  : new Blob([convertBase64ToUint8Array(audio)]);
185
185
 
186
186
  formData.append('model', this.modelId);