@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.
- package/.github/workflows/cortex-file-handler-test.yml +61 -0
- package/README.md +31 -7
- package/config/default.example.json +15 -0
- package/config.js +133 -12
- package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
- package/helper-apps/cortex-autogen2/Dockerfile +31 -0
- package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
- package/helper-apps/cortex-autogen2/README.md +183 -0
- package/helper-apps/cortex-autogen2/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/agents.py +131 -0
- package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
- package/helper-apps/cortex-autogen2/function_app.py +55 -0
- package/helper-apps/cortex-autogen2/host.json +15 -0
- package/helper-apps/cortex-autogen2/main.py +126 -0
- package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
- package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
- package/helper-apps/cortex-autogen2/requirements.txt +20 -0
- package/helper-apps/cortex-autogen2/send_task.py +105 -0
- package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
- package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
- package/helper-apps/cortex-autogen2/task_processor.py +488 -0
- package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
- package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
- package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
- package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
- package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
- package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
- package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
- package/helper-apps/cortex-azure-cleaner/README.md +36 -0
- package/helper-apps/cortex-file-converter/README.md +93 -0
- package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
- package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
- package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
- package/helper-apps/cortex-file-converter/requirements.txt +1 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
- package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
- package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
- package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
- package/helper-apps/cortex-file-handler/package.json +1 -1
- package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
- package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
- package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
- package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
- package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
- package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
- package/helper-apps/cortex-file-handler/src/constants.js +3 -0
- package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
- package/helper-apps/cortex-file-handler/src/index.js +116 -9
- package/helper-apps/cortex-file-handler/src/redis.js +61 -1
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
- package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
- package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
- package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
- package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
- package/helper-apps/cortex-file-handler/src/start.js +27 -17
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
- package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
- package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
- package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
- package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
- package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
- package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
- package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
- package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
- package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
- package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
- package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
- package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
- package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
- package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
- package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
- package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
- package/lib/cortexResponse.js +153 -0
- package/lib/entityConstants.js +21 -3
- package/lib/logger.js +21 -4
- package/lib/pathwayTools.js +28 -9
- package/lib/util.js +49 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +1 -0
- package/pathways/bing_afagent.js +54 -1
- package/pathways/call_tools.js +2 -3
- package/pathways/chat_jarvis.js +1 -1
- package/pathways/google_cse.js +27 -0
- package/pathways/grok_live_search.js +18 -0
- package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
- package/pathways/system/entity/memory/sys_memory_required.js +1 -0
- package/pathways/system/entity/memory/sys_search_memory.js +1 -0
- package/pathways/system/entity/sys_entity_agent.js +56 -4
- package/pathways/system/entity/sys_generator_quick.js +1 -0
- package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
- package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
- package/pathways/system/entity/tools/sys_tool_image.js +1 -1
- package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
- package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
- package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
- package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
- package/pathways/vision.js +1 -1
- package/server/graphql.js +1 -1
- package/server/modelExecutor.js +8 -0
- package/server/pathwayResolver.js +166 -16
- package/server/pathwayResponseParser.js +16 -8
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- package/server/plugins/claude3VertexPlugin.js +193 -45
- package/server/plugins/gemini15ChatPlugin.js +21 -0
- package/server/plugins/gemini15VisionPlugin.js +360 -0
- package/server/plugins/googleCsePlugin.js +94 -0
- package/server/plugins/grokVisionPlugin.js +365 -0
- package/server/plugins/modelPlugin.js +3 -1
- package/server/plugins/openAiChatPlugin.js +106 -13
- package/server/plugins/openAiVisionPlugin.js +42 -30
- package/server/resolver.js +28 -4
- package/server/rest.js +270 -53
- package/server/typeDef.js +1 -0
- package/tests/{mocks.js → helpers/mocks.js} +5 -2
- package/tests/{server.js → helpers/server.js} +2 -2
- package/tests/helpers/sseAssert.js +23 -0
- package/tests/helpers/sseClient.js +73 -0
- package/tests/helpers/subscriptionAssert.js +11 -0
- package/tests/helpers/subscriptions.js +113 -0
- package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
- package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
- package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
- package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
- package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
- package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
- package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
- package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
- package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
- package/tests/integration/graphql/features/grok/grok.test.js +688 -0
- package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
- package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
- package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
- package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
- package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
- package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
- package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
- package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
- package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
- package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
- package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
- package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
- package/tests/{config.test.js → unit/core/config.test.js} +3 -3
- package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
- package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
- package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
- package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
- package/tests/unit/core/mergeResolver.test.js +952 -0
- package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
- package/tests/unit/core/pathwayResolver.test.js +187 -0
- package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
- package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
- package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
- package/tests/{util.test.js → unit/core/util.test.js} +1 -1
- package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
- package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
- package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
- package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
- package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
- package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
- package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
- package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
- package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
- package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
- package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
- package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
- package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
- package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
- package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
- package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
- package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
- package/pathways/system/workspaces/run_gpt4.js +0 -20
- package/pathways/system/workspaces/run_gpt4_32.js +0 -20
- package/tests/agentic.test.js +0 -256
- package/tests/pathwayResolver.test.js +0 -78
- package/tests/subscription.test.js +0 -387
- /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
- /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 =
|
|
11
|
-
this.backupProvider =
|
|
10
|
+
this.primaryProvider = null;
|
|
11
|
+
this.backupProvider = null;
|
|
12
|
+
this._initialized = false;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
});
|