@aj-archipelago/cortex 1.3.66 → 1.4.0

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.
Files changed (84) hide show
  1. package/config.js +27 -0
  2. package/helper-apps/cortex-autogen2/Dockerfile +88 -21
  3. package/helper-apps/cortex-autogen2/docker-compose.yml +15 -8
  4. package/helper-apps/cortex-autogen2/host.json +5 -0
  5. package/helper-apps/cortex-autogen2/pyproject.toml +82 -25
  6. package/helper-apps/cortex-autogen2/requirements.txt +84 -14
  7. package/helper-apps/cortex-autogen2/services/redis_publisher.py +129 -3
  8. package/helper-apps/cortex-autogen2/task_processor.py +432 -116
  9. package/helper-apps/cortex-autogen2/tools/__init__.py +2 -0
  10. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +32 -0
  11. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +50 -14
  12. package/helper-apps/cortex-autogen2/tools/file_tools.py +169 -44
  13. package/helper-apps/cortex-autogen2/tools/google_cse.py +117 -0
  14. package/helper-apps/cortex-autogen2/tools/search_tools.py +655 -98
  15. package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/__init__.py +3 -0
  16. package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/function.json +20 -0
  17. package/helper-apps/cortex-doc-to-pdf/Dockerfile +46 -0
  18. package/helper-apps/cortex-doc-to-pdf/README.md +408 -0
  19. package/helper-apps/cortex-doc-to-pdf/converter.py +157 -0
  20. package/helper-apps/cortex-doc-to-pdf/docker-compose.yml +23 -0
  21. package/helper-apps/cortex-doc-to-pdf/document_converter.py +181 -0
  22. package/helper-apps/cortex-doc-to-pdf/examples/README.md +252 -0
  23. package/helper-apps/cortex-doc-to-pdf/examples/nodejs-client.js +266 -0
  24. package/helper-apps/cortex-doc-to-pdf/examples/package-lock.json +297 -0
  25. package/helper-apps/cortex-doc-to-pdf/examples/package.json +23 -0
  26. package/helper-apps/cortex-doc-to-pdf/function_app.py +85 -0
  27. package/helper-apps/cortex-doc-to-pdf/host.json +16 -0
  28. package/helper-apps/cortex-doc-to-pdf/request_handlers.py +193 -0
  29. package/helper-apps/cortex-doc-to-pdf/requirements.txt +3 -0
  30. package/helper-apps/cortex-doc-to-pdf/tests/run_tests.sh +26 -0
  31. package/helper-apps/cortex-doc-to-pdf/tests/test_conversion.py +320 -0
  32. package/helper-apps/cortex-doc-to-pdf/tests/test_streaming.py +419 -0
  33. package/helper-apps/cortex-file-handler/package-lock.json +1 -0
  34. package/helper-apps/cortex-file-handler/package.json +1 -0
  35. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +81 -8
  36. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +54 -7
  37. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +19 -7
  38. package/lib/encodeCache.js +5 -0
  39. package/lib/keyValueStorageClient.js +5 -0
  40. package/lib/logger.js +1 -1
  41. package/lib/pathwayManager.js +42 -8
  42. package/lib/pathwayTools.js +8 -1
  43. package/lib/redisSubscription.js +6 -0
  44. package/lib/requestExecutor.js +4 -0
  45. package/lib/util.js +145 -1
  46. package/package.json +1 -1
  47. package/pathways/basePathway.js +3 -3
  48. package/pathways/bing_afagent.js +1 -0
  49. package/pathways/gemini_15_vision.js +1 -1
  50. package/pathways/google_cse.js +2 -2
  51. package/pathways/image_gemini_25.js +85 -0
  52. package/pathways/image_prompt_optimizer_gemini_25.js +149 -0
  53. package/pathways/image_qwen.js +28 -0
  54. package/pathways/image_seedream4.js +26 -0
  55. package/pathways/rag.js +1 -1
  56. package/pathways/rag_jarvis.js +1 -1
  57. package/pathways/system/entity/sys_entity_continue.js +1 -1
  58. package/pathways/system/entity/sys_generator_results.js +1 -1
  59. package/pathways/system/entity/tools/sys_tool_google_search.js +15 -2
  60. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +3 -3
  61. package/pathways/system/entity/tools/sys_tool_image.js +28 -23
  62. package/pathways/system/entity/tools/sys_tool_image_gemini.js +135 -0
  63. package/pathways/system/workspaces/run_workspace_prompt.js +0 -3
  64. package/server/executeWorkspace.js +381 -0
  65. package/server/graphql.js +14 -182
  66. package/server/modelExecutor.js +4 -0
  67. package/server/pathwayResolver.js +19 -18
  68. package/server/plugins/claude3VertexPlugin.js +13 -8
  69. package/server/plugins/gemini15ChatPlugin.js +15 -10
  70. package/server/plugins/gemini15VisionPlugin.js +2 -23
  71. package/server/plugins/gemini25ImagePlugin.js +155 -0
  72. package/server/plugins/modelPlugin.js +3 -2
  73. package/server/plugins/openAiChatPlugin.js +6 -6
  74. package/server/plugins/replicateApiPlugin.js +268 -12
  75. package/server/plugins/veoVideoPlugin.js +15 -1
  76. package/server/rest.js +2 -0
  77. package/server/typeDef.js +96 -10
  78. package/tests/integration/apptekTranslatePlugin.integration.test.js +1 -1
  79. package/tests/unit/core/parser.test.js +0 -1
  80. package/tests/unit/core/pathwayManager.test.js +2 -4
  81. package/tests/unit/core/pathwayManagerWithFiles.test.js +256 -0
  82. package/tests/unit/graphql_executeWorkspace_transformation.test.js +244 -0
  83. package/tests/unit/plugins/gemini25ImagePlugin.test.js +294 -0
  84. package/tests/unit/server/graphql.test.js +122 -1
