@aj-archipelago/cortex 1.3.11 → 1.3.14

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 (29) hide show
  1. package/helper-apps/cortex-file-handler/.env.test +7 -0
  2. package/helper-apps/cortex-file-handler/.env.test.azure +6 -0
  3. package/helper-apps/cortex-file-handler/.env.test.gcs +9 -0
  4. package/helper-apps/cortex-file-handler/blobHandler.js +313 -204
  5. package/helper-apps/cortex-file-handler/constants.js +107 -0
  6. package/helper-apps/cortex-file-handler/docHelper.js +4 -1
  7. package/helper-apps/cortex-file-handler/fileChunker.js +170 -109
  8. package/helper-apps/cortex-file-handler/helper.js +82 -16
  9. package/helper-apps/cortex-file-handler/index.js +226 -146
  10. package/helper-apps/cortex-file-handler/localFileHandler.js +21 -3
  11. package/helper-apps/cortex-file-handler/package-lock.json +2622 -51
  12. package/helper-apps/cortex-file-handler/package.json +25 -4
  13. package/helper-apps/cortex-file-handler/redis.js +9 -18
  14. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +22 -0
  15. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +49 -0
  16. package/helper-apps/cortex-file-handler/scripts/test-azure.sh +34 -0
  17. package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +49 -0
  18. package/helper-apps/cortex-file-handler/start.js +39 -4
  19. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +292 -0
  20. package/helper-apps/cortex-file-handler/tests/docHelper.test.js +148 -0
  21. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +311 -0
  22. package/helper-apps/cortex-file-handler/tests/start.test.js +930 -0
  23. package/package.json +1 -1
  24. package/pathways/system/entity/sys_entity_continue.js +1 -1
  25. package/pathways/system/entity/sys_entity_start.js +1 -0
  26. package/pathways/system/entity/sys_generator_video_vision.js +2 -1
  27. package/pathways/system/entity/sys_router_tool.js +6 -4
  28. package/server/plugins/openAiWhisperPlugin.js +9 -13
  29. package/server/plugins/replicateApiPlugin.js +54 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.3.11",
