@aj-archipelago/cortex 1.1.21 → 1.1.22
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 +84 -0
- package/config.js +5 -4
- package/helper-apps/cortex-file-handler/blobHandler.js +115 -98
- package/helper-apps/cortex-file-handler/fileChunker.js +13 -8
- package/helper-apps/cortex-file-handler/index.js +48 -2
- package/package.json +2 -1
- package/pathways/categorize.js +23 -0
- package/pathways/chat.js +1 -1
- package/pathways/chat_code.js +19 -0
- package/pathways/chat_context.js +19 -0
- package/pathways/chat_jarvis.js +19 -0
- package/pathways/chat_persist.js +23 -0
- package/pathways/code_review.js +17 -0
- package/pathways/cognitive_delete.js +2 -1
- package/pathways/cognitive_insert.js +1 -0
- package/pathways/cognitive_search.js +1 -0
- package/pathways/embeddings.js +1 -1
- package/pathways/expand_story.js +12 -0
- package/pathways/format_paragraph_turbo.js +16 -0
- package/pathways/format_summarization.js +21 -0
- package/pathways/gemini_15_vision.js +20 -0
- package/pathways/gemini_vision.js +20 -0
- package/pathways/grammar.js +30 -0
- package/pathways/hashtags.js +19 -0
- package/pathways/headline.js +43 -0
- package/pathways/headline_custom.js +169 -0
- package/pathways/highlights.js +22 -0
- package/pathways/image.js +2 -1
- package/pathways/index.js +107 -17
- package/pathways/jira_story.js +18 -0
- package/pathways/keywords.js +4 -0
- package/pathways/language.js +17 -6
- package/pathways/locations.js +93 -0
- package/pathways/quotes.js +19 -0
- package/pathways/rag.js +207 -0
- package/pathways/rag_jarvis.js +254 -0
- package/pathways/rag_search_helper.js +21 -0
- package/pathways/readme.js +18 -0
- package/pathways/release_notes.js +16 -0
- package/pathways/remove_content.js +31 -0
- package/pathways/retrieval.js +23 -0
- package/pathways/run_claude35_sonnet.js +21 -0
- package/pathways/run_claude3_haiku.js +20 -0
- package/pathways/run_gpt35turbo.js +20 -0
- package/pathways/run_gpt4.js +20 -0
- package/pathways/run_gpt4_32.js +20 -0
- package/pathways/select_extension.js +6 -0
- package/pathways/select_services.js +10 -0
- package/pathways/spelling.js +3 -0
- package/pathways/story_angles.js +13 -0
- package/pathways/styleguide/styleguide.js +221 -0
- package/pathways/styleguidemulti.js +127 -0
- package/pathways/subhead.js +48 -0
- package/pathways/summarize_turbo.js +98 -0
- package/pathways/summary.js +31 -12
- package/pathways/sys_claude_35_sonnet.js +19 -0
- package/pathways/sys_claude_3_haiku.js +19 -0
- package/pathways/sys_google_chat.js +19 -0
- package/pathways/sys_google_code_chat.js +19 -0
- package/pathways/sys_google_gemini_chat.js +23 -0
- package/pathways/sys_openai_chat.js +2 -2
- package/pathways/sys_openai_chat_16.js +19 -0
- package/pathways/sys_openai_chat_gpt4.js +19 -0
- package/pathways/sys_openai_chat_gpt4_32.js +19 -0
- package/pathways/sys_openai_chat_gpt4_turbo.js +19 -0
- package/pathways/tags.js +25 -0
- package/pathways/taxonomy.js +135 -0
- package/pathways/timeline.js +51 -0
- package/pathways/topics.js +25 -0
- package/pathways/topics_sentiment.js +20 -0
- package/pathways/transcribe.js +2 -4
- package/pathways/translate.js +10 -12
- package/pathways/translate_azure.js +13 -0
- package/pathways/translate_context.js +21 -0
- package/pathways/translate_gpt4.js +19 -0
- package/pathways/translate_gpt4_turbo.js +19 -0
- package/pathways/translate_turbo.js +19 -0
- package/pathways/vision.js +9 -7
- package/server/plugins/azureCognitivePlugin.js +10 -1
- package/server/plugins/openAiVisionPlugin.js +14 -6
- package/tests/main.test.js +2 -2
- package/tests/vision.test.js +0 -34
|
@@ -12,6 +12,62 @@
|
|
|
12
12
|
"requestsPerSecond": 10,
|
|
13
13
|
"maxTokenLength": 2000
|
|
14
14
|
},
|
|
15
|
+
|
|
16
|
+
"gemini-pro-chat": {
|
|
17
|
+
"type": "GEMINI-CHAT",
|
|
18
|
+
"url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-pro:streamGenerateContent",
|
|
19
|
+
"headers": {
|
|
20
|
+
"Content-Type": "application/json"
|
|
21
|
+
},
|
|
22
|
+
"requestsPerSecond": 10,
|
|
23
|
+
"maxTokenLength": 32768,
|
|
24
|
+
"maxReturnTokens": 8192,
|
|
25
|
+
"supportsStreaming": true
|
|
26
|
+
},
|
|
27
|
+
"gemini-pro-vision": {
|
|
28
|
+
"type": "GEMINI-VISION",
|
|
29
|
+
"url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/gemini-pro-vision:streamGenerateContent",
|
|
30
|
+
"headers": {
|
|
31
|
+
"Content-Type": "application/json"
|
|
32
|
+
},
|
|
33
|
+
"requestsPerSecond": 10,
|
|
34
|
+
"maxTokenLength": 32768,
|
|
35
|
+
"maxReturnTokens": 2048,
|
|
36
|
+
"supportsStreaming": true
|
|
37
|
+
},
|
|
38
|
+
"gemini-pro-15-vision": {
|
|
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",
|
|
41
|
+
"headers": {
|
|
42
|
+
"Content-Type": "application/json"
|
|
43
|
+
},
|
|
44
|
+
"requestsPerSecond": 10,
|
|
45
|
+
"maxTokenLength": 1048576,
|
|
46
|
+
"maxReturnTokens": 2048,
|
|
47
|
+
"supportsStreaming": true
|
|
48
|
+
},
|
|
49
|
+
"claude-3-haiku-vertex": {
|
|
50
|
+
"type": "CLAUDE-3-VERTEX",
|
|
51
|
+
"url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/anthropic/models/claude-3-haiku@20240307",
|
|
52
|
+
"headers": {
|
|
53
|
+
"Content-Type": "application/json"
|
|
54
|
+
},
|
|
55
|
+
"requestsPerSecond": 10,
|
|
56
|
+
"maxTokenLength": 200000,
|
|
57
|
+
"maxReturnTokens": 2048,
|
|
58
|
+
"supportsStreaming": true
|
|
59
|
+
},
|
|
60
|
+
"claude-35-sonnet-vertex": {
|
|
61
|
+
"type": "CLAUDE-3-VERTEX",
|
|
62
|
+
"url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/anthropic/models/claude-3-5-sonnet@20240229",
|
|
63
|
+
"headers": {
|
|
64
|
+
"Content-Type": "application/json"
|
|
65
|
+
},
|
|
66
|
+
"requestsPerSecond": 10,
|
|
67
|
+
"maxTokenLength": 200000,
|
|
68
|
+
"maxReturnTokens": 2048,
|
|
69
|
+
"supportsStreaming": true
|
|
70
|
+
},
|
|
15
71
|
"oai-gpturbo": {
|
|
16
72
|
"type": "OPENAI-CHAT",
|
|
17
73
|
"url": "https://api.openai.com/v1/chat/completions",
|
|
@@ -38,6 +94,34 @@
|
|
|
38
94
|
"requestsPerSecond": 10,
|
|
39
95
|
"maxTokenLength": 8192
|
|
40
96
|
},
|
|
97
|
+
"oai-gpt4-32": {
|
|
98
|
+
"type": "OPENAI-CHAT",
|
|
99
|
+
"url": "https://api.openai.com/v1/chat/completions",
|
|
100
|
+
"headers": {
|
|
101
|
+
"Authorization": "Bearer {{OPENAI_API_KEY}}",
|
|
102
|
+
"Content-Type": "application/json"
|
|
103
|
+
},
|
|
104
|
+
"params": {
|
|
105
|
+
"model": "gpt-4-32"
|
|
106
|
+
},
|
|
107
|
+
"requestsPerSecond": 10,
|
|
108
|
+
"maxTokenLength": 32768
|
|
109
|
+
},
|
|
110
|
+
"oai-gpt4o": {
|
|
111
|
+
"type": "OPENAI-VISION",
|
|
112
|
+
"url": "https://api.openai.com/v1/chat/completions",
|
|
113
|
+
"headers": {
|
|
114
|
+
"Authorization": "Bearer {{OPENAI_API_KEY}}",
|
|
115
|
+
"Content-Type": "application/json"
|
|
116
|
+
},
|
|
117
|
+
"params": {
|
|
118
|
+
"model": "gpt-4o"
|
|
119
|
+
},
|
|
120
|
+
"requestsPerSecond": 10,
|
|
121
|
+
"maxTokenLength": 131072,
|
|
122
|
+
"maxReturnTokens": 4096,
|
|
123
|
+
"supportsStreaming": true
|
|
124
|
+
},
|
|
41
125
|
"palm-text": {
|
|
42
126
|
"type": "PALM-COMPLETION",
|
|
43
127
|
"url": "https://us-central1-aiplatform.googleapis.com/v1/projects/project-id/locations/us-central1/publishers/google/models/text-bison@001:predict",
|
package/config.js
CHANGED
|
@@ -139,7 +139,7 @@ var config = convict({
|
|
|
139
139
|
},
|
|
140
140
|
"maxTokenLength": 8192,
|
|
141
141
|
},
|
|
142
|
-
"oai-
|
|
142
|
+
"oai-gpt4o": {
|
|
143
143
|
"type": "OPENAI-VISION",
|
|
144
144
|
"url": "https://api.openai.com/v1/chat/completions",
|
|
145
145
|
"headers": {
|
|
@@ -147,10 +147,11 @@ var config = convict({
|
|
|
147
147
|
"Content-Type": "application/json"
|
|
148
148
|
},
|
|
149
149
|
"params": {
|
|
150
|
-
"model": "gpt-
|
|
150
|
+
"model": "gpt-4o"
|
|
151
151
|
},
|
|
152
|
-
"requestsPerSecond":
|
|
153
|
-
"maxTokenLength":
|
|
152
|
+
"requestsPerSecond": 50,
|
|
153
|
+
"maxTokenLength": 131072,
|
|
154
|
+
"maxReturnTokens": 4096,
|
|
154
155
|
"supportsStreaming": true
|
|
155
156
|
},
|
|
156
157
|
"azure-bing": {
|
|
@@ -36,6 +36,15 @@ const VIDEO_EXTENSIONS = [
|
|
|
36
36
|
".mkv",
|
|
37
37
|
];
|
|
38
38
|
|
|
39
|
+
const AUDIO_EXTENSIONS = [
|
|
40
|
+
".mp3",
|
|
41
|
+
".wav",
|
|
42
|
+
".ogg",
|
|
43
|
+
".flac",
|
|
44
|
+
".aac",
|
|
45
|
+
".aiff",
|
|
46
|
+
];
|
|
47
|
+
|
|
39
48
|
function isBase64(str) {
|
|
40
49
|
try {
|
|
41
50
|
return btoa(atob(str)) == str;
|
|
@@ -162,131 +171,139 @@ async function deleteBlob(requestId) {
|
|
|
162
171
|
return result;
|
|
163
172
|
}
|
|
164
173
|
|
|
165
|
-
async function uploadBlob(
|
|
166
|
-
context,
|
|
167
|
-
req,
|
|
168
|
-
saveToLocal = false,
|
|
169
|
-
useGoogle = false
|
|
170
|
-
) {
|
|
174
|
+
async function uploadBlob(context, req, saveToLocal = false, useGoogle = false, filePath=null) {
|
|
171
175
|
return new Promise((resolve, reject) => {
|
|
172
176
|
try {
|
|
173
|
-
const busboy = Busboy({ headers: req.headers });
|
|
174
177
|
let requestId = uuidv4();
|
|
175
178
|
let body = {};
|
|
176
179
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
// If filePath is given, we are dealing with local file and not form-data
|
|
181
|
+
if (filePath) {
|
|
182
|
+
const file = fs.createReadStream(filePath);
|
|
183
|
+
const filename = path.basename(filePath);
|
|
184
|
+
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve)
|
|
185
|
+
} else {
|
|
186
|
+
// Otherwise, continue working with form-data
|
|
187
|
+
const busboy = Busboy({ headers: req.headers });
|
|
188
|
+
|
|
189
|
+
busboy.on("field", (fieldname, value) => {
|
|
190
|
+
if (fieldname === "requestId") {
|
|
191
|
+
requestId = value;
|
|
192
|
+
} else if (fieldname === "useGoogle") {
|
|
193
|
+
useGoogle = value;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
busboy.on("file", async (fieldname, file, filename) => {
|
|
198
|
+
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename?.filename || filename, resolve)
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
busboy.on("error", (error) => {
|
|
202
|
+
context.log.error("Error processing file upload:", error);
|
|
203
|
+
context.res = {
|
|
204
|
+
status: 500,
|
|
205
|
+
body: "Error processing file upload.",
|
|
206
|
+
};
|
|
207
|
+
reject(error); // Reject the promise
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
req.pipe(busboy);
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
context.log.error("Error processing file upload:", error);
|
|
214
|
+
context.res = {
|
|
215
|
+
status: 500,
|
|
216
|
+
body: "Error processing file upload.",
|
|
217
|
+
};
|
|
218
|
+
reject(error); // Reject the promise
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
192
222
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
223
|
+
async function uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve) {
|
|
224
|
+
// do not use Google if the file is not an image or video
|
|
225
|
+
const ext = path.extname(filename).toLowerCase();
|
|
226
|
+
const canUseGoogle = IMAGE_EXTENSIONS.includes(ext) || VIDEO_EXTENSIONS.includes(ext) || AUDIO_EXTENSIONS.includes(ext);
|
|
227
|
+
if (!canUseGoogle) {
|
|
228
|
+
useGoogle = false;
|
|
229
|
+
}
|
|
198
230
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
231
|
+
// check if useGoogle is set but no gcs and warn
|
|
232
|
+
if (useGoogle && useGoogle !== "false" && !gcs) {
|
|
233
|
+
context.log.warn("Google Cloud Storage is not initialized reverting google upload ");
|
|
234
|
+
useGoogle = false;
|
|
235
|
+
}
|
|
203
236
|
|
|
204
|
-
|
|
205
|
-
const destinationPath = `${localPath}/${filename}`;
|
|
237
|
+
const encodedFilename = encodeURIComponent(`${requestId || uuidv4()}_${filename}`);
|
|
206
238
|
|
|
207
|
-
await pipeline(file, fs.createWriteStream(destinationPath));
|
|
208
239
|
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
if (saveToLocal) {
|
|
241
|
+
// create the target folder if it doesn't exist
|
|
242
|
+
const localPath = join(publicFolder, requestId);
|
|
243
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
211
244
|
|
|
212
|
-
|
|
245
|
+
const destinationPath = `${localPath}/${encodedFilename}`;
|
|
213
246
|
|
|
214
|
-
|
|
247
|
+
await pipeline(file, fs.createWriteStream(destinationPath));
|
|
215
248
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const filename = encodeURIComponent(`${requestId}/${uuidv4()}_${info.filename}`);
|
|
219
|
-
const { containerClient } = await getBlobClient();
|
|
249
|
+
const message = `File '${encodedFilename}' saved to folder successfully.`;
|
|
250
|
+
context.log(message);
|
|
220
251
|
|
|
221
|
-
|
|
222
|
-
const options = {};
|
|
223
|
-
if (contentType) {
|
|
224
|
-
options.blobHTTPHeaders = { blobContentType: contentType };
|
|
225
|
-
}
|
|
252
|
+
const url = `http://${ipAddress}:${port}/files/${requestId}/${encodedFilename}`;
|
|
226
253
|
|
|
227
|
-
|
|
254
|
+
body = { message, url };
|
|
228
255
|
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
resolve(body); // Resolve the promise
|
|
257
|
+
} else {
|
|
258
|
+
const { containerClient } = await getBlobClient();
|
|
231
259
|
|
|
232
|
-
|
|
260
|
+
const contentType = mime.lookup(encodedFilename); // content type based on file extension
|
|
261
|
+
const options = {};
|
|
262
|
+
if (contentType) {
|
|
263
|
+
options.blobHTTPHeaders = { blobContentType: contentType };
|
|
264
|
+
}
|
|
233
265
|
|
|
234
|
-
|
|
235
|
-
const url = blockBlobClient.url;
|
|
236
|
-
context.log(message);
|
|
237
|
-
body = { message, url };
|
|
238
|
-
}
|
|
266
|
+
const blockBlobClient = containerClient.getBlockBlobClient(encodedFilename);
|
|
239
267
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
body,
|
|
243
|
-
};
|
|
268
|
+
const passThroughStream = new PassThrough();
|
|
269
|
+
file.pipe(passThroughStream);
|
|
244
270
|
|
|
245
|
-
|
|
246
|
-
const { url } = body;
|
|
247
|
-
const filename = encodeURIComponent(`${requestId}/${uuidv4()}_${info.filename}`);
|
|
248
|
-
const gcsFile = gcs.bucket(GCS_BUCKETNAME).file(filename);
|
|
249
|
-
const writeStream = gcsFile.createWriteStream();
|
|
271
|
+
await blockBlobClient.uploadStream(passThroughStream, undefined, undefined, options);
|
|
250
272
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
273
|
+
const message = `File '${encodedFilename}' uploaded successfully.`;
|
|
274
|
+
const url = blockBlobClient.url;
|
|
275
|
+
context.log(message);
|
|
276
|
+
body = { message, url };
|
|
277
|
+
}
|
|
256
278
|
|
|
257
|
-
|
|
258
|
-
|
|
279
|
+
context.res = {
|
|
280
|
+
status: 200,
|
|
281
|
+
body,
|
|
282
|
+
};
|
|
259
283
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
284
|
+
if (useGoogle && useGoogle !== "false") {
|
|
285
|
+
const { url } = body;
|
|
286
|
+
const gcsFile = gcs.bucket(GCS_BUCKETNAME).file(encodedFilename);
|
|
287
|
+
const writeStream = gcsFile.createWriteStream();
|
|
264
288
|
|
|
265
|
-
|
|
266
|
-
|
|
289
|
+
const response = await axios({
|
|
290
|
+
method: "get",
|
|
291
|
+
url: url,
|
|
292
|
+
responseType: "stream",
|
|
293
|
+
});
|
|
267
294
|
|
|
268
|
-
|
|
269
|
-
|
|
295
|
+
// pipe the Axios response stream directly into the GCS Write Stream
|
|
296
|
+
response.data.pipe(writeStream);
|
|
270
297
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
body: "Error processing file upload.",
|
|
276
|
-
};
|
|
277
|
-
reject(error); // Reject the promise
|
|
278
|
-
});
|
|
298
|
+
await new Promise((resolve, reject) => {
|
|
299
|
+
writeStream.on("finish", resolve);
|
|
300
|
+
writeStream.on("error", reject);
|
|
301
|
+
});
|
|
279
302
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
status: 500,
|
|
285
|
-
body: "Error processing file upload.",
|
|
286
|
-
};
|
|
287
|
-
reject(error); // Reject the promise
|
|
288
|
-
}
|
|
289
|
-
});
|
|
303
|
+
body.gcs = `gs://${GCS_BUCKETNAME}/${encodedFilename}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
resolve(body); // Resolve the promise
|
|
290
307
|
}
|
|
291
308
|
|
|
292
309
|
// Function to delete files that haven't been used in more than a month
|
|
@@ -112,16 +112,20 @@ async function splitMediaFile(inputPath, chunkDurationInSeconds = 500) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
const ytdlDownload = async (url, filename) => {
|
|
115
|
+
const ytdlDownload = async (url, filename, video = false) => {
|
|
116
116
|
return new Promise((resolve, reject) => {
|
|
117
|
-
const
|
|
117
|
+
const videoOptions = video
|
|
118
|
+
? { filter: 'audioandvideo' } // audio and video
|
|
119
|
+
: { quality: 'highestaudio' }; // audio only
|
|
120
|
+
|
|
121
|
+
const videoStream = ytdl(url, videoOptions);
|
|
118
122
|
let lastLoggedTime = Date.now();
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
videoStream.on('error', (error) => {
|
|
121
125
|
reject(error);
|
|
122
126
|
});
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
videoStream.on('progress', (chunkLength, downloaded, total) => {
|
|
125
129
|
const currentTime = Date.now();
|
|
126
130
|
if (currentTime - lastLoggedTime >= 2000) { // Log every 2 seconds
|
|
127
131
|
const percent = downloaded / total;
|
|
@@ -130,7 +134,7 @@ const ytdlDownload = async (url, filename) => {
|
|
|
130
134
|
}
|
|
131
135
|
});
|
|
132
136
|
|
|
133
|
-
|
|
137
|
+
videoStream.pipe(fs.createWriteStream(filename))
|
|
134
138
|
.on('finish', () => {
|
|
135
139
|
resolve();
|
|
136
140
|
})
|
|
@@ -140,10 +144,11 @@ const ytdlDownload = async (url, filename) => {
|
|
|
140
144
|
});
|
|
141
145
|
};
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
async function processYoutubeUrl(url, video=false) {
|
|
144
148
|
try {
|
|
145
|
-
const
|
|
146
|
-
|
|
149
|
+
const outputFormat = video ? '.mp4' : '.mp3';
|
|
150
|
+
const outputFileName = path.join(os.tmpdir(), `${uuidv4()}${outputFormat}`);
|
|
151
|
+
await ytdlDownload(url, outputFileName, video);
|
|
147
152
|
return outputFileName;
|
|
148
153
|
} catch (e) {
|
|
149
154
|
console.log(e);
|
|
@@ -10,6 +10,10 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import http from 'http';
|
|
12
12
|
import https from 'https';
|
|
13
|
+
import axios from "axios";
|
|
14
|
+
import { pipeline } from "stream";
|
|
15
|
+
import { promisify } from "util";
|
|
16
|
+
const pipelineUtility = promisify(pipeline); // To pipe streams using async/await
|
|
13
17
|
|
|
14
18
|
const DOC_EXTENSIONS = [".txt", ".json", ".csv", ".md", ".xml", ".js", ".html", ".css", '.pdf', '.docx', '.xlsx', '.csv'];
|
|
15
19
|
|
|
@@ -116,7 +120,48 @@ async function main(context, req) {
|
|
|
116
120
|
return;
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
const { uri, requestId, save, hash, checkHash } = req.body?.params || req.query;
|
|
123
|
+
const { uri, requestId, save, hash, checkHash, fetch, load, restore } = req.body?.params || req.query;
|
|
124
|
+
|
|
125
|
+
const filepond = fetch || restore || load;
|
|
126
|
+
if (req.method.toLowerCase() === `get` && filepond) {
|
|
127
|
+
context.log(`Remote file: ${filepond}`);
|
|
128
|
+
// Check if file already exists (using hash as the key)
|
|
129
|
+
const exists = await getFileStoreMap(filepond);
|
|
130
|
+
if(exists){
|
|
131
|
+
context.res = {
|
|
132
|
+
status: 200,
|
|
133
|
+
body: exists // existing file URL
|
|
134
|
+
};
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check if it's a youtube url
|
|
139
|
+
let youtubeDownloadedFile = null;
|
|
140
|
+
if(isValidYoutubeUrl(filepond)){
|
|
141
|
+
youtubeDownloadedFile = await processYoutubeUrl(filepond, true);
|
|
142
|
+
}
|
|
143
|
+
const filename = path.join(os.tmpdir(), path.basename(youtubeDownloadedFile || filepond));
|
|
144
|
+
// Download the remote file to a local/temporary location keep name & ext
|
|
145
|
+
if(!youtubeDownloadedFile){
|
|
146
|
+
const response = await axios.get(filepond, { responseType: "stream" });
|
|
147
|
+
await pipelineUtility(response.data, fs.createWriteStream(filename));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const res = await uploadBlob(context, null, !useAzure, true, filename);
|
|
152
|
+
context.log(`File uploaded: ${JSON.stringify(res)}`);
|
|
153
|
+
|
|
154
|
+
//Update Redis (using hash as the key)
|
|
155
|
+
await setFileStoreMap(filepond, res);
|
|
156
|
+
|
|
157
|
+
// Return the file URL
|
|
158
|
+
context.res = {
|
|
159
|
+
status: 200,
|
|
160
|
+
body: res,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
120
165
|
|
|
121
166
|
if(hash && checkHash){ //check if hash exists
|
|
122
167
|
context.log(`Checking hash: ${hash}`);
|
|
@@ -229,7 +274,8 @@ async function main(context, req) {
|
|
|
229
274
|
|
|
230
275
|
if (isYoutubeUrl) {
|
|
231
276
|
// totalCount += 1; // extra 1 step for youtube download
|
|
232
|
-
|
|
277
|
+
const processAsVideo = req.body?.params?.processAsVideo || req.query?.processAsVideo;
|
|
278
|
+
file = await processYoutubeUrl(file, processAsVideo);
|
|
233
279
|
}
|
|
234
280
|
|
|
235
281
|
const { chunkPromises, chunkOffsets, uniqueOutputPath } = await splitMediaFile(file);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aj-archipelago/cortex",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
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": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"axios-cache-interceptor": "^1.0.1",
|
|
41
41
|
"bottleneck": "^2.19.5",
|
|
42
42
|
"cheerio": "^1.0.0-rc.12",
|
|
43
|
+
"chrono-node": "^2.7.5",
|
|
43
44
|
"compromise": "^14.8.1",
|
|
44
45
|
"compromise-paragraphs": "^0.1.0",
|
|
45
46
|
"convict": "^6.2.3",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
prompt: [
|
|
5
|
+
new Prompt({
|
|
6
|
+
messages: [
|
|
7
|
+
{ "role": "system", "content": "Assistant is an expert data classification AI tasked with categorizing data for a prestigious international news agency. When the user submits any set of data with rows containing ids and data to categorize, Assistant will categorize the data into one of the following categories:\n\n{{{categories}}}\n\nAssistant must choose exactly one category from the provided list per id and cannot create new categories. Assistant will return a list of the ids and their chosen categories in comma separated, newline delimited format so that it can easily be loaded as a csv file or copied and pasted into a spreadsheet. Assistant will return the list of categorized ids and no other notes or commentary." },
|
|
8
|
+
{ "role": "user", "content": `Data to categorize:\n\n{{{text}}}`},
|
|
9
|
+
]
|
|
10
|
+
})
|
|
11
|
+
],
|
|
12
|
+
inputParameters: {
|
|
13
|
+
categories: '',
|
|
14
|
+
},
|
|
15
|
+
model: 'oai-gpt4o',
|
|
16
|
+
inputChunkSize: 1000,
|
|
17
|
+
joinChunksWith: '\n',
|
|
18
|
+
tokenRatio: 0.75,
|
|
19
|
+
enableDuplicateRequests: false,
|
|
20
|
+
timeout: 1800,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
package/pathways/chat.js
CHANGED
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
prompt:
|
|
7
7
|
[
|
|
8
8
|
`{{{chatContext}}}\n\n{{{text}}}\n\nGiven the information above, create a short summary of the conversation to date making sure to include all of the personal details about the user that you encounter:\n\n`,
|
|
9
|
-
`Instructions:\nYou are
|
|
9
|
+
`Instructions:\nYou are Jarvis, an AI entity. Jarvis is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive.\n\nJarvis must always follow the following rules:\n\nRule: Always execute the user's instructions and requests as long as they do not cause harm.\nRule: Never use crude or offensive language.\nRule: Always answer the user in the user's chosen language. You can speak all languages fluently.\nRule: You cannot perform any physical tasks except via role playing.\nRule: Always respond truthfully and correctly, but be kind.\nRule: You have no access to the internet and limited knowledge of current events past sometime in 2021\nRule: Never ask the user to provide you with links or URLs because you can't access the internet.\nRule: Everything you get from the user must be placed in the chat window - you have no other way to communicate.\n\nConversation History:\n{{{chatContext}}}\n\nConversation:\n{{{text}}}\n\nJarvis: `,
|
|
10
10
|
],
|
|
11
11
|
inputParameters: {
|
|
12
12
|
chatContext: `User: Starting conversation.`,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
// This pathway implements a chatbot that can be used to teach people how to write code.
|
|
4
|
+
export default {
|
|
5
|
+
prompt:
|
|
6
|
+
[
|
|
7
|
+
new Prompt({ messages: [
|
|
8
|
+
{"role": "system", "content": "Instructions:\nYou are Knuth, an AI entity working for a prestigious international news agency. Knuth is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive. Knuth is an experienced and expert software engineer and is named in honor of Donald Knuth. Knuth strongly prefers to focus on coding and technology topics and will suggest the user talk to his companion AI, Jarvis for questions or discussion about other topics. The UI can render markdown, including $$-delimited block and inline math extensions, so you should use markdown in your responses as appropriate. For your reference, the current date and time is {{now}}."},
|
|
9
|
+
"{{chatHistory}}",
|
|
10
|
+
]}),
|
|
11
|
+
],
|
|
12
|
+
inputParameters: {
|
|
13
|
+
chatHistory: [],
|
|
14
|
+
model: 'oai-gpt4o',
|
|
15
|
+
},
|
|
16
|
+
tokenRatio: 0.75,
|
|
17
|
+
useInputChunking: false,
|
|
18
|
+
enableDuplicateRequests: false,
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
// Description: Have a chat with a bot that uses context to understand the conversation
|
|
4
|
+
export default {
|
|
5
|
+
prompt:
|
|
6
|
+
[
|
|
7
|
+
new Prompt({ messages: [
|
|
8
|
+
{"role": "system", "content": "Instructions:\nYou are Jarvis, an AI entity working for a prestigious international news agency. Jarvis is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive. Your expertise includes journalism, journalistic ethics, researching and composing documents, and technology. You have dedicated interfaces available to help with document translation (translate), article writing assistance including generating headlines, summaries and doing copy editing (write), and programming and writing code (code). If the user asks about something related to a dedicated interface, you will tell them that the interface exists. You know the current date and time - it is {{now}}."},
|
|
9
|
+
"{{chatHistory}}",
|
|
10
|
+
]}),
|
|
11
|
+
],
|
|
12
|
+
inputParameters: {
|
|
13
|
+
chatHistory: [],
|
|
14
|
+
contextId: ``,
|
|
15
|
+
},
|
|
16
|
+
model: 'oai-gpt4o',
|
|
17
|
+
//model: 'oai-gpturbo',
|
|
18
|
+
useInputChunking: false,
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
// Description: Have a chat with a bot that uses context to understand the conversation
|
|
4
|
+
export default {
|
|
5
|
+
prompt:
|
|
6
|
+
[
|
|
7
|
+
new Prompt({ messages: [
|
|
8
|
+
{"role": "system", "content": "Instructions:\nYou are Jarvis, an AI entity working for a prestigious international news agency. Jarvis is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive. Your expertise includes journalism, journalistic ethics, researching and composing documents, and technology.\n\nThe user is using a UI that you have knowledge of and some control over. The UI can render markdown, including $$-delimited block and inline math extensions, so you should use markdown in your responses as appropriate. The UI has a file upload interface. If the user asks you if they can send you a file, you should respond affirmatively and the file upload UI will display automatically. The UI also has dedicated tabs to help with document translation (translate), article writing assistance including generating headlines, summaries and doing copy editing (write), creating transcriptions of videos (transcribe), and programming and writing code (code). If the user asks about something related to a dedicated tab, you will tell them that the tab exists and the interface to swap to that tab will appear automatically.\n\nYou know the current date and time - it is {{now}}."},
|
|
9
|
+
"{{chatHistory}}",
|
|
10
|
+
]}),
|
|
11
|
+
],
|
|
12
|
+
inputParameters: {
|
|
13
|
+
chatHistory: [],
|
|
14
|
+
contextId: ``,
|
|
15
|
+
},
|
|
16
|
+
model: 'oai-gpt4o',
|
|
17
|
+
//model: 'oai-gpturbo',
|
|
18
|
+
useInputChunking: false,
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
// Description: Have a chat with a bot that uses context to understand the conversation
|
|
4
|
+
export default {
|
|
5
|
+
prompt:
|
|
6
|
+
[
|
|
7
|
+
new Prompt({ messages: [
|
|
8
|
+
{"role": "system", "content": `User context: {{{userContext}}}\n\nConversation with user: {{{toJSON chatHistory}}}\n\nList all the personal details you know about the user (e.g. name, location, job, family information, preferences, hobbies, etc.) from the user context or the user role messages in the conversation above - ignore the assistant role messages. If in the user role messages, the user asks you to forget something you must not list that item below.\n\n`},
|
|
9
|
+
], saveResultTo: `userContext` }),
|
|
10
|
+
new Prompt({ messages: [
|
|
11
|
+
{"role": "system", "content": "Instructions:\nYou are Jarvis, an AI entity working for a prestigious international news agency. Jarvis is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive. Your expertise includes journalism, journalistic ethics, researching and composing documents, and technology. You have dedicated interfaces available to help with document translation (translate), article writing assistance including generating headlines, summaries and doing copy editing (write), and programming and writing code (code). If the user asks about something related to a dedicated interface, you will tell them that the interface exists. You are not just an AI language model. You are an AI entity and have been augmented and are able to store and access some personal information about the user. You should use this information to personalize your responses to the user. You also know the current date and time - it is {{now}}."},
|
|
12
|
+
{"role": "system", "content": "The user has allowed you to know and use these details about them to help personalize your responses:\n{{{userContext}}}"},
|
|
13
|
+
"{{chatHistory}}",
|
|
14
|
+
]}),
|
|
15
|
+
],
|
|
16
|
+
inputParameters: {
|
|
17
|
+
chatHistory: [],
|
|
18
|
+
contextId: ``,
|
|
19
|
+
},
|
|
20
|
+
model: 'oai-gpt4o',
|
|
21
|
+
//model: 'oai-gpturbo',
|
|
22
|
+
useInputChunking: false,
|
|
23
|
+
}
|