@ai-sdk/topaz 2.0.0 → 3.0.1
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 +18 -2
- package/dist/index.d.ts +183 -180
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1100 -1294
- package/dist/index.js.map +1 -1
- package/docs/210-topaz.mdx +17 -6
- package/package.json +11 -11
- package/src/topaz-image-model.ts +34 -16
- package/src/topaz-provider.ts +11 -11
- package/src/topaz-video-model-options.ts +0 -6
- package/src/topaz-video-model.ts +61 -105
- package/src/topaz-get-from-api.ts +0 -53
package/src/topaz-image-model.ts
CHANGED
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
AISDKError,
|
|
3
3
|
InvalidArgumentError,
|
|
4
4
|
InvalidResponseDataError,
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
5
|
+
type ImageModelV4,
|
|
6
|
+
type ImageModelV4CallOptions,
|
|
7
|
+
type ImageModelV4Result,
|
|
8
|
+
type SharedV4Warning,
|
|
8
9
|
} from '@ai-sdk/provider';
|
|
9
10
|
import {
|
|
10
11
|
combineHeaders,
|
|
@@ -12,16 +13,19 @@ import {
|
|
|
12
13
|
createBinaryResponseHandler,
|
|
13
14
|
createJsonResponseHandler,
|
|
14
15
|
delay,
|
|
16
|
+
getFromApi,
|
|
15
17
|
mediaTypeToExtension,
|
|
16
18
|
parseProviderOptions,
|
|
17
19
|
postFormDataToApi,
|
|
18
20
|
removeUndefinedEntries,
|
|
19
21
|
resolve,
|
|
22
|
+
serializeModelOptions,
|
|
23
|
+
WORKFLOW_DESERIALIZE,
|
|
24
|
+
WORKFLOW_SERIALIZE,
|
|
20
25
|
} from '@ai-sdk/provider-utils';
|
|
21
26
|
import { z } from 'zod/v4';
|
|
22
27
|
import type { TopazConfig } from './topaz-config';
|
|
23
28
|
import { topazFailedResponseHandler } from './topaz-error';
|
|
24
|
-
import { topazGetFromApi } from './topaz-get-from-api';
|
|
25
29
|
import {
|
|
26
30
|
topazImageModelOptionsSchema,
|
|
27
31
|
type TopazImageModelOptions,
|
|
@@ -38,8 +42,8 @@ const DEFAULT_POLL_TIMEOUT_MILLIS = 600_000;
|
|
|
38
42
|
* Topaz enhances an image that the caller supplies, so `files` is required and
|
|
39
43
|
* exactly one image is processed per call.
|
|
40
44
|
*/
|
|
41
|
-
export class TopazImageModel implements
|
|
42
|
-
readonly specificationVersion = '
|
|
45
|
+
export class TopazImageModel implements ImageModelV4 {
|
|
46
|
+
readonly specificationVersion = 'v4';
|
|
43
47
|
readonly maxImagesPerCall = 1;
|
|
44
48
|
readonly supportsFileInputs = true;
|
|
45
49
|
readonly supportsMaskInputs = false;
|
|
@@ -48,16 +52,30 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
48
52
|
return this.config.provider;
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
static [WORKFLOW_SERIALIZE](model: TopazImageModel) {
|
|
56
|
+
return serializeModelOptions({
|
|
57
|
+
modelId: model.modelId,
|
|
58
|
+
config: model.config,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
63
|
+
modelId: TopazImageModelId;
|
|
64
|
+
config: TopazConfig;
|
|
65
|
+
}) {
|
|
66
|
+
return new TopazImageModel(options.modelId, options.config);
|
|
67
|
+
}
|
|
68
|
+
|
|
51
69
|
constructor(
|
|
52
70
|
readonly modelId: TopazImageModelId,
|
|
53
71
|
private readonly config: TopazConfig,
|
|
54
72
|
) {}
|
|
55
73
|
|
|
56
74
|
async doGenerate(
|
|
57
|
-
options:
|
|
58
|
-
): Promise<
|
|
75
|
+
options: ImageModelV4CallOptions,
|
|
76
|
+
): Promise<ImageModelV4Result> {
|
|
59
77
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
60
|
-
const warnings:
|
|
78
|
+
const warnings: SharedV4Warning[] = [];
|
|
61
79
|
|
|
62
80
|
const topazOptions = await parseProviderOptions({
|
|
63
81
|
provider: 'topaz',
|
|
@@ -151,8 +169,8 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
private addUnsupportedWarnings(
|
|
154
|
-
options:
|
|
155
|
-
warnings:
|
|
172
|
+
options: ImageModelV4CallOptions,
|
|
173
|
+
warnings: SharedV4Warning[],
|
|
156
174
|
): void {
|
|
157
175
|
if (options.prompt != null) {
|
|
158
176
|
warnings.push({
|
|
@@ -202,9 +220,9 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
202
220
|
}
|
|
203
221
|
|
|
204
222
|
private async buildFormData(
|
|
205
|
-
options:
|
|
223
|
+
options: ImageModelV4CallOptions,
|
|
206
224
|
topazOptions: TopazImageModelOptions | undefined,
|
|
207
|
-
warnings:
|
|
225
|
+
warnings: SharedV4Warning[],
|
|
208
226
|
): Promise<FormData> {
|
|
209
227
|
const file = options.files?.[0];
|
|
210
228
|
|
|
@@ -298,7 +316,7 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
298
316
|
const deadline = Date.now() + pollTimeoutMillis;
|
|
299
317
|
|
|
300
318
|
while (true) {
|
|
301
|
-
const { value: status } = await
|
|
319
|
+
const { value: status } = await getFromApi({
|
|
302
320
|
url: `${this.config.baseURL}/image/v1/status/${processId}`,
|
|
303
321
|
// Built from the configured baseURL, not from response data.
|
|
304
322
|
validateUrl: false,
|
|
@@ -367,7 +385,7 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
367
385
|
image: Uint8Array;
|
|
368
386
|
responseHeaders: Record<string, string> | undefined;
|
|
369
387
|
}> {
|
|
370
|
-
const { value: downloadResponse } = await
|
|
388
|
+
const { value: downloadResponse } = await getFromApi({
|
|
371
389
|
url: `${this.config.baseURL}/image/v1/download/${processId}`,
|
|
372
390
|
// Built from the configured baseURL, not from response data.
|
|
373
391
|
validateUrl: false,
|
|
@@ -388,7 +406,7 @@ export class TopazImageModel implements ImageModelV3 {
|
|
|
388
406
|
});
|
|
389
407
|
}
|
|
390
408
|
|
|
391
|
-
const { value: image, responseHeaders } = await
|
|
409
|
+
const { value: image, responseHeaders } = await getFromApi({
|
|
392
410
|
url: downloadUrl,
|
|
393
411
|
// Comes from the Topaz response body and normally points at object
|
|
394
412
|
// storage, so it is validated and the API key is withheld off-origin.
|
package/src/topaz-provider.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NoSuchModelError,
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
type
|
|
3
|
+
type Experimental_VideoModelV4 as VideoModelV4,
|
|
4
|
+
type ImageModelV4,
|
|
5
|
+
type ProviderV4,
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
7
|
import {
|
|
8
8
|
loadApiKey,
|
|
@@ -42,26 +42,26 @@ export interface TopazProviderSettings {
|
|
|
42
42
|
fetch?: FetchFunction;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export interface TopazProvider extends
|
|
45
|
+
export interface TopazProvider extends ProviderV4 {
|
|
46
46
|
/**
|
|
47
47
|
* Creates a model for image enhancement.
|
|
48
48
|
*/
|
|
49
|
-
image(modelId: TopazImageModelId):
|
|
49
|
+
image(modelId: TopazImageModelId): ImageModelV4;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Creates a model for image enhancement.
|
|
53
53
|
*/
|
|
54
|
-
imageModel(modelId: TopazImageModelId):
|
|
54
|
+
imageModel(modelId: TopazImageModelId): ImageModelV4;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Creates a model for video enhancement.
|
|
58
58
|
*/
|
|
59
|
-
video(modelId: TopazVideoModelId):
|
|
59
|
+
video(modelId: TopazVideoModelId): VideoModelV4;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Creates a model for video enhancement.
|
|
63
63
|
*/
|
|
64
|
-
videoModel(modelId: TopazVideoModelId):
|
|
64
|
+
videoModel(modelId: TopazVideoModelId): VideoModelV4;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const defaultBaseURL = 'https://api.topazlabs.com';
|
|
@@ -89,7 +89,7 @@ export function createTopaz(
|
|
|
89
89
|
`ai-sdk-topaz/${VERSION}`,
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
-
const createImageModel = (modelId: TopazImageModelId):
|
|
92
|
+
const createImageModel = (modelId: TopazImageModelId): ImageModelV4 =>
|
|
93
93
|
new TopazImageModel(modelId, {
|
|
94
94
|
provider: 'topaz.image',
|
|
95
95
|
baseURL,
|
|
@@ -97,7 +97,7 @@ export function createTopaz(
|
|
|
97
97
|
fetch: options.fetch,
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
const createVideoModel = (modelId: TopazVideoModelId):
|
|
100
|
+
const createVideoModel = (modelId: TopazVideoModelId): VideoModelV4 =>
|
|
101
101
|
new TopazVideoModel(modelId, {
|
|
102
102
|
provider: 'topaz.video',
|
|
103
103
|
baseURL,
|
|
@@ -118,7 +118,7 @@ export function createTopaz(
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
const provider: TopazProvider = {
|
|
121
|
-
specificationVersion: '
|
|
121
|
+
specificationVersion: 'v4' as const,
|
|
122
122
|
image: createImageModel,
|
|
123
123
|
imageModel: createImageModel,
|
|
124
124
|
video: createVideoModel,
|
|
@@ -175,10 +175,6 @@ export type TopazVideoOutput = z.infer<typeof topazVideoOutputSchema>;
|
|
|
175
175
|
export const topazVideoModelOptionsSchema = lazySchema(() =>
|
|
176
176
|
zodSchema(
|
|
177
177
|
z.object({
|
|
178
|
-
/** Interval between status polls, in milliseconds. Defaults to 2000. */
|
|
179
|
-
pollIntervalMillis: z.number().positive().optional(),
|
|
180
|
-
/** Maximum time to poll, in milliseconds. Defaults to 3600000 (1 hour). */
|
|
181
|
-
pollTimeoutMillis: z.number().positive().optional(),
|
|
182
178
|
source: topazVideoSourceSchema.optional(),
|
|
183
179
|
output: topazVideoOutputSchema.optional(),
|
|
184
180
|
|
|
@@ -280,8 +276,6 @@ export type TopazVideoModelOptions = InferSchema<
|
|
|
280
276
|
* forwarded into the `filters[]` entry.
|
|
281
277
|
*/
|
|
282
278
|
export const TOPAZ_NON_FILTER_OPTION_KEYS = [
|
|
283
|
-
'pollIntervalMillis',
|
|
284
|
-
'pollTimeoutMillis',
|
|
285
279
|
'source',
|
|
286
280
|
'output',
|
|
287
281
|
'additionalFilters',
|
package/src/topaz-video-model.ts
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AISDKError,
|
|
3
2
|
APICallError,
|
|
4
3
|
InvalidArgumentError,
|
|
5
4
|
InvalidResponseDataError,
|
|
6
|
-
type
|
|
7
|
-
type
|
|
8
|
-
type
|
|
5
|
+
type Experimental_VideoModelV4 as VideoModelV4,
|
|
6
|
+
type Experimental_VideoModelV4File as VideoModelV4File,
|
|
7
|
+
type Experimental_VideoModelV4OperationStartResult as VideoModelV4OperationStartResult,
|
|
8
|
+
type Experimental_VideoModelV4OperationStatusResult as VideoModelV4OperationStatusResult,
|
|
9
|
+
type SharedV4Warning,
|
|
9
10
|
} from '@ai-sdk/provider';
|
|
10
11
|
import {
|
|
11
12
|
combineHeaders,
|
|
12
13
|
convertBase64ToUint8Array,
|
|
13
14
|
createJsonResponseHandler,
|
|
14
|
-
|
|
15
|
+
getFromApi,
|
|
15
16
|
isSameOrigin,
|
|
16
17
|
parseProviderOptions,
|
|
17
18
|
postJsonToApi,
|
|
18
19
|
removeUndefinedEntries,
|
|
19
20
|
resolve,
|
|
21
|
+
serializeModelOptions,
|
|
20
22
|
validateDownloadUrl,
|
|
21
23
|
withUserAgentSuffix,
|
|
24
|
+
WORKFLOW_DESERIALIZE,
|
|
25
|
+
WORKFLOW_SERIALIZE,
|
|
22
26
|
} from '@ai-sdk/provider-utils';
|
|
23
27
|
import { z } from 'zod/v4';
|
|
24
28
|
import type { TopazConfig } from './topaz-config';
|
|
25
29
|
import { topazFailedResponseHandler } from './topaz-error';
|
|
26
|
-
import { topazGetFromApi } from './topaz-get-from-api';
|
|
27
30
|
import {
|
|
28
31
|
TOPAZ_NON_FILTER_OPTION_KEYS,
|
|
29
32
|
type topazOutputContainers,
|
|
@@ -37,12 +40,6 @@ import {
|
|
|
37
40
|
} from './topaz-video-settings';
|
|
38
41
|
import { VERSION } from './version';
|
|
39
42
|
|
|
40
|
-
type VideoModelResult = Awaited<ReturnType<VideoModelV3['doGenerate']>>;
|
|
41
|
-
type TopazVideoOperation = { requestId: string; outputContainer: string };
|
|
42
|
-
|
|
43
|
-
const DEFAULT_POLL_INTERVAL_MILLIS = 2000;
|
|
44
|
-
const DEFAULT_POLL_TIMEOUT_MILLIS = 3_600_000;
|
|
45
|
-
|
|
46
43
|
type TopazSourceContainer = (typeof topazSourceContainers)[number];
|
|
47
44
|
type TopazOutputContainer = (typeof topazOutputContainers)[number];
|
|
48
45
|
|
|
@@ -87,91 +84,42 @@ const containerMediaTypes: Record<string, string> = {
|
|
|
87
84
|
|
|
88
85
|
/**
|
|
89
86
|
* Topaz video models enhance a video the caller supplies, passed through
|
|
90
|
-
* `inputReferences`. `
|
|
87
|
+
* `inputReferences`. `doStart` creates an express request: Topaz fetches URL
|
|
91
88
|
* inputs itself, and file inputs are uploaded to the returned URL. Processing
|
|
92
|
-
* starts once Topaz has the video, and
|
|
89
|
+
* starts once Topaz has the video, and `doStatus` polls.
|
|
93
90
|
*/
|
|
94
|
-
export class TopazVideoModel implements
|
|
95
|
-
readonly specificationVersion = '
|
|
91
|
+
export class TopazVideoModel implements VideoModelV4 {
|
|
92
|
+
readonly specificationVersion = 'v4';
|
|
96
93
|
readonly maxVideosPerCall = 1;
|
|
97
94
|
|
|
98
95
|
get provider(): string {
|
|
99
96
|
return this.config.provider;
|
|
100
97
|
}
|
|
101
98
|
|
|
99
|
+
static [WORKFLOW_SERIALIZE](model: TopazVideoModel) {
|
|
100
|
+
return serializeModelOptions({
|
|
101
|
+
modelId: model.modelId,
|
|
102
|
+
config: model.config,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
107
|
+
modelId: TopazVideoModelId;
|
|
108
|
+
config: TopazConfig;
|
|
109
|
+
}) {
|
|
110
|
+
return new TopazVideoModel(options.modelId, options.config);
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
constructor(
|
|
103
114
|
readonly modelId: TopazVideoModelId,
|
|
104
115
|
private readonly config: TopazConfig,
|
|
105
116
|
) {}
|
|
106
117
|
|
|
107
|
-
async
|
|
108
|
-
options: Parameters<
|
|
109
|
-
): Promise<
|
|
110
|
-
const topazOptions = await parseProviderOptions({
|
|
111
|
-
provider: 'topaz',
|
|
112
|
-
providerOptions: options.providerOptions,
|
|
113
|
-
schema: topazVideoModelOptionsSchema,
|
|
114
|
-
});
|
|
115
|
-
const started = await this.start(options);
|
|
116
|
-
const pollIntervalMillis =
|
|
117
|
-
topazOptions?.pollIntervalMillis ?? DEFAULT_POLL_INTERVAL_MILLIS;
|
|
118
|
-
const pollTimeoutMillis =
|
|
119
|
-
topazOptions?.pollTimeoutMillis ?? DEFAULT_POLL_TIMEOUT_MILLIS;
|
|
120
|
-
const deadline = Date.now() + pollTimeoutMillis;
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
while (true) {
|
|
124
|
-
const result = await this.getStatus({
|
|
125
|
-
operation: started.operation,
|
|
126
|
-
headers: options.headers,
|
|
127
|
-
abortSignal: options.abortSignal,
|
|
128
|
-
});
|
|
129
|
-
if (result != null) {
|
|
130
|
-
return {
|
|
131
|
-
...result,
|
|
132
|
-
warnings: [...started.warnings, ...result.warnings],
|
|
133
|
-
response: {
|
|
134
|
-
...result.response,
|
|
135
|
-
timestamp: started.response.timestamp,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (Date.now() + pollIntervalMillis > deadline) {
|
|
141
|
-
const headers = combineHeaders(
|
|
142
|
-
await resolve(this.config.headers),
|
|
143
|
-
options.headers,
|
|
144
|
-
);
|
|
145
|
-
await this.cancelQuietly(started.operation.requestId, headers);
|
|
146
|
-
throw new AISDKError({
|
|
147
|
-
name: 'TOPAZ_VIDEO_ENHANCEMENT_TIMEOUT',
|
|
148
|
-
message:
|
|
149
|
-
`Topaz video enhancement did not finish within ${pollTimeoutMillis}ms ` +
|
|
150
|
-
`(request ${started.operation.requestId}). ` +
|
|
151
|
-
'Increase the `pollTimeoutMillis` provider option if the job needs longer.',
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
await delay(pollIntervalMillis, { abortSignal: options.abortSignal });
|
|
155
|
-
}
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (options.abortSignal?.aborted) {
|
|
158
|
-
const headers = combineHeaders(
|
|
159
|
-
await resolve(this.config.headers),
|
|
160
|
-
options.headers,
|
|
161
|
-
);
|
|
162
|
-
await this.cancelQuietly(started.operation.requestId, headers);
|
|
163
|
-
}
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private async start(
|
|
169
|
-
options: Parameters<VideoModelV3['doGenerate']>[0],
|
|
170
|
-
): Promise<
|
|
171
|
-
Omit<VideoModelResult, 'videos'> & { operation: TopazVideoOperation }
|
|
172
|
-
> {
|
|
118
|
+
async doStart(
|
|
119
|
+
options: Parameters<NonNullable<VideoModelV4['doStart']>>[0],
|
|
120
|
+
): Promise<VideoModelV4OperationStartResult> {
|
|
173
121
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
174
|
-
const warnings:
|
|
122
|
+
const warnings: SharedV4Warning[] = [];
|
|
175
123
|
|
|
176
124
|
const topazOptions = await parseProviderOptions({
|
|
177
125
|
provider: 'topaz',
|
|
@@ -248,7 +196,7 @@ export class TopazVideoModel implements VideoModelV3 {
|
|
|
248
196
|
}
|
|
249
197
|
|
|
250
198
|
return {
|
|
251
|
-
// The output container travels with the operation so
|
|
199
|
+
// The output container travels with the operation so `doStatus` can
|
|
252
200
|
// report the right media type without re-deriving it.
|
|
253
201
|
operation: { requestId, outputContainer: output.container },
|
|
254
202
|
warnings,
|
|
@@ -277,18 +225,16 @@ export class TopazVideoModel implements VideoModelV3 {
|
|
|
277
225
|
}
|
|
278
226
|
}
|
|
279
227
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
abortSignal?: AbortSignal;
|
|
284
|
-
}): Promise<VideoModelResult | undefined> {
|
|
228
|
+
async doStatus(
|
|
229
|
+
options: Parameters<NonNullable<VideoModelV4['doStatus']>>[0],
|
|
230
|
+
): Promise<VideoModelV4OperationStatusResult> {
|
|
285
231
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
286
232
|
const { requestId, outputContainer } = options.operation as {
|
|
287
233
|
requestId: string;
|
|
288
234
|
outputContainer?: string;
|
|
289
235
|
};
|
|
290
236
|
|
|
291
|
-
const { value: status, responseHeaders } = await
|
|
237
|
+
const { value: status, responseHeaders } = await getFromApi({
|
|
292
238
|
url: `${this.config.baseURL}/video/${requestId}/status`,
|
|
293
239
|
// Built from the configured baseURL, not from response data.
|
|
294
240
|
validateUrl: false,
|
|
@@ -324,6 +270,7 @@ export class TopazVideoModel implements VideoModelV3 {
|
|
|
324
270
|
}
|
|
325
271
|
|
|
326
272
|
return {
|
|
273
|
+
status: 'completed',
|
|
327
274
|
videos: [
|
|
328
275
|
{
|
|
329
276
|
type: 'url',
|
|
@@ -355,24 +302,33 @@ export class TopazVideoModel implements VideoModelV3 {
|
|
|
355
302
|
}
|
|
356
303
|
|
|
357
304
|
if (status.status === 'failed' || status.status === 'canceled') {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
305
|
+
return {
|
|
306
|
+
status: 'error',
|
|
307
|
+
error:
|
|
361
308
|
`Topaz video request ${requestId} ${status.status}` +
|
|
362
309
|
(status.errorCode != null ? ` (${status.errorCode})` : '') +
|
|
363
310
|
(status.message != null ? `: ${status.message}` : '.'),
|
|
364
|
-
|
|
311
|
+
providerMetadata: {
|
|
312
|
+
topaz: {
|
|
313
|
+
requestId,
|
|
314
|
+
...(status.errorCode != null
|
|
315
|
+
? { errorCode: status.errorCode }
|
|
316
|
+
: {}),
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
response,
|
|
320
|
+
};
|
|
365
321
|
}
|
|
366
322
|
|
|
367
323
|
// Documented in-progress values are requested, accepted, initializing,
|
|
368
324
|
// preprocessing, processing, postprocessing and canceling. Anything else
|
|
369
325
|
// keeps polling too, so a newly added state cannot break running jobs.
|
|
370
|
-
return
|
|
326
|
+
return { status: 'pending', response };
|
|
371
327
|
}
|
|
372
328
|
|
|
373
329
|
private addUnsupportedWarnings(
|
|
374
|
-
options: Parameters<
|
|
375
|
-
warnings:
|
|
330
|
+
options: Parameters<NonNullable<VideoModelV4['doStart']>>[0],
|
|
331
|
+
warnings: SharedV4Warning[],
|
|
376
332
|
): void {
|
|
377
333
|
// `generateVideo` requires a prompt, so an empty one is the expected way
|
|
378
334
|
// to call an enhancement model and does not warrant a warning.
|
|
@@ -445,9 +401,9 @@ export class TopazVideoModel implements VideoModelV3 {
|
|
|
445
401
|
}
|
|
446
402
|
|
|
447
403
|
private selectInputVideo(
|
|
448
|
-
options: Parameters<
|
|
449
|
-
warnings:
|
|
450
|
-
):
|
|
404
|
+
options: Parameters<NonNullable<VideoModelV4['doStart']>>[0],
|
|
405
|
+
warnings: SharedV4Warning[],
|
|
406
|
+
): VideoModelV4File {
|
|
451
407
|
const references = options.inputReferences ?? [];
|
|
452
408
|
const videos = references.filter(isVideoReference);
|
|
453
409
|
|
|
@@ -560,7 +516,7 @@ function lowerBoundCredits(
|
|
|
560
516
|
}
|
|
561
517
|
|
|
562
518
|
function fileBytes(
|
|
563
|
-
input: Extract<
|
|
519
|
+
input: Extract<VideoModelV4File, { type: 'file' }>,
|
|
564
520
|
): Uint8Array {
|
|
565
521
|
return typeof input.data === 'string'
|
|
566
522
|
? convertBase64ToUint8Array(input.data)
|
|
@@ -568,7 +524,7 @@ function fileBytes(
|
|
|
568
524
|
}
|
|
569
525
|
|
|
570
526
|
function resolveContainer(
|
|
571
|
-
input:
|
|
527
|
+
input: VideoModelV4File,
|
|
572
528
|
declaredContainer: TopazSourceContainer | undefined,
|
|
573
529
|
): TopazSourceContainer {
|
|
574
530
|
if (declaredContainer != null) {
|
|
@@ -607,7 +563,7 @@ function resolveContainer(
|
|
|
607
563
|
return container;
|
|
608
564
|
}
|
|
609
565
|
|
|
610
|
-
function isVideoReference(reference:
|
|
566
|
+
function isVideoReference(reference: VideoModelV4File): boolean {
|
|
611
567
|
if (reference.type === 'file') {
|
|
612
568
|
return reference.mediaType.toLowerCase().startsWith('video/');
|
|
613
569
|
}
|
|
@@ -709,7 +665,7 @@ function buildOutput({
|
|
|
709
665
|
topazOptions,
|
|
710
666
|
source,
|
|
711
667
|
}: {
|
|
712
|
-
options: Parameters<
|
|
668
|
+
options: Parameters<NonNullable<VideoModelV4['doStart']>>[0];
|
|
713
669
|
topazOptions: TopazVideoModelOptions | undefined;
|
|
714
670
|
source: ResolvedSource;
|
|
715
671
|
}): Record<string, unknown> & { container: TopazOutputContainer } {
|
|
@@ -762,7 +718,7 @@ function buildOutput({
|
|
|
762
718
|
}
|
|
763
719
|
|
|
764
720
|
/**
|
|
765
|
-
* Mirrors Topaz's container rules so
|
|
721
|
+
* Mirrors Topaz's container rules so `doStatus` can report the media type of
|
|
766
722
|
* the file Topaz will actually produce.
|
|
767
723
|
*/
|
|
768
724
|
function resolveOutputContainer(
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fetchWithValidatedEndpoint,
|
|
3
|
-
fetchWithValidatedRedirects,
|
|
4
|
-
getFromApi,
|
|
5
|
-
isSameOrigin,
|
|
6
|
-
type FetchFunction,
|
|
7
|
-
} from '@ai-sdk/provider-utils';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Adapts URL validation to v6's getFromApi, which does not yet expose the
|
|
11
|
-
* validateUrl and credentialedOrigin options.
|
|
12
|
-
*/
|
|
13
|
-
export async function topazGetFromApi<T>({
|
|
14
|
-
validateUrl,
|
|
15
|
-
credentialedOrigin,
|
|
16
|
-
trustedOrigin,
|
|
17
|
-
...options
|
|
18
|
-
}: Parameters<typeof getFromApi<T>>[0] & {
|
|
19
|
-
validateUrl: boolean;
|
|
20
|
-
credentialedOrigin?: string;
|
|
21
|
-
trustedOrigin?: string;
|
|
22
|
-
}) {
|
|
23
|
-
const validatedFetch: FetchFunction = (url, init) =>
|
|
24
|
-
fetchWithValidatedRedirects({
|
|
25
|
-
url: url.toString(),
|
|
26
|
-
headers: init?.headers,
|
|
27
|
-
abortSignal: init?.signal ?? undefined,
|
|
28
|
-
trustedOrigin,
|
|
29
|
-
fetch: (hopUrl, hopInit) =>
|
|
30
|
-
fetchWithValidatedEndpoint({
|
|
31
|
-
url: hopUrl.toString(),
|
|
32
|
-
init: {
|
|
33
|
-
...hopInit,
|
|
34
|
-
headers:
|
|
35
|
-
credentialedOrigin != null &&
|
|
36
|
-
isSameOrigin(hopUrl.toString(), credentialedOrigin)
|
|
37
|
-
? hopInit?.headers
|
|
38
|
-
: undefined,
|
|
39
|
-
},
|
|
40
|
-
trustedOrigin,
|
|
41
|
-
redirect: 'manual',
|
|
42
|
-
fetch: options.fetch,
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// v6 ignores this flag; validation is handled by the injected fetch above.
|
|
47
|
-
const requestOptions = {
|
|
48
|
-
...options,
|
|
49
|
-
validateUrl: false,
|
|
50
|
-
fetch: validateUrl ? validatedFetch : options.fetch,
|
|
51
|
-
};
|
|
52
|
-
return getFromApi(requestOptions);
|
|
53
|
-
}
|