@aj-archipelago/cortex 1.3.62 → 1.3.63

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 (211) hide show
  1. package/.github/workflows/cortex-file-handler-test.yml +61 -0
  2. package/README.md +31 -7
  3. package/config/default.example.json +15 -0
  4. package/config.js +133 -12
  5. package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
  6. package/helper-apps/cortex-autogen2/Dockerfile +31 -0
  7. package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
  8. package/helper-apps/cortex-autogen2/README.md +183 -0
  9. package/helper-apps/cortex-autogen2/__init__.py +1 -0
  10. package/helper-apps/cortex-autogen2/agents.py +131 -0
  11. package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
  12. package/helper-apps/cortex-autogen2/function_app.py +55 -0
  13. package/helper-apps/cortex-autogen2/host.json +15 -0
  14. package/helper-apps/cortex-autogen2/main.py +126 -0
  15. package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
  16. package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
  17. package/helper-apps/cortex-autogen2/requirements.txt +20 -0
  18. package/helper-apps/cortex-autogen2/send_task.py +105 -0
  19. package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
  20. package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
  21. package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
  22. package/helper-apps/cortex-autogen2/task_processor.py +488 -0
  23. package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
  24. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
  25. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
  26. package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
  27. package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
  28. package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
  29. package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
  30. package/helper-apps/cortex-azure-cleaner/README.md +36 -0
  31. package/helper-apps/cortex-file-converter/README.md +93 -0
  32. package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
  33. package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
  34. package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
  35. package/helper-apps/cortex-file-converter/requirements.txt +1 -0
  36. package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
  37. package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
  38. package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
  39. package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
  40. package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
  41. package/helper-apps/cortex-file-handler/package.json +1 -1
  42. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
  43. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
  44. package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
  45. package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
  46. package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
  47. package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
  48. package/helper-apps/cortex-file-handler/src/constants.js +3 -0
  49. package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
  50. package/helper-apps/cortex-file-handler/src/index.js +116 -9
  51. package/helper-apps/cortex-file-handler/src/redis.js +61 -1
  52. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
  53. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
  54. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
  55. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
  56. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
  57. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
  58. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
  59. package/helper-apps/cortex-file-handler/src/start.js +27 -17
  60. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
  61. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
  62. package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
  63. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
  64. package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
  65. package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
  66. package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
  67. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
  68. package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
  69. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
  70. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
  71. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
  72. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
  73. package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
  74. package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
  75. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
  76. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
  77. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
  78. package/lib/cortexResponse.js +153 -0
  79. package/lib/entityConstants.js +21 -3
  80. package/lib/logger.js +21 -4
  81. package/lib/pathwayTools.js +28 -9
  82. package/lib/util.js +49 -0
  83. package/package.json +1 -1
  84. package/pathways/basePathway.js +1 -0
  85. package/pathways/bing_afagent.js +54 -1
  86. package/pathways/call_tools.js +2 -3
  87. package/pathways/chat_jarvis.js +1 -1
  88. package/pathways/google_cse.js +27 -0
  89. package/pathways/grok_live_search.js +18 -0
  90. package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
  91. package/pathways/system/entity/memory/sys_memory_required.js +1 -0
  92. package/pathways/system/entity/memory/sys_search_memory.js +1 -0
  93. package/pathways/system/entity/sys_entity_agent.js +56 -4
  94. package/pathways/system/entity/sys_generator_quick.js +1 -0
  95. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
  96. package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
  97. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
  98. package/pathways/system/entity/tools/sys_tool_image.js +1 -1
  99. package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
  100. package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
  101. package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
  102. package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
  103. package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
  104. package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
  105. package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
  106. package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
  107. package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
  108. package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
  109. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
  110. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
  111. package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
  112. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
  113. package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
  114. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
  115. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
  116. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
  117. package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
  118. package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
  119. package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
  120. package/pathways/vision.js +1 -1
  121. package/server/graphql.js +1 -1
  122. package/server/modelExecutor.js +8 -0
  123. package/server/pathwayResolver.js +166 -16
  124. package/server/pathwayResponseParser.js +16 -8
  125. package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
  126. package/server/plugins/claude3VertexPlugin.js +193 -45
  127. package/server/plugins/gemini15ChatPlugin.js +21 -0
  128. package/server/plugins/gemini15VisionPlugin.js +360 -0
  129. package/server/plugins/googleCsePlugin.js +94 -0
  130. package/server/plugins/grokVisionPlugin.js +365 -0
  131. package/server/plugins/modelPlugin.js +3 -1
  132. package/server/plugins/openAiChatPlugin.js +106 -13
  133. package/server/plugins/openAiVisionPlugin.js +42 -30
  134. package/server/resolver.js +28 -4
  135. package/server/rest.js +270 -53
  136. package/server/typeDef.js +1 -0
  137. package/tests/{mocks.js → helpers/mocks.js} +5 -2
  138. package/tests/{server.js → helpers/server.js} +2 -2
  139. package/tests/helpers/sseAssert.js +23 -0
  140. package/tests/helpers/sseClient.js +73 -0
  141. package/tests/helpers/subscriptionAssert.js +11 -0
  142. package/tests/helpers/subscriptions.js +113 -0
  143. package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
  144. package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
  145. package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
  146. package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
  147. package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
  148. package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
  149. package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
  150. package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
  151. package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
  152. package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
  153. package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
  154. package/tests/integration/graphql/features/grok/grok.test.js +688 -0
  155. package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
  156. package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
  157. package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
  158. package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
  159. package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
  160. package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
  161. package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
  162. package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
  163. package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
  164. package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
  165. package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
  166. package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
  167. package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
  168. package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
  169. package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
  170. package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
  171. package/tests/{config.test.js → unit/core/config.test.js} +3 -3
  172. package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
  173. package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
  174. package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
  175. package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
  176. package/tests/unit/core/mergeResolver.test.js +952 -0
  177. package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
  178. package/tests/unit/core/pathwayResolver.test.js +187 -0
  179. package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
  180. package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
  181. package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
  182. package/tests/{util.test.js → unit/core/util.test.js} +1 -1
  183. package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
  184. package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
  185. package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
  186. package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
  187. package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
  188. package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
  189. package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
  190. package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
  191. package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
  192. package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
  193. package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
  194. package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
  195. package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
  196. package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
  197. package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
  198. package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
  199. package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
  200. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
  201. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
  202. package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
  203. package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
  204. package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
  205. package/pathways/system/workspaces/run_gpt4.js +0 -20
  206. package/pathways/system/workspaces/run_gpt4_32.js +0 -20
  207. package/tests/agentic.test.js +0 -256
  208. package/tests/pathwayResolver.test.js +0 -78
  209. package/tests/subscription.test.js +0 -387
  210. /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
  211. /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