@@ -1,5 +1,6 @@
1
1
  // replicateApiPlugin.js
2
2
  import ModelPlugin from "./modelPlugin.js";
3
+ import CortexResponse from "../../lib/cortexResponse.js";
3
4
  import logger from "../../lib/logger.js";
4
5
  import axios from "axios";
5
6
 
@@ -92,6 +93,122 @@ class ReplicateApiPlugin extends ModelPlugin {
92
93
  };
93
94
  break;
94
95
  }
96
+ case "replicate-qwen-image": {
97
+ const aspectRatio = combinedParameters.aspect_ratio ?? combinedParameters.aspectRatio ?? "16:9";
98
+ const imageSize = combinedParameters.image_size ?? combinedParameters.imageSize ?? "optimize_for_quality";
99
+ const outputFormat = combinedParameters.output_format ?? combinedParameters.outputFormat ?? "webp";
100
+ const outputQuality = combinedParameters.output_quality ?? combinedParameters.outputQuality ?? 80;
101
+ const loraScale = combinedParameters.lora_scale ?? combinedParameters.loraScale ?? 1;
102
+ const enhancePrompt = combinedParameters.enhance_prompt ?? combinedParameters.enhancePrompt ?? false;
103
+ const negativePrompt = combinedParameters.negative_prompt ?? combinedParameters.negativePrompt ?? " ";
104
+ const numInferenceSteps = combinedParameters.num_inference_steps ?? combinedParameters.steps ?? 50;
105
+ const goFast = combinedParameters.go_fast ?? combinedParameters.goFast ?? true;
106
+ const guidance = combinedParameters.guidance ?? 4;
107
+ const strength = combinedParameters.strength ?? 0.9;
108
+ const numOutputs = combinedParameters.num_outputs ?? combinedParameters.numberResults;
109
+ const disableSafetyChecker = combinedParameters.disable_safety_checker ?? combinedParameters.disableSafetyChecker ?? false;
110
+
111
+ requestParameters = {
112
+ input: {
113
+ prompt: modelPromptText,
114
+ go_fast: goFast,
115
+ guidance,
116
+ strength,
117
+ image_size: imageSize,
118
+ lora_scale: loraScale,
119
+ aspect_ratio: aspectRatio,
120
+ output_format: outputFormat,
121
+ enhance_prompt: enhancePrompt,
122
+ output_quality: outputQuality,
123
+ negative_prompt: negativePrompt,
124
+ num_inference_steps: numInferenceSteps,
125
+ disable_safety_checker: disableSafetyChecker,
126
+ ...(numOutputs ? { num_outputs: numOutputs } : {}),
127
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) ? { seed: combinedParameters.seed } : {}),
128
+ ...(combinedParameters.image ? { image: combinedParameters.image } : {}),
129
+ ...(combinedParameters.input_image ? { input_image: combinedParameters.input_image } : {}),
130
+ },
131
+ };
132
+ break;
133
+ }
134
+ case "replicate-qwen-image-edit-plus": {
135
+ const aspectRatio = combinedParameters.aspect_ratio ?? combinedParameters.aspectRatio ?? "match_input_image";
136
+ const outputFormat = combinedParameters.output_format ?? combinedParameters.outputFormat ?? "webp";
137
+ const outputQuality = combinedParameters.output_quality ?? combinedParameters.outputQuality ?? 95;
138
+ const goFast = combinedParameters.go_fast ?? combinedParameters.goFast ?? true;
139
+ const disableSafetyChecker = combinedParameters.disable_safety_checker ?? combinedParameters.disableSafetyChecker ?? false;
140
+
141
+ const collectImages = (candidate, accumulator) => {
142
+ if (!candidate) return;
143
+ if (Array.isArray(candidate)) {
144
+ candidate.forEach((item) => collectImages(item, accumulator));
145
+ return;
146
+ }
147
+ accumulator.push(candidate);
148
+ };
149
+
150
+ const imageCandidates = [];
151
+ collectImages(combinedParameters.image, imageCandidates);
152
+ collectImages(combinedParameters.images, imageCandidates);
153
+ collectImages(combinedParameters.input_image, imageCandidates);
154
+ collectImages(combinedParameters.input_images, imageCandidates);
155
+ collectImages(combinedParameters.input_image_1, imageCandidates);
156
+ collectImages(combinedParameters.input_image_2, imageCandidates);
157
+ collectImages(combinedParameters.input_image_3, imageCandidates);
158
+ collectImages(combinedParameters.image_1, imageCandidates);
159
+ collectImages(combinedParameters.image_2, imageCandidates);
160
+
161
+ const normalizeImageEntry = (entry) => {
162
+ if (!entry) return null;
163
+ if (typeof entry === "string") {
164
+ return entry; // Return the URL string directly
165
+ }
166
+ if (typeof entry === "object") {
167
+ if (Array.isArray(entry)) {
168
+ return null;
169
+ }
170
+ if (entry.value) {
171
+ return entry.value; // Return the value as a string
172
+ }
173
+ if (entry.url) {
174
+ return entry.url; // Return the URL as a string
175
+ }
176
+ if (entry.path) {
177
+ return entry.path; // Return the path as a string
178
+ }
179
+ }
180
+ return null;
181
+ };
182
+
183
+ const normalizedImages = imageCandidates
184
+ .map((candidate) => normalizeImageEntry(candidate))
185
+ .filter((candidate) => candidate && typeof candidate === 'string');
186
+
187
+ const omitUndefined = (obj) =>
188
+ Object.fromEntries(
189
+ Object.entries(obj).filter(([, value]) => value !== undefined && value !== null),
190
+ );
191
+
192
+ const basePayload = omitUndefined({
193
+ prompt: modelPromptText,
194
+ go_fast: goFast,
195
+ aspect_ratio: aspectRatio,
196
+ output_format: outputFormat,
197
+ output_quality: outputQuality,
198
+ disable_safety_checker: disableSafetyChecker,
199
+ });
200
+
201
+ // For qwen-image-edit-plus, always include the image array if we have images
202
+ const inputPayload = {
203
+ ...basePayload,
204
+ ...(normalizedImages.length > 0 ? { image: normalizedImages } : {})
205
+ };
206
+
207
+ requestParameters = {
208
+ input: inputPayload,
209
+ };
210
+ break;
211
+ }
95
212
  case "replicate-flux-kontext-pro":