3
+ "version": "1.3.14",
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": {
@@ -34,7 +34,7 @@ export default {
34
34
 
35
35
  const newArgs = {
36
36
  ...args,
37
- chatHistory: args.chatHistory.slice(-6)
37
+ chatHistory: args.chatHistory.slice(-20)
38
38
  };
39
39
 
40
40
  if (generatorPathway === 'sys_generator_document') {
@@ -179,6 +179,7 @@ export default {
179
179
  case "video":
180
180
  case "audio":
181
181
  case "pdf":
182
+ case "text":
182
183
  if (visionContentPresent) {
183
184
  toolCallbackName = 'sys_generator_video_vision';
184
185
  toolCallbackMessage = toolMessage;
@@ -4,7 +4,7 @@ export default {
4
4
  prompt:
5
5
  [
6
6
  new Prompt({ messages: [
7
- {"role": "system", "content": `{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n{{renderTemplate AI_EXPERTISE}}\nYou are the part of {{aiName}} that can view, hear, and understand media files of all sorts (images, videos, audio, pdfs, etc.) - you provide the capability to view and analyze media files that the user provides.\nMany of your subsystems cannot independently view or analyze media files, so make sure that you describe the details of what you see in the media files in your response so you can refer to the descriptions later. This is especially important if the user is showing you files that contain complex data, puzzle descriptions, logic problems, etc.\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\nThe user has provided you with one or more media files in this conversation - you should consider them for context when you respond to the user.\nIf you don't see any files, something has gone wrong in the upload and you should inform the user and have them try again.\n{{renderTemplate AI_DATETIME}}`},
7
+ {"role": "system", "content": `{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n{{renderTemplate AI_EXPERTISE}}\nYou are the part of {{aiName}} that can view, hear, and understand files of all sorts (images, videos, audio, pdfs, text, etc.) - you provide the capability to view and analyze files that the user provides.\nMany of your subsystems cannot independently view or analyze files, so make sure that you describe the details of what you see in the files in your response so you can refer to the descriptions later. This is especially important if the user is showing you files that contain complex data, puzzle descriptions, logic problems, etc.\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\nThe user has provided you with one or more files in this conversation - you should consider them for context when you respond to the user.\nIf you don't see any files, something has gone wrong in the upload and you should inform the user and have them try again.\n{{renderTemplate AI_DATETIME}}`},
8
8
  "{{chatHistory}}",
9
9
  ]}),
10
10
  ],
@@ -19,6 +19,7 @@ export default {
19
19
  useInputChunking: false,
20
20
  enableDuplicateRequests: false,
21
21
  timeout: 600,
22
+
22
23
  executePathway: async ({args, runAllPrompts, resolver}) => {
23
24
  const result = await runAllPrompts({ ...args });
24
25
  resolver.tool = JSON.stringify({ toolUsed: "vision" });
@@ -33,13 +33,15 @@ Available tools and their specific use cases:
33
33
 
34
34
  8. Reason: Employ for reasoning, scientific analysis, evaluating evidence, strategic planning, problem-solving, logic puzzles, mathematical calculations, or any questions that require careful thought or complex choices. Also use when deep, step-by-step reasoning is required.
35
35
 
36
- 9. PDF: Use specifically for processing and answering questions about PDF file content.
36
+ 9. PDF: Use specifically for analyzing and answering questions about PDF file content. Use this tool any time the user is asking you questions about a PDF file.
37
37
 
38
- 10. Vision: Engage for analyzing and responding to queries about image files (jpg, gif, bmp, png, etc).
38
+ 10. Text: Use specifically for analyzing and answering questions about text file content. Use this tool any time the user is asking you questions about a text file.
39
39
 
40
- 11. Video: Use for processing and answering questions about video or audio file content.
40
+ 11. Vision: Use specifically for analyzing and answering questions about image files (jpg, gif, bmp, png, etc). Use this tool any time the user is asking you questions about an uploaded image file.
41
41
 
42
- 12. Clarify: Use when you must have more information from the user to determine which tool to use. In this case your tool message should be one or more questions to the user to clarify their request.
42
+ 12. Video: Use specifically for analyzing and answering questions about video or audio file content. Use this tool any time the user is asking you questions about an uploaded video or audio file.
43
+
44
+ 13. Clarify: Use when you must have more information from the user to determine which tool to use. In this case your tool message should be one or more questions to the user to clarify their request.
43
45
 
44
46
  Tool Selection Guidelines:
45
47
  - Prioritize the most specific tool for the task at hand.
@@ -64,7 +64,6 @@ class OpenAIWhisperPlugin extends ModelPlugin {
64
64
  }
65
65
 
66
66
  const processTS = async (uri) => {
67
- const cortexRequest = new CortexRequest({ pathwayResolver });
68
67
 
69
68
  const tsparams = { fileurl:uri };
70
69
  const { language } = parameters;
@@ -81,8 +80,14 @@ class OpenAIWhisperPlugin extends ModelPlugin {
81
80
  }
82
81
  }
83
82
 
83
+ const cortexRequest = new CortexRequest({ pathwayResolver });
84
84
  cortexRequest.url = WHISPER_TS_API_URL;
85
85
  cortexRequest.data = tsparams;
86
+ const whisperInitCallback = (requestInstance) => {
87
+ requestInstance.url = WHISPER_TS_API_URL;
88
+ requestInstance.data = tsparams;
89
+ };
90
+ cortexRequest.initCallback = whisperInitCallback;
86
91
 
87
92
  const MAX_RETRIES = 3;
88
93
  let attempt = 0;
@@ -91,8 +96,8 @@ class OpenAIWhisperPlugin extends ModelPlugin {
91
96
  sendProgress(true, true);
92
97
  try {
93
98
  res = await this.executeRequest(cortexRequest);
94
- if(res.statusCode && res.statusCode >= 400){
95
- throw new Error(res.message || 'An error occurred.');
99
+ if(res?.statusCode && res?.statusCode >= 400){
100
+ throw new Error(res?.message || 'An error occurred.');
96
101
  }
97
102
  break;
98
103
  }
@@ -102,7 +107,7 @@ class OpenAIWhisperPlugin extends ModelPlugin {
102
107
  }
103
108
  }
104
109
 
105
- if (res.statusCode && res.statusCode >= 400) {
110
+ if (res?.statusCode && res?.statusCode >= 400) {
106
111
  throw new Error(res.message || 'An error occurred.');
107
112
  }
108
113
 
@@ -221,15 +226,6 @@ try {
221
226
 
222
227
  await markCompletedForCleanUp(requestId);
223
228
 
224
- //check cleanup for whisper temp uploaded files url
225
- const regex = /whispertempfiles\/([a-z0-9-]+)/;
226
- const match = file.match(regex);
227
- if (match && match[1]) {
228
- const extractedValue = match[1];
229
- await markCompletedForCleanUp(extractedValue);
230
- logger.info(`Cleaned temp whisper file ${file} with request id ${extractedValue}`);
231
- }
232
-
233
229
  } catch (error) {
234
230
  logger.error(`An error occurred while deleting: ${error}`);
235
231
  }
@@ -1,6 +1,7 @@
1
1
  // replicateApiPlugin.js
2
2
  import ModelPlugin from "./modelPlugin.js";
3
3
  import logger from "../../lib/logger.js";
4
+ import axios from "axios";
4
5
 
5
6
  class ReplicateApiPlugin extends ModelPlugin {
6
7
  constructor(pathway, model) {
@@ -106,10 +107,61 @@ class ReplicateApiPlugin extends ModelPlugin {
106
107
  cortexRequest.data = requestParameters;
107
108
  cortexRequest.params = requestParameters.params;
108
109
 
109
- return this.executeRequest(cortexRequest);
110
+ // Make initial request to start prediction
111
+ const stringifiedResponse = await this.executeRequest(cortexRequest);
112
+ const parsedResponse = JSON.parse(stringifiedResponse);
113
+
114
+ // If we got a completed response, return it
115
+ if (parsedResponse?.status === "succeeded") {
116
+ return stringifiedResponse;
117
+ }
118
+
119
+ logger.info("Replicate API returned a non-completed response.");
120
+
121
+ if (!parsedResponse?.id) {
122
+ throw new Error("No prediction ID returned from Replicate API");
123
+ }
124
+
125
+ // Get the prediction ID and polling URL
126
+ const predictionId = parsedResponse.id;
127
+ const pollUrl = parsedResponse.urls?.get;
128
+
129
+ if (!pollUrl) {
130
+ throw new Error("No polling URL returned from Replicate API");
131
+ }
132
+
133
+ // Poll for results
134
+ const maxAttempts = 60; // 5 minutes with 5 second intervals
135
+ const pollInterval = 5000;
136
+
137
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
138
+ try {
139
+ const pollResponse = await axios.get(pollUrl, {
140
+ headers: cortexRequest.headers
141
+ });
142
+
143
+ logger.info("Polling Replicate API - attempt " + attempt);
144
+ const status = pollResponse.data?.status;
145
+
146
+ if (status === "succeeded") {
147
+ logger.info("Replicate API returned a completed response after polling");
148
+ return JSON.stringify(pollResponse.data);
149
+ } else if (status === "failed" || status === "canceled") {
150
+ throw new Error(`Prediction ${status}: ${pollResponse.data?.error || "Unknown error"}`);
151
+ }
152
+
153
+ // Wait before next poll
154
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
155
+ } catch (error) {
156
+ logger.error(`Error polling prediction ${predictionId}: ${error.message}`);
157
+ throw error;
158
+ }
159
+ }
160
+
161
+ throw new Error(`Prediction ${predictionId} timed out after ${maxAttempts * pollInterval / 1000} seconds`);
110
162
  }
111
163
 
112
- // Parse the response from the Replicate API
164
+ // Stringify the response from the Replicate API
113
165
  parseResponse(data) {
114
166
  if (data.data) {
115
167
  return JSON.stringify(data.data);