@contentgrowth/llm-service 1.1.2 → 1.2.1

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
@@ -71,8 +71,10 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
71
71
  _buildTenantConfig(tenantConfig, env) {
72
72
  return {
73
73
  provider: tenantConfig.provider,
74
- apiKey: tenantConfig.api_key,
75
74
  models: MODEL_CONFIGS[tenantConfig.provider],
75
+ apiKey: tenantConfig.api_key,
76
+ project: tenantConfig.project,
77
+ location: tenantConfig.location,
76
78
  temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
77
79
  maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536"),
78
80
  capabilities: tenantConfig.capabilities || { chat: true, image: false, video: false },
@@ -105,6 +107,28 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
105
107
  image: env.GEMINI_IMAGE_MODEL || providerDefaults.image,
106
108
  video: env.GEMINI_VIDEO_MODEL || providerDefaults.video
107
109
  };
110
+ } else if (provider === "vertex") {
111
+ apiKey = env.VERTEX_API_KEY;
112
+ const project = env.VERTEX_PROJECT || env.GOOGLE_CLOUD_PROJECT;
113
+ const location = env.VERTEX_LOCATION || env.GOOGLE_CLOUD_LOCATION || "us-central1";
114
+ models = {
115
+ default: env.VERTEX_MODEL || providerDefaults.default,
116
+ edge: env.VERTEX_MODEL_EDGE || providerDefaults.edge,
117
+ fast: env.VERTEX_MODEL_FAST || providerDefaults.fast,
118
+ cost: env.VERTEX_MODEL_COST || providerDefaults.cost,
119
+ free: env.VERTEX_MODEL_FREE || providerDefaults.free,
120
+ image: env.VERTEX_IMAGE_MODEL || providerDefaults.image,
121
+ video: env.VERTEX_VIDEO_MODEL || providerDefaults.video
122
+ };
123
+ return {
124
+ provider,
125
+ apiKey,
126
+ project,
127
+ location,
128
+ models,
129
+ temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
130
+ maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536")
131
+ };
108
132
  }
109
133
  return {
110
134
  provider,
@@ -139,6 +163,15 @@ var MODEL_CONFIGS = {
139
163
  video: "veo",
140
164
  image: "gemini-3-pro-image-preview"
141
165
  // Default image generation model
166
+ },
167
+ vertex: {
168
+ default: "gemini-3-flash-preview",
169
+ edge: "gemini-3-pro-preview",
170
+ fast: "gemini-3-flash-preview",
171
+ cost: "gemini-3-flash-preview",
172
+ free: "gemini-3-flash-preview",
173
+ video: "veo",
174
+ image: "gemini-3-pro-image-preview"
142
175
  }
143
176
  };
144
177
  var ConfigManager = class {
@@ -536,25 +569,43 @@ var OpenAIProvider = class extends BaseLLMProvider {
536
569
  }
537
570
  };
538
571
 
539
- // src/llm/providers/gemini-provider.js
572
+ // src/llm/providers/google-provider.js
540
573
  import { GoogleGenAI } from "@google/genai";