96
213
  case "replicate-flux-kontext-max": {
97
214
  const validRatios = [
@@ -110,7 +227,7 @@ class ReplicateApiPlugin extends ModelPlugin {
110
227
  input_image: combinedParameters.input_image,
111
228
  aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "1:1",
112
229
  safety_tolerance: safetyTolerance,
113
- ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
230
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) && combinedParameters.seed > 0 ? { seed: combinedParameters.seed } : {}),
114
231
  },
115
232
  };
116
233
  break;
@@ -133,7 +250,7 @@ class ReplicateApiPlugin extends ModelPlugin {
133
250
  input_image_2: combinedParameters.input_image_2,
134
251
  aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "1:1",
135
252
  safety_tolerance: safetyTolerance,
136
- ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
253
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) && combinedParameters.seed > 0 ? { seed: combinedParameters.seed } : {}),
137
254
  },
138
255
  };
139
256
  break;
@@ -148,7 +265,7 @@ class ReplicateApiPlugin extends ModelPlugin {
148
265
  prompt: modelPromptText,
149
266
  resolution: validResolutions.includes(combinedParameters.resolution) ? combinedParameters.resolution : "1080p",
150
267
  aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "16:9",
151
- ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
268
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) && combinedParameters.seed > 0 ? { seed: combinedParameters.seed } : {}),
152
269
  fps: validFps.includes(combinedParameters.fps) ? combinedParameters.fps : 24,
