@aj-archipelago/cortex 1.3.58 → 1.3.60

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 (57) hide show
  1. package/config/default.example.json +15 -1
  2. package/config.js +42 -0
  3. package/helper-apps/cortex-file-handler/INTERFACE.md +20 -9
  4. package/helper-apps/cortex-file-handler/package-lock.json +2 -2
  5. package/helper-apps/cortex-file-handler/package.json +1 -1
  6. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +17 -17
  7. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +35 -35
  8. package/helper-apps/cortex-file-handler/src/blobHandler.js +1010 -909
  9. package/helper-apps/cortex-file-handler/src/constants.js +98 -98
  10. package/helper-apps/cortex-file-handler/src/docHelper.js +27 -27
  11. package/helper-apps/cortex-file-handler/src/fileChunker.js +224 -214
  12. package/helper-apps/cortex-file-handler/src/helper.js +93 -93
  13. package/helper-apps/cortex-file-handler/src/index.js +584 -550
  14. package/helper-apps/cortex-file-handler/src/localFileHandler.js +86 -86
  15. package/helper-apps/cortex-file-handler/src/redis.js +186 -90
  16. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +301 -273
  17. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +55 -55
  18. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +174 -154
  19. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +239 -223
  20. package/helper-apps/cortex-file-handler/src/services/storage/LocalStorageProvider.js +161 -159
  21. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +73 -71
  22. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +46 -45
  23. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +256 -213
  24. package/helper-apps/cortex-file-handler/src/start.js +4 -1
  25. package/helper-apps/cortex-file-handler/src/utils/filenameUtils.js +59 -25
  26. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +119 -116
  27. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +257 -257
  28. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +676 -0
  29. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +124 -124
  30. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +249 -208
  31. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +439 -380
  32. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +299 -263
  33. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +265 -239
  34. package/helper-apps/cortex-file-handler/tests/start.test.js +1230 -1201
  35. package/helper-apps/cortex-file-handler/tests/storage/AzureStorageProvider.test.js +110 -105
  36. package/helper-apps/cortex-file-handler/tests/storage/GCSStorageProvider.test.js +201 -175
  37. package/helper-apps/cortex-file-handler/tests/storage/LocalStorageProvider.test.js +128 -125
  38. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +78 -73
  39. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +99 -99
  40. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -70
  41. package/lib/azureAuthTokenHelper.js +78 -0
  42. package/lib/entityConstants.js +5 -4
  43. package/package.json +1 -1
  44. package/pathways/bing_afagent.js +13 -0
  45. package/pathways/gemini_15_vision.js +4 -0
  46. package/pathways/system/entity/tools/sys_tool_bing_search.js +1 -1
  47. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +141 -0
  48. package/pathways/system/entity/tools/sys_tool_browser_jina.js +1 -1
  49. package/pathways/system/entity/tools/sys_tool_readfile.js +4 -0
  50. package/pathways/system/workspaces/workspace_applet_edit.js +4 -0
  51. package/pathways/transcribe_gemini.js +4 -0
  52. package/pathways/translate_subtitle.js +15 -8
  53. package/server/modelExecutor.js +4 -0
  54. package/server/plugins/azureFoundryAgentsPlugin.js +372 -0
  55. package/server/plugins/gemini15ChatPlugin.js +3 -3
  56. package/tests/azureAuthTokenHelper.test.js +150 -0
  57. package/tests/azureFoundryAgents.test.js +212 -0
@@ -1,57 +1,57 @@
1
- import { ConversionService } from './ConversionService.js';
2
- import { getFileStoreMap, setFileStoreMap } from '../redis.js';
3
- import { urlExists } from '../helper.js';
4
- import { gcsUrlExists, uploadChunkToGCS, gcs } from '../blobHandler.js';
5
- import { downloadFile } from '../fileChunker.js';
6
- import { saveFileToBlob } from '../blobHandler.js';
7
- import { moveFileToPublicFolder } from '../localFileHandler.js';
8
- import { v4 as uuidv4 } from 'uuid';
1
+ import { ConversionService } from "./ConversionService.js";
2
+ import { getFileStoreMap, setFileStoreMap } from "../redis.js";
3
+ import { urlExists } from "../helper.js";
4
+ import { gcsUrlExists, uploadChunkToGCS, gcs } from "../blobHandler.js";
5
+ import { downloadFile } from "../fileChunker.js";
6
+ import { saveFileToBlob } from "../blobHandler.js";
7
+ import { moveFileToPublicFolder } from "../localFileHandler.js";
8
+ import { v4 as uuidv4 } from "uuid";
9
9
 