@@ -7,25 +7,40 @@ import { generateShortId } from "../../utils/filenameUtils.js";
7
7
  export class StorageService {
8
8
  constructor(factory) {
9
9
  this.factory = factory || new StorageFactory();
10
- this.primaryProvider = this.factory.getPrimaryProvider();
11
- this.backupProvider = this.factory.getGCSProvider();
10
+ this.primaryProvider = null;
11
+ this.backupProvider = null;
12
+ this._initialized = false;
12
13
  }
13
14
 
14
- getPrimaryProvider() {
15
+ async _initialize() {
16
+ if (!this._initialized) {
17
+ this.primaryProvider = await this.factory.getPrimaryProvider();
18
+ this.backupProvider = this.factory.getGCSProvider();
19
+ this._initialized = true;
20
+ }
21
+ }
22
+
23
+ async getPrimaryProvider() {
24
+ await this._initialize();
15
25
  return this.primaryProvider;
16
26
  }
17
27
 
18
- getBackupProvider() {
28
+ async getBackupProvider() {
29
+ await this._initialize();
19
30
  return this.backupProvider;
20
31
  }
21
32
 
33
+
34
+
22
35
  async uploadFile(...args) {
23
36
  /*
24
37
  Supported call shapes:
25
38
  1) uploadFile(buffer, filename)
26
- 2) uploadFile(context, filePath, requestId, hash?) – legacy internal use
39
+ 2) uploadFile(context, filePath, requestId, hash?, filename?, containerName?) – legacy internal use
27
40
  */
28
41
 
42
+ await this._initialize();
43
+
29
44
  // Shape (buffer, filename)
30
45
  if (
31
46
  args.length === 2 &&
@@ -50,12 +65,14 @@ export class StorageService {
50
65
  }
51
66
  }
52
67
 
53
- // Fallback to legacy (context, filePath, requestId, hash?)
54
- const [context, filePath, requestId, hash] = args;
55
- return this.uploadFileWithProviders(context, filePath, requestId, hash);
68
+ // Fallback to legacy (context, filePath, requestId, hash?, filename?, containerName?)
69
+ const [context, filePath, requestId, hash, filename, containerName] = args;
70
+ return this.uploadFileWithProviders(context, filePath, requestId, hash, filename, containerName);
56
71
  }
57
72
 
58
73
  async uploadFileToBackup(fileOrBuffer, filename) {
74
+ await this._initialize();
75
+
59
76
  if (!this.backupProvider) {
60
77
  throw new Error("Backup provider not configured");
61
78
  }
@@ -86,6 +103,8 @@ export class StorageService {
86
103
  }
87
104
 
88
105
  async downloadFile(url, destinationPath = null) {
106
+ await this._initialize();
107
+
89
108
  const useBackup = url.startsWith("gs://");
90
109
 
91
110
  if (useBackup && !this.backupProvider) {
@@ -119,6 +138,8 @@ export class StorageService {
119
138
  }
120
139
 
121
140
  async deleteFile(url) {
141
+ await this._initialize();
142
+
122
143
  if (typeof this.primaryProvider.deleteFile === "function") {
123
144
  return await this.primaryProvider.deleteFile(url);
124
145
  }
@@ -127,6 +148,8 @@ export class StorageService {
127
148
  }
128
149
 
129
150
  async deleteFileFromBackup(url) {
151
+ await this._initialize();
152
+
130
153
  if (!this.backupProvider) {
131
154
  throw new Error("Backup provider not configured");
132
155
  }
@@ -137,18 +160,89 @@ export class StorageService {
137
160
  return await this.backupProvider.deleteFiles([url]);
138
161
  }
139
162
 
140
- async uploadFileWithProviders(context, filePath, requestId, hash = null) {
141
- // Generate filename once to ensure both providers use the same name
142
- const fileExtension = path.extname(filePath);
143
- const shortId = generateShortId();
144
- const filename = `${shortId}${fileExtension}`;
163
+ /**
164
+ * Delete a single file by its hash from both primary and backup storage
165
+ * @param {string} hash - The hash of the file to delete
166
+ * @returns {Promise<Object>} Object containing deletion results and file info
167
+ */
168
+ async deleteFileByHash(hash) {
169
+ await this._initialize();
170
+
171
+ if (!hash) {
172
+ throw new Error("Missing hash parameter");
173
+ }
174
+
175
+ const results = [];
176
+
177
+ // Get and remove file information from Redis map (non-atomic operations)
178
+ const { getFileStoreMap, removeFromFileStoreMap } = await import("../../redis.js");
179
+ const hashResult = await getFileStoreMap(hash);
180
+
181
+ if (hashResult) {
182
+ await removeFromFileStoreMap(hash);
183
+ }
184
+
185
+ if (!hashResult) {
186
+ throw new Error(`File with hash ${hash} not found`);
187
+ }
188
+
189
+ // Delete from primary storage
190
+ if (hashResult.url) {
191
+ try {
192
+ const primaryResult = await this.deleteFile(hashResult.url);
193
+ if (primaryResult) {
194
+ results.push({ provider: 'primary', result: primaryResult });
195
+ }
196
+ } catch (error) {
197
+ console.error(`Error deleting file from primary storage:`, error);
198
+ results.push({ provider: 'primary', error: error.message });
199
+ }
200
+ }
201
+
202
+ // Delete from backup storage (GCS)
203
+ if (hashResult.gcs && this.backupProvider) {
204
+ try {
205
+ const backupResult = await this.deleteFileFromBackup(hashResult.gcs);
206
+ if (backupResult) {
207
+ results.push({ provider: 'backup', result: backupResult });
208
+ }
209
+ } catch (error) {
210
+ console.error(`Error deleting file from backup storage:`, error);
211
+ results.push({ provider: 'backup', error: error.message });
212
+ }
213
+ }
145
214
 
146
- const primaryResult = await this.primaryProvider.uploadFile(
215
+ // Note: Hash was already removed from Redis atomically at the beginning
216
+ // No need to remove again
217
+
218
+ return {
219
+ hash,
220
+ deleted: results,
221
+ filename: hashResult.filename
222
+ };
223
+ }
224
+
225
+ async uploadFileWithProviders(context, filePath, requestId, hash = null, filename = null, containerName = null) {
226
+ await this._initialize();
227
+
228
+ // Use provided filename or generate one
229
+ const finalFilename = filename || (() => {
230
+ const fileExtension = path.extname(filePath);
231
+ const shortId = generateShortId();
232
+ return `${shortId}${fileExtension}`;
233
+ })();
234
+
235
+ // Get the appropriate provider for the container
236
+ const primaryProvider = containerName ?
237
+ await this.factory.getAzureProvider(containerName) :
238
+ this.primaryProvider;
239
+
240
+ const primaryResult = await primaryProvider.uploadFile(
147
241
  context,
148
242
  filePath,
149
243
  requestId,
150
244
  hash,
151
- filename,
245
+ finalFilename,
152
246
  );
153
247
 
154
248
  let gcsResult = null;
@@ -158,7 +252,7 @@ export class StorageService {
158
252
  filePath,
159
253
  requestId,
160
254
  hash,
161
- filename,
255
+ finalFilename,
162
256
  );
163
257
  }
164
258
 
@@ -166,6 +260,8 @@ export class StorageService {
166
260
  }
167
261
 
168
262
  async deleteFiles(requestId) {
263
+ await this._initialize();
264
+
169
265
  if (!requestId) {
170
266
  throw new Error("Missing requestId parameter");
171
267
  }
@@ -201,6 +297,8 @@ export class StorageService {
201
297
  }
202
298
 
203
299
  async fileExists(url) {
300
+ await this._initialize();
301
+
204
302
  if (!url) {
205
303
  return false;
206
304
  }
@@ -219,6 +317,8 @@ export class StorageService {
219
317
  }
220
318
 
221
319
  async cleanup(urls) {
320
+ await this._initialize();
321
+
222
322
  if (!urls || !urls.length) return;
223
323
 
224
324
  const results = [];
@@ -251,6 +351,8 @@ export class StorageService {
251
351
  }
252
352
 
253
353
  async ensureGCSUpload(context, existingFile) {
354
+ await this._initialize();
355
+
254
356
  if (
255
357
  !this.backupProvider ||
256
358
  !existingFile.url ||
@@ -293,6 +395,8 @@ export class StorageService {
293
395
  }
294
396
 
295
397
  async downloadFileFromBackup(url, destinationPath = null) {
398
+ await this._initialize();
399
+
296
400
  if (!this.backupProvider) {
297
401
  throw new Error("Backup provider not configured");
298
402
  }
@@ -15,17 +15,21 @@ import { publicIpv4 } from "public-ip";
15
15
  // a test run.
16
16
 
17
17
  let ipAddress = "localhost";
18
- if (process.env.NODE_ENV !== "test") {
19
- try {
20
- ipAddress = await publicIpv4();
21
- } catch (err) {
22
- // In rare cases querying the public IP can fail (e.g. no network when
23
- // running offline). Keep the default of "localhost" in that case so we
24
- // still generate valid URLs.
25
- console.warn(
26
- "Unable to determine public IPv4 address defaulting to 'localhost'.",
27
- err,
28
- );
18
+
19
+ // Initialize IP address asynchronously (only for non-test environments)
20
+ async function initializeIpAddress() {
21
+ if (process.env.NODE_ENV !== "test") {
22
+ try {
23
+ ipAddress = await publicIpv4();
24
+ } catch (err) {
25
+ // In rare cases querying the public IP can fail (e.g. no network when
26
+ // running offline). Keep the default of "localhost" in that case so we
27
+ // still generate valid URLs.
28
+ console.warn(
29
+ "Unable to determine public IPv4 address – defaulting to 'localhost'.",
30
+ err,
31
+ );
32
+ }
29
33
  }
30
34
  }
31
35
 
@@ -82,10 +86,16 @@ app.all("/api/MediaFileChunker", async (req, res) => {
82
86
  }
83
87
  });
84
88
 
85
- app.listen(port, () => {
86
- console.log(
87
- `Cortex File Handler v${version} running on port ${port} (includes legacy MediaFileChunker endpoint)`,
88
- );
89
- });
89
+ // Only start the server if this module is being run directly (not imported for tests)
90
+ if (import.meta.url === `file://${process.argv[1]}`) {
91
+ initializeIpAddress().then(() => {
92
+ app.listen(port, () => {
93
+ console.log(
94
+ `Cortex File Handler v${version} running on port ${port} (includes legacy MediaFileChunker endpoint)`,
95
+ );
96
+ });
97
+ });
98
+ }
99
+ // For tests, we'll keep ipAddress as "localhost" by default - no need to initialize
90
100
 
91
- export { port, publicFolder, ipAddress };
101
+ export { port, publicFolder, ipAddress, app };
@@ -90,6 +90,10 @@ test("converts Excel to CSV successfully", async (t) => {
90
90
 
91
91
  // Test document conversion with MarkItDown API
92
92
  test("converts document to markdown via MarkItDown API", async (t) => {
93
+ // Set the environment variable for the test
94
+ const originalEnv = process.env.MARKITDOWN_CONVERT_URL;
95
+ process.env.MARKITDOWN_CONVERT_URL = "http://localhost:8080/convert?url=";
96
+
93
97
  // Mock axios.get for MarkItDown API
94
98
  const originalAxiosGet = axios.get;
95
99
  axios.get = async (url) => {
@@ -118,8 +122,13 @@ test("converts document to markdown via MarkItDown API", async (t) => {
118
122
  t.true(content.includes("# Test Document"));
119
123
  t.true(content.includes("This is a test document converted to markdown"));
120
124
 
121
- // Restore original axios.get
125
+ // Restore original axios.get and environment variable
122
126
  axios.get = originalAxiosGet;
127
+ if (originalEnv) {
128
+ process.env.MARKITDOWN_CONVERT_URL = originalEnv;
129
+ } else {
130
+ delete process.env.MARKITDOWN_CONVERT_URL;
131
+ }
123
132
  });
124
133
 
125
134
  // Test error handling for missing original URL
@@ -145,3 +154,45 @@ test("correctly detects file extensions", (t) => {
145
154
  t.false(service.needsConversion("test.txt"));
146
155
  t.false(service.needsConversion("test.json"));
147
156
  });
157
+
158
+ // Test _saveConvertedFile method signature and container parameter handling
159
+ test("_saveConvertedFile accepts container parameter", async (t) => {
160
+ const service = new FileConversionService(mockContext, false); // Use local storage for testing
161
+
162
+ // Create a test file
163
+ const testFile = join(t.context.testDir, "container-param-test.txt");
164
+ await fs.writeFile(testFile, "Test content for container parameter");
165
+
166
+ // Test that the method accepts all parameters without throwing
167
+ const result = await service._saveConvertedFile(
168
+ testFile,
169
+ "test-request-id",
170
+ "test-filename.txt",
171
+ "test-container"
172
+ );
173
+
174
+ t.truthy(result);
175
+ t.truthy(result.url);
176
+ t.true(typeof result.url === 'string');
177
+ });
178
+
179
+ // Test ensureConvertedVersion method signature with container parameter
180
+ test("ensureConvertedVersion accepts container parameter", async (t) => {
181
+ const service = new FileConversionService(mockContext, false);
182
+
183
+ // Mock file info object
184
+ const fileInfo = {
185
+ url: "http://example.com/test.txt", // Non-convertible file
186
+ gcs: "gs://bucket/test.txt"
187
+ };
188
+
189
+ // Test that the method accepts container parameter without throwing
190
+ const result = await service.ensureConvertedVersion(
191
+ fileInfo,
192
+ "test-request-id",
193
+ "test-container"
194
+ );
195
+
196
+ t.truthy(result);
197
+ t.is(result.url, fileInfo.url); // Should return original for non-convertible file
198
+ });
@@ -11,6 +11,9 @@ import {
11
11
  gcsUrlExists,
12
12
  deleteGCS,
13
13
  getBlobClient,
14
+ AZURE_STORAGE_CONTAINER_NAMES,
15
+ DEFAULT_AZURE_STORAGE_CONTAINER_NAME,
16
+ isValidContainerName,
14
17
  } from "../src/blobHandler.js";
15
18
  import { urlExists } from "../src/helper.js";
16
19
  import CortexFileHandler from "../src/index.js";
@@ -314,3 +317,40 @@ test("test hash check returns 404 when both storages are empty", async (t) => {
314
317
  }
315
318
  }
316
319
  });
320
+
321
+ // Container name parsing and validation tests
322
+ test("AZURE_STORAGE_CONTAINER_NAMES should be an array with at least one container", (t) => {
323
+ t.true(Array.isArray(AZURE_STORAGE_CONTAINER_NAMES), "Should be an array");
324
+ t.true(AZURE_STORAGE_CONTAINER_NAMES.length > 0, "Should have at least one container");
325
+
326
+ // All items should be non-empty strings
327
+ AZURE_STORAGE_CONTAINER_NAMES.forEach((name, index) => {
328
+ t.is(typeof name, 'string', `Container at index ${index} should be a string`);
329
+ t.true(name.length > 0, `Container at index ${index} should not be empty`);
330
+ });
331
+ });
332
+
333
+ test("DEFAULT_AZURE_STORAGE_CONTAINER_NAME should be the first container", (t) => {
334
+ t.is(DEFAULT_AZURE_STORAGE_CONTAINER_NAME, AZURE_STORAGE_CONTAINER_NAMES[0]);
335
+ t.truthy(DEFAULT_AZURE_STORAGE_CONTAINER_NAME);
336
+ t.is(typeof DEFAULT_AZURE_STORAGE_CONTAINER_NAME, 'string');
337
+ });
338
+
339
+ test("isValidContainerName should validate container names correctly", (t) => {
340
+ // All configured containers should be valid
341
+ AZURE_STORAGE_CONTAINER_NAMES.forEach(containerName => {
342
+ t.true(isValidContainerName(containerName), `${containerName} should be valid`);
343
+ });
344
+
345
+ // Invalid containers should return false
346
+ t.false(isValidContainerName("invalid-container"));
347
+ t.false(isValidContainerName(""));
348
+ t.false(isValidContainerName(null));
349
+ t.false(isValidContainerName(undefined));
350
+ t.false(isValidContainerName("non-existent"));
351
+ });
352
+
353
+ test("container names should be unique", (t) => {
354
+ const uniqueNames = new Set(AZURE_STORAGE_CONTAINER_NAMES);
355
+ t.is(uniqueNames.size, AZURE_STORAGE_CONTAINER_NAMES.length, "All container names should be unique");
356
+ });