@contentgrowth/llm-service 0.6.6 → 0.6.8

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentgrowth/llm-service",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "Unified LLM Service for Content Growth",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -46,4 +46,26 @@ export class BaseLLMProvider {
46
46
  async imageGeneration(prompt, modelName, systemPrompt, options) {
47
47
  throw new Error('Image generation not supported by this provider');
48
48
  }
49
+
50
+ /**
51
+ * Start video generation (returns operation name for polling)
52
+ * @param {string} prompt
53
+ * @param {Array} images
54
+ * @param {string} modelName
55
+ * @param {string} systemPrompt
56
+ * @param {Object} options
57
+ * @returns {Promise<{operationName: string}>}
58
+ */
59
+ async startVideoGeneration(prompt, images, modelName, systemPrompt, options) {
60
+ throw new Error('Video generation not supported by this provider');
61
+ }
62
+
63
+ /**
64
+ * Get video generation status (poll operation)
65
+ * @param {string} operationName
66
+ * @returns {Promise<{done: boolean, progress: number, state: string, videoUri?: string, error?: object}>}
67
+ */
68
+ async getVideoGenerationStatus(operationName) {
69
+ throw new Error('Video generation not supported by this provider');
70
+ }
49
71
  }
@@ -327,8 +327,6 @@ export class GeminiProvider extends BaseLLMProvider {
327
327
  });
328
328
  }
329
329
 
330
- console.log('[GeminiProvider] Generating image with parts:', parts.map(p => p.text ? `Text: ${p.text.substring(0, 50)}...` : `Image: ${p.inlineData?.mimeType} (${p.inlineData?.data?.length} chars)`));
331
-
332
330
  const result = await model.generateContent({
333
331
  contents: [{
334
332
  role: "user",
@@ -356,47 +354,45 @@ export class GeminiProvider extends BaseLLMProvider {
356
354
  return this.models[tier] || this.models.default;
357
355
  }
358
356
 
359
- async videoGeneration(prompt, images, modelName, systemPrompt, options = {}) {
360
- const model = this.client.getGenerativeModel({
357
+ async startVideoGeneration(prompt, images, modelName, systemPrompt, options = {}) {
358
+ // 1. Initiate the request
359
+ const operation = await this.client.models.generateVideos({
361
360
  model: modelName,
362
- systemInstruction: systemPrompt,
363
- });
364
-
365
- // Prepare image parts
366
- const imageParts = images.map(img => ({
367
- inlineData: {
368
- data: img.data, // Base64 string
369
- mimeType: img.mimeType
361
+ prompt: prompt,
362
+ config: {
363
+ referenceImages: images,
370
364
  }
371
- }));
372
-
373
- const result = await model.generateContent({
374
- contents: [{
375
- role: "user",
376
- parts: [
377
- { text: prompt },
378
- ...imageParts
379
- ]
380
- }]
381
365
  });
382
366
 
383
- const response = result.response;
384
-
385
- // Check for video attachment/URI in the response
386
- // This structure depends on the specific API response for Veo
387
- // Assuming it might return a file URI or a specific part type
367
+ return { operationName: operation.name };
368
+ }
388
369
 
389
- // Fallback: Return text if no specific video part is found,
390
- // but try to find a URI in the text if possible.
391
- const text = response.text();
370
+ async getVideoGenerationStatus(operationName) {
371
+ // 2. Get operation status
372
+ // Assuming the SDK supports retrieving operation by name via this.client.models.getOperation
373
+ // If not, we might need to adjust based on the specific SDK version.
374
+ const operation = await this.client.models.getOperation(operationName);
392
375
 
393
- // TODO: Update this once Veo API response structure is fully documented/available
394
- // For now, we return the text which might contain the URI or status.
376
+ // Refresh status
377
+ await operation.get();
395
378
 
396
- return {
397
- content: text,
398
- // potential video URI extraction
399
- videoUri: text.match(/https?:\/\/[^\s]+/) ? text.match(/https?:\/\/[^\s]+/)[0] : null
379
+ const result = {
380
+ done: operation.done,
381
+ // Extract progress if available in metadata
382
+ progress: operation.metadata?.progressPercent || 0,
383
+ state: operation.metadata?.state || (operation.done ? 'COMPLETED' : 'PROCESSING'),
400
384
  };
385
+
386
+ if (operation.done) {
387
+ if (operation.error) {
388
+ result.error = operation.error;
389
+ } else {
390
+ const videoResult = operation.response;
391
+ result.videoUri = videoResult.uri || (videoResult.generatedAssets && videoResult.generatedAssets[0] && videoResult.generatedAssets[0].uri);
392
+ result.content = "Video generation completed.";
393
+ }
394
+ }
395
+
396
+ return result;
401
397
  }
402
398
  }
@@ -205,11 +205,43 @@ export class LLMService {
205
205
  }
206
206
 
207
207
  /**
208
- * Generate a video
208
+ * Generate a video (async wrapper with polling - backward compatibility)
209
209
  */
210
210
  async videoGeneration(prompt, images, tenantId, modelName, systemPrompt, options = {}) {
211
+ const { operationName } = await this.startVideoGeneration(prompt, images, tenantId, modelName, systemPrompt, options);
212
+
213
+ let status = await this.getVideoGenerationStatus(operationName, tenantId);
214
+
215
+ while (!status.done) {
216
+ console.log(`Waiting for video generation... Progress: ${status.progress}%`);
217
+ await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
218
+ status = await this.getVideoGenerationStatus(operationName, tenantId);
219
+ }
220
+
221
+ if (status.error) {
222
+ throw new Error(`Video generation failed: ${status.error.message || JSON.stringify(status.error)}`);
223
+ }
224
+
225
+ return {
226
+ content: status.content || "Video generation completed.",
227
+ videoUri: status.videoUri
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Start video generation (returns operation name for polling)
233
+ */
234
+ async startVideoGeneration(prompt, images, tenantId, modelName, systemPrompt, options = {}) {
235
+ const provider = await this._getProvider(tenantId);
236
+ return provider.startVideoGeneration(prompt, images, modelName, systemPrompt, options);
237
+ }
238
+
239
+ /**
240
+ * Get video generation status
241
+ */
242
+ async getVideoGenerationStatus(operationName, tenantId) {
211
243
  const provider = await this._getProvider(tenantId);
212
- return provider.videoGeneration(prompt, images, modelName, systemPrompt, options);
244
+ return provider.getVideoGenerationStatus(operationName);
213
245
  }
214
246
 
215
247
  /**