@core-ai/google-genai 0.19.0 → 0.21.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/index.d.ts CHANGED
@@ -9,6 +9,11 @@ type GoogleModelCapabilities = Omit<ModelCapabilities, 'reasoning'> & {
9
9
  };
10
10
  declare function getGoogleModelCapabilities(modelId: string): GoogleModelCapabilities;
11
11
 
12
+ /**
13
+ * Thought signature Google attaches under the `google` provider metadata key.
14
+ * Used on reasoning parts today, and on tool-call parts so Gemini 3 function
15
+ * calls can be replayed without a missing-`thought_signature` 400.
16
+ */
12
17
  type GoogleReasoningMetadata = {
13
18
  thoughtSignature?: string;
14
19
  };
package/dist/index.js CHANGED
@@ -180,12 +180,17 @@ function convertMessages(messages) {
180
180
  }
181
181
  if (part.type === "tool-call") {
182
182
  toolCallNameById.set(part.toolCall.id, part.toolCall.name);
183
+ const toolCallSignature = getProviderMetadata(
184
+ part.providerMetadata,
185
+ "google"
186
+ )?.thoughtSignature;
183
187
  assistantParts.push({
184
188
  functionCall: {
185
189
  id: part.toolCall.id,
186
190
  name: part.toolCall.name,
187
191
  args: part.toolCall.arguments
188
- }
192
+ },
193
+ ...typeof toolCallSignature === "string" ? { thoughtSignature: toolCallSignature } : {}
189
194
  });
190
195
  continue;
191
196
  }
@@ -453,6 +458,7 @@ function mapFinishReason(reason) {
453
458
  }
454
459
  async function* transformStream(stream) {
455
460
  const bufferedToolCalls = /* @__PURE__ */ new Map();
461
+ const toolCallSignatures = /* @__PURE__ */ new Map();
456
462
  let finishReason = "unknown";
457
463
  let sawToolCalls = false;
458
464
  let textOpen = false;
@@ -508,18 +514,21 @@ async function* transformStream(stream) {
508
514
  };
509
515
  }
510
516
  }
