@ai-sdk/prodia 1.0.22 → 1.0.23
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.
- package/CHANGELOG.md +10 -0
- package/dist/index.d.mts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +676 -151
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +684 -139
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -0
- package/src/prodia-api.ts +198 -0
- package/src/prodia-image-model.ts +16 -196
- package/src/prodia-language-model-settings.ts +6 -0
- package/src/prodia-language-model.ts +395 -0
- package/src/prodia-provider.ts +40 -8
- package/src/prodia-video-model-settings.ts +7 -0
- package/src/prodia-video-model.ts +282 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV3,
|
|
3
|
+
LanguageModelV3CallOptions,
|
|
4
|
+
LanguageModelV3Content,
|
|
5
|
+
LanguageModelV3StreamPart,
|
|
6
|
+
SharedV3Warning,
|
|
7
|
+
} from '@ai-sdk/provider';
|
|
8
|
+
import type { InferSchema } from '@ai-sdk/provider-utils';
|
|
9
|
+
import {
|
|
10
|
+
combineHeaders,
|
|
11
|
+
convertBase64ToUint8Array,
|
|
12
|
+
generateId,
|
|
13
|
+
lazySchema,
|
|
14
|
+
parseJSON,
|
|
15
|
+
parseProviderOptions,
|
|
16
|
+
postFormDataToApi,
|
|
17
|
+
resolve,
|
|
18
|
+
zodSchema,
|
|
19
|
+
} from '@ai-sdk/provider-utils';
|
|
20
|
+
import { z } from 'zod/v4';
|
|
21
|
+
import type { ProdiaModelConfig } from './prodia-api';
|
|
22
|
+
import {
|
|
23
|
+
buildProdiaProviderMetadata,
|
|
24
|
+
parseMultipart,
|
|
25
|
+
prodiaFailedResponseHandler,
|
|
26
|
+
prodiaJobResultSchema,
|
|
27
|
+
} from './prodia-api';
|
|
28
|
+
import type { ProdiaJobResult } from './prodia-api';
|
|
29
|
+
import type { ProdiaLanguageModelId } from './prodia-language-model-settings';
|
|
30
|
+
|
|
31
|
+
export class ProdiaLanguageModel implements LanguageModelV3 {
|
|
32
|
+
readonly specificationVersion = 'v3';
|
|
33
|
+
readonly supportedUrls = {};
|
|
34
|
+
|
|
35
|
+
get provider(): string {
|
|
36
|
+
return this.config.provider;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
readonly modelId: ProdiaLanguageModelId,
|
|
41
|
+
private readonly config: ProdiaModelConfig,
|
|
42
|
+
) {}
|
|
43
|
+
|
|
44
|
+
async doGenerate(options: LanguageModelV3CallOptions) {
|
|
45
|
+
const warnings: Array<SharedV3Warning> = [];
|
|
46
|
+
|
|
47
|
+
// Warn about unsupported LLM features
|
|
48
|
+
if (options.temperature !== undefined) {
|
|
49
|
+
warnings.push({ type: 'unsupported', feature: 'temperature' });
|
|
50
|
+
}
|
|
51
|
+
if (options.topP !== undefined) {
|
|
52
|
+
warnings.push({ type: 'unsupported', feature: 'topP' });
|
|
53
|
+
}
|
|
54
|
+
if (options.topK !== undefined) {
|
|
55
|
+
warnings.push({ type: 'unsupported', feature: 'topK' });
|
|
56
|
+
}
|
|
57
|
+
if (options.maxOutputTokens !== undefined) {
|
|
58
|
+
warnings.push({ type: 'unsupported', feature: 'maxOutputTokens' });
|
|
59
|
+
}
|
|
60
|
+
if (options.stopSequences !== undefined) {
|
|
61
|
+
warnings.push({ type: 'unsupported', feature: 'stopSequences' });
|
|
62
|
+
}
|
|
63
|
+
if (options.presencePenalty !== undefined) {
|
|
64
|
+
warnings.push({ type: 'unsupported', feature: 'presencePenalty' });
|
|
65
|
+
}
|
|
66
|
+
if (options.frequencyPenalty !== undefined) {
|
|
67
|
+
warnings.push({ type: 'unsupported', feature: 'frequencyPenalty' });
|
|
68
|
+
}
|
|
69
|
+
if (options.tools !== undefined && options.tools.length > 0) {
|
|
70
|
+
warnings.push({ type: 'unsupported', feature: 'tools' });
|
|
71
|
+
}
|
|
72
|
+
if (options.toolChoice !== undefined) {
|
|
73
|
+
warnings.push({ type: 'unsupported', feature: 'toolChoice' });
|
|
74
|
+
}
|
|
75
|
+
if (
|
|
76
|
+
options.responseFormat !== undefined &&
|
|
77
|
+
options.responseFormat.type !== 'text'
|
|
78
|
+
) {
|
|
79
|
+
warnings.push({ type: 'unsupported', feature: 'responseFormat' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const prodiaOptions = await parseProviderOptions({
|
|
83
|
+
provider: 'prodia',
|
|
84
|
+
providerOptions: options.providerOptions,
|
|
85
|
+
schema: prodiaLanguageModelOptionsSchema,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Extract text prompt from messages
|
|
89
|
+
let prompt = '';
|
|
90
|
+
let systemMessage = '';
|
|
91
|
+
for (const message of options.prompt) {
|
|
92
|
+
if (message.role === 'system') {
|
|
93
|
+
systemMessage = message.content;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Get text from the last user message
|
|
97
|
+
for (let i = options.prompt.length - 1; i >= 0; i--) {
|
|
98
|
+
const message = options.prompt[i];
|
|
99
|
+
if (message.role === 'user') {
|
|
100
|
+
for (const part of message.content) {
|
|
101
|
+
if (part.type === 'text') {
|
|
102
|
+
prompt += (prompt ? '\n' : '') + part.text;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (systemMessage) {
|
|
109
|
+
prompt = systemMessage + '\n' + prompt;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Extract image from user messages
|
|
113
|
+
let imageBytes: Uint8Array | undefined;
|
|
114
|
+
let imageMediaType = 'image/png';
|
|
115
|
+
for (let i = options.prompt.length - 1; i >= 0; i--) {
|
|
116
|
+
const message = options.prompt[i];
|
|
117
|
+
if (message.role === 'user') {
|
|
118
|
+
for (const part of message.content) {
|
|
119
|
+
if (part.type === 'file' && part.mediaType.startsWith('image/')) {
|
|
120
|
+
if (part.data instanceof Uint8Array) {
|
|
121
|
+
imageBytes = part.data;
|
|
122
|
+
} else if (typeof part.data === 'string') {
|
|
123
|
+
// base64 encoded
|
|
124
|
+
imageBytes = convertBase64ToUint8Array(part.data);
|
|
125
|
+
} else if (part.data instanceof URL) {
|
|
126
|
+
const fetchFn = this.config.fetch ?? globalThis.fetch;
|
|
127
|
+
const response = await fetchFn(part.data.toString());
|
|
128
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
129
|
+
imageBytes = new Uint8Array(arrayBuffer);
|
|
130
|
+
}
|
|
131
|
+
imageMediaType = part.mediaType;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const jobConfig: Record<string, unknown> = {
|
|
140
|
+
prompt,
|
|
141
|
+
include_messages: true,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (prodiaOptions?.aspectRatio !== undefined) {
|
|
145
|
+
jobConfig.aspect_ratio = prodiaOptions.aspectRatio;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const body = {
|
|
149
|
+
type: this.modelId,
|
|
150
|
+
config: jobConfig,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
154
|
+
const combinedHeaders = combineHeaders(
|
|
155
|
+
await resolve(this.config.headers),
|
|
156
|
+
options.headers,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Always use multipart form-data since img2img requires image input
|
|
160
|
+
const formData = new FormData();
|
|
161
|
+
formData.append(
|
|
162
|
+
'job',
|
|
163
|
+
new Blob([JSON.stringify(body)], { type: 'application/json' }),
|
|
164
|
+
'job.json',
|
|
165
|
+
);
|
|
166
|
+
if (imageBytes) {
|
|
167
|
+
const ext =
|
|
168
|
+
imageMediaType === 'image/png'
|
|
169
|
+
? '.png'
|
|
170
|
+
: imageMediaType === 'image/jpeg'
|
|
171
|
+
? '.jpg'
|
|
172
|
+
: imageMediaType === 'image/webp'
|
|
173
|
+
? '.webp'
|
|
174
|
+
: '';
|
|
175
|
+
formData.append(
|
|
176
|
+
'input',
|
|
177
|
+
new Blob([imageBytes], { type: imageMediaType }),
|
|
178
|
+
'input' + ext,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { value: multipartResult, responseHeaders } = await postFormDataToApi(
|
|
183
|
+
{
|
|
184
|
+
url: `${this.config.baseURL}/job?price=true`,
|
|
185
|
+
headers: {
|
|
186
|
+
...combinedHeaders,
|
|
187
|
+
Accept: 'multipart/form-data',
|
|
188
|
+
},
|
|
189
|
+
formData,
|
|
190
|
+
failedResponseHandler: prodiaFailedResponseHandler,
|
|
191
|
+
successfulResponseHandler: createLanguageMultipartResponseHandler(),
|
|
192
|
+
abortSignal: options.abortSignal,
|
|
193
|
+
fetch: this.config.fetch,
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const { jobResult, textContent, fileContent } = multipartResult;
|
|
198
|
+
|
|
199
|
+
const content: Array<LanguageModelV3Content> = [];
|
|
200
|
+
if (textContent !== undefined) {
|
|
201
|
+
content.push({ type: 'text', text: textContent });
|
|
202
|
+
}
|
|
203
|
+
for (const file of fileContent) {
|
|
204
|
+
content.push({
|
|
205
|
+
type: 'file',
|
|
206
|
+
mediaType: file.mediaType,
|
|
207
|
+
data: file.data,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
content,
|
|
213
|
+
finishReason: { unified: 'stop' as const, raw: undefined },
|
|
214
|
+
usage: {
|
|
215
|
+
inputTokens: {
|
|
216
|
+
total: undefined,
|
|
217
|
+
noCache: undefined,
|
|
218
|
+
cacheRead: undefined,
|
|
219
|
+
cacheWrite: undefined,
|
|
220
|
+
},
|
|
221
|
+
outputTokens: {
|
|
222
|
+
total: undefined,
|
|
223
|
+
text: undefined,
|
|
224
|
+
reasoning: undefined,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
warnings,
|
|
228
|
+
providerMetadata: {
|
|
229
|
+
prodia: buildProdiaProviderMetadata(jobResult),
|
|
230
|
+
},
|
|
231
|
+
response: {
|
|
232
|
+
modelId: this.modelId,
|
|
233
|
+
timestamp: currentDate,
|
|
234
|
+
headers: responseHeaders,
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async doStream(options: LanguageModelV3CallOptions) {
|
|
240
|
+
const result = await this.doGenerate(options);
|
|
241
|
+
|
|
242
|
+
const stream = new ReadableStream<LanguageModelV3StreamPart>({
|
|
243
|
+
start(controller) {
|
|
244
|
+
controller.enqueue({
|
|
245
|
+
type: 'stream-start',
|
|
246
|
+
warnings: result.warnings,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
controller.enqueue({
|
|
250
|
+
type: 'response-metadata',
|
|
251
|
+
modelId: result.response?.modelId,
|
|
252
|
+
timestamp: result.response?.timestamp,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
for (const part of result.content) {
|
|
256
|
+
if (part.type === 'text') {
|
|
257
|
+
const id = generateId();
|
|
258
|
+
controller.enqueue({ type: 'text-start', id });
|
|
259
|
+
controller.enqueue({
|
|
260
|
+
type: 'text-delta',
|
|
261
|
+
id,
|
|
262
|
+
delta: part.text,
|
|
263
|
+
});
|
|
264
|
+
controller.enqueue({ type: 'text-end', id });
|
|
265
|
+
} else if (part.type === 'file') {
|
|
266
|
+
controller.enqueue({
|
|
267
|
+
type: 'file',
|
|
268
|
+
mediaType: part.mediaType,
|
|
269
|
+
data: part.data,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
controller.enqueue({
|
|
275
|
+
type: 'finish',
|
|
276
|
+
usage: result.usage,
|
|
277
|
+
finishReason: result.finishReason,
|
|
278
|
+
providerMetadata: result.providerMetadata,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
controller.close();
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
stream,
|
|
287
|
+
response: {
|
|
288
|
+
headers: result.response?.headers,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export const prodiaLanguageModelOptionsSchema = lazySchema(() =>
|
|
295
|
+
zodSchema(
|
|
296
|
+
z.object({
|
|
297
|
+
/**
|
|
298
|
+
* Aspect ratio for the output image.
|
|
299
|
+
*/
|
|
300
|
+
aspectRatio: z
|
|
301
|
+
.enum([
|
|
302
|
+
'1:1',
|
|
303
|
+
'2:3',
|
|
304
|
+
'3:2',
|
|
305
|
+
'4:5',
|
|
306
|
+
'5:4',
|
|
307
|
+
'4:7',
|
|
308
|
+
'7:4',
|
|
309
|
+
'9:16',
|
|
310
|
+
'16:9',
|
|
311
|
+
'9:21',
|
|
312
|
+
'21:9',
|
|
313
|
+
])
|
|
314
|
+
.optional(),
|
|
315
|
+
}),
|
|
316
|
+
),
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
export type ProdiaLanguageModelOptions = InferSchema<
|
|
320
|
+
typeof prodiaLanguageModelOptionsSchema
|
|
321
|
+
>;
|
|
322
|
+
|
|
323
|
+
interface LanguageMultipartResult {
|
|
324
|
+
jobResult: ProdiaJobResult;
|
|
325
|
+
textContent: string | undefined;
|
|
326
|
+
fileContent: Array<{ mediaType: string; data: Uint8Array }>;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function createLanguageMultipartResponseHandler() {
|
|
330
|
+
return async ({
|
|
331
|
+
response,
|
|
332
|
+
}: {
|
|
333
|
+
response: Response;
|
|
334
|
+
}): Promise<{
|
|
335
|
+
value: LanguageMultipartResult;
|
|
336
|
+
responseHeaders: Record<string, string>;
|
|
337
|
+
}> => {
|
|
338
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
339
|
+
const responseHeaders: Record<string, string> = {};
|
|
340
|
+
response.headers.forEach((value, key) => {
|
|
341
|
+
responseHeaders[key] = value;
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const boundaryMatch = contentType.match(/boundary=([^\s;]+)/);
|
|
345
|
+
if (!boundaryMatch) {
|
|
346
|
+
throw new Error(
|
|
347
|
+
`Prodia response missing multipart boundary in content-type: ${contentType}`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
const boundary = boundaryMatch[1];
|
|
351
|
+
|
|
352
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
353
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
354
|
+
|
|
355
|
+
const parts = parseMultipart(bytes, boundary);
|
|
356
|
+
|
|
357
|
+
let jobResult: ProdiaJobResult | undefined;
|
|
358
|
+
let textContent: string | undefined;
|
|
359
|
+
const fileContent: Array<{ mediaType: string; data: Uint8Array }> = [];
|
|
360
|
+
|
|
361
|
+
for (const part of parts) {
|
|
362
|
+
const contentDisposition = part.headers['content-disposition'] ?? '';
|
|
363
|
+
const partContentType = part.headers['content-type'] ?? '';
|
|
364
|
+
|
|
365
|
+
if (contentDisposition.includes('name="job"')) {
|
|
366
|
+
const jsonStr = new TextDecoder().decode(part.body);
|
|
367
|
+
jobResult = await parseJSON({
|
|
368
|
+
text: jsonStr,
|
|
369
|
+
schema: zodSchema(prodiaJobResultSchema),
|
|
370
|
+
});
|
|
371
|
+
} else if (contentDisposition.includes('name="output"')) {
|
|
372
|
+
if (
|
|
373
|
+
partContentType.startsWith('text/') ||
|
|
374
|
+
contentDisposition.includes('.txt')
|
|
375
|
+
) {
|
|
376
|
+
textContent = new TextDecoder().decode(part.body);
|
|
377
|
+
} else if (partContentType.startsWith('image/')) {
|
|
378
|
+
fileContent.push({
|
|
379
|
+
mediaType: partContentType,
|
|
380
|
+
data: part.body,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (!jobResult) {
|
|
387
|
+
throw new Error('Prodia multipart response missing job part');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
value: { jobResult, textContent, fileContent },
|
|
392
|
+
responseHeaders,
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
}
|
package/src/prodia-provider.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type Experimental_VideoModelV3,
|
|
2
3
|
type ImageModelV3,
|
|
4
|
+
type LanguageModelV3,
|
|
3
5
|
NoSuchModelError,
|
|
4
6
|
type ProviderV3,
|
|
5
7
|
} from '@ai-sdk/provider';
|
|
@@ -11,6 +13,10 @@ import {
|
|
|
11
13
|
} from '@ai-sdk/provider-utils';
|
|
12
14
|
import { ProdiaImageModel } from './prodia-image-model';
|
|
13
15
|
import type { ProdiaImageModelId } from './prodia-image-settings';
|
|
16
|
+
import { ProdiaLanguageModel } from './prodia-language-model';
|
|
17
|
+
import type { ProdiaLanguageModelId } from './prodia-language-model-settings';
|
|
18
|
+
import { ProdiaVideoModel } from './prodia-video-model';
|
|
19
|
+
import type { ProdiaVideoModelId } from './prodia-video-model-settings';
|
|
14
20
|
import { VERSION } from './version';
|
|
15
21
|
|
|
16
22
|
export interface ProdiaProviderSettings {
|
|
@@ -37,6 +43,11 @@ export interface ProdiaProviderSettings {
|
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
export interface ProdiaProvider extends ProviderV3 {
|
|
46
|
+
/**
|
|
47
|
+
* Creates a language model for multimodal generation (img2img with text+image output).
|
|
48
|
+
*/
|
|
49
|
+
languageModel(modelId: ProdiaLanguageModelId): LanguageModelV3;
|
|
50
|
+
|
|
40
51
|
/**
|
|
41
52
|
* Creates a model for image generation.
|
|
42
53
|
*/
|
|
@@ -47,6 +58,16 @@ export interface ProdiaProvider extends ProviderV3 {
|
|
|
47
58
|
*/
|
|
48
59
|
imageModel(modelId: ProdiaImageModelId): ImageModelV3;
|
|
49
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Creates a model for video generation.
|
|
63
|
+
*/
|
|
64
|
+
video(modelId: ProdiaVideoModelId): Experimental_VideoModelV3;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a model for video generation.
|
|
68
|
+
*/
|
|
69
|
+
videoModel(modelId: ProdiaVideoModelId): Experimental_VideoModelV3;
|
|
70
|
+
|
|
50
71
|
/**
|
|
51
72
|
* @deprecated Use `embeddingModel` instead.
|
|
52
73
|
*/
|
|
@@ -80,25 +101,36 @@ export function createProdia(
|
|
|
80
101
|
fetch: options.fetch,
|
|
81
102
|
});
|
|
82
103
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
const createLanguageModel = (modelId: ProdiaLanguageModelId) =>
|
|
105
|
+
new ProdiaLanguageModel(modelId, {
|
|
106
|
+
provider: 'prodia.language',
|
|
107
|
+
baseURL: baseURL ?? defaultBaseURL,
|
|
108
|
+
headers: getHeaders,
|
|
109
|
+
fetch: options.fetch,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const createVideoModel = (modelId: ProdiaVideoModelId) =>
|
|
113
|
+
new ProdiaVideoModel(modelId, {
|
|
114
|
+
provider: 'prodia.video',
|
|
115
|
+
baseURL: baseURL ?? defaultBaseURL,
|
|
116
|
+
headers: getHeaders,
|
|
117
|
+
fetch: options.fetch,
|
|
87
118
|
});
|
|
88
|
-
};
|
|
89
119
|
|
|
90
|
-
const
|
|
120
|
+
const embeddingModel = (modelId: string) => {
|
|
91
121
|
throw new NoSuchModelError({
|
|
92
122
|
modelId,
|
|
93
|
-
modelType: '
|
|
123
|
+
modelType: 'embeddingModel',
|
|
94
124
|
});
|
|
95
125
|
};
|
|
96
126
|
|
|
97
127
|
return {
|
|
98
128
|
specificationVersion: 'v3',
|
|
129
|
+
languageModel: createLanguageModel,
|
|
99
130
|
imageModel: createImageModel,
|
|
100
131
|
image: createImageModel,
|
|
101
|
-
|
|
132
|
+
videoModel: createVideoModel,
|
|
133
|
+
video: createVideoModel,
|
|
102
134
|
embeddingModel,
|
|
103
135
|
textEmbeddingModel: embeddingModel,
|
|
104
136
|
};
|