10
10
  export class FileConversionService extends ConversionService {
11
- constructor(context, useAzure = true) {
12
- super(context);
13
- this.useAzure = useAzure;
14
- }
15
-
16
- async _getFileStoreMap(key) {
17
- return getFileStoreMap(key);
18
- }
19
-
20
- async _setFileStoreMap(key, value) {
21
- return setFileStoreMap(key, value);
22
- }
23
-
24
- async _urlExists(url) {
25
- return urlExists(url);
26
- }
27
-
28
- async _gcsUrlExists(url) {
29
- return gcsUrlExists(url);
30
- }
31
-
32
- async _downloadFile(url, destination) {
33
- return downloadFile(url, destination);
34
- }
35
-
36
- async _saveConvertedFile(filePath, requestId) {
37
- // Generate a fallback requestId if none supplied (e.g. during checkHash calls)
38
- const reqId = requestId || uuidv4();
39
-
40
- let fileUrl;
41
- if (this.useAzure) {
42
- const savedBlob = await saveFileToBlob(filePath, reqId);
43
- fileUrl = savedBlob.url;
44
- } else {
45
- fileUrl = await moveFileToPublicFolder(filePath, reqId);
46
- }
47
- return { url: fileUrl };
48
- }
49
-
50
- async _uploadChunkToGCS(filePath, requestId) {
51
- return uploadChunkToGCS(filePath, requestId);
52
- }
53
-
54
- _isGCSConfigured() {
55
- return !!gcs;
56
- }
57
- }
11
+ constructor(context, useAzure = true) {
12
+ super(context);
13
+ this.useAzure = useAzure;
14
+ }
15
+
16
+ async _getFileStoreMap(key) {
17
+ return getFileStoreMap(key);
18
+ }
19
+
20
+ async _setFileStoreMap(key, value) {
21
+ return setFileStoreMap(key, value);
22
+ }
23
+
24
+ async _urlExists(url) {
25
+ return urlExists(url);
26
+ }
27
+
28
+ async _gcsUrlExists(url) {
29
+ return gcsUrlExists(url);
30
+ }
31
+
32
+ async _downloadFile(url, destination) {
33
+ return downloadFile(url, destination);
34
+ }
35
+
36
+ async _saveConvertedFile(filePath, requestId, filename = null) {
37
+ // Generate a fallback requestId if none supplied (e.g. during checkHash calls)
38
+ const reqId = requestId || uuidv4();
39
+
40
+ let fileUrl;
41
+ if (this.useAzure) {
42
+ const savedBlob = await saveFileToBlob(filePath, reqId, filename);
43
+ fileUrl = savedBlob.url;
44
+ } else {
45
+ fileUrl = await moveFileToPublicFolder(filePath, reqId);
46
+ }
47
+ return { url: fileUrl };
48
+ }
49
+
50
+ async _uploadChunkToGCS(filePath, requestId, filename = null) {
51
+ return uploadChunkToGCS(filePath, requestId, filename);
52
+ }
53
+
54
+ _isGCSConfigured() {
55
+ return !!gcs;
56
+ }
57
+ }
@@ -1,177 +1,197 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { v4 as uuidv4 } from 'uuid';
4
1
  import {
5
- generateBlobSASQueryParameters,
6
- StorageSharedKeyCredential,
7
- BlobServiceClient,
8
- } from '@azure/storage-blob';
9
- import { sanitizeFilename } from '../../utils/filenameUtils.js';
10
-
11
- import { StorageProvider } from './StorageProvider.js';
2
+ BlobServiceClient,
3
+ StorageSharedKeyCredential,
4
+ generateBlobSASQueryParameters,
5
+ } from "@azure/storage-blob";
6
+ import fs from "fs";
7
+ import path from "path";
8
+
9
+ import { StorageProvider } from "./StorageProvider.js";
10
+ import {
11
+ generateShortId,
12
+ generateBlobName,
13
+ } from "../../utils/filenameUtils.js";
12
14
 
