@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.cjs
CHANGED
|
@@ -35,13 +35,15 @@ __export(index_exports, {
|
|
|
35
35
|
ConfigManager: () => ConfigManager,
|
|
36
36
|
DefaultConfigProvider: () => DefaultConfigProvider,
|
|
37
37
|
FINISH_REASONS: () => FINISH_REASONS,
|
|
38
|
-
GeminiProvider: () =>
|
|
38
|
+
GeminiProvider: () => GoogleProvider,
|
|
39
|
+
GoogleProvider: () => GoogleProvider,
|
|
39
40
|
LLMService: () => LLMService,
|
|
40
41
|
LLMServiceException: () => LLMServiceException,
|
|
41
42
|
MODEL_CONFIGS: () => MODEL_CONFIGS,
|
|
42
43
|
OpenAIProvider: () => OpenAIProvider,
|
|
43
44
|
TranscriptionService: () => TranscriptionService,
|
|
44
45
|
TranscriptionServiceException: () => TranscriptionServiceException,
|
|
46
|
+
VertexProvider: () => GoogleProvider,
|
|
45
47
|
createSpeechHandler: () => createSpeechHandler,
|
|
46
48
|
extractJsonFromResponse: () => extractJsonFromResponse,
|
|
47
49
|
extractTextAndJson: () => extractTextAndJson,
|
|
@@ -119,8 +121,10 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
119
121
|
_buildTenantConfig(tenantConfig, env) {
|
|
120
122
|
return {
|
|
121
123
|
provider: tenantConfig.provider,
|
|
122
|
-
apiKey: tenantConfig.api_key,
|
|
123
124
|
models: MODEL_CONFIGS[tenantConfig.provider],
|
|
125
|
+
apiKey: tenantConfig.api_key,
|
|
126
|
+
project: tenantConfig.project,
|
|
127
|
+
location: tenantConfig.location,
|
|
124
128
|
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
125
129
|
maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536"),
|
|
126
130
|
capabilities: tenantConfig.capabilities || { chat: true, image: false, video: false },
|
|
@@ -153,6 +157,28 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
153
157
|
image: env.GEMINI_IMAGE_MODEL || providerDefaults.image,
|
|
154
158
|
video: env.GEMINI_VIDEO_MODEL || providerDefaults.video
|
|
155
159
|
};
|
|
160
|
+
} else if (provider === "vertex") {
|
|
161
|
+
apiKey = env.VERTEX_API_KEY;
|
|
162
|
+
const project = env.VERTEX_PROJECT || env.GOOGLE_CLOUD_PROJECT;
|
|
163
|
+
const location = env.VERTEX_LOCATION || env.GOOGLE_CLOUD_LOCATION || "us-central1";
|
|
164
|
+
models = {
|
|
165
|
+
default: env.VERTEX_MODEL || providerDefaults.default,
|
|
166
|
+
edge: env.VERTEX_MODEL_EDGE || providerDefaults.edge,
|
|
167
|
+
fast: env.VERTEX_MODEL_FAST || providerDefaults.fast,
|
|
168
|
+
cost: env.VERTEX_MODEL_COST || providerDefaults.cost,
|
|
169
|
+
free: env.VERTEX_MODEL_FREE || providerDefaults.free,
|
|
170
|
+
image: env.VERTEX_IMAGE_MODEL || providerDefaults.image,
|
|
171
|
+
video: env.VERTEX_VIDEO_MODEL || providerDefaults.video
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
provider,
|
|
175
|
+
apiKey,
|
|
176
|
+
project,
|
|
177
|
+
location,
|
|
178
|
+
models,
|
|
179
|
+
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
180
|
+
maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536")
|
|
181
|
+
};
|
|
156
182
|
}
|
|
157
183
|
return {
|
|
158
184
|
provider,
|
|
@@ -187,6 +213,15 @@ var MODEL_CONFIGS = {
|
|
|
187
213
|
video: "veo",
|
|
188
214
|
image: "gemini-3-pro-image-preview"
|
|
189
215
|
// Default image generation model
|
|
216
|
+
},
|
|
217
|
+
vertex: {
|
|
218
|
+
default: "gemini-3-flash-preview",
|
|
219
|
+
edge: "gemini-3-pro-preview",
|
|
220
|
+
fast: "gemini-3-flash-preview",
|
|
221
|
+
cost: "gemini-3-flash-preview",
|
|
222
|
+
free: "gemini-3-flash-preview",
|
|
223
|
+
video: "veo",
|
|
224
|
+
image: "gemini-3-pro-image-preview"
|
|
190
225
|
}
|
|
191
226
|
};
|
|
192
227
|
var ConfigManager = class {
|
|
@@ -584,25 +619,43 @@ var OpenAIProvider = class extends BaseLLMProvider {
|
|
|
584
619
|
}
|
|
585
620
|
};
|
|
586
621
|
|
|
587
|
-
// src/llm/providers/
|
|
622
|
+
// src/llm/providers/google-provider.js
|
|
588
623
|
var import_genai = require("@google/genai");
|
|
589
|
-
var
|
|
624
|
+
var GoogleProvider = class extends BaseLLMProvider {
|
|
590
625
|
constructor(config) {
|
|
591
626
|
super(config);
|
|
592
|
-
const clientConfig = {};
|
|
593
|
-
if (config.project || config.location) {
|
|
594
|
-
console.log(`[GeminiProvider] Initializing with Vertex AI (Project: ${config.project}, Location: ${config.location || "us-central1"})`);
|
|
595
|
-
clientConfig.vertexAI = {
|
|
596
|
-
project: config.project,
|
|
597
|
-
location: config.location || "us-central1"
|
|
598
|
-
};
|
|
599
|
-
} else {
|
|
600
|
-
clientConfig.apiKey = config.apiKey;
|
|
601
|
-
}
|
|
602
|
-
this.client = new import_genai.GoogleGenAI(clientConfig);
|
|
603
627
|
this.models = config.models;
|
|
604
628
|
this.defaultModel = config.models.default;
|
|
605
629
|
this._pendingOperations = /* @__PURE__ */ new Map();
|
|
630
|
+
if (config.provider === "vertex") {
|
|
631
|
+
if (config.apiKey) {
|
|
632
|
+
this.client = new import_genai.GoogleGenAI({
|
|
633
|
+
vertexai: true,
|
|
634
|
+
apiKey: config.apiKey
|
|
635
|
+
});
|
|
636
|
+
} else {
|
|
637
|
+
if (!config.project) {
|
|
638
|
+
console.warn("[GoogleProvider] Vertex AI: no project ID and no API key. Calls will likely fail.");
|
|
639
|
+
}
|
|
640
|
+
this.client = new import_genai.GoogleGenAI({
|
|
641
|
+
vertexai: true,
|
|
642
|
+
project: config.project,
|
|
643
|
+
location: config.location || "us-central1"
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
this.client = new import_genai.GoogleGenAI({
|
|
648
|
+
apiKey: config.apiKey
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Perform the actual API call. Both AI Studio and Vertex AI use the
|
|
654
|
+
* same @google/genai SDK method — the routing is determined by how
|
|
655
|
+
* the client was constructed.
|
|
656
|
+
*/
|
|
657
|
+
async _generateContent(requestOptions) {
|
|
658
|
+
return this.client.models.generateContent(requestOptions);
|
|
606
659
|
}
|
|
607
660
|
async chat(userMessage, systemPrompt = "", options = {}) {
|
|
608
661
|
const messages = [{ role: "user", content: userMessage }];
|
|
@@ -634,7 +687,7 @@ var GeminiProvider = class extends BaseLLMProvider {
|
|
|
634
687
|
);
|
|
635
688
|
}
|
|
636
689
|
async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
|
|
637
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
690
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
638
691
|
const generationConfig = {
|
|
639
692
|
temperature: (_a = options.temperature) != null ? _a : temperature,
|
|
640
693
|
maxOutputTokens: (_b = options.maxTokens) != null ? _b : maxTokens
|
|
@@ -660,8 +713,41 @@ ${msg.content}`;
|
|
|
660
713
|
}
|
|
661
714
|
}
|
|
662
715
|
}
|
|
663
|
-
const contents =
|
|
664
|
-
|
|
716
|
+
const contents = [];
|
|
717
|
+
let pendingToolParts = [];
|
|
718
|
+
for (let index = 0; index < geminiMessages.length; index++) {
|
|
719
|
+
const msg = geminiMessages[index];
|
|
720
|
+
if (msg.role === "tool") {
|
|
721
|
+
let assistantMsg = null;
|
|
722
|
+
for (let j = index - 1; j >= 0; j--) {
|
|
723
|
+
if (geminiMessages[j].role === "assistant" && geminiMessages[j].tool_calls) {
|
|
724
|
+
assistantMsg = geminiMessages[j];
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
const toolCall = (_c = assistantMsg == null ? void 0 : assistantMsg.tool_calls) == null ? void 0 : _c.find((tc) => tc.id === msg.tool_call_id);
|
|
729
|
+
pendingToolParts.push({
|
|
730
|
+
functionResponse: {
|
|
731
|
+
name: ((_d = toolCall == null ? void 0 : toolCall.function) == null ? void 0 : _d.name) || "unknown_tool",
|
|
732
|
+
response: { content: msg.content }
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
const nextMsg = geminiMessages[index + 1];
|
|
736
|
+
if (!nextMsg || nextMsg.role !== "tool") {
|
|
737
|
+
if (options.responseFormat === "json" || ((_e = options.responseFormat) == null ? void 0 : _e.type) === "json_schema" || options.responseSchema) {
|
|
738
|
+
pendingToolParts.push({ text: "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]" });
|
|
739
|
+
} else {
|
|
740
|
+
pendingToolParts.push({ text: "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]" });
|
|
741
|
+
}
|
|
742
|
+
contents.push({ role: "user", parts: pendingToolParts });
|
|
743
|
+
pendingToolParts = [];
|
|
744
|
+
}
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (pendingToolParts.length > 0) {
|
|
748
|
+
contents.push({ role: "user", parts: pendingToolParts });
|
|
749
|
+
pendingToolParts = [];
|
|
750
|
+
}
|
|
665
751
|
let role = "";
|
|
666
752
|
let parts2;
|
|
667
753
|
switch (msg.role) {
|
|
@@ -670,7 +756,7 @@ ${msg.content}`;
|
|
|
670
756
|
parts2 = [{ text: msg.content }];
|
|
671
757
|
if (index === geminiMessages.length - 1) {
|
|
672
758
|
let reminder = "";
|
|
673
|
-
if (options.responseFormat === "json" || ((
|
|
759
|
+
if (options.responseFormat === "json" || ((_f = options.responseFormat) == null ? void 0 : _f.type) === "json_schema" || options.responseSchema) {
|
|
674
760
|
reminder = "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]";
|
|
675
761
|
} else {
|
|
676
762
|
reminder = "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]";
|
|
@@ -704,27 +790,11 @@ ${msg.content}`;
|
|
|
704
790
|
parts2 = [part];
|
|
705
791
|
}
|
|
706
792
|
break;
|
|
707
|
-
case "tool":
|
|
708
|
-
role = "user";
|
|
709
|
-
const preceding_message = messages[index - 1];
|
|
710
|
-
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);
|
|
711
|
-
parts2 = [{
|
|
712
|
-
functionResponse: {
|
|
713
|
-
name: ((_c2 = tool_call == null ? void 0 : tool_call.function) == null ? void 0 : _c2.name) || "unknown_tool",
|
|
714
|
-
response: { content: msg.content }
|
|
715
|
-
}
|
|
716
|
-
}];
|
|
717
|
-
if (options.responseFormat === "json" || ((_d2 = options.responseFormat) == null ? void 0 : _d2.type) === "json_schema" || options.responseSchema) {
|
|
718
|
-
parts2.push({ text: "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]" });
|
|
719
|
-
} else {
|
|
720
|
-
parts2.push({ text: "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]" });
|
|
721
|
-
}
|
|
722
|
-
break;
|
|
723
793
|
default:
|
|
724
|
-
|
|
794
|
+
continue;
|
|
725
795
|
}
|
|
726
|
-
|
|
727
|
-
}
|
|
796
|
+
contents.push({ role, parts: parts2 });
|
|
797
|
+
}
|
|
728
798
|
while (contents.length > 0 && contents[0].role !== "user") {
|
|
729
799
|
contents.shift();
|
|
730
800
|
}
|
|
@@ -742,23 +812,17 @@ ${msg.content}`;
|
|
|
742
812
|
if (tools && tools.length > 0) {
|
|
743
813
|
requestOptions.config.tools = [{ functionDeclarations: tools.map((t) => t.function) }];
|
|
744
814
|
if (requestOptions.config.responseMimeType === "application/json") {
|
|
745
|
-
console.warn(
|
|
815
|
+
console.warn(`[${this.constructor.name}] Disabling strict JSON mode because tools are present. Relying on system prompt.`);
|
|
746
816
|
delete requestOptions.config.responseMimeType;
|
|
747
817
|
delete requestOptions.config.responseSchema;
|
|
748
818
|
}
|
|
749
819
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
response = await this.client.models.generateContent(requestOptions);
|
|
753
|
-
} catch (error) {
|
|
754
|
-
console.error(`[GeminiProvider] generateContent failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
755
|
-
throw error;
|
|
756
|
-
}
|
|
757
|
-
const candidate = (_c = response.candidates) == null ? void 0 : _c[0];
|
|
820
|
+
const response = await this._generateContent(requestOptions);
|
|
821
|
+
const candidate = (_g = response.candidates) == null ? void 0 : _g[0];
|
|
758
822
|
if (!candidate) {
|
|
759
823
|
throw new LLMServiceException("No candidates returned from model", 500);
|
|
760
824
|
}
|
|
761
|
-
const parts = ((
|
|
825
|
+
const parts = ((_h = candidate.content) == null ? void 0 : _h.parts) || [];
|
|
762
826
|
let textContent = "";
|
|
763
827
|
let toolCalls = null;
|
|
764
828
|
let responseThoughtSignature = null;
|
|
@@ -783,10 +847,8 @@ ${msg.content}`;
|
|
|
783
847
|
}
|
|
784
848
|
}
|
|
785
849
|
if (!textContent && (!toolCalls || toolCalls.length === 0)) {
|
|
786
|
-
console.error(
|
|
787
|
-
console.error(
|
|
788
|
-
console.error("[GeminiProvider] Safety Ratings:", JSON.stringify(candidate.safetyRatings, null, 2));
|
|
789
|
-
console.error("[GeminiProvider] Full Candidate:", JSON.stringify(candidate, null, 2));
|
|
850
|
+
console.error(`[${this.constructor.name}] Model returned empty response (no text, no tool calls)`);
|
|
851
|
+
console.error(`[${this.constructor.name}] Finish Reason:`, candidate.finishReason);
|
|
790
852
|
throw new LLMServiceException(
|
|
791
853
|
`Model returned empty response. Finish Reason: ${candidate.finishReason}.`,
|
|
792
854
|
500
|
|
@@ -796,22 +858,18 @@ ${msg.content}`;
|
|
|
796
858
|
return {
|
|
797
859
|
content: textContent,
|
|
798
860
|
thought_signature: responseThoughtSignature,
|
|
799
|
-
// Return signature to caller
|
|
800
861
|
tool_calls: toolCalls ? (Array.isArray(toolCalls) ? toolCalls : [toolCalls]).map((fc) => ({
|
|
801
862
|
type: "function",
|
|
802
863
|
function: fc,
|
|
803
864
|
thought_signature: fc.thought_signature
|
|
804
865
|
})) : null,
|
|
805
866
|
finishReason: normalizedFinishReason,
|
|
806
|
-
// Standardized: 'completed', 'truncated', etc.
|
|
807
867
|
_rawFinishReason: candidate.finishReason,
|
|
808
|
-
// Keep original for debugging
|
|
809
868
|
_responseFormat: options.responseFormat,
|
|
810
|
-
// Return usage stats
|
|
811
869
|
usage: {
|
|
812
|
-
prompt_tokens: ((
|
|
813
|
-
completion_tokens: ((
|
|
814
|
-
total_tokens: ((
|
|
870
|
+
prompt_tokens: ((_i = response.usageMetadata) == null ? void 0 : _i.promptTokenCount) || 0,
|
|
871
|
+
completion_tokens: ((_j = response.usageMetadata) == null ? void 0 : _j.candidatesTokenCount) || 0,
|
|
872
|
+
total_tokens: ((_k = response.usageMetadata) == null ? void 0 : _k.totalTokenCount) || 0
|
|
815
873
|
},
|
|
816
874
|
...options.responseFormat && this._shouldAutoParse(options) ? {
|
|
817
875
|
parsedContent: this._safeJsonParse(textContent)
|
|
@@ -833,7 +891,7 @@ ${msg.content}`;
|
|
|
833
891
|
if (schema) {
|
|
834
892
|
config.responseSchema = this._convertToGeminiSchema(schema);
|
|
835
893
|
} else {
|
|
836
|
-
console.warn(
|
|
894
|
+
console.warn(`[${this.constructor.name}] Using legacy JSON mode without schema - may produce markdown wrappers`);
|
|
837
895
|
}
|
|
838
896
|
}
|
|
839
897
|
}
|
|
@@ -891,8 +949,7 @@ ${msg.content}`;
|
|
|
891
949
|
if (!content) return null;
|
|
892
950
|
const parsed = extractJsonFromResponse(content);
|
|
893
951
|
if (!parsed) {
|
|
894
|
-
console.error(
|
|
895
|
-
console.error("[GeminiProvider] Content preview:", content.substring(0, 200));
|
|
952
|
+
console.error(`[${this.constructor.name}] Failed to extract valid JSON from response`);
|
|
896
953
|
}
|
|
897
954
|
return parsed;
|
|
898
955
|
}
|
|
@@ -922,9 +979,9 @@ ${msg.content}`;
|
|
|
922
979
|
toolResults.forEach((result) => messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.output }));
|
|
923
980
|
}
|
|
924
981
|
async imageGeneration(prompt, systemPrompt, options = {}) {
|
|
925
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i
|
|
982
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
926
983
|
const modelName = options.model || this.models.image || "gemini-3-pro-image-preview";
|
|
927
|
-
console.log(`[
|
|
984
|
+
console.log(`[${this.constructor.name}] Generating image with model: ${modelName}`);
|
|
928
985
|
const hasReferenceImages = options.images && options.images.length > 0;
|
|
929
986
|
const generationConfig = {
|
|
930
987
|
responseModalities: hasReferenceImages ? ["TEXT", "IMAGE"] : ["IMAGE"]
|
|
@@ -956,7 +1013,7 @@ ${msg.content}`;
|
|
|
956
1013
|
if (systemPrompt) {
|
|
957
1014
|
requestOptions.config.systemInstruction = { parts: [{ text: systemPrompt }] };
|
|
958
1015
|
}
|
|
959
|
-
const response = await this.
|
|
1016
|
+
const response = await this._generateContent(requestOptions);
|
|
960
1017
|
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(
|
|
961
1018
|
(part) => {
|
|
962
1019
|
var _a2;
|
|
@@ -964,30 +1021,21 @@ ${msg.content}`;
|
|
|
964
1021
|
}
|
|
965
1022
|
);
|
|
966
1023
|
if (!imagePart || !imagePart.inlineData) {
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
console.error("[GeminiProvider] Image generation failed (no image data)");
|
|
970
|
-
if (candidate) {
|
|
971
|
-
console.error("[GeminiProvider] Finish Reason:", candidate.finishReason);
|
|
972
|
-
console.error("[GeminiProvider] Safety Ratings:", JSON.stringify(candidate.safetyRatings, null, 2));
|
|
973
|
-
console.error("[GeminiProvider] Full Candidate:", JSON.stringify(candidate, null, 2));
|
|
974
|
-
}
|
|
975
|
-
if (textPart) {
|
|
976
|
-
console.warn("[GeminiProvider] Model returned text instead of image:", textPart.text);
|
|
977
|
-
}
|
|
1024
|
+
const candidate = (_e = response.candidates) == null ? void 0 : _e[0];
|
|
1025
|
+
console.error(`[${this.constructor.name}] Image generation failed (no image data)`);
|
|
978
1026
|
throw new Error(`No image data in response. Finish Reason: ${candidate == null ? void 0 : candidate.finishReason}`);
|
|
979
1027
|
}
|
|
980
1028
|
let thoughtSignature = null;
|
|
981
1029
|
if (imagePart.thought_signature || imagePart.thoughtSignature) {
|
|
982
1030
|
thoughtSignature = imagePart.thought_signature || imagePart.thoughtSignature;
|
|
983
1031
|
} else {
|
|
984
|
-
const signaturePart = (
|
|
1032
|
+
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);
|
|
985
1033
|
if (signaturePart) {
|
|
986
1034
|
thoughtSignature = signaturePart.thought_signature || signaturePart.thoughtSignature;
|
|
987
1035
|
}
|
|
988
1036
|
}
|
|
989
1037
|
if (thoughtSignature && thoughtSignature.length > 5e4) {
|
|
990
|
-
console.warn(`[
|
|
1038
|
+
console.warn(`[${this.constructor.name}] \u26A0\uFE0F Thought signature is abnormally large (${thoughtSignature.length} chars). Replacing with bypass token.`);
|
|
991
1039
|
thoughtSignature = "skip_thought_signature_validator";
|
|
992
1040
|
}
|
|
993
1041
|
return {
|
|
@@ -1010,31 +1058,20 @@ ${prompt}` : prompt;
|
|
|
1010
1058
|
durationSeconds: options.durationSeconds || 6,
|
|
1011
1059
|
aspectRatio: options.aspectRatio || "16:9",
|
|
1012
1060
|
numberOfVideos: 1,
|
|
1013
|
-
// Pass reference images if provided
|
|
1014
1061
|
...images && images.length > 0 ? { referenceImages: images } : {}
|
|
1015
1062
|
}
|
|
1016
1063
|
};
|
|
1017
|
-
const logConfig = JSON.parse(JSON.stringify(requestConfig));
|
|
1018
|
-
if (logConfig.config && logConfig.config.referenceImages) {
|
|
1019
|
-
logConfig.config.referenceImages = logConfig.config.referenceImages.map((img) => ({
|
|
1020
|
-
...img,
|
|
1021
|
-
data: `... (${img.data ? img.data.length : 0} bytes)`
|
|
1022
|
-
// Summarize data
|
|
1023
|
-
}));
|
|
1024
|
-
}
|
|
1025
|
-
console.log("[GeminiProvider] startVideoGeneration request:", JSON.stringify(logConfig, null, 2));
|
|
1026
1064
|
try {
|
|
1027
1065
|
const operation = await this.client.models.generateVideos(requestConfig);
|
|
1028
1066
|
this._pendingOperations.set(operation.name, operation);
|
|
1029
1067
|
return { operationName: operation.name };
|
|
1030
1068
|
} catch (error) {
|
|
1031
|
-
console.error(`[
|
|
1069
|
+
console.error(`[${this.constructor.name}] startVideoGeneration failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
1032
1070
|
throw error;
|
|
1033
1071
|
}
|
|
1034
1072
|
}
|
|
1035
1073
|
async getVideoGenerationStatus(operationName) {
|
|
1036
1074
|
var _a, _b, _c, _d, _e, _f;
|
|
1037
|
-
console.log(`[GeminiProvider] Checking status for operation: ${operationName}`);
|
|
1038
1075
|
let operation = this._pendingOperations.get(operationName);
|
|
1039
1076
|
if (!operation) {
|
|
1040
1077
|
operation = await this.client.models.getOperation(operationName);
|
|
@@ -1046,11 +1083,9 @@ ${prompt}` : prompt;
|
|
|
1046
1083
|
progress: ((_a = operation.metadata) == null ? void 0 : _a.progressPercent) || 0,
|
|
1047
1084
|
state: ((_b = operation.metadata) == null ? void 0 : _b.state) || (operation.done ? "COMPLETED" : "PROCESSING")
|
|
1048
1085
|
};
|
|
1049
|
-
console.log(`[GeminiProvider] Operation status: ${result.state}, Progress: ${result.progress}%`);
|
|
1050
1086
|
if (operation.done) {
|
|
1051
1087
|
this._pendingOperations.delete(operationName);
|
|
1052
1088
|
if (operation.error) {
|
|
1053
|
-
console.error("[GeminiProvider] Video generation failed:", JSON.stringify(operation.error, null, 2));
|
|
1054
1089
|
result.error = operation.error;
|
|
1055
1090
|
} else {
|
|
1056
1091
|
const videoResult = operation.response;
|
|
@@ -1062,20 +1097,17 @@ ${prompt}` : prompt;
|
|
|
1062
1097
|
}
|
|
1063
1098
|
async startDeepResearch(prompt, options = {}) {
|
|
1064
1099
|
const agent = options.agent || "deep-research-pro-preview-12-2025";
|
|
1065
|
-
console.log(`[
|
|
1100
|
+
console.log(`[${this.constructor.name}] Starting Deep Research with agent: ${agent}`);
|
|
1066
1101
|
try {
|
|
1067
1102
|
const interaction = await this.client.interactions.create({
|
|
1068
1103
|
agent,
|
|
1069
1104
|
input: prompt,
|
|
1070
1105
|
background: true,
|
|
1071
|
-
// Required for long running
|
|
1072
1106
|
store: true
|
|
1073
|
-
// Required for polling
|
|
1074
1107
|
});
|
|
1075
|
-
console.log(`[GeminiProvider] Deep Research started. Interaction ID: ${interaction.id}`);
|
|
1076
1108
|
return { operationId: interaction.id };
|
|
1077
1109
|
} catch (error) {
|
|
1078
|
-
console.error(`[
|
|
1110
|
+
console.error(`[${this.constructor.name}] startDeepResearch failed:`, error);
|
|
1079
1111
|
throw error;
|
|
1080
1112
|
}
|
|
1081
1113
|
}
|
|
@@ -1094,18 +1126,10 @@ ${prompt}` : prompt;
|
|
|
1094
1126
|
}
|
|
1095
1127
|
return response;
|
|
1096
1128
|
} catch (error) {
|
|
1097
|
-
console.error(`[
|
|
1129
|
+
console.error(`[${this.constructor.name}] getDeepResearchStatus failed for ${operationId}:`, error);
|
|
1098
1130
|
throw error;
|
|
1099
1131
|
}
|
|
1100
1132
|
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Extract structured data from a file (PDF, Image, etc.) using Gemini Multimodal capabilities.
|
|
1103
|
-
* @param {Buffer|string} fileData - Base64 string or Buffer of the file
|
|
1104
|
-
* @param {string} mimeType - Mime type (e.g., 'application/pdf', 'image/png')
|
|
1105
|
-
* @param {string} prompt - Extraction prompt
|
|
1106
|
-
* @param {Object} schema - JSON schema for the output
|
|
1107
|
-
* @param {Object} options - Additional options
|
|
1108
|
-
*/
|
|
1109
1133
|
async extractWithLLM(fileData, mimeType, prompt, schema = null, options = {}) {
|
|
1110
1134
|
var _a, _b, _c, _d;
|
|
1111
1135
|
const tier = options.tier || "default";
|
|
@@ -1117,9 +1141,7 @@ ${prompt}` : prompt;
|
|
|
1117
1141
|
maxTokens,
|
|
1118
1142
|
temperature
|
|
1119
1143
|
);
|
|
1120
|
-
const parts = [
|
|
1121
|
-
{ text: prompt }
|
|
1122
|
-
];
|
|
1144
|
+
const parts = [{ text: prompt }];
|
|
1123
1145
|
let base64Data = fileData;
|
|
1124
1146
|
if (typeof fileData !== "string") {
|
|
1125
1147
|
try {
|
|
@@ -1142,7 +1164,7 @@ ${prompt}` : prompt;
|
|
|
1142
1164
|
config: generationConfig
|
|
1143
1165
|
};
|
|
1144
1166
|
try {
|
|
1145
|
-
const response = await this.
|
|
1167
|
+
const response = await this._generateContent(requestOptions);
|
|
1146
1168
|
const candidate = (_a = response.candidates) == null ? void 0 : _a[0];
|
|
1147
1169
|
if (!candidate) {
|
|
1148
1170
|
throw new LLMServiceException("No candidates returned from model during extraction", 500);
|
|
@@ -1153,7 +1175,7 @@ ${prompt}` : prompt;
|
|
|
1153
1175
|
}
|
|
1154
1176
|
return textContent;
|
|
1155
1177
|
} catch (error) {
|
|
1156
|
-
console.error(`[
|
|
1178
|
+
console.error(`[${this.constructor.name}] extractWithLLM failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
1157
1179
|
throw error;
|
|
1158
1180
|
}
|
|
1159
1181
|
}
|
|
@@ -1172,14 +1194,14 @@ var LLMService = class {
|
|
|
1172
1194
|
return this.providerCache.get(cacheKey);
|
|
1173
1195
|
}
|
|
1174
1196
|
const config = await ConfigManager.getConfig(tenantId, this.env);
|
|
1175
|
-
if (!config.apiKey) {
|
|
1197
|
+
if (!config.apiKey && config.provider !== "vertex") {
|
|
1176
1198
|
throw new LLMServiceException(`LLM service is not configured for ${config.provider}. Missing API Key.`, 500);
|
|
1177
1199
|
}
|
|
1178
1200
|
let provider;
|
|
1179
1201
|
if (config.provider === "openai") {
|
|
1180
1202
|
provider = new OpenAIProvider(config);
|
|
1181
|
-
} else if (config.provider === "gemini") {
|
|
1182
|
-
provider = new
|
|
1203
|
+
} else if (config.provider === "gemini" || config.provider === "vertex") {
|
|
1204
|
+
provider = new GoogleProvider(config);
|
|
1183
1205
|
} else {
|
|
1184
1206
|
throw new LLMServiceException(`Unsupported LLM provider: ${config.provider}`, 500);
|
|
1185
1207
|
}
|
|
@@ -1753,12 +1775,14 @@ function createSpeechHandler(app, getConfig) {
|
|
|
1753
1775
|
DefaultConfigProvider,
|
|
1754
1776
|
FINISH_REASONS,
|
|
1755
1777
|
GeminiProvider,
|
|
1778
|
+
GoogleProvider,
|
|
1756
1779
|
LLMService,
|
|
1757
1780
|
LLMServiceException,
|
|
1758
1781
|
MODEL_CONFIGS,
|
|
1759
1782
|
OpenAIProvider,
|
|
1760
1783
|
TranscriptionService,
|
|
1761
1784
|
TranscriptionServiceException,
|
|
1785
|
+
VertexProvider,
|
|
1762
1786
|
createSpeechHandler,
|
|
1763
1787
|
extractJsonFromResponse,
|
|
1764
1788
|
extractTextAndJson,
|