@depup/ai-sdk__google 3.0.43-depup.0
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 +2531 -0
- package/LICENSE +13 -0
- package/README.md +25 -0
- package/changes.json +5 -0
- package/dist/index.d.mts +367 -0
- package/dist/index.d.ts +367 -0
- package/dist/index.js +2404 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2454 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal/index.d.mts +283 -0
- package/dist/internal/index.d.ts +283 -0
- package/dist/internal/index.js +1670 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/internal/index.mjs +1678 -0
- package/dist/internal/index.mjs.map +1 -0
- package/docs/15-google-generative-ai.mdx +1298 -0
- package/internal.d.ts +1 -0
- package/package.json +96 -0
- package/src/convert-google-generative-ai-usage.ts +51 -0
- package/src/convert-json-schema-to-openapi-schema.ts +158 -0
- package/src/convert-to-google-generative-ai-messages.ts +236 -0
- package/src/get-model-path.ts +3 -0
- package/src/google-error.ts +26 -0
- package/src/google-generative-ai-embedding-model.ts +159 -0
- package/src/google-generative-ai-embedding-options.ts +51 -0
- package/src/google-generative-ai-image-model.ts +359 -0
- package/src/google-generative-ai-image-settings.ts +17 -0
- package/src/google-generative-ai-language-model.ts +1056 -0
- package/src/google-generative-ai-options.ts +198 -0
- package/src/google-generative-ai-prompt.ts +38 -0
- package/src/google-generative-ai-video-model.ts +374 -0
- package/src/google-generative-ai-video-settings.ts +8 -0
- package/src/google-prepare-tools.ts +254 -0
- package/src/google-provider.ts +227 -0
- package/src/google-supported-file-url.ts +20 -0
- package/src/google-tools.ts +71 -0
- package/src/index.ts +29 -0
- package/src/internal/index.ts +3 -0
- package/src/map-google-generative-ai-finish-reason.ts +29 -0
- package/src/tool/code-execution.ts +35 -0
- package/src/tool/enterprise-web-search.ts +18 -0
- package/src/tool/file-search.ts +51 -0
- package/src/tool/google-maps.ts +14 -0
- package/src/tool/google-search.ts +43 -0
- package/src/tool/url-context.ts +16 -0
- package/src/tool/vertex-rag-store.ts +31 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
export type GoogleGenerativeAIModelId =
|
|
5
|
+
// Stable models
|
|
6
|
+
// https://ai.google.dev/gemini-api/docs/models/gemini
|
|
7
|
+
| 'gemini-2.0-flash'
|
|
8
|
+
| 'gemini-2.0-flash-001'
|
|
9
|
+
| 'gemini-2.0-flash-lite'
|
|
10
|
+
| 'gemini-2.0-flash-exp-image-generation'
|
|
11
|
+
| 'gemini-2.0-flash-lite-001'
|
|
12
|
+
| 'gemini-2.5-pro'
|
|
13
|
+
| 'gemini-2.5-flash'
|
|
14
|
+
| 'gemini-2.5-flash-image'
|
|
15
|
+
| 'gemini-2.5-flash-lite'
|
|
16
|
+
| 'gemini-2.5-flash-lite-preview-09-2025'
|
|
17
|
+
| 'gemini-2.5-flash-preview-tts'
|
|
18
|
+
| 'gemini-2.5-pro-preview-tts'
|
|
19
|
+
| 'gemini-2.5-flash-native-audio-latest'
|
|
20
|
+
| 'gemini-2.5-flash-native-audio-preview-09-2025'
|
|
21
|
+
| 'gemini-2.5-flash-native-audio-preview-12-2025'
|
|
22
|
+
| 'gemini-2.5-computer-use-preview-10-2025'
|
|
23
|
+
| 'gemini-3-pro-preview'
|
|
24
|
+
| 'gemini-3-pro-image-preview'
|
|
25
|
+
| 'gemini-3-flash-preview'
|
|
26
|
+
| 'gemini-3.1-pro-preview'
|
|
27
|
+
| 'gemini-3.1-pro-preview-customtools'
|
|
28
|
+
| 'gemini-3.1-flash-image-preview'
|
|
29
|
+
| 'gemini-3.1-flash-lite-preview'
|
|
30
|
+
// latest version
|
|
31
|
+
// https://ai.google.dev/gemini-api/docs/models#latest
|
|
32
|
+
| 'gemini-pro-latest'
|
|
33
|
+
| 'gemini-flash-latest'
|
|
34
|
+
| 'gemini-flash-lite-latest'
|
|
35
|
+
| 'deep-research-pro-preview-12-2025'
|
|
36
|
+
| 'nano-banana-pro-preview'
|
|
37
|
+
| 'aqa'
|
|
38
|
+
// Experimental models
|
|
39
|
+
// https://ai.google.dev/gemini-api/docs/models/experimental-models
|
|
40
|
+
| 'gemini-robotics-er-1.5-preview'
|
|
41
|
+
| 'gemma-3-1b-it'
|
|
42
|
+
| 'gemma-3-4b-it'
|
|
43
|
+
| 'gemma-3n-e4b-it'
|
|
44
|
+
| 'gemma-3n-e2b-it'
|
|
45
|
+
| 'gemma-3-12b-it'
|
|
46
|
+
| 'gemma-3-27b-it'
|
|
47
|
+
| (string & {});
|
|
48
|
+
|
|
49
|
+
export const googleLanguageModelOptions = lazySchema(() =>
|
|
50
|
+
zodSchema(
|
|
51
|
+
z.object({
|
|
52
|
+
responseModalities: z.array(z.enum(['TEXT', 'IMAGE'])).optional(),
|
|
53
|
+
|
|
54
|
+
thinkingConfig: z
|
|
55
|
+
.object({
|
|
56
|
+
thinkingBudget: z.number().optional(),
|
|
57
|
+
includeThoughts: z.boolean().optional(),
|
|
58
|
+
// https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#thinking_level
|
|
59
|
+
thinkingLevel: z
|
|
60
|
+
.enum(['minimal', 'low', 'medium', 'high'])
|
|
61
|
+
.optional(),
|
|
62
|
+
})
|
|
63
|
+
.optional(),
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Optional.
|
|
67
|
+
* The name of the cached content used as context to serve the prediction.
|
|
68
|
+
* Format: cachedContents/{cachedContent}
|
|
69
|
+
*/
|
|
70
|
+
cachedContent: z.string().optional(),
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Optional. Enable structured output. Default is true.
|
|
74
|
+
*
|
|
75
|
+
* This is useful when the JSON Schema contains elements that are
|
|
76
|
+
* not supported by the OpenAPI schema version that
|
|
77
|
+
* Google Generative AI uses. You can use this to disable
|
|
78
|
+
* structured outputs if you need to.
|
|
79
|
+
*/
|
|
80
|
+
structuredOutputs: z.boolean().optional(),
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Optional. A list of unique safety settings for blocking unsafe content.
|
|
84
|
+
*/
|
|
85
|
+
safetySettings: z
|
|
86
|
+
.array(
|
|
87
|
+
z.object({
|
|
88
|
+
category: z.enum([
|
|
89
|
+
'HARM_CATEGORY_UNSPECIFIED',
|
|
90
|
+
'HARM_CATEGORY_HATE_SPEECH',
|
|
91
|
+
'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
92
|
+
'HARM_CATEGORY_HARASSMENT',
|
|
93
|
+
'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
94
|
+
'HARM_CATEGORY_CIVIC_INTEGRITY',
|
|
95
|
+
]),
|
|
96
|
+
threshold: z.enum([
|
|
97
|
+
'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
98
|
+
'BLOCK_LOW_AND_ABOVE',
|
|
99
|
+
'BLOCK_MEDIUM_AND_ABOVE',
|
|
100
|
+
'BLOCK_ONLY_HIGH',
|
|
101
|
+
'BLOCK_NONE',
|
|
102
|
+
'OFF',
|
|
103
|
+
]),
|
|
104
|
+
}),
|
|
105
|
+
)
|
|
106
|
+
.optional(),
|
|
107
|
+
|
|
108
|
+
threshold: z
|
|
109
|
+
.enum([
|
|
110
|
+
'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
111
|
+
'BLOCK_LOW_AND_ABOVE',
|
|
112
|
+
'BLOCK_MEDIUM_AND_ABOVE',
|
|
113
|
+
'BLOCK_ONLY_HIGH',
|
|
114
|
+
'BLOCK_NONE',
|
|
115
|
+
'OFF',
|
|
116
|
+
])
|
|
117
|
+
.optional(),
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Optional. Enables timestamp understanding for audio-only files.
|
|
121
|
+
*
|
|
122
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
|
|
123
|
+
*/
|
|
124
|
+
audioTimestamp: z.boolean().optional(),
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Optional. Defines labels used in billing reports. Available on Vertex AI only.
|
|
128
|
+
*
|
|
129
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls
|
|
130
|
+
*/
|
|
131
|
+
labels: z.record(z.string(), z.string()).optional(),
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Optional. If specified, the media resolution specified will be used.
|
|
135
|
+
*
|
|
136
|
+
* https://ai.google.dev/api/generate-content#MediaResolution
|
|
137
|
+
*/
|
|
138
|
+
mediaResolution: z
|
|
139
|
+
.enum([
|
|
140
|
+
'MEDIA_RESOLUTION_UNSPECIFIED',
|
|
141
|
+
'MEDIA_RESOLUTION_LOW',
|
|
142
|
+
'MEDIA_RESOLUTION_MEDIUM',
|
|
143
|
+
'MEDIA_RESOLUTION_HIGH',
|
|
144
|
+
])
|
|
145
|
+
.optional(),
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Optional. Configures the image generation aspect ratio for Gemini models.
|
|
149
|
+
*
|
|
150
|
+
* https://ai.google.dev/gemini-api/docs/image-generation#aspect_ratios
|
|
151
|
+
*/
|
|
152
|
+
imageConfig: z
|
|
153
|
+
.object({
|
|
154
|
+
aspectRatio: z
|
|
155
|
+
.enum([
|
|
156
|
+
'1:1',
|
|
157
|
+
'2:3',
|
|
158
|
+
'3:2',
|
|
159
|
+
'3:4',
|
|
160
|
+
'4:3',
|
|
161
|
+
'4:5',
|
|
162
|
+
'5:4',
|
|
163
|
+
'9:16',
|
|
164
|
+
'16:9',
|
|
165
|
+
'21:9',
|
|
166
|
+
'1:8',
|
|
167
|
+
'8:1',
|
|
168
|
+
'1:4',
|
|
169
|
+
'4:1',
|
|
170
|
+
])
|
|
171
|
+
.optional(),
|
|
172
|
+
imageSize: z.enum(['1K', '2K', '4K', '512']).optional(),
|
|
173
|
+
})
|
|
174
|
+
.optional(),
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Optional. Configuration for grounding retrieval.
|
|
178
|
+
* Used to provide location context for Google Maps and Google Search grounding.
|
|
179
|
+
*
|
|
180
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
|
|
181
|
+
*/
|
|
182
|
+
retrievalConfig: z
|
|
183
|
+
.object({
|
|
184
|
+
latLng: z
|
|
185
|
+
.object({
|
|
186
|
+
latitude: z.number(),
|
|
187
|
+
longitude: z.number(),
|
|
188
|
+
})
|
|
189
|
+
.optional(),
|
|
190
|
+
})
|
|
191
|
+
.optional(),
|
|
192
|
+
}),
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
export type GoogleLanguageModelOptions = InferSchema<
|
|
197
|
+
typeof googleLanguageModelOptions
|
|
198
|
+
>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GroundingMetadataSchema,
|
|
3
|
+
UrlContextMetadataSchema,
|
|
4
|
+
} from './google-generative-ai-language-model';
|
|
5
|
+
import { type SafetyRatingSchema } from './google-generative-ai-language-model';
|
|
6
|
+
|
|
7
|
+
export type GoogleGenerativeAIPrompt = {
|
|
8
|
+
systemInstruction?: GoogleGenerativeAISystemInstruction;
|
|
9
|
+
contents: Array<GoogleGenerativeAIContent>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type GoogleGenerativeAISystemInstruction = {
|
|
13
|
+
parts: Array<{ text: string }>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type GoogleGenerativeAIContent = {
|
|
17
|
+
role: 'user' | 'model';
|
|
18
|
+
parts: Array<GoogleGenerativeAIContentPart>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type GoogleGenerativeAIContentPart =
|
|
22
|
+
| { text: string; thought?: boolean; thoughtSignature?: string }
|
|
23
|
+
| { inlineData: { mimeType: string; data: string } }
|
|
24
|
+
| { functionCall: { name: string; args: unknown }; thoughtSignature?: string }
|
|
25
|
+
| { functionResponse: { name: string; response: unknown } }
|
|
26
|
+
| { fileData: { mimeType: string; fileUri: string } };
|
|
27
|
+
|
|
28
|
+
export type GoogleGenerativeAIGroundingMetadata = GroundingMetadataSchema;
|
|
29
|
+
|
|
30
|
+
export type GoogleGenerativeAIUrlContextMetadata = UrlContextMetadataSchema;
|
|
31
|
+
|
|
32
|
+
export type GoogleGenerativeAISafetyRating = SafetyRatingSchema;
|
|
33
|
+
|
|
34
|
+
export interface GoogleGenerativeAIProviderMetadata {
|
|
35
|
+
groundingMetadata: GoogleGenerativeAIGroundingMetadata | null;
|
|
36
|
+
urlContextMetadata: GoogleGenerativeAIUrlContextMetadata | null;
|
|
37
|
+
safetyRatings: GoogleGenerativeAISafetyRating[] | null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AISDKError,
|
|
3
|
+
type Experimental_VideoModelV3,
|
|
4
|
+
type SharedV3Warning,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
combineHeaders,
|
|
8
|
+
convertUint8ArrayToBase64,
|
|
9
|
+
createJsonResponseHandler,
|
|
10
|
+
delay,
|
|
11
|
+
type FetchFunction,
|
|
12
|
+
getFromApi,
|
|
13
|
+
lazySchema,
|
|
14
|
+
parseProviderOptions,
|
|
15
|
+
postJsonToApi,
|
|
16
|
+
type Resolvable,
|
|
17
|
+
resolve,
|
|
18
|
+
zodSchema,
|
|
19
|
+
} from '@ai-sdk/provider-utils';
|
|
20
|
+
import { z } from 'zod/v4';
|
|
21
|
+
import { googleFailedResponseHandler } from './google-error';
|
|
22
|
+
import type { GoogleGenerativeAIVideoModelId } from './google-generative-ai-video-settings';
|
|
23
|
+
|
|
24
|
+
export type GoogleVideoModelOptions = {
|
|
25
|
+
// Polling configuration
|
|
26
|
+
pollIntervalMs?: number | null;
|
|
27
|
+
pollTimeoutMs?: number | null;
|
|
28
|
+
|
|
29
|
+
// Video generation options
|
|
30
|
+
personGeneration?: 'dont_allow' | 'allow_adult' | 'allow_all' | null;
|
|
31
|
+
negativePrompt?: string | null;
|
|
32
|
+
|
|
33
|
+
// Reference images (for style/asset reference)
|
|
34
|
+
referenceImages?: Array<{
|
|
35
|
+
bytesBase64Encoded?: string;
|
|
36
|
+
gcsUri?: string;
|
|
37
|
+
}> | null;
|
|
38
|
+
|
|
39
|
+
[key: string]: unknown; // For passthrough
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
interface GoogleGenerativeAIVideoModelConfig {
|
|
43
|
+
provider: string;
|
|
44
|
+
baseURL: string;
|
|
45
|
+
headers?: Resolvable<Record<string, string | undefined>>;
|
|
46
|
+
fetch?: FetchFunction;
|
|
47
|
+
generateId?: () => string;
|
|
48
|
+
_internal?: {
|
|
49
|
+
currentDate?: () => Date;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class GoogleGenerativeAIVideoModel implements Experimental_VideoModelV3 {
|
|
54
|
+
readonly specificationVersion = 'v3';
|
|
55
|
+
|
|
56
|
+
get provider(): string {
|
|
57
|
+
return this.config.provider;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get maxVideosPerCall(): number {
|
|
61
|
+
// Google supports multiple videos via sampleCount
|
|
62
|
+
return 4;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
constructor(
|
|
66
|
+
readonly modelId: GoogleGenerativeAIVideoModelId,
|
|
67
|
+
private readonly config: GoogleGenerativeAIVideoModelConfig,
|
|
68
|
+
) {}
|
|
69
|
+
|
|
70
|
+
async doGenerate(
|
|
71
|
+
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
72
|
+
): Promise<Awaited<ReturnType<Experimental_VideoModelV3['doGenerate']>>> {
|
|
73
|
+
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
74
|
+
const warnings: SharedV3Warning[] = [];
|
|
75
|
+
|
|
76
|
+
const googleOptions = (await parseProviderOptions({
|
|
77
|
+
provider: 'google',
|
|
78
|
+
providerOptions: options.providerOptions,
|
|
79
|
+
schema: googleVideoModelOptionsSchema,
|
|
80
|
+
})) as GoogleVideoModelOptions | undefined;
|
|
81
|
+
|
|
82
|
+
const instances: Array<Record<string, unknown>> = [{}];
|
|
83
|
+
const instance = instances[0];
|
|
84
|
+
|
|
85
|
+
if (options.prompt != null) {
|
|
86
|
+
instance.prompt = options.prompt;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle image-to-video: convert image to base64
|
|
90
|
+
if (options.image != null) {
|
|
91
|
+
if (options.image.type === 'url') {
|
|
92
|
+
warnings.push({
|
|
93
|
+
type: 'unsupported',
|
|
94
|
+
feature: 'URL-based image input',
|
|
95
|
+
details:
|
|
96
|
+
'Google Generative AI video models require base64-encoded images. URL will be ignored.',
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
const base64Data =
|
|
100
|
+
typeof options.image.data === 'string'
|
|
101
|
+
? options.image.data
|
|
102
|
+
: convertUint8ArrayToBase64(options.image.data);
|
|
103
|
+
|
|
104
|
+
instance.image = {
|
|
105
|
+
inlineData: {
|
|
106
|
+
mimeType: options.image.mediaType || 'image/png',
|
|
107
|
+
data: base64Data,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (googleOptions?.referenceImages != null) {
|
|
114
|
+
instance.referenceImages = googleOptions.referenceImages.map(refImg => {
|
|
115
|
+
if (refImg.bytesBase64Encoded) {
|
|
116
|
+
return {
|
|
117
|
+
inlineData: {
|
|
118
|
+
mimeType: 'image/png',
|
|
119
|
+
data: refImg.bytesBase64Encoded,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
} else if (refImg.gcsUri) {
|
|
123
|
+
return {
|
|
124
|
+
gcsUri: refImg.gcsUri,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return refImg;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parameters: Record<string, unknown> = {
|
|
132
|
+
sampleCount: options.n,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (options.aspectRatio) {
|
|
136
|
+
parameters.aspectRatio = options.aspectRatio;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (options.resolution) {
|
|
140
|
+
const resolutionMap: Record<string, string> = {
|
|
141
|
+
'1280x720': '720p',
|
|
142
|
+
'1920x1080': '1080p',
|
|
143
|
+
'3840x2160': '4k',
|
|
144
|
+
};
|
|
145
|
+
parameters.resolution =
|
|
146
|
+
resolutionMap[options.resolution] || options.resolution;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (options.duration) {
|
|
150
|
+
parameters.durationSeconds = options.duration;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (options.seed) {
|
|
154
|
+
parameters.seed = options.seed;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (googleOptions != null) {
|
|
158
|
+
const opts = googleOptions as GoogleVideoModelOptions;
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
opts.personGeneration !== undefined &&
|
|
162
|
+
opts.personGeneration !== null
|
|
163
|
+
) {
|
|
164
|
+
parameters.personGeneration = opts.personGeneration;
|
|
165
|
+
}
|
|
166
|
+
if (opts.negativePrompt !== undefined && opts.negativePrompt !== null) {
|
|
167
|
+
parameters.negativePrompt = opts.negativePrompt;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
171
|
+
if (
|
|
172
|
+
![
|
|
173
|
+
'pollIntervalMs',
|
|
174
|
+
'pollTimeoutMs',
|
|
175
|
+
'personGeneration',
|
|
176
|
+
'negativePrompt',
|
|
177
|
+
'referenceImages',
|
|
178
|
+
].includes(key)
|
|
179
|
+
) {
|
|
180
|
+
parameters[key] = value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { value: operation } = await postJsonToApi({
|
|
186
|
+
url: `${this.config.baseURL}/models/${this.modelId}:predictLongRunning`,
|
|
187
|
+
headers: combineHeaders(
|
|
188
|
+
await resolve(this.config.headers),
|
|
189
|
+
options.headers,
|
|
190
|
+
),
|
|
191
|
+
body: {
|
|
192
|
+
instances,
|
|
193
|
+
parameters,
|
|
194
|
+
},
|
|
195
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
196
|
+
googleOperationSchema,
|
|
197
|
+
),
|
|
198
|
+
failedResponseHandler: googleFailedResponseHandler,
|
|
199
|
+
abortSignal: options.abortSignal,
|
|
200
|
+
fetch: this.config.fetch,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const operationName = operation.name;
|
|
204
|
+
if (!operationName) {
|
|
205
|
+
throw new AISDKError({
|
|
206
|
+
name: 'GOOGLE_VIDEO_GENERATION_ERROR',
|
|
207
|
+
message: 'No operation name returned from API',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const pollIntervalMs = googleOptions?.pollIntervalMs ?? 10000; // 10 seconds (per Google docs)
|
|
212
|
+
const pollTimeoutMs = googleOptions?.pollTimeoutMs ?? 600000; // 10 minutes
|
|
213
|
+
|
|
214
|
+
const startTime = Date.now();
|
|
215
|
+
let finalOperation = operation;
|
|
216
|
+
let responseHeaders: Record<string, string> | undefined;
|
|
217
|
+
|
|
218
|
+
while (!finalOperation.done) {
|
|
219
|
+
if (Date.now() - startTime > pollTimeoutMs) {
|
|
220
|
+
throw new AISDKError({
|
|
221
|
+
name: 'GOOGLE_VIDEO_GENERATION_TIMEOUT',
|
|
222
|
+
message: `Video generation timed out after ${pollTimeoutMs}ms`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await delay(pollIntervalMs);
|
|
227
|
+
|
|
228
|
+
if (options.abortSignal?.aborted) {
|
|
229
|
+
throw new AISDKError({
|
|
230
|
+
name: 'GOOGLE_VIDEO_GENERATION_ABORTED',
|
|
231
|
+
message: 'Video generation request was aborted',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const { value: statusOperation, responseHeaders: pollHeaders } =
|
|
236
|
+
await getFromApi({
|
|
237
|
+
url: `${this.config.baseURL}/${operationName}`,
|
|
238
|
+
headers: combineHeaders(
|
|
239
|
+
await resolve(this.config.headers),
|
|
240
|
+
options.headers,
|
|
241
|
+
),
|
|
242
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
243
|
+
googleOperationSchema,
|
|
244
|
+
),
|
|
245
|
+
failedResponseHandler: googleFailedResponseHandler,
|
|
246
|
+
abortSignal: options.abortSignal,
|
|
247
|
+
fetch: this.config.fetch,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
finalOperation = statusOperation;
|
|
251
|
+
responseHeaders = pollHeaders;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (finalOperation.error) {
|
|
255
|
+
throw new AISDKError({
|
|
256
|
+
name: 'GOOGLE_VIDEO_GENERATION_FAILED',
|
|
257
|
+
message: `Video generation failed: ${finalOperation.error.message}`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const response = finalOperation.response;
|
|
262
|
+
if (
|
|
263
|
+
!response?.generateVideoResponse?.generatedSamples ||
|
|
264
|
+
response.generateVideoResponse.generatedSamples.length === 0
|
|
265
|
+
) {
|
|
266
|
+
throw new AISDKError({
|
|
267
|
+
name: 'GOOGLE_VIDEO_GENERATION_ERROR',
|
|
268
|
+
message: `No videos in response. Response: ${JSON.stringify(finalOperation)}`,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const videos: Array<{ type: 'url'; url: string; mediaType: string }> = [];
|
|
273
|
+
const videoMetadata: Array<{ uri: string }> = [];
|
|
274
|
+
|
|
275
|
+
// Get API key from headers to append to download URLs
|
|
276
|
+
const resolvedHeaders = await resolve(this.config.headers);
|
|
277
|
+
const apiKey = resolvedHeaders?.['x-goog-api-key'];
|
|
278
|
+
|
|
279
|
+
for (const generatedSample of response.generateVideoResponse
|
|
280
|
+
.generatedSamples) {
|
|
281
|
+
if (generatedSample.video?.uri) {
|
|
282
|
+
// Append API key to URL for authentication during download
|
|
283
|
+
const urlWithAuth = apiKey
|
|
284
|
+
? `${generatedSample.video.uri}${generatedSample.video.uri.includes('?') ? '&' : '?'}key=${apiKey}`
|
|
285
|
+
: generatedSample.video.uri;
|
|
286
|
+
|
|
287
|
+
videos.push({
|
|
288
|
+
type: 'url',
|
|
289
|
+
url: urlWithAuth,
|
|
290
|
+
mediaType: 'video/mp4',
|
|
291
|
+
});
|
|
292
|
+
videoMetadata.push({
|
|
293
|
+
uri: generatedSample.video.uri,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (videos.length === 0) {
|
|
299
|
+
throw new AISDKError({
|
|
300
|
+
name: 'GOOGLE_VIDEO_GENERATION_ERROR',
|
|
301
|
+
message: 'No valid videos in response',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
videos,
|
|
307
|
+
warnings,
|
|
308
|
+
response: {
|
|
309
|
+
timestamp: currentDate,
|
|
310
|
+
modelId: this.modelId,
|
|
311
|
+
headers: responseHeaders,
|
|
312
|
+
},
|
|
313
|
+
providerMetadata: {
|
|
314
|
+
google: {
|
|
315
|
+
videos: videoMetadata,
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const googleOperationSchema = z.object({
|
|
323
|
+
name: z.string().nullish(),
|
|
324
|
+
done: z.boolean().nullish(),
|
|
325
|
+
error: z
|
|
326
|
+
.object({
|
|
327
|
+
code: z.number().nullish(),
|
|
328
|
+
message: z.string(),
|
|
329
|
+
status: z.string().nullish(),
|
|
330
|
+
})
|
|
331
|
+
.nullish(),
|
|
332
|
+
response: z
|
|
333
|
+
.object({
|
|
334
|
+
generateVideoResponse: z
|
|
335
|
+
.object({
|
|
336
|
+
generatedSamples: z
|
|
337
|
+
.array(
|
|
338
|
+
z.object({
|
|
339
|
+
video: z
|
|
340
|
+
.object({
|
|
341
|
+
uri: z.string().nullish(),
|
|
342
|
+
})
|
|
343
|
+
.nullish(),
|
|
344
|
+
}),
|
|
345
|
+
)
|
|
346
|
+
.nullish(),
|
|
347
|
+
})
|
|
348
|
+
.nullish(),
|
|
349
|
+
})
|
|
350
|
+
.nullish(),
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const googleVideoModelOptionsSchema = lazySchema(() =>
|
|
354
|
+
zodSchema(
|
|
355
|
+
z
|
|
356
|
+
.object({
|
|
357
|
+
pollIntervalMs: z.number().positive().nullish(),
|
|
358
|
+
pollTimeoutMs: z.number().positive().nullish(),
|
|
359
|
+
personGeneration: z
|
|
360
|
+
.enum(['dont_allow', 'allow_adult', 'allow_all'])
|
|
361
|
+
.nullish(),
|
|
362
|
+
negativePrompt: z.string().nullish(),
|
|
363
|
+
referenceImages: z
|
|
364
|
+
.array(
|
|
365
|
+
z.object({
|
|
366
|
+
bytesBase64Encoded: z.string().nullish(),
|
|
367
|
+
gcsUri: z.string().nullish(),
|
|
368
|
+
}),
|
|
369
|
+
)
|
|
370
|
+
.nullish(),
|
|
371
|
+
})
|
|
372
|
+
.passthrough(),
|
|
373
|
+
),
|
|
374
|
+
);
|