@tanstack/ai-gemini 0.10.2 → 0.10.4
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.d.ts +23 -48
- package/dist/esm/adapters/summarize.js +9 -159
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +2 -2
- package/dist/esm/adapters/text.js +107 -102
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -3
- package/dist/esm/message-types.d.ts +13 -0
- package/package.json +3 -3
- package/src/adapters/summarize.ts +37 -252
- package/src/adapters/text.ts +124 -111
- package/src/index.ts +2 -5
- package/src/message-types.ts +14 -0
package/src/adapters/text.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FinishReason } from '@google/genai'
|
|
2
|
+
import { EventType } from '@tanstack/ai'
|
|
2
3
|
import { BaseTextAdapter } from '@tanstack/ai/adapters'
|
|
3
4
|
import { convertToolsToProviderFormat } from '../tools/tool-converter'
|
|
4
5
|
import {
|
|
@@ -33,14 +34,12 @@ import type {
|
|
|
33
34
|
TextOptions,
|
|
34
35
|
} from '@tanstack/ai'
|
|
35
36
|
import type { ExternalTextProviderOptions } from '../text/text-provider-options'
|
|
36
|
-
import type {
|
|
37
|
+
import type {
|
|
38
|
+
GeminiMessageMetadataByModality,
|
|
39
|
+
GeminiToolCallMetadata,
|
|
40
|
+
} from '../message-types'
|
|
37
41
|
import type { GeminiClientConfig } from '../utils'
|
|
38
42
|
|
|
39
|
-
/** Cast an event object to StreamChunk. Adapters construct events with string
|
|
40
|
-
* literal types which are structurally compatible with the EventType enum. */
|
|
41
|
-
const asChunk = (chunk: Record<string, unknown>) =>
|
|
42
|
-
chunk as unknown as StreamChunk
|
|
43
|
-
|
|
44
43
|
/**
|
|
45
44
|
* Configuration for Gemini text adapter
|
|
46
45
|
*/
|
|
@@ -104,7 +103,8 @@ export class GeminiTextAdapter<
|
|
|
104
103
|
TProviderOptions,
|
|
105
104
|
TInputModalities,
|
|
106
105
|
GeminiMessageMetadataByModality,
|
|
107
|
-
TToolCapabilities
|
|
106
|
+
TToolCapabilities,
|
|
107
|
+
GeminiToolCallMetadata
|
|
108
108
|
> {
|
|
109
109
|
readonly kind = 'text' as const
|
|
110
110
|
readonly name = 'gemini' as const
|
|
@@ -132,15 +132,14 @@ export class GeminiTextAdapter<
|
|
|
132
132
|
|
|
133
133
|
yield* this.processStreamChunks(result, options, logger)
|
|
134
134
|
} catch (error) {
|
|
135
|
-
const timestamp = Date.now()
|
|
136
135
|
logger.errors('gemini.chatStream fatal', {
|
|
137
136
|
error,
|
|
138
137
|
source: 'gemini.chatStream',
|
|
139
138
|
})
|
|
140
|
-
yield
|
|
141
|
-
type:
|
|
139
|
+
yield {
|
|
140
|
+
type: EventType.RUN_ERROR,
|
|
142
141
|
model: options.model,
|
|
143
|
-
timestamp,
|
|
142
|
+
timestamp: Date.now(),
|
|
144
143
|
message:
|
|
145
144
|
error instanceof Error
|
|
146
145
|
? error.message
|
|
@@ -151,7 +150,7 @@ export class GeminiTextAdapter<
|
|
|
151
150
|
? error.message
|
|
152
151
|
: 'An unknown error occurred during the chat stream.',
|
|
153
152
|
},
|
|
154
|
-
}
|
|
153
|
+
}
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
|
|
@@ -236,7 +235,6 @@ export class GeminiTextAdapter<
|
|
|
236
235
|
logger: InternalLogger,
|
|
237
236
|
): AsyncIterable<StreamChunk> {
|
|
238
237
|
const model = options.model
|
|
239
|
-
const timestamp = Date.now()
|
|
240
238
|
let accumulatedContent = ''
|
|
241
239
|
let accumulatedThinking = ''
|
|
242
240
|
const toolCallMap = new Map<
|
|
@@ -267,13 +265,13 @@ export class GeminiTextAdapter<
|
|
|
267
265
|
// Emit RUN_STARTED on first chunk
|
|
268
266
|
if (!hasEmittedRunStarted) {
|
|
269
267
|
hasEmittedRunStarted = true
|
|
270
|
-
yield
|
|
271
|
-
type:
|
|
268
|
+
yield {
|
|
269
|
+
type: EventType.RUN_STARTED,
|
|
272
270
|
runId,
|
|
273
271
|
threadId,
|
|
274
272
|
model,
|
|
275
|
-
timestamp,
|
|
276
|
-
}
|
|
273
|
+
timestamp: Date.now(),
|
|
274
|
+
}
|
|
277
275
|
}
|
|
278
276
|
|
|
279
277
|
if (chunk.candidates?.[0]?.content?.parts) {
|
|
@@ -289,92 +287,92 @@ export class GeminiTextAdapter<
|
|
|
289
287
|
reasoningMessageId = generateId(this.name)
|
|
290
288
|
|
|
291
289
|
// Spec REASONING events
|
|
292
|
-
yield
|
|
293
|
-
type:
|
|
290
|
+
yield {
|
|
291
|
+
type: EventType.REASONING_START,
|
|
294
292
|
messageId: reasoningMessageId,
|
|
295
293
|
model,
|
|
296
|
-
timestamp,
|
|
297
|
-
}
|
|
298
|
-
yield
|
|
299
|
-
type:
|
|
294
|
+
timestamp: Date.now(),
|
|
295
|
+
}
|
|
296
|
+
yield {
|
|
297
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
300
298
|
messageId: reasoningMessageId,
|
|
301
299
|
role: 'reasoning' as const,
|
|
302
300
|
model,
|
|
303
|
-
timestamp,
|
|
304
|
-
}
|
|
301
|
+
timestamp: Date.now(),
|
|
302
|
+
}
|
|
305
303
|
|
|
306
304
|
// Legacy STEP events (kept during transition)
|
|
307
|
-
yield
|
|
308
|
-
type:
|
|
305
|
+
yield {
|
|
306
|
+
type: EventType.STEP_STARTED,
|
|
309
307
|
stepName: stepId,
|
|
310
308
|
stepId,
|
|
311
309
|
model,
|
|
312
|
-
timestamp,
|
|
310
|
+
timestamp: Date.now(),
|
|
313
311
|
stepType: 'thinking',
|
|
314
|
-
}
|
|
312
|
+
}
|
|
315
313
|
}
|
|
316
314
|
|
|
317
315
|
accumulatedThinking += part.text
|
|
318
316
|
|
|
319
317
|
// Spec REASONING content event
|
|
320
|
-
yield
|
|
321
|
-
type:
|
|
318
|
+
yield {
|
|
319
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
322
320
|
messageId: reasoningMessageId!,
|
|
323
321
|
delta: part.text,
|
|
324
322
|
model,
|
|
325
|
-
timestamp,
|
|
326
|
-
}
|
|
323
|
+
timestamp: Date.now(),
|
|
324
|
+
}
|
|
327
325
|
|
|
328
326
|
// Legacy STEP event
|
|
329
|
-
yield
|
|
330
|
-
type:
|
|
327
|
+
yield {
|
|
328
|
+
type: EventType.STEP_FINISHED,
|
|
331
329
|
stepName: stepId || generateId(this.name),
|
|
332
330
|
stepId: stepId || generateId(this.name),
|
|
333
331
|
model,
|
|
334
|
-
timestamp,
|
|
332
|
+
timestamp: Date.now(),
|
|
335
333
|
delta: part.text,
|
|
336
334
|
content: accumulatedThinking,
|
|
337
|
-
}
|
|
335
|
+
}
|
|
338
336
|
} else if (part.text.trim()) {
|
|
339
337
|
// Close reasoning before text starts
|
|
340
338
|
if (reasoningMessageId && !hasClosedReasoning) {
|
|
341
339
|
hasClosedReasoning = true
|
|
342
|
-
yield
|
|
343
|
-
type:
|
|
340
|
+
yield {
|
|
341
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
344
342
|
messageId: reasoningMessageId,
|
|
345
343
|
model,
|
|
346
|
-
timestamp,
|
|
347
|
-
}
|
|
348
|
-
yield
|
|
349
|
-
type:
|
|
344
|
+
timestamp: Date.now(),
|
|
345
|
+
}
|
|
346
|
+
yield {
|
|
347
|
+
type: EventType.REASONING_END,
|
|
350
348
|
messageId: reasoningMessageId,
|
|
351
349
|
model,
|
|
352
|
-
timestamp,
|
|
353
|
-
}
|
|
350
|
+
timestamp: Date.now(),
|
|
351
|
+
}
|
|
354
352
|
}
|
|
355
353
|
|
|
356
354
|
// Skip whitespace-only text parts (e.g. "\n" during auto-continuation)
|
|
357
355
|
// Emit TEXT_MESSAGE_START on first text content
|
|
358
356
|
if (!hasEmittedTextMessageStart) {
|
|
359
357
|
hasEmittedTextMessageStart = true
|
|
360
|
-
yield
|
|
361
|
-
type:
|
|
358
|
+
yield {
|
|
359
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
362
360
|
messageId,
|
|
363
361
|
model,
|
|
364
|
-
timestamp,
|
|
362
|
+
timestamp: Date.now(),
|
|
365
363
|
role: 'assistant',
|
|
366
|
-
}
|
|
364
|
+
}
|
|
367
365
|
}
|
|
368
366
|
|
|
369
367
|
accumulatedContent += part.text
|
|
370
|
-
yield
|
|
371
|
-
type:
|
|
368
|
+
yield {
|
|
369
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
372
370
|
messageId,
|
|
373
371
|
model,
|
|
374
|
-
timestamp,
|
|
372
|
+
timestamp: Date.now(),
|
|
375
373
|
delta: part.text,
|
|
376
374
|
content: accumulatedContent,
|
|
377
|
-
}
|
|
375
|
+
}
|
|
378
376
|
}
|
|
379
377
|
}
|
|
380
378
|
|
|
@@ -385,6 +383,11 @@ export class GeminiTextAdapter<
|
|
|
385
383
|
`${functionCall.name}_${Date.now()}_${nextToolIndex}`
|
|
386
384
|
const functionArgs = functionCall.args || {}
|
|
387
385
|
|
|
386
|
+
// Gemini emits thoughtSignature as a Part-level sibling of
|
|
387
|
+
// functionCall (per @google/genai Part type), not nested inside
|
|
388
|
+
// functionCall itself.
|
|
389
|
+
const partThoughtSignature = part.thoughtSignature || undefined
|
|
390
|
+
|
|
388
391
|
let toolCallData = toolCallMap.get(toolCallId)
|
|
389
392
|
if (!toolCallData) {
|
|
390
393
|
toolCallData = {
|
|
@@ -395,11 +398,13 @@ export class GeminiTextAdapter<
|
|
|
395
398
|
: JSON.stringify(functionArgs),
|
|
396
399
|
index: nextToolIndex++,
|
|
397
400
|
started: false,
|
|
398
|
-
thoughtSignature:
|
|
399
|
-
(functionCall as any).thoughtSignature || undefined,
|
|
401
|
+
thoughtSignature: partThoughtSignature,
|
|
400
402
|
}
|
|
401
403
|
toolCallMap.set(toolCallId, toolCallData)
|
|
402
404
|
} else {
|
|
405
|
+
if (!toolCallData.thoughtSignature && partThoughtSignature) {
|
|
406
|
+
toolCallData.thoughtSignature = partThoughtSignature
|
|
407
|
+
}
|
|
403
408
|
try {
|
|
404
409
|
const existingArgs = JSON.parse(toolCallData.args)
|
|
405
410
|
const newArgs =
|
|
@@ -419,31 +424,31 @@ export class GeminiTextAdapter<
|
|
|
419
424
|
// Emit TOOL_CALL_START if not already started
|
|
420
425
|
if (!toolCallData.started) {
|
|
421
426
|
toolCallData.started = true
|
|
422
|
-
yield
|
|
423
|
-
type:
|
|
427
|
+
yield {
|
|
428
|
+
type: EventType.TOOL_CALL_START,
|
|
424
429
|
toolCallId,
|
|
425
430
|
toolCallName: toolCallData.name,
|
|
426
431
|
toolName: toolCallData.name,
|
|
427
432
|
model,
|
|
428
|
-
timestamp,
|
|
433
|
+
timestamp: Date.now(),
|
|
429
434
|
index: toolCallData.index,
|
|
430
435
|
...(toolCallData.thoughtSignature && {
|
|
431
|
-
|
|
436
|
+
metadata: {
|
|
432
437
|
thoughtSignature: toolCallData.thoughtSignature,
|
|
433
|
-
},
|
|
438
|
+
} satisfies GeminiToolCallMetadata,
|
|
434
439
|
}),
|
|
435
|
-
}
|
|
440
|
+
}
|
|
436
441
|
}
|
|
437
442
|
|
|
438
443
|
// Emit TOOL_CALL_ARGS
|
|
439
|
-
yield
|
|
440
|
-
type:
|
|
444
|
+
yield {
|
|
445
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
441
446
|
toolCallId,
|
|
442
447
|
model,
|
|
443
|
-
timestamp,
|
|
448
|
+
timestamp: Date.now(),
|
|
444
449
|
delta: toolCallData.args,
|
|
445
450
|
args: toolCallData.args,
|
|
446
|
-
}
|
|
451
|
+
}
|
|
447
452
|
}
|
|
448
453
|
}
|
|
449
454
|
} else if (chunk.data && chunk.data.trim()) {
|
|
@@ -451,24 +456,24 @@ export class GeminiTextAdapter<
|
|
|
451
456
|
// Emit TEXT_MESSAGE_START on first text content
|
|
452
457
|
if (!hasEmittedTextMessageStart) {
|
|
453
458
|
hasEmittedTextMessageStart = true
|
|
454
|
-
yield
|
|
455
|
-
type:
|
|
459
|
+
yield {
|
|
460
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
456
461
|
messageId,
|
|
457
462
|
model,
|
|
458
|
-
timestamp,
|
|
463
|
+
timestamp: Date.now(),
|
|
459
464
|
role: 'assistant',
|
|
460
|
-
}
|
|
465
|
+
}
|
|
461
466
|
}
|
|
462
467
|
|
|
463
468
|
accumulatedContent += chunk.data
|
|
464
|
-
yield
|
|
465
|
-
type:
|
|
469
|
+
yield {
|
|
470
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
466
471
|
messageId,
|
|
467
472
|
model,
|
|
468
|
-
timestamp,
|
|
473
|
+
timestamp: Date.now(),
|
|
469
474
|
delta: chunk.data,
|
|
470
475
|
content: accumulatedContent,
|
|
471
|
-
}
|
|
476
|
+
}
|
|
472
477
|
}
|
|
473
478
|
|
|
474
479
|
if (chunk.candidates?.[0]?.finishReason) {
|
|
@@ -497,15 +502,15 @@ export class GeminiTextAdapter<
|
|
|
497
502
|
})
|
|
498
503
|
|
|
499
504
|
// Emit TOOL_CALL_START
|
|
500
|
-
yield
|
|
501
|
-
type:
|
|
505
|
+
yield {
|
|
506
|
+
type: EventType.TOOL_CALL_START,
|
|
502
507
|
toolCallId,
|
|
503
508
|
toolCallName: functionCall.name || '',
|
|
504
509
|
toolName: functionCall.name || '',
|
|
505
510
|
model,
|
|
506
|
-
timestamp,
|
|
511
|
+
timestamp: Date.now(),
|
|
507
512
|
index: nextToolIndex - 1,
|
|
508
|
-
}
|
|
513
|
+
}
|
|
509
514
|
|
|
510
515
|
// Emit TOOL_CALL_END with parsed input
|
|
511
516
|
let parsedInput: unknown = {}
|
|
@@ -520,15 +525,15 @@ export class GeminiTextAdapter<
|
|
|
520
525
|
parsedInput = {}
|
|
521
526
|
}
|
|
522
527
|
|
|
523
|
-
yield
|
|
524
|
-
type:
|
|
528
|
+
yield {
|
|
529
|
+
type: EventType.TOOL_CALL_END,
|
|
525
530
|
toolCallId,
|
|
526
531
|
toolCallName: functionCall.name || '',
|
|
527
532
|
toolName: functionCall.name || '',
|
|
528
533
|
model,
|
|
529
|
-
timestamp,
|
|
534
|
+
timestamp: Date.now(),
|
|
530
535
|
input: parsedInput,
|
|
531
|
-
}
|
|
536
|
+
}
|
|
532
537
|
}
|
|
533
538
|
}
|
|
534
539
|
}
|
|
@@ -544,15 +549,15 @@ export class GeminiTextAdapter<
|
|
|
544
549
|
parsedInput = {}
|
|
545
550
|
}
|
|
546
551
|
|
|
547
|
-
yield
|
|
548
|
-
type:
|
|
552
|
+
yield {
|
|
553
|
+
type: EventType.TOOL_CALL_END,
|
|
549
554
|
toolCallId,
|
|
550
555
|
toolCallName: toolCallData.name,
|
|
551
556
|
toolName: toolCallData.name,
|
|
552
557
|
model,
|
|
553
|
-
timestamp,
|
|
558
|
+
timestamp: Date.now(),
|
|
554
559
|
input: parsedInput,
|
|
555
|
-
}
|
|
560
|
+
}
|
|
556
561
|
}
|
|
557
562
|
|
|
558
563
|
// Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls
|
|
@@ -561,11 +566,11 @@ export class GeminiTextAdapter<
|
|
|
561
566
|
}
|
|
562
567
|
|
|
563
568
|
if (finishReason === FinishReason.MAX_TOKENS) {
|
|
564
|
-
yield
|
|
565
|
-
type:
|
|
569
|
+
yield {
|
|
570
|
+
type: EventType.RUN_ERROR,
|
|
566
571
|
runId,
|
|
567
572
|
model,
|
|
568
|
-
timestamp,
|
|
573
|
+
timestamp: Date.now(),
|
|
569
574
|
message:
|
|
570
575
|
'The response was cut off because the maximum token limit was reached.',
|
|
571
576
|
code: 'max_tokens',
|
|
@@ -574,42 +579,42 @@ export class GeminiTextAdapter<
|
|
|
574
579
|
'The response was cut off because the maximum token limit was reached.',
|
|
575
580
|
code: 'max_tokens',
|
|
576
581
|
},
|
|
577
|
-
}
|
|
582
|
+
}
|
|
578
583
|
}
|
|
579
584
|
|
|
580
585
|
// Close reasoning events if still open
|
|
581
586
|
if (reasoningMessageId && !hasClosedReasoning) {
|
|
582
587
|
hasClosedReasoning = true
|
|
583
|
-
yield
|
|
584
|
-
type:
|
|
588
|
+
yield {
|
|
589
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
585
590
|
messageId: reasoningMessageId,
|
|
586
591
|
model,
|
|
587
|
-
timestamp,
|
|
588
|
-
}
|
|
589
|
-
yield
|
|
590
|
-
type:
|
|
592
|
+
timestamp: Date.now(),
|
|
593
|
+
}
|
|
594
|
+
yield {
|
|
595
|
+
type: EventType.REASONING_END,
|
|
591
596
|
messageId: reasoningMessageId,
|
|
592
597
|
model,
|
|
593
|
-
timestamp,
|
|
594
|
-
}
|
|
598
|
+
timestamp: Date.now(),
|
|
599
|
+
}
|
|
595
600
|
}
|
|
596
601
|
|
|
597
602
|
// Emit TEXT_MESSAGE_END if we had text content
|
|
598
603
|
if (hasEmittedTextMessageStart) {
|
|
599
|
-
yield
|
|
600
|
-
type:
|
|
604
|
+
yield {
|
|
605
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
601
606
|
messageId,
|
|
602
607
|
model,
|
|
603
|
-
timestamp,
|
|
604
|
-
}
|
|
608
|
+
timestamp: Date.now(),
|
|
609
|
+
}
|
|
605
610
|
}
|
|
606
611
|
|
|
607
|
-
yield
|
|
608
|
-
type:
|
|
612
|
+
yield {
|
|
613
|
+
type: EventType.RUN_FINISHED,
|
|
609
614
|
runId,
|
|
610
615
|
threadId,
|
|
611
616
|
model,
|
|
612
|
-
timestamp,
|
|
617
|
+
timestamp: Date.now(),
|
|
613
618
|
finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',
|
|
614
619
|
usage: chunk.usageMetadata
|
|
615
620
|
? {
|
|
@@ -618,7 +623,7 @@ export class GeminiTextAdapter<
|
|
|
618
623
|
totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,
|
|
619
624
|
}
|
|
620
625
|
: undefined,
|
|
621
|
-
}
|
|
626
|
+
}
|
|
622
627
|
}
|
|
623
628
|
}
|
|
624
629
|
}
|
|
@@ -707,16 +712,24 @@ export class GeminiTextAdapter<
|
|
|
707
712
|
>
|
|
708
713
|
}
|
|
709
714
|
|
|
710
|
-
const thoughtSignature =
|
|
711
|
-
|
|
712
|
-
|
|
715
|
+
const thoughtSignature = (
|
|
716
|
+
toolCall.metadata as GeminiToolCallMetadata | undefined
|
|
717
|
+
)?.thoughtSignature
|
|
718
|
+
// Gemini requires thoughtSignature at the Part level (sibling of
|
|
719
|
+
// functionCall), not nested inside functionCall. Nesting it causes
|
|
720
|
+
// the API to reject the next turn with
|
|
721
|
+
// "Function call is missing a thought_signature".
|
|
722
|
+
const part: Part = {
|
|
713
723
|
functionCall: {
|
|
714
724
|
id: toolCall.id,
|
|
715
725
|
name: toolCall.function.name,
|
|
716
726
|
args: parsedArgs,
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
727
|
+
},
|
|
728
|
+
}
|
|
729
|
+
if (thoughtSignature) {
|
|
730
|
+
part.thoughtSignature = thoughtSignature
|
|
731
|
+
}
|
|
732
|
+
parts.push(part)
|
|
720
733
|
}
|
|
721
734
|
}
|
|
722
735
|
|
package/src/index.ts
CHANGED
|
@@ -11,15 +11,12 @@ export {
|
|
|
11
11
|
type GeminiTextProviderOptions,
|
|
12
12
|
} from './adapters/text'
|
|
13
13
|
|
|
14
|
-
// Summarize
|
|
14
|
+
// Summarize - thin factory functions over @tanstack/ai's ChatStreamSummarizeAdapter
|
|
15
15
|
export {
|
|
16
|
-
GeminiSummarizeAdapter,
|
|
17
|
-
GeminiSummarizeModels,
|
|
18
16
|
createGeminiSummarize,
|
|
19
17
|
geminiSummarize,
|
|
20
|
-
type
|
|
18
|
+
type GeminiSummarizeConfig,
|
|
21
19
|
type GeminiSummarizeModel,
|
|
22
|
-
type GeminiSummarizeProviderOptions,
|
|
23
20
|
} from './adapters/summarize'
|
|
24
21
|
|
|
25
22
|
// Image adapter
|
package/src/message-types.ts
CHANGED
|
@@ -130,3 +130,17 @@ export interface GeminiMessageMetadataByModality {
|
|
|
130
130
|
video: GeminiVideoMetadata
|
|
131
131
|
document: GeminiDocumentMetadata
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Provider-specific metadata that round-trips with each Gemini tool call.
|
|
136
|
+
*
|
|
137
|
+
* `thoughtSignature` is emitted by Gemini 3.x (and 2.5 thinking) models on
|
|
138
|
+
* the Part containing the `functionCall`. The same signature must be echoed
|
|
139
|
+
* back at the Part level on the next turn or the API rejects the request
|
|
140
|
+
* with `400 INVALID_ARGUMENT: "Function call is missing a thought_signature"`.
|
|
141
|
+
*
|
|
142
|
+
* @see https://ai.google.dev/gemini-api/docs/thinking
|
|
143
|
+
*/
|
|
144
|
+
export interface GeminiToolCallMetadata {
|
|
145
|
+
thoughtSignature?: string
|
|
146
|
+
}
|