541
- var GeminiProvider = class extends BaseLLMProvider {
574
+ var GoogleProvider = class extends BaseLLMProvider {
542
575
  constructor(config) {
543
576
  super(config);
544
- const clientConfig = {};
545
- if (config.project || config.location) {
546
- console.log(`[GeminiProvider] Initializing with Vertex AI (Project: ${config.project}, Location: ${config.location || "us-central1"})`);
547
- clientConfig.vertexAI = {
548
- project: config.project,
549
- location: config.location || "us-central1"
550
- };
551
- } else {
552
- clientConfig.apiKey = config.apiKey;
553
- }
554
- this.client = new GoogleGenAI(clientConfig);
555
577
  this.models = config.models;
556
578
  this.defaultModel = config.models.default;
557
579
  this._pendingOperations = /* @__PURE__ */ new Map();
580
+ if (config.provider === "vertex") {
581
+ if (config.apiKey) {
582
+ this.client = new GoogleGenAI({
583
+ vertexai: true,
584
+ apiKey: config.apiKey
585
+ });
586
+ } else {
587
+ if (!config.project) {
588
+ console.warn("[GoogleProvider] Vertex AI: no project ID and no API key. Calls will likely fail.");
589
+ }
590
+ this.client = new GoogleGenAI({
591
+ vertexai: true,
592
+ project: config.project,
593
+ location: config.location || "us-central1"
594
+ });
595
+ }
596
+ } else {
597
+ this.client = new GoogleGenAI({
598
+ apiKey: config.apiKey
599
+ });
600
+ }
601
+ }
602
+ /**
603
+ * Perform the actual API call. Both AI Studio and Vertex AI use the
604
+ * same @google/genai SDK method — the routing is determined by how
605
+ * the client was constructed.
606
+ */
607
+ async _generateContent(requestOptions) {
608
+ return this.client.models.generateContent(requestOptions);
558
609
  }
559
610
  async chat(userMessage, systemPrompt = "", options = {}) {
560
611
  const messages = [{ role: "user", content: userMessage }];
@@ -586,7 +637,7 @@ var GeminiProvider = class extends BaseLLMProvider {
586
637
  );
587
638
  }
588
639
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
589
- var _a, _b, _c, _d, _e, _f, _g;
640
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
590
641
  const generationConfig = {
591
642
  temperature: (_a = options.temperature) != null ? _a : temperature,
592
643
  maxOutputTokens: (_b = options.maxTokens) != null ? _b : maxTokens
@@ -612,8 +663,41 @@ ${msg.content}`;
612
663
  }
613
664
  }
614
665
  }
615
- const contents = geminiMessages.map((msg, index) => {
616
- var _a2, _b2, _c2, _d2;
666
+ const contents = [];
667
+ let pendingToolParts = [];
668
+ for (let index = 0; index < geminiMessages.length; index++) {
669
+ const msg = geminiMessages[index];
670
+ if (msg.role === "tool") {
671
+ let assistantMsg = null;
672
+ for (let j = index - 1; j >= 0; j--) {
673
+ if (geminiMessages[j].role === "assistant" && geminiMessages[j].tool_calls) {
674
+ assistantMsg = geminiMessages[j];
675
+ break;
676
+ }
677
+ }
678
+ const toolCall = (_c = assistantMsg == null ? void 0 : assistantMsg.tool_calls) == null ? void 0 : _c.find((tc) => tc.id === msg.tool_call_id);
679
+ pendingToolParts.push({
680
+ functionResponse: {
681
+ name: ((_d = toolCall == null ? void 0 : toolCall.function) == null ? void 0 : _d.name) || "unknown_tool",
682
+ response: { content: msg.content }
683
+ }
684
+ });
685
+ const nextMsg = geminiMessages[index + 1];
686
+ if (!nextMsg || nextMsg.role !== "tool") {
687
+ if (options.responseFormat === "json" || ((_e = options.responseFormat) == null ? void 0 : _e.type) === "json_schema" || options.responseSchema) {
688
+ pendingToolParts.push({ text: "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]" });
689
+ } else {
690
+ pendingToolParts.push({ text: "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]" });
691
+ }
692
+ contents.push({ role: "user", parts: pendingToolParts });
693
+ pendingToolParts = [];
694
+ }
695
+ continue;
696
+ }
697
+ if (pendingToolParts.length > 0) {
698
+ contents.push({ role: "user", parts: pendingToolParts });
699
+ pendingToolParts = [];
700
+ }
617
701
  let role = "";
618
702
  let parts2;
619
703
  switch (msg.role) {
@@ -622,7 +706,7 @@ ${msg.content}`;
622
706
  parts2 = [{ text: msg.content }];
623
707
  if (index === geminiMessages.length - 1) {
624
708
  let reminder = "";
625
- if (options.responseFormat === "json" || ((_a2 = options.responseFormat) == null ? void 0 : _a2.type) === "json_schema" || options.responseSchema) {
709
+ if (options.responseFormat === "json" || ((_f = options.responseFormat) == null ? void 0 : _f.type) === "json_schema" || options.responseSchema) {
626
710
  reminder = "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]";
627
711
  } else {
628
712
  reminder = "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]";
@@ -656,27 +740,11 @@ ${msg.content}`;
656
740
  parts2 = [part];
657
741
  }
658
742
  break;
659
- case "tool":
660
- role = "user";
661
- const preceding_message = messages[index - 1];
662
- const tool_call = (_b2 = preceding_message == null ? void 0 : preceding_message.tool_calls) == null ? void 0 : _b2.find((tc) => tc.id === msg.tool_call_id);
663
- parts2 = [{
664
- functionResponse: {
665
- name: ((_c2 = tool_call == null ? void 0 : tool_call.function) == null ? void 0 : _c2.name) || "unknown_tool",
666
- response: { content: msg.content }
667
- }
668
- }];
669
- if (options.responseFormat === "json" || ((_d2 = options.responseFormat) == null ? void 0 : _d2.type) === "json_schema" || options.responseSchema) {
670
- parts2.push({ text: "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]" });
671
- } else {
672
- parts2.push({ text: "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]" });
673
- }
674
- break;
675
743
  default:
676
- return null;
744
+ continue;
677
745
  }
678
- return { role, parts: parts2 };
679
- }).filter(Boolean);
746
+ contents.push({ role, parts: parts2 });
747
+ }
680
748
  while (contents.length > 0 && contents[0].role !== "user") {
681
749
  contents.shift();
682
750
  }
@@ -694,23 +762,17 @@ ${msg.content}`;
694
762
  if (tools && tools.length > 0) {
695
763
  requestOptions.config.tools = [{ functionDeclarations: tools.map((t) => t.function) }];
696
764
  if (requestOptions.config.responseMimeType === "application/json") {
697
- console.warn("[GeminiProvider] Disabling strict JSON mode because tools are present. Relying on system prompt.");
765
+ console.warn(`[${this.constructor.name}] Disabling strict JSON mode because tools are present. Relying on system prompt.`);
698
766
  delete requestOptions.config.responseMimeType;
699
767
  delete requestOptions.config.responseSchema;
700
768
  }
701
769
  }
702
- let response;
703
- try {
704
- response = await this.client.models.generateContent(requestOptions);
705
- } catch (error) {
706
- console.error(`[GeminiProvider] generateContent failed (API Key: ${this._getMaskedApiKey()}):`, error);
707
- throw error;
708
- }
709
- const candidate = (_c = response.candidates) == null ? void 0 : _c[0];
770
+ const response = await this._generateContent(requestOptions);
771
+ const candidate = (_g = response.candidates) == null ? void 0 : _g[0];
710
772
  if (!candidate) {
711
773
  throw new LLMServiceException("No candidates returned from model", 500);
712
774
  }
713
- const parts = ((_d = candidate.content) == null ? void 0 : _d.parts) || [];
775
+ const parts = ((_h = candidate.content) == null ? void 0 : _h.parts) || [];
714
776
  let textContent = "";
715
777
  let toolCalls = null;
716
778
  let responseThoughtSignature = null;
@@ -735,10 +797,8 @@ ${msg.content}`;
735
797
  }
736
798
  }
737
799
  if (!textContent && (!toolCalls || toolCalls.length === 0)) {
738
- console.error("[GeminiProvider] Model returned empty response (no text, no tool calls)");
739
- console.error("[GeminiProvider] Finish Reason:", candidate.finishReason);
740
- console.error("[GeminiProvider] Safety Ratings:", JSON.stringify(candidate.safetyRatings, null, 2));
741
- console.error("[GeminiProvider] Full Candidate:", JSON.stringify(candidate, null, 2));
800
+ console.error(`[${this.constructor.name}] Model returned empty response (no text, no tool calls)`);
801
+ console.error(`[${this.constructor.name}] Finish Reason:`, candidate.finishReason);
742
802
  throw new LLMServiceException(
743
803
  `Model returned empty response. Finish Reason: ${candidate.finishReason}.`,
744
804
  500
@@ -748,22 +808,18 @@ ${msg.content}`;
748
808
  return {
749
809
  content: textContent,
750
810
  thought_signature: responseThoughtSignature,
751
- // Return signature to caller
752
811
  tool_calls: toolCalls ? (Array.isArray(toolCalls) ? toolCalls : [toolCalls]).map((fc) => ({
753
812
  type: "function",
754
813
  function: fc,
755
814
  thought_signature: fc.thought_signature
756
815
  })) : null,
757
816
  finishReason: normalizedFinishReason,
758
- // Standardized: 'completed', 'truncated', etc.
759
817
  _rawFinishReason: candidate.finishReason,
760
- // Keep original for debugging
761
818
  _responseFormat: options.responseFormat,
762
- // Return usage stats
763
819
  usage: {
764
- prompt_tokens: ((_e = response.usageMetadata) == null ? void 0 : _e.promptTokenCount) || 0,
765
- completion_tokens: ((_f = response.usageMetadata) == null ? void 0 : _f.candidatesTokenCount) || 0,
766
- total_tokens: ((_g = response.usageMetadata) == null ? void 0 : _g.totalTokenCount) || 0
820
+ prompt_tokens: ((_i = response.usageMetadata) == null ? void 0 : _i.promptTokenCount) || 0,
821
+ completion_tokens: ((_j = response.usageMetadata) == null ? void 0 : _j.candidatesTokenCount) || 0,
822
+ total_tokens: ((_k = response.usageMetadata) == null ? void 0 : _k.totalTokenCount) || 0
767
823
  },
768
824
  ...options.responseFormat && this._shouldAutoParse(options) ? {
769
825
  parsedContent: this._safeJsonParse(textContent)
@@ -785,7 +841,7 @@ ${msg.content}`;
785
841
  if (schema) {
786
842
  config.responseSchema = this._convertToGeminiSchema(schema);
787
843
  } else {
788
- console.warn("[GeminiProvider] Using legacy JSON mode without schema - may produce markdown wrappers");
844
+ console.warn(`[${this.constructor.name}] Using legacy JSON mode without schema - may produce markdown wrappers`);
789
845
  }
790
846
  }
791
847
  }
@@ -843,8 +899,7 @@ ${msg.content}`;
843
899
  if (!content) return null;
844
900
  const parsed = extractJsonFromResponse(content);
845
901
  if (!parsed) {
846
- console.error("[GeminiProvider] Failed to extract valid JSON from response");
847
- console.error("[GeminiProvider] Content preview:", content.substring(0, 200));
902
+ console.error(`[${this.constructor.name}] Failed to extract valid JSON from response`);
848
903
  }
849
904
  return parsed;
850
905
  }
@@ -874,9 +929,9 @@ ${msg.content}`;
874
929
  toolResults.forEach((result) => messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.output }));
875
930
  }
876
931
  async imageGeneration(prompt, systemPrompt, options = {}) {
877
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
932
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
878
933
  const modelName = options.model || this.models.image || "gemini-3-pro-image-preview";
879
- console.log(`[GeminiProvider] Generating image with model: ${modelName}`);
934
+ console.log(`[${this.constructor.name}] Generating image with model: ${modelName}`);
880
935
  const hasReferenceImages = options.images && options.images.length > 0;
881
936
  const generationConfig = {
882
937
  responseModalities: hasReferenceImages ? ["TEXT", "IMAGE"] : ["IMAGE"]
@@ -908,7 +963,7 @@ ${msg.content}`;
908
963
  if (systemPrompt) {
909
964
  requestOptions.config.systemInstruction = { parts: [{ text: systemPrompt }] };
910
965
  }
911
- const response = await this.client.models.generateContent(requestOptions);
966
+ const response = await this._generateContent(requestOptions);
912
967
  const imagePart = (_d = (_c = (_b = (_a = response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.content) == null ? void 0 : _c.parts) == null ? void 0 : _d.find(
913
968
  (part) => {
914
969
  var _a2;
@@ -916,30 +971,21 @@ ${msg.content}`;
916
971
  }
917
972
  );
918
973
  if (!imagePart || !imagePart.inlineData) {
919
- const textPart = (_h = (_g = (_f = (_e = response.candidates) == null ? void 0 : _e[0]) == null ? void 0 : _f.content) == null ? void 0 : _g.parts) == null ? void 0 : _h.find((p) => p.text);
920
- const candidate = (_i = response.candidates) == null ? void 0 : _i[0];
921
- console.error("[GeminiProvider] Image generation failed (no image data)");
922
- if (candidate) {
923
- console.error("[GeminiProvider] Finish Reason:", candidate.finishReason);
924
- console.error("[GeminiProvider] Safety Ratings:", JSON.stringify(candidate.safetyRatings, null, 2));
925
- console.error("[GeminiProvider] Full Candidate:", JSON.stringify(candidate, null, 2));
926
- }
927
- if (textPart) {
928
- console.warn("[GeminiProvider] Model returned text instead of image:", textPart.text);
929
- }
974
+ const candidate = (_e = response.candidates) == null ? void 0 : _e[0];
975
+ console.error(`[${this.constructor.name}] Image generation failed (no image data)`);
930
976
  throw new Error(`No image data in response. Finish Reason: ${candidate == null ? void 0 : candidate.finishReason}`);
931
977
  }
932
978
  let thoughtSignature = null;
933
979
  if (imagePart.thought_signature || imagePart.thoughtSignature) {
934
980
  thoughtSignature = imagePart.thought_signature || imagePart.thoughtSignature;
935
981
  } else {
936
- const signaturePart = (_m = (_l = (_k = (_j = response.candidates) == null ? void 0 : _j[0]) == null ? void 0 : _k.content) == null ? void 0 : _l.parts) == null ? void 0 : _m.find((p) => p.thought_signature || p.thoughtSignature);
982
+ const signaturePart = (_i = (_h = (_g = (_f = response.candidates) == null ? void 0 : _f[0]) == null ? void 0 : _g.content) == null ? void 0 : _h.parts) == null ? void 0 : _i.find((p) => p.thought_signature || p.thoughtSignature);
937
983
  if (signaturePart) {
938
984
  thoughtSignature = signaturePart.thought_signature || signaturePart.thoughtSignature;
939
985
  }
940
986
  }
941
987
  if (thoughtSignature && thoughtSignature.length > 5e4) {
942
- console.warn(`[GeminiProvider] \u26A0\uFE0F Thought signature is abnormally large (${thoughtSignature.length} chars). Replacing with bypass token to save context.`);
988
+ console.warn(`[${this.constructor.name}] \u26A0\uFE0F Thought signature is abnormally large (${thoughtSignature.length} chars). Replacing with bypass token.`);
943
989
  thoughtSignature = "skip_thought_signature_validator";
944
990
  }
945
991
  return {
@@ -962,31 +1008,20 @@ ${prompt}` : prompt;
962
1008
  durationSeconds: options.durationSeconds || 6,
963
1009
  aspectRatio: options.aspectRatio || "16:9",
964
1010
  numberOfVideos: 1,
965
- // Pass reference images if provided
966
1011
  ...images && images.length > 0 ? { referenceImages: images } : {}
967
1012
  }
968
1013
  };
969
- const logConfig = JSON.parse(JSON.stringify(requestConfig));
970
- if (logConfig.config && logConfig.config.referenceImages) {
971
- logConfig.config.referenceImages = logConfig.config.referenceImages.map((img) => ({
972
- ...img,
973
- data: `... (${img.data ? img.data.length : 0} bytes)`
974
- // Summarize data
975
- }));
976
- }
977
- console.log("[GeminiProvider] startVideoGeneration request:", JSON.stringify(logConfig, null, 2));
978
1014
  try {
979
1015
  const operation = await this.client.models.generateVideos(requestConfig);
980
1016
  this._pendingOperations.set(operation.name, operation);
981
1017
  return { operationName: operation.name };
982
1018
  } catch (error) {
983
- console.error(`[GeminiProvider] startVideoGeneration failed (API Key: ${this._getMaskedApiKey()}):`, error);
1019
+ console.error(`[${this.constructor.name}] startVideoGeneration failed (API Key: ${this._getMaskedApiKey()}):`, error);
984
1020
  throw error;
985
1021
  }
986
1022
  }
987
1023
  async getVideoGenerationStatus(operationName) {
988
1024
  var _a, _b, _c, _d, _e, _f;
989
- console.log(`[GeminiProvider] Checking status for operation: ${operationName}`);
990
1025
  let operation = this._pendingOperations.get(operationName);
991
1026
  if (!operation) {
992
1027
  operation = await this.client.models.getOperation(operationName);
@@ -998,11 +1033,9 @@ ${prompt}` : prompt;
998
1033
  progress: ((_a = operation.metadata) == null ? void 0 : _a.progressPercent) || 0,
999
1034
  state: ((_b = operation.metadata) == null ? void 0 : _b.state) || (operation.done ? "COMPLETED" : "PROCESSING")
1000
1035
  };
1001
- console.log(`[GeminiProvider] Operation status: ${result.state}, Progress: ${result.progress}%`);
1002
1036
  if (operation.done) {
1003
1037
  this._pendingOperations.delete(operationName);
1004
1038
  if (operation.error) {
1005
- console.error("[GeminiProvider] Video generation failed:", JSON.stringify(operation.error, null, 2));
1006
1039
  result.error = operation.error;
1007
1040
  } else {
1008
1041
  const videoResult = operation.response;
@@ -1014,20 +1047,17 @@ ${prompt}` : prompt;
1014
1047
  }
1015
1048
  async startDeepResearch(prompt, options = {}) {
1016
1049
  const agent = options.agent || "deep-research-pro-preview-12-2025";
1017
- console.log(`[GeminiProvider] Starting Deep Research with agent: ${agent}`);
1050
+ console.log(`[${this.constructor.name}] Starting Deep Research with agent: ${agent}`);
1018
1051
  try {
1019
1052
  const interaction = await this.client.interactions.create({
1020
1053
  agent,
1021
1054
  input: prompt,
1022
1055
  background: true,
1023
- // Required for long running
1024
1056
  store: true
1025
- // Required for polling
1026
1057
  });
1027
- console.log(`[GeminiProvider] Deep Research started. Interaction ID: ${interaction.id}`);
1028
1058
  return { operationId: interaction.id };
1029
1059
  } catch (error) {
1030
- console.error(`[GeminiProvider] startDeepResearch failed:`, error);
1060
+ console.error(`[${this.constructor.name}] startDeepResearch failed:`, error);
1031
1061
  throw error;
1032
1062
  }
1033
1063
  }
@@ -1046,18 +1076,10 @@ ${prompt}` : prompt;
1046
1076
  }
1047
1077
  return response;
1048
1078
  } catch (error) {
1049
- console.error(`[GeminiProvider] getDeepResearchStatus failed for ${operationId}:`, error);
1079
+ console.error(`[${this.constructor.name}] getDeepResearchStatus failed for ${operationId}:`, error);
1050
1080
  throw error;
1051
1081
  }
1052
1082
  }
1053
- /**
1054
- * Extract structured data from a file (PDF, Image, etc.) using Gemini Multimodal capabilities.
1055
- * @param {Buffer|string} fileData - Base64 string or Buffer of the file
1056
- * @param {string} mimeType - Mime type (e.g., 'application/pdf', 'image/png')
1057
- * @param {string} prompt - Extraction prompt
1058
- * @param {Object} schema - JSON schema for the output
1059
- * @param {Object} options - Additional options
1060
- */
1061
1083
  async extractWithLLM(fileData, mimeType, prompt, schema = null, options = {}) {
1062
1084
  var _a, _b, _c, _d;
1063
1085
  const tier = options.tier || "default";
@@ -1069,9 +1091,7 @@ ${prompt}` : prompt;
1069
1091
  maxTokens,
1070
1092
  temperature
1071
1093
  );
1072
- const parts = [
1073
- { text: prompt }
1074
- ];
1094
+ const parts = [{ text: prompt }];
1075
1095
  let base64Data = fileData;
1076
1096
  if (typeof fileData !== "string") {
1077
1097
  try {
@@ -1094,7 +1114,7 @@ ${prompt}` : prompt;
1094
1114
  config: generationConfig
1095
1115
  };
1096
1116
  try {
1097
- const response = await this.client.models.generateContent(requestOptions);
1117
+ const response = await this._generateContent(requestOptions);
1098
1118
  const candidate = (_a = response.candidates) == null ? void 0 : _a[0];
1099
1119
  if (!candidate) {
1100
1120
  throw new LLMServiceException("No candidates returned from model during extraction", 500);
@@ -1105,7 +1125,7 @@ ${prompt}` : prompt;
1105
1125
  }
1106
1126
  return textContent;
1107
1127
  } catch (error) {
1108
- console.error(`[GeminiProvider] extractWithLLM failed (API Key: ${this._getMaskedApiKey()}):`, error);
1128
+ console.error(`[${this.constructor.name}] extractWithLLM failed (API Key: ${this._getMaskedApiKey()}):`, error);
1109
1129
  throw error;
1110
1130
  }
1111
1131
  }
@@ -1124,14 +1144,14 @@ var LLMService = class {
1124
1144
  return this.providerCache.get(cacheKey);
1125
1145
  }
1126
1146
  const config = await ConfigManager.getConfig(tenantId, this.env);
1127
- if (!config.apiKey) {
1147
+ if (!config.apiKey && config.provider !== "vertex") {
1128
1148
  throw new LLMServiceException(`LLM service is not configured for ${config.provider}. Missing API Key.`, 500);
1129
1149
  }
1130
1150
  let provider;
1131
1151
  if (config.provider === "openai") {
1132
1152
  provider = new OpenAIProvider(config);
1133
- } else if (config.provider === "gemini") {
1134
- provider = new GeminiProvider(config);
1153
+ } else if (config.provider === "gemini" || config.provider === "vertex") {
1154
+ provider = new GoogleProvider(config);
1135
1155
  } else {
1136
1156
  throw new LLMServiceException(`Unsupported LLM provider: ${config.provider}`, 500);
1137
1157
  }
@@ -1703,13 +1723,15 @@ export {
1703
1723
  ConfigManager,
1704
1724
  DefaultConfigProvider,
1705
1725
  FINISH_REASONS,
1706
- GeminiProvider,
1726
+ GoogleProvider as GeminiProvider,
1727
+ GoogleProvider,
1707
1728
  LLMService,
1708
1729
  LLMServiceException,
1709
1730
  MODEL_CONFIGS,
1710
1731
  OpenAIProvider,
1711
1732
  TranscriptionService,
1712
1733
  TranscriptionServiceException,
1734
+ GoogleProvider as VertexProvider,
1713
1735
  createSpeechHandler,
1714
1736
  extractJsonFromResponse,
1715
1737
  extractTextAndJson,