@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,36 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "cortex-autogen2"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Multi-agent coding assistant using AutoGen"
|
|
5
|
+
authors = ["Your Name <your.email@example.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "cortex_autogen2", from = "src"}]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = ">=3.11,<3.13"
|
|
11
|
+
autogen-agentchat = {extras = ["openai"], version = "^0.6.4"}
|
|
12
|
+
python-dotenv = "^1.0.1"
|
|
13
|
+
requests = "^2.32.3"
|
|
14
|
+
redis = "^5.0.4"
|
|
15
|
+
azure-storage-queue = "^12.10.1"
|
|
16
|
+
azure-storage-blob = "^12.19.0"
|
|
17
|
+
sqlalchemy = "^2.0.30"
|
|
18
|
+
Pillow = "^11.0.0"
|
|
19
|
+
pymysql = "^1.1.1"
|
|
20
|
+
playwright = "^1.54.0"
|
|
21
|
+
markitdown = "^0.1.2"
|
|
22
|
+
docker = "^7.1.0"
|
|
23
|
+
autogen-ext = {extras = ["docker"], version = "^0.6.4"}
|
|
24
|
+
openai = "^1.97.1"
|
|
25
|
+
tiktoken = "^0.9.0"
|
|
26
|
+
aiofiles = "^24.1.0"
|
|
27
|
+
pandas = "^2.3.1"
|
|
28
|
+
matplotlib = "^3.10.3"
|
|
29
|
+
aiohttp = "^3.12.14"
|
|
30
|
+
|
|
31
|
+
[tool.poetry.group.dev.dependencies]
|
|
32
|
+
ipykernel = "^6.29.5"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["poetry-core"]
|
|
36
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
azure-functions>=1.20.0
|
|
2
|
+
autogen-agentchat[openai]>=0.6.4
|
|
3
|
+
python-dotenv>=1.0.1
|
|
4
|
+
requests>=2.32.3
|
|
5
|
+
redis>=5.0.4
|
|
6
|
+
azure-storage-queue>=12.10.1
|
|
7
|
+
azure-storage-blob>=12.19.0
|
|
8
|
+
sqlalchemy>=2.0.30
|
|
9
|
+
Pillow>=11.0.0
|
|
10
|
+
pymysql>=1.1.1
|
|
11
|
+
playwright>=1.54.0
|
|
12
|
+
markitdown>=0.1.2
|
|
13
|
+
docker>=7.1.0
|
|
14
|
+
autogen-ext[docker]>=0.6.4
|
|
15
|
+
openai>=1.97.1
|
|
16
|
+
tiktoken>=0.9.0
|
|
17
|
+
aiofiles>=24.1.0
|
|
18
|
+
aiohttp>=3.12.14
|
|
19
|
+
pandas
|
|
20
|
+
matplotlib
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
🚀 Cortex AutoGen Task Sender
|
|
4
|
+
|
|
5
|
+
Send a task to the Azure Storage Queue for processing by the Cortex AutoGen worker.
|
|
6
|
+
|
|
7
|
+
CRITICAL: Clean worker state prevents conflicts
|
|
8
|
+
----------------------------------------------
|
|
9
|
+
1) Kill existing workers:
|
|
10
|
+
pkill -f "python -m src.cortex_autogen2.main" || true
|
|
11
|
+
pkill -f "python main.py" || true
|
|
12
|
+
|
|
13
|
+
2) Start fresh worker (non-continuous) in background:
|
|
14
|
+
CONTINUOUS_MODE=false python -m src.cortex_autogen2.main &
|
|
15
|
+
# or: CONTINUOUS_MODE=false python main.py &
|
|
16
|
+
|
|
17
|
+
3) Send a task:
|
|
18
|
+
python send_task.py "Create a simple PDF about cats and upload it"
|
|
19
|
+
|
|
20
|
+
Why this order? Multiple workers can cause duplicate processing, queue conflicts, and noisy progress updates.
|
|
21
|
+
|
|
22
|
+
Usage
|
|
23
|
+
-----
|
|
24
|
+
python send_task.py "<your task text>" [--queue <name>] [--connection <conn_str>]
|
|
25
|
+
|
|
26
|
+
Environment
|
|
27
|
+
-----------
|
|
28
|
+
- AZURE_STORAGE_CONNECTION_STRING (required if --connection not provided)
|
|
29
|
+
- AZURE_QUEUE_NAME (default queue if --queue not provided)
|
|
30
|
+
- .env is loaded automatically
|
|
31
|
+
|
|
32
|
+
Message format
|
|
33
|
+
--------------
|
|
34
|
+
- Base64-encoded JSON payload with a `content` field:
|
|
35
|
+
{"request_id": "<uuid>", "message_id": "<uuid>", "content": "<task text>"}
|
|
36
|
+
|
|
37
|
+
Notes
|
|
38
|
+
-----
|
|
39
|
+
- Tasks persist in the queue until a worker processes them.
|
|
40
|
+
- Progress is published to Redis (see README for details).
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
import json
|
|
44
|
+
import uuid
|
|
45
|
+
import argparse
|
|
46
|
+
import base64
|
|
47
|
+
from azure.storage.queue import QueueClient
|
|
48
|
+
from dotenv import load_dotenv
|
|
49
|
+
import os
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
"""Sends a simple task to the Azure Queue."""
|
|
53
|
+
load_dotenv()
|
|
54
|
+
|
|
55
|
+
parser = argparse.ArgumentParser(description="Send a task to the AutoGen agent processor.")
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"task_prompt",
|
|
58
|
+
type=str,
|
|
59
|
+
nargs='?',
|
|
60
|
+
default="list the files in the current directory",
|
|
61
|
+
help="The prompt for the task to be executed."
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--queue",
|
|
65
|
+
dest="queue_name",
|
|
66
|
+
type=str,
|
|
67
|
+
default=os.getenv("AZURE_QUEUE_NAME", "autogen-test-message-queue"),
|
|
68
|
+
help="Azure Storage Queue name (overrides AZURE_QUEUE_NAME)."
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--connection",
|
|
72
|
+
dest="connection_string",
|
|
73
|
+
type=str,
|
|
74
|
+
default=os.getenv("AZURE_STORAGE_CONNECTION_STRING"),
|
|
75
|
+
help="Azure Storage connection string (overrides AZURE_STORAGE_CONNECTION_STRING)."
|
|
76
|
+
)
|
|
77
|
+
args = parser.parse_args()
|
|
78
|
+
|
|
79
|
+
connection_string = args.connection_string
|
|
80
|
+
queue_name = args.queue_name
|
|
81
|
+
print(f"Using queue: {queue_name}")
|
|
82
|
+
|
|
83
|
+
if not connection_string:
|
|
84
|
+
print("Error: AZURE_STORAGE_CONNECTION_STRING is not set and --connection was not provided.")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
task = {
|
|
88
|
+
"request_id": str(uuid.uuid4()),
|
|
89
|
+
"message_id": str(uuid.uuid4()),
|
|
90
|
+
"content": args.task_prompt
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
message = json.dumps(task)
|
|
94
|
+
|
|
95
|
+
# Encode message as Base64 to match Azure Functions MessageEncoding setting
|
|
96
|
+
encoded_message = base64.b64encode(message.encode('utf-8')).decode('utf-8')
|
|
97
|
+
|
|
98
|
+
# Use synchronous client for instant execution
|
|
99
|
+
queue_client = QueueClient.from_connection_string(connection_string, queue_name)
|
|
100
|
+
queue_client.send_message(encoded_message)
|
|
101
|
+
print(f"Task sent to queue '{queue_name}': {task}")
|
|
102
|
+
print(f"Message encoded as Base64: {encoded_message[:50]}...")
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from azure.storage.queue.aio import QueueServiceClient, QueueClient
|
|
3
|
+
from azure.core.exceptions import ResourceExistsError, AzureError
|
|
4
|
+
import os
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
class AzureQueueService:
|
|
10
|
+
"""
|
|
11
|
+
A service for interacting with Azure Queue Storage.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, connection_string: str, queue_name: str):
|
|
15
|
+
self.connection_string = connection_string
|
|
16
|
+
self.queue_name = queue_name
|
|
17
|
+
self.queue_client = QueueClient.from_connection_string(
|
|
18
|
+
conn_str=self.connection_string, queue_name=self.queue_name
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
async def initialize(self):
|
|
22
|
+
"""
|
|
23
|
+
Initializes the queue, creating it if it doesn't exist.
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
await self.queue_client.create_queue()
|
|
27
|
+
except ResourceExistsError:
|
|
28
|
+
pass
|
|
29
|
+
except Exception as e:
|
|
30
|
+
logger.error(f"💥 Failed to create or connect to queue '{self.queue_name}': {e}")
|
|
31
|
+
raise
|
|
32
|
+
|
|
33
|
+
async def get_task(self) -> dict | None:
|
|
34
|
+
"""
|
|
35
|
+
Receives a single message from the queue.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
messages = self.queue_client.receive_messages(
|
|
39
|
+
messages_per_page=1,
|
|
40
|
+
visibility_timeout=1800,
|
|
41
|
+
timeout=30
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async for message in messages:
|
|
45
|
+
logger.info(f"📨 Azure Queue: Received message with ID: {message.id}")
|
|
46
|
+
return {
|
|
47
|
+
"id": message.id,
|
|
48
|
+
"content": message.content,
|
|
49
|
+
"pop_receipt": message.pop_receipt,
|
|
50
|
+
}
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
except AzureError as e:
|
|
54
|
+
logger.error(f"❌ Azure Queue: An Azure-specific error occurred: {e}")
|
|
55
|
+
return None
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.error(f"❌ Azure Queue: Unexpected error receiving message: {e}", exc_info=True)
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
async def delete_task(self, message_id: str, pop_receipt: str):
|
|
61
|
+
"""
|
|
62
|
+
Deletes a message from the queue after it has been processed.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
await self.queue_client.delete_message(message_id, pop_receipt)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"💥 Failed to delete message {message_id}: {e}")
|
|
68
|
+
raise
|
|
69
|
+
|
|
70
|
+
async def close(self):
|
|
71
|
+
"""
|
|
72
|
+
Closes the QueueClient.
|
|
73
|
+
"""
|
|
74
|
+
await self.queue_client.close()
|
|
75
|
+
|
|
76
|
+
async def get_queue_service() -> AzureQueueService:
|
|
77
|
+
"""
|
|
78
|
+
Factory function to create and initialize an AzureQueueService instance.
|
|
79
|
+
"""
|
|
80
|
+
connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
|
|
81
|
+
queue_name = os.getenv("AZURE_QUEUE_NAME")
|
|
82
|
+
|
|
83
|
+
queue_service = AzureQueueService(connection_string, queue_name)
|
|
84
|
+
await queue_service.initialize()
|
|
85
|
+
return queue_service
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import redis
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
# Global Redis client - persistent connection like the working version
|
|
11
|
+
redis_client = None
|
|
12
|
+
|
|
13
|
+
def connect_redis() -> bool:
|
|
14
|
+
"""Check and ensure Redis connection is active - matches working version pattern"""
|
|
15
|
+
global redis_client
|
|
16
|
+
|
|
17
|
+
redis_conn_string = os.getenv("REDIS_CONNECTION_STRING")
|
|
18
|
+
|
|
19
|
+
# Initialize client if not exists
|
|
20
|
+
if redis_client is None:
|
|
21
|
+
try:
|
|
22
|
+
redis_client = redis.from_url(redis_conn_string)
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.warning(f"Failed to create Redis client: {e}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
# Test connection
|
|
28
|
+
try:
|
|
29
|
+
redis_client.ping()
|
|
30
|
+
return True
|
|
31
|
+
except redis.ConnectionError as e:
|
|
32
|
+
logger.warning(f"Redis connection error: {e}")
|
|
33
|
+
try:
|
|
34
|
+
# Try to reconnect
|
|
35
|
+
redis_client = redis.from_url(redis_conn_string)
|
|
36
|
+
redis_client.ping()
|
|
37
|
+
return True
|
|
38
|
+
except Exception as reconnect_error:
|
|
39
|
+
logger.error(f"Error reconnecting to Redis: {reconnect_error}")
|
|
40
|
+
return False
|
|
41
|
+
except Exception as e:
|
|
42
|
+
logger.warning(f"Redis ping failed: {e}")
|
|
43
|
+
# Handle the case where client is closed
|
|
44
|
+
if "Client must be connected" in str(e) or "closed" in str(e).lower():
|
|
45
|
+
logger.info("Redis client was closed, attempting to create new connection...")
|
|
46
|
+
try:
|
|
47
|
+
redis_client = redis.from_url(redis_conn_string)
|
|
48
|
+
redis_client.ping()
|
|
49
|
+
return True
|
|
50
|
+
except Exception as reconnect_error:
|
|
51
|
+
logger.error(f"Error creating new Redis connection: {reconnect_error}")
|
|
52
|
+
return False
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
def publish_request_progress(data: Dict[str, Any]) -> bool:
|
|
56
|
+
"""Publish progress data to Redis channel - matches working version pattern"""
|
|
57
|
+
if connect_redis():
|
|
58
|
+
try:
|
|
59
|
+
message = json.dumps(data)
|
|
60
|
+
result = redis_client.publish(os.getenv("REDIS_CHANNEL"), message)
|
|
61
|
+
logger.info(f"Published progress update for request {data.get('requestId')}: progress={data.get('progress')}, subscribers={result}")
|
|
62
|
+
return True
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"Error publishing message to Redis: {e}")
|
|
65
|
+
return False
|
|
66
|
+
else:
|
|
67
|
+
logger.error(f"Redis not connected, failed to publish progress update for request {data.get('requestId')}")
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
class RedisPublisher:
|
|
71
|
+
"""Wrapper class for compatibility with existing code"""
|
|
72
|
+
|
|
73
|
+
def __init__(self):
|
|
74
|
+
self.connected = False
|
|
75
|
+
|
|
76
|
+
async def connect(self):
|
|
77
|
+
"""Initialize Redis connection"""
|
|
78
|
+
self.connected = connect_redis()
|
|
79
|
+
if self.connected:
|
|
80
|
+
logger.info("Connected to Redis successfully")
|
|
81
|
+
else:
|
|
82
|
+
logger.warning("Failed to connect to Redis")
|
|
83
|
+
logger.warning("Redis progress publishing will be disabled")
|
|
84
|
+
|
|
85
|
+
def publish_request_progress(self, data: Dict[str, Any]) -> bool:
|
|
86
|
+
"""Publish progress data to Redis channel"""
|
|
87
|
+
return publish_request_progress(data)
|
|
88
|
+
|
|
89
|
+
async def publish_progress(self, request_id: str, progress: float, info: str = "", data: str = None) -> bool:
|
|
90
|
+
"""Publish progress update for a specific request - async version"""
|
|
91
|
+
message_data = {
|
|
92
|
+
"requestId": request_id,
|
|
93
|
+
"progress": progress,
|
|
94
|
+
"info": info
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Add data field for final results
|
|
98
|
+
if data is not None:
|
|
99
|
+
message_data["data"] = data
|
|
100
|
+
|
|
101
|
+
return self.publish_request_progress(message_data)
|
|
102
|
+
|
|
103
|
+
def store_final_result(self, request_id: str, result_data: Dict[str, Any], expiry_seconds: int = 3600) -> bool:
|
|
104
|
+
"""Store final result in Redis key for retrieval"""
|
|
105
|
+
if connect_redis():
|
|
106
|
+
try:
|
|
107
|
+
# Store in multiple keys for compatibility
|
|
108
|
+
keys_to_store = [
|
|
109
|
+
f"result:{request_id}",
|
|
110
|
+
f"final:{request_id}",
|
|
111
|
+
f"progress:{request_id}" # Also store in progress key for consistency
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
message = json.dumps(result_data)
|
|
115
|
+
|
|
116
|
+
for key in keys_to_store:
|
|
117
|
+
redis_client.set(key, message, ex=expiry_seconds)
|
|
118
|
+
|
|
119
|
+
logger.info(f"Stored final result for request {request_id} in {len(keys_to_store)} Redis keys")
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Error storing final result: {e}")
|
|
124
|
+
return False
|
|
125
|
+
else:
|
|
126
|
+
logger.debug(f"Redis not connected, skipping final result storage for request {request_id}")
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
async def close(self):
|
|
130
|
+
"""Close Redis connection gracefully"""
|
|
131
|
+
global redis_client
|
|
132
|
+
if redis_client:
|
|
133
|
+
try:
|
|
134
|
+
# Don't actually close the connection in non-continuous mode
|
|
135
|
+
# Just mark it as disconnected so it can be recreated if needed
|
|
136
|
+
logger.info("Redis connection marked for cleanup")
|
|
137
|
+
# Only close if we're in continuous mode or shutting down completely
|
|
138
|
+
# redis_client.close() # Comment out to prevent premature closure
|
|
139
|
+
# redis_client = None
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.warning(f"Error during Redis connection cleanup: {e}")
|
|
142
|
+
self.connected = False
|
|
143
|
+
|
|
144
|
+
# Global instance
|
|
145
|
+
_redis_publisher: Optional[RedisPublisher] = None
|
|
146
|
+
|
|
147
|
+
async def get_redis_publisher() -> RedisPublisher:
|
|
148
|
+
"""Get or create Redis publisher instance"""
|
|
149
|
+
global _redis_publisher
|
|
150
|
+
if _redis_publisher is None:
|
|
151
|
+
_redis_publisher = RedisPublisher()
|
|
152
|
+
await _redis_publisher.connect()
|
|
153
|
+
return _redis_publisher
|