@anvia/gemini 1.0.0-rc.6 → 1.0.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.js CHANGED
@@ -8,6 +8,9 @@ var __export = (target, all) => {
8
8
  var gemini_exports = {};
9
9
  __export(gemini_exports, {
10
10
  GEMINI_2_5_FLASH_IMAGE: () => GEMINI_2_5_FLASH_IMAGE,
11
+ GEMINI_3_1_FLASH_IMAGE: () => GEMINI_3_1_FLASH_IMAGE,
12
+ GEMINI_3_1_FLASH_LITE_IMAGE: () => GEMINI_3_1_FLASH_LITE_IMAGE,
13
+ GEMINI_3_PRO_IMAGE: () => GEMINI_3_PRO_IMAGE,
11
14
  GEMINI_3_PRO_IMAGE_PREVIEW: () => GEMINI_3_PRO_IMAGE_PREVIEW,
12
15
  GeminiClient: () => GeminiClient,
13
16
  IMAGEN_4_GENERATE: () => IMAGEN_4_GENERATE
@@ -25,6 +28,8 @@ import { GoogleGenAI } from "@google/genai";
25
28
  // src/gemini/completion.ts
26
29
  import {
27
30
  assertCompletionRequestSupported,
31
+ CompletionProviderOutputError,
32
+ isJsonValue,
28
33
  Usage,
29
34
  withContextUsage
30
35
  } from "@anvia/core/completion";
@@ -102,21 +107,37 @@ var GeminiCompletionModel = class {
102
107
  const params = toGeminiGenerateContentParams(this.modelId, request);
103
108
  applyAbortSignal(params, options);
104
109
  const stream = await this.client.models.generateContentStream(params);
110
+ const streamState = new GeminiCompletionStreamState();
105
111
  for await (const chunk of stream) {
106
- for (const event of fromGeminiGenerateContentStreamChunk(chunk)) {
107
- yield event.type === "final" ? {
108
- ...event,
109
- response: withContextUsage(event.response, this.modelInfo())
110
- } : event;
112
+ const mapping = mapGeminiGenerateContentStreamChunk(chunk);
113
+ streamState.accept(chunk, mapping);
114
+ for (const event of mapping.events) {
115
+ if (event.type !== "final") {
116
+ yield event;
117
+ }
111
118
  }
112
119
  }
120
+ streamState.assertComplete();
121
+ const finalEvent = streamState.finalEvent();
122
+ if (finalEvent !== void 0) {
123
+ yield {
124
+ ...finalEvent,
125
+ response: withContextUsage(finalEvent.response, this.modelInfo())
126
+ };
127
+ }
113
128
  }
114
129
  };
115
130
  function toGeminiGenerateContentParams(modelId, request) {
116
131
  const messages = requestMessages(request);
117
- const providerOptions = isPlainObject2(request.providerOptions) ? request.providerOptions : {};
132
+ if (request.providerOptions !== void 0 && (!isPlainObject2(request.providerOptions) || !isJsonValue(request.providerOptions))) {
133
+ throw new TypeError("Gemini providerOptions must be a JSON object.");
134
+ }
135
+ const providerOptions = request.providerOptions ?? {};
118
136
  const { config: providerConfigValue, ...providerTopLevel } = providerOptions;
119
- const providerConfig = isPlainObject2(providerConfigValue) ? { ...providerConfigValue } : {};
137
+ if (providerConfigValue !== void 0 && !isPlainObject2(providerConfigValue)) {
138
+ throw new TypeError("Gemini providerOptions.config must be a JSON object.");
139
+ }
140
+ const providerConfig = providerConfigValue === void 0 ? {} : { ...providerConfigValue };
120
141
  delete providerConfig.tools;
121
142
  const config = disableGeminiNativeRetries({
122
143
  ...providerConfig,
@@ -164,10 +185,10 @@ function toolChoiceSummary(toolChoice) {
164
185
  function compactJsonObject(values) {
165
186
  return Object.fromEntries(
166
187
  Object.entries(values).flatMap(([key, value]) => {
167
- if (value === void 0) {
188
+ if (!isJsonValue(value)) {
168
189
  return [];
169
190
  }
170
- return [[key, toJsonValue(value)]];
191
+ return [[key, value]];
171
192
  })
172
193
  );
173
194
  }
@@ -416,11 +437,20 @@ function toolChoiceToGemini(toolChoice) {
416
437
  };
417
438
  }
418
439
  function fromGeminiGenerateContentResponse(response) {
419
- const raw = response;
420
- const choice = assistantContentFromGeminiResponse(raw);
440
+ const raw = isPlainObject2(response) ? response : {};
441
+ const usage = usageFromGemini(raw.usageMetadata);
442
+ const parts = candidateParts(raw, usage);
443
+ const providerFinishReason = providerFinishReasonFromGeminiResponse(raw);
444
+ const finishError = geminiToolFinishError(
445
+ providerFinishReason,
446
+ hasGeminiFunctionCallMarker(raw, parts),
447
+ usage
448
+ );
449
+ if (finishError !== void 0) throw finishError;
450
+ const choice = assistantContentFromGeminiResponse(raw, usage);
421
451
  const result = {
422
452
  choice,
423
- usage: usageFromGemini(raw.usageMetadata),
453
+ usage,
424
454
  rawResponse: response
425
455
  };
426
456
  applyGeminiFinishReason(result, raw);
@@ -428,34 +458,52 @@ function fromGeminiGenerateContentResponse(response) {
428
458
  if (id !== void 0) {
429
459
  result.messageId = id;
430
460
  }
461
+ assertSafeGeminiCompletionResponse(result);
431
462
  return result;
432
463
  }
433
- function applyGeminiFinishReason(response, raw) {
434
- const value = stringFrom(primaryGeminiCandidate(raw)?.finishReason);
464
+ function applyGeminiFinishReason(response, raw, hasStreamedToolCalls = false) {
465
+ const value = providerFinishReasonFromGeminiResponse(raw);
435
466
  if (value === void 0) return;
436
- response.finishReason = geminiFinishReason(value, response.choice);
467
+ applyGeminiFinishReasonValue(response, value, hasStreamedToolCalls);
468
+ }
469
+ function applyGeminiFinishReasonValue(response, value, hasStreamedToolCalls) {
470
+ response.finishReason = geminiFinishReason(
471
+ value,
472
+ hasStreamedToolCalls || response.choice.some((part) => part.type === "tool-call")
473
+ );
437
474
  response.providerFinishReason = value;
438
475
  }
439
- function geminiFinishReason(value, choice) {
476
+ function geminiFinishReason(value, hasToolCalls) {
440
477
  if (value === "MAX_TOKENS") return "length";
441
478
  if (value === "SAFETY" || value === "RECITATION" || value === "BLOCKLIST" || value === "PROHIBITED_CONTENT" || value === "SPII" || value === "IMAGE_SAFETY" || value === "IMAGE_PROHIBITED_CONTENT") {
442
479
  return "content-filter";
443
480
  }
444
481
  if (value === "STOP") {
445
- return choice.some((part) => part.type === "tool-call") ? "tool-calls" : "stop";
482
+ return hasToolCalls ? "tool-calls" : "stop";
446
483
  }
447
484
  return "other";
448
485
  }
449
- function fromGeminiGenerateContentStreamChunk(chunk) {
486
+ function mapGeminiGenerateContentStreamChunk(chunk) {
450
487
  if (!isPlainObject2(chunk)) {
451
- return [];
488
+ return {
489
+ events: [],
490
+ toolCalls: [],
491
+ hasToolCallMarker: false,
492
+ hasSyntheticToolCalls: false
493
+ };
452
494
  }
453
495
  const events = [];
454
- const directText = typeof chunk.text === "string" ? chunk.text : "";
455
- if (directText.length > 0 && candidateParts(chunk).length === 0) {
496
+ const usage = usageFromGemini(chunk.usageMetadata);
497
+ const parts = candidateParts(chunk, usage);
498
+ const providerFinishReason = providerFinishReasonFromGeminiResponse(chunk);
499
+ const hasToolCallMarker = hasGeminiFunctionCallMarker(chunk, parts);
500
+ const terminalError = providerFinishReason === void 0 ? void 0 : geminiToolFinishError(providerFinishReason, hasToolCallMarker, usage);
501
+ const calls = terminalError === void 0 ? functionCallsFromGeminiResponse(chunk, parts, usage) : [];
502
+ const directText = textFromGeminiResponse(chunk, parts);
503
+ if (terminalError === void 0 && directText.length > 0 && parts.length === 0) {
456
504
  events.push({ type: "text_delta", delta: directText });
457
505
  }
458
- for (const part of candidateParts(chunk)) {
506
+ for (const { part } of terminalError === void 0 ? parts : []) {
459
507
  if (typeof part.text === "string" && part.text.length > 0) {
460
508
  if (part.thought === true) {
461
509
  events.push({ type: "reasoning_delta", delta: part.text, contentType: "summary" });
@@ -464,18 +512,22 @@ function fromGeminiGenerateContentStreamChunk(chunk) {
464
512
  }
465
513
  }
466
514
  }
467
- for (const call of functionCallsFromGeminiResponse(chunk)) {
515
+ for (const call of calls) {
468
516
  events.push(
469
- toolCallDelta(call.id ?? call.name, {
470
- callId: call.id,
517
+ toolCallDelta(call.toolCallId, {
518
+ callId: call.callId,
471
519
  name: call.name,
472
520
  signature: call.signature
473
521
  })
474
522
  );
523
+ const argumentsDelta = JSON.stringify(call.args);
524
+ if (argumentsDelta === void 0) {
525
+ throw invalidGeminiToolCallArguments(call.toolCallId, usage);
526
+ }
475
527
  events.push(
476
- toolCallDelta(call.id ?? call.name, {
477
- callId: call.id,
478
- argumentsDelta: JSON.stringify(call.args ?? {}),
528
+ toolCallDelta(call.toolCallId, {
529
+ callId: call.callId,
530
+ argumentsDelta,
479
531
  argumentsMode: "replace"
480
532
  })
481
533
  );
@@ -485,18 +537,171 @@ function fromGeminiGenerateContentStreamChunk(chunk) {
485
537
  events.push({ type: "message_id", id });
486
538
  }
487
539
  if (isPlainObject2(chunk.usageMetadata)) {
488
- events.push({ type: "final", response: fromGeminiGenerateContentResponse(chunk) });
540
+ if (terminalError === void 0) {
541
+ events.push({ type: "final", response: fromGeminiGenerateContentResponse(chunk) });
542
+ } else {
543
+ const response = { choice: [], usage, rawResponse: chunk };
544
+ if (providerFinishReason !== void 0) {
545
+ applyGeminiFinishReasonValue(response, providerFinishReason, true);
546
+ }
547
+ events.push({ type: "final", response });
548
+ }
549
+ }
550
+ return providerFinishReason === void 0 ? {
551
+ events,
552
+ toolCalls: calls.map(toolCallFromGeminiFunctionCall),
553
+ hasToolCallMarker,
554
+ hasSyntheticToolCalls: calls.some((call) => call.callId === void 0),
555
+ terminalError
556
+ } : {
557
+ events,
558
+ toolCalls: calls.map(toolCallFromGeminiFunctionCall),
559
+ hasToolCallMarker,
560
+ hasSyntheticToolCalls: calls.some((call) => call.callId === void 0),
561
+ providerFinishReason,
562
+ terminalError
563
+ };
564
+ }
565
+ var GeminiCompletionStreamState = class {
566
+ toolCalls = /* @__PURE__ */ new Map();
567
+ providerFinishReason;
568
+ terminalChunk;
569
+ finalResponse;
570
+ sawToolCallMarker = false;
571
+ sawSyntheticToolCallChunk = false;
572
+ terminalError;
573
+ accept(chunk, mapping) {
574
+ if (mapping.hasSyntheticToolCalls && this.sawSyntheticToolCallChunk) {
575
+ const toolCallId = mapping.toolCalls.find(
576
+ (toolCall) => toolCall.callId === void 0
577
+ )?.toolCallId;
578
+ throw toolCallId === void 0 ? new CompletionProviderOutputError({
579
+ kind: "invalid-tool-call",
580
+ usage: this.currentUsage()
581
+ }) : invalidGeminiToolCall(toolCallId, this.currentUsage());
582
+ }
583
+ if (this.providerFinishReason !== void 0 && (mapping.hasToolCallMarker || hasGeminiSemanticProgress(mapping.events))) {
584
+ throw new CompletionProviderOutputError({
585
+ kind: "invalid-tool-call",
586
+ finishReason: "other",
587
+ usage: this.currentUsage()
588
+ });
589
+ }
590
+ for (const toolCall of mapping.toolCalls) {
591
+ const existing = this.toolCalls.get(toolCall.toolCallId);
592
+ if (existing !== void 0) {
593
+ if (existing.callId === void 0 || toolCall.callId === void 0 || !sameGeminiToolCall(existing, toolCall)) {
594
+ throw invalidGeminiToolCall(toolCall.toolCallId, this.currentUsage());
595
+ }
596
+ continue;
597
+ }
598
+ this.toolCalls.set(toolCall.toolCallId, toolCall);
599
+ }
600
+ this.sawToolCallMarker ||= mapping.hasToolCallMarker;
601
+ this.sawSyntheticToolCallChunk ||= mapping.hasSyntheticToolCalls;
602
+ for (const event of mapping.events) {
603
+ if (event.type === "final") {
604
+ this.finalResponse = event.response;
605
+ }
606
+ }
607
+ this.terminalError ??= mapping.terminalError;
608
+ if (mapping.providerFinishReason !== void 0) {
609
+ if (this.providerFinishReason !== void 0 && this.providerFinishReason !== mapping.providerFinishReason) {
610
+ throw new CompletionProviderOutputError({
611
+ kind: "invalid-tool-call",
612
+ finishReason: "other",
613
+ usage: this.currentUsage()
614
+ });
615
+ }
616
+ this.providerFinishReason = mapping.providerFinishReason;
617
+ this.terminalChunk = chunk;
618
+ }
619
+ }
620
+ assertComplete() {
621
+ if (this.terminalError !== void 0) {
622
+ throw geminiProviderOutputErrorWithUsage(this.terminalError, this.currentUsage());
623
+ }
624
+ if (!this.sawToolCallMarker && this.toolCalls.size === 0) return;
625
+ if (this.providerFinishReason !== void 0) {
626
+ assertSafeGeminiToolFinishReason(this.providerFinishReason, this.currentUsage());
627
+ return;
628
+ }
629
+ const toolCallId = this.toolCalls.keys().next().value;
630
+ throw new CompletionProviderOutputError(
631
+ toolCallId === void 0 ? { kind: "incomplete-tool-call", usage: this.currentUsage() } : { kind: "incomplete-tool-call", toolCallId, usage: this.currentUsage() }
632
+ );
489
633
  }
490
- return events;
634
+ finalEvent() {
635
+ let response = this.finalResponse;
636
+ if (response === void 0) {
637
+ if (this.terminalChunk === void 0) {
638
+ return void 0;
639
+ }
640
+ const raw = isPlainObject2(this.terminalChunk) ? this.terminalChunk : {};
641
+ response = {
642
+ choice: [],
643
+ usage: usageFromGemini(raw.usageMetadata),
644
+ rawResponse: this.terminalChunk
645
+ };
646
+ const messageId = stringFrom(raw.responseId) ?? stringFrom(raw.id);
647
+ if (messageId !== void 0) response.messageId = messageId;
648
+ }
649
+ response = mergeGeminiStreamToolCalls(response, [...this.toolCalls.values()]);
650
+ if (this.providerFinishReason !== void 0) {
651
+ applyGeminiFinishReasonValue(response, this.providerFinishReason, this.toolCalls.size > 0);
652
+ }
653
+ assertSafeGeminiCompletionResponse(response);
654
+ return { type: "final", response };
655
+ }
656
+ currentUsage() {
657
+ if (this.finalResponse !== void 0) {
658
+ return this.finalResponse.usage;
659
+ }
660
+ const raw = isPlainObject2(this.terminalChunk) ? this.terminalChunk : {};
661
+ return usageFromGemini(raw.usageMetadata);
662
+ }
663
+ };
664
+ function hasGeminiSemanticProgress(events) {
665
+ return events.some(
666
+ (event) => event.type === "text_delta" || event.type === "reasoning_delta" || event.type === "tool_call_delta" || event.type === "tool_call" || event.type === "provider_tool_call"
667
+ );
668
+ }
669
+ function sameGeminiToolCall(left, right) {
670
+ return left.toolName === right.toolName && left.callId === right.callId && left.signature === right.signature && sameJsonValue(left.input, right.input);
491
671
  }
492
- function assistantContentFromGeminiResponse(response) {
493
- const parts = candidateParts(response);
672
+ function sameJsonValue(left, right) {
673
+ if (Object.is(left, right)) return true;
674
+ if (isJsonArray(left) || isJsonArray(right)) {
675
+ return isJsonArray(left) && isJsonArray(right) && left.length === right.length && left.every((value, index) => {
676
+ const rightValue = right[index];
677
+ return rightValue !== void 0 && sameJsonValue(value, rightValue);
678
+ });
679
+ }
680
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") {
681
+ return false;
682
+ }
683
+ const leftKeys = Object.keys(left);
684
+ const rightKeys = Object.keys(right);
685
+ return leftKeys.length === rightKeys.length && leftKeys.every(
686
+ (key) => Object.hasOwn(right, key) && sameJsonValue(left[key], right[key])
687
+ );
688
+ }
689
+ function isJsonArray(value) {
690
+ return Array.isArray(value);
691
+ }
692
+ function assistantContentFromGeminiResponse(response, usage) {
693
+ const parts = candidateParts(response, usage);
694
+ const calls = functionCallsFromGeminiResponse(response, parts, usage);
494
695
  if (parts.length === 0) {
495
- const text = textFromGeminiResponse(response);
496
- return text.length > 0 ? [{ type: "text", text }] : [];
696
+ const choice2 = [];
697
+ const text = textFromGeminiResponse(response, parts);
698
+ if (text.length > 0) choice2.push({ type: "text", text });
699
+ choice2.push(...calls.map(toolCallFromGeminiFunctionCall));
700
+ return choice2;
497
701
  }
702
+ const callsByPartIndex = new Map(calls.map((call) => [call.partIndex, call]));
498
703
  const choice = [];
499
- for (const part of parts) {
704
+ for (const { index, part } of parts) {
500
705
  if (typeof part.text === "string" && part.text.length > 0) {
501
706
  if (part.thought === true) {
502
707
  choice.push({
@@ -514,89 +719,336 @@ function assistantContentFromGeminiResponse(response) {
514
719
  choice.push(text);
515
720
  }
516
721
  }
517
- if (isPlainObject2(part.functionCall)) {
518
- const call = functionCallFromGeminiPart(part.functionCall, part);
519
- if (call !== void 0) {
520
- const toolCallId = call.id ?? crypto.randomUUID();
521
- let toolCall = {
522
- type: "tool-call",
523
- toolCallId,
524
- toolName: call.name,
525
- input: call.args
526
- };
527
- if (call.id !== void 0) toolCall = { ...toolCall, callId: call.id };
528
- if (call.signature !== void 0) toolCall = { ...toolCall, signature: call.signature };
529
- choice.push(toolCall);
530
- }
722
+ const call = callsByPartIndex.get(index);
723
+ if (call !== void 0) {
724
+ choice.push(toolCallFromGeminiFunctionCall(call));
531
725
  }
532
726
  }
533
727
  return choice;
534
728
  }
535
- function textFromGeminiResponse(response) {
536
- if (typeof response.text === "string") {
537
- return response.text;
729
+ function textFromGeminiResponse(response, parts = candidateParts(response)) {
730
+ if (parts.length > 0) {
731
+ return parts.flatMap(
732
+ ({ part }) => part.thought !== true && typeof part.text === "string" ? [part.text] : []
733
+ ).join("");
538
734
  }
539
- return candidateParts(response).flatMap((part) => part.thought !== true && typeof part.text === "string" ? [part.text] : []).join("");
735
+ if (hasGeminiCandidatePayload(response)) {
736
+ return "";
737
+ }
738
+ const directText = ownDataProperty(response, "text");
739
+ return typeof directText === "string" ? directText : "";
540
740
  }
541
- function functionCallsFromGeminiResponse(response) {
542
- const directCalls = Array.isArray(response.functionCalls) ? response.functionCalls : Array.isArray(response.function_calls) ? response.function_calls : [];
543
- const partCalls = candidateParts(response).flatMap((part) => {
544
- if (!isPlainObject2(part.functionCall)) {
545
- return [];
546
- }
547
- const call = functionCallFromGeminiPart(part.functionCall, part);
548
- return call === void 0 ? [] : [call];
549
- });
550
- const normalizedDirectCalls = directCalls.flatMap((call) => {
551
- if (!isPlainObject2(call) || typeof call.name !== "string") {
552
- return [];
553
- }
554
- const normalized = {
555
- name: call.name,
556
- args: toJsonValue(call.args ?? {})
557
- };
558
- const id = stringFrom(call.id);
559
- const signature = thoughtSignatureFrom(call);
560
- if (id !== void 0) {
561
- normalized.id = id;
562
- }
563
- if (signature !== void 0) {
564
- normalized.signature = signature;
741
+ function functionCallsFromGeminiResponse(response, parts, usage) {
742
+ if (parts.length > 0) {
743
+ const calls2 = parts.flatMap(({ index, part }) => {
744
+ if (part.functionCall === void 0) {
745
+ return [];
746
+ }
747
+ if (!isPlainObject2(part.functionCall)) {
748
+ throw invalidGeminiToolCall(deterministicGeminiToolCallId(index), usage);
749
+ }
750
+ return [functionCallFromGeminiPart(part.functionCall, part, index, usage)];
751
+ });
752
+ assertDistinctGeminiFunctionCalls(calls2, usage);
753
+ return calls2;
754
+ }
755
+ if (hasGeminiCandidatePayload(response)) {
756
+ return [];
757
+ }
758
+ const camelCaseCalls = ownDataProperty(response, "functionCalls");
759
+ const snakeCaseCalls = ownDataProperty(response, "function_calls");
760
+ const directCalls = Array.isArray(camelCaseCalls) ? camelCaseCalls : Array.isArray(snakeCaseCalls) ? snakeCaseCalls : [];
761
+ const calls = directCalls.map((call, index) => {
762
+ if (!isPlainObject2(call)) {
763
+ throw invalidGeminiToolCall(deterministicGeminiToolCallId(index), usage);
565
764
  }
566
- return [normalized];
765
+ return functionCallFromGeminiPart(call, call, index, usage);
567
766
  });
568
- return [...normalizedDirectCalls, ...partCalls];
569
- }
570
- function functionCallFromGeminiPart(call, part) {
571
- if (typeof call.name !== "string") {
572
- return void 0;
767
+ assertDistinctGeminiFunctionCalls(calls, usage);
768
+ return calls;
769
+ }
770
+ function functionCallFromGeminiPart(call, part, partIndex, usage) {
771
+ const fallbackId = deterministicGeminiToolCallId(partIndex);
772
+ const rawCallId = call.id;
773
+ const callId = isNonblankString(rawCallId) ? rawCallId : void 0;
774
+ const toolCallId = callId ?? fallbackId;
775
+ if (rawCallId !== void 0 && callId === void 0) {
776
+ throw invalidGeminiToolCall(toolCallId, usage);
777
+ }
778
+ if (call.partialArgs !== void 0 || call.partial_args !== void 0 || call.willContinue !== void 0 || call.will_continue !== void 0) {
779
+ throw new CompletionProviderOutputError({
780
+ kind: "incomplete-tool-call",
781
+ toolCallId,
782
+ usage
783
+ });
784
+ }
785
+ const name = call.name;
786
+ if (!isNonblankString(name)) {
787
+ throw invalidGeminiToolCall(toolCallId, usage);
788
+ }
789
+ const args = call.args;
790
+ if (!isPlainObject2(args) || !isJsonValue(args)) {
791
+ throw invalidGeminiToolCallArguments(toolCallId, usage);
573
792
  }
574
793
  const normalized = {
575
- name: call.name,
576
- args: toJsonValue(call.args ?? {})
794
+ toolCallId,
795
+ name,
796
+ args,
797
+ partIndex
577
798
  };
578
- const id = stringFrom(call.id);
579
799
  const signature = thoughtSignatureFrom(part) ?? thoughtSignatureFrom(call);
580
- if (id !== void 0) {
581
- normalized.id = id;
800
+ if (callId !== void 0) {
801
+ return signature === void 0 ? { ...normalized, callId } : { ...normalized, callId, signature };
802
+ }
803
+ return signature === void 0 ? normalized : { ...normalized, signature };
804
+ }
805
+ function candidateParts(response, usage) {
806
+ const candidate = primaryGeminiCandidate(response);
807
+ if (candidate === void 0 || candidate.content === void 0) {
808
+ return [];
809
+ }
810
+ if (!isPlainObject2(candidate.content)) {
811
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
582
812
  }
583
- if (signature !== void 0) {
584
- normalized.signature = signature;
813
+ if (candidate.content.parts === void 0) return [];
814
+ if (!Array.isArray(candidate.content.parts)) {
815
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
585
816
  }
586
- return normalized;
817
+ if (candidate.content.parts.some((part) => !isPlainObject2(part))) {
818
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
819
+ }
820
+ return candidate.content.parts.map((part, index) => ({
821
+ index,
822
+ part
823
+ }));
587
824
  }
588
- function candidateParts(response) {
589
- const candidates = Array.isArray(response.candidates) ? response.candidates : [];
590
- return candidates.flatMap((candidate) => {
591
- if (!isPlainObject2(candidate) || !isPlainObject2(candidate.content)) {
592
- return [];
825
+ function primaryGeminiCandidate(response) {
826
+ if (response.candidates === void 0) return void 0;
827
+ if (!Array.isArray(response.candidates)) {
828
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
829
+ }
830
+ const candidates = response.candidates;
831
+ if (candidates.some(
832
+ (candidate2) => !isPlainObject2(candidate2) || candidate2.index !== void 0 && (!Number.isSafeInteger(candidate2.index) || candidate2.index < 0)
833
+ )) {
834
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
835
+ }
836
+ const primaryCandidates = candidates.filter(
837
+ (candidate2) => isPlainObject2(candidate2) && candidate2.index === 0
838
+ );
839
+ if (primaryCandidates.length === 1) {
840
+ return primaryCandidates[0];
841
+ }
842
+ if (primaryCandidates.length > 1) {
843
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
844
+ }
845
+ if (candidates.length === 0) return void 0;
846
+ if (candidates.length !== 1) {
847
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
848
+ }
849
+ const candidate = candidates[0];
850
+ if (candidate.index !== void 0 && candidate.index !== 0) {
851
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call" });
852
+ }
853
+ return candidate;
854
+ }
855
+ function providerFinishReasonFromGeminiResponse(response) {
856
+ const candidateFinishReason = stringFrom(primaryGeminiCandidate(response)?.finishReason);
857
+ if (candidateFinishReason !== void 0) return candidateFinishReason;
858
+ const promptFeedback = response.promptFeedback ?? response.prompt_feedback;
859
+ if (promptFeedback === void 0) return void 0;
860
+ if (!isPlainObject2(promptFeedback)) {
861
+ throw new CompletionProviderOutputError({ kind: "invalid-response" });
862
+ }
863
+ const blockReason = promptFeedback.blockReason ?? promptFeedback.block_reason;
864
+ if (blockReason === void 0) return void 0;
865
+ if (typeof blockReason !== "string" || blockReason.length === 0) {
866
+ throw new CompletionProviderOutputError({ kind: "invalid-response" });
867
+ }
868
+ return blockReason;
869
+ }
870
+ function hasGeminiCandidatePayload(response) {
871
+ return Array.isArray(response.candidates) && response.candidates.length > 0;
872
+ }
873
+ function hasGeminiFunctionCallMarker(response, parts) {
874
+ if (parts.some(({ part }) => ownDataProperty(part, "functionCall") !== void 0)) {
875
+ return true;
876
+ }
877
+ if (hasGeminiCandidatePayload(response)) return false;
878
+ const camelCaseCalls = ownDataProperty(response, "functionCalls");
879
+ const snakeCaseCalls = ownDataProperty(response, "function_calls");
880
+ return Array.isArray(camelCaseCalls) && camelCaseCalls.length > 0 || Array.isArray(snakeCaseCalls) && snakeCaseCalls.length > 0;
881
+ }
882
+ function deterministicGeminiToolCallId(partIndex) {
883
+ return `gemini-tool-${partIndex.toString()}`;
884
+ }
885
+ function toolCallFromGeminiFunctionCall(call) {
886
+ let toolCall = {
887
+ type: "tool-call",
888
+ toolCallId: call.toolCallId,
889
+ toolName: call.name,
890
+ input: call.args
891
+ };
892
+ if (call.callId !== void 0) toolCall = { ...toolCall, callId: call.callId };
893
+ if (call.signature !== void 0) toolCall = { ...toolCall, signature: call.signature };
894
+ return toolCall;
895
+ }
896
+ function mergeGeminiStreamToolCalls(response, toolCalls) {
897
+ if (toolCalls.length === 0) return response;
898
+ const finalIds = new Set(
899
+ response.choice.flatMap((part) => part.type === "tool-call" ? [part.toolCallId] : [])
900
+ );
901
+ const missing = toolCalls.filter((toolCall) => !finalIds.has(toolCall.toolCallId));
902
+ return missing.length === 0 ? response : { ...response, choice: [...response.choice, ...missing] };
903
+ }
904
+ function assertSafeGeminiToolFinishReason(value, usage) {
905
+ const error = geminiToolFinishError(value, true, usage);
906
+ if (error !== void 0) throw error;
907
+ }
908
+ function geminiToolFinishError(value, hasToolCalls, usage) {
909
+ if (value === "MALFORMED_FUNCTION_CALL") {
910
+ return new CompletionProviderOutputError({ kind: "malformed-tool-arguments", usage });
911
+ }
912
+ if (value === "UNEXPECTED_TOOL_CALL" || value === "TOO_MANY_TOOL_CALLS") {
913
+ return new CompletionProviderOutputError({ kind: "invalid-tool-call", usage });
914
+ }
915
+ if (!hasToolCalls) return void 0;
916
+ if (value === void 0) {
917
+ return new CompletionProviderOutputError({ kind: "incomplete-tool-call", usage });
918
+ }
919
+ const finishReason = geminiFinishReason(value, true);
920
+ if (finishReason === "tool-calls") return void 0;
921
+ if (finishReason === "length") {
922
+ return new CompletionProviderOutputError({
923
+ kind: "truncated-tool-call",
924
+ finishReason,
925
+ usage
926
+ });
927
+ }
928
+ if (finishReason === "content-filter") {
929
+ return new CompletionProviderOutputError({
930
+ kind: "filtered-tool-call",
931
+ finishReason,
932
+ usage
933
+ });
934
+ }
935
+ return new CompletionProviderOutputError({ kind: "invalid-tool-call", finishReason, usage });
936
+ }
937
+ function assertSafeGeminiCompletionResponse(response) {
938
+ const toolCalls = response.choice.filter(
939
+ (part) => part.type === "tool-call"
940
+ );
941
+ if (toolCalls.length > 0) {
942
+ if (response.finishReason === void 0) {
943
+ throw new CompletionProviderOutputError({
944
+ kind: "incomplete-tool-call",
945
+ usage: response.usage
946
+ });
947
+ }
948
+ assertNormalizedGeminiToolFinishReason(response.finishReason, response.usage);
949
+ } else if (toolCalls.length === 0 && response.finishReason === "tool-calls") {
950
+ throw new CompletionProviderOutputError({
951
+ kind: "invalid-tool-call",
952
+ finishReason: response.finishReason,
953
+ usage: response.usage
954
+ });
955
+ }
956
+ const toolCallIds = /* @__PURE__ */ new Set();
957
+ const callIds = /* @__PURE__ */ new Set();
958
+ for (const toolCall of toolCalls) {
959
+ if (toolCallIds.has(toolCall.toolCallId)) {
960
+ throw invalidGeminiToolCall(toolCall.toolCallId, response.usage);
593
961
  }
594
- return Array.isArray(candidate.content.parts) ? candidate.content.parts.filter(isPlainObject2) : [];
962
+ toolCallIds.add(toolCall.toolCallId);
963
+ if (toolCall.callId !== void 0) {
964
+ if (callIds.has(toolCall.callId)) {
965
+ throw invalidGeminiToolCall(toolCall.toolCallId, response.usage);
966
+ }
967
+ callIds.add(toolCall.callId);
968
+ }
969
+ }
970
+ }
971
+ function assertDistinctGeminiFunctionCalls(calls, usage) {
972
+ const toolCallIds = /* @__PURE__ */ new Set();
973
+ const callIds = /* @__PURE__ */ new Set();
974
+ for (const call of calls) {
975
+ if (toolCallIds.has(call.toolCallId)) {
976
+ throw invalidGeminiToolCall(call.toolCallId, usage);
977
+ }
978
+ toolCallIds.add(call.toolCallId);
979
+ if (call.callId !== void 0) {
980
+ if (callIds.has(call.callId)) {
981
+ throw invalidGeminiToolCall(call.toolCallId, usage);
982
+ }
983
+ callIds.add(call.callId);
984
+ }
985
+ }
986
+ }
987
+ function assertNormalizedGeminiToolFinishReason(finishReason, usage) {
988
+ if (finishReason === "stop" || finishReason === "tool-calls") return;
989
+ if (finishReason === "length") {
990
+ throw new CompletionProviderOutputError({
991
+ kind: "truncated-tool-call",
992
+ finishReason,
993
+ usage
994
+ });
995
+ }
996
+ if (finishReason === "content-filter") {
997
+ throw new CompletionProviderOutputError({
998
+ kind: "filtered-tool-call",
999
+ finishReason,
1000
+ usage
1001
+ });
1002
+ }
1003
+ throw new CompletionProviderOutputError({ kind: "invalid-tool-call", finishReason, usage });
1004
+ }
1005
+ function geminiProviderOutputErrorWithUsage(error, usage) {
1006
+ if (error.kind === "truncated-tool-call") {
1007
+ return new CompletionProviderOutputError({
1008
+ kind: error.kind,
1009
+ finishReason: "length",
1010
+ toolCallId: error.toolCallId,
1011
+ usage
1012
+ });
1013
+ }
1014
+ if (error.kind === "filtered-tool-call") {
1015
+ return new CompletionProviderOutputError({
1016
+ kind: error.kind,
1017
+ finishReason: "content-filter",
1018
+ toolCallId: error.toolCallId,
1019
+ usage
1020
+ });
1021
+ }
1022
+ if (error.finishReason === "length" || error.finishReason === "content-filter") {
1023
+ throw error;
1024
+ }
1025
+ return new CompletionProviderOutputError({
1026
+ kind: error.kind,
1027
+ finishReason: error.finishReason,
1028
+ toolCallId: error.toolCallId,
1029
+ usage
595
1030
  });
596
1031
  }
597
- function primaryGeminiCandidate(response) {
598
- const candidates = Array.isArray(response.candidates) ? response.candidates.filter(isPlainObject2) : [];
599
- return candidates.find((candidate) => candidate.index === 0) ?? candidates[0];
1032
+ function invalidGeminiToolCall(toolCallId, usage) {
1033
+ return new CompletionProviderOutputError({ kind: "invalid-tool-call", toolCallId, usage });
1034
+ }
1035
+ function invalidGeminiToolCallArguments(toolCallId, usage) {
1036
+ return new CompletionProviderOutputError({
1037
+ kind: "invalid-tool-arguments",
1038
+ toolCallId,
1039
+ usage
1040
+ });
1041
+ }
1042
+ function ownDataProperty(value, key) {
1043
+ try {
1044
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1045
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
1046
+ } catch {
1047
+ return void 0;
1048
+ }
1049
+ }
1050
+ function isNonblankString(value) {
1051
+ return typeof value === "string" && value.trim().length > 0;
600
1052
  }
601
1053
  function usageFromGemini(usage) {
602
1054
  const raw = isPlainObject2(usage) ? usage : {};
@@ -624,17 +1076,11 @@ function usageFromGemini(usage) {
624
1076
  }
625
1077
  };
626
1078
  }
627
- function toJsonValue(value) {
628
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || Array.isArray(value) || isPlainObject2(value)) {
629
- return value;
630
- }
631
- return String(value);
632
- }
633
1079
  function isPlainObject2(value) {
634
1080
  return typeof value === "object" && value !== null && !Array.isArray(value);
635
1081
  }
636
1082
  function numberFrom(value) {
637
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
1083
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
638
1084
  }
639
1085
  function stringFrom(value) {
640
1086
  return typeof value === "string" ? value : void 0;
@@ -735,6 +1181,9 @@ function isNumberArray(value) {
735
1181
  // src/gemini/image-generation.ts
736
1182
  import { Buffer as Buffer2 } from "buffer";
737
1183
  var GEMINI_2_5_FLASH_IMAGE = "gemini-2.5-flash-image";
1184
+ var GEMINI_3_1_FLASH_IMAGE = "gemini-3.1-flash-image";
1185
+ var GEMINI_3_1_FLASH_LITE_IMAGE = "gemini-3.1-flash-lite-image";
1186
+ var GEMINI_3_PRO_IMAGE = "gemini-3-pro-image";
738
1187
  var GEMINI_3_PRO_IMAGE_PREVIEW = "gemini-3-pro-image-preview";
739
1188
  var IMAGEN_4_GENERATE = "imagen-4.0-generate-001";
740
1189
  var GeminiImageGenerationModel = class {
@@ -931,11 +1380,29 @@ var GEMINI_COMPLETION_MODEL_CONTEXT_LIMITS = {
931
1380
  maxInputTokens: 65536,
932
1381
  maxOutputTokens: 65536
933
1382
  },
1383
+ "gemini-3.1-flash-image": {
1384
+ contextWindow: 131072,
1385
+ maxInputTokens: 131072,
1386
+ maxOutputTokens: 32768
1387
+ },
1388
+ "gemini-3.1-flash-lite-image": {
1389
+ contextWindow: 65536,
1390
+ maxInputTokens: 65536,
1391
+ maxOutputTokens: 4096
1392
+ },
934
1393
  "gemini-3.1-flash-lite": CONTEXT_1M_64K,
935
1394
  "gemini-3.1-flash-lite-preview": CONTEXT_1M_64K,
936
1395
  "gemini-3.1-pro-preview": CONTEXT_1M_64K,
937
1396
  "gemini-3.1-pro-preview-customtools": CONTEXT_1M_64K,
938
1397
  "gemini-3.5-flash": CONTEXT_1M_64K,
1398
+ "gemini-3.5-flash-lite": CONTEXT_1M_64K,
1399
+ "gemini-3.6-flash": CONTEXT_1M_64K,
1400
+ "gemini-3.7-flash": CONTEXT_1M_64K,
1401
+ "gemini-3-pro-image": {
1402
+ contextWindow: 65536,
1403
+ maxInputTokens: 65536,
1404
+ maxOutputTokens: 32768
1405
+ },
939
1406
  "gemini-flash-latest": CONTEXT_1M_64K,
940
1407
  "gemini-flash-lite-latest": CONTEXT_1M_64K,
941
1408
  "gemma-4-26b-a4b-it": {
@@ -1220,6 +1687,9 @@ function isAsyncIterable(value) {
1220
1687
  }
1221
1688
  export {
1222
1689
  GEMINI_2_5_FLASH_IMAGE,
1690
+ GEMINI_3_1_FLASH_IMAGE,
1691
+ GEMINI_3_1_FLASH_LITE_IMAGE,
1692
+ GEMINI_3_PRO_IMAGE,
1223
1693
  GEMINI_3_PRO_IMAGE_PREVIEW,
1224
1694
  GeminiClient,
1225
1695
  IMAGEN_4_GENERATE,