@aj-archipelago/cortex 1.1.35 → 1.1.36

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 `PATHWAY_PUBLISH_KEY` environment variable must be set to enable pathway publishing.
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.
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "storageType": "azure",
3
3
  "filePath": "./pathways/dynamic/pathways.json",
4
+ "publishKey": "development"
4
5
  }
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);
@@ -1,8 +1,8 @@
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
- const { PATHWAY_PUBLISH_KEY } = process.env;
6
6
 
7
7
  class StorageStrategy {
8
8
  async load() { throw new Error('Not implemented'); }
@@ -20,6 +20,8 @@ class LocalStorage extends StorageStrategy {
20
20
  if (!fs.existsSync(this.filePath)) {
21
21
  // create it. log
22
22
  logger.info(`Creating dynamic pathways local file: ${this.filePath}`);
23
+ // create directory if it doesn't exist
24
+ await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });
23
25
  await fs.promises.writeFile(this.filePath, JSON.stringify({}));
24
26
  }
25
27
 
@@ -219,6 +221,7 @@ class S3Storage extends StorageStrategy {
219
221
  class PathwayManager {
220
222
  constructor(config, basePathway) {
221
223
  this.storage = this.getStorageStrategy(config);
224
+ this.publishKey = config.publishKey;
222
225
  this.pathways = {};
223
226
  this.lastUpdated = 0;
224
227
  this.basePathway = basePathway;
@@ -226,6 +229,10 @@ class PathwayManager {
226
229
  if (config.storageType === 'local') {
227
230
  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
231
  }
232
+
233
+ if (!this.publishKey) {
234
+ logger.warn('WARNING: dynamicPathwaysConfig.publishKey is not set. Dynamic pathways will not be editable in this instance of Cortex.');
235
+ }
229
236
  }
230
237
 
231
238
  getStorageStrategy(config) {
@@ -283,6 +290,7 @@ class PathwayManager {
283
290
  throw new Error('Both userId and secret are mandatory for adding or updating a pathway');
284
291
  }
285
292
 
293
+ await this.getLatestPathways();
286
294
  this.pathways[userId] = this.pathways[userId] || {};
287
295
 
288
296
  if (this.pathways[userId][name] && this.pathways[userId][name].secret !== secret) {
@@ -296,9 +304,12 @@ class PathwayManager {
296
304
  }
297
305
 
298
306
  async removePathway(name, userId, secret) {
307
+ await this.getLatestPathways();
308
+
299
309
  if (!this.pathways[userId] || !this.pathways[userId][name]) {
300
310
  return;
301
311
  }
312
+
302
313
  if (this.pathways[userId][name].secret !== secret) {
303
314
  throw new Error('Invalid secret');
304
315
  }
@@ -344,11 +355,11 @@ class PathwayManager {
344
355
  return {
345
356
  Mutation: {
346
357
  putPathway: async (_, { name, pathway, userId, secret, displayName, key }) => {
347
- if (!PATHWAY_PUBLISH_KEY) {
358
+ if (!this.publishKey) {
348
359
  throw new Error("Invalid configuration. Pathway publishing key is not configured in Cortex.")
349
360
  }
350
361
 
351
- if (key !== PATHWAY_PUBLISH_KEY) {
362
+ if (key !== this.publishKey) {
352
363
  throw new Error('Invalid pathway publishing key. The key provided did not match the key configured in Cortex.');
353
364
  }
354
365
 
@@ -360,10 +371,10 @@ class PathwayManager {
360
371
  }
361
372
  },
362
373
  deletePathway: async (_, { name, userId, secret, key }) => {
363
- if (!PATHWAY_PUBLISH_KEY) {
374
+ if (!this.publishKey) {
364
375
  throw new Error("Invalid configuration. Pathway publishing key is not configured in Cortex.")
365
376
  }
366
- if (key !== PATHWAY_PUBLISH_KEY) {
377
+ if (key !== this.publishKey) {
367
378
  throw new Error('Invalid pathway publishing key. The key provided did not match the key configured in Cortex.');
368
379
  }
369
380
 
@@ -378,7 +389,7 @@ class PathwayManager {
378
389
  };
379
390
  }
380
391
 
381
- async getPathways() {
392
+ async getLatestPathways() {
382
393
  try {
383
394
  const currentTimestamp = await this.storage.getLastModified();
384
395
 
@@ -390,13 +401,13 @@ class PathwayManager {
390
401
 
391
402
  return this.pathways;
392
403
  } catch (error) {
393
- logger.error('Error in getPathways:', error);
404
+ logger.error('Error in getLatestPathways:', error);
394
405
  throw error;
395
406
  }
396
407
  }
397
408
 
398
409
  async getPathway(userId, pathwayName) {
399
- const pathways = await this.getPathways();
410
+ const pathways = await this.getLatestPathways();
400
411
 
401
412
  if (!pathways[userId] || !pathways[userId][pathwayName]) {
402
413
  throw new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.1.35",
3
+ "version": "1.1.36",
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;