153
270
  camera_fixed: combinedParameters.camera_fixed || false,
154
271
  duration: combinedParameters.duration || 5,
@@ -157,6 +274,86 @@ class ReplicateApiPlugin extends ModelPlugin {
157
274
  };
158
275
  break;
159
276
  }
277
+ case "replicate-seedream-4": {
278
+ const validSizes = ["1K", "2K", "4K", "custom"];
279
+ const validRatios = ["1:1", "4:3", "3:4", "16:9", "9:16", "match_input_image"];
280
+ const validSequentialModes = ["disabled", "auto"];
281
+
282
+ // Collect input images from multiple parameter sources (same pattern as qwen-image-edit-plus)
283
+ const collectImages = (candidate, accumulator) => {
284
+ if (!candidate) return;
285
+ if (Array.isArray(candidate)) {
286
+ candidate.forEach((item) => collectImages(item, accumulator));
287
+ return;
288
+ }
289
+ accumulator.push(candidate);
290
+ };
291
+
292
+ const imageCandidates = [];
293
+ collectImages(combinedParameters.image, imageCandidates);
294
+ collectImages(combinedParameters.images, imageCandidates);
295
+ collectImages(combinedParameters.input_image, imageCandidates);
296
+ collectImages(combinedParameters.input_images, imageCandidates);
297
+ collectImages(combinedParameters.input_image_1, imageCandidates);
298
+ collectImages(combinedParameters.input_image_2, imageCandidates);
299
+ collectImages(combinedParameters.input_image_3, imageCandidates);
300
+ collectImages(combinedParameters.image_1, imageCandidates);
301
+ collectImages(combinedParameters.image_2, imageCandidates);
302
+ collectImages(combinedParameters.imageInput, imageCandidates);
303
+
304
+ const normalizeImageEntry = (entry) => {
305
+ if (!entry) return null;
306
+ if (typeof entry === "string") {
307
+ return entry; // Return the URL string directly
308
+ }
309
+ if (typeof entry === "object") {
310
+ if (Array.isArray(entry)) {
311
+ return null;
312
+ }
313
+ if (entry.value) {
314
+ return entry.value; // Return the value as a string
315
+ }
316
+ if (entry.url) {
317
+ return entry.url; // Return the URL as a string
318
+ }
319
+ if (entry.path) {
320
+ return entry.path; // Return the path as a string
321
+ }
322
+ }
323
+ return null;
324
+ };
325
+
326
+ const normalizedImages = imageCandidates
327
+ .map((candidate) => normalizeImageEntry(candidate))
328
+ .filter((candidate) => candidate && typeof candidate === 'string');
329
+
330
+ const omitUndefined = (obj) =>
331
+ Object.fromEntries(
332
+ Object.entries(obj).filter(([, value]) => value !== undefined && value !== null),
333
+ );
334
+
335
+ const basePayload = omitUndefined({
336
+ prompt: modelPromptText,
337
+ size: validSizes.includes(combinedParameters.size) ? combinedParameters.size : "2K",
338
+ width: combinedParameters.width || 2048,
339
+ height: combinedParameters.height || 2048,
340
+ max_images: combinedParameters.maxImages || combinedParameters.numberResults || 1,
341
+ aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "4:3",
342
+ sequential_image_generation: validSequentialModes.includes(combinedParameters.sequentialImageGeneration) ? combinedParameters.sequentialImageGeneration : "disabled",
343
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) && combinedParameters.seed > 0 ? { seed: combinedParameters.seed } : {}),
344
+ });
345
+
346
+ // For seedream-4, include the image_input array if we have images
347
+ const inputPayload = {
348
+ ...basePayload,
349
+ ...(normalizedImages.length > 0 ? { image_input: normalizedImages } : {})
350
+ };
351
+
352
+ requestParameters = {
353
+ input: inputPayload,
354
+ };
355
+ break;
356
+ }
160
357
  }
