@tanstack/ai-gemini 0.8.8 → 0.9.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/dist/esm/adapters/summarize.js +5 -4
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +9 -4
- package/dist/esm/adapters/text.js +102 -35
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/model-meta.d.ts +38 -10
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/tools/code-execution-tool.d.ts +6 -3
- package/dist/esm/tools/code-execution-tool.js +8 -0
- package/dist/esm/tools/code-execution-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.d.ts +6 -3
- package/dist/esm/tools/computer-use-tool.js +11 -0
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.d.ts +6 -3
- package/dist/esm/tools/file-search-tool.js +9 -1
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-declaration-tool.js +29 -0
- package/dist/esm/tools/function-declaration-tool.js.map +1 -0
- package/dist/esm/tools/google-maps-tool.d.ts +6 -3
- package/dist/esm/tools/google-maps-tool.js +9 -1
- package/dist/esm/tools/google-maps-tool.js.map +1 -1
- package/dist/esm/tools/google-search-retriveal-tool.d.ts +6 -3
- package/dist/esm/tools/google-search-retriveal-tool.js +9 -1
- package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
- package/dist/esm/tools/google-search-tool.d.ts +6 -3
- package/dist/esm/tools/google-search-tool.js +9 -1
- package/dist/esm/tools/google-search-tool.js.map +1 -1
- package/dist/esm/tools/index.d.ts +9 -0
- package/dist/esm/tools/index.js +21 -0
- package/dist/esm/tools/index.js.map +1 -0
- package/dist/esm/tools/url-context-tool.d.ts +6 -3
- package/dist/esm/tools/url-context-tool.js +9 -1
- package/dist/esm/tools/url-context-tool.js.map +1 -1
- package/package.json +8 -3
- package/src/adapters/summarize.ts +8 -4
- package/src/adapters/text.ts +142 -40
- package/src/index.ts +1 -0
- package/src/model-meta.ts +59 -72
- package/src/tools/code-execution-tool.ts +10 -4
- package/src/tools/computer-use-tool.ts +13 -5
- package/src/tools/file-search-tool.ts +13 -5
- package/src/tools/google-maps-tool.ts +13 -5
- package/src/tools/google-search-retriveal-tool.ts +15 -6
- package/src/tools/google-search-tool.ts +13 -5
- package/src/tools/index.ts +51 -0
- package/src/tools/url-context-tool.ts +10 -4
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { codeExecutionTool } from "./code-execution-tool.js";
|
|
2
|
+
import { computerUseTool } from "./computer-use-tool.js";
|
|
3
|
+
import { fileSearchTool } from "./file-search-tool.js";
|
|
4
|
+
import { functionDeclarationTools } from "./function-declaration-tool.js";
|
|
5
|
+
import { googleMapsTool } from "./google-maps-tool.js";
|
|
6
|
+
import { googleSearchRetrievalTool } from "./google-search-retriveal-tool.js";
|
|
7
|
+
import { googleSearchTool } from "./google-search-tool.js";
|
|
8
|
+
import { urlContextTool } from "./url-context-tool.js";
|
|
9
|
+
import { convertToolsToProviderFormat } from "./tool-converter.js";
|
|
10
|
+
export {
|
|
11
|
+
codeExecutionTool,
|
|
12
|
+
computerUseTool,
|
|
13
|
+
convertToolsToProviderFormat,
|
|
14
|
+
fileSearchTool,
|
|
15
|
+
functionDeclarationTools,
|
|
16
|
+
googleMapsTool,
|
|
17
|
+
googleSearchRetrievalTool,
|
|
18
|
+
googleSearchTool,
|
|
19
|
+
urlContextTool
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { Tool } from '@tanstack/ai';
|
|
2
|
-
export interface
|
|
1
|
+
import { ProviderTool, Tool } from '@tanstack/ai';
|
|
2
|
+
export interface UrlContextToolConfig {
|
|
3
3
|
}
|
|
4
|
+
/** @deprecated Renamed to `UrlContextToolConfig`. Will be removed in a future release. */
|
|
5
|
+
export type UrlContextTool = UrlContextToolConfig;
|
|
6
|
+
export type GeminiUrlContextTool = ProviderTool<'gemini', 'url_context'>;
|
|
4
7
|
export declare function convertUrlContextToolToAdapterFormat(_tool: Tool): {
|
|
5
8
|
urlContext: {};
|
|
6
9
|
};
|
|
7
|
-
export declare function urlContextTool():
|
|
10
|
+
export declare function urlContextTool(): GeminiUrlContextTool;
|
|
@@ -3,7 +3,15 @@ function convertUrlContextToolToAdapterFormat(_tool) {
|
|
|
3
3
|
urlContext: {}
|
|
4
4
|
};
|
|
5
5
|
}
|
|
6
|
+
function urlContextTool() {
|
|
7
|
+
return {
|
|
8
|
+
name: "url_context",
|
|
9
|
+
description: "",
|
|
10
|
+
metadata: {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
6
13
|
export {
|
|
7
|
-
convertUrlContextToolToAdapterFormat
|
|
14
|
+
convertUrlContextToolToAdapterFormat,
|
|
15
|
+
urlContextTool
|
|
8
16
|
};
|
|
9
17
|
//# sourceMappingURL=url-context-tool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-context-tool.js","sources":["../../../src/tools/url-context-tool.ts"],"sourcesContent":["import type { Tool } from '@tanstack/ai'\n\nexport interface
|
|
1
|
+
{"version":3,"file":"url-context-tool.js","sources":["../../../src/tools/url-context-tool.ts"],"sourcesContent":["import type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport interface UrlContextToolConfig {}\n\n/** @deprecated Renamed to `UrlContextToolConfig`. Will be removed in a future release. */\nexport type UrlContextTool = UrlContextToolConfig\n\nexport type GeminiUrlContextTool = ProviderTool<'gemini', 'url_context'>\n\nexport function convertUrlContextToolToAdapterFormat(_tool: Tool) {\n return {\n urlContext: {},\n }\n}\n\nexport function urlContextTool(): GeminiUrlContextTool {\n // Phantom-brand cast: '~provider'/'~toolKind' are type-only and never assigned at runtime.\n return {\n name: 'url_context',\n description: '',\n metadata: {},\n } as unknown as GeminiUrlContextTool\n}\n"],"names":[],"mappings":"AASO,SAAS,qCAAqC,OAAa;AAChE,SAAO;AAAA,IACL,YAAY,CAAA;AAAA,EAAC;AAEjB;AAEO,SAAS,iBAAuC;AAErD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU,CAAA;AAAA,EAAC;AAEf;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-gemini",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Google Gemini adapter for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/esm/index.d.ts",
|
|
18
18
|
"import": "./dist/esm/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./tools": {
|
|
21
|
+
"types": "./dist/esm/tools/index.d.ts",
|
|
22
|
+
"import": "./dist/esm/tools/index.js"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"files": [
|
|
@@ -33,12 +37,13 @@
|
|
|
33
37
|
"@google/genai": "^1.43.0"
|
|
34
38
|
},
|
|
35
39
|
"peerDependencies": {
|
|
36
|
-
"@tanstack/ai": "^0.
|
|
40
|
+
"@tanstack/ai": "^0.12.0"
|
|
37
41
|
},
|
|
38
42
|
"devDependencies": {
|
|
39
43
|
"@vitest/coverage-v8": "4.0.14",
|
|
40
44
|
"vite": "^7.2.7",
|
|
41
|
-
"
|
|
45
|
+
"zod": "^4.2.0",
|
|
46
|
+
"@tanstack/ai": "0.12.0"
|
|
42
47
|
},
|
|
43
48
|
"scripts": {
|
|
44
49
|
"build": "vite build",
|
|
@@ -13,6 +13,10 @@ import type {
|
|
|
13
13
|
SummarizationResult,
|
|
14
14
|
} from '@tanstack/ai'
|
|
15
15
|
|
|
16
|
+
/** Cast an event object to StreamChunk. */
|
|
17
|
+
const asChunk = (chunk: Record<string, unknown>) =>
|
|
18
|
+
chunk as unknown as StreamChunk
|
|
19
|
+
|
|
16
20
|
/**
|
|
17
21
|
* Configuration for Gemini summarize adapter
|
|
18
22
|
*/
|
|
@@ -161,14 +165,14 @@ export class GeminiSummarizeAdapter<
|
|
|
161
165
|
for (const part of chunk.candidates[0].content.parts) {
|
|
162
166
|
if (part.text) {
|
|
163
167
|
accumulatedContent += part.text
|
|
164
|
-
yield {
|
|
168
|
+
yield asChunk({
|
|
165
169
|
type: 'TEXT_MESSAGE_CONTENT',
|
|
166
170
|
messageId: id,
|
|
167
171
|
model,
|
|
168
172
|
timestamp: Date.now(),
|
|
169
173
|
delta: part.text,
|
|
170
174
|
content: accumulatedContent,
|
|
171
|
-
}
|
|
175
|
+
})
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
}
|
|
@@ -180,7 +184,7 @@ export class GeminiSummarizeAdapter<
|
|
|
180
184
|
finishReason === FinishReason.MAX_TOKENS ||
|
|
181
185
|
finishReason === FinishReason.SAFETY
|
|
182
186
|
) {
|
|
183
|
-
yield {
|
|
187
|
+
yield asChunk({
|
|
184
188
|
type: 'RUN_FINISHED',
|
|
185
189
|
runId: id,
|
|
186
190
|
model,
|
|
@@ -196,7 +200,7 @@ export class GeminiSummarizeAdapter<
|
|
|
196
200
|
completionTokens: outputTokens,
|
|
197
201
|
totalTokens: inputTokens + outputTokens,
|
|
198
202
|
},
|
|
199
|
-
}
|
|
203
|
+
})
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
}
|
package/src/adapters/text.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import type {
|
|
10
10
|
GEMINI_MODELS,
|
|
11
11
|
GeminiChatModelProviderOptionsByName,
|
|
12
|
+
GeminiChatModelToolCapabilitiesByName,
|
|
12
13
|
GeminiModelInputModalitiesByName,
|
|
13
14
|
} from '../model-meta'
|
|
14
15
|
import type {
|
|
@@ -34,6 +35,11 @@ import type { ExternalTextProviderOptions } from '../text/text-provider-options'
|
|
|
34
35
|
import type { GeminiMessageMetadataByModality } from '../message-types'
|
|
35
36
|
import type { GeminiClientConfig } from '../utils'
|
|
36
37
|
|
|
38
|
+
/** Cast an event object to StreamChunk. Adapters construct events with string
|
|
39
|
+
* literal types which are structurally compatible with the EventType enum. */
|
|
40
|
+
const asChunk = (chunk: Record<string, unknown>) =>
|
|
41
|
+
chunk as unknown as StreamChunk
|
|
42
|
+
|
|
37
43
|
/**
|
|
38
44
|
* Configuration for Gemini text adapter
|
|
39
45
|
*/
|
|
@@ -66,6 +72,15 @@ type ResolveInputModalities<TModel extends string> =
|
|
|
66
72
|
? GeminiModelInputModalitiesByName[TModel]
|
|
67
73
|
: readonly ['text', 'image', 'audio', 'video', 'document']
|
|
68
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Resolve tool capabilities for a specific model.
|
|
77
|
+
* If the model has explicit tools in the map, use those; otherwise use empty tuple.
|
|
78
|
+
*/
|
|
79
|
+
type ResolveToolCapabilities<TModel extends string> =
|
|
80
|
+
TModel extends keyof GeminiChatModelToolCapabilitiesByName
|
|
81
|
+
? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>
|
|
82
|
+
: readonly []
|
|
83
|
+
|
|
69
84
|
// ===========================
|
|
70
85
|
// Adapter Implementation
|
|
71
86
|
// ===========================
|
|
@@ -78,14 +93,17 @@ type ResolveInputModalities<TModel extends string> =
|
|
|
78
93
|
*/
|
|
79
94
|
export class GeminiTextAdapter<
|
|
80
95
|
TModel extends (typeof GEMINI_MODELS)[number],
|
|
81
|
-
TProviderOptions extends
|
|
96
|
+
TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,
|
|
82
97
|
TInputModalities extends ReadonlyArray<Modality> =
|
|
83
98
|
ResolveInputModalities<TModel>,
|
|
99
|
+
TToolCapabilities extends ReadonlyArray<string> =
|
|
100
|
+
ResolveToolCapabilities<TModel>,
|
|
84
101
|
> extends BaseTextAdapter<
|
|
85
102
|
TModel,
|
|
86
103
|
TProviderOptions,
|
|
87
104
|
TInputModalities,
|
|
88
|
-
GeminiMessageMetadataByModality
|
|
105
|
+
GeminiMessageMetadataByModality,
|
|
106
|
+
TToolCapabilities
|
|
89
107
|
> {
|
|
90
108
|
readonly kind = 'text' as const
|
|
91
109
|
readonly name = 'gemini' as const
|
|
@@ -106,20 +124,24 @@ export class GeminiTextAdapter<
|
|
|
106
124
|
const result =
|
|
107
125
|
await this.client.models.generateContentStream(mappedOptions)
|
|
108
126
|
|
|
109
|
-
yield* this.processStreamChunks(result, options
|
|
127
|
+
yield* this.processStreamChunks(result, options)
|
|
110
128
|
} catch (error) {
|
|
111
129
|
const timestamp = Date.now()
|
|
112
|
-
yield {
|
|
130
|
+
yield asChunk({
|
|
113
131
|
type: 'RUN_ERROR',
|
|
114
132
|
model: options.model,
|
|
115
133
|
timestamp,
|
|
134
|
+
message:
|
|
135
|
+
error instanceof Error
|
|
136
|
+
? error.message
|
|
137
|
+
: 'An unknown error occurred during the chat stream.',
|
|
116
138
|
error: {
|
|
117
139
|
message:
|
|
118
140
|
error instanceof Error
|
|
119
141
|
? error.message
|
|
120
142
|
: 'An unknown error occurred during the chat stream.',
|
|
121
143
|
},
|
|
122
|
-
}
|
|
144
|
+
})
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
147
|
|
|
@@ -191,8 +213,9 @@ export class GeminiTextAdapter<
|
|
|
191
213
|
|
|
192
214
|
private async *processStreamChunks(
|
|
193
215
|
result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,
|
|
194
|
-
|
|
216
|
+
options: TextOptions<GeminiTextProviderOptions>,
|
|
195
217
|
): AsyncIterable<StreamChunk> {
|
|
218
|
+
const model = options.model
|
|
196
219
|
const timestamp = Date.now()
|
|
197
220
|
let accumulatedContent = ''
|
|
198
221
|
let accumulatedThinking = ''
|
|
@@ -209,9 +232,12 @@ export class GeminiTextAdapter<
|
|
|
209
232
|
let nextToolIndex = 0
|
|
210
233
|
|
|
211
234
|
// AG-UI lifecycle tracking
|
|
212
|
-
const runId = generateId(this.name)
|
|
235
|
+
const runId = options.runId ?? generateId(this.name)
|
|
236
|
+
const threadId = options.threadId ?? generateId(this.name)
|
|
213
237
|
const messageId = generateId(this.name)
|
|
214
238
|
let stepId: string | null = null
|
|
239
|
+
let reasoningMessageId: string | null = null
|
|
240
|
+
let hasClosedReasoning = false
|
|
215
241
|
let hasEmittedRunStarted = false
|
|
216
242
|
let hasEmittedTextMessageStart = false
|
|
217
243
|
let hasEmittedStepStarted = false
|
|
@@ -220,12 +246,13 @@ export class GeminiTextAdapter<
|
|
|
220
246
|
// Emit RUN_STARTED on first chunk
|
|
221
247
|
if (!hasEmittedRunStarted) {
|
|
222
248
|
hasEmittedRunStarted = true
|
|
223
|
-
yield {
|
|
249
|
+
yield asChunk({
|
|
224
250
|
type: 'RUN_STARTED',
|
|
225
251
|
runId,
|
|
252
|
+
threadId,
|
|
226
253
|
model,
|
|
227
254
|
timestamp,
|
|
228
|
-
}
|
|
255
|
+
})
|
|
229
256
|
}
|
|
230
257
|
|
|
231
258
|
if (chunk.candidates?.[0]?.content?.parts) {
|
|
@@ -234,51 +261,99 @@ export class GeminiTextAdapter<
|
|
|
234
261
|
for (const part of parts) {
|
|
235
262
|
if (part.text) {
|
|
236
263
|
if (part.thought) {
|
|
237
|
-
// Emit STEP_STARTED on first thinking content
|
|
264
|
+
// Emit STEP_STARTED and REASONING events on first thinking content
|
|
238
265
|
if (!hasEmittedStepStarted) {
|
|
239
266
|
hasEmittedStepStarted = true
|
|
240
267
|
stepId = generateId(this.name)
|
|
241
|
-
|
|
268
|
+
reasoningMessageId = generateId(this.name)
|
|
269
|
+
|
|
270
|
+
// Spec REASONING events
|
|
271
|
+
yield asChunk({
|
|
272
|
+
type: 'REASONING_START',
|
|
273
|
+
messageId: reasoningMessageId,
|
|
274
|
+
model,
|
|
275
|
+
timestamp,
|
|
276
|
+
})
|
|
277
|
+
yield asChunk({
|
|
278
|
+
type: 'REASONING_MESSAGE_START',
|
|
279
|
+
messageId: reasoningMessageId,
|
|
280
|
+
role: 'reasoning' as const,
|
|
281
|
+
model,
|
|
282
|
+
timestamp,
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
// Legacy STEP events (kept during transition)
|
|
286
|
+
yield asChunk({
|
|
242
287
|
type: 'STEP_STARTED',
|
|
288
|
+
stepName: stepId,
|
|
243
289
|
stepId,
|
|
244
290
|
model,
|
|
245
291
|
timestamp,
|
|
246
292
|
stepType: 'thinking',
|
|
247
|
-
}
|
|
293
|
+
})
|
|
248
294
|
}
|
|
249
295
|
|
|
250
296
|
accumulatedThinking += part.text
|
|
251
|
-
|
|
297
|
+
|
|
298
|
+
// Spec REASONING content event
|
|
299
|
+
yield asChunk({
|
|
300
|
+
type: 'REASONING_MESSAGE_CONTENT',
|
|
301
|
+
messageId: reasoningMessageId!,
|
|
302
|
+
delta: part.text,
|
|
303
|
+
model,
|
|
304
|
+
timestamp,
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// Legacy STEP event
|
|
308
|
+
yield asChunk({
|
|
252
309
|
type: 'STEP_FINISHED',
|
|
310
|
+
stepName: stepId || generateId(this.name),
|
|
253
311
|
stepId: stepId || generateId(this.name),
|
|
254
312
|
model,
|
|
255
313
|
timestamp,
|
|
256
314
|
delta: part.text,
|
|
257
315
|
content: accumulatedThinking,
|
|
258
|
-
}
|
|
316
|
+
})
|
|
259
317
|
} else if (part.text.trim()) {
|
|
318
|
+
// Close reasoning before text starts
|
|
319
|
+
if (reasoningMessageId && !hasClosedReasoning) {
|
|
320
|
+
hasClosedReasoning = true
|
|
321
|
+
yield asChunk({
|
|
322
|
+
type: 'REASONING_MESSAGE_END',
|
|
323
|
+
messageId: reasoningMessageId,
|
|
324
|
+
model,
|
|
325
|
+
timestamp,
|
|
326
|
+
})
|
|
327
|
+
yield asChunk({
|
|
328
|
+
type: 'REASONING_END',
|
|
329
|
+
messageId: reasoningMessageId,
|
|
330
|
+
model,
|
|
331
|
+
timestamp,
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
|
|
260
335
|
// Skip whitespace-only text parts (e.g. "\n" during auto-continuation)
|
|
261
336
|
// Emit TEXT_MESSAGE_START on first text content
|
|
262
337
|
if (!hasEmittedTextMessageStart) {
|
|
263
338
|
hasEmittedTextMessageStart = true
|
|
264
|
-
yield {
|
|
339
|
+
yield asChunk({
|
|
265
340
|
type: 'TEXT_MESSAGE_START',
|
|
266
341
|
messageId,
|
|
267
342
|
model,
|
|
268
343
|
timestamp,
|
|
269
344
|
role: 'assistant',
|
|
270
|
-
}
|
|
345
|
+
})
|
|
271
346
|
}
|
|
272
347
|
|
|
273
348
|
accumulatedContent += part.text
|
|
274
|
-
yield {
|
|
349
|
+
yield asChunk({
|
|
275
350
|
type: 'TEXT_MESSAGE_CONTENT',
|
|
276
351
|
messageId,
|
|
277
352
|
model,
|
|
278
353
|
timestamp,
|
|
279
354
|
delta: part.text,
|
|
280
355
|
content: accumulatedContent,
|
|
281
|
-
}
|
|
356
|
+
})
|
|
282
357
|
}
|
|
283
358
|
}
|
|
284
359
|
|
|
@@ -323,9 +398,10 @@ export class GeminiTextAdapter<
|
|
|
323
398
|
// Emit TOOL_CALL_START if not already started
|
|
324
399
|
if (!toolCallData.started) {
|
|
325
400
|
toolCallData.started = true
|
|
326
|
-
yield {
|
|
401
|
+
yield asChunk({
|
|
327
402
|
type: 'TOOL_CALL_START',
|
|
328
403
|
toolCallId,
|
|
404
|
+
toolCallName: toolCallData.name,
|
|
329
405
|
toolName: toolCallData.name,
|
|
330
406
|
model,
|
|
331
407
|
timestamp,
|
|
@@ -335,18 +411,18 @@ export class GeminiTextAdapter<
|
|
|
335
411
|
thoughtSignature: toolCallData.thoughtSignature,
|
|
336
412
|
},
|
|
337
413
|
}),
|
|
338
|
-
}
|
|
414
|
+
})
|
|
339
415
|
}
|
|
340
416
|
|
|
341
417
|
// Emit TOOL_CALL_ARGS
|
|
342
|
-
yield {
|
|
418
|
+
yield asChunk({
|
|
343
419
|
type: 'TOOL_CALL_ARGS',
|
|
344
420
|
toolCallId,
|
|
345
421
|
model,
|
|
346
422
|
timestamp,
|
|
347
423
|
delta: toolCallData.args,
|
|
348
424
|
args: toolCallData.args,
|
|
349
|
-
}
|
|
425
|
+
})
|
|
350
426
|
}
|
|
351
427
|
}
|
|
352
428
|
} else if (chunk.data && chunk.data.trim()) {
|
|
@@ -354,24 +430,24 @@ export class GeminiTextAdapter<
|
|
|
354
430
|
// Emit TEXT_MESSAGE_START on first text content
|
|
355
431
|
if (!hasEmittedTextMessageStart) {
|
|
356
432
|
hasEmittedTextMessageStart = true
|
|
357
|
-
yield {
|
|
433
|
+
yield asChunk({
|
|
358
434
|
type: 'TEXT_MESSAGE_START',
|
|
359
435
|
messageId,
|
|
360
436
|
model,
|
|
361
437
|
timestamp,
|
|
362
438
|
role: 'assistant',
|
|
363
|
-
}
|
|
439
|
+
})
|
|
364
440
|
}
|
|
365
441
|
|
|
366
442
|
accumulatedContent += chunk.data
|
|
367
|
-
yield {
|
|
443
|
+
yield asChunk({
|
|
368
444
|
type: 'TEXT_MESSAGE_CONTENT',
|
|
369
445
|
messageId,
|
|
370
446
|
model,
|
|
371
447
|
timestamp,
|
|
372
448
|
delta: chunk.data,
|
|
373
449
|
content: accumulatedContent,
|
|
374
|
-
}
|
|
450
|
+
})
|
|
375
451
|
}
|
|
376
452
|
|
|
377
453
|
if (chunk.candidates?.[0]?.finishReason) {
|
|
@@ -400,14 +476,15 @@ export class GeminiTextAdapter<
|
|
|
400
476
|
})
|
|
401
477
|
|
|
402
478
|
// Emit TOOL_CALL_START
|
|
403
|
-
yield {
|
|
479
|
+
yield asChunk({
|
|
404
480
|
type: 'TOOL_CALL_START',
|
|
405
481
|
toolCallId,
|
|
482
|
+
toolCallName: functionCall.name || '',
|
|
406
483
|
toolName: functionCall.name || '',
|
|
407
484
|
model,
|
|
408
485
|
timestamp,
|
|
409
486
|
index: nextToolIndex - 1,
|
|
410
|
-
}
|
|
487
|
+
})
|
|
411
488
|
|
|
412
489
|
// Emit TOOL_CALL_END with parsed input
|
|
413
490
|
let parsedInput: unknown = {}
|
|
@@ -422,14 +499,15 @@ export class GeminiTextAdapter<
|
|
|
422
499
|
parsedInput = {}
|
|
423
500
|
}
|
|
424
501
|
|
|
425
|
-
yield {
|
|
502
|
+
yield asChunk({
|
|
426
503
|
type: 'TOOL_CALL_END',
|
|
427
504
|
toolCallId,
|
|
505
|
+
toolCallName: functionCall.name || '',
|
|
428
506
|
toolName: functionCall.name || '',
|
|
429
507
|
model,
|
|
430
508
|
timestamp,
|
|
431
509
|
input: parsedInput,
|
|
432
|
-
}
|
|
510
|
+
})
|
|
433
511
|
}
|
|
434
512
|
}
|
|
435
513
|
}
|
|
@@ -445,14 +523,15 @@ export class GeminiTextAdapter<
|
|
|
445
523
|
parsedInput = {}
|
|
446
524
|
}
|
|
447
525
|
|
|
448
|
-
yield {
|
|
526
|
+
yield asChunk({
|
|
449
527
|
type: 'TOOL_CALL_END',
|
|
450
528
|
toolCallId,
|
|
529
|
+
toolCallName: toolCallData.name,
|
|
451
530
|
toolName: toolCallData.name,
|
|
452
531
|
model,
|
|
453
532
|
timestamp,
|
|
454
533
|
input: parsedInput,
|
|
455
|
-
}
|
|
534
|
+
})
|
|
456
535
|
}
|
|
457
536
|
|
|
458
537
|
// Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls
|
|
@@ -461,32 +540,53 @@ export class GeminiTextAdapter<
|
|
|
461
540
|
}
|
|
462
541
|
|
|
463
542
|
if (finishReason === FinishReason.MAX_TOKENS) {
|
|
464
|
-
yield {
|
|
543
|
+
yield asChunk({
|
|
465
544
|
type: 'RUN_ERROR',
|
|
466
545
|
runId,
|
|
467
546
|
model,
|
|
468
547
|
timestamp,
|
|
548
|
+
message:
|
|
549
|
+
'The response was cut off because the maximum token limit was reached.',
|
|
550
|
+
code: 'max_tokens',
|
|
469
551
|
error: {
|
|
470
552
|
message:
|
|
471
553
|
'The response was cut off because the maximum token limit was reached.',
|
|
472
554
|
code: 'max_tokens',
|
|
473
555
|
},
|
|
474
|
-
}
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Close reasoning events if still open
|
|
560
|
+
if (reasoningMessageId && !hasClosedReasoning) {
|
|
561
|
+
hasClosedReasoning = true
|
|
562
|
+
yield asChunk({
|
|
563
|
+
type: 'REASONING_MESSAGE_END',
|
|
564
|
+
messageId: reasoningMessageId,
|
|
565
|
+
model,
|
|
566
|
+
timestamp,
|
|
567
|
+
})
|
|
568
|
+
yield asChunk({
|
|
569
|
+
type: 'REASONING_END',
|
|
570
|
+
messageId: reasoningMessageId,
|
|
571
|
+
model,
|
|
572
|
+
timestamp,
|
|
573
|
+
})
|
|
475
574
|
}
|
|
476
575
|
|
|
477
576
|
// Emit TEXT_MESSAGE_END if we had text content
|
|
478
577
|
if (hasEmittedTextMessageStart) {
|
|
479
|
-
yield {
|
|
578
|
+
yield asChunk({
|
|
480
579
|
type: 'TEXT_MESSAGE_END',
|
|
481
580
|
messageId,
|
|
482
581
|
model,
|
|
483
582
|
timestamp,
|
|
484
|
-
}
|
|
583
|
+
})
|
|
485
584
|
}
|
|
486
585
|
|
|
487
|
-
yield {
|
|
586
|
+
yield asChunk({
|
|
488
587
|
type: 'RUN_FINISHED',
|
|
489
588
|
runId,
|
|
589
|
+
threadId,
|
|
490
590
|
model,
|
|
491
591
|
timestamp,
|
|
492
592
|
finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',
|
|
@@ -497,7 +597,7 @@ export class GeminiTextAdapter<
|
|
|
497
597
|
totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,
|
|
498
598
|
}
|
|
499
599
|
: undefined,
|
|
500
|
-
}
|
|
600
|
+
})
|
|
501
601
|
}
|
|
502
602
|
}
|
|
503
603
|
}
|
|
@@ -723,7 +823,8 @@ export function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(
|
|
|
723
823
|
): GeminiTextAdapter<
|
|
724
824
|
TModel,
|
|
725
825
|
ResolveProviderOptions<TModel>,
|
|
726
|
-
ResolveInputModalities<TModel
|
|
826
|
+
ResolveInputModalities<TModel>,
|
|
827
|
+
ResolveToolCapabilities<TModel>
|
|
727
828
|
> {
|
|
728
829
|
return new GeminiTextAdapter({ apiKey, ...config }, model)
|
|
729
830
|
}
|
|
@@ -738,7 +839,8 @@ export function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(
|
|
|
738
839
|
): GeminiTextAdapter<
|
|
739
840
|
TModel,
|
|
740
841
|
ResolveProviderOptions<TModel>,
|
|
741
|
-
ResolveInputModalities<TModel
|
|
842
|
+
ResolveInputModalities<TModel>,
|
|
843
|
+
ResolveToolCapabilities<TModel>
|
|
742
844
|
> {
|
|
743
845
|
const apiKey = getGeminiApiKeyFromEnv()
|
|
744
846
|
return createGeminiChat(model, apiKey, config)
|