@aj-archipelago/cortex 1.3.55 → 1.3.56

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.
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "gemini-pro-15-vision": {
39
39
  "type": "GEMINI-VISION",
40
- "url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-1.5-pro-preview-0215:streamGenerateContent",
40
+ "url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent",
41
41
  "headers": {
42
42
  "Content-Type": "application/json"
43
43
  },
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "gemini-pro-25-vision": {
50
50
  "type": "GEMINI-VISION",
51
- "url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-2.5-pro-exp-03-25:streamGenerateContent",
51
+ "url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-2.5-pro:streamGenerateContent",
52
52
  "headers": {
53
53
  "Content-Type": "application/json"
54
54
  },
package/config.js CHANGED
@@ -290,6 +290,15 @@ var config = convict({
290
290
  "Content-Type": "application/json"
291
291
  },
292
292
  },
293
+ "replicate-seedance-1-pro": {
294
+ "type": "REPLICATE-API",
295
+ "url": "https://api.replicate.com/v1/models/bytedance/seedance-1-pro/predictions",
296
+ "headers": {
297
+ "Prefer": "wait",
298
+ "Authorization": "Token {{REPLICATE_API_KEY}}",
299
+ "Content-Type": "application/json"
300
+ },
301
+ },
293
302
  "replicate-flux-11-pro": {
294
303
  "type": "REPLICATE-API",
295
304
  "url": "https://api.replicate.com/v1/models/black-forest-labs/flux-1.1-pro/predictions",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.3.55",
3
+ "version": "1.3.56",
4
4
  "description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
5
5
  "private": false,
6
6
  "repository": {
@@ -0,0 +1,20 @@
1
+ import { Prompt } from '../../server/prompt.js';
2
+
3
+ export default {
4
+ prompt: [
5
+ new Prompt({
6
+ messages: [
7
+ { "role": "system", "content": "Assistant is a response quality evaluator. When given a chat history and a response to evaluate, assistant will carefully analyze whether the response is reasonable given the context of the conversation. Primarily the assistant is trying to detect errors and hallucinations in the underlying models. As long as the response looks basically correct and relevant, even if it's not ideal or complete or contradicts some rules, it should be isReasonable: true.\n\nAssistant will return a JSON object with the following structure:\n{\n \"isReasonable\": boolean,\n \"score\": number (0-10),\n \"reasoning\": string\n}\n\nWhere:\n- isReasonable: Overall judgment of whether the response is reasonable\n- score: Numerical score from 0-10 (10 being excellent)\n- reasoning: Brief explanation of the judgment" },
8
+ "{{chatHistory}}",
9
+ { "role": "user", "content": `The response to the above conversation from the model was: '{{{modelResponse}}}'. Is that response acceptable?`},
10
+ ]
11
+ })
12
+ ],
13
+ inputParameters: {
14
+ chatHistory: [{role: '', content: []}],
15
+ modelResponse: '',
16
+ },
17
+ model: 'oai-gpt4o-mini',
18
+ enableDuplicateRequests: false,
19
+ json: true
20
+ }
@@ -0,0 +1,17 @@
1
+ export default {
2
+ prompt: ["{{text}}"],
3
+
4
+ enableDuplicateRequests: false,
5
+ inputParameters: {
6
+ model: "replicate-seedance-1-pro",
7
+ resolution: "1080p",
8
+ aspectRatio: "16:9",
9
+ fps: 24,
10
+ duration: 5,
11
+ image: "",
12
+ camera_fixed: false,
13
+ seed: -1,
14
+ },
15
+
16
+ timeout: 60 * 30, // 30 minutes
17
+ };
@@ -0,0 +1,31 @@
1
+ // video_veo.js
2
+ // Pathway for generating videos using Google's Veo model via Vertex AI
3
+ //
4
+ // Model-specific constraints:
5
+ // - Veo 2.0: durationSeconds 5-8, no generateAudio, supports lastFrame/video
6
+ // - Veo 3.0: durationSeconds always 8, generateAudio required, no lastFrame/video
7
+
8
+ export default {
9
+ prompt: ["Generate a video based on the following description: {{text}}"],
10
+
11
+ enableDuplicateRequests: false,
12
+ inputParameters: {
13
+ text: "",
14
+ image: "",
15
+ video: "",
16
+ lastFrame: "",
17
+ model: "veo-2.0-generate",
18
+ aspectRatio: "16:9",
19
+ durationSeconds: 8, // 5-8 for 2.0, always 8 for 3.0
20
+ enhancePrompt: true,
21
+ generateAudio: false, // not supported in 2.0, required in 3.0
22
+ negativePrompt: "",
23
+ personGeneration: "allow_all",
24
+ sampleCount: 1,
25
+ storageUri: "",
26
+ location: "us-central1",
27
+ seed: -1,
28
+ },
29
+
30
+ timeout: 60 * 30, // 30 minutes
31
+ };
@@ -29,6 +29,7 @@ import ReplicateApiPlugin from './plugins/replicateApiPlugin.js';
29
29
  import AzureVideoTranslatePlugin from './plugins/azureVideoTranslatePlugin.js';
30
30
  import OllamaChatPlugin from './plugins/ollamaChatPlugin.js';
31
31
  import OllamaCompletionPlugin from './plugins/ollamaCompletionPlugin.js';
32
+ import VeoVideoPlugin from './plugins/veoVideoPlugin.js';
32
33
 
33
34
  class ModelExecutor {
34
35
  constructor(pathway, model) {
@@ -117,6 +118,9 @@ class ModelExecutor {
117
118
  case 'OLLAMA-COMPLETION':
118
119
  plugin = new OllamaCompletionPlugin(pathway, model);
119
120
  break;
121
+ case 'VEO-VIDEO':
122
+ plugin = new VeoVideoPlugin(pathway, model);
123
+ break;
120
124
  default:
121
125
  throw new Error(`Unsupported model type: ${model.type}`);
122
126
  }
@@ -35,6 +35,7 @@ class ReplicateApiPlugin extends ModelPlugin {
35
35
  height: combinedParameters.height,
36
36
  size: combinedParameters.size || "1024x1024",
37
37
  style: combinedParameters.style || "realistic_image",
38
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed) ? { seed: combinedParameters.seed } : {}),
38
39
  },
39
40
  };
40
41
  break;
@@ -109,6 +110,7 @@ class ReplicateApiPlugin extends ModelPlugin {
109
110
  input_image: combinedParameters.input_image,
110
111
  aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "1:1",
111
112
  safety_tolerance: safetyTolerance,
113
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
112
114
  },
113
115
  };
114
116
  break;
@@ -131,6 +133,26 @@ class ReplicateApiPlugin extends ModelPlugin {
131
133
  input_image_2: combinedParameters.input_image_2,
132
134
  aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "1:1",
133
135
  safety_tolerance: safetyTolerance,
136
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
137
+ },
138
+ };
139
+ break;
140
+ }
141
+ case "replicate-seedance-1-pro": {
142
+ const validResolutions = ["480p", "1080p"];
143
+ const validRatios = ["16:9", "4:3", "9:16", "1:1", "3:4", "21:9", "9:21"];
144
+ const validFps = [24];
145
+
146
+ requestParameters = {
147
+ input: {
148
+ prompt: modelPromptText,
149
+ resolution: validResolutions.includes(combinedParameters.resolution) ? combinedParameters.resolution : "1080p",
150
+ aspect_ratio: validRatios.includes(combinedParameters.aspectRatio) ? combinedParameters.aspectRatio : "16:9",
151
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
152
+ fps: validFps.includes(combinedParameters.fps) ? combinedParameters.fps : 24,
153
+ camera_fixed: combinedParameters.camera_fixed || false,
154
+ duration: combinedParameters.duration || 5,
155
+ ...(combinedParameters.image ? { image: combinedParameters.image } : {}),
134
156
  },
135
157
  };
136
158
  break;
@@ -0,0 +1,218 @@
1
+ import ModelPlugin from "./modelPlugin.js";
2
+ import logger from "../../lib/logger.js";
3
+ import axios from "axios";
4
+
5
+ class VeoVideoPlugin extends ModelPlugin {
6
+ constructor(pathway, model) {
7
+ super(pathway, model);
8
+ }
9
+
10
+ // Set up parameters specific to the Veo API
11
+ getRequestParameters(text, parameters, prompt) {
12
+ const combinedParameters = { ...this.promptParameters, ...parameters };
13
+ const { modelPromptText } = this.getCompiledPrompt(
14
+ text,
15
+ parameters,
16
+ prompt,
17
+ );
18
+
19
+ // Available Veo models
20
+ const availableModels = {
21
+ 'veo-2.0-generate': 'GA',
22
+ 'veo-3.0-generate': 'Preview'
23
+ };
24
+
25
+ // Get the model ID from the pathway or use default
26
+ const model = combinedParameters.model || 'veo-2.0-generate';
27
+
28
+ if (!availableModels[model]) {
29
+ throw new Error(`Invalid Veo model ID: ${model}. Available models: ${Object.keys(availableModels).join(', ')}`);
30
+ }
31
+
32
+ // Validate model-specific parameter constraints
33
+ this.validateModelSpecificParameters(combinedParameters, model);
34
+
35
+ // Build the request parameters based on Veo API documentation
36
+ const requestParameters = {
37
+ instances: [
38
+ {
39
+ prompt: modelPromptText,
40
+ // Optional input media fields
41
+ ...(combinedParameters.image && { image: JSON.parse(combinedParameters.image) }),
42
+ // lastFrame and video are only supported in 2.0
43
+ ...(model === 'veo-2.0-generate' && combinedParameters.lastFrame && { lastFrame: JSON.parse(combinedParameters.lastFrame) }),
44
+ ...(model === 'veo-2.0-generate' && combinedParameters.video && { video: JSON.parse(combinedParameters.video) }),
45
+ }
46
+ ],
47
+ parameters: {
48
+ // Generation parameters
49
+ ...(combinedParameters.aspectRatio && { aspectRatio: combinedParameters.aspectRatio }),
50
+ ...(combinedParameters.durationSeconds && { durationSeconds: combinedParameters.durationSeconds }),
51
+ ...(combinedParameters.enhancePrompt !== undefined && { enhancePrompt: combinedParameters.enhancePrompt }),
52
+ // generateAudio is required for 3.0 and not supported by 2.0
53
+ ...(model === 'veo-3.0-generate' && { generateAudio: combinedParameters.generateAudio !== undefined ? combinedParameters.generateAudio : true }),
54
+ ...(combinedParameters.negativePrompt && { negativePrompt: combinedParameters.negativePrompt }),
55
+ ...(combinedParameters.personGeneration && { personGeneration: combinedParameters.personGeneration }),
56
+ ...(combinedParameters.sampleCount && { sampleCount: combinedParameters.sampleCount }),
57
+ ...(combinedParameters.seed && Number.isInteger(combinedParameters.seed && combinedParameters.seed > 0) ? { seed: combinedParameters.seed } : {}),
58
+ ...(combinedParameters.storageUri && { storageUri: combinedParameters.storageUri }),
59
+ }
60
+ };
61
+
62
+ return requestParameters;
63
+ }
64
+
65
+ // Validate model-specific parameter constraints
66
+ validateModelSpecificParameters(parameters, model) {
67
+ // Duration constraints
68
+ if (parameters.durationSeconds !== undefined) {
69
+ if (model === 'veo-3.0-generate' && parameters.durationSeconds !== 8) {
70
+ throw new Error(`Veo 3.0 only supports durationSeconds: 8, got: ${parameters.durationSeconds}`);
71
+ }
72
+ if (model === 'veo-2.0-generate' && (parameters.durationSeconds < 5 || parameters.durationSeconds > 8)) {
73
+ throw new Error(`Veo 2.0 supports durationSeconds between 5-8, got: ${parameters.durationSeconds}`);
74
+ }
75
+ }
76
+
77
+ // lastFrame and video constraints
78
+ if (model === 'veo-3.0-generate') {
79
+ if (parameters.lastFrame) {
80
+ throw new Error('lastFrame parameter is not supported in Veo 3.0');
81
+ }
82
+ if (parameters.video) {
83
+ throw new Error('video parameter is not supported in Veo 3.0');
84
+ }
85
+ }
86
+
87
+ // generateAudio constraints
88
+ if (model === 'veo-2.0-generate' && parameters.generateAudio) {
89
+ throw new Error('generateAudio parameter is not supported in Veo 2.0');
90
+ }
91
+ if (model === 'veo-3.0-generate' && parameters.generateAudio === undefined) {
92
+ logger.warn('generateAudio is required for Veo 3.0, defaulting to true');
93
+ }
94
+ }
95
+
96
+ // Execute the request to the Veo API
97
+ async execute(text, parameters, prompt, cortexRequest) {
98
+ const requestParameters = this.getRequestParameters(
99
+ text,
100
+ parameters,
101
+ prompt,
102
+ );
103
+
104
+ cortexRequest.data = requestParameters;
105
+ cortexRequest.params = requestParameters.params;
106
+
107
+ // Get the model ID for the URL
108
+ const model = parameters.model || 'veo-2.0-generate';
109
+
110
+ // Use the URL from the model configuration (cortexRequest.url is set by Cortex)
111
+ const baseUrl = cortexRequest.url;
112
+ const predictUrl = `${baseUrl}:predictLongRunning`;
113
+
114
+ // Set up the request
115
+ const requestConfig = {
116
+ method: 'POST',
117
+ url: predictUrl,
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ ...cortexRequest.headers
121
+ },
122
+ data: requestParameters
123
+ };
124
+
125
+ // Get authentication token
126
+ const gcpAuthTokenHelper = this.config.get('gcpAuthTokenHelper');
127
+ const authToken = await gcpAuthTokenHelper.getAccessToken();
128
+ requestConfig.headers.Authorization = `Bearer ${authToken}`;
129
+
130
+ logger.info(`Starting Veo video generation with model: ${model}`);
131
+
132
+ try {
133
+ // Make initial request to start video generation
134
+ const response = await axios(requestConfig);
135
+ const operationName = response.data.name;
136
+
137
+ if (!operationName) {
138
+ throw new Error("No operation name returned from Veo API");
139
+ }
140
+
141
+ logger.info(`Veo video generation started. Operation: ${operationName}`);
142
+
143
+ // Poll for results
144
+ const maxAttempts = 120; // 10 minutes with 5 second intervals
145
+ const pollInterval = 5000;
146
+
147
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
148
+ try {
149
+ // Poll the operation status
150
+ const pollResponse = await axios.post(
151
+ `${baseUrl}:fetchPredictOperation`,
152
+ { operationName },
153
+ {
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ 'Authorization': `Bearer ${authToken}`
157
+ }
158
+ }
159
+ );
160
+
161
+ const operationData = pollResponse.data;
162
+ logger.info(`Polling Veo operation ${operationName} - attempt ${attempt + 1}, done: ${operationData.done || false}`);
163
+
164
+ if (operationData.done) {
165
+ if (operationData.response && operationData.response.videos) {
166
+ logger.info(`Veo video generation completed successfully`);
167
+ return JSON.stringify(operationData);
168
+ } else {
169
+ throw new Error(`Veo operation completed but no videos returned: ${JSON.stringify(operationData)}`);
170
+ }
171
+ }
172
+
173
+ // Wait before next poll
174
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
175
+ } catch (error) {
176
+ logger.error(`Error polling Veo operation: ${error.message}`);
177
+ throw error;
178
+ }
179
+ }
180
+
181
+ throw new Error(`Veo video generation timed out after ${maxAttempts * pollInterval / 1000} seconds`);
182
+ } catch (error) {
183
+ logger.error(`Veo video generation failed: ${error.message}`);
184
+ throw error;
185
+ }
186
+ }
187
+
188
+ // Parse the response from the Veo API
189
+ parseResponse(data) {
190
+ if (data.response && data.response.videos) {
191
+ // Return the videos array with GCS URIs
192
+ return JSON.stringify({
193
+ videos: data.response.videos,
194
+ operationName: data.name,
195
+ status: 'completed'
196
+ });
197
+ }
198
+ return JSON.stringify(data);
199
+ }
200
+
201
+ // Override the logging function to display the request and response
202
+ logRequestData(data, responseData, prompt) {
203
+ const modelInput = data?.instances?.[0]?.prompt;
204
+ const model = this.model || 'veo-2.0-generate';
205
+ const parameters = data?.parameters || {};
206
+
207
+ logger.verbose(`Veo Model: ${model}`);
208
+ logger.verbose(`Prompt: ${modelInput}`);
209
+ logger.verbose(`Parameters: ${JSON.stringify(parameters)}`);
210
+ logger.verbose(`Response: ${this.parseResponse(responseData)}`);
211
+
212
+ prompt &&
213
+ prompt.debugInfo &&
214
+ (prompt.debugInfo += `\n${JSON.stringify(data)}`);
215
+ }
216
+ }
217
+
218
+ export default VeoVideoPlugin;