@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
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
generateBlobName,
|
|
22
22
|
} from "./utils/filenameUtils.js";
|
|
23
23
|
import { publicFolder, port, ipAddress } from "./start.js";
|
|
24
|
-
import { CONVERTED_EXTENSIONS } from "./constants.js";
|
|
24
|
+
import { CONVERTED_EXTENSIONS, AZURITE_ACCOUNT_NAME } from "./constants.js";
|
|
25
25
|
import { FileConversionService } from "./services/FileConversionService.js";
|
|
26
26
|
|
|
27
27
|
const pipeline = promisify(_pipeline);
|
|
@@ -35,14 +35,23 @@ function isBase64(str) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const { SAS_TOKEN_LIFE_DAYS = 30 } = process.env;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
let GCP_SERVICE_ACCOUNT;
|
|
39
|
+
let GCP_PROJECT_ID;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const GCP_SERVICE_ACCOUNT_KEY =
|
|
43
|
+
process.env.GCP_SERVICE_ACCOUNT_KEY_BASE64 ||
|
|
44
|
+
process.env.GCP_SERVICE_ACCOUNT_KEY ||
|
|
45
|
+
"{}";
|
|
46
|
+
GCP_SERVICE_ACCOUNT = isBase64(GCP_SERVICE_ACCOUNT_KEY)
|
|
47
|
+
? JSON.parse(Buffer.from(GCP_SERVICE_ACCOUNT_KEY, "base64").toString())
|
|
48
|
+
: JSON.parse(GCP_SERVICE_ACCOUNT_KEY);
|
|
49
|
+
GCP_PROJECT_ID = GCP_SERVICE_ACCOUNT.project_id;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn("Error parsing GCP service account credentials, GCS will not be used:", error.message);
|
|
52
|
+
GCP_SERVICE_ACCOUNT = {};
|
|
53
|
+
GCP_PROJECT_ID = null;
|
|
54
|
+
}
|
|
46
55
|
|
|
47
56
|
let gcs;
|
|
48
57
|
if (!GCP_PROJECT_ID || !GCP_SERVICE_ACCOUNT) {
|
|
@@ -65,10 +74,21 @@ if (!GCP_PROJECT_ID || !GCP_SERVICE_ACCOUNT) {
|
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
// Parse comma-separated container names from environment variable
|
|
78
|
+
const parseContainerNames = () => {
|
|
79
|
+
const containerStr = process.env.AZURE_STORAGE_CONTAINER_NAME || "whispertempfiles";
|
|
80
|
+
return containerStr.split(',').map(name => name.trim());
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const AZURE_STORAGE_CONTAINER_NAMES = parseContainerNames();
|
|
84
|
+
export const DEFAULT_AZURE_STORAGE_CONTAINER_NAME = AZURE_STORAGE_CONTAINER_NAMES[0];
|
|
70
85
|
export const GCS_BUCKETNAME = process.env.GCS_BUCKETNAME || "cortextempfiles";
|
|
71
86
|
|
|
87
|
+
// Validate if a container name is allowed
|
|
88
|
+
export const isValidContainerName = (containerName) => {
|
|
89
|
+
return AZURE_STORAGE_CONTAINER_NAMES.includes(containerName);
|
|
90
|
+
};
|
|
91
|
+
|
|
72
92
|
function isEncoded(str) {
|
|
73
93
|
// Checks for any percent-encoded sequence
|
|
74
94
|
return /%[0-9A-Fa-f]{2}/.test(str);
|
|
@@ -169,10 +189,18 @@ async function downloadFromGCS(gcsUrl, destinationPath) {
|
|
|
169
189
|
}
|
|
170
190
|
}
|
|
171
191
|
|
|
172
|
-
export const getBlobClient = async () => {
|
|
192
|
+
export const getBlobClient = async (containerName = null) => {
|
|
173
193
|
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
|
|
174
|
-
const
|
|
175
|
-
|
|
194
|
+
const finalContainerName = containerName || DEFAULT_AZURE_STORAGE_CONTAINER_NAME;
|
|
195
|
+
|
|
196
|
+
// Validate container name is in whitelist
|
|
197
|
+
if (!isValidContainerName(finalContainerName)) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Invalid container name '${finalContainerName}'. Allowed containers: ${AZURE_STORAGE_CONTAINER_NAMES.join(', ')}`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!connectionString || !finalContainerName) {
|
|
176
204
|
throw new Error(
|
|
177
205
|
"Missing Azure Storage connection string or container name environment variable",
|
|
178
206
|
);
|
|
@@ -187,13 +215,13 @@ export const getBlobClient = async () => {
|
|
|
187
215
|
await blobServiceClient.setProperties(serviceProperties);
|
|
188
216
|
}
|
|
189
217
|
|
|
190
|
-
const containerClient = blobServiceClient.getContainerClient(
|
|
218
|
+
const containerClient = blobServiceClient.getContainerClient(finalContainerName);
|
|
191
219
|
|
|
192
220
|
return { blobServiceClient, containerClient };
|
|
193
221
|
};
|
|
194
222
|
|
|
195
|
-
async function saveFileToBlob(chunkPath, requestId, filename = null) {
|
|
196
|
-
const { containerClient } = await getBlobClient();
|
|
223
|
+
async function saveFileToBlob(chunkPath, requestId, filename = null, containerName = null) {
|
|
224
|
+
const { containerClient } = await getBlobClient(containerName);
|
|
197
225
|
// Use provided filename or generate LLM-friendly naming
|
|
198
226
|
let blobName;
|
|
199
227
|
if (filename) {
|
|
@@ -224,20 +252,55 @@ async function saveFileToBlob(chunkPath, requestId, filename = null) {
|
|
|
224
252
|
const generateSASToken = (
|
|
225
253
|
containerClient,
|
|
226
254
|
blobName,
|
|
227
|
-
|
|
255
|
+
options = {},
|
|
228
256
|
) => {
|
|
229
|
-
|
|
257
|
+
// Handle Azurite (development storage) credentials with fallback
|
|
258
|
+
let accountName, accountKey;
|
|
259
|
+
|
|
260
|
+
if (containerClient.credential && containerClient.credential.accountName) {
|
|
261
|
+
// Regular Azure Storage credentials
|
|
262
|
+
accountName = containerClient.credential.accountName;
|
|
263
|
+
|
|
264
|
+
// Handle Buffer case (Azurite) vs string case (real Azure)
|
|
265
|
+
if (Buffer.isBuffer(containerClient.credential.accountKey)) {
|
|
266
|
+
accountKey = containerClient.credential.accountKey.toString('base64');
|
|
267
|
+
} else {
|
|
268
|
+
accountKey = containerClient.credential.accountKey;
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
// Azurite development storage fallback
|
|
272
|
+
accountName = AZURITE_ACCOUNT_NAME;
|
|
273
|
+
accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
|
|
274
|
+
}
|
|
275
|
+
|
|
230
276
|
const sharedKeyCredential = new StorageSharedKeyCredential(
|
|
231
277
|
accountName,
|
|
232
278
|
accountKey,
|
|
233
279
|
);
|
|
234
280
|
|
|
281
|
+
// Support custom duration: minutes, hours, or fall back to default
|
|
282
|
+
let expirationTime;
|
|
283
|
+
if (options.minutes) {
|
|
284
|
+
expirationTime = new Date(new Date().valueOf() + options.minutes * 60 * 1000);
|
|
285
|
+
} else if (options.hours) {
|
|
286
|
+
expirationTime = new Date(new Date().valueOf() + options.hours * 60 * 60 * 1000);
|
|
287
|
+
} else if (options.days) {
|
|
288
|
+
expirationTime = new Date(new Date().valueOf() + options.days * 24 * 60 * 60 * 1000);
|
|
289
|
+
} else if (options.expiryTimeSeconds !== undefined) {
|
|
290
|
+
// Legacy support for existing parameter
|
|
291
|
+
expirationTime = new Date(new Date().valueOf() + options.expiryTimeSeconds * 1000);
|
|
292
|
+
} else {
|
|
293
|
+
// Default to SAS_TOKEN_LIFE_DAYS environment variable
|
|
294
|
+
const defaultExpirySeconds = parseInt(SAS_TOKEN_LIFE_DAYS) * 24 * 60 * 60;
|
|
295
|
+
expirationTime = new Date(new Date().valueOf() + defaultExpirySeconds * 1000);
|
|
296
|
+
}
|
|
297
|
+
|
|
235
298
|
const sasOptions = {
|
|
236
299
|
containerName: containerClient.containerName,
|
|
237
300
|
blobName: blobName,
|
|
238
|
-
permissions: "r", // Read permission
|
|
301
|
+
permissions: options.permissions || "r", // Read permission
|
|
239
302
|
startsOn: new Date(),
|
|
240
|
-
expiresOn:
|
|
303
|
+
expiresOn: expirationTime,
|
|
241
304
|
};
|
|
242
305
|
|
|
243
306
|
const sasToken = generateBlobSASQueryParameters(
|
|
@@ -248,9 +311,9 @@ const generateSASToken = (
|
|
|
248
311
|
};
|
|
249
312
|
|
|
250
313
|
//deletes blob that has the requestId
|
|
251
|
-
async function deleteBlob(requestId) {
|
|
314
|
+
async function deleteBlob(requestId, containerName = null) {
|
|
252
315
|
if (!requestId) throw new Error("Missing requestId parameter");
|
|
253
|
-
const { containerClient } = await getBlobClient();
|
|
316
|
+
const { containerClient } = await getBlobClient(containerName);
|
|
254
317
|
// List all blobs in the container
|
|
255
318
|
const blobs = containerClient.listBlobsFlat();
|
|
256
319
|
|
|
@@ -280,12 +343,15 @@ function uploadBlob(
|
|
|
280
343
|
saveToLocal = false,
|
|
281
344
|
filePath = null,
|
|
282
345
|
hash = null,
|
|
346
|
+
containerParam = null,
|
|
283
347
|
) {
|
|
284
348
|
return new Promise((resolve, reject) => {
|
|
285
349
|
(async () => {
|
|
286
350
|
try {
|
|
287
351
|
let requestId = uuidv4();
|
|
352
|
+
let containerName = containerParam;
|
|
288
353
|
const body = {};
|
|
354
|
+
const fields = {}; // Buffer for all fields
|
|
289
355
|
|
|
290
356
|
// If filePath is given, we are dealing with local file and not form-data
|
|
291
357
|
if (filePath) {
|
|
@@ -308,29 +374,42 @@ function uploadBlob(
|
|
|
308
374
|
uploadName, // Use the LLM-friendly filename
|
|
309
375
|
resolve,
|
|
310
376
|
hash,
|
|
377
|
+
containerName,
|
|
311
378
|
);
|
|
312
379
|
resolve(result);
|
|
313
380
|
} catch (error) {
|
|
314
|
-
|
|
381
|
+
console.error("Error in uploadFile (local file path):", error);
|
|
382
|
+
const err = new Error(`Error processing file upload: ${error.message}`);
|
|
315
383
|
err.status = 500;
|
|
316
384
|
throw err;
|
|
317
385
|
}
|
|
318
386
|
} else {
|
|
319
|
-
// Otherwise, continue working with form-data
|
|
320
387
|
const busboy = Busboy({ headers: req.headers });
|
|
321
388
|
let hasFile = false;
|
|
322
389
|
let errorOccurred = false;
|
|
323
390
|
|
|
391
|
+
|
|
324
392
|
busboy.on("field", (fieldname, value) => {
|
|
325
393
|
if (fieldname === "requestId") {
|
|
326
394
|
requestId = value;
|
|
327
395
|
} else if (fieldname === "hash") {
|
|
328
396
|
hash = value;
|
|
397
|
+
} else if (fieldname === "container") {
|
|
398
|
+
if (value && !isValidContainerName(value)) {
|
|
399
|
+
errorOccurred = true;
|
|
400
|
+
const err = new Error(`Invalid container name '${value}'. Allowed containers: ${AZURE_STORAGE_CONTAINER_NAMES.join(', ')}`);
|
|
401
|
+
err.status = 400;
|
|
402
|
+
reject(err);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
containerName = value;
|
|
329
406
|
}
|
|
407
|
+
fields[fieldname] = value; // Store all fields
|
|
330
408
|
});
|
|
331
409
|
|
|
332
410
|
busboy.on("file", async (fieldname, file, info) => {
|
|
333
411
|
if (errorOccurred) return;
|
|
412
|
+
|
|
334
413
|
hasFile = true;
|
|
335
414
|
|
|
336
415
|
// Validate file
|
|
@@ -342,6 +421,28 @@ function uploadBlob(
|
|
|
342
421
|
return;
|
|
343
422
|
}
|
|
344
423
|
|
|
424
|
+
// Simple approach: small delay to allow container field to be processed
|
|
425
|
+
console.log("File received, giving fields time to process...");
|
|
426
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
427
|
+
console.log("Processing file with containerName:", containerName);
|
|
428
|
+
|
|
429
|
+
if (errorOccurred) return; // Check again after waiting
|
|
430
|
+
|
|
431
|
+
await processFile(fieldname, file, info);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const processFile = async (fieldname, file, info) => {
|
|
435
|
+
if (errorOccurred) return;
|
|
436
|
+
|
|
437
|
+
// Validate file
|
|
438
|
+
if (!info.filename || info.filename.trim() === "") {
|
|
439
|
+
errorOccurred = true;
|
|
440
|
+
const err = new Error("Invalid file: missing filename");
|
|
441
|
+
err.status = 400;
|
|
442
|
+
reject(err);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
345
446
|
// Prepare for streaming to cloud destinations
|
|
346
447
|
const filename = info.filename;
|
|
347
448
|
const fileExtension = path.extname(filename);
|
|
@@ -426,6 +527,7 @@ function uploadBlob(
|
|
|
426
527
|
context,
|
|
427
528
|
uploadName,
|
|
428
529
|
azureStream,
|
|
530
|
+
containerName,
|
|
429
531
|
).catch(async (err) => {
|
|
430
532
|
cloudUploadError = err;
|
|
431
533
|
// Fallback: try from disk if available
|
|
@@ -435,7 +537,7 @@ function uploadBlob(
|
|
|
435
537
|
highWaterMark: 1024 * 1024,
|
|
436
538
|
autoClose: true,
|
|
437
539
|
});
|
|
438
|
-
return saveToAzureStorage(context, uploadName, diskStream);
|
|
540
|
+
return saveToAzureStorage(context, uploadName, diskStream, containerName);
|
|
439
541
|
}
|
|
440
542
|
throw err;
|
|
441
543
|
});
|
|
@@ -557,6 +659,8 @@ function uploadBlob(
|
|
|
557
659
|
await conversionService._saveConvertedFile(
|
|
558
660
|
conversion.convertedPath,
|
|
559
661
|
requestId,
|
|
662
|
+
null,
|
|
663
|
+
containerName,
|
|
560
664
|
);
|
|
561
665
|
|
|
562
666
|
// Optionally save to GCS
|
|
@@ -592,6 +696,8 @@ function uploadBlob(
|
|
|
592
696
|
context.res = { status: 200, body: result };
|
|
593
697
|
resolve(result);
|
|
594
698
|
} catch (err) {
|
|
699
|
+
console.error("Error in main busboy processing:", err);
|
|
700
|
+
console.error("Stack trace:", err.stack);
|
|
595
701
|
errorOccurred = true;
|
|
596
702
|
reject(err);
|
|
597
703
|
} finally {
|
|
@@ -600,7 +706,7 @@ function uploadBlob(
|
|
|
600
706
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
601
707
|
}
|
|
602
708
|
}
|
|
603
|
-
}
|
|
709
|
+
};
|
|
604
710
|
|
|
605
711
|
busboy.on("error", (error) => {
|
|
606
712
|
if (errorOccurred) return;
|
|
@@ -611,7 +717,6 @@ function uploadBlob(
|
|
|
611
717
|
});
|
|
612
718
|
|
|
613
719
|
busboy.on("finish", () => {
|
|
614
|
-
if (errorOccurred) return;
|
|
615
720
|
if (!hasFile) {
|
|
616
721
|
errorOccurred = true;
|
|
617
722
|
const err = new Error("No file provided in request");
|
|
@@ -648,10 +753,10 @@ function uploadBlob(
|
|
|
648
753
|
}
|
|
649
754
|
}
|
|
650
755
|
} catch (error) {
|
|
651
|
-
//
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
756
|
+
// Always log errors with full details for debugging
|
|
757
|
+
console.error("Top-level error processing file upload:", error);
|
|
758
|
+
console.error("Error stack trace:", error.stack);
|
|
759
|
+
context.log("Error processing file upload:", error);
|
|
655
760
|
const err = new Error(error.message || "Error processing file upload.");
|
|
656
761
|
err.status = error.status || 500;
|
|
657
762
|
reject(err);
|
|
@@ -674,8 +779,8 @@ async function saveToLocalStorage(context, requestId, encodedFilename, file) {
|
|
|
674
779
|
}
|
|
675
780
|
|
|
676
781
|
// Helper function to handle Azure blob storage
|
|
677
|
-
async function saveToAzureStorage(context, encodedFilename, file) {
|
|
678
|
-
const { containerClient } = await getBlobClient();
|
|
782
|
+
async function saveToAzureStorage(context, encodedFilename, file, containerName = null) {
|
|
783
|
+
const { containerClient } = await getBlobClient(containerName);
|
|
679
784
|
const contentType = mime.lookup(encodedFilename);
|
|
680
785
|
|
|
681
786
|
// Create a safe blob name that is URI-encoded once (no double encoding)
|
|
@@ -689,6 +794,7 @@ async function saveToAzureStorage(context, encodedFilename, file) {
|
|
|
689
794
|
};
|
|
690
795
|
|
|
691
796
|
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
|
|
797
|
+
console.log("Uploading to container:", containerName || "default");
|
|
692
798
|
context.log(`Uploading to Azure... ${blobName}`);
|
|
693
799
|
await blockBlobClient.uploadStream(file, undefined, undefined, options);
|
|
694
800
|
const sasToken = generateSASToken(containerClient, blobName);
|
|
@@ -731,6 +837,7 @@ async function uploadFile(
|
|
|
731
837
|
filename,
|
|
732
838
|
resolve,
|
|
733
839
|
hash = null,
|
|
840
|
+
containerName = null,
|
|
734
841
|
) {
|
|
735
842
|
try {
|
|
736
843
|
if (!file) {
|
|
@@ -782,16 +889,17 @@ async function uploadFile(
|
|
|
782
889
|
context.log("Starting primary storage upload...");
|
|
783
890
|
const primaryPromise = saveToLocal
|
|
784
891
|
? saveToLocalStorage(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
892
|
+
context,
|
|
893
|
+
requestId,
|
|
894
|
+
uploadName,
|
|
895
|
+
createOptimizedReadStream(uploadPath),
|
|
896
|
+
)
|
|
790
897
|
: saveToAzureStorage(
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
898
|
+
context,
|
|
899
|
+
uploadName,
|
|
900
|
+
createOptimizedReadStream(uploadPath),
|
|
901
|
+
containerName,
|
|
902
|
+
);
|
|
795
903
|
storagePromises.push(
|
|
796
904
|
primaryPromise.then((url) => {
|
|
797
905
|
context.log("Primary storage upload completed");
|
|
@@ -854,6 +962,8 @@ async function uploadFile(
|
|
|
854
962
|
await conversionService._saveConvertedFile(
|
|
855
963
|
conversion.convertedPath,
|
|
856
964
|
requestId,
|
|
965
|
+
null,
|
|
966
|
+
containerName,
|
|
857
967
|
);
|
|
858
968
|
context.log("Converted file saved to primary storage");
|
|
859
969
|
|
|
@@ -900,7 +1010,7 @@ async function uploadFile(
|
|
|
900
1010
|
context.log("Error in upload process:", error);
|
|
901
1011
|
if (body.url) {
|
|
902
1012
|
try {
|
|
903
|
-
await cleanup(context, [body.url]);
|
|
1013
|
+
await cleanup(context, [body.url], containerName);
|
|
904
1014
|
} catch (cleanupError) {
|
|
905
1015
|
context.log("Error during cleanup after failure:", cleanupError);
|
|
906
1016
|
}
|
|
@@ -920,8 +1030,8 @@ async function streamToBuffer(stream) {
|
|
|
920
1030
|
}
|
|
921
1031
|
|
|
922
1032
|
// Function to delete files that haven't been used in more than a month
|
|
923
|
-
async function cleanup(context, urls = null) {
|
|
924
|
-
const { containerClient } = await getBlobClient();
|
|
1033
|
+
async function cleanup(context, urls = null, containerName = null) {
|
|
1034
|
+
const { containerClient } = await getBlobClient(containerName);
|
|
925
1035
|
const cleanedURLs = [];
|
|
926
1036
|
|
|
927
1037
|
if (!urls) {
|
|
@@ -1095,11 +1205,11 @@ async function deleteGCS(blobName) {
|
|
|
1095
1205
|
errors: error.errors,
|
|
1096
1206
|
response: error.response
|
|
1097
1207
|
? {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1208
|
+
status: error.response.status,
|
|
1209
|
+
statusText: error.response.statusText,
|
|
1210
|
+
data: error.response.data,
|
|
1211
|
+
headers: error.response.headers,
|
|
1212
|
+
}
|
|
1103
1213
|
: null,
|
|
1104
1214
|
});
|
|
1105
1215
|
// Don't throw the error - we want to continue with cleanup even if GCS deletion fails
|
|
@@ -37,14 +37,16 @@ async function cleanupTempDirectories() {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
// Setup periodic cleanup
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
40
|
+
// Setup periodic cleanup (only in production, not during tests)
|
|
41
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
42
|
+
setInterval(async () => {
|
|
43
|
+
try {
|
|
44
|
+
await cleanupTempDirectories();
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error("Error during periodic cleanup:", err);
|
|
47
|
+
}
|
|
48
|
+
}, CLEANUP_INTERVAL_MS);
|
|
49
|
+
}
|
|
48
50
|
|
|
49
51
|
// Process a single chunk with streaming and progress tracking
|
|
50
52
|
async function processChunk(inputPath, outputFileName, start, duration) {
|