@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.cjs +138 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -91
- package/dist/index.d.ts +145 -91
- package/dist/index.js +136 -114
- package/dist/index.js.map +1 -1
- package/package.json +16 -14
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/
|
|
572
|
+
// src/llm/providers/google-provider.js
|
|
540
573
|
import { GoogleGenAI } from "@google/genai";
|
|
541
|
-
var
|
|
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 =
|
|
616
|
-
|
|
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" || ((
|
|
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
|
-
|
|
744
|
+
continue;
|
|
677
745
|
}
|
|
678
|
-
|
|
679
|
-
}
|
|
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(
|
|
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
|
-
|
|
703
|
-
|
|
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 = ((
|
|
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(
|
|
739
|
-
console.error(
|
|
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: ((
|
|
765
|
-
completion_tokens: ((
|
|
766
|
-
total_tokens: ((
|
|
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(
|
|
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(
|
|
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
|
|
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(`[
|
|
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.
|
|
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
|
|
920
|
-
|
|
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 = (
|
|
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(`[
|
|
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(`[
|
|
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(`[
|
|
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(`[
|
|
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(`[
|
|
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.
|
|
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(`[
|
|
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
|
|
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,
|