@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
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Azure Blob Storage tools for uploading task files and generating SAS URLs.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import mimetypes
|
|
9
|
+
import uuid
|
|
10
|
+
import time
|
|
11
|
+
from datetime import datetime, timedelta
|
|
12
|
+
from urllib.parse import urlparse, parse_qs
|
|
13
|
+
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions, ContentSettings
|
|
14
|
+
from azure.core.exceptions import AzureError, ServiceResponseError
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
def _validate_sas_url(url: str) -> bool:
|
|
19
|
+
"""Private helper to validate an Azure blob SAS URL."""
|
|
20
|
+
try:
|
|
21
|
+
parsed = urlparse(url)
|
|
22
|
+
if not parsed.hostname or 'blob.core.windows.net' not in parsed.hostname:
|
|
23
|
+
return False
|
|
24
|
+
if not parsed.query:
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
query_params = parse_qs(parsed.query)
|
|
28
|
+
required_params = ['se', 'sp', 'sv', 'sr', 'sig']
|
|
29
|
+
if not all(param in query_params for param in required_params):
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
expiry_str_decoded = query_params['se'][0].replace('%3A', ':')
|
|
33
|
+
expiry_time = datetime.fromisoformat(expiry_str_decoded.replace('Z', '+00:00'))
|
|
34
|
+
if expiry_time <= datetime.now().replace(tzinfo=expiry_time.tzinfo):
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
logger.info(f"✅ Valid SAS URL with proper expiry: {url[:100]}...")
|
|
38
|
+
return True
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.error(f"Error validating SAS URL: {e}")
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
class AzureBlobUploader:
|
|
44
|
+
"""A centralized class for handling uploads and SAS URL generation for Azure Blob Storage."""
|
|
45
|
+
_instance = None
|
|
46
|
+
|
|
47
|
+
def __new__(cls):
|
|
48
|
+
if cls._instance is None:
|
|
49
|
+
cls._instance = super(AzureBlobUploader, cls).__new__(cls)
|
|
50
|
+
cls._instance._initialized = False
|
|
51
|
+
return cls._instance
|
|
52
|
+
|
|
53
|
+
def __init__(self):
|
|
54
|
+
if self._initialized:
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
self.connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
|
|
58
|
+
self.container_name = os.getenv("AZURE_BLOB_CONTAINER", "autogentempfiles")
|
|
59
|
+
|
|
60
|
+
if not self.connection_string:
|
|
61
|
+
raise ValueError("AZURE_STORAGE_CONNECTION_STRING environment variable is required")
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
self.blob_service_client = BlobServiceClient.from_connection_string(self.connection_string)
|
|
65
|
+
parts = {k: v for k, v in (item.split('=', 1) for item in self.connection_string.split(';') if '=' in item)}
|
|
66
|
+
self.account_name = parts.get('AccountName')
|
|
67
|
+
self.account_key = parts.get('AccountKey')
|
|
68
|
+
if not self.account_name or not self.account_key:
|
|
69
|
+
raise ValueError("Connection string must contain AccountName and AccountKey")
|
|
70
|
+
self._initialized = True
|
|
71
|
+
self.ensure_container_exists()
|
|
72
|
+
except Exception as e:
|
|
73
|
+
raise ValueError(f"Invalid Azure Storage connection string or configuration error: {e}")
|
|
74
|
+
|
|
75
|
+
def ensure_container_exists(self):
|
|
76
|
+
"""Ensures the blob container exists, creating it if necessary."""
|
|
77
|
+
try:
|
|
78
|
+
self.blob_service_client.create_container(self.container_name)
|
|
79
|
+
logger.info(f"Container '{self.container_name}' created.")
|
|
80
|
+
except AzureError as e:
|
|
81
|
+
if "ContainerAlreadyExists" not in str(e):
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
def generate_sas_url(self, blob_name: str) -> str:
|
|
85
|
+
"""Generates a 30-day read-only SAS URL for a specific blob."""
|
|
86
|
+
# Ensure blob_name has no leading slashes
|
|
87
|
+
clean_name = blob_name.lstrip('/')
|
|
88
|
+
sas_token = generate_blob_sas(
|
|
89
|
+
account_name=self.account_name,
|
|
90
|
+
container_name=self.container_name,
|
|
91
|
+
blob_name=clean_name,
|
|
92
|
+
account_key=self.account_key,
|
|
93
|
+
permission=BlobSasPermissions(read=True),
|
|
94
|
+
expiry=datetime.utcnow() + timedelta(days=30)
|
|
95
|
+
)
|
|
96
|
+
# Build URL using account name to avoid any emulator/devhost base_url leaks
|
|
97
|
+
return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{clean_name}?{sas_token}"
|
|
98
|
+
|
|
99
|
+
def upload_file(self, file_path: str, blob_name: str = None) -> dict:
|
|
100
|
+
"""Uploads a local file and returns a dictionary with the SAS URL. Retries on transient errors."""
|
|
101
|
+
if not os.path.exists(file_path):
|
|
102
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
103
|
+
|
|
104
|
+
if blob_name is None:
|
|
105
|
+
original_base = os.path.basename(file_path)
|
|
106
|
+
name, ext = os.path.splitext(original_base)
|
|
107
|
+
# Prefix support for virtual folders
|
|
108
|
+
prefix = (os.getenv("AZURE_BLOB_PREFIX") or "").strip().strip("/")
|
|
109
|
+
# Decide uniqueness policy: default add timestamp+short id to avoid static overwrites
|
|
110
|
+
preserve = (os.getenv("PRESERVE_BLOB_FILENAME", "false").lower() in ("1", "true", "yes"))
|
|
111
|
+
if preserve:
|
|
112
|
+
final_name = original_base
|
|
113
|
+
else:
|
|
114
|
+
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
|
115
|
+
short_id = uuid.uuid4().hex[:8]
|
|
116
|
+
final_name = f"{name}__{timestamp}_{short_id}{ext}"
|
|
117
|
+
blob_name = f"{prefix}/{final_name}" if prefix else final_name
|
|
118
|
+
|
|
119
|
+
# Normalize any accidental leading slashes in blob path
|
|
120
|
+
normalized_blob_name = blob_name.lstrip("/")
|
|
121
|
+
blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=normalized_blob_name)
|
|
122
|
+
|
|
123
|
+
# Detect content type for better browser handling
|
|
124
|
+
content_type, _ = mimetypes.guess_type(file_path)
|
|
125
|
+
if not content_type:
|
|
126
|
+
content_type = "application/octet-stream"
|
|
127
|
+
content_settings = ContentSettings(content_type=content_type)
|
|
128
|
+
|
|
129
|
+
# Hint SDK to use smaller single-put and block sizes to avoid timeouts on moderate networks
|
|
130
|
+
try:
|
|
131
|
+
if hasattr(blob_client, "_config"):
|
|
132
|
+
if hasattr(blob_client._config, "max_single_put_size"):
|
|
133
|
+
blob_client._config.max_single_put_size = 4 * 1024 * 1024 # 4 MB
|
|
134
|
+
if hasattr(blob_client._config, "max_block_size"):
|
|
135
|
+
blob_client._config.max_block_size = 4 * 1024 * 1024 # 4 MB
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
# Simple upload; SDK will handle block uploads automatically for large blobs
|
|
140
|
+
with open(file_path, "rb") as data:
|
|
141
|
+
blob_client.upload_blob(
|
|
142
|
+
data,
|
|
143
|
+
overwrite=True,
|
|
144
|
+
content_settings=content_settings,
|
|
145
|
+
max_concurrency=4,
|
|
146
|
+
timeout=900,
|
|
147
|
+
)
|
|
148
|
+
logger.info(f"Uploaded file: {file_path} -> {normalized_blob_name}")
|
|
149
|
+
sas_url = self.generate_sas_url(normalized_blob_name)
|
|
150
|
+
if not _validate_sas_url(sas_url):
|
|
151
|
+
raise Exception("Generated SAS URL failed validation.")
|
|
152
|
+
return {"blob_name": blob_name, "download_url": sas_url}
|
|
153
|
+
|
|
154
|
+
# Keep a single function for external calls to use the singleton uploader
|
|
155
|
+
def upload_file_to_azure_blob(file_path: str, blob_name: str = None) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Uploads a file to Azure Blob Storage and returns a JSON string with the download URL.
|
|
158
|
+
This function uses the singleton AzureBlobUploader instance.
|
|
159
|
+
|
|
160
|
+
Reference local files in absolute path.
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
uploader = AzureBlobUploader()
|
|
165
|
+
result = uploader.upload_file(file_path, blob_name)
|
|
166
|
+
logger.info(f"✅ Successfully uploaded and got SAS URL for {file_path}")
|
|
167
|
+
return json.dumps(result)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"❌ Failed to upload {file_path}. Error: {e}", exc_info=True)
|
|
170
|
+
return json.dumps({"error": str(e)})
|
|
171
|
+
|
|
172
|
+
# This function is no longer needed as the class handles text uploads if necessary,
|
|
173
|
+
# and direct calls should go through the singleton.
|
|
174
|
+
# def upload_text_to_azure_blob(content: str, blob_name: str) -> dict:
|
|
175
|
+
# ...
|