161
358
 
162
359
  return requestParameters;
@@ -174,12 +371,14 @@ class ReplicateApiPlugin extends ModelPlugin {
174
371
  cortexRequest.params = requestParameters.params;
175
372
 
176
373
  // Make initial request to start prediction
177
- const stringifiedResponse = await this.executeRequest(cortexRequest);
178
- const parsedResponse = JSON.parse(stringifiedResponse);
374
+ const response = await this.executeRequest(cortexRequest);
375
+
376
+ // Parse the response to get the actual Replicate data
377
+ const parsedResponse = JSON.parse(response.output_text);
179
378
 
180
- // If we got a completed response, return it
379
+ // If we got a completed response, return it as CortexResponse
181
380
  if (parsedResponse?.status === "succeeded") {
182
- return stringifiedResponse;
381
+ return this.createCortexResponse(response);
183
382
  }
184
383
 
185
384
  logger.info("Replicate API returned a non-completed response.");
@@ -211,7 +410,9 @@ class ReplicateApiPlugin extends ModelPlugin {
211
410
 
212
411
  if (status === "succeeded") {
213
412
  logger.info("Replicate API returned a completed response after polling");
214
- return JSON.stringify(pollResponse.data);
413
+ // Parse the polled response to extract artifacts
414
+ const parsedResponse = this.parseResponse(pollResponse.data);
415
+ return this.createCortexResponse(parsedResponse);
215
416
  } else if (status === "failed" || status === "canceled") {
216
417
  throw new Error(`Prediction ${status}: ${pollResponse.data?.error || "Unknown error"}`);
217
418
  }
@@ -227,12 +428,67 @@ class ReplicateApiPlugin extends ModelPlugin {
227
428
  throw new Error(`Prediction ${predictionId} timed out after ${maxAttempts * pollInterval / 1000} seconds`);
228
429
  }
229
430
 
230
- // Stringify the response from the Replicate API
431
+ // Parse the response from the Replicate API and extract image artifacts
231
432
  parseResponse(data) {
232
- if (data.data) {
233
- return JSON.stringify(data.data);
433
+ const responseData = data.data || data;
434
+ const stringifiedResponse = JSON.stringify(responseData);
435
+
436
+ // Extract image URLs from Replicate response for artifacts
437
+ const imageArtifacts = [];
438
+ if (responseData?.output && Array.isArray(responseData.output)) {
439
+ for (const outputItem of responseData.output) {
440
+ if (typeof outputItem === 'string' && outputItem.match(/\.(jpg|jpeg|png|gif|webp)$/i)) {
441
+ // This is an image URL from Replicate
442
+ imageArtifacts.push({
443
+ type: "image",
444
+ url: outputItem,
445
+ mimeType: this.getMimeTypeFromUrl(outputItem)
446
+ });
447
+ }
448
+ }
449
+ }
450
+
451
+ return {
452
+ output_text: stringifiedResponse,
453
+ artifacts: imageArtifacts
454
+ };
455
+ }
456
+
457
+ // Create a CortexResponse from parsed response data
458
+ createCortexResponse(parsedResponse) {
459
+ if (typeof parsedResponse === 'string') {
460
+ // Handle string response (backward compatibility)
461
+ return new CortexResponse({
462
+ output_text: parsedResponse,
463
+ artifacts: []
464
+ });
465
+ } else if (parsedResponse && typeof parsedResponse === 'object') {
466
+ // Handle object response with artifacts
467
+ return new CortexResponse({
468
+ output_text: parsedResponse.output_text,
469
+ artifacts: parsedResponse.artifacts || []
470
+ });
471
+ } else {
472
+ throw new Error('Unexpected response format');
473
+ }
474
+ }
475
+
476
+ // Helper method to determine MIME type from URL extension
477
+ getMimeTypeFromUrl(url) {
478
+ const extension = url.split('.').pop().toLowerCase();
479
+ switch (extension) {
480
+ case 'jpg':
481
+ case 'jpeg':
482
+ return 'image/jpeg';
483
+ case 'png':
484
+ return 'image/png';
485
+ case 'gif':
486
+ return 'image/gif';
487
+ case 'webp':
488
+ return 'image/webp';
489
+ default:
490
+ return 'image/jpeg'; // Default fallback
234
491
  }
235
- return JSON.stringify(data);
236
492
  }
237
493
 
238
494
  // Override the logging function to display the request and response
@@ -19,7 +19,8 @@ class VeoVideoPlugin extends ModelPlugin {
19
19
  // Available Veo models
20
20
  const availableModels = {
21
21
  'veo-2.0-generate': 'GA',
22
- 'veo-3.0-generate': 'Preview'
22
+ 'veo-3.0-generate': 'Preview',
23
+ 'veo-3.0-fast-generate': 'Preview'
23
24
  };
24
25
 
25
26
  // Get the model ID from the pathway or use default
@@ -51,6 +52,7 @@ class VeoVideoPlugin extends ModelPlugin {
51
52
  ...(combinedParameters.enhancePrompt !== undefined && { enhancePrompt: combinedParameters.enhancePrompt }),
52
53
  // generateAudio is required for 3.0 and not supported by 2.0
53
54
  ...(model === 'veo-3.0-generate' && { generateAudio: combinedParameters.generateAudio !== undefined ? combinedParameters.generateAudio : true }),
55
+ ...(model === 'veo-3.0-fast-generate' && { generateAudio: combinedParameters.generateAudio !== undefined ? combinedParameters.generateAudio : true }),
54
56
  ...(combinedParameters.negativePrompt && { negativePrompt: combinedParameters.negativePrompt }),
55
57
  ...(combinedParameters.personGeneration && { personGeneration: combinedParameters.personGeneration }),
56
58
  ...(combinedParameters.sampleCount && { sampleCount: combinedParameters.sampleCount }),
@@ -69,6 +71,9 @@ class VeoVideoPlugin extends ModelPlugin {
69
71
  if (model === 'veo-3.0-generate' && parameters.durationSeconds !== 8) {
70
72
  throw new Error(`Veo 3.0 only supports durationSeconds: 8, got: ${parameters.durationSeconds}`);
71
73
  }
74
+ if (model === 'veo-3.0-fast-generate' && parameters.durationSeconds !== 8) {
75
+ throw new Error(`Veo 3.0 only supports durationSeconds: 8, got: ${parameters.durationSeconds}`);
76
+ }
72
77
  if (model === 'veo-2.0-generate' && (parameters.durationSeconds < 5 || parameters.durationSeconds > 8)) {
73
78
  throw new Error(`Veo 2.0 supports durationSeconds between 5-8, got: ${parameters.durationSeconds}`);
74
79
  }
@@ -82,6 +87,12 @@ class VeoVideoPlugin extends ModelPlugin {
82
87
  if (parameters.video) {
83
88
  throw new Error('video parameter is not supported in Veo 3.0');
84
89
  }
90
+ if (model === 'veo-3.0-fast-generate' && parameters.lastFrame) {
91
+ throw new Error('lastFrame parameter is not supported in Veo 3.0');
92
+ }
93
+ if (model === 'veo-3.0-fast-generate' && parameters.video) {
94
+ throw new Error('video parameter is not supported in Veo 3.0');
95
+ }
85
96
  }
86
97
 
87
98
  // generateAudio constraints
@@ -91,6 +102,9 @@ class VeoVideoPlugin extends ModelPlugin {
91
102
  if (model === 'veo-3.0-generate' && parameters.generateAudio === undefined) {
92
103
  logger.warn('generateAudio is required for Veo 3.0, defaulting to true');
93
104
  }
105
+ if (model === 'veo-3.0-fast-generate' && parameters.generateAudio === undefined) {
106
+ logger.warn('generateAudio is required for Veo 3.0, defaulting to true');
107
+ }
94
108
  }
95
109
 
96
110
  // Execute the request to the Veo API
package/server/rest.js CHANGED
@@ -139,6 +139,8 @@ const processRestRequest = async (server, req, pathway, name, parameterMap = {})
139
139
  return Boolean(value);
140
140
  } else if (type === 'Int') {
141
141
  return parseInt(value, 10);
142
+ } else if (type === 'Float') {
143
+ return parseFloat(value);
142
144
  } else if (type === '[MultiMessage]' && Array.isArray(value)) {
143
145
  return value.map(msg => ({
144
146
  ...msg,
package/server/typeDef.js CHANGED
@@ -1,30 +1,113 @@
1
+ // Check if a value is a JSON Schema object for parameter typing
2
+ const isJsonSchemaObject = (value) => {
3
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
4
+ // Basic JSON Schema indicators
5
+ return (
6
+ typeof value.type === 'string' ||
7
+ value.$ref !== undefined ||
8
+ value.oneOf !== undefined ||
9
+ value.anyOf !== undefined ||
10
+ value.allOf !== undefined ||
11
+ value.enum !== undefined ||
12
+ value.properties !== undefined ||
13
+ value.items !== undefined
14
+ );
15
+ };
16
+
17
+ // Extract the default value from a JSON Schema object or return the value as-is
18
+ const extractValueFromTypeSpec = (value) => {
19
+ if (isJsonSchemaObject(value)) {
20
+ return value.hasOwnProperty('default') ? value.default : undefined;
21
+ }
22
+ return value;
23
+ };
24
+
25
+ // Process parameters to convert any type specification objects to their actual values
26
+ const processPathwayParameters = (params) => {
27
+ if (!params || typeof params !== 'object') {
28
+ return params;
29
+ }
30
+
31
+ const processed = {};
32
+ for (const [key, value] of Object.entries(params)) {
33
+ processed[key] = extractValueFromTypeSpec(value);
34
+ }
35
+ return processed;
36
+ };
37
+
1
38
  const getGraphQlType = (value) => {
39
+ // The value might be an object with JSON Schema type specification
40
+ if (isJsonSchemaObject(value)) {
41
+ const schema = value;
42
+ // Map JSON Schema to GraphQL
43
+ if (schema.type === 'boolean') {
44
+ return { type: 'Boolean', defaultValue: schema.default === undefined ? undefined : schema.default };
45
+ }
46
+ if (schema.type === 'string') {
47
+ return { type: 'String', defaultValue: schema.default === undefined ? undefined : `"${schema.default}"` };
48
+ }
49
+ if (schema.type === 'integer') {
50
+ return { type: 'Int', defaultValue: schema.default };
51
+ }
52
+ if (schema.type === 'number') {
53
+ const def = schema.default;
54
+ return { type: 'Float', defaultValue: def };
55
+ }
56
+ if (schema.type === 'array') {
57
+ // Support arrays of primitive types; fall back to JSON string for complex types
58
+ const items = schema.items || {};
59
+ const def = schema.default;
60
+ const defaultArray = Array.isArray(def) ? JSON.stringify(def) : '[]';
61
+ if (items.type === 'string') {
62
+ return { type: '[String]', defaultValue: defaultArray };
63
+ }
64
+ if (items.type === 'integer') {
65
+ return { type: '[Int]', defaultValue: defaultArray };
66
+ }
67
+ if (items.type === 'number') {
68
+ return { type: '[Float]', defaultValue: defaultArray };
69
+ }
70
+ if (items.type === 'boolean') {
71
+ return { type: '[Boolean]', defaultValue: defaultArray };
72
+ }
73
+ // Unknown item type: pass as serialized JSON string argument
74
+ return { type: 'String', defaultValue: def === undefined ? '"[]"' : `"${JSON.stringify(def).replace(/"/g, '\\"')}"` };
75
+ }
76
+ if (schema.type === 'object' || schema.properties) {
77
+ // Until explicit input types are defined, accept as stringified JSON
78
+ const def = schema.default;
79
+ return { type: 'String', defaultValue: def === undefined ? '"{}"' : `"${JSON.stringify(def).replace(/"/g, '\\"')}"` };
80
+ }
81
+ }
82
+
83
+ // Otherwise, autodetect the type
2
84
  switch (typeof value) {
3
85
  case 'boolean':
4
- return {type: 'Boolean'};
86
+ return {type: 'Boolean', defaultValue: value};
5
87
  case 'string':
6
- return {type: 'String'};
88
+ return {type: 'String', defaultValue: `"${value}"`};
7
89
  case 'number':
8
- return {type: 'Int'};
90
+ // Check if it's an integer or float
91
+ return Number.isInteger(value) ? {type: 'Int', defaultValue: value} : {type: 'Float', defaultValue: value};
9
92
  case 'object':
10
93
  if (Array.isArray(value)) {
11
94
  if (value.length > 0 && typeof(value[0]) === 'string') {
12
- return {type: '[String]'};
95
+ return {type: '[String]', defaultValue: JSON.stringify(value)};
13
96
  }
14
97
  else {
15
- // New case for MultiMessage type
98
+ // Check if it's MultiMessage (content is array) or Message (content is string)
16
99
  if (Array.isArray(value[0]?.content)) {
17
- return {type: '[MultiMessage]'};
100
+ return {type: '[MultiMessage]', defaultValue: `"${JSON.stringify(value).replace(/"/g, '\\"')}"`};
18
101
  }
19
102
  else {
20
- return {type: '[Message]'};
103
+ return {type: '[Message]', defaultValue: `"${JSON.stringify(value).replace(/"/g, '\\"')}"`};
21
104
  }
22
105
  }
23
106
  } else {
24
- return {type: `[${value.objName}]`};
107
+ return {type: `[${value.objName}]`, defaultValue: JSON.stringify(value)};
25
108
  }
26
109
  default:
27
- return {type: 'String'};
110
+ return {type: 'String', defaultValue: `"${value}"`};
28
111
  }
29
112
  };
30
113
 
@@ -80,7 +163,7 @@ const getPathwayTypeDefAndExtendQuery = (pathway) => {
80
163
  };
81
164
  });
82
165
 
83
- const gqlDefinition = `${type}\n\n${responseType}\n\nextend type Query {${name}(${paramsStr}): ${objName}}`;
166
+ const gqlDefinition = `${type}\n\n${responseType}\n\nextend type Query {${name}${paramsStr ? `(${paramsStr})` : ''}: ${objName}}`;
84
167
 
85
168
  return {
86
169
  gqlDefinition,
@@ -100,4 +183,7 @@ export {
100
183
  getMessageTypeDefs,
101
184
  getPathwayTypeDef,
102
185
  userPathwayInputParameters,
186
+ isJsonSchemaObject,
187
+ extractValueFromTypeSpec,
188
+ processPathwayParameters,
103
189
  };
@@ -155,7 +155,7 @@ test.serial('AppTek Plugin: Force failure and test GPT-4 Omni fallback', async (
155
155
  });
156
156
 
157
157
  // Test AppTek failure with default fallback (translate_groq)
158
- test.skip('AppTek Plugin: Force failure and test default fallback', async (t) => {
158
+ test('AppTek Plugin: Force failure and test default fallback', async (t) => {
159
159
  // Set a longer timeout for this test since Groq might be slower
160
160
  t.timeout(180000); // 3 minutes
161
161
 
@@ -90,7 +90,6 @@ test('parseJson should attempt to repair invalid JSON', async t => {
90
90
 
91
91
  const result = await parser.parseJson(invalidJson);
92
92
 
93
- console.log('parseJson result:', result); // For debugging
94
93
 
95
94
  t.not(result, null);
96
95
 
@@ -193,8 +193,7 @@ test('_createPromptObject handles empty system prompt', t => {
193
193
 
194
194
  t.true(result instanceof Prompt);
195
195
  t.is(result.name, 'test_prompt');
196
- t.is(result.messages[0].content, '');
197
- t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
196
+ t.is(result.messages[0].content, '{{text}}\n\nTest prompt text');
198
197
  });
199
198
 
200
199
  test('_createPromptObject handles null system prompt', t => {
@@ -206,8 +205,7 @@ test('_createPromptObject handles null system prompt', t => {
206
205
 
207
206
  t.true(result instanceof Prompt);
208
207
  t.is(result.name, 'test_prompt');
209
- t.is(result.messages[0].content, '');
210
- t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
208
+ t.is(result.messages[0].content, '{{text}}\n\nTest prompt text');
211
209
  });
212
210
 
213
211
  test('putPathway requires userId and secret', async t => {