13
15
  export class AzureStorageProvider extends StorageProvider {
14
- constructor(connectionString, containerName) {
15
- super();
16
- if (!connectionString || !containerName) {
17
- throw new Error('Missing Azure Storage connection string or container name');
18
- }
19
- this.connectionString = connectionString;
20
- this.containerName = containerName;
21
- this.sasTokenLifeDays = process.env.SAS_TOKEN_LIFE_DAYS || 30;
16
+ constructor(connectionString, containerName) {
17
+ super();
18
+ if (!connectionString || !containerName) {
19
+ throw new Error(
20
+ "Missing Azure Storage connection string or container name",
21
+ );
22
22
  }
23
-
24
- async getBlobClient() {
25
- const blobServiceClient = BlobServiceClient.fromConnectionString(this.connectionString);
26
-
27
- // Ensure service version is set
28
- const serviceProperties = await blobServiceClient.getProperties();
29
- if (!serviceProperties.defaultServiceVersion) {
30
- serviceProperties.defaultServiceVersion = '2020-02-10';
31
- await blobServiceClient.setProperties(serviceProperties);
32
- }
33
-
34
- const containerClient = blobServiceClient.getContainerClient(this.containerName);
35
- return { blobServiceClient, containerClient };
23
+ this.connectionString = connectionString;
24
+ this.containerName = containerName;
25
+ this.sasTokenLifeDays = process.env.SAS_TOKEN_LIFE_DAYS || 30;
26
+ }
27
+
28
+ async getBlobClient() {
29
+ const blobServiceClient = BlobServiceClient.fromConnectionString(
30
+ this.connectionString,
31
+ );
32
+
33
+ // Ensure service version is set
34
+ const serviceProperties = await blobServiceClient.getProperties();
35
+ if (!serviceProperties.defaultServiceVersion) {
36
+ serviceProperties.defaultServiceVersion = "2020-02-10";
37
+ await blobServiceClient.setProperties(serviceProperties);
36
38
  }
37
39
 
38
- generateSASToken(containerClient, blobName) {
39
- const { accountName, accountKey } = containerClient.credential;
40
- const sharedKeyCredential = new StorageSharedKeyCredential(
41
- accountName,
42
- accountKey,
43
- );
44
-
45
- const sasOptions = {
46
- containerName: containerClient.containerName,
47
- blobName: blobName,
48
- permissions: 'r',
49
- startsOn: new Date(),
50
- expiresOn: new Date(new Date().valueOf() + this.sasTokenLifeDays * 24 * 60 * 60 * 1000),
51
- };
52
-
53
- return generateBlobSASQueryParameters(sasOptions, sharedKeyCredential).toString();
40
+ const containerClient = blobServiceClient.getContainerClient(
41
+ this.containerName,
42
+ );
43
+ return { blobServiceClient, containerClient };
44
+ }
45
+
46
+ generateSASToken(containerClient, blobName) {
47
+ const { accountName, accountKey } = containerClient.credential;
48
+ const sharedKeyCredential = new StorageSharedKeyCredential(
49
+ accountName,
50
+ accountKey,
51
+ );
52
+
53
+ const sasOptions = {
54
+ containerName: containerClient.containerName,
55
+ blobName: blobName,
56
+ permissions: "r",
57
+ startsOn: new Date(),
58
+ expiresOn: new Date(
59
+ new Date().valueOf() + this.sasTokenLifeDays * 24 * 60 * 60 * 1000,
60
+ ),
61
+ };
62
+
63
+ return generateBlobSASQueryParameters(
64
+ sasOptions,
65
+ sharedKeyCredential,
66
+ ).toString();
67
+ }
68
+
69
+ async uploadFile(context, filePath, requestId, hash = null, filename = null) {
70
+ const { containerClient } = await this.getBlobClient();
71
+
72
+ // Use provided filename or generate LLM-friendly naming
73
+ let blobName;
74
+ if (filename) {
75
+ blobName = generateBlobName(requestId, filename);
76
+ } else {
77
+ const fileExtension = path.extname(filePath);
78
+ const shortId = generateShortId();
79
+ blobName = generateBlobName(requestId, `${shortId}${fileExtension}`);
54
80
  }
55
81
 
56
- async uploadFile(context, filePath, requestId, hash = null) {
57
- const { containerClient } = await this.getBlobClient();
58
-
59
- // Create a consistent, sanitised blob name and encode once for Azure URL
60
- let baseName = sanitizeFilename(path.basename(filePath));
61
- baseName = encodeURIComponent(baseName);
62
- const blobName = `${requestId}/${uuidv4()}_${baseName}`;
63
-
64
- // Create a read stream for the file
65
- const fileStream = fs.createReadStream(filePath);
66
-
67
- // Upload the file to Azure Blob Storage using the stream
68
- const blockBlobClient = containerClient.getBlockBlobClient(blobName);
69
- await blockBlobClient.uploadStream(fileStream);
70
-
71
- // Generate SAS token after successful upload
72
- const sasToken = this.generateSASToken(containerClient, blobName);
73
-
74
- return {
75
- url: `${blockBlobClient.url}?${sasToken}`,
76
- blobName: blobName
77
- };
78
- }
82
+ // Create a read stream for the file
83
+ const fileStream = fs.createReadStream(filePath);
79
84
 
80
- async deleteFiles(requestId) {
81
- if (!requestId) throw new Error('Missing requestId parameter');
82
- const { containerClient } = await this.getBlobClient();
83
-
84
- const result = [];
85
- const blobs = containerClient.listBlobsFlat();
86
-
87
- for await (const blob of blobs) {
88
- if (blob.name.startsWith(requestId)) {
89
- const blockBlobClient = containerClient.getBlockBlobClient(blob.name);
90
- try {
91
- await blockBlobClient.delete();
92
- result.push(blob.name);
93
- } catch (error) {
94
- if (error.statusCode === 404) {
95
- console.warn(`Azure blob already missing during delete: ${blob.name}`);
96
- } else {
97
- throw error;
98
- }
99
- }
100
- }
101
- }
102
-
103
- return result;
104
- }
85
+ // Upload the file to Azure Blob Storage using the stream
86
+ const blockBlobClient = containerClient.getBlockBlobClient(blobName);
87
+ await blockBlobClient.uploadStream(fileStream);
88
+
89
+ // Generate SAS token after successful upload
90
+ const sasToken = this.generateSASToken(containerClient, blobName);
91
+
92
+ return {
93
+ url: `${blockBlobClient.url}?${sasToken}`,
94
+ blobName: blobName,
95
+ };
96
+ }
105
97
 
106
- async fileExists(url) {
98
+ async deleteFiles(requestId) {
99
+ if (!requestId) throw new Error("Missing requestId parameter");
100
+ const { containerClient } = await this.getBlobClient();
101
+
102
+ const result = [];
103
+ const blobs = containerClient.listBlobsFlat();
104
+
105
+ for await (const blob of blobs) {
106
+ if (blob.name.startsWith(requestId)) {
107
+ const blockBlobClient = containerClient.getBlockBlobClient(blob.name);
107
108
  try {
108
- // First attempt a lightweight HEAD request
109
- const headResp = await fetch(url, { method: 'HEAD' });
110
- if (headResp.ok) return true;
111
-
112
- // Some emulators (e.g. Azurite) may not properly support HEAD with SAS.
113
- // Fall back to a ranged GET of a single byte.
114
- const getResp = await fetch(url, {
115
- method: 'GET',
116
- headers: { Range: 'bytes=0-0' },
117
- });
118
- return getResp.ok || getResp.status === 206; // 206 Partial Content
109
+ await blockBlobClient.delete();
110
+ result.push(blob.name);
119
111
  } catch (error) {
120
- console.error('Error checking if file exists:', error);
121
- return false;
112
+ if (error.statusCode === 404) {
113
+ console.warn(
114
+ `Azure blob already missing during delete: ${blob.name}`,
115
+ );
116
+ } else {
117
+ throw error;
118
+ }
122
119
  }
120
+ }
123
121
  }
124
122
 
125
- async downloadFile(url, destinationPath) {
126
- const response = await fetch(url);
127
- if (!response.ok) {
128
- throw new Error(`Failed to download file: ${response.statusText}`);
129
- }
123
+ return result;
124
+ }
125
+
126
+ async fileExists(url) {
127
+ try {
128
+ // First attempt a lightweight HEAD request
129
+ const headResp = await fetch(url, { method: "HEAD" });
130
+ if (headResp.ok) return true;
131
+
132
+ // Some emulators (e.g. Azurite) may not properly support HEAD with SAS.
133
+ // Fall back to a ranged GET of a single byte.
134
+ const getResp = await fetch(url, {
135
+ method: "GET",
136
+ headers: { Range: "bytes=0-0" },
137
+ });
138
+ return getResp.ok || getResp.status === 206; // 206 Partial Content
139
+ } catch (error) {
140
+ console.error("Error checking if file exists:", error);
141
+ return false;
142
+ }
143
+ }
130
144
 
131
- // In newer Node versions, response.body is a web-stream, not a Node stream.
132
- // Easier + reliable: read into a Buffer then write to file.
133
- const arrayBuffer = await response.arrayBuffer();
134
- const buffer = Buffer.from(arrayBuffer);
135
- await fs.promises.writeFile(destinationPath, buffer);
145
+ async downloadFile(url, destinationPath) {
146
+ const response = await fetch(url);
147
+ if (!response.ok) {
148
+ throw new Error(`Failed to download file: ${response.statusText}`);
136
149
  }
137
150
 
138
- async cleanup(urls) {
139
- if (!urls || !urls.length) return;
140
-
141
- const { containerClient } = await this.getBlobClient();
142
- const result = [];
143
-
144
- for (const url of urls) {
145
- try {
146
- const blobName = this.extractBlobNameFromUrl(url);
147
- if (blobName) {
148
- const blockBlobClient = containerClient.getBlockBlobClient(blobName);
149
- await blockBlobClient.delete();
150
- result.push(blobName);
151
- }
152
- } catch (error) {
153
- console.error(`Error cleaning up blob ${url}:`, error);
154
- }
151
+ // In newer Node versions, response.body is a web-stream, not a Node stream.
152
+ // Easier + reliable: read into a Buffer then write to file.
153
+ const arrayBuffer = await response.arrayBuffer();
154
+ const buffer = Buffer.from(arrayBuffer);
155
+ await fs.promises.writeFile(destinationPath, buffer);
156
+ }
157
+
158
+ async cleanup(urls) {
159
+ if (!urls || !urls.length) return;
160
+
161
+ const { containerClient } = await this.getBlobClient();
162
+ const result = [];
163
+
164
+ for (const url of urls) {
165
+ try {
166
+ const blobName = this.extractBlobNameFromUrl(url);
167
+ if (blobName) {
168
+ const blockBlobClient = containerClient.getBlockBlobClient(blobName);
169
+ await blockBlobClient.delete();
170
+ result.push(blobName);
155
171
  }
156
-
157
- return result;
172
+ } catch (error) {
173
+ console.error(`Error cleaning up blob ${url}:`, error);
174
+ }
158
175
  }
159
176
 
160
- isEncoded(str) {
161
- return /%[0-9A-Fa-f]{2}/.test(str);
162
- }
177
+ return result;
178
+ }
163
179
 
164
- extractBlobNameFromUrl(url) {
165
- try {
166
- const urlObj = new URL(url);
167
- const pathParts = urlObj.pathname.split('/');
168
- const containerIndex = pathParts.indexOf(this.containerName);
169
- if (containerIndex === -1) return null;
170
-
171
- return pathParts.slice(containerIndex + 1).join('/');
172
- } catch (error) {
173
- console.error('Error extracting blob name from URL:', error);
174
- return null;
175
- }
180
+ isEncoded(str) {
181
+ return /%[0-9A-Fa-f]{2}/.test(str);
182
+ }
183
+
184
+ extractBlobNameFromUrl(url) {
185
+ try {
186
+ const urlObj = new URL(url);
187
+ const pathParts = urlObj.pathname.split("/");
188
+ const containerIndex = pathParts.indexOf(this.containerName);
189
+ if (containerIndex === -1) return null;
190
+
191
+ return pathParts.slice(containerIndex + 1).join("/");
192
+ } catch (error) {
193
+ console.error("Error extracting blob name from URL:", error);
194
+ return null;
176
195
  }
177
- }
196
+ }
197
+ }