@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.
- package/config/default.example.json +2 -2
- package/config.js +9 -0
- package/package.json +1 -1
- package/pathways/system/sys_test_response_reasonableness.js +20 -0
- package/pathways/video_seedance.js +17 -0
- package/pathways/video_veo.js +31 -0
- package/server/modelExecutor.js +4 -0
- package/server/plugins/replicateApiPlugin.js +22 -0
- package/server/plugins/veoVideoPlugin.js +218 -0
|
@@ -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
|
|
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
|
|
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.
|
|
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
|
+
};
|
package/server/modelExecutor.js
CHANGED
|
@@ -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;
|