@contentgrowth/llm-service 0.7.0 → 0.7.2
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/package.json
CHANGED
|
@@ -90,6 +90,26 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
90
90
|
case 'user':
|
|
91
91
|
role = 'user';
|
|
92
92
|
parts = [{ text: msg.content }];
|
|
93
|
+
|
|
94
|
+
// Enhancement: If this is the LAST message (current turn), append the reminder.
|
|
95
|
+
// This helps the model respect the system prompt (especially format) even with long context history.
|
|
96
|
+
if (index === geminiMessages.length - 1) {
|
|
97
|
+
let reminder = "";
|
|
98
|
+
if (options.responseFormat === 'json' || options.responseFormat?.type === 'json_schema' || options.responseSchema) {
|
|
99
|
+
reminder = "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]";
|
|
100
|
+
} else {
|
|
101
|
+
reminder = "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Append to the existing text part (Safest method)
|
|
105
|
+
const lastPart = parts.find(p => p.text);
|
|
106
|
+
if (lastPart) {
|
|
107
|
+
lastPart.text += reminder;
|
|
108
|
+
} else {
|
|
109
|
+
// Fallback if message was image-only
|
|
110
|
+
parts.push({ text: reminder });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
93
113
|
break;
|
|
94
114
|
case 'assistant':
|
|
95
115
|
role = 'model';
|
|
@@ -115,11 +135,11 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
115
135
|
// Fix for JSON mode: If JSON is requested, remind the model to output JSON after tool execution
|
|
116
136
|
// This is necessary because strict JSON mode is disabled when tools are present.
|
|
117
137
|
if (options.responseFormat === 'json' || options.responseFormat?.type === 'json_schema' || options.responseSchema) {
|
|
118
|
-
parts.push({ text: "
|
|
138
|
+
parts.push({ text: "\n\n[SYSTEM NOTE: The output MUST be valid JSON as per the schema. Do not include markdown formatting or explanations.]" });
|
|
119
139
|
} else {
|
|
120
140
|
// Generic reminder to help model stay on track with system prompt instructions (e.g. formatting)
|
|
121
141
|
// even if no specific JSON mode is configured.
|
|
122
|
-
parts.push({ text: "Please ensure the
|
|
142
|
+
parts.push({ text: "\n\n[SYSTEM NOTE: Please ensure your response adheres strictly to the constraints defined in the System Prompt.]" });
|
|
123
143
|
}
|
|
124
144
|
break;
|
|
125
145
|
default:
|
|
@@ -160,7 +180,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
|
|
163
|
-
console.log('[GeminiProvider] generateContent request:', JSON.stringify(requestOptions, null, 2));
|
|
183
|
+
// console.log('[GeminiProvider] generateContent request:', JSON.stringify(requestOptions, null, 2));
|
|
164
184
|
|
|
165
185
|
let response;
|
|
166
186
|
try {
|
|
@@ -208,7 +228,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
208
228
|
);
|
|
209
229
|
}
|
|
210
230
|
|
|
211
|
-
console.log('Gemini returns:', textContent);
|
|
231
|
+
// console.log('Gemini returns:', textContent);
|
|
212
232
|
// Return with parsed JSON if applicable
|
|
213
233
|
return {
|
|
214
234
|
content: textContent,
|
|
@@ -245,7 +265,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
245
265
|
// Use responseSchema for strict structured output
|
|
246
266
|
// Must convert to Gemini Schema format (Uppercase types)
|
|
247
267
|
config.responseSchema = this._convertToGeminiSchema(schema);
|
|
248
|
-
console.log('[GeminiProvider] Using Strict JSON mode with schema (responseSchema)');
|
|
268
|
+
// console.log('[GeminiProvider] Using Strict JSON mode with schema (responseSchema)');
|
|
249
269
|
} else {
|
|
250
270
|
console.warn('[GeminiProvider] Using legacy JSON mode without schema - may produce markdown wrappers');
|
|
251
271
|
}
|
|
@@ -316,9 +336,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
316
336
|
// - Brace extraction as fallback
|
|
317
337
|
const parsed = extractJsonFromResponse(content);
|
|
318
338
|
|
|
319
|
-
if (parsed) {
|
|
320
|
-
console.log('[GeminiProvider] Successfully parsed JSON from response');
|
|
321
|
-
} else {
|
|
339
|
+
if (!parsed) {
|
|
322
340
|
console.error('[GeminiProvider] Failed to extract valid JSON from response');
|
|
323
341
|
console.error('[GeminiProvider] Content preview:', content.substring(0, 200));
|
|
324
342
|
}
|
|
@@ -334,7 +352,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
334
352
|
const tool_call_id = `gemini-tool-call-${index}`;
|
|
335
353
|
toolCall.id = tool_call_id;
|
|
336
354
|
|
|
337
|
-
console.log(`[Tool Call] ${toolName} with arguments:`, toolCall.function.args);
|
|
355
|
+
// console.log(`[Tool Call] ${toolName} with arguments:`, toolCall.function.args);
|
|
338
356
|
|
|
339
357
|
if (!tool) {
|
|
340
358
|
console.error(`[Tool Error] Tool '${toolName}' not found`);
|
|
@@ -342,7 +360,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
342
360
|
}
|
|
343
361
|
try {
|
|
344
362
|
const output = await tool(toolCall.function.args, { env, tenantId });
|
|
345
|
-
console.log(`[Tool Result] ${toolName} returned:`, output.substring(0, 200) + (output.length > 200 ? '...' : ''));
|
|
363
|
+
// console.log(`[Tool Result] ${toolName} returned:`, output.substring(0, 200) + (output.length > 200 ? '...' : ''));
|
|
346
364
|
return { tool_call_id, output };
|
|
347
365
|
} catch (error) {
|
|
348
366
|
console.error(`[Tool Error] ${toolName} failed:`, error.message);
|
|
@@ -391,7 +409,7 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
391
409
|
requestOptions.config.systemInstruction = { parts: [{ text: systemPrompt }] };
|
|
392
410
|
}
|
|
393
411
|
|
|
394
|
-
console.log('[GeminiProvider] imageGeneration request:', JSON.stringify(requestOptions, null, 2));
|
|
412
|
+
// console.log('[GeminiProvider] imageGeneration request:', JSON.stringify(requestOptions, null, 2));
|
|
395
413
|
|
|
396
414
|
const response = await this.client.models.generateContent(requestOptions);
|
|
397
415
|
|
|
@@ -429,9 +447,12 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
429
447
|
|
|
430
448
|
async startVideoGeneration(prompt, images, modelName, systemPrompt, options = {}) {
|
|
431
449
|
// Use unified client for video generation
|
|
432
|
-
|
|
450
|
+
// Prepend system prompt to user prompt if provided, as video models often expect instructions in the prompt
|
|
451
|
+
const effectivePrompt = systemPrompt ? `${systemPrompt}\n\n${prompt}` : prompt;
|
|
452
|
+
|
|
453
|
+
const requestConfig = {
|
|
433
454
|
model: modelName,
|
|
434
|
-
prompt:
|
|
455
|
+
prompt: effectivePrompt,
|
|
435
456
|
config: {
|
|
436
457
|
durationSeconds: options.durationSeconds || 6,
|
|
437
458
|
aspectRatio: options.aspectRatio || '16:9',
|
|
@@ -439,15 +460,35 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
439
460
|
// Pass reference images if provided
|
|
440
461
|
...(images && images.length > 0 ? { referenceImages: images } : {}),
|
|
441
462
|
}
|
|
442
|
-
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// Create a loggable copy of the config
|
|
466
|
+
const logConfig = JSON.parse(JSON.stringify(requestConfig));
|
|
467
|
+
if (logConfig.config && logConfig.config.referenceImages) {
|
|
468
|
+
logConfig.config.referenceImages = logConfig.config.referenceImages.map(img => ({
|
|
469
|
+
...img,
|
|
470
|
+
data: `... (${img.data ? img.data.length : 0} bytes)` // Summarize data
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.log('[GeminiProvider] startVideoGeneration request:', JSON.stringify(logConfig, null, 2));
|
|
443
475
|
|
|
444
|
-
|
|
445
|
-
|
|
476
|
+
try {
|
|
477
|
+
const operation = await this.client.models.generateVideos(requestConfig);
|
|
446
478
|
|
|
447
|
-
|
|
479
|
+
// Store operation for later polling
|
|
480
|
+
this._pendingOperations.set(operation.name, operation);
|
|
481
|
+
|
|
482
|
+
return { operationName: operation.name };
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error('[GeminiProvider] startVideoGeneration failed:', error);
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
448
487
|
}
|
|
449
488
|
|
|
450
489
|
async getVideoGenerationStatus(operationName) {
|
|
490
|
+
console.log(`[GeminiProvider] Checking status for operation: ${operationName}`);
|
|
491
|
+
|
|
451
492
|
// Get the operation from cache or fetch it
|
|
452
493
|
let operation = this._pendingOperations.get(operationName);
|
|
453
494
|
|
|
@@ -468,6 +509,8 @@ export class GeminiProvider extends BaseLLMProvider {
|
|
|
468
509
|
state: operation.metadata?.state || (operation.done ? 'COMPLETED' : 'PROCESSING'),
|
|
469
510
|
};
|
|
470
511
|
|
|
512
|
+
console.log(`[GeminiProvider] Operation status: ${result.state}, Progress: ${result.progress}%`);
|
|
513
|
+
|
|
471
514
|
if (operation.done) {
|
|
472
515
|
// Clean up from cache
|
|
473
516
|
this._pendingOperations.delete(operationName);
|
package/src/llm-service.js
CHANGED
|
@@ -159,18 +159,19 @@ export class LLMService {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
async chatWithTools(messages, tenantId, systemPrompt, tools = []) {
|
|
162
|
+
async chatWithTools(messages, tenantId, systemPrompt, tools = [], options = {}) {
|
|
163
163
|
const provider = await this._getProvider(tenantId);
|
|
164
164
|
|
|
165
165
|
let currentMessages = [...messages];
|
|
166
166
|
const MAX_ITERATIONS = 10; // Prevent infinite loops
|
|
167
167
|
let iteration = 0;
|
|
168
168
|
|
|
169
|
-
// Initial call
|
|
169
|
+
// Initial call
|
|
170
170
|
const initialResponse = await provider.chatCompletion(
|
|
171
171
|
currentMessages,
|
|
172
172
|
systemPrompt,
|
|
173
|
-
tools
|
|
173
|
+
tools,
|
|
174
|
+
options
|
|
174
175
|
);
|
|
175
176
|
|
|
176
177
|
let { content, tool_calls, parsedContent } = initialResponse;
|
|
@@ -184,11 +185,12 @@ export class LLMService {
|
|
|
184
185
|
// Execute tools using the provider's helper (which formats results for that provider)
|
|
185
186
|
await provider.executeTools(tool_calls, currentMessages, tenantId, this.toolImplementations, this.env);
|
|
186
187
|
|
|
187
|
-
// Next call
|
|
188
|
+
// Next call
|
|
188
189
|
const nextResponse = await provider.chatCompletion(
|
|
189
190
|
currentMessages,
|
|
190
191
|
systemPrompt,
|
|
191
|
-
tools
|
|
192
|
+
tools,
|
|
193
|
+
options
|
|
192
194
|
);
|
|
193
195
|
|
|
194
196
|
content = nextResponse.content;
|