@aj-archipelago/cortex 1.1.35 → 1.1.37
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/README.md
CHANGED
|
@@ -503,7 +503,8 @@ The configuration should include the following properties:
|
|
|
503
503
|
"awsAccessKeyId": "your_access_key_id", // Only for AWS S3
|
|
504
504
|
"awsSecretAccessKey": "your_secret_access_key", // Only for AWS S3
|
|
505
505
|
"awsRegion": "your_aws_region", // Only for AWS S3
|
|
506
|
-
"awsBucketName": "cortexdynamicpathways" // Optional, default is "cortexdynamicpathways"
|
|
506
|
+
"awsBucketName": "cortexdynamicpathways", // Optional, default is "cortexdynamicpathways"
|
|
507
|
+
"publishKey": "your_publish_key"
|
|
507
508
|
}
|
|
508
509
|
```
|
|
509
510
|
|
|
@@ -559,7 +560,7 @@ query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String!)
|
|
|
559
560
|
|
|
560
561
|
To ensure the security of dynamic pathways:
|
|
561
562
|
|
|
562
|
-
1. A `
|
|
563
|
+
1. A `publishKey` must be set in the dynamic pathways configuration to enable pathway publishing.
|
|
563
564
|
2. This key must be provided in the `key` parameter when adding, updating, or deleting pathways.
|
|
564
565
|
3. Each pathway is associated with a `userId` and `secret`. The secret must be provided to modify or delete an existing pathway.
|
|
565
566
|
|
|
@@ -567,4 +568,4 @@ To ensure the security of dynamic pathways:
|
|
|
567
568
|
|
|
568
569
|
Each instance of Cortex maintains its own local cache of pathways. On every dynamic pathway request, it checks if the local cache is up to date by comparing the last modified timestamp of the storage with the last update time of the local cache. If the local cache is out of date, it reloads the pathways from storage.
|
|
569
570
|
|
|
570
|
-
This approach ensures that all instances of Cortex will eventually have access to the most up-to-date dynamic pathways without requiring immediate synchronization.
|
|
571
|
+
This approach ensures that all instances of Cortex will eventually have access to the most up-to-date dynamic pathways without requiring immediate synchronization.
|
package/config.js
CHANGED
|
@@ -321,6 +321,7 @@ const createDynamicPathwayManager = async (config, basePathway) => {
|
|
|
321
321
|
awsSecretAccessKey: dynamicPathwayConfig.awsSecretAccessKey,
|
|
322
322
|
awsRegion: dynamicPathwayConfig.awsRegion,
|
|
323
323
|
awsBucketName: dynamicPathwayConfig.awsBucketName || 'cortexdynamicpathways',
|
|
324
|
+
publishKey: dynamicPathwayConfig.publishKey,
|
|
324
325
|
};
|
|
325
326
|
|
|
326
327
|
const pathwayManager = new PathwayManager(storageConfig, basePathway);
|
package/lib/pathwayManager.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { BlobServiceClient } from '@azure/storage-blob';
|
|
2
2
|
import { S3 } from '@aws-sdk/client-s3';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
4
5
|
import logger from './logger.js';
|
|
5
|
-
|
|
6
|
+
import { Prompt } from '../server/prompt.js';
|
|
6
7
|
|
|
7
8
|
class StorageStrategy {
|
|
8
9
|
async load() { throw new Error('Not implemented'); }
|
|
@@ -20,6 +21,8 @@ class LocalStorage extends StorageStrategy {
|
|
|
20
21
|
if (!fs.existsSync(this.filePath)) {
|
|
21
22
|
// create it. log
|
|
22
23
|
logger.info(`Creating dynamic pathways local file: ${this.filePath}`);
|
|
24
|
+
// create directory if it doesn't exist
|
|
25
|
+
await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
23
26
|
await fs.promises.writeFile(this.filePath, JSON.stringify({}));
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -219,6 +222,7 @@ class S3Storage extends StorageStrategy {
|
|
|
219
222
|
class PathwayManager {
|
|
220
223
|
constructor(config, basePathway) {
|
|
221
224
|
this.storage = this.getStorageStrategy(config);
|
|
225
|
+
this.publishKey = config.publishKey;
|
|
222
226
|
this.pathways = {};
|
|
223
227
|
this.lastUpdated = 0;
|
|
224
228
|
this.basePathway = basePathway;
|
|
@@ -226,6 +230,10 @@ class PathwayManager {
|
|
|
226
230
|
if (config.storageType === 'local') {
|
|
227
231
|
logger.warn('WARNING: Local file storage is being used for dynamic pathways. If there are multiple instances of Cortex, they will not be synced. Consider using cloud storage such as S3 or Azure for production environments.');
|
|
228
232
|
}
|
|
233
|
+
|
|
234
|
+
if (!this.publishKey) {
|
|
235
|
+
logger.warn('WARNING: dynamicPathwaysConfig.publishKey is not set. Dynamic pathways will not be editable in this instance of Cortex.');
|
|
236
|
+
}
|
|
229
237
|
}
|
|
230
238
|
|
|
231
239
|
getStorageStrategy(config) {
|
|
@@ -278,11 +286,39 @@ class PathwayManager {
|
|
|
278
286
|
await this.storage.save(pathways);
|
|
279
287
|
}
|
|
280
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Transforms the prompts in a pathway to include the system prompt.
|
|
291
|
+
* @param {Object} pathway - The pathway object to transform.
|
|
292
|
+
* @param {string[]} pathway.prompt - Array of user prompts.
|
|
293
|
+
* @param {string} pathway.systemPrompt - The system prompt to prepend to each user prompt.
|
|
294
|
+
* @returns {Object} A new pathway object with transformed prompts.
|
|
295
|
+
*/
|
|
296
|
+
async transformPrompts(pathway) {
|
|
297
|
+
const { prompt, systemPrompt } = pathway;
|
|
298
|
+
|
|
299
|
+
const newPathway = { ...pathway };
|
|
300
|
+
|
|
301
|
+
// Transform each prompt in the array
|
|
302
|
+
newPathway.prompt = prompt.map(p => {
|
|
303
|
+
return new Prompt({
|
|
304
|
+
messages: [
|
|
305
|
+
// Prepend the system prompt as a system message
|
|
306
|
+
{ "role": "system", "content": systemPrompt },
|
|
307
|
+
// Add the original prompt as a user message
|
|
308
|
+
{ "role": "user", "content": p },
|
|
309
|
+
]
|
|
310
|
+
})
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return newPathway;
|
|
314
|
+
}
|
|
315
|
+
|
|
281
316
|
async putPathway(name, pathway, userId, secret, displayName) {
|
|
282
317
|
if (!userId || !secret) {
|
|
283
318
|
throw new Error('Both userId and secret are mandatory for adding or updating a pathway');
|
|
284
319
|
}
|
|
285
320
|
|
|
321
|
+
await this.getLatestPathways();
|
|
286
322
|
this.pathways[userId] = this.pathways[userId] || {};
|
|
287
323
|
|
|
288
324
|
if (this.pathways[userId][name] && this.pathways[userId][name].secret !== secret) {
|
|
@@ -296,9 +332,12 @@ class PathwayManager {
|
|
|
296
332
|
}
|
|
297
333
|
|
|
298
334
|
async removePathway(name, userId, secret) {
|
|
335
|
+
await this.getLatestPathways();
|
|
336
|
+
|
|
299
337
|
if (!this.pathways[userId] || !this.pathways[userId][name]) {
|
|
300
338
|
return;
|
|
301
339
|
}
|
|
340
|
+
|
|
302
341
|
if (this.pathways[userId][name].secret !== secret) {
|
|
303
342
|
throw new Error('Invalid secret');
|
|
304
343
|
}
|
|
@@ -318,6 +357,7 @@ class PathwayManager {
|
|
|
318
357
|
|
|
319
358
|
input PathwayInput {
|
|
320
359
|
prompt: [String!]!
|
|
360
|
+
systemPrompt: String
|
|
321
361
|
inputParameters: JSONObject
|
|
322
362
|
model: String
|
|
323
363
|
enableCache: Boolean
|
|
@@ -344,11 +384,11 @@ class PathwayManager {
|
|
|
344
384
|
return {
|
|
345
385
|
Mutation: {
|
|
346
386
|
putPathway: async (_, { name, pathway, userId, secret, displayName, key }) => {
|
|
347
|
-
if (!
|
|
387
|
+
if (!this.publishKey) {
|
|
348
388
|
throw new Error("Invalid configuration. Pathway publishing key is not configured in Cortex.")
|
|
349
389
|
}
|
|
350
390
|
|
|
351
|
-
if (key !==
|
|
391
|
+
if (key !== this.publishKey) {
|
|
352
392
|
throw new Error('Invalid pathway publishing key. The key provided did not match the key configured in Cortex.');
|
|
353
393
|
}
|
|
354
394
|
|
|
@@ -360,10 +400,10 @@ class PathwayManager {
|
|
|
360
400
|
}
|
|
361
401
|
},
|
|
362
402
|
deletePathway: async (_, { name, userId, secret, key }) => {
|
|
363
|
-
if (!
|
|
403
|
+
if (!this.publishKey) {
|
|
364
404
|
throw new Error("Invalid configuration. Pathway publishing key is not configured in Cortex.")
|
|
365
405
|
}
|
|
366
|
-
if (key !==
|
|
406
|
+
if (key !== this.publishKey) {
|
|
367
407
|
throw new Error('Invalid pathway publishing key. The key provided did not match the key configured in Cortex.');
|
|
368
408
|
}
|
|
369
409
|
|
|
@@ -378,7 +418,7 @@ class PathwayManager {
|
|
|
378
418
|
};
|
|
379
419
|
}
|
|
380
420
|
|
|
381
|
-
async
|
|
421
|
+
async getLatestPathways() {
|
|
382
422
|
try {
|
|
383
423
|
const currentTimestamp = await this.storage.getLastModified();
|
|
384
424
|
|
|
@@ -390,19 +430,19 @@ class PathwayManager {
|
|
|
390
430
|
|
|
391
431
|
return this.pathways;
|
|
392
432
|
} catch (error) {
|
|
393
|
-
logger.error('Error in
|
|
433
|
+
logger.error('Error in getLatestPathways:', error);
|
|
394
434
|
throw error;
|
|
395
435
|
}
|
|
396
436
|
}
|
|
397
437
|
|
|
398
438
|
async getPathway(userId, pathwayName) {
|
|
399
|
-
const pathways = await this.
|
|
439
|
+
const pathways = await this.getLatestPathways();
|
|
400
440
|
|
|
401
441
|
if (!pathways[userId] || !pathways[userId][pathwayName]) {
|
|
402
442
|
throw new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
|
|
403
443
|
}
|
|
404
444
|
|
|
405
|
-
return pathways[userId][pathwayName];
|
|
445
|
+
return this.transformPrompts(pathways[userId][pathwayName]);
|
|
406
446
|
}
|
|
407
447
|
}
|
|
408
448
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aj-archipelago/cortex",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.37",
|
|
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,71 @@
|
|
|
1
|
+
// replicateApiPlugin.js
|
|
2
|
+
import ModelPlugin from "./modelPlugin.js";
|
|
3
|
+
import logger from "../../lib/logger.js";
|
|
4
|
+
|
|
5
|
+
class ReplicateApiPlugin extends ModelPlugin {
|
|
6
|
+
constructor(pathway, model) {
|
|
7
|
+
super(pathway, model);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Set up parameters specific to the Replicate 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
|
+
const requestParameters = {
|
|
20
|
+
input: {
|
|
21
|
+
aspect_ratio: "1:1",
|
|
22
|
+
output_format: "webp",
|
|
23
|
+
output_quality: 80,
|
|
24
|
+
prompt: modelPromptText,
|
|
25
|
+
//prompt_upsampling: false,
|
|
26
|
+
//safety_tolerance: 5,
|
|
27
|
+
go_fast: true,
|
|
28
|
+
megapixels: "1",
|
|
29
|
+
num_outputs: combinedParameters.numberResults,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return requestParameters;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Execute the request to the Replicate API
|
|
37
|
+
async execute(text, parameters, prompt, cortexRequest) {
|
|
38
|
+
const requestParameters = this.getRequestParameters(
|
|
39
|
+
text,
|
|
40
|
+
parameters,
|
|
41
|
+
prompt,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
cortexRequest.data = requestParameters;
|
|
45
|
+
cortexRequest.params = requestParameters.params;
|
|
46
|
+
|
|
47
|
+
return this.executeRequest(cortexRequest);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Parse the response from the Replicate API
|
|
51
|
+
parseResponse(data) {
|
|
52
|
+
if (data.data) {
|
|
53
|
+
return JSON.stringify(data.data);
|
|
54
|
+
}
|
|
55
|
+
return JSON.stringify(data);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Override the logging function to display the request and response
|
|
59
|
+
logRequestData(data, responseData, prompt) {
|
|
60
|
+
const modelInput = data?.input?.prompt;
|
|
61
|
+
|
|
62
|
+
logger.verbose(`${modelInput}`);
|
|
63
|
+
logger.verbose(`${this.parseResponse(responseData)}`);
|
|
64
|
+
|
|
65
|
+
prompt &&
|
|
66
|
+
prompt.debugInfo &&
|
|
67
|
+
(prompt.debugInfo += `\n${JSON.stringify(data)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default ReplicateApiPlugin;
|