@core-ai/google-genai 0.18.0 → 0.20.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
@@ -1,7 +1,23 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
- import { ChatModel, EmbeddingModel, ImageModel, ModelCapabilities } from '@core-ai/core-ai';
2
+ import { ModelCapabilities, ChatModel, EmbeddingModel, ImageModel } from '@core-ai/core-ai';
3
3
  import { z } from 'zod';
4
4
 
5
+ type GoogleModelCapabilities = Omit<ModelCapabilities, 'reasoning'> & {
6
+ reasoning: ModelCapabilities['reasoning'] & {
7
+ thinkingParam: 'thinkingLevel' | 'thinkingBudget';
8
+ };
9
+ };
10
+ declare function getGoogleModelCapabilities(modelId: string): GoogleModelCapabilities;
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
+ */
17
+ type GoogleReasoningMetadata = {
18
+ thoughtSignature?: string;
19
+ };
20
+
5
21
  type GoogleGenAIClient = {
6
22
  models: GoogleGenAI['models'];
7
23
  };
@@ -23,17 +39,6 @@ type GoogleGenAIProvider = {
23
39
  declare function createGoogleGenAIProvider(options?: GoogleGenAIProviderBaseOptions, factoryOptions?: GoogleGenAIProviderFactoryOptions): GoogleGenAIProvider;
24
40
  declare function createGoogleGenAI(options?: GoogleGenAIProviderOptions): GoogleGenAIProvider;
25
41
 
26
- type GoogleReasoningMetadata = {
27
- thoughtSignature?: string;
28
- };
29
-
30
- type GoogleModelCapabilities = {
31
- reasoning: ModelCapabilities['reasoning'] & {
32
- thinkingParam: 'thinkingLevel' | 'thinkingBudget';
33
- };
34
- };
35
- declare function getGoogleModelCapabilities(modelId: string): GoogleModelCapabilities;
36
-
37
42
  declare const googleGenerateProviderOptionsSchema: z.ZodObject<{
38
43
  stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
44
  frequencyPenalty: z.ZodOptional<z.ZodNumber>;
package/dist/index.js CHANGED
@@ -1,20 +1,15 @@
1
1
  // src/provider.ts
2
2
  import { GoogleGenAI } from "@google/genai";
3
3
 
4
- // src/chat-model.ts
5
- import {
6
- StructuredOutputNoObjectGeneratedError,
7
- StructuredOutputParseError,
8
- StructuredOutputValidationError,
9
- createObjectStream,
10
- createChatStream
11
- } from "@core-ai/core-ai";
12
-
13
4
  // src/chat-adapter.ts
14
5
  import {
15
6
  FunctionCallingConfigMode
16
7
  } from "@google/genai";
17
- import { getProviderMetadata, zodSchemaToJsonSchema } from "@core-ai/core-ai";
8
+ import {
9
+ getProviderMetadata,
10
+ validateInputModalities,
11
+ zodSchemaToJsonSchema
12
+ } from "@core-ai/core-ai";
18
13
 
19
14
  // src/model-capabilities.ts
20
15
  import {
@@ -27,6 +22,10 @@ var ALL_EFFORTS = [
27
22
  "high",
28
23
  "max"
29
24
  ];
25
+ var GOOGLE_INPUT_MODALITIES = {
26
+ input: ["text", "image", "file", "audio"],
27
+ output: ["text"]
28
+ };
30
29
  function createCapabilities(config) {
31
30
  return {
32
31
  reasoning: {
@@ -35,7 +34,8 @@ function createCapabilities(config) {
35
34
  restrictsSamplingParams: false,
36
35
  supportedToolChoices: ["auto", "none", "required", "tool"],
37
36
  thinkingParam: config.thinkingParam
38
- }
37
+ },
38
+ modalities: GOOGLE_INPUT_MODALITIES
39
39
  };
40
40
  }
41
41
  var DEFAULT_CAPABILITIES = createCapabilities({
@@ -151,6 +151,7 @@ function parseGoogleImageProviderOptions(providerOptions) {
151
151
  var googleProviderOptionsSchema = googleGenerateProviderOptionsSchema;
152
152
 
153
153
  // src/chat-adapter.ts
154
+ var DEFAULT_PROVIDER_ID = "google";
154
155
  var DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME = "core_ai_generate_object";
155
156
  var DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION = "Return a JSON object that matches the requested schema.";
156
157
  function convertMessages(messages) {
@@ -179,12 +180,17 @@ function convertMessages(messages) {
179
180
  }
180
181
  if (part.type === "tool-call") {
181
182
  toolCallNameById.set(part.toolCall.id, part.toolCall.name);
183
+ const toolCallSignature = getProviderMetadata(
184
+ part.providerMetadata,
185
+ "google"
186
+ )?.thoughtSignature;
182
187
  assistantParts.push({
183
188
  functionCall: {
184
189
  id: part.toolCall.id,
185
190
  name: part.toolCall.name,
186
191
  args: part.toolCall.arguments
187
- }
192
+ },
193
+ ...typeof toolCallSignature === "string" ? { thoughtSignature: toolCallSignature } : {}
188
194
  });
189
195
  continue;
190
196
  }
@@ -254,6 +260,14 @@ function convertUserContentPart(part) {
254
260
  }
255
261
  };
256
262
  }
263
+ if (part.type === "audio") {
264
+ return {
265
+ inlineData: {
266
+ data: part.source.data,
267
+ mimeType: part.source.mediaType
268
+ }
269
+ };
270
+ }
257
271
  return {
258
272
  inlineData: {
259
273
  data: part.data,
@@ -361,17 +375,24 @@ function inferMimeTypeFromUrl(url) {
361
375
  }
362
376
  return "application/octet-stream";
363
377
  }
364
- function createGenerateRequest(modelId, options) {
378
+ function createGenerateRequest(modelId, options, provider = DEFAULT_PROVIDER_ID, adapterOptions = {}) {
365
379
  const googleOptions = parseGoogleGenerateProviderOptions(
366
380
  options.providerOptions
367
381
  );
382
+ const capabilities = adapterOptions.capabilities ?? getGoogleModelCapabilities(modelId);
383
+ validateInputModalities({
384
+ messages: options.messages,
385
+ capabilities,
386
+ modelId,
387
+ providerId: provider
388
+ });
368
389
  const convertedMessages = convertMessages(options.messages);
369
390
  const requestConfig = {
370
391
  ...convertedMessages.systemInstruction ? { systemInstruction: convertedMessages.systemInstruction } : {},
371
392
  ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
372
393
  ...options.toolChoice ? { toolConfig: convertToolChoice(options.toolChoice) } : {},
373
394
  ...mapSamplingToConfig(options),
374
- ...mapReasoningToConfig(modelId, options),
395
+ ...mapReasoningToConfig(options, capabilities),
375
396
  ...mapGoogleProviderOptionsToConfig(googleOptions),
376
397
  ...options.signal ? { abortSignal: options.signal } : {}
377
398
  };
@@ -437,6 +458,7 @@ function mapFinishReason(reason) {
437
458
  }
438
459
  async function* transformStream(stream) {
439
460
  const bufferedToolCalls = /* @__PURE__ */ new Map();
461
+ const toolCallSignatures = /* @__PURE__ */ new Map();
440
462
  let finishReason = "unknown";
441
463
  let sawToolCalls = false;
442
464
  let textOpen = false;
@@ -492,18 +514,21 @@ async function* transformStream(stream) {
492
514
  };
493
515
  }
494
516
  }
495
- if (chunk.text) {
517
+ const textDeltas = extractTextDeltas(chunk);
518
+ if (textDeltas.length > 0) {
496
519
  const reasoningEnd2 = closeReasoning();
497
520
  if (reasoningEnd2) {
498
521
  yield reasoningEnd2;
499
522
  }
500
523
  yield* startText();
501
- yield {
502
- type: "text-delta",
503
- text: chunk.text
504
- };
524
+ for (const text of textDeltas) {
525
+ yield {
526
+ type: "text-delta",
527
+ text
528
+ };
529
+ }
505
530
  }
506
- const functionCalls = chunk.functionCalls ?? [];
531
+ const functionCalls = extractStreamedFunctionCalls(chunk);
507
532
  if (functionCalls.length > 0) {
508
533
  yield* closeText();
509
534
  const reasoningEnd2 = closeReasoning();
@@ -511,8 +536,13 @@ async function* transformStream(stream) {
511
536
  yield reasoningEnd2;
512
537
  }
513
538
  sawToolCalls = true;
514
- for (const [index, functionCall] of functionCalls.entries()) {
515
- 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
+ }
516
546
  const existing = bufferedToolCalls.get(mappedCall.id);
517
547
  if (!existing) {
518
548
  bufferedToolCalls.set(mappedCall.id, mappedCall);
@@ -558,9 +588,11 @@ async function* transformStream(stream) {
558
588
  }
559
589
  yield* closeText();
560
590
  for (const toolCall of bufferedToolCalls.values()) {
591
+ const thoughtSignature = toolCallSignatures.get(toolCall.id);
561
592
  yield {
562
593
  type: "tool-call-end",
563
- toolCall
594
+ toolCall,
595
+ ...thoughtSignature ? { providerMetadata: { google: { thoughtSignature } } } : {}
564
596
  };
565
597
  }
566
598
  if (sawToolCalls && finishReason !== "content-filter") {
@@ -572,11 +604,10 @@ async function* transformStream(stream) {
572
604
  usage
573
605
  };
574
606
  }
575
- function mapReasoningToConfig(modelId, options) {
607
+ function mapReasoningToConfig(options, capabilities) {
576
608
  if (!options.reasoning) {
577
609
  return {};
578
610
  }
579
- const capabilities = getGoogleModelCapabilities(modelId);
580
611
  if (capabilities.reasoning.thinkingParam === "thinkingLevel") {
581
612
  return {
582
613
  thinkingConfig: {
@@ -602,7 +633,7 @@ function extractAssistantParts(response) {
602
633
  if (thoughtText.length === 0) {
603
634
  continue;
604
635
  }
605
- const thoughtSignature = typeof part.thoughtSignature === "string" ? part.thoughtSignature : void 0;
636
+ const thoughtSignature = readThoughtSignature(part);
606
637
  parts.push({
607
638
  type: "reasoning",
608
639
  text: thoughtText,
@@ -619,9 +650,15 @@ function extractAssistantParts(response) {
619
650
  const key = `${toolCall.id}:${toolCall.name}`;
620
651
  if (!seenToolCalls.has(key)) {
621
652
  seenToolCalls.add(key);
653
+ const thoughtSignature = readThoughtSignature(part);
622
654
  parts.push({
623
655
  type: "tool-call",
624
- toolCall
656
+ toolCall,
657
+ ...thoughtSignature ? {
658
+ providerMetadata: {
659
+ google: { thoughtSignature }
660
+ }
661
+ } : {}
625
662
  });
626
663
  }
627
664
  continue;
@@ -645,14 +682,57 @@ function extractAssistantParts(response) {
645
682
  toolCall
646
683
  });
647
684
  }
648
- if (parts.length === 0 && response.text) {
649
- parts.push({
650
- type: "text",
651
- text: response.text
652
- });
685
+ if (parts.length === 0) {
686
+ for (const text of extractTextDeltas(response)) {
687
+ parts.push({
688
+ type: "text",
689
+ text
690
+ });
691
+ }
653
692
  }
654
693
  return parts;
655
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
+ }
656
736
  function extractReasoningDeltas(response) {
657
737
  const candidateParts = response.candidates?.[0]?.content?.parts ?? [];
658
738
  return candidateParts.flatMap((part) => {
@@ -681,6 +761,15 @@ function mapUsage(response, fallback) {
681
761
  };
682
762
  }
683
763
 
764
+ // src/chat-model.ts
765
+ import {
766
+ StructuredOutputNoObjectGeneratedError,
767
+ StructuredOutputParseError,
768
+ StructuredOutputValidationError,
769
+ createObjectStream,
770
+ createChatStream
771
+ } from "@core-ai/core-ai";
772
+
684
773
  // src/google-error.ts
685
774
  import { ApiError } from "@google/genai";
686
775
  import {
@@ -814,6 +903,8 @@ function toNumericHttpCode(body) {
814
903
 
815
904
  // src/chat-model.ts
816
905
  function createGoogleGenAIChatModel(client, modelId, provider = "google") {
906
+ const capabilities = getGoogleModelCapabilities(modelId);
907
+ const adapterOptions = { capabilities };
817
908
  async function callGenerateContentApi(request) {
818
909
  try {
819
910
  return await client.models.generateContent(request);
@@ -829,12 +920,22 @@ function createGoogleGenAIChatModel(client, modelId, provider = "google") {
829
920
  }
830
921
  }
831
922
  async function generateChat(options) {
832
- const request = createGenerateRequest(modelId, options);
923
+ const request = createGenerateRequest(
924
+ modelId,
925
+ options,
926
+ provider,
927
+ adapterOptions
928
+ );
833
929
  const response = await callGenerateContentApi(request);
834
930
  return mapGenerateResponse(response);
835
931
  }
836
932
  async function streamChat(options) {
837
- const request = createGenerateRequest(modelId, options);
933
+ const request = createGenerateRequest(
934
+ modelId,
935
+ options,
936
+ provider,
937
+ adapterOptions
938
+ );
838
939
  return createChatStream(
839
940
  async () => transformStream(await callGenerateContentStreamApi(request)),
840
941
  { signal: options.signal }
@@ -843,7 +944,7 @@ function createGoogleGenAIChatModel(client, modelId, provider = "google") {
843
944
  return {
844
945
  provider,
845
946
  modelId,
846
- capabilities: getGoogleModelCapabilities(modelId),
947
+ capabilities,
847
948
  generate: generateChat,
848
949
  stream: streamChat,
849
950
  async generateObject(options) {
@@ -1247,7 +1348,6 @@ function greatestCommonDivisor(a, b) {
1247
1348
  }
1248
1349
 
1249
1350
  // src/provider.ts
1250
- var DEFAULT_PROVIDER_ID = "google";
1251
1351
  function createGoogleGenAIProvider(options = {}, factoryOptions = {}) {
1252
1352
  const client = options.client ?? new GoogleGenAI({
1253
1353
  apiKey: options.apiKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/google-genai",
3
- "version": "0.18.0",
3
+ "version": "0.20.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.18.0",
48
+ "@core-ai/core-ai": "^0.20.0",
49
49
  "@google/genai": "^1.42.0"
50
50
  },
51
51
  "peerDependencies": {