@anvia/gemini 0.4.1 → 1.0.0-rc.10
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/README.md +34 -28
- package/dist/index.d.ts +78 -102
- package/dist/index.js +880 -336
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -10,15 +10,13 @@ __export(gemini_exports, {
|
|
|
10
10
|
GEMINI_2_5_FLASH_IMAGE: () => GEMINI_2_5_FLASH_IMAGE,
|
|
11
11
|
GEMINI_3_PRO_IMAGE_PREVIEW: () => GEMINI_3_PRO_IMAGE_PREVIEW,
|
|
12
12
|
GeminiClient: () => GeminiClient,
|
|
13
|
-
GeminiCompletionModel: () => GeminiCompletionModel,
|
|
14
|
-
GeminiEmbeddingModel: () => GeminiEmbeddingModel,
|
|
15
|
-
GeminiImageGenerationModel: () => GeminiImageGenerationModel,
|
|
16
|
-
GeminiImagenGenerationModel: () => GeminiImagenGenerationModel,
|
|
17
|
-
GeminiTranscriptionModel: () => GeminiTranscriptionModel,
|
|
18
13
|
IMAGEN_4_GENERATE: () => IMAGEN_4_GENERATE
|
|
19
14
|
});
|
|
20
15
|
|
|
21
16
|
// src/gemini/client.ts
|
|
17
|
+
import {
|
|
18
|
+
resolveModelContextLimits
|
|
19
|
+
} from "@anvia/core/completion";
|
|
22
20
|
import {
|
|
23
21
|
ModelListingError
|
|
24
22
|
} from "@anvia/core/model-listing";
|
|
@@ -26,9 +24,9 @@ import { GoogleGenAI } from "@google/genai";
|
|
|
26
24
|
|
|
27
25
|
// src/gemini/completion.ts
|
|
28
26
|
import {
|
|
29
|
-
AssistantContent,
|
|
30
27
|
assertCompletionRequestSupported,
|
|
31
|
-
|
|
28
|
+
CompletionProviderOutputError,
|
|
29
|
+
isJsonValue,
|
|
32
30
|
Usage,
|
|
33
31
|
withContextUsage
|
|
34
32
|
} from "@anvia/core/completion";
|
|
@@ -48,82 +46,35 @@ function orderedRequestMessages(request) {
|
|
|
48
46
|
return messages;
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
// src/gemini/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"gemini-2.5-flash-image": {
|
|
70
|
-
contextWindow: 32768,
|
|
71
|
-
maxInputTokens: 32768,
|
|
72
|
-
maxOutputTokens: 32768
|
|
73
|
-
},
|
|
74
|
-
"gemini-2.5-flash-lite": CONTEXT_1M_64K,
|
|
75
|
-
"gemini-2.5-flash-preview-tts": {
|
|
76
|
-
contextWindow: 8192,
|
|
77
|
-
maxInputTokens: 8192,
|
|
78
|
-
maxOutputTokens: 16384
|
|
79
|
-
},
|
|
80
|
-
"gemini-2.5-pro": CONTEXT_1M_64K,
|
|
81
|
-
"gemini-2.5-pro-preview-tts": {
|
|
82
|
-
contextWindow: 8192,
|
|
83
|
-
maxInputTokens: 8192,
|
|
84
|
-
maxOutputTokens: 16384
|
|
85
|
-
},
|
|
86
|
-
"gemini-3-flash-preview": CONTEXT_1M_64K,
|
|
87
|
-
"gemini-3-pro-image-preview": {
|
|
88
|
-
contextWindow: 131072,
|
|
89
|
-
maxInputTokens: 131072,
|
|
90
|
-
maxOutputTokens: 32768
|
|
91
|
-
},
|
|
92
|
-
"gemini-3-pro-preview": CONTEXT_1M_64K,
|
|
93
|
-
"gemini-3.1-flash-image-preview": {
|
|
94
|
-
contextWindow: 65536,
|
|
95
|
-
maxInputTokens: 65536,
|
|
96
|
-
maxOutputTokens: 65536
|
|
97
|
-
},
|
|
98
|
-
"gemini-3.1-flash-lite": CONTEXT_1M_64K,
|
|
99
|
-
"gemini-3.1-flash-lite-preview": CONTEXT_1M_64K,
|
|
100
|
-
"gemini-3.1-pro-preview": CONTEXT_1M_64K,
|
|
101
|
-
"gemini-3.1-pro-preview-customtools": CONTEXT_1M_64K,
|
|
102
|
-
"gemini-3.5-flash": CONTEXT_1M_64K,
|
|
103
|
-
"gemini-flash-latest": CONTEXT_1M_64K,
|
|
104
|
-
"gemini-flash-lite-latest": CONTEXT_1M_64K,
|
|
105
|
-
"gemma-4-26b-a4b-it": {
|
|
106
|
-
contextWindow: 262144,
|
|
107
|
-
maxInputTokens: 262144,
|
|
108
|
-
maxOutputTokens: 32768
|
|
109
|
-
},
|
|
110
|
-
"gemma-4-31b-it": {
|
|
111
|
-
contextWindow: 262144,
|
|
112
|
-
maxInputTokens: 262144,
|
|
113
|
-
maxOutputTokens: 32768
|
|
114
|
-
}
|
|
115
|
-
};
|
|
49
|
+
// src/gemini/retry.ts
|
|
50
|
+
function disableGeminiNativeRetries(config) {
|
|
51
|
+
const httpOptions = isPlainObject(config.httpOptions) ? config.httpOptions : {};
|
|
52
|
+
const retryOptions = isPlainObject(httpOptions.retryOptions) ? httpOptions.retryOptions : {};
|
|
53
|
+
return {
|
|
54
|
+
...config,
|
|
55
|
+
httpOptions: {
|
|
56
|
+
...httpOptions,
|
|
57
|
+
retryOptions: {
|
|
58
|
+
...retryOptions,
|
|
59
|
+
attempts: 1
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function isPlainObject(value) {
|
|
65
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
116
67
|
|
|
117
68
|
// src/gemini/completion.ts
|
|
118
69
|
var GeminiCompletionModel = class {
|
|
119
|
-
constructor(client,
|
|
70
|
+
constructor(client, modelId, contextLimits) {
|
|
120
71
|
this.client = client;
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
72
|
+
this.modelId = modelId;
|
|
73
|
+
this.contextLimits = contextLimits;
|
|
123
74
|
}
|
|
124
75
|
client;
|
|
125
|
-
|
|
126
|
-
|
|
76
|
+
modelId;
|
|
77
|
+
contextLimits;
|
|
127
78
|
provider = "gemini";
|
|
128
79
|
capabilities = {
|
|
129
80
|
streaming: true,
|
|
@@ -134,62 +85,76 @@ var GeminiCompletionModel = class {
|
|
|
134
85
|
outputSchema: true,
|
|
135
86
|
reasoning: true
|
|
136
87
|
};
|
|
137
|
-
|
|
138
|
-
return
|
|
139
|
-
model,
|
|
140
|
-
GEMINI_COMPLETION_MODEL_CONTEXT_LIMITS,
|
|
141
|
-
this.metadataOptions.modelOverrides
|
|
142
|
-
);
|
|
88
|
+
modelInfo() {
|
|
89
|
+
return this.contextLimits === void 0 ? void 0 : { modelId: this.modelId, context: this.contextLimits };
|
|
143
90
|
}
|
|
144
91
|
traceRequest(request, options = {}) {
|
|
145
|
-
const params = toGeminiGenerateContentParams(this.
|
|
92
|
+
const params = toGeminiGenerateContentParams(this.modelId, request);
|
|
146
93
|
return providerRequestSummary(params, request, options);
|
|
147
94
|
}
|
|
148
|
-
async completion(request) {
|
|
95
|
+
async completion(request, options) {
|
|
149
96
|
assertCompletionRequestSupported(this, request);
|
|
150
|
-
const params = toGeminiGenerateContentParams(this.
|
|
97
|
+
const params = toGeminiGenerateContentParams(this.modelId, request);
|
|
98
|
+
applyAbortSignal(params, options);
|
|
151
99
|
const response = await this.client.models.generateContent(params);
|
|
152
|
-
return withContextUsage(
|
|
153
|
-
fromGeminiGenerateContentResponse(response),
|
|
154
|
-
this.getModelInfo(request.model ?? this.defaultModel)
|
|
155
|
-
);
|
|
100
|
+
return withContextUsage(fromGeminiGenerateContentResponse(response), this.modelInfo());
|
|
156
101
|
}
|
|
157
|
-
async *streamCompletion(request) {
|
|
102
|
+
async *streamCompletion(request, options) {
|
|
158
103
|
assertCompletionRequestSupported(this, request, { streaming: true });
|
|
159
|
-
const params = toGeminiGenerateContentParams(this.
|
|
104
|
+
const params = toGeminiGenerateContentParams(this.modelId, request);
|
|
105
|
+
applyAbortSignal(params, options);
|
|
160
106
|
const stream = await this.client.models.generateContentStream(params);
|
|
107
|
+
const streamState = new GeminiCompletionStreamState();
|
|
161
108
|
for await (const chunk of stream) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
-
} : event;
|
|
109
|
+
const mapping = mapGeminiGenerateContentStreamChunk(chunk);
|
|
110
|
+
streamState.accept(chunk, mapping);
|
|
111
|
+
for (const event of mapping.events) {
|
|
112
|
+
if (event.type !== "final") {
|
|
113
|
+
yield event;
|
|
114
|
+
}
|
|
170
115
|
}
|
|
171
116
|
}
|
|
117
|
+
streamState.assertComplete();
|
|
118
|
+
const finalEvent = streamState.finalEvent();
|
|
119
|
+
if (finalEvent !== void 0) {
|
|
120
|
+
yield {
|
|
121
|
+
...finalEvent,
|
|
122
|
+
response: withContextUsage(finalEvent.response, this.modelInfo())
|
|
123
|
+
};
|
|
124
|
+
}
|
|
172
125
|
}
|
|
173
126
|
};
|
|
174
|
-
function toGeminiGenerateContentParams(
|
|
127
|
+
function toGeminiGenerateContentParams(modelId, request) {
|
|
175
128
|
const messages = requestMessages(request);
|
|
176
|
-
|
|
129
|
+
if (request.providerOptions !== void 0 && (!isPlainObject2(request.providerOptions) || !isJsonValue(request.providerOptions))) {
|
|
130
|
+
throw new TypeError("Gemini providerOptions must be a JSON object.");
|
|
131
|
+
}
|
|
132
|
+
const providerOptions = request.providerOptions ?? {};
|
|
133
|
+
const { config: providerConfigValue, ...providerTopLevel } = providerOptions;
|
|
134
|
+
if (providerConfigValue !== void 0 && !isPlainObject2(providerConfigValue)) {
|
|
135
|
+
throw new TypeError("Gemini providerOptions.config must be a JSON object.");
|
|
136
|
+
}
|
|
137
|
+
const providerConfig = providerConfigValue === void 0 ? {} : { ...providerConfigValue };
|
|
138
|
+
delete providerConfig.tools;
|
|
139
|
+
const config = disableGeminiNativeRetries({
|
|
140
|
+
...providerConfig,
|
|
141
|
+
...geminiConfig(request, messages)
|
|
142
|
+
});
|
|
177
143
|
const params = {
|
|
178
|
-
|
|
144
|
+
...providerTopLevel,
|
|
145
|
+
model: modelId,
|
|
179
146
|
contents: messagesToGeminiContents(messages),
|
|
180
147
|
config
|
|
181
148
|
};
|
|
182
|
-
if (request.additionalParams !== void 0 && isPlainObject(request.additionalParams)) {
|
|
183
|
-
const { config: additionalConfig, ...additionalTopLevel } = request.additionalParams;
|
|
184
|
-
Object.assign(params, additionalTopLevel);
|
|
185
|
-
if (isPlainObject(additionalConfig)) {
|
|
186
|
-
params.config = { ...config, ...additionalConfig };
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
149
|
return params;
|
|
190
150
|
}
|
|
151
|
+
function applyAbortSignal(params, options) {
|
|
152
|
+
if (options?.abortSignal === void 0) return;
|
|
153
|
+
const config = isPlainObject2(params.config) ? params.config : {};
|
|
154
|
+
params.config = { ...config, abortSignal: options.abortSignal };
|
|
155
|
+
}
|
|
191
156
|
function providerRequestSummary(params, request, options) {
|
|
192
|
-
const config =
|
|
157
|
+
const config = isPlainObject2(params.config) ? params.config : {};
|
|
193
158
|
return compactJsonObject({
|
|
194
159
|
provider: "gemini",
|
|
195
160
|
api: options.stream === true ? "models.generateContentStream" : "models.generateContent",
|
|
@@ -205,7 +170,7 @@ function providerRequestSummary(params, request, options) {
|
|
|
205
170
|
temperature: request.temperature,
|
|
206
171
|
maxTokens: request.maxTokens,
|
|
207
172
|
toolChoice: toolChoiceSummary(request.toolChoice),
|
|
208
|
-
|
|
173
|
+
providerOptionKeys: isPlainObject2(request.providerOptions) ? Object.keys(request.providerOptions).sort() : void 0
|
|
209
174
|
});
|
|
210
175
|
}
|
|
211
176
|
function toolChoiceSummary(toolChoice) {
|
|
@@ -217,10 +182,10 @@ function toolChoiceSummary(toolChoice) {
|
|
|
217
182
|
function compactJsonObject(values) {
|
|
218
183
|
return Object.fromEntries(
|
|
219
184
|
Object.entries(values).flatMap(([key, value]) => {
|
|
220
|
-
if (value
|
|
185
|
+
if (!isJsonValue(value)) {
|
|
221
186
|
return [];
|
|
222
187
|
}
|
|
223
|
-
return [[key,
|
|
188
|
+
return [[key, value]];
|
|
224
189
|
})
|
|
225
190
|
);
|
|
226
191
|
}
|
|
@@ -269,11 +234,11 @@ function messagesToGeminiContents(messages) {
|
|
|
269
234
|
}
|
|
270
235
|
if (message.role === "assistant") {
|
|
271
236
|
const content2 = assistantMessageToGeminiContent(message);
|
|
272
|
-
for (const item of message.content) {
|
|
273
|
-
if (item.type === "
|
|
274
|
-
toolNamesById.set(item.
|
|
237
|
+
for (const item of typeof message.content === "string" ? [] : message.content) {
|
|
238
|
+
if (item.type === "tool-call") {
|
|
239
|
+
toolNamesById.set(item.toolCallId, item.toolName);
|
|
275
240
|
if (item.callId !== void 0) {
|
|
276
|
-
toolNamesById.set(item.callId, item.
|
|
241
|
+
toolNamesById.set(item.callId, item.toolName);
|
|
277
242
|
}
|
|
278
243
|
}
|
|
279
244
|
}
|
|
@@ -292,19 +257,26 @@ function messagesToGeminiContents(messages) {
|
|
|
292
257
|
function userMessageToGeminiContent(message) {
|
|
293
258
|
return {
|
|
294
259
|
role: "user",
|
|
295
|
-
parts: message.content.map(userContentToGeminiPart)
|
|
260
|
+
parts: typeof message.content === "string" ? [{ text: message.content }] : message.content.map(userContentToGeminiPart)
|
|
296
261
|
};
|
|
297
262
|
}
|
|
298
263
|
function toolMessageToGeminiContent(message, toolNamesById) {
|
|
299
264
|
return {
|
|
300
265
|
role: "user",
|
|
301
|
-
parts: message.content.map((content) =>
|
|
266
|
+
parts: message.content.map((content) => {
|
|
267
|
+
if (content.type !== "tool-result") {
|
|
268
|
+
throw new TypeError(
|
|
269
|
+
"Anvia interaction responses must be resolved by Agent before provider calls."
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return toolContentToGeminiPart(content, toolNamesById);
|
|
273
|
+
})
|
|
302
274
|
};
|
|
303
275
|
}
|
|
304
276
|
function assistantMessageToGeminiContent(message) {
|
|
305
277
|
return {
|
|
306
278
|
role: "model",
|
|
307
|
-
parts: message.content.flatMap((content) => {
|
|
279
|
+
parts: typeof message.content === "string" ? [{ text: message.content }] : message.content.flatMap((content) => {
|
|
308
280
|
if (content.type === "text") {
|
|
309
281
|
const part = { text: content.text };
|
|
310
282
|
if (content.signature !== void 0) {
|
|
@@ -312,10 +284,10 @@ function assistantMessageToGeminiContent(message) {
|
|
|
312
284
|
}
|
|
313
285
|
return [part];
|
|
314
286
|
}
|
|
315
|
-
if (content.type === "
|
|
287
|
+
if (content.type === "tool-call") {
|
|
316
288
|
const functionCall = {
|
|
317
|
-
name: content.
|
|
318
|
-
args: content.
|
|
289
|
+
name: content.toolName,
|
|
290
|
+
args: content.input
|
|
319
291
|
};
|
|
320
292
|
if (content.callId !== void 0) {
|
|
321
293
|
functionCall.id = content.callId;
|
|
@@ -326,8 +298,8 @@ function assistantMessageToGeminiContent(message) {
|
|
|
326
298
|
}
|
|
327
299
|
return [part];
|
|
328
300
|
}
|
|
329
|
-
if (content.type === "reasoning" && content.
|
|
330
|
-
return content.
|
|
301
|
+
if (content.type === "reasoning" && content.details !== void 0) {
|
|
302
|
+
return content.details.flatMap((reasoning) => {
|
|
331
303
|
if (reasoning.type !== "text" && reasoning.type !== "summary") {
|
|
332
304
|
return [];
|
|
333
305
|
}
|
|
@@ -338,8 +310,10 @@ function assistantMessageToGeminiContent(message) {
|
|
|
338
310
|
return [part];
|
|
339
311
|
});
|
|
340
312
|
}
|
|
341
|
-
if (content.type === "image") {
|
|
342
|
-
throw new Error(
|
|
313
|
+
if (content.type === "image" || content.type === "file") {
|
|
314
|
+
throw new Error(
|
|
315
|
+
"Gemini does not support image or file content in assistant history yet"
|
|
316
|
+
);
|
|
343
317
|
}
|
|
344
318
|
return [];
|
|
345
319
|
})
|
|
@@ -355,18 +329,18 @@ function userContentToGeminiPart(content) {
|
|
|
355
329
|
return documentContentToGeminiPart(content);
|
|
356
330
|
}
|
|
357
331
|
function imageContentToGeminiPart(content) {
|
|
358
|
-
if (content.
|
|
332
|
+
if (content.image.type === "data") {
|
|
359
333
|
return {
|
|
360
334
|
inlineData: {
|
|
361
|
-
mimeType: content.
|
|
362
|
-
data: content.
|
|
335
|
+
mimeType: content.mediaType ?? "image/png",
|
|
336
|
+
data: content.image.data
|
|
363
337
|
}
|
|
364
338
|
};
|
|
365
339
|
}
|
|
366
340
|
return {
|
|
367
341
|
fileData: {
|
|
368
|
-
fileUri: content.
|
|
369
|
-
mimeType: mimeTypeFromImageUrl(content.
|
|
342
|
+
fileUri: content.image.url,
|
|
343
|
+
mimeType: content.mediaType ?? mimeTypeFromImageUrl(content.image.url)
|
|
370
344
|
}
|
|
371
345
|
};
|
|
372
346
|
}
|
|
@@ -388,29 +362,29 @@ function safeUrlPathname(url) {
|
|
|
388
362
|
}
|
|
389
363
|
}
|
|
390
364
|
function documentContentToGeminiPart(content) {
|
|
391
|
-
if (content.
|
|
392
|
-
return { text: content.
|
|
365
|
+
if (content.data.type === "text") {
|
|
366
|
+
return { text: content.data.text };
|
|
393
367
|
}
|
|
394
|
-
if (content.
|
|
368
|
+
if (content.data.type === "data") {
|
|
395
369
|
return {
|
|
396
370
|
inlineData: {
|
|
397
|
-
mimeType: content.
|
|
398
|
-
data: content.
|
|
371
|
+
mimeType: content.mediaType,
|
|
372
|
+
data: content.data.data
|
|
399
373
|
}
|
|
400
374
|
};
|
|
401
375
|
}
|
|
402
376
|
return {
|
|
403
377
|
fileData: {
|
|
404
|
-
fileUri: content.
|
|
405
|
-
mimeType: content.
|
|
378
|
+
fileUri: content.data.url,
|
|
379
|
+
mimeType: content.mediaType
|
|
406
380
|
}
|
|
407
381
|
};
|
|
408
382
|
}
|
|
409
383
|
function toolContentToGeminiPart(content, toolNamesById) {
|
|
410
|
-
const id = content.callId ?? content.
|
|
384
|
+
const id = content.callId ?? content.toolCallId;
|
|
411
385
|
const functionResponse = {
|
|
412
|
-
name: content.toolName
|
|
413
|
-
response: toolResultResponse(content
|
|
386
|
+
name: content.toolName || toolNamesById.get(id) || content.toolCallId,
|
|
387
|
+
response: toolResultResponse(content)
|
|
414
388
|
};
|
|
415
389
|
if (content.callId !== void 0) {
|
|
416
390
|
functionResponse.id = content.callId;
|
|
@@ -418,10 +392,21 @@ function toolContentToGeminiPart(content, toolNamesById) {
|
|
|
418
392
|
return { functionResponse };
|
|
419
393
|
}
|
|
420
394
|
function toolResultResponse(content) {
|
|
395
|
+
const output = content.output;
|
|
396
|
+
if (output.type === "json") {
|
|
397
|
+
return { result: output.value };
|
|
398
|
+
}
|
|
399
|
+
if (output.type === "text") {
|
|
400
|
+
return { content: output.value };
|
|
401
|
+
}
|
|
402
|
+
if (output.type === "error-json" || output.type === "error-text") {
|
|
403
|
+
return { error: output.value };
|
|
404
|
+
}
|
|
405
|
+
if (output.type === "execution-denied") {
|
|
406
|
+
return { error: output.reason ?? "Tool execution was denied." };
|
|
407
|
+
}
|
|
421
408
|
return {
|
|
422
|
-
content:
|
|
423
|
-
(item) => item.type === "text" ? item.text : `[image:${item.mediaType ?? "image/png"}]`
|
|
424
|
-
).join("\n")
|
|
409
|
+
content: output.value.map((item) => item.type === "text" ? item.text : `[file:${item.mediaType}]`).join("\n")
|
|
425
410
|
};
|
|
426
411
|
}
|
|
427
412
|
function toolDefinitionToGemini(tool) {
|
|
@@ -449,29 +434,73 @@ function toolChoiceToGemini(toolChoice) {
|
|
|
449
434
|
};
|
|
450
435
|
}
|
|
451
436
|
function fromGeminiGenerateContentResponse(response) {
|
|
452
|
-
const raw = response;
|
|
453
|
-
const
|
|
437
|
+
const raw = isPlainObject2(response) ? response : {};
|
|
438
|
+
const usage = usageFromGemini(raw.usageMetadata);
|
|
439
|
+
const parts = candidateParts(raw, usage);
|
|
440
|
+
const providerFinishReason = providerFinishReasonFromGeminiResponse(raw);
|
|
441
|
+
const finishError = geminiToolFinishError(
|
|
442
|
+
providerFinishReason,
|
|
443
|
+
hasGeminiFunctionCallMarker(raw, parts),
|
|
444
|
+
usage
|
|
445
|
+
);
|
|
446
|
+
if (finishError !== void 0) throw finishError;
|
|
447
|
+
const choice = assistantContentFromGeminiResponse(raw, usage);
|
|
454
448
|
const result = {
|
|
455
449
|
choice,
|
|
456
|
-
usage
|
|
450
|
+
usage,
|
|
457
451
|
rawResponse: response
|
|
458
452
|
};
|
|
453
|
+
applyGeminiFinishReason(result, raw);
|
|
459
454
|
const id = stringFrom(raw.responseId) ?? stringFrom(raw.id);
|
|
460
455
|
if (id !== void 0) {
|
|
461
456
|
result.messageId = id;
|
|
462
457
|
}
|
|
458
|
+
assertSafeGeminiCompletionResponse(result);
|
|
463
459
|
return result;
|
|
464
460
|
}
|
|
465
|
-
function
|
|
466
|
-
|
|
467
|
-
|
|
461
|
+
function applyGeminiFinishReason(response, raw, hasStreamedToolCalls = false) {
|
|
462
|
+
const value = providerFinishReasonFromGeminiResponse(raw);
|
|
463
|
+
if (value === void 0) return;
|
|
464
|
+
applyGeminiFinishReasonValue(response, value, hasStreamedToolCalls);
|
|
465
|
+
}
|
|
466
|
+
function applyGeminiFinishReasonValue(response, value, hasStreamedToolCalls) {
|
|
467
|
+
response.finishReason = geminiFinishReason(
|
|
468
|
+
value,
|
|
469
|
+
hasStreamedToolCalls || response.choice.some((part) => part.type === "tool-call")
|
|
470
|
+
);
|
|
471
|
+
response.providerFinishReason = value;
|
|
472
|
+
}
|
|
473
|
+
function geminiFinishReason(value, hasToolCalls) {
|
|
474
|
+
if (value === "MAX_TOKENS") return "length";
|
|
475
|
+
if (value === "SAFETY" || value === "RECITATION" || value === "BLOCKLIST" || value === "PROHIBITED_CONTENT" || value === "SPII" || value === "IMAGE_SAFETY" || value === "IMAGE_PROHIBITED_CONTENT") {
|
|
476
|
+
return "content-filter";
|
|
477
|
+
}
|
|
478
|
+
if (value === "STOP") {
|
|
479
|
+
return hasToolCalls ? "tool-calls" : "stop";
|
|
480
|
+
}
|
|
481
|
+
return "other";
|
|
482
|
+
}
|
|
483
|
+
function mapGeminiGenerateContentStreamChunk(chunk) {
|
|
484
|
+
if (!isPlainObject2(chunk)) {
|
|
485
|
+
return {
|
|
486
|
+
events: [],
|
|
487
|
+
toolCalls: [],
|
|
488
|
+
hasToolCallMarker: false,
|
|
489
|
+
hasSyntheticToolCalls: false
|
|
490
|
+
};
|
|
468
491
|
}
|
|
469
492
|
const events = [];
|
|
470
|
-
const
|
|
471
|
-
|
|
493
|
+
const usage = usageFromGemini(chunk.usageMetadata);
|
|
494
|
+
const parts = candidateParts(chunk, usage);
|
|
495
|
+
const providerFinishReason = providerFinishReasonFromGeminiResponse(chunk);
|
|
496
|
+
const hasToolCallMarker = hasGeminiFunctionCallMarker(chunk, parts);
|
|
497
|
+
const terminalError = providerFinishReason === void 0 ? void 0 : geminiToolFinishError(providerFinishReason, hasToolCallMarker, usage);
|
|
498
|
+
const calls = terminalError === void 0 ? functionCallsFromGeminiResponse(chunk, parts, usage) : [];
|
|
499
|
+
const directText = textFromGeminiResponse(chunk, parts);
|
|
500
|
+
if (terminalError === void 0 && directText.length > 0 && parts.length === 0) {
|
|
472
501
|
events.push({ type: "text_delta", delta: directText });
|
|
473
502
|
}
|
|
474
|
-
for (const part of
|
|
503
|
+
for (const { part } of terminalError === void 0 ? parts : []) {
|
|
475
504
|
if (typeof part.text === "string" && part.text.length > 0) {
|
|
476
505
|
if (part.thought === true) {
|
|
477
506
|
events.push({ type: "reasoning_delta", delta: part.text, contentType: "summary" });
|
|
@@ -480,18 +509,22 @@ function fromGeminiGenerateContentStreamChunk(chunk) {
|
|
|
480
509
|
}
|
|
481
510
|
}
|
|
482
511
|
}
|
|
483
|
-
for (const call of
|
|
512
|
+
for (const call of calls) {
|
|
484
513
|
events.push(
|
|
485
|
-
toolCallDelta(call.
|
|
486
|
-
callId: call.
|
|
514
|
+
toolCallDelta(call.toolCallId, {
|
|
515
|
+
callId: call.callId,
|
|
487
516
|
name: call.name,
|
|
488
517
|
signature: call.signature
|
|
489
518
|
})
|
|
490
519
|
);
|
|
520
|
+
const argumentsDelta = JSON.stringify(call.args);
|
|
521
|
+
if (argumentsDelta === void 0) {
|
|
522
|
+
throw invalidGeminiToolCallArguments(call.toolCallId, usage);
|
|
523
|
+
}
|
|
491
524
|
events.push(
|
|
492
|
-
toolCallDelta(call.
|
|
493
|
-
callId: call.
|
|
494
|
-
argumentsDelta
|
|
525
|
+
toolCallDelta(call.toolCallId, {
|
|
526
|
+
callId: call.callId,
|
|
527
|
+
argumentsDelta,
|
|
495
528
|
argumentsMode: "replace"
|
|
496
529
|
})
|
|
497
530
|
);
|
|
@@ -500,113 +533,522 @@ function fromGeminiGenerateContentStreamChunk(chunk) {
|
|
|
500
533
|
if (id !== void 0) {
|
|
501
534
|
events.push({ type: "message_id", id });
|
|
502
535
|
}
|
|
503
|
-
if (
|
|
504
|
-
|
|
536
|
+
if (isPlainObject2(chunk.usageMetadata)) {
|
|
537
|
+
if (terminalError === void 0) {
|
|
538
|
+
events.push({ type: "final", response: fromGeminiGenerateContentResponse(chunk) });
|
|
539
|
+
} else {
|
|
540
|
+
const response = { choice: [], usage, rawResponse: chunk };
|
|
541
|
+
if (providerFinishReason !== void 0) {
|
|
542
|
+
applyGeminiFinishReasonValue(response, providerFinishReason, true);
|
|
543
|
+
}
|
|
544
|
+
events.push({ type: "final", response });
|
|
545
|
+
}
|
|
505
546
|
}
|
|
506
|
-
return
|
|
547
|
+
return providerFinishReason === void 0 ? {
|
|
548
|
+
events,
|
|
549
|
+
toolCalls: calls.map(toolCallFromGeminiFunctionCall),
|
|
550
|
+
hasToolCallMarker,
|
|
551
|
+
hasSyntheticToolCalls: calls.some((call) => call.callId === void 0),
|
|
552
|
+
terminalError
|
|
553
|
+
} : {
|
|
554
|
+
events,
|
|
555
|
+
toolCalls: calls.map(toolCallFromGeminiFunctionCall),
|
|
556
|
+
hasToolCallMarker,
|
|
557
|
+
hasSyntheticToolCalls: calls.some((call) => call.callId === void 0),
|
|
558
|
+
providerFinishReason,
|
|
559
|
+
terminalError
|
|
560
|
+
};
|
|
507
561
|
}
|
|
508
|
-
|
|
509
|
-
|
|
562
|
+
var GeminiCompletionStreamState = class {
|
|
563
|
+
toolCalls = /* @__PURE__ */ new Map();
|
|
564
|
+
providerFinishReason;
|
|
565
|
+
terminalChunk;
|
|
566
|
+
finalResponse;
|
|
567
|
+
sawToolCallMarker = false;
|
|
568
|
+
sawSyntheticToolCallChunk = false;
|
|
569
|
+
terminalError;
|
|
570
|
+
accept(chunk, mapping) {
|
|
571
|
+
if (mapping.hasSyntheticToolCalls && this.sawSyntheticToolCallChunk) {
|
|
572
|
+
const toolCallId = mapping.toolCalls.find(
|
|
573
|
+
(toolCall) => toolCall.callId === void 0
|
|
574
|
+
)?.toolCallId;
|
|
575
|
+
throw toolCallId === void 0 ? new CompletionProviderOutputError({
|
|
576
|
+
kind: "invalid-tool-call",
|
|
577
|
+
usage: this.currentUsage()
|
|
578
|
+
}) : invalidGeminiToolCall(toolCallId, this.currentUsage());
|
|
579
|
+
}
|
|
580
|
+
if (this.providerFinishReason !== void 0 && (mapping.hasToolCallMarker || hasGeminiSemanticProgress(mapping.events))) {
|
|
581
|
+
throw new CompletionProviderOutputError({
|
|
582
|
+
kind: "invalid-tool-call",
|
|
583
|
+
finishReason: "other",
|
|
584
|
+
usage: this.currentUsage()
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
for (const toolCall of mapping.toolCalls) {
|
|
588
|
+
const existing = this.toolCalls.get(toolCall.toolCallId);
|
|
589
|
+
if (existing !== void 0) {
|
|
590
|
+
if (existing.callId === void 0 || toolCall.callId === void 0 || !sameGeminiToolCall(existing, toolCall)) {
|
|
591
|
+
throw invalidGeminiToolCall(toolCall.toolCallId, this.currentUsage());
|
|
592
|
+
}
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
this.toolCalls.set(toolCall.toolCallId, toolCall);
|
|
596
|
+
}
|
|
597
|
+
this.sawToolCallMarker ||= mapping.hasToolCallMarker;
|
|
598
|
+
this.sawSyntheticToolCallChunk ||= mapping.hasSyntheticToolCalls;
|
|
599
|
+
for (const event of mapping.events) {
|
|
600
|
+
if (event.type === "final") {
|
|
601
|
+
this.finalResponse = event.response;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this.terminalError ??= mapping.terminalError;
|
|
605
|
+
if (mapping.providerFinishReason !== void 0) {
|
|
606
|
+
if (this.providerFinishReason !== void 0 && this.providerFinishReason !== mapping.providerFinishReason) {
|
|
607
|
+
throw new CompletionProviderOutputError({
|
|
608
|
+
kind: "invalid-tool-call",
|
|
609
|
+
finishReason: "other",
|
|
610
|
+
usage: this.currentUsage()
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
this.providerFinishReason = mapping.providerFinishReason;
|
|
614
|
+
this.terminalChunk = chunk;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
assertComplete() {
|
|
618
|
+
if (this.terminalError !== void 0) {
|
|
619
|
+
throw geminiProviderOutputErrorWithUsage(this.terminalError, this.currentUsage());
|
|
620
|
+
}
|
|
621
|
+
if (!this.sawToolCallMarker && this.toolCalls.size === 0) return;
|
|
622
|
+
if (this.providerFinishReason !== void 0) {
|
|
623
|
+
assertSafeGeminiToolFinishReason(this.providerFinishReason, this.currentUsage());
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const toolCallId = this.toolCalls.keys().next().value;
|
|
627
|
+
throw new CompletionProviderOutputError(
|
|
628
|
+
toolCallId === void 0 ? { kind: "incomplete-tool-call", usage: this.currentUsage() } : { kind: "incomplete-tool-call", toolCallId, usage: this.currentUsage() }
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
finalEvent() {
|
|
632
|
+
let response = this.finalResponse;
|
|
633
|
+
if (response === void 0) {
|
|
634
|
+
if (this.terminalChunk === void 0) {
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
const raw = isPlainObject2(this.terminalChunk) ? this.terminalChunk : {};
|
|
638
|
+
response = {
|
|
639
|
+
choice: [],
|
|
640
|
+
usage: usageFromGemini(raw.usageMetadata),
|
|
641
|
+
rawResponse: this.terminalChunk
|
|
642
|
+
};
|
|
643
|
+
const messageId = stringFrom(raw.responseId) ?? stringFrom(raw.id);
|
|
644
|
+
if (messageId !== void 0) response.messageId = messageId;
|
|
645
|
+
}
|
|
646
|
+
response = mergeGeminiStreamToolCalls(response, [...this.toolCalls.values()]);
|
|
647
|
+
if (this.providerFinishReason !== void 0) {
|
|
648
|
+
applyGeminiFinishReasonValue(response, this.providerFinishReason, this.toolCalls.size > 0);
|
|
649
|
+
}
|
|
650
|
+
assertSafeGeminiCompletionResponse(response);
|
|
651
|
+
return { type: "final", response };
|
|
652
|
+
}
|
|
653
|
+
currentUsage() {
|
|
654
|
+
if (this.finalResponse !== void 0) {
|
|
655
|
+
return this.finalResponse.usage;
|
|
656
|
+
}
|
|
657
|
+
const raw = isPlainObject2(this.terminalChunk) ? this.terminalChunk : {};
|
|
658
|
+
return usageFromGemini(raw.usageMetadata);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
function hasGeminiSemanticProgress(events) {
|
|
662
|
+
return events.some(
|
|
663
|
+
(event) => event.type === "text_delta" || event.type === "reasoning_delta" || event.type === "tool_call_delta" || event.type === "tool_call" || event.type === "provider_tool_call"
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
function sameGeminiToolCall(left, right) {
|
|
667
|
+
return left.toolName === right.toolName && left.callId === right.callId && left.signature === right.signature && sameJsonValue(left.input, right.input);
|
|
668
|
+
}
|
|
669
|
+
function sameJsonValue(left, right) {
|
|
670
|
+
if (Object.is(left, right)) return true;
|
|
671
|
+
if (isJsonArray(left) || isJsonArray(right)) {
|
|
672
|
+
return isJsonArray(left) && isJsonArray(right) && left.length === right.length && left.every((value, index) => {
|
|
673
|
+
const rightValue = right[index];
|
|
674
|
+
return rightValue !== void 0 && sameJsonValue(value, rightValue);
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const leftKeys = Object.keys(left);
|
|
681
|
+
const rightKeys = Object.keys(right);
|
|
682
|
+
return leftKeys.length === rightKeys.length && leftKeys.every(
|
|
683
|
+
(key) => Object.hasOwn(right, key) && sameJsonValue(left[key], right[key])
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
function isJsonArray(value) {
|
|
687
|
+
return Array.isArray(value);
|
|
688
|
+
}
|
|
689
|
+
function assistantContentFromGeminiResponse(response, usage) {
|
|
690
|
+
const parts = candidateParts(response, usage);
|
|
691
|
+
const calls = functionCallsFromGeminiResponse(response, parts, usage);
|
|
510
692
|
if (parts.length === 0) {
|
|
511
|
-
const
|
|
512
|
-
|
|
693
|
+
const choice2 = [];
|
|
694
|
+
const text = textFromGeminiResponse(response, parts);
|
|
695
|
+
if (text.length > 0) choice2.push({ type: "text", text });
|
|
696
|
+
choice2.push(...calls.map(toolCallFromGeminiFunctionCall));
|
|
697
|
+
return choice2;
|
|
513
698
|
}
|
|
699
|
+
const callsByPartIndex = new Map(calls.map((call) => [call.partIndex, call]));
|
|
514
700
|
const choice = [];
|
|
515
|
-
for (const part of parts) {
|
|
701
|
+
for (const { index, part } of parts) {
|
|
516
702
|
if (typeof part.text === "string" && part.text.length > 0) {
|
|
517
703
|
if (part.thought === true) {
|
|
518
|
-
choice.push(
|
|
704
|
+
choice.push({
|
|
705
|
+
type: "reasoning",
|
|
706
|
+
text: part.text,
|
|
707
|
+
details: [{ type: "summary", text: part.text }]
|
|
708
|
+
});
|
|
519
709
|
} else {
|
|
520
|
-
const text = AssistantContent.text(part.text);
|
|
521
710
|
const signature = thoughtSignatureFrom(part);
|
|
522
|
-
|
|
523
|
-
text
|
|
524
|
-
|
|
711
|
+
let text = {
|
|
712
|
+
type: "text",
|
|
713
|
+
text: part.text
|
|
714
|
+
};
|
|
715
|
+
if (signature !== void 0) text = { ...text, signature };
|
|
525
716
|
choice.push(text);
|
|
526
717
|
}
|
|
527
718
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const toolCall = AssistantContent.toolCall(
|
|
532
|
-
call.id ?? crypto.randomUUID(),
|
|
533
|
-
call.name,
|
|
534
|
-
call.args,
|
|
535
|
-
call.id
|
|
536
|
-
);
|
|
537
|
-
if (call.signature !== void 0) {
|
|
538
|
-
toolCall.signature = call.signature;
|
|
539
|
-
}
|
|
540
|
-
choice.push(toolCall);
|
|
541
|
-
}
|
|
719
|
+
const call = callsByPartIndex.get(index);
|
|
720
|
+
if (call !== void 0) {
|
|
721
|
+
choice.push(toolCallFromGeminiFunctionCall(call));
|
|
542
722
|
}
|
|
543
723
|
}
|
|
544
724
|
return choice;
|
|
545
725
|
}
|
|
546
|
-
function textFromGeminiResponse(response) {
|
|
547
|
-
if (
|
|
548
|
-
return
|
|
726
|
+
function textFromGeminiResponse(response, parts = candidateParts(response)) {
|
|
727
|
+
if (parts.length > 0) {
|
|
728
|
+
return parts.flatMap(
|
|
729
|
+
({ part }) => part.thought !== true && typeof part.text === "string" ? [part.text] : []
|
|
730
|
+
).join("");
|
|
549
731
|
}
|
|
550
|
-
|
|
732
|
+
if (hasGeminiCandidatePayload(response)) {
|
|
733
|
+
return "";
|
|
734
|
+
}
|
|
735
|
+
const directText = ownDataProperty(response, "text");
|
|
736
|
+
return typeof directText === "string" ? directText : "";
|
|
551
737
|
}
|
|
552
|
-
function functionCallsFromGeminiResponse(response) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
normalized.signature = signature;
|
|
738
|
+
function functionCallsFromGeminiResponse(response, parts, usage) {
|
|
739
|
+
if (parts.length > 0) {
|
|
740
|
+
const calls2 = parts.flatMap(({ index, part }) => {
|
|
741
|
+
if (part.functionCall === void 0) {
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
if (!isPlainObject2(part.functionCall)) {
|
|
745
|
+
throw invalidGeminiToolCall(deterministicGeminiToolCallId(index), usage);
|
|
746
|
+
}
|
|
747
|
+
return [functionCallFromGeminiPart(part.functionCall, part, index, usage)];
|
|
748
|
+
});
|
|
749
|
+
assertDistinctGeminiFunctionCalls(calls2, usage);
|
|
750
|
+
return calls2;
|
|
751
|
+
}
|
|
752
|
+
if (hasGeminiCandidatePayload(response)) {
|
|
753
|
+
return [];
|
|
754
|
+
}
|
|
755
|
+
const camelCaseCalls = ownDataProperty(response, "functionCalls");
|
|
756
|
+
const snakeCaseCalls = ownDataProperty(response, "function_calls");
|
|
757
|
+
const directCalls = Array.isArray(camelCaseCalls) ? camelCaseCalls : Array.isArray(snakeCaseCalls) ? snakeCaseCalls : [];
|
|
758
|
+
const calls = directCalls.map((call, index) => {
|
|
759
|
+
if (!isPlainObject2(call)) {
|
|
760
|
+
throw invalidGeminiToolCall(deterministicGeminiToolCallId(index), usage);
|
|
576
761
|
}
|
|
577
|
-
return
|
|
762
|
+
return functionCallFromGeminiPart(call, call, index, usage);
|
|
578
763
|
});
|
|
579
|
-
|
|
764
|
+
assertDistinctGeminiFunctionCalls(calls, usage);
|
|
765
|
+
return calls;
|
|
580
766
|
}
|
|
581
|
-
function functionCallFromGeminiPart(call, part) {
|
|
582
|
-
|
|
583
|
-
|
|
767
|
+
function functionCallFromGeminiPart(call, part, partIndex, usage) {
|
|
768
|
+
const fallbackId = deterministicGeminiToolCallId(partIndex);
|
|
769
|
+
const rawCallId = call.id;
|
|
770
|
+
const callId = isNonblankString(rawCallId) ? rawCallId : void 0;
|
|
771
|
+
const toolCallId = callId ?? fallbackId;
|
|
772
|
+
if (rawCallId !== void 0 && callId === void 0) {
|
|
773
|
+
throw invalidGeminiToolCall(toolCallId, usage);
|
|
774
|
+
}
|
|
775
|
+
if (call.partialArgs !== void 0 || call.partial_args !== void 0 || call.willContinue !== void 0 || call.will_continue !== void 0) {
|
|
776
|
+
throw new CompletionProviderOutputError({
|
|
777
|
+
kind: "incomplete-tool-call",
|
|
778
|
+
toolCallId,
|
|
779
|
+
usage
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
const name = call.name;
|
|
783
|
+
if (!isNonblankString(name)) {
|
|
784
|
+
throw invalidGeminiToolCall(toolCallId, usage);
|
|
785
|
+
}
|
|
786
|
+
const args = call.args;
|
|
787
|
+
if (!isPlainObject2(args) || !isJsonValue(args)) {
|
|
788
|
+
throw invalidGeminiToolCallArguments(toolCallId, usage);
|
|
584
789
|
}
|
|
585
790
|
const normalized = {
|
|
586
|
-
|
|
587
|
-
|
|
791
|
+
toolCallId,
|
|
792
|
+
name,
|
|
793
|
+
args,
|
|
794
|
+
partIndex
|
|
588
795
|
};
|
|
589
|
-
const id = stringFrom(call.id);
|
|
590
796
|
const signature = thoughtSignatureFrom(part) ?? thoughtSignatureFrom(call);
|
|
591
|
-
if (
|
|
592
|
-
normalized
|
|
797
|
+
if (callId !== void 0) {
|
|
798
|
+
return signature === void 0 ? { ...normalized, callId } : { ...normalized, callId, signature };
|
|
593
799
|
}
|
|
594
|
-
|
|
595
|
-
|
|
800
|
+
return signature === void 0 ? normalized : { ...normalized, signature };
|
|
801
|
+
}
|
|
802
|
+
function candidateParts(response, usage) {
|
|
803
|
+
const candidate = primaryGeminiCandidate(response);
|
|
804
|
+
if (candidate === void 0 || candidate.content === void 0) {
|
|
805
|
+
return [];
|
|
806
|
+
}
|
|
807
|
+
if (!isPlainObject2(candidate.content)) {
|
|
808
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
|
|
596
809
|
}
|
|
597
|
-
return
|
|
810
|
+
if (candidate.content.parts === void 0) return [];
|
|
811
|
+
if (!Array.isArray(candidate.content.parts)) {
|
|
812
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
|
|
813
|
+
}
|
|
814
|
+
if (candidate.content.parts.some((part) => !isPlainObject2(part))) {
|
|
815
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
|
|
816
|
+
}
|
|
817
|
+
return candidate.content.parts.map((part, index) => ({
|
|
818
|
+
index,
|
|
819
|
+
part
|
|
820
|
+
}));
|
|
598
821
|
}
|
|
599
|
-
function
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
822
|
+
function primaryGeminiCandidate(response) {
|
|
823
|
+
if (response.candidates === void 0) return void 0;
|
|
824
|
+
if (!Array.isArray(response.candidates)) {
|
|
825
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
|
|
826
|
+
}
|
|
827
|
+
const candidates = response.candidates;
|
|
828
|
+
if (candidates.some(
|
|
829
|
+
(candidate2) => !isPlainObject2(candidate2) || candidate2.index !== void 0 && (!Number.isSafeInteger(candidate2.index) || candidate2.index < 0)
|
|
830
|
+
)) {
|
|
831
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
|
|
832
|
+
}
|
|
833
|
+
const primaryCandidates = candidates.filter(
|
|
834
|
+
(candidate2) => isPlainObject2(candidate2) && candidate2.index === 0
|
|
835
|
+
);
|
|
836
|
+
if (primaryCandidates.length === 1) {
|
|
837
|
+
return primaryCandidates[0];
|
|
838
|
+
}
|
|
839
|
+
if (primaryCandidates.length > 1) {
|
|
840
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
|
|
841
|
+
}
|
|
842
|
+
if (candidates.length === 0) return void 0;
|
|
843
|
+
if (candidates.length !== 1) {
|
|
844
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
|
|
845
|
+
}
|
|
846
|
+
const candidate = candidates[0];
|
|
847
|
+
if (candidate.index !== void 0 && candidate.index !== 0) {
|
|
848
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
|
|
849
|
+
}
|
|
850
|
+
return candidate;
|
|
851
|
+
}
|
|
852
|
+
function providerFinishReasonFromGeminiResponse(response) {
|
|
853
|
+
const candidateFinishReason = stringFrom(primaryGeminiCandidate(response)?.finishReason);
|
|
854
|
+
if (candidateFinishReason !== void 0) return candidateFinishReason;
|
|
855
|
+
const promptFeedback = response.promptFeedback ?? response.prompt_feedback;
|
|
856
|
+
if (promptFeedback === void 0) return void 0;
|
|
857
|
+
if (!isPlainObject2(promptFeedback)) {
|
|
858
|
+
throw new CompletionProviderOutputError({ kind: "invalid-response" });
|
|
859
|
+
}
|
|
860
|
+
const blockReason = promptFeedback.blockReason ?? promptFeedback.block_reason;
|
|
861
|
+
if (blockReason === void 0) return void 0;
|
|
862
|
+
if (typeof blockReason !== "string" || blockReason.length === 0) {
|
|
863
|
+
throw new CompletionProviderOutputError({ kind: "invalid-response" });
|
|
864
|
+
}
|
|
865
|
+
return blockReason;
|
|
866
|
+
}
|
|
867
|
+
function hasGeminiCandidatePayload(response) {
|
|
868
|
+
return Array.isArray(response.candidates) && response.candidates.length > 0;
|
|
869
|
+
}
|
|
870
|
+
function hasGeminiFunctionCallMarker(response, parts) {
|
|
871
|
+
if (parts.some(({ part }) => ownDataProperty(part, "functionCall") !== void 0)) {
|
|
872
|
+
return true;
|
|
873
|
+
}
|
|
874
|
+
if (hasGeminiCandidatePayload(response)) return false;
|
|
875
|
+
const camelCaseCalls = ownDataProperty(response, "functionCalls");
|
|
876
|
+
const snakeCaseCalls = ownDataProperty(response, "function_calls");
|
|
877
|
+
return Array.isArray(camelCaseCalls) && camelCaseCalls.length > 0 || Array.isArray(snakeCaseCalls) && snakeCaseCalls.length > 0;
|
|
878
|
+
}
|
|
879
|
+
function deterministicGeminiToolCallId(partIndex) {
|
|
880
|
+
return `gemini-tool-${partIndex.toString()}`;
|
|
881
|
+
}
|
|
882
|
+
function toolCallFromGeminiFunctionCall(call) {
|
|
883
|
+
let toolCall = {
|
|
884
|
+
type: "tool-call",
|
|
885
|
+
toolCallId: call.toolCallId,
|
|
886
|
+
toolName: call.name,
|
|
887
|
+
input: call.args
|
|
888
|
+
};
|
|
889
|
+
if (call.callId !== void 0) toolCall = { ...toolCall, callId: call.callId };
|
|
890
|
+
if (call.signature !== void 0) toolCall = { ...toolCall, signature: call.signature };
|
|
891
|
+
return toolCall;
|
|
892
|
+
}
|
|
893
|
+
function mergeGeminiStreamToolCalls(response, toolCalls) {
|
|
894
|
+
if (toolCalls.length === 0) return response;
|
|
895
|
+
const finalIds = new Set(
|
|
896
|
+
response.choice.flatMap((part) => part.type === "tool-call" ? [part.toolCallId] : [])
|
|
897
|
+
);
|
|
898
|
+
const missing = toolCalls.filter((toolCall) => !finalIds.has(toolCall.toolCallId));
|
|
899
|
+
return missing.length === 0 ? response : { ...response, choice: [...response.choice, ...missing] };
|
|
900
|
+
}
|
|
901
|
+
function assertSafeGeminiToolFinishReason(value, usage) {
|
|
902
|
+
const error = geminiToolFinishError(value, true, usage);
|
|
903
|
+
if (error !== void 0) throw error;
|
|
904
|
+
}
|
|
905
|
+
function geminiToolFinishError(value, hasToolCalls, usage) {
|
|
906
|
+
if (value === "MALFORMED_FUNCTION_CALL") {
|
|
907
|
+
return new CompletionProviderOutputError({ kind: "malformed-tool-arguments", usage });
|
|
908
|
+
}
|
|
909
|
+
if (value === "UNEXPECTED_TOOL_CALL" || value === "TOO_MANY_TOOL_CALLS") {
|
|
910
|
+
return new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
|
|
911
|
+
}
|
|
912
|
+
if (!hasToolCalls) return void 0;
|
|
913
|
+
if (value === void 0) {
|
|
914
|
+
return new CompletionProviderOutputError({ kind: "incomplete-tool-call", usage });
|
|
915
|
+
}
|
|
916
|
+
const finishReason = geminiFinishReason(value, true);
|
|
917
|
+
if (finishReason === "tool-calls") return void 0;
|
|
918
|
+
if (finishReason === "length") {
|
|
919
|
+
return new CompletionProviderOutputError({
|
|
920
|
+
kind: "truncated-tool-call",
|
|
921
|
+
finishReason,
|
|
922
|
+
usage
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
if (finishReason === "content-filter") {
|
|
926
|
+
return new CompletionProviderOutputError({
|
|
927
|
+
kind: "filtered-tool-call",
|
|
928
|
+
finishReason,
|
|
929
|
+
usage
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
return new CompletionProviderOutputError({ kind: "invalid-tool-call", finishReason, usage });
|
|
933
|
+
}
|
|
934
|
+
function assertSafeGeminiCompletionResponse(response) {
|
|
935
|
+
const toolCalls = response.choice.filter(
|
|
936
|
+
(part) => part.type === "tool-call"
|
|
937
|
+
);
|
|
938
|
+
if (toolCalls.length > 0) {
|
|
939
|
+
if (response.finishReason === void 0) {
|
|
940
|
+
throw new CompletionProviderOutputError({
|
|
941
|
+
kind: "incomplete-tool-call",
|
|
942
|
+
usage: response.usage
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
assertNormalizedGeminiToolFinishReason(response.finishReason, response.usage);
|
|
946
|
+
} else if (toolCalls.length === 0 && response.finishReason === "tool-calls") {
|
|
947
|
+
throw new CompletionProviderOutputError({
|
|
948
|
+
kind: "invalid-tool-call",
|
|
949
|
+
finishReason: response.finishReason,
|
|
950
|
+
usage: response.usage
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
const toolCallIds = /* @__PURE__ */ new Set();
|
|
954
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
955
|
+
for (const toolCall of toolCalls) {
|
|
956
|
+
if (toolCallIds.has(toolCall.toolCallId)) {
|
|
957
|
+
throw invalidGeminiToolCall(toolCall.toolCallId, response.usage);
|
|
958
|
+
}
|
|
959
|
+
toolCallIds.add(toolCall.toolCallId);
|
|
960
|
+
if (toolCall.callId !== void 0) {
|
|
961
|
+
if (callIds.has(toolCall.callId)) {
|
|
962
|
+
throw invalidGeminiToolCall(toolCall.toolCallId, response.usage);
|
|
963
|
+
}
|
|
964
|
+
callIds.add(toolCall.callId);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function assertDistinctGeminiFunctionCalls(calls, usage) {
|
|
969
|
+
const toolCallIds = /* @__PURE__ */ new Set();
|
|
970
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
971
|
+
for (const call of calls) {
|
|
972
|
+
if (toolCallIds.has(call.toolCallId)) {
|
|
973
|
+
throw invalidGeminiToolCall(call.toolCallId, usage);
|
|
974
|
+
}
|
|
975
|
+
toolCallIds.add(call.toolCallId);
|
|
976
|
+
if (call.callId !== void 0) {
|
|
977
|
+
if (callIds.has(call.callId)) {
|
|
978
|
+
throw invalidGeminiToolCall(call.toolCallId, usage);
|
|
979
|
+
}
|
|
980
|
+
callIds.add(call.callId);
|
|
604
981
|
}
|
|
605
|
-
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function assertNormalizedGeminiToolFinishReason(finishReason, usage) {
|
|
985
|
+
if (finishReason === "stop" || finishReason === "tool-calls") return;
|
|
986
|
+
if (finishReason === "length") {
|
|
987
|
+
throw new CompletionProviderOutputError({
|
|
988
|
+
kind: "truncated-tool-call",
|
|
989
|
+
finishReason,
|
|
990
|
+
usage
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
if (finishReason === "content-filter") {
|
|
994
|
+
throw new CompletionProviderOutputError({
|
|
995
|
+
kind: "filtered-tool-call",
|
|
996
|
+
finishReason,
|
|
997
|
+
usage
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
throw new CompletionProviderOutputError({ kind: "invalid-tool-call", finishReason, usage });
|
|
1001
|
+
}
|
|
1002
|
+
function geminiProviderOutputErrorWithUsage(error, usage) {
|
|
1003
|
+
if (error.kind === "truncated-tool-call") {
|
|
1004
|
+
return new CompletionProviderOutputError({
|
|
1005
|
+
kind: error.kind,
|
|
1006
|
+
finishReason: "length",
|
|
1007
|
+
toolCallId: error.toolCallId,
|
|
1008
|
+
usage
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
if (error.kind === "filtered-tool-call") {
|
|
1012
|
+
return new CompletionProviderOutputError({
|
|
1013
|
+
kind: error.kind,
|
|
1014
|
+
finishReason: "content-filter",
|
|
1015
|
+
toolCallId: error.toolCallId,
|
|
1016
|
+
usage
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
if (error.finishReason === "length" || error.finishReason === "content-filter") {
|
|
1020
|
+
throw error;
|
|
1021
|
+
}
|
|
1022
|
+
return new CompletionProviderOutputError({
|
|
1023
|
+
kind: error.kind,
|
|
1024
|
+
finishReason: error.finishReason,
|
|
1025
|
+
toolCallId: error.toolCallId,
|
|
1026
|
+
usage
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
function invalidGeminiToolCall(toolCallId, usage) {
|
|
1030
|
+
return new CompletionProviderOutputError({ kind: "invalid-tool-call", toolCallId, usage });
|
|
1031
|
+
}
|
|
1032
|
+
function invalidGeminiToolCallArguments(toolCallId, usage) {
|
|
1033
|
+
return new CompletionProviderOutputError({
|
|
1034
|
+
kind: "invalid-tool-arguments",
|
|
1035
|
+
toolCallId,
|
|
1036
|
+
usage
|
|
606
1037
|
});
|
|
607
1038
|
}
|
|
1039
|
+
function ownDataProperty(value, key) {
|
|
1040
|
+
try {
|
|
1041
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1042
|
+
return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
1043
|
+
} catch {
|
|
1044
|
+
return void 0;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
function isNonblankString(value) {
|
|
1048
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1049
|
+
}
|
|
608
1050
|
function usageFromGemini(usage) {
|
|
609
|
-
const raw =
|
|
1051
|
+
const raw = isPlainObject2(usage) ? usage : {};
|
|
610
1052
|
const promptInputTokens = numberFrom(raw.promptTokenCount);
|
|
611
1053
|
const toolInputTokens = numberFrom(raw.toolUsePromptTokenCount);
|
|
612
1054
|
const inputTokens = promptInputTokens + toolInputTokens;
|
|
@@ -631,17 +1073,11 @@ function usageFromGemini(usage) {
|
|
|
631
1073
|
}
|
|
632
1074
|
};
|
|
633
1075
|
}
|
|
634
|
-
function
|
|
635
|
-
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || Array.isArray(value) || isPlainObject(value)) {
|
|
636
|
-
return value;
|
|
637
|
-
}
|
|
638
|
-
return String(value);
|
|
639
|
-
}
|
|
640
|
-
function isPlainObject(value) {
|
|
1076
|
+
function isPlainObject2(value) {
|
|
641
1077
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
642
1078
|
}
|
|
643
1079
|
function numberFrom(value) {
|
|
644
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1080
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
645
1081
|
}
|
|
646
1082
|
function stringFrom(value) {
|
|
647
1083
|
return typeof value === "string" ? value : void 0;
|
|
@@ -661,36 +1097,41 @@ function toolCallDelta(id, values) {
|
|
|
661
1097
|
|
|
662
1098
|
// src/gemini/embedding.ts
|
|
663
1099
|
var GeminiEmbeddingModel = class {
|
|
664
|
-
constructor(client,
|
|
1100
|
+
constructor(client, options) {
|
|
665
1101
|
this.client = client;
|
|
666
|
-
this.
|
|
1102
|
+
this.modelId = options.modelId;
|
|
667
1103
|
this.dimensions = options.dimensions;
|
|
668
1104
|
this.maxBatchSize = options.maxBatchSize ?? 100;
|
|
669
1105
|
this.taskType = options.taskType;
|
|
670
1106
|
this.title = options.title;
|
|
671
1107
|
}
|
|
672
1108
|
client;
|
|
673
|
-
|
|
1109
|
+
provider = "gemini";
|
|
1110
|
+
modelId;
|
|
674
1111
|
dimensions;
|
|
675
1112
|
maxBatchSize;
|
|
676
1113
|
taskType;
|
|
677
1114
|
title;
|
|
678
|
-
async embedTexts(texts) {
|
|
1115
|
+
async embedTexts(texts, options) {
|
|
679
1116
|
const embeddings = [];
|
|
680
1117
|
for (let index = 0; index < texts.length; index += this.maxBatchSize) {
|
|
681
1118
|
const batch = texts.slice(index, index + this.maxBatchSize);
|
|
682
|
-
embeddings.push(...await this.embedBatch(batch));
|
|
1119
|
+
embeddings.push(...await this.embedBatch(batch, options));
|
|
683
1120
|
}
|
|
684
1121
|
return embeddings;
|
|
685
1122
|
}
|
|
686
|
-
async embedBatch(texts) {
|
|
1123
|
+
async embedBatch(texts, options) {
|
|
687
1124
|
if (texts.length === 0) {
|
|
688
1125
|
return [];
|
|
689
1126
|
}
|
|
1127
|
+
const config = { ...this.embeddingConfig() };
|
|
1128
|
+
if (options?.abortSignal !== void 0) {
|
|
1129
|
+
Object.assign(config, { abortSignal: options.abortSignal });
|
|
1130
|
+
}
|
|
690
1131
|
const response = await this.client.models.embedContent({
|
|
691
|
-
model: this.
|
|
1132
|
+
model: this.modelId,
|
|
692
1133
|
contents: texts,
|
|
693
|
-
config:
|
|
1134
|
+
config: disableGeminiNativeRetries(config)
|
|
694
1135
|
});
|
|
695
1136
|
const rawEmbeddings = embeddingsFromResponse(response);
|
|
696
1137
|
if (rawEmbeddings.length !== texts.length) {
|
|
@@ -740,54 +1181,60 @@ var GEMINI_2_5_FLASH_IMAGE = "gemini-2.5-flash-image";
|
|
|
740
1181
|
var GEMINI_3_PRO_IMAGE_PREVIEW = "gemini-3-pro-image-preview";
|
|
741
1182
|
var IMAGEN_4_GENERATE = "imagen-4.0-generate-001";
|
|
742
1183
|
var GeminiImageGenerationModel = class {
|
|
743
|
-
constructor(client,
|
|
1184
|
+
constructor(client, modelId) {
|
|
744
1185
|
this.client = client;
|
|
745
|
-
this.
|
|
1186
|
+
this.modelId = modelId;
|
|
746
1187
|
}
|
|
747
1188
|
client;
|
|
748
|
-
|
|
1189
|
+
modelId;
|
|
749
1190
|
provider = "gemini";
|
|
750
|
-
async imageGeneration(request) {
|
|
1191
|
+
async imageGeneration(request, options) {
|
|
1192
|
+
const providerOptions = isPlainObject3(request.providerOptions) ? request.providerOptions : {};
|
|
1193
|
+
const { config: providerConfigValue, ...providerTopLevel } = providerOptions;
|
|
1194
|
+
const providerConfig = isPlainObject3(providerConfigValue) ? providerConfigValue : {};
|
|
1195
|
+
const providerImageConfig = isPlainObject3(providerConfig.imageConfig) ? providerConfig.imageConfig : {};
|
|
1196
|
+
const config = {
|
|
1197
|
+
...providerConfig,
|
|
1198
|
+
responseModalities: ["TEXT", "IMAGE"],
|
|
1199
|
+
imageConfig: {
|
|
1200
|
+
...providerImageConfig,
|
|
1201
|
+
aspectRatio: aspectRatio(request.width, request.height)
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
if (options?.abortSignal !== void 0) config.abortSignal = options.abortSignal;
|
|
751
1205
|
const params = {
|
|
752
|
-
|
|
1206
|
+
...providerTopLevel,
|
|
1207
|
+
model: this.modelId,
|
|
753
1208
|
contents: request.prompt,
|
|
754
|
-
config:
|
|
755
|
-
responseModalities: ["TEXT", "IMAGE"],
|
|
756
|
-
imageConfig: { aspectRatio: aspectRatio(request.width, request.height) }
|
|
757
|
-
}
|
|
1209
|
+
config: disableGeminiNativeRetries(config)
|
|
758
1210
|
};
|
|
759
|
-
if (isPlainObject2(request.additionalParams)) {
|
|
760
|
-
const { config, ...topLevel } = request.additionalParams;
|
|
761
|
-
Object.assign(params, topLevel);
|
|
762
|
-
if (isPlainObject2(config)) {
|
|
763
|
-
params.config = { ...params.config, ...config };
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
1211
|
const response = await this.client.models.generateContent(params);
|
|
767
1212
|
return nativeImageResponseFromGemini(response);
|
|
768
1213
|
}
|
|
769
1214
|
};
|
|
770
1215
|
var GeminiImagenGenerationModel = class {
|
|
771
|
-
constructor(client,
|
|
1216
|
+
constructor(client, modelId) {
|
|
772
1217
|
this.client = client;
|
|
773
|
-
this.
|
|
1218
|
+
this.modelId = modelId;
|
|
774
1219
|
}
|
|
775
1220
|
client;
|
|
776
|
-
|
|
1221
|
+
modelId;
|
|
777
1222
|
provider = "gemini";
|
|
778
|
-
async imageGeneration(request) {
|
|
1223
|
+
async imageGeneration(request, options) {
|
|
1224
|
+
const providerOptions = isPlainObject3(request.providerOptions) ? request.providerOptions : {};
|
|
1225
|
+
const { config: providerConfigValue, ...providerTopLevel } = providerOptions;
|
|
1226
|
+
const providerConfig = isPlainObject3(providerConfigValue) ? providerConfigValue : {};
|
|
1227
|
+
const config = {
|
|
1228
|
+
...providerConfig,
|
|
1229
|
+
aspectRatio: aspectRatio(request.width, request.height)
|
|
1230
|
+
};
|
|
1231
|
+
if (options?.abortSignal !== void 0) config.abortSignal = options.abortSignal;
|
|
779
1232
|
const params = {
|
|
780
|
-
|
|
1233
|
+
...providerTopLevel,
|
|
1234
|
+
model: this.modelId,
|
|
781
1235
|
prompt: request.prompt,
|
|
782
|
-
config:
|
|
1236
|
+
config: disableGeminiNativeRetries(config)
|
|
783
1237
|
};
|
|
784
|
-
if (isPlainObject2(request.additionalParams)) {
|
|
785
|
-
const { config, ...topLevel } = request.additionalParams;
|
|
786
|
-
Object.assign(params, topLevel);
|
|
787
|
-
if (isPlainObject2(config)) {
|
|
788
|
-
params.config = { ...params.config, ...config };
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
1238
|
const response = await this.client.models.generateImages(params);
|
|
792
1239
|
return imagenResponseFromGemini(response);
|
|
793
1240
|
}
|
|
@@ -796,12 +1243,12 @@ function nativeImageResponseFromGemini(response) {
|
|
|
796
1243
|
const raw = response;
|
|
797
1244
|
const candidates = Array.isArray(raw.candidates) ? raw.candidates : [];
|
|
798
1245
|
const images = candidates.flatMap((candidate) => {
|
|
799
|
-
if (!
|
|
1246
|
+
if (!isPlainObject3(candidate) || !isPlainObject3(candidate.content)) {
|
|
800
1247
|
return [];
|
|
801
1248
|
}
|
|
802
1249
|
const parts = Array.isArray(candidate.content.parts) ? candidate.content.parts : [];
|
|
803
1250
|
return parts.flatMap((part) => {
|
|
804
|
-
if (!
|
|
1251
|
+
if (!isPlainObject3(part) || !isPlainObject3(part.inlineData)) {
|
|
805
1252
|
return [];
|
|
806
1253
|
}
|
|
807
1254
|
const data = part.inlineData.data;
|
|
@@ -816,14 +1263,11 @@ function nativeImageResponseFromGemini(response) {
|
|
|
816
1263
|
];
|
|
817
1264
|
});
|
|
818
1265
|
});
|
|
819
|
-
|
|
820
|
-
if (image === void 0) {
|
|
1266
|
+
if (images.length === 0) {
|
|
821
1267
|
throw new Error("Gemini image generation response contained no inline image data.");
|
|
822
1268
|
}
|
|
823
1269
|
return {
|
|
824
|
-
image,
|
|
825
1270
|
images,
|
|
826
|
-
mediaType: images[0]?.mediaType,
|
|
827
1271
|
rawResponse: response
|
|
828
1272
|
};
|
|
829
1273
|
}
|
|
@@ -831,7 +1275,7 @@ function imagenResponseFromGemini(response) {
|
|
|
831
1275
|
const raw = response;
|
|
832
1276
|
const images = (Array.isArray(raw.generatedImages) ? raw.generatedImages : []).flatMap(
|
|
833
1277
|
(item) => {
|
|
834
|
-
if (!
|
|
1278
|
+
if (!isPlainObject3(item) || !isPlainObject3(item.image)) {
|
|
835
1279
|
return [];
|
|
836
1280
|
}
|
|
837
1281
|
const imageBytes = item.image.imageBytes;
|
|
@@ -846,14 +1290,11 @@ function imagenResponseFromGemini(response) {
|
|
|
846
1290
|
];
|
|
847
1291
|
}
|
|
848
1292
|
);
|
|
849
|
-
|
|
850
|
-
if (image === void 0) {
|
|
1293
|
+
if (images.length === 0) {
|
|
851
1294
|
throw new Error("Gemini image generation response contained no base64 images.");
|
|
852
1295
|
}
|
|
853
1296
|
return {
|
|
854
|
-
image,
|
|
855
1297
|
images,
|
|
856
|
-
mediaType: images[0]?.mediaType,
|
|
857
1298
|
rawResponse: response
|
|
858
1299
|
};
|
|
859
1300
|
}
|
|
@@ -882,50 +1323,114 @@ function decodeBase64Image(value) {
|
|
|
882
1323
|
}
|
|
883
1324
|
return new Uint8Array(bytes);
|
|
884
1325
|
}
|
|
885
|
-
function
|
|
1326
|
+
function isPlainObject3(value) {
|
|
886
1327
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
887
1328
|
}
|
|
888
1329
|
|
|
1330
|
+
// src/gemini/models.ts
|
|
1331
|
+
var CONTEXT_1M_64K = {
|
|
1332
|
+
contextWindow: 1048576,
|
|
1333
|
+
maxInputTokens: 1048576,
|
|
1334
|
+
maxOutputTokens: 65536
|
|
1335
|
+
};
|
|
1336
|
+
var GEMINI_COMPLETION_MODEL_CONTEXT_LIMITS = {
|
|
1337
|
+
"gemini-2.0-flash": {
|
|
1338
|
+
contextWindow: 1048576,
|
|
1339
|
+
maxInputTokens: 1048576,
|
|
1340
|
+
maxOutputTokens: 8192
|
|
1341
|
+
},
|
|
1342
|
+
"gemini-2.0-flash-lite": {
|
|
1343
|
+
contextWindow: 1048576,
|
|
1344
|
+
maxInputTokens: 1048576,
|
|
1345
|
+
maxOutputTokens: 8192
|
|
1346
|
+
},
|
|
1347
|
+
"gemini-2.5-flash": CONTEXT_1M_64K,
|
|
1348
|
+
"gemini-2.5-flash-image": {
|
|
1349
|
+
contextWindow: 32768,
|
|
1350
|
+
maxInputTokens: 32768,
|
|
1351
|
+
maxOutputTokens: 32768
|
|
1352
|
+
},
|
|
1353
|
+
"gemini-2.5-flash-lite": CONTEXT_1M_64K,
|
|
1354
|
+
"gemini-2.5-flash-preview-tts": {
|
|
1355
|
+
contextWindow: 8192,
|
|
1356
|
+
maxInputTokens: 8192,
|
|
1357
|
+
maxOutputTokens: 16384
|
|
1358
|
+
},
|
|
1359
|
+
"gemini-2.5-pro": CONTEXT_1M_64K,
|
|
1360
|
+
"gemini-2.5-pro-preview-tts": {
|
|
1361
|
+
contextWindow: 8192,
|
|
1362
|
+
maxInputTokens: 8192,
|
|
1363
|
+
maxOutputTokens: 16384
|
|
1364
|
+
},
|
|
1365
|
+
"gemini-3-flash-preview": CONTEXT_1M_64K,
|
|
1366
|
+
"gemini-3-pro-image-preview": {
|
|
1367
|
+
contextWindow: 131072,
|
|
1368
|
+
maxInputTokens: 131072,
|
|
1369
|
+
maxOutputTokens: 32768
|
|
1370
|
+
},
|
|
1371
|
+
"gemini-3-pro-preview": CONTEXT_1M_64K,
|
|
1372
|
+
"gemini-3.1-flash-image-preview": {
|
|
1373
|
+
contextWindow: 65536,
|
|
1374
|
+
maxInputTokens: 65536,
|
|
1375
|
+
maxOutputTokens: 65536
|
|
1376
|
+
},
|
|
1377
|
+
"gemini-3.1-flash-lite": CONTEXT_1M_64K,
|
|
1378
|
+
"gemini-3.1-flash-lite-preview": CONTEXT_1M_64K,
|
|
1379
|
+
"gemini-3.1-pro-preview": CONTEXT_1M_64K,
|
|
1380
|
+
"gemini-3.1-pro-preview-customtools": CONTEXT_1M_64K,
|
|
1381
|
+
"gemini-3.5-flash": CONTEXT_1M_64K,
|
|
1382
|
+
"gemini-flash-latest": CONTEXT_1M_64K,
|
|
1383
|
+
"gemini-flash-lite-latest": CONTEXT_1M_64K,
|
|
1384
|
+
"gemma-4-26b-a4b-it": {
|
|
1385
|
+
contextWindow: 262144,
|
|
1386
|
+
maxInputTokens: 262144,
|
|
1387
|
+
maxOutputTokens: 32768
|
|
1388
|
+
},
|
|
1389
|
+
"gemma-4-31b-it": {
|
|
1390
|
+
contextWindow: 262144,
|
|
1391
|
+
maxInputTokens: 262144,
|
|
1392
|
+
maxOutputTokens: 32768
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
|
|
889
1396
|
// src/gemini/transcription.ts
|
|
890
1397
|
import { Buffer as Buffer3 } from "buffer";
|
|
891
1398
|
var TRANSCRIPTION_PREAMBLE = "Transcribe the provided audio exactly. Do not add additional information.";
|
|
892
1399
|
var GeminiTranscriptionModel = class {
|
|
893
|
-
constructor(client,
|
|
1400
|
+
constructor(client, modelId) {
|
|
894
1401
|
this.client = client;
|
|
895
|
-
this.
|
|
1402
|
+
this.modelId = modelId;
|
|
896
1403
|
}
|
|
897
1404
|
client;
|
|
898
|
-
|
|
1405
|
+
modelId;
|
|
899
1406
|
provider = "gemini";
|
|
900
|
-
async transcription(request) {
|
|
901
|
-
const config = {};
|
|
1407
|
+
async transcription(request, options) {
|
|
1408
|
+
const config = isPlainObject4(request.providerOptions) ? { ...request.providerOptions } : {};
|
|
902
1409
|
if (request.temperature !== void 0) {
|
|
903
1410
|
config.temperature = request.temperature;
|
|
904
1411
|
}
|
|
905
|
-
if (
|
|
906
|
-
Object.assign(config, request.additionalParams);
|
|
907
|
-
}
|
|
1412
|
+
if (options?.abortSignal !== void 0) config.abortSignal = options.abortSignal;
|
|
908
1413
|
const response = await this.client.models.generateContent({
|
|
909
|
-
model: this.
|
|
1414
|
+
model: this.modelId,
|
|
910
1415
|
contents: [
|
|
911
1416
|
{
|
|
912
1417
|
role: "user",
|
|
913
1418
|
parts: [
|
|
914
1419
|
{
|
|
915
1420
|
inlineData: {
|
|
916
|
-
mimeType: mimeTypeFromFilename(request.filename),
|
|
1421
|
+
mimeType: request.mediaType ?? mimeTypeFromFilename(request.filename),
|
|
917
1422
|
data: Buffer3.from(request.data).toString("base64")
|
|
918
1423
|
}
|
|
919
1424
|
}
|
|
920
1425
|
]
|
|
921
1426
|
}
|
|
922
1427
|
],
|
|
923
|
-
config: {
|
|
1428
|
+
config: disableGeminiNativeRetries({
|
|
924
1429
|
...config,
|
|
925
1430
|
systemInstruction: request.prompt === void 0 ? TRANSCRIPTION_PREAMBLE : `${TRANSCRIPTION_PREAMBLE}
|
|
926
1431
|
|
|
927
1432
|
${request.prompt}`
|
|
928
|
-
}
|
|
1433
|
+
})
|
|
929
1434
|
});
|
|
930
1435
|
return {
|
|
931
1436
|
text: textFromGenerateContentResponse(response),
|
|
@@ -940,12 +1445,12 @@ function textFromGenerateContentResponse(response) {
|
|
|
940
1445
|
}
|
|
941
1446
|
const candidates = Array.isArray(raw.candidates) ? raw.candidates : [];
|
|
942
1447
|
for (const candidate of candidates) {
|
|
943
|
-
if (!
|
|
1448
|
+
if (!isPlainObject4(candidate) || !isPlainObject4(candidate.content)) {
|
|
944
1449
|
continue;
|
|
945
1450
|
}
|
|
946
1451
|
const parts = Array.isArray(candidate.content.parts) ? candidate.content.parts : [];
|
|
947
1452
|
for (const part of parts) {
|
|
948
|
-
if (
|
|
1453
|
+
if (isPlainObject4(part) && typeof part.text === "string") {
|
|
949
1454
|
return part.text;
|
|
950
1455
|
}
|
|
951
1456
|
}
|
|
@@ -962,34 +1467,55 @@ function mimeTypeFromFilename(filename) {
|
|
|
962
1467
|
if (lower.endsWith(".opus")) return "audio/opus";
|
|
963
1468
|
return "audio/mpeg";
|
|
964
1469
|
}
|
|
965
|
-
function
|
|
1470
|
+
function isPlainObject4(value) {
|
|
966
1471
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
967
1472
|
}
|
|
968
1473
|
|
|
969
1474
|
// src/gemini/client.ts
|
|
970
1475
|
var GeminiClient = class {
|
|
971
|
-
|
|
972
|
-
constructor(options
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1476
|
+
sdk;
|
|
1477
|
+
constructor(options) {
|
|
1478
|
+
if (options.client !== void 0) {
|
|
1479
|
+
rejectManagedOptionsWithInjectedClient(options, ["apiKey", "vertexAi"]);
|
|
1480
|
+
this.sdk = options.client;
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
this.sdk = new GoogleGenAI(toGoogleGenAIOptions(options));
|
|
1484
|
+
}
|
|
1485
|
+
completionModel(options) {
|
|
1486
|
+
const modelId = requireModelId(options.modelId);
|
|
1487
|
+
return new GeminiCompletionModel(
|
|
1488
|
+
this.sdk,
|
|
1489
|
+
modelId,
|
|
1490
|
+
resolveModelContextLimits(
|
|
1491
|
+
modelId,
|
|
1492
|
+
GEMINI_COMPLETION_MODEL_CONTEXT_LIMITS,
|
|
1493
|
+
options.contextLimits
|
|
1494
|
+
)
|
|
1495
|
+
);
|
|
980
1496
|
}
|
|
981
|
-
|
|
982
|
-
|
|
1497
|
+
embeddingModel(options) {
|
|
1498
|
+
requireModelId(options.modelId);
|
|
1499
|
+
validateOptionalPositiveSafeInteger(options.dimensions, "dimensions");
|
|
1500
|
+
validateOptionalPositiveSafeInteger(options.maxBatchSize, "maxBatchSize");
|
|
1501
|
+
return new GeminiEmbeddingModel(this.sdk, options);
|
|
983
1502
|
}
|
|
984
|
-
|
|
985
|
-
|
|
1503
|
+
imageGenerationModel(options) {
|
|
1504
|
+
const modelId = requireModelId(options.modelId);
|
|
1505
|
+
return options.api === "generateContent" ? new GeminiImageGenerationModel(this.sdk, modelId) : new GeminiImagenGenerationModel(this.sdk, modelId);
|
|
986
1506
|
}
|
|
987
|
-
transcriptionModel(
|
|
988
|
-
return new GeminiTranscriptionModel(this.
|
|
1507
|
+
transcriptionModel(options) {
|
|
1508
|
+
return new GeminiTranscriptionModel(this.sdk, requireModelId(options.modelId));
|
|
989
1509
|
}
|
|
990
|
-
async listModels() {
|
|
1510
|
+
async listModels(options = {}) {
|
|
991
1511
|
try {
|
|
992
|
-
const
|
|
1512
|
+
const config = { pageSize: 1e3 };
|
|
1513
|
+
if (options.abortSignal !== void 0) {
|
|
1514
|
+
Object.assign(config, { abortSignal: options.abortSignal });
|
|
1515
|
+
}
|
|
1516
|
+
const response = await this.sdk.models.list({
|
|
1517
|
+
config: disableGeminiNativeRetries(config)
|
|
1518
|
+
});
|
|
993
1519
|
const data = (await collectModelsFromResponse(response)).map(toListedModel).filter(isListedModel);
|
|
994
1520
|
return { data };
|
|
995
1521
|
} catch (error) {
|
|
@@ -997,25 +1523,48 @@ var GeminiClient = class {
|
|
|
997
1523
|
}
|
|
998
1524
|
}
|
|
999
1525
|
};
|
|
1526
|
+
function rejectManagedOptionsWithInjectedClient(options, keys) {
|
|
1527
|
+
const conflict = keys.find((key) => key in options);
|
|
1528
|
+
if (conflict !== void 0) {
|
|
1529
|
+
throw new TypeError(`GeminiClient cannot combine client with ${conflict}.`);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1000
1532
|
function toGoogleGenAIOptions(options) {
|
|
1001
|
-
if (options.
|
|
1002
|
-
|
|
1533
|
+
if ("client" in options && options.client !== void 0) {
|
|
1534
|
+
throw new TypeError("Injected Gemini clients do not have managed SDK options.");
|
|
1535
|
+
}
|
|
1536
|
+
if ("vertexAi" in options && options.vertexAi !== void 0) {
|
|
1537
|
+
const sdkOptions = {
|
|
1003
1538
|
vertexai: true,
|
|
1004
|
-
project: requireOption(options.
|
|
1005
|
-
location: requireOption(options.location, "location", "Vertex Gemini")
|
|
1006
|
-
...options.googleAuthOptions === void 0 ? {} : { googleAuthOptions: options.googleAuthOptions }
|
|
1539
|
+
project: requireOption(options.vertexAi.projectId, "projectId", "Vertex Gemini"),
|
|
1540
|
+
location: requireOption(options.vertexAi.location, "location", "Vertex Gemini")
|
|
1007
1541
|
};
|
|
1542
|
+
if (options.vertexAi.googleAuthOptions !== void 0) {
|
|
1543
|
+
sdkOptions.googleAuthOptions = options.vertexAi.googleAuthOptions;
|
|
1544
|
+
}
|
|
1545
|
+
return sdkOptions;
|
|
1008
1546
|
}
|
|
1009
1547
|
return {
|
|
1010
1548
|
apiKey: requireOption(options.apiKey, "apiKey", "Gemini")
|
|
1011
1549
|
};
|
|
1012
1550
|
}
|
|
1013
1551
|
function requireOption(value, name, label) {
|
|
1014
|
-
if (value === void 0 || value.length === 0) {
|
|
1552
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
1015
1553
|
throw new Error(`Missing ${label} ${name}. Pass ${name} when constructing GeminiClient.`);
|
|
1016
1554
|
}
|
|
1017
1555
|
return value;
|
|
1018
1556
|
}
|
|
1557
|
+
function requireModelId(modelId) {
|
|
1558
|
+
if (modelId.trim().length === 0) {
|
|
1559
|
+
throw new TypeError("modelId must be a non-empty string");
|
|
1560
|
+
}
|
|
1561
|
+
return modelId;
|
|
1562
|
+
}
|
|
1563
|
+
function validateOptionalPositiveSafeInteger(value, name) {
|
|
1564
|
+
if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) {
|
|
1565
|
+
throw new TypeError(`${name} must be a positive safe integer`);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1019
1568
|
async function collectModelsFromResponse(response) {
|
|
1020
1569
|
if (isAsyncIterable(response)) {
|
|
1021
1570
|
const models = [];
|
|
@@ -1116,11 +1665,6 @@ export {
|
|
|
1116
1665
|
GEMINI_2_5_FLASH_IMAGE,
|
|
1117
1666
|
GEMINI_3_PRO_IMAGE_PREVIEW,
|
|
1118
1667
|
GeminiClient,
|
|
1119
|
-
GeminiCompletionModel,
|
|
1120
|
-
GeminiEmbeddingModel,
|
|
1121
|
-
GeminiImageGenerationModel,
|
|
1122
|
-
GeminiImagenGenerationModel,
|
|
1123
|
-
GeminiTranscriptionModel,
|
|
1124
1668
|
IMAGEN_4_GENERATE,
|
|
1125
1669
|
gemini_exports as gemini
|
|
1126
1670
|
};
|