511
- if (chunk.text) {
517
+ const textDeltas = extractTextDeltas(chunk);
518
+ if (textDeltas.length > 0) {
512
519
  const reasoningEnd2 = closeReasoning();
513
520
  if (reasoningEnd2) {
514
521
  yield reasoningEnd2;
515
522
  }
516
523
  yield* startText();
517
- yield {
518
- type: "text-delta",
519
- text: chunk.text
520
- };
524
+ for (const text of textDeltas) {
525
+ yield {
526
+ type: "text-delta",
527
+ text
528
+ };
529
+ }
521
530
  }
522
- const functionCalls = chunk.functionCalls ?? [];
531
+ const functionCalls = extractStreamedFunctionCalls(chunk);
523
532
  if (functionCalls.length > 0) {
524
533
  yield* closeText();
525
534
  const reasoningEnd2 = closeReasoning();
@@ -527,8 +536,13 @@ async function* transformStream(stream) {
527
536
  yield reasoningEnd2;
528
537
  }
529
538
  sawToolCalls = true;
530
- for (const [index, functionCall] of functionCalls.entries()) {
531
- const mappedCall = mapFunctionCall(functionCall, index);
539
+ for (const {
540
+ toolCall: mappedCall,
541
+ thoughtSignature
542
+ } of functionCalls) {
543
+ if (thoughtSignature) {
544
+ toolCallSignatures.set(mappedCall.id, thoughtSignature);
545
+ }
532
546
  const existing = bufferedToolCalls.get(mappedCall.id);
533
547
  if (!existing) {
534
548
  bufferedToolCalls.set(mappedCall.id, mappedCall);
@@ -574,9 +588,11 @@ async function* transformStream(stream) {
574
588
  }
575
589
  yield* closeText();
576
590
  for (const toolCall of bufferedToolCalls.values()) {
591
+ const thoughtSignature = toolCallSignatures.get(toolCall.id);
577
592
  yield {
578
593
  type: "tool-call-end",
579
- toolCall
594
+ toolCall,
595
+ ...thoughtSignature ? { providerMetadata: { google: { thoughtSignature } } } : {}
580
596
  };
581
597
  }
582
598
  if (sawToolCalls && finishReason !== "content-filter") {
@@ -617,7 +633,7 @@ function extractAssistantParts(response) {
617
633
  if (thoughtText.length === 0) {
618
634
  continue;
619
635
  }
620
- const thoughtSignature = typeof part.thoughtSignature === "string" ? part.thoughtSignature : void 0;
636
+ const thoughtSignature = readThoughtSignature(part);
621
637
  parts.push({
622
638
  type: "reasoning",
623
639
  text: thoughtText,
@@ -634,9 +650,15 @@ function extractAssistantParts(response) {
634
650
  const key = `${toolCall.id}:${toolCall.name}`;
635
651
  if (!seenToolCalls.has(key)) {
636
652
  seenToolCalls.add(key);
653
+ const thoughtSignature = readThoughtSignature(part);
637
654
  parts.push({
638
655
  type: "tool-call",
639
- toolCall
656
+ toolCall,
657
+ ...thoughtSignature ? {
658
+ providerMetadata: {
659
+ google: { thoughtSignature }
660
+ }
661
+ } : {}
640
662
  });
641
663
  }
642
664
  continue;
@@ -660,14 +682,57 @@ function extractAssistantParts(response) {
660
682
  toolCall
661
683
  });
662
684
  }
663
- if (parts.length === 0 && response.text) {
664
- parts.push({
665
- type: "text",
666
- text: response.text
667
- });
685
+ if (parts.length === 0) {
686
+ for (const text of extractTextDeltas(response)) {
687
+ parts.push({
688
+ type: "text",
689
+ text
690
+ });
691
+ }
668
692
  }
669
693
  return parts;
670
694
  }
695
+ function extractTextDeltas(response) {
696
+ const candidateParts = response.candidates?.[0]?.content?.parts ?? [];
697
+ if (candidateParts.length > 0) {
698
+ return candidateParts.flatMap((part) => {
699
+ if (part.thought || part.functionCall) {
700
+ return [];
701
+ }
702
+ if (typeof part.text === "string" && part.text.length > 0) {
703
+ return [part.text];
704
+ }
705
+ return [];
706
+ });
707
+ }
708
+ return typeof response.text === "string" && response.text.length > 0 ? [response.text] : [];
709
+ }
710
+ function extractStreamedFunctionCalls(chunk) {
711
+ const candidateParts = chunk.candidates?.[0]?.content?.parts ?? [];
712
+ const fromCandidateParts = [];
713
+ for (const part of candidateParts) {
714
+ if (!part.functionCall) {
715
+ continue;
716
+ }
717
+ const thoughtSignature = readThoughtSignature(part);
718
+ fromCandidateParts.push({
719
+ toolCall: mapFunctionCall(
720
+ part.functionCall,
721
+ fromCandidateParts.length
722
+ ),
723
+ ...thoughtSignature ? { thoughtSignature } : {}
724
+ });
725
+ }
726
+ if (fromCandidateParts.length > 0) {
727
+ return fromCandidateParts;
728
+ }
729
+ return (chunk.functionCalls ?? []).map((functionCall, index) => ({
730
+ toolCall: mapFunctionCall(functionCall, index)
731
+ }));
732
+ }
733
+ function readThoughtSignature(part) {
734
+ return typeof part.thoughtSignature === "string" && part.thoughtSignature.length > 0 ? part.thoughtSignature : void 0;
735
+ }
671
736
  function extractReasoningDeltas(response) {
672
737
  const candidateParts = response.candidates?.[0]?.content?.parts ?? [];
673
738
  return candidateParts.flatMap((part) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/google-genai",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Google GenAI provider package for @core-ai/core-ai",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -45,7 +45,7 @@
45
45
  "test:watch": "vitest"
46
46
  },
47
47
  "dependencies": {
48
- "@core-ai/core-ai": "^0.19.0",
48
+ "@core-ai/core-ai": "^0.21.0",
49
49
  "@google/genai": "^1.42.0"
50
50
  },
51
51
  "peerDependencies": {