machinaos 0.0.78 → 0.0.80
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/.env.template +74 -5
- package/{workflows/AI Assistant_workflow-1778504793388-ou1m1tz2x.json → .machina/workflows/AI Assistant_example_workflow-1779017037684-e2e5da7a.json } +164 -105
- package/{workflows/AI Employee_example_workflow-1777720598005-u4cm858dv.json → .machina/workflows/AI Employee_example_workflow-1779102911870-cbc76c82.json } +582 -328
- package/{workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json → .machina/workflows/Claude Assistant_example_workflow-1779095939967-2369cff4.json } +152 -83
- package/README.md +5 -2
- package/bin/cli.js +2 -2
- package/{machina → cli}/__main__.py +11 -7
- package/cli/_common.py +122 -0
- package/cli/buildenv.py +40 -0
- package/cli/cli.py +204 -0
- package/{machina → cli}/colors.py +10 -2
- package/cli/commands/__init__.py +1 -0
- package/cli/commands/_temporal_specs.py +59 -0
- package/{machina → cli}/commands/build.py +35 -45
- package/cli/commands/clean.py +141 -0
- package/cli/commands/daemon/__init__.py +47 -0
- package/cli/commands/daemon/_state.py +97 -0
- package/cli/commands/daemon/restart.py +14 -0
- package/cli/commands/daemon/start.py +49 -0
- package/cli/commands/daemon/status.py +20 -0
- package/cli/commands/daemon/stop.py +22 -0
- package/{machina → cli}/commands/dev.py +32 -42
- package/{machina → cli}/commands/docs.py +13 -11
- package/{machina → cli}/commands/start.py +69 -62
- package/{machina → cli}/commands/stop.py +7 -10
- package/{machina → cli}/commands/version.py +12 -6
- package/cli/config.py +170 -0
- package/cli/platform_.py +169 -0
- package/{machina → cli}/ports.py +42 -3
- package/{machina → cli}/run.py +29 -2
- package/{machina → cli}/supervisor.py +29 -12
- package/{machina → cli}/tcp.py +6 -2
- package/{machina → cli}/tree.py +38 -11
- package/client/dist/assets/{ActionBar-Du2MSFSz.js → ActionBar-Cjr3TF7g.js} +1 -1
- package/client/dist/assets/{ApiKeyInput-k2LBmBjb.js → ApiKeyInput-DIJE2PVA.js} +1 -1
- package/client/dist/assets/{ApiKeyPanel-C_bV9U0X.js → ApiKeyPanel-CPmye7uh.js} +1 -1
- package/client/dist/assets/{ApiUsageSection-CmVfwZzL.js → ApiUsageSection-TF_7gH2D.js} +1 -1
- package/client/dist/assets/{EmailPanel-CeKIMGu-.js → EmailPanel-Bs-xvbKR.js} +1 -1
- package/client/dist/assets/{OAuthPanel-KA3t3Q2K.js → OAuthPanel-BDtVJhAV.js} +1 -1
- package/client/dist/assets/{QrPairingPanel-NgNpJNuk.js → QrPairingPanel-BwJehTuZ.js} +1 -1
- package/client/dist/assets/{RateLimitSection-Du5YNVIA.js → RateLimitSection-CfNOoPIS.js} +1 -1
- package/client/dist/assets/{StatusCard-DNLyayXc.js → StatusCard-DkwIrgdP.js} +1 -1
- package/client/dist/assets/index-P2FzntoL.js +165 -0
- package/client/dist/index.html +1 -1
- package/client/package.json +1 -1
- package/client/src/Dashboard.tsx +128 -76
- package/client/src/adapters/nodeSpecToDescription.ts +7 -0
- package/client/src/assets/icons/index.test.ts +10 -0
- package/client/src/assets/icons/index.ts +16 -3
- package/client/src/components/AIAgentNode.tsx +8 -8
- package/client/src/components/ParameterRenderer.tsx +6 -3
- package/client/src/components/SkillEditorModal.tsx +1 -0
- package/client/src/components/credentials/panels/EmailPanel.tsx +2 -0
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +2 -0
- package/client/src/components/credentials/sections/RateLimitSection.tsx +1 -0
- package/client/src/components/icons/AIProviderIcons.tsx +1 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +1 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1 -0
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +1 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +1 -0
- package/client/src/components/ui/ComponentPalette.tsx +1 -0
- package/client/src/components/ui/MapSelector.tsx +1 -0
- package/client/src/components/ui/NodeContextMenu.tsx +3 -3
- package/client/src/components/ui/SettingsPanel.tsx +1 -0
- package/client/src/components/ui/action-button.tsx +1 -0
- package/client/src/components/ui/badge.tsx +1 -0
- package/client/src/components/ui/button.tsx +1 -0
- package/client/src/components/ui/form.tsx +1 -0
- package/client/src/components/ui/tabs.tsx +1 -0
- package/client/src/contexts/AuthContext.tsx +1 -0
- package/client/src/contexts/ThemeContext.tsx +1 -0
- package/client/src/contexts/WebSocketContext.tsx +104 -34
- package/client/src/hooks/__tests__/useApiKeys.test.ts +2 -2
- package/client/src/hooks/useReactFlowNodes.ts +1 -0
- package/client/src/hooks/useWorkflowValidation.ts +142 -0
- package/client/src/lib/nodeSpec.ts +1 -0
- package/client/src/test/providers.tsx +1 -0
- package/client/src/types/__tests__/cloudEvents.test.ts +5 -2
- package/client/src/types/cloudEvents.ts +19 -7
- package/client/src/utils/nodeUtils.ts +1 -1
- package/client/src/utils/workflow.ts +8 -2
- package/client/src/utils/workflowExport.ts +60 -3
- package/package.json +24 -23
- package/scripts/install.js +16 -27
- package/scripts/migrate_icons.py +3 -1
- package/scripts/migrate_skill_icons.py +6 -7
- package/scripts/postinstall.js +11 -9
- package/server/config/ai_cli_providers.json +2 -3
- package/server/config/credential_providers.json +15 -15
- package/server/config/llm_defaults.json +1 -1
- package/server/config/model_registry.json +416 -611
- package/server/constants.py +285 -223
- package/server/core/__init__.py +1 -1
- package/server/core/cache.py +9 -29
- package/server/core/cleanup.py +12 -24
- package/server/core/config.py +148 -24
- package/server/core/container.py +68 -59
- package/server/core/credential_backends.py +5 -13
- package/server/core/credentials_database.py +13 -43
- package/server/core/database.py +292 -353
- package/server/core/health.py +4 -5
- package/server/core/logging.py +241 -87
- package/server/core/paths.py +285 -0
- package/server/core/tracing.py +2 -8
- package/server/gunicorn.conf.py +1 -0
- package/server/main.py +150 -74
- package/server/middleware/auth.py +18 -24
- package/server/models/__init__.py +1 -1
- package/server/models/auth.py +5 -12
- package/server/models/database.py +36 -68
- package/server/models/node_metadata.py +25 -18
- package/server/nodejs/dist/index.js +107 -0
- package/server/nodes/README.md +11 -5
- package/server/nodes/__init__.py +1 -1
- package/server/nodes/_visuals.py +146 -14
- package/server/nodes/agent/_events.py +124 -0
- package/server/nodes/agent/_handles.py +15 -29
- package/server/nodes/agent/_inline.py +28 -25
- package/server/nodes/agent/_specialized.py +30 -15
- package/server/nodes/agent/{ai_agent.py → ai_agent/__init__.py} +33 -17
- package/server/nodes/agent/ai_agent/meta.json +3 -0
- package/server/nodes/agent/{ai_employee.py → ai_employee/__init__.py} +5 -2
- package/server/nodes/agent/ai_employee/meta.json +3 -0
- package/server/nodes/agent/{android_agent.py → android_agent/__init__.py} +1 -1
- package/server/nodes/agent/android_agent/meta.json +3 -0
- package/server/nodes/agent/{autonomous_agent.py → autonomous_agent/__init__.py} +2 -1
- package/server/nodes/agent/autonomous_agent/meta.json +3 -0
- package/server/nodes/agent/{chat_agent.py → chat_agent/__init__.py} +29 -12
- package/server/nodes/agent/chat_agent/meta.json +3 -0
- package/server/nodes/agent/{claude_code_agent.py → claude_code_agent/__init__.py} +192 -95
- package/server/nodes/agent/claude_code_agent/_handlers.py +169 -0
- package/server/{services/claude_oauth.py → nodes/agent/claude_code_agent/_oauth.py} +26 -13
- package/server/nodes/agent/claude_code_agent/_pool.py +1020 -0
- package/server/nodes/agent/claude_code_agent/_provider.py +513 -0
- package/server/nodes/agent/claude_code_agent/_skills.py +245 -0
- package/server/nodes/agent/claude_code_agent/meta.json +3 -0
- package/server/nodes/agent/{codex_agent.py → codex_agent/__init__.py} +26 -35
- package/server/nodes/agent/codex_agent/meta.json +3 -0
- package/server/nodes/agent/{coding_agent.py → coding_agent/__init__.py} +1 -1
- package/server/nodes/agent/coding_agent/meta.json +3 -0
- package/server/nodes/agent/{consumer_agent.py → consumer_agent/__init__.py} +1 -1
- package/server/nodes/agent/consumer_agent/meta.json +3 -0
- package/server/nodes/agent/{orchestrator_agent.py → orchestrator_agent/__init__.py} +5 -2
- package/server/nodes/agent/orchestrator_agent/meta.json +3 -0
- package/server/nodes/agent/{payments_agent.py → payments_agent/__init__.py} +1 -1
- package/server/nodes/agent/payments_agent/meta.json +3 -0
- package/server/nodes/agent/{productivity_agent.py → productivity_agent/__init__.py} +1 -1
- package/server/nodes/agent/productivity_agent/meta.json +3 -0
- package/server/nodes/agent/{rlm_agent.py → rlm_agent/__init__.py} +18 -17
- package/server/nodes/agent/rlm_agent/meta.json +3 -0
- package/server/nodes/agent/{social_agent.py → social_agent/__init__.py} +1 -1
- package/server/nodes/agent/social_agent/meta.json +3 -0
- package/server/nodes/agent/{task_agent.py → task_agent/__init__.py} +1 -1
- package/server/nodes/agent/task_agent/meta.json +3 -0
- package/server/nodes/agent/{tool_agent.py → tool_agent/__init__.py} +1 -1
- package/server/nodes/agent/tool_agent/meta.json +3 -0
- package/server/nodes/agent/{travel_agent.py → travel_agent/__init__.py} +1 -1
- package/server/nodes/agent/travel_agent/meta.json +3 -0
- package/server/nodes/agent/{web_agent.py → web_agent/__init__.py} +1 -1
- package/server/nodes/agent/web_agent/meta.json +3 -0
- package/server/nodes/android/__init__.py +24 -0
- package/server/nodes/android/_base.py +93 -76
- package/server/nodes/android/_dispatcher.py +140 -223
- package/server/nodes/android/_events.py +154 -0
- package/server/nodes/android/_handlers.py +13 -7
- package/server/nodes/android/_option_loaders.py +1 -4
- package/server/nodes/android/_refresh.py +27 -37
- package/server/nodes/android/_relay/broadcaster.py +25 -41
- package/server/nodes/android/_relay/client.py +23 -42
- package/server/nodes/android/_relay/manager.py +1 -0
- package/server/nodes/android/_relay/protocol.py +6 -0
- package/server/nodes/android/_router.py +48 -133
- package/server/nodes/android/{airplane_mode_control.py → airplane_mode_control/__init__.py} +2 -1
- package/server/nodes/android/airplane_mode_control/meta.json +3 -0
- package/server/nodes/android/{app_launcher.py → app_launcher/__init__.py} +2 -1
- package/server/nodes/android/app_launcher/meta.json +3 -0
- package/server/nodes/android/{app_list.py → app_list/__init__.py} +2 -1
- package/server/nodes/android/app_list/meta.json +3 -0
- package/server/nodes/android/{audio_automation.py → audio_automation/__init__.py} +2 -1
- package/server/nodes/android/audio_automation/meta.json +3 -0
- package/server/nodes/android/{battery_monitor.py → battery_monitor/__init__.py} +2 -1
- package/server/nodes/android/battery_monitor/meta.json +3 -0
- package/server/nodes/android/{bluetooth_automation.py → bluetooth_automation/__init__.py} +2 -1
- package/server/nodes/android/bluetooth_automation/meta.json +3 -0
- package/server/nodes/android/{camera_control.py → camera_control/__init__.py} +2 -1
- package/server/nodes/android/camera_control/meta.json +3 -0
- package/server/nodes/android/{device_state_automation.py → device_state_automation/__init__.py} +2 -1
- package/server/nodes/android/device_state_automation/meta.json +3 -0
- package/server/nodes/android/{environmental_sensors.py → environmental_sensors/__init__.py} +2 -1
- package/server/nodes/android/environmental_sensors/meta.json +3 -0
- package/server/nodes/android/{location.py → location/__init__.py} +2 -1
- package/server/nodes/android/location/meta.json +3 -0
- package/server/nodes/android/{media_control.py → media_control/__init__.py} +2 -1
- package/server/nodes/android/media_control/meta.json +3 -0
- package/server/nodes/android/{motion_detection.py → motion_detection/__init__.py} +2 -1
- package/server/nodes/android/motion_detection/meta.json +3 -0
- package/server/nodes/android/{network_monitor.py → network_monitor/__init__.py} +2 -1
- package/server/nodes/android/network_monitor/meta.json +3 -0
- package/server/nodes/android/{screen_control_automation.py → screen_control_automation/__init__.py} +2 -1
- package/server/nodes/android/screen_control_automation/meta.json +3 -0
- package/server/nodes/android/{system_info.py → system_info/__init__.py} +2 -1
- package/server/nodes/android/system_info/meta.json +3 -0
- package/server/nodes/android/{wifi_automation.py → wifi_automation/__init__.py} +2 -1
- package/server/nodes/android/wifi_automation/meta.json +3 -0
- package/server/nodes/browser/__init__.py +22 -1
- package/server/nodes/browser/_install.py +63 -0
- package/server/nodes/browser/_service.py +21 -25
- package/server/nodes/browser/{browser.py → browser/__init__.py} +58 -25
- package/server/nodes/browser/browser/meta.json +3 -0
- package/server/nodes/chat/{chat_history.py → chat_history/__init__.py} +2 -4
- package/server/nodes/chat/chat_history/meta.json +3 -0
- package/server/nodes/chat/{chat_send.py → chat_send/__init__.py} +2 -4
- package/server/nodes/chat/chat_send/icon.svg +1 -0
- package/server/nodes/chat/chat_send/meta.json +3 -0
- package/server/nodes/code/_base.py +1 -1
- package/server/nodes/code/{javascript_executor.py → javascript_executor/__init__.py} +5 -5
- package/server/nodes/code/javascript_executor/meta.json +3 -0
- package/server/nodes/code/{python_executor.py → python_executor/__init__.py} +32 -14
- package/server/nodes/code/python_executor/meta.json +3 -0
- package/server/nodes/code/{typescript_executor.py → typescript_executor/__init__.py} +5 -5
- package/server/nodes/code/typescript_executor/meta.json +3 -0
- package/server/nodes/document/{document_parser.py → document_parser/__init__.py} +26 -15
- package/server/nodes/document/document_parser/meta.json +3 -0
- package/server/nodes/document/{embedding_generator.py → embedding_generator/__init__.py} +16 -9
- package/server/nodes/document/embedding_generator/meta.json +3 -0
- package/server/nodes/document/{file_downloader.py → file_downloader/__init__.py} +30 -20
- package/server/nodes/document/file_downloader/meta.json +3 -0
- package/server/nodes/document/{http_scraper.py → http_scraper/__init__.py} +31 -21
- package/server/nodes/document/http_scraper/meta.json +3 -0
- package/server/nodes/document/{text_chunker.py → text_chunker/__init__.py} +17 -12
- package/server/nodes/document/text_chunker/meta.json +3 -0
- package/server/nodes/document/{vector_store.py → vector_store/__init__.py} +88 -72
- package/server/nodes/document/vector_store/meta.json +3 -0
- package/server/nodes/email/__init__.py +9 -2
- package/server/nodes/email/_events.py +54 -0
- package/server/nodes/email/_filters.py +3 -3
- package/server/nodes/email/_himalaya.py +95 -50
- package/server/nodes/email/_service.py +23 -13
- package/server/nodes/email/{email_read.py → email_read/__init__.py} +23 -11
- package/server/nodes/email/email_read/icon.svg +6 -0
- package/server/nodes/email/email_read/meta.json +3 -0
- package/server/nodes/email/{email_receive.py → email_receive/__init__.py} +45 -23
- package/server/nodes/email/email_receive/meta.json +3 -0
- package/server/nodes/email/{email_send.py → email_send/__init__.py} +13 -7
- package/server/nodes/email/email_send/meta.json +3 -0
- package/server/nodes/filesystem/_backend.py +1 -5
- package/server/nodes/filesystem/{file_modify.py → file_modify/__init__.py} +10 -5
- package/server/nodes/filesystem/file_modify/meta.json +3 -0
- package/server/nodes/filesystem/{file_read.py → file_read/__init__.py} +7 -3
- package/server/nodes/filesystem/file_read/meta.json +3 -0
- package/server/nodes/filesystem/{fs_search.py → fs_search/__init__.py} +11 -3
- package/server/nodes/filesystem/fs_search/meta.json +3 -0
- package/server/nodes/filesystem/{shell.py → shell/__init__.py} +12 -5
- package/server/nodes/filesystem/shell/meta.json +3 -0
- package/server/nodes/google/__init__.py +12 -0
- package/server/nodes/google/_auth_helper.py +7 -13
- package/server/nodes/google/_base.py +14 -11
- package/server/nodes/google/_credentials.py +2 -1
- package/server/nodes/google/_events.py +47 -0
- package/server/nodes/google/_filters.py +3 -3
- package/server/nodes/google/_gmail.py +70 -47
- package/server/nodes/google/_handlers.py +3 -1
- package/server/nodes/google/_oauth.py +25 -11
- package/server/nodes/google/_option_loaders.py +9 -30
- package/server/nodes/google/_refresh.py +8 -12
- package/server/nodes/google/_router.py +4 -5
- package/server/nodes/google/{calendar.py → calendar/__init__.py} +87 -64
- package/server/nodes/google/calendar/meta.json +3 -0
- package/server/nodes/google/{contacts.py → contacts/__init__.py} +84 -72
- package/server/nodes/google/contacts/meta.json +3 -0
- package/server/nodes/google/{drive.py → drive/__init__.py} +87 -72
- package/server/nodes/google/drive/meta.json +3 -0
- package/server/nodes/google/{gmail.py → gmail/__init__.py} +73 -39
- package/server/nodes/google/gmail/meta.json +3 -0
- package/server/nodes/google/{gmail_receive.py → gmail_receive/__init__.py} +31 -24
- package/server/nodes/google/gmail_receive/icon.svg +7 -0
- package/server/nodes/google/gmail_receive/meta.json +3 -0
- package/server/nodes/google/google.svg +7 -0
- package/server/nodes/google/{sheets.py → sheets/__init__.py} +54 -42
- package/server/nodes/google/sheets/meta.json +3 -0
- package/server/nodes/google/{tasks.py → tasks/__init__.py} +56 -43
- package/server/nodes/google/tasks/meta.json +3 -0
- package/server/nodes/groups.py +28 -28
- package/server/nodes/location/__init__.py +31 -1
- package/server/nodes/location/_credentials.py +1 -6
- package/server/nodes/location/_service.py +88 -107
- package/server/nodes/location/{gmaps_create.py → gmaps_create/__init__.py} +6 -6
- package/server/nodes/location/gmaps_create/meta.json +3 -0
- package/server/nodes/location/{gmaps_locations.py → gmaps_locations/__init__.py} +8 -6
- package/server/nodes/location/gmaps_locations/meta.json +3 -0
- package/server/nodes/location/{gmaps_nearby_places.py → gmaps_nearby_places/__init__.py} +8 -6
- package/server/nodes/location/gmaps_nearby_places/meta.json +3 -0
- package/server/nodes/model/_base.py +10 -7
- package/server/nodes/model/_credentials.py +10 -10
- package/server/nodes/model/_local_validator.py +28 -24
- package/server/nodes/model/{anthropic_chat_model.py → anthropic_chat_model/__init__.py} +5 -3
- package/server/nodes/model/anthropic_chat_model/meta.json +3 -0
- package/server/nodes/model/{cerebras_chat_model.py → cerebras_chat_model/__init__.py} +5 -3
- package/server/nodes/model/cerebras_chat_model/meta.json +3 -0
- package/server/nodes/model/{deepseek_chat_model.py → deepseek_chat_model/__init__.py} +8 -4
- package/server/nodes/model/deepseek_chat_model/meta.json +3 -0
- package/server/nodes/model/{gemini_chat_model.py → gemini_chat_model/__init__.py} +5 -3
- package/server/nodes/model/gemini_chat_model/meta.json +3 -0
- package/server/nodes/model/{groq_chat_model.py → groq_chat_model/__init__.py} +2 -2
- package/server/nodes/model/groq_chat_model/meta.json +3 -0
- package/server/nodes/model/{kimi_chat_model.py → kimi_chat_model/__init__.py} +2 -2
- package/server/nodes/model/kimi_chat_model/meta.json +3 -0
- package/server/nodes/model/{lmstudio_chat_model.py → lmstudio_chat_model/__init__.py} +2 -2
- package/server/nodes/model/lmstudio_chat_model/meta.json +3 -0
- package/server/nodes/model/{mistral_chat_model.py → mistral_chat_model/__init__.py} +2 -2
- package/server/nodes/model/mistral_chat_model/meta.json +3 -0
- package/server/nodes/model/{ollama_chat_model.py → ollama_chat_model/__init__.py} +2 -2
- package/server/nodes/model/ollama_chat_model/meta.json +3 -0
- package/server/nodes/model/{openai_chat_model.py → openai_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openai_chat_model/meta.json +3 -0
- package/server/nodes/model/{openrouter_chat_model.py → openrouter_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openrouter_chat_model/meta.json +3 -0
- package/server/nodes/proxy/_usage.py +14 -15
- package/server/nodes/proxy/{proxy_config.py → proxy_config/__init__.py} +39 -30
- package/server/nodes/proxy/proxy_config/meta.json +3 -0
- package/server/nodes/proxy/{proxy_request.py → proxy_request/__init__.py} +30 -16
- package/server/nodes/proxy/proxy_request/meta.json +3 -0
- package/server/nodes/proxy/{proxy_status.py → proxy_status/__init__.py} +2 -0
- package/server/nodes/proxy/proxy_status/meta.json +3 -0
- package/server/nodes/scheduler/{cron_scheduler.py → cron_scheduler/__init__.py} +96 -23
- package/server/nodes/scheduler/cron_scheduler/_workflow.py +155 -0
- package/server/nodes/scheduler/cron_scheduler/meta.json +3 -0
- package/server/nodes/scheduler/{timer.py → timer/__init__.py} +6 -5
- package/server/nodes/scheduler/timer/meta.json +3 -0
- package/server/nodes/scraper/_credentials.py +0 -1
- package/server/nodes/scraper/{apify_actor.py → apify_actor/__init__.py} +44 -35
- package/server/nodes/scraper/apify_actor/icon.svg +5 -0
- package/server/nodes/scraper/apify_actor/meta.json +3 -0
- package/server/nodes/scraper/{crawlee_scraper.py → crawlee_scraper/__init__.py} +96 -57
- package/server/nodes/scraper/crawlee_scraper/meta.json +3 -0
- package/server/nodes/search/{brave_search.py → brave_search/__init__.py} +6 -5
- package/server/nodes/search/brave_search/icon.svg +3 -0
- package/server/nodes/search/brave_search/meta.json +3 -0
- package/server/nodes/search/{duckduckgo_search.py → duckduckgo_search/__init__.py} +17 -6
- package/server/nodes/search/duckduckgo_search/meta.json +3 -0
- package/server/nodes/search/{perplexity_search.py → perplexity_search/__init__.py} +4 -5
- package/server/nodes/search/perplexity_search/icon.svg +3 -0
- package/server/nodes/search/perplexity_search/meta.json +3 -0
- package/server/nodes/search/{serper_search.py → serper_search/__init__.py} +32 -25
- package/server/nodes/search/serper_search/icon.svg +3 -0
- package/server/nodes/search/serper_search/meta.json +3 -0
- package/server/nodes/skill/__init__.py +21 -1
- package/server/nodes/skill/_expander.py +75 -0
- package/server/nodes/skill/{master_skill.py → master_skill/__init__.py} +2 -8
- package/server/nodes/skill/master_skill/_events.py +84 -0
- package/server/nodes/skill/master_skill/meta.json +3 -0
- package/server/nodes/skill/{simple_memory.py → simple_memory/__init__.py} +8 -16
- package/server/nodes/skill/simple_memory/meta.json +3 -0
- package/server/nodes/social/_base.py +223 -231
- package/server/nodes/social/{social_receive.py → social_receive/__init__.py} +38 -13
- package/server/nodes/social/social_receive/meta.json +3 -0
- package/server/nodes/social/{social_send.py → social_send/__init__.py} +71 -29
- package/server/nodes/social/social_send/icon.svg +1 -0
- package/server/nodes/social/social_send/meta.json +3 -0
- package/server/nodes/stripe/__init__.py +7 -3
- package/server/nodes/stripe/_credentials.py +0 -1
- package/server/nodes/stripe/_handlers.py +18 -7
- package/server/nodes/stripe/_install.py +14 -15
- package/server/nodes/stripe/_source.py +5 -5
- package/server/nodes/stripe/icon.svg +1 -0
- package/server/nodes/stripe/meta.json +3 -0
- package/server/nodes/stripe/stripe_action.py +4 -4
- package/server/nodes/stripe/stripe_receive.py +6 -9
- package/server/nodes/telegram/__init__.py +13 -0
- package/server/nodes/telegram/_credentials.py +2 -7
- package/server/nodes/telegram/_events.py +167 -0
- package/server/nodes/telegram/_filters.py +3 -11
- package/server/nodes/telegram/_handlers.py +17 -7
- package/server/nodes/telegram/_refresh.py +24 -34
- package/server/nodes/telegram/_service.py +29 -45
- package/server/nodes/telegram/meta.json +3 -0
- package/server/nodes/telegram/telegram.svg +3 -0
- package/server/nodes/telegram/telegram_receive.py +38 -18
- package/server/nodes/telegram/telegram_send.py +21 -19
- package/server/nodes/text/{file_handler.py → file_handler/__init__.py} +7 -1
- package/server/nodes/text/file_handler/meta.json +3 -0
- package/server/nodes/text/{text_generator.py → text_generator/__init__.py} +2 -1
- package/server/nodes/text/text_generator/meta.json +3 -0
- package/server/nodes/tool/{agent_builder.py → agent_builder/__init__.py} +105 -100
- package/server/nodes/tool/agent_builder/_events.py +91 -0
- package/server/nodes/tool/agent_builder/meta.json +3 -0
- package/server/nodes/tool/{calculator_tool.py → calculator_tool/__init__.py} +19 -7
- package/server/nodes/tool/calculator_tool/meta.json +3 -0
- package/server/nodes/tool/{current_time_tool.py → current_time_tool/__init__.py} +6 -4
- package/server/nodes/tool/current_time_tool/meta.json +3 -0
- package/server/nodes/tool/{task_manager.py → task_manager/__init__.py} +17 -18
- package/server/nodes/tool/task_manager/meta.json +3 -0
- package/server/nodes/tool/{write_todos.py → write_todos/__init__.py} +20 -6
- package/server/nodes/tool/write_todos/meta.json +3 -0
- package/server/nodes/trigger/{chat_trigger.py → chat_trigger/__init__.py} +11 -7
- package/server/nodes/trigger/chat_trigger/_events.py +53 -0
- package/server/nodes/trigger/chat_trigger/meta.json +3 -0
- package/server/nodes/trigger/{task_trigger.py → task_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/task_trigger/meta.json +3 -0
- package/server/nodes/trigger/{webhook_trigger.py → webhook_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/webhook_trigger/_events.py +54 -0
- package/server/nodes/trigger/webhook_trigger/meta.json +3 -0
- package/server/nodes/twitter/__init__.py +7 -1
- package/server/nodes/twitter/_base.py +86 -61
- package/server/nodes/twitter/_credentials.py +7 -5
- package/server/nodes/twitter/_events.py +101 -0
- package/server/nodes/twitter/_filters.py +9 -9
- package/server/nodes/twitter/_handlers.py +3 -1
- package/server/nodes/twitter/_oauth.py +1 -2
- package/server/nodes/twitter/_refresh.py +8 -12
- package/server/nodes/twitter/{twitter_receive.py → twitter_receive/__init__.py} +7 -7
- package/server/nodes/twitter/twitter_receive/icon.svg +1 -0
- package/server/nodes/twitter/twitter_receive/meta.json +3 -0
- package/server/nodes/twitter/{twitter_search.py → twitter_search/__init__.py} +16 -11
- package/server/nodes/twitter/twitter_search/icon.svg +1 -0
- package/server/nodes/twitter/twitter_search/meta.json +3 -0
- package/server/nodes/twitter/{twitter_send.py → twitter_send/__init__.py} +60 -27
- package/server/nodes/twitter/twitter_send/icon.svg +1 -0
- package/server/nodes/twitter/twitter_send/meta.json +3 -0
- package/server/nodes/twitter/{twitter_user.py → twitter_user/__init__.py} +34 -19
- package/server/nodes/twitter/twitter_user/icon.svg +1 -0
- package/server/nodes/twitter/twitter_user/meta.json +3 -0
- package/server/nodes/utility/{console.py → console/__init__.py} +17 -22
- package/server/nodes/utility/console/meta.json +3 -0
- package/server/nodes/utility/{http_request.py → http_request/__init__.py} +9 -6
- package/server/nodes/utility/http_request/meta.json +3 -0
- package/server/nodes/utility/{process_manager.py → process_manager/__init__.py} +10 -6
- package/server/nodes/utility/process_manager/meta.json +3 -0
- package/server/nodes/utility/team_monitor/meta.json +3 -0
- package/server/nodes/utility/{webhook_response.py → webhook_response/__init__.py} +12 -7
- package/server/nodes/utility/webhook_response/meta.json +3 -0
- package/server/nodes/visuals.json +69 -251
- package/server/nodes/whatsapp/__init__.py +24 -0
- package/server/nodes/whatsapp/_base.py +283 -338
- package/server/nodes/whatsapp/_credentials.py +44 -0
- package/server/nodes/whatsapp/_events.py +277 -0
- package/server/nodes/whatsapp/_filters.py +36 -37
- package/server/nodes/whatsapp/_handlers.py +2 -0
- package/server/nodes/whatsapp/_option_loaders.py +1 -3
- package/server/nodes/whatsapp/_refresh.py +13 -18
- package/server/nodes/whatsapp/_runtime.py +9 -6
- package/server/nodes/whatsapp/_service.py +89 -152
- package/server/nodes/whatsapp/meta.json +3 -0
- package/server/nodes/whatsapp/whatsapp_db.py +116 -54
- package/server/nodes/whatsapp/whatsapp_receive.py +30 -13
- package/server/nodes/whatsapp/whatsapp_send.py +60 -37
- package/server/nodes/workflow/{start.py → start/__init__.py} +1 -4
- package/server/nodes/workflow/start/meta.json +3 -0
- package/server/package-lock.json +3 -3
- package/server/package.json +3 -0
- package/server/pyproject.toml +39 -10
- package/server/requirements.txt +3 -5
- package/server/routers/__init__.py +1 -1
- package/server/routers/auth.py +16 -56
- package/server/routers/database.py +27 -50
- package/server/routers/nodejs_compat.py +25 -87
- package/server/routers/schemas.py +66 -2
- package/server/routers/webhook.py +12 -12
- package/server/routers/websocket.py +312 -1716
- package/server/routers/workflow.py +28 -53
- package/server/scripts/smoke_test_skills.py +178 -0
- package/server/services/__init__.py +1 -1
- package/server/services/_supervisor/process.py +9 -3
- package/server/services/_supervisor/registry.py +3 -3
- package/server/services/_supervisor/util.py +1 -1
- package/server/services/agent_team.py +15 -43
- package/server/services/agent_teams/__init__.py +17 -0
- package/server/services/agent_teams/handlers.py +195 -0
- package/server/services/ai.py +853 -1108
- package/server/services/auth.py +10 -34
- package/server/services/chat_client.py +5 -34
- package/server/services/circuit_breaker.py +2 -6
- package/server/services/cli_agent/__init__.py +28 -4
- package/server/services/cli_agent/_cli_auth.py +61 -0
- package/server/services/cli_agent/_handlers.py +24 -183
- package/server/services/cli_agent/config.py +5 -8
- package/server/services/cli_agent/factory.py +168 -22
- package/server/services/cli_agent/jsonl_watcher.py +380 -0
- package/server/services/cli_agent/lockfile.py +9 -2
- package/server/services/cli_agent/mcp_server.py +110 -34
- package/server/services/cli_agent/protocol.py +37 -19
- package/server/services/cli_agent/providers/__init__.py +8 -4
- package/server/services/cli_agent/providers/google_gemini.py +11 -5
- package/server/services/cli_agent/providers/openai_codex.py +34 -34
- package/server/services/cli_agent/service.py +245 -83
- package/server/services/cli_agent/session.py +409 -229
- package/server/services/cli_agent/transports/__init__.py +47 -0
- package/server/services/cli_agent/transports/base.py +111 -0
- package/server/services/cli_agent/transports/posix.py +196 -0
- package/server/services/cli_agent/transports/windows.py +189 -0
- package/server/services/cli_agent/types.py +45 -18
- package/server/services/cli_agent/workflow_tools.py +28 -15
- package/server/services/compaction.py +68 -52
- package/server/services/credential_registry.py +6 -20
- package/server/services/credentials/__init__.py +18 -0
- package/server/services/credentials/handlers.py +196 -0
- package/server/services/deployment/__init__.py +12 -1
- package/server/services/deployment/canary_registry.py +137 -0
- package/server/services/deployment/handlers.py +382 -0
- package/server/services/deployment/manager.py +653 -163
- package/server/services/deployment/poll_registry.py +2 -6
- package/server/services/deployment/state.py +2 -0
- package/server/services/deployment/triggers.py +87 -93
- package/server/services/event_waiter.py +47 -54
- package/server/services/events/__init__.py +11 -0
- package/server/services/events/admin_handlers.py +368 -0
- package/server/services/events/daemon.py +3 -1
- package/server/services/events/dispatch.py +188 -0
- package/server/services/events/envelope.py +264 -45
- package/server/services/events/oauth_lifecycle.py +98 -42
- package/server/services/events/triggers.py +3 -13
- package/server/services/events/verifiers/hmac_basic.py +1 -1
- package/server/services/events/verifiers/standard_webhooks.py +2 -4
- package/server/services/events/webhook.py +2 -3
- package/server/services/example_loader.py +73 -15
- package/server/services/execution/cache.py +36 -76
- package/server/services/execution/conditions.py +7 -20
- package/server/services/execution/dlq.py +20 -24
- package/server/services/execution/executor.py +234 -265
- package/server/services/execution/models.py +40 -46
- package/server/services/execution/recovery.py +23 -46
- package/server/services/handlers/__init__.py +12 -16
- package/server/services/handlers/todo.py +3 -6
- package/server/services/handlers/tools.py +143 -194
- package/server/services/handlers/triggers.py +24 -23
- package/server/services/llm/config.py +10 -1
- package/server/services/llm/factory.py +16 -4
- package/server/services/llm/messages.py +1 -5
- package/server/services/llm/protocol.py +9 -1
- package/server/services/llm/providers/anthropic.py +23 -12
- package/server/services/llm/providers/gemini.py +43 -22
- package/server/services/llm/providers/openai.py +14 -6
- package/server/services/llm/providers/openrouter.py +6 -1
- package/server/services/markdown_formatter.py +1 -2
- package/server/services/memory/__init__.py +2 -2
- package/server/services/memory/jsonl.py +6 -2
- package/server/services/memory/markdown.py +6 -6
- package/server/services/memory/state.py +6 -5
- package/server/services/memory_store.py +8 -12
- package/server/services/model_registry.py +22 -20
- package/server/services/node_executor.py +85 -80
- package/server/services/node_output_schemas.py +4 -7
- package/server/services/node_registry.py +40 -4
- package/server/services/node_spec.py +3 -7
- package/server/services/nodejs_client.py +4 -14
- package/server/services/oauth_utils.py +11 -7
- package/server/services/parameter_resolver.py +30 -36
- package/server/services/plugin/base.py +321 -38
- package/server/services/plugin/connection.py +12 -7
- package/server/services/plugin/credential.py +80 -22
- package/server/services/plugin/edge_walker.py +128 -105
- package/server/services/plugin/identifiers.py +48 -0
- package/server/services/plugin/interceptor.py +1 -1
- package/server/services/plugin/oauth.py +25 -21
- package/server/services/plugin/operation.py +1 -1
- package/server/services/plugin/polling.py +151 -26
- package/server/services/plugin/registry.py +52 -4
- package/server/services/plugin/routing.py +6 -9
- package/server/services/plugin/scaling.py +36 -18
- package/server/services/plugin/service_factories.py +95 -0
- package/server/services/plugin/shutdown_hooks.py +103 -0
- package/server/services/plugin/social_provider_registry.py +80 -0
- package/server/services/plugin/ws.py +2 -1
- package/server/services/pricing.py +26 -40
- package/server/services/pricing_handlers.py +90 -0
- package/server/services/process_service.py +33 -32
- package/server/services/proxy/models.py +15 -9
- package/server/services/proxy/service.py +26 -40
- package/server/services/rlm/adapters.py +43 -40
- package/server/services/rlm/constants.py +9 -9
- package/server/services/rlm/service.py +57 -45
- package/server/services/scheduler.py +8 -39
- package/server/services/settings/__init__.py +16 -0
- package/server/services/settings/handlers.py +275 -0
- package/server/services/skill_loader.py +53 -45
- package/server/services/skill_prompt.py +8 -6
- package/server/services/skills/__init__.py +23 -0
- package/server/services/skills/handlers.py +479 -0
- package/server/services/status_broadcaster.py +314 -291
- package/server/services/temporal/__init__.py +22 -1
- package/server/services/temporal/_handlers.py +65 -0
- package/server/services/temporal/_install.py +158 -0
- package/server/services/temporal/_refresh.py +57 -0
- package/server/services/temporal/_retry_policies.py +85 -0
- package/server/services/temporal/_runtime.py +181 -0
- package/server/services/temporal/_supervised_runtime.py +102 -0
- package/server/services/temporal/activities.py +168 -11
- package/server/services/temporal/agent_activities.py +683 -0
- package/server/services/temporal/agent_workflow.py +601 -0
- package/server/services/temporal/client.py +58 -13
- package/server/services/temporal/executor.py +2 -3
- package/server/services/temporal/plugin_activities.py +37 -2
- package/server/services/temporal/plugin_registry.py +82 -0
- package/server/services/temporal/polling_trigger_workflow.py +267 -0
- package/server/services/temporal/schedules.py +220 -0
- package/server/services/temporal/search_attributes.py +177 -0
- package/server/services/temporal/trigger_listener_workflow.py +378 -0
- package/server/services/temporal/worker.py +111 -18
- package/server/services/temporal/workflow.py +259 -40
- package/server/services/temporal/ws_client.py +22 -11
- package/server/services/text.py +14 -28
- package/server/services/tracked_http.py +29 -49
- package/server/services/user_auth.py +7 -21
- package/server/services/workflow.py +28 -20
- package/server/services/workflow_import.py +351 -0
- package/server/services/workflow_ops.py +4 -0
- package/server/services/workflow_storage/__init__.py +18 -0
- package/server/services/workflow_storage/handlers.py +132 -0
- package/server/services/workflow_validator.py +209 -0
- package/server/services/ws_handler_registry.py +80 -9
- package/server/skills/assistant/agent-builder-skill/SKILL.md +6 -6
- package/server/tests/conftest.py +54 -3
- package/server/tests/credentials/test_auth_service.py +9 -21
- package/server/tests/credentials/test_credential_broadcasts.py +116 -22
- package/server/tests/credentials/test_credentials_database.py +12 -38
- package/server/tests/credentials/test_encryption.py +3 -9
- package/server/tests/credentials/test_google_oauth.py +1 -3
- package/server/tests/credentials/test_oauth_utils.py +31 -38
- package/server/tests/credentials/test_twitter_oauth.py +1 -3
- package/server/tests/credentials/test_websocket_handlers.py +37 -72
- package/server/tests/fixtures/tool_names_snapshot.json +78 -0
- package/server/tests/llm/test_factory.py +12 -4
- package/server/tests/llm/test_providers.py +25 -32
- package/server/tests/llm/test_wiring.py +27 -22
- package/server/tests/nodes/_compat.py +4 -5
- package/server/tests/nodes/_harness.py +31 -24
- package/server/tests/nodes/_mocks.py +2 -6
- package/server/tests/nodes/test_agent_builder.py +43 -35
- package/server/tests/nodes/test_ai_agents.py +29 -24
- package/server/tests/nodes/test_ai_chat_models.py +3 -9
- package/server/tests/nodes/test_ai_tools.py +29 -24
- package/server/tests/nodes/test_android.py +34 -64
- package/server/tests/nodes/test_chat_utility.py +2 -2
- package/server/tests/nodes/test_code_fs_process.py +26 -84
- package/server/tests/nodes/test_document.py +23 -47
- package/server/tests/nodes/test_email.py +88 -51
- package/server/tests/nodes/test_google_workspace.py +26 -20
- package/server/tests/nodes/test_http_proxy.py +43 -89
- package/server/tests/nodes/test_search.py +3 -9
- package/server/tests/nodes/test_specialized_agents.py +58 -162
- package/server/tests/nodes/test_stripe_plugin.py +25 -5
- package/server/tests/nodes/test_telegram_social.py +33 -37
- package/server/tests/nodes/test_twitter.py +59 -150
- package/server/tests/nodes/test_web_automation.py +21 -51
- package/server/tests/nodes/test_whatsapp.py +13 -19
- package/server/tests/nodes/test_workflow_triggers.py +16 -45
- package/server/tests/services/cli_agent/test_claude_session_events.py +201 -0
- package/server/tests/services/cli_agent/test_jsonl_watcher.py +190 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +67 -29
- package/server/tests/services/cli_agent/test_providers.py +236 -47
- package/server/tests/services/cli_agent/test_service.py +9 -7
- package/server/tests/services/memory/test_jsonl.py +30 -25
- package/server/tests/services/test_events.py +26 -7
- package/server/tests/services/test_identifiers.py +122 -0
- package/server/tests/services/test_process_lifecycle.py +129 -0
- package/server/tests/services/test_supervisor.py +0 -1
- package/server/tests/temporal/__init__.py +0 -0
- package/server/tests/temporal/test_agent_workflow.py +215 -0
- package/server/tests/temporal/test_dispatch.py +231 -0
- package/server/tests/test_admin_handlers.py +394 -0
- package/server/tests/test_auto_skill.py +4 -2
- package/server/tests/test_canary_registry.py +310 -0
- package/server/tests/test_chat_trigger_canary_producer.py +101 -0
- package/server/tests/test_cloudevents_node_parameters.py +129 -0
- package/server/tests/test_credential_icon.py +115 -0
- package/server/tests/test_cron_canary.py +511 -0
- package/server/tests/test_deployment_canary_listener.py +692 -0
- package/server/tests/test_event_framework_phase_a.py +537 -0
- package/server/tests/test_no_raw_prints.py +131 -0
- package/server/tests/test_node_spec.py +196 -103
- package/server/tests/test_parameter_resolver.py +20 -20
- package/server/tests/test_plugin_contract.py +76 -49
- package/server/tests/test_plugin_helpers.py +0 -1
- package/server/tests/test_plugin_self_containment.py +40 -47
- package/server/tests/test_polling_trigger_workflow.py +572 -0
- package/server/tests/test_retry_policies.py +146 -0
- package/server/tests/test_service_factories.py +168 -0
- package/server/tests/test_shutdown_hooks.py +199 -0
- package/server/tests/test_social_provider_registry.py +177 -0
- package/server/tests/test_status_broadcasts.py +214 -63
- package/server/tests/test_task_trigger_canary_producer.py +131 -0
- package/server/tests/test_telegram_trigger_canary_producer.py +113 -0
- package/server/tests/test_tool_registry.py +110 -0
- package/server/tests/test_trigger_listener_workflow.py +365 -0
- package/server/tests/test_whatsapp_trigger_canary_producer.py +164 -0
- package/server/tests/test_workflow_ops.py +1 -3
- package/server/tests/test_workflow_validator.py +791 -0
- package/server/uv.lock +3539 -0
- package/client/dist/assets/index-DQ0nwhec.js +0 -257
- package/client/src/assets/icons/apify/index.ts +0 -19
- package/client/src/assets/icons/browser/index.ts +0 -17
- package/client/src/assets/icons/email/index.ts +0 -22
- package/client/src/assets/icons/google/index.ts +0 -34
- package/client/src/assets/icons/llm/deepseek.svg +0 -1
- package/client/src/assets/icons/llm/index.ts +0 -18
- package/client/src/assets/icons/llm/kimi.svg +0 -1
- package/client/src/assets/icons/llm/mistral.svg +0 -1
- package/client/src/assets/icons/search/index.ts +0 -28
- package/client/src/assets/icons/telegram/index.ts +0 -19
- package/machina/buildenv.py +0 -44
- package/machina/cli.py +0 -55
- package/machina/commands/__init__.py +0 -1
- package/machina/commands/clean.py +0 -80
- package/machina/commands/daemon.py +0 -150
- package/machina/config.py +0 -93
- package/machina/platform_.py +0 -37
- package/machina/pyproject.toml +0 -33
- package/server/nodes/agent/deep_agent.py +0 -103
- package/server/services/agents/__init__.py +0 -9
- package/server/services/agents/adapters.py +0 -199
- package/server/services/agents/constants.py +0 -10
- package/server/services/agents/service.py +0 -297
- package/server/services/cli_agent/providers/anthropic_claude.py +0 -419
- /package/{machina → cli}/README.md +0 -0
- /package/{machina → cli}/__init__.py +0 -0
- /package/{client/src/assets/icons/apify → server/credentials/icons}/apify.svg +0 -0
- /package/{client/src/assets/icons/search/brave.svg → server/credentials/icons/brave_search.svg} +0 -0
- /package/{client/src/assets/icons/email/read.svg → server/credentials/icons/email_himalaya.svg} +0 -0
- /package/{client/src/assets/icons/search → server/credentials/icons}/perplexity.svg +0 -0
- /package/{client/src/assets/icons/search/google.svg → server/credentials/icons/serper.svg} +0 -0
- /package/{client/src/assets → server/credentials}/icons/stripe.svg +0 -0
- /package/{client/src/assets/icons/twitter/x.svg → server/credentials/icons/twitter.svg} +0 -0
- /package/{client/src/assets/icons/browser/chrome.svg → server/nodes/browser/browser/icon.svg} +0 -0
- /package/{client/src/assets/icons/chat/chat.svg → server/nodes/chat/chat_history/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/javascript.svg → server/nodes/code/javascript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/python.svg → server/nodes/code/python_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/typescript.svg → server/nodes/code/typescript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/receive.svg → server/nodes/email/email_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/send.svg → server/nodes/email/email_send/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/calendar.svg → server/nodes/google/calendar/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/contacts.svg → server/nodes/google/contacts/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/drive.svg → server/nodes/google/drive/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/gmail.svg → server/nodes/google/gmail/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/sheets.svg → server/nodes/google/sheets/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/tasks.svg → server/nodes/google/tasks/icon.svg} +0 -0
- /package/{client/src/assets/icons/search/duckduckgo.svg → server/nodes/search/duckduckgo_search/icon.svg} +0 -0
- /package/{client/src/assets/icons/social/social.svg → server/nodes/social/social_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/telegram/telegram.svg → server/nodes/telegram/icon.svg} +0 -0
- /package/server/nodes/utility/{team_monitor.py → team_monitor/__init__.py} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-db.svg → server/nodes/whatsapp/icon_whatsappDb.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-receive.svg → server/nodes/whatsapp/icon_whatsappReceive.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-send.svg → server/nodes/whatsapp/icon_whatsappSend.svg} +0 -0
- /package/{client/src/assets/icons → server/nodes}/whatsapp/whatsapp.svg +0 -0
|
@@ -57,10 +57,13 @@ class WorkflowExecutor:
|
|
|
57
57
|
- Event history for debugging and recovery
|
|
58
58
|
"""
|
|
59
59
|
|
|
60
|
-
def __init__(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
cache: ExecutionCache,
|
|
63
|
+
node_executor: Callable[[str, str, Dict, Dict], Awaitable[Dict]],
|
|
64
|
+
status_callback: Callable[[str, str, Dict], Awaitable[None]] = None,
|
|
65
|
+
dlq_enabled: bool = False,
|
|
66
|
+
):
|
|
64
67
|
"""Initialize executor.
|
|
65
68
|
|
|
66
69
|
Args:
|
|
@@ -85,9 +88,9 @@ class WorkflowExecutor:
|
|
|
85
88
|
# EXECUTION ENTRY POINTS
|
|
86
89
|
# =========================================================================
|
|
87
90
|
|
|
88
|
-
async def execute_workflow(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
async def execute_workflow(
|
|
92
|
+
self, workflow_id: str, nodes: List[Dict], edges: List[Dict], session_id: str = "default", enable_caching: bool = True
|
|
93
|
+
) -> Dict[str, Any]:
|
|
91
94
|
"""Execute a workflow with parallel node execution.
|
|
92
95
|
|
|
93
96
|
Args:
|
|
@@ -113,11 +116,13 @@ class WorkflowExecutor:
|
|
|
113
116
|
# Compute execution layers (for parallel batches)
|
|
114
117
|
ctx.execution_order = self._compute_execution_layers(nodes, edges)
|
|
115
118
|
|
|
116
|
-
logger.info(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
logger.info(
|
|
120
|
+
"Starting workflow execution",
|
|
121
|
+
execution_id=ctx.execution_id,
|
|
122
|
+
workflow_id=workflow_id,
|
|
123
|
+
node_count=len(nodes),
|
|
124
|
+
layers=len(ctx.execution_order),
|
|
125
|
+
)
|
|
121
126
|
|
|
122
127
|
# Track in memory
|
|
123
128
|
self._active_contexts[ctx.execution_id] = ctx
|
|
@@ -128,10 +133,14 @@ class WorkflowExecutor:
|
|
|
128
133
|
await self.cache.save_execution_state(ctx)
|
|
129
134
|
|
|
130
135
|
# Add workflow_started event
|
|
131
|
-
await self.cache.add_event(
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
|
|
136
|
+
await self.cache.add_event(
|
|
137
|
+
ctx.execution_id,
|
|
138
|
+
"workflow_started",
|
|
139
|
+
{
|
|
140
|
+
"workflow_id": workflow_id,
|
|
141
|
+
"node_count": len(nodes),
|
|
142
|
+
},
|
|
143
|
+
)
|
|
135
144
|
|
|
136
145
|
try:
|
|
137
146
|
# Run the decide loop
|
|
@@ -147,11 +156,15 @@ class WorkflowExecutor:
|
|
|
147
156
|
await self.cache.save_execution_state(ctx)
|
|
148
157
|
|
|
149
158
|
# Add workflow_completed event
|
|
150
|
-
await self.cache.add_event(
|
|
151
|
-
|
|
152
|
-
"
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
await self.cache.add_event(
|
|
160
|
+
ctx.execution_id,
|
|
161
|
+
"workflow_completed",
|
|
162
|
+
{
|
|
163
|
+
"status": ctx.status.value,
|
|
164
|
+
"completed_nodes": len(ctx.get_completed_nodes()),
|
|
165
|
+
"execution_time": ctx.completed_at - ctx.started_at,
|
|
166
|
+
},
|
|
167
|
+
)
|
|
155
168
|
|
|
156
169
|
return {
|
|
157
170
|
"success": ctx.status == WorkflowStatus.COMPLETED,
|
|
@@ -178,14 +191,17 @@ class WorkflowExecutor:
|
|
|
178
191
|
}
|
|
179
192
|
|
|
180
193
|
except Exception as e:
|
|
181
|
-
logger.error("Workflow execution failed", execution_id=ctx.execution_id,
|
|
182
|
-
error=str(e))
|
|
194
|
+
logger.error("Workflow execution failed", execution_id=ctx.execution_id, error=str(e))
|
|
183
195
|
ctx.status = WorkflowStatus.FAILED
|
|
184
196
|
ctx.errors.append({"error": str(e), "timestamp": time.time()})
|
|
185
197
|
await self.cache.save_execution_state(ctx)
|
|
186
|
-
await self.cache.add_event(
|
|
187
|
-
|
|
188
|
-
|
|
198
|
+
await self.cache.add_event(
|
|
199
|
+
ctx.execution_id,
|
|
200
|
+
"workflow_failed",
|
|
201
|
+
{
|
|
202
|
+
"error": str(e),
|
|
203
|
+
},
|
|
204
|
+
)
|
|
189
205
|
return {
|
|
190
206
|
"success": False,
|
|
191
207
|
"execution_id": ctx.execution_id,
|
|
@@ -211,8 +227,7 @@ class WorkflowExecutor:
|
|
|
211
227
|
if ctx:
|
|
212
228
|
ctx.status = WorkflowStatus.CANCELLED
|
|
213
229
|
for node_exec in ctx.node_executions.values():
|
|
214
|
-
if node_exec.status in (TaskStatus.PENDING, TaskStatus.SCHEDULED,
|
|
215
|
-
TaskStatus.RUNNING, TaskStatus.WAITING):
|
|
230
|
+
if node_exec.status in (TaskStatus.PENDING, TaskStatus.SCHEDULED, TaskStatus.RUNNING, TaskStatus.WAITING):
|
|
216
231
|
node_exec.status = TaskStatus.CANCELLED
|
|
217
232
|
await self.cache.save_execution_state(ctx)
|
|
218
233
|
logger.info("Execution cancelled", execution_id=execution_id)
|
|
@@ -223,8 +238,7 @@ class WorkflowExecutor:
|
|
|
223
238
|
# CONDUCTOR DECIDE PATTERN
|
|
224
239
|
# =========================================================================
|
|
225
240
|
|
|
226
|
-
async def _workflow_decide(self, ctx: ExecutionContext,
|
|
227
|
-
enable_caching: bool = True) -> None:
|
|
241
|
+
async def _workflow_decide(self, ctx: ExecutionContext, enable_caching: bool = True) -> None:
|
|
228
242
|
"""Core orchestration loop - Conductor's decide pattern.
|
|
229
243
|
|
|
230
244
|
Evaluates current state, finds ready nodes, executes them in parallel,
|
|
@@ -236,19 +250,15 @@ class WorkflowExecutor:
|
|
|
236
250
|
"""
|
|
237
251
|
# Distributed lock prevents concurrent decides for same execution
|
|
238
252
|
try:
|
|
239
|
-
async with self.cache.distributed_lock(
|
|
240
|
-
f"execution:{ctx.execution_id}:decide", timeout=60
|
|
241
|
-
):
|
|
253
|
+
async with self.cache.distributed_lock(f"execution:{ctx.execution_id}:decide", timeout=60):
|
|
242
254
|
await self._decide_iteration(ctx, enable_caching)
|
|
243
255
|
except TimeoutError:
|
|
244
|
-
logger.warning("Could not acquire decide lock",
|
|
245
|
-
execution_id=ctx.execution_id)
|
|
256
|
+
logger.warning("Could not acquire decide lock", execution_id=ctx.execution_id)
|
|
246
257
|
# Retry after short delay
|
|
247
258
|
await asyncio.sleep(0.5)
|
|
248
259
|
await self._workflow_decide(ctx, enable_caching)
|
|
249
260
|
|
|
250
|
-
async def _decide_iteration(self, ctx: ExecutionContext,
|
|
251
|
-
enable_caching: bool) -> None:
|
|
261
|
+
async def _decide_iteration(self, ctx: ExecutionContext, enable_caching: bool) -> None:
|
|
252
262
|
"""Continuous scheduling loop - Temporal/Conductor pattern.
|
|
253
263
|
|
|
254
264
|
When any node completes, immediately check for newly-ready dependents
|
|
@@ -270,15 +280,15 @@ class WorkflowExecutor:
|
|
|
270
280
|
else:
|
|
271
281
|
pending = ctx.get_pending_nodes()
|
|
272
282
|
if pending:
|
|
273
|
-
logger.warning("Stuck: pending nodes with unsatisfied deps",
|
|
274
|
-
execution_id=ctx.execution_id,
|
|
275
|
-
pending=pending)
|
|
283
|
+
logger.warning("Stuck: pending nodes with unsatisfied deps", execution_id=ctx.execution_id, pending=pending)
|
|
276
284
|
return
|
|
277
285
|
|
|
278
|
-
logger.info(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
286
|
+
logger.info(
|
|
287
|
+
"Starting continuous execution",
|
|
288
|
+
execution_id=ctx.execution_id,
|
|
289
|
+
initial_batch=len(ready_nodes),
|
|
290
|
+
nodes=[n.node_id for n in ready_nodes],
|
|
291
|
+
)
|
|
282
292
|
|
|
283
293
|
# Execute with continuous scheduling - new pattern
|
|
284
294
|
await self._execute_with_continuous_scheduling(ctx, ready_nodes, enable_caching)
|
|
@@ -291,10 +301,7 @@ class WorkflowExecutor:
|
|
|
291
301
|
# =========================================================================
|
|
292
302
|
|
|
293
303
|
async def _execute_with_continuous_scheduling(
|
|
294
|
-
self,
|
|
295
|
-
ctx: ExecutionContext,
|
|
296
|
-
initial_nodes: List[NodeExecution],
|
|
297
|
-
enable_caching: bool
|
|
304
|
+
self, ctx: ExecutionContext, initial_nodes: List[NodeExecution], enable_caching: bool
|
|
298
305
|
) -> None:
|
|
299
306
|
"""Execute workflow with continuous scheduling.
|
|
300
307
|
|
|
@@ -317,10 +324,7 @@ class WorkflowExecutor:
|
|
|
317
324
|
def create_node_task(node: NodeExecution) -> asyncio.Task:
|
|
318
325
|
"""Create and track a task for node execution."""
|
|
319
326
|
node.status = TaskStatus.SCHEDULED
|
|
320
|
-
task = asyncio.create_task(
|
|
321
|
-
self._execute_node_with_retry(ctx, node, enable_caching),
|
|
322
|
-
name=f"node_{node.node_id}"
|
|
323
|
-
)
|
|
327
|
+
task = asyncio.create_task(self._execute_node_with_retry(ctx, node, enable_caching), name=f"node_{node.node_id}")
|
|
324
328
|
task_to_node[task] = node
|
|
325
329
|
pending_tasks.add(task)
|
|
326
330
|
return task
|
|
@@ -340,10 +344,7 @@ class WorkflowExecutor:
|
|
|
340
344
|
break
|
|
341
345
|
|
|
342
346
|
# Wait for ANY task to complete
|
|
343
|
-
done, pending_tasks = await asyncio.wait(
|
|
344
|
-
pending_tasks,
|
|
345
|
-
return_when=asyncio.FIRST_COMPLETED
|
|
346
|
-
)
|
|
347
|
+
done, pending_tasks = await asyncio.wait(pending_tasks, return_when=asyncio.FIRST_COMPLETED)
|
|
347
348
|
|
|
348
349
|
# Process each completed task
|
|
349
350
|
for task in done:
|
|
@@ -357,11 +358,13 @@ class WorkflowExecutor:
|
|
|
357
358
|
node.status = TaskStatus.FAILED
|
|
358
359
|
node.error = str(result)
|
|
359
360
|
node.completed_at = time.time()
|
|
360
|
-
ctx.errors.append(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
361
|
+
ctx.errors.append(
|
|
362
|
+
{
|
|
363
|
+
"node_id": node.node_id,
|
|
364
|
+
"error": str(result),
|
|
365
|
+
"timestamp": time.time(),
|
|
366
|
+
}
|
|
367
|
+
)
|
|
365
368
|
await self._notify_status(node.node_id, "error", {"error": str(result)})
|
|
366
369
|
logger.error("Node failed", node_id=node.node_id, error=str(result))
|
|
367
370
|
workflow_failed = True
|
|
@@ -370,23 +373,27 @@ class WorkflowExecutor:
|
|
|
370
373
|
node.status = TaskStatus.FAILED
|
|
371
374
|
node.error = result.get("error", "Unknown error")
|
|
372
375
|
node.completed_at = time.time()
|
|
373
|
-
ctx.errors.append(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
376
|
+
ctx.errors.append(
|
|
377
|
+
{
|
|
378
|
+
"node_id": node.node_id,
|
|
379
|
+
"error": node.error,
|
|
380
|
+
"retries_exhausted": True,
|
|
381
|
+
"timestamp": time.time(),
|
|
382
|
+
}
|
|
383
|
+
)
|
|
379
384
|
workflow_failed = True
|
|
380
385
|
|
|
381
386
|
elif not result.get("success"):
|
|
382
387
|
node.status = TaskStatus.FAILED
|
|
383
388
|
node.error = result.get("error", "Unknown error")
|
|
384
389
|
node.completed_at = time.time()
|
|
385
|
-
ctx.errors.append(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
ctx.errors.append(
|
|
391
|
+
{
|
|
392
|
+
"node_id": node.node_id,
|
|
393
|
+
"error": node.error,
|
|
394
|
+
"timestamp": time.time(),
|
|
395
|
+
}
|
|
396
|
+
)
|
|
390
397
|
await self._notify_status(node.node_id, "error", {"error": node.error})
|
|
391
398
|
logger.error("Node failed", node_id=node.node_id, error=node.error)
|
|
392
399
|
workflow_failed = True
|
|
@@ -408,11 +415,13 @@ class WorkflowExecutor:
|
|
|
408
415
|
node.status = TaskStatus.FAILED
|
|
409
416
|
node.error = str(e)
|
|
410
417
|
node.completed_at = time.time()
|
|
411
|
-
ctx.errors.append(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
418
|
+
ctx.errors.append(
|
|
419
|
+
{
|
|
420
|
+
"node_id": node.node_id,
|
|
421
|
+
"error": str(e),
|
|
422
|
+
"timestamp": time.time(),
|
|
423
|
+
}
|
|
424
|
+
)
|
|
416
425
|
await self._notify_status(node.node_id, "error", {"error": str(e)})
|
|
417
426
|
logger.error("Node exception", node_id=node.node_id, error=str(e))
|
|
418
427
|
workflow_failed = True
|
|
@@ -422,27 +431,21 @@ class WorkflowExecutor:
|
|
|
422
431
|
for ready_node in newly_ready:
|
|
423
432
|
create_node_task(ready_node)
|
|
424
433
|
await self._notify_status(ready_node.node_id, "scheduled", {})
|
|
425
|
-
logger.info("Scheduled dependent node",
|
|
426
|
-
node_id=ready_node.node_id,
|
|
427
|
-
triggered_by=node.node_id)
|
|
434
|
+
logger.info("Scheduled dependent node", node_id=ready_node.node_id, triggered_by=node.node_id)
|
|
428
435
|
|
|
429
436
|
# Periodic state save
|
|
430
437
|
await self.cache.save_execution_state(ctx)
|
|
431
438
|
|
|
432
439
|
# Handle workflow failure - cancel remaining tasks
|
|
433
440
|
if workflow_failed and pending_tasks:
|
|
434
|
-
logger.info("Workflow failed, cancelling remaining tasks",
|
|
435
|
-
pending_count=len(pending_tasks))
|
|
441
|
+
logger.info("Workflow failed, cancelling remaining tasks", pending_count=len(pending_tasks))
|
|
436
442
|
|
|
437
443
|
for task in pending_tasks:
|
|
438
444
|
task.cancel()
|
|
439
445
|
|
|
440
446
|
# Wait for cancelled tasks
|
|
441
447
|
if pending_tasks:
|
|
442
|
-
cancelled_done, _ = await asyncio.wait(
|
|
443
|
-
pending_tasks,
|
|
444
|
-
return_when=asyncio.ALL_COMPLETED
|
|
445
|
-
)
|
|
448
|
+
cancelled_done, _ = await asyncio.wait(pending_tasks, return_when=asyncio.ALL_COMPLETED)
|
|
446
449
|
|
|
447
450
|
for task in cancelled_done:
|
|
448
451
|
node = task_to_node.get(task)
|
|
@@ -456,9 +459,7 @@ class WorkflowExecutor:
|
|
|
456
459
|
# PARALLEL EXECUTION (Legacy - Fork/Join with FIRST_COMPLETED pattern)
|
|
457
460
|
# =========================================================================
|
|
458
461
|
|
|
459
|
-
async def _execute_parallel_nodes(self, ctx: ExecutionContext,
|
|
460
|
-
nodes: List[NodeExecution],
|
|
461
|
-
enable_caching: bool) -> None:
|
|
462
|
+
async def _execute_parallel_nodes(self, ctx: ExecutionContext, nodes: List[NodeExecution], enable_caching: bool) -> None:
|
|
462
463
|
"""Execute multiple nodes in parallel using asyncio.wait with FIRST_COMPLETED.
|
|
463
464
|
|
|
464
465
|
Uses the standard asyncio pattern for mixed task types:
|
|
@@ -485,10 +486,7 @@ class WorkflowExecutor:
|
|
|
485
486
|
task_to_node: Dict[asyncio.Task, NodeExecution] = {}
|
|
486
487
|
|
|
487
488
|
for node in nodes:
|
|
488
|
-
task = asyncio.create_task(
|
|
489
|
-
self._execute_node_with_retry(ctx, node, enable_caching),
|
|
490
|
-
name=f"node_{node.node_id}"
|
|
491
|
-
)
|
|
489
|
+
task = asyncio.create_task(self._execute_node_with_retry(ctx, node, enable_caching), name=f"node_{node.node_id}")
|
|
492
490
|
node_to_task[node.node_id] = task
|
|
493
491
|
task_to_node[task] = node
|
|
494
492
|
|
|
@@ -498,10 +496,7 @@ class WorkflowExecutor:
|
|
|
498
496
|
# Process tasks as they complete using FIRST_COMPLETED pattern
|
|
499
497
|
while pending:
|
|
500
498
|
# Wait for any task to complete
|
|
501
|
-
done, pending = await asyncio.wait(
|
|
502
|
-
pending,
|
|
503
|
-
return_when=asyncio.FIRST_COMPLETED
|
|
504
|
-
)
|
|
499
|
+
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
|
|
505
500
|
|
|
506
501
|
# Process completed tasks
|
|
507
502
|
for task in done:
|
|
@@ -515,14 +510,15 @@ class WorkflowExecutor:
|
|
|
515
510
|
node.status = TaskStatus.FAILED
|
|
516
511
|
node.error = str(result)
|
|
517
512
|
node.completed_at = time.time()
|
|
518
|
-
ctx.errors.append(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
513
|
+
ctx.errors.append(
|
|
514
|
+
{
|
|
515
|
+
"node_id": node.node_id,
|
|
516
|
+
"error": str(result),
|
|
517
|
+
"timestamp": time.time(),
|
|
518
|
+
}
|
|
519
|
+
)
|
|
523
520
|
await self._notify_status(node.node_id, "error", {"error": str(result)})
|
|
524
|
-
logger.error("Parallel node failed",
|
|
525
|
-
node_id=node.node_id, error=str(result))
|
|
521
|
+
logger.error("Parallel node failed", node_id=node.node_id, error=str(result))
|
|
526
522
|
workflow_failed = True
|
|
527
523
|
|
|
528
524
|
elif result.get("retries_exhausted"):
|
|
@@ -530,12 +526,14 @@ class WorkflowExecutor:
|
|
|
530
526
|
node.status = TaskStatus.FAILED
|
|
531
527
|
node.error = result.get("error", "Unknown error")
|
|
532
528
|
node.completed_at = time.time()
|
|
533
|
-
ctx.errors.append(
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
529
|
+
ctx.errors.append(
|
|
530
|
+
{
|
|
531
|
+
"node_id": node.node_id,
|
|
532
|
+
"error": node.error,
|
|
533
|
+
"retries_exhausted": True,
|
|
534
|
+
"timestamp": time.time(),
|
|
535
|
+
}
|
|
536
|
+
)
|
|
539
537
|
workflow_failed = True
|
|
540
538
|
|
|
541
539
|
elif not result.get("success"):
|
|
@@ -543,14 +541,15 @@ class WorkflowExecutor:
|
|
|
543
541
|
node.status = TaskStatus.FAILED
|
|
544
542
|
node.error = result.get("error", "Unknown error")
|
|
545
543
|
node.completed_at = time.time()
|
|
546
|
-
ctx.errors.append(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
544
|
+
ctx.errors.append(
|
|
545
|
+
{
|
|
546
|
+
"node_id": node.node_id,
|
|
547
|
+
"error": node.error,
|
|
548
|
+
"timestamp": time.time(),
|
|
549
|
+
}
|
|
550
|
+
)
|
|
551
551
|
await self._notify_status(node.node_id, "error", {"error": node.error})
|
|
552
|
-
logger.error("Parallel node failed",
|
|
553
|
-
node_id=node.node_id, error=node.error)
|
|
552
|
+
logger.error("Parallel node failed", node_id=node.node_id, error=node.error)
|
|
554
553
|
workflow_failed = True
|
|
555
554
|
|
|
556
555
|
except asyncio.CancelledError:
|
|
@@ -564,31 +563,28 @@ class WorkflowExecutor:
|
|
|
564
563
|
node.status = TaskStatus.FAILED
|
|
565
564
|
node.error = str(e)
|
|
566
565
|
node.completed_at = time.time()
|
|
567
|
-
ctx.errors.append(
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
566
|
+
ctx.errors.append(
|
|
567
|
+
{
|
|
568
|
+
"node_id": node.node_id,
|
|
569
|
+
"error": str(e),
|
|
570
|
+
"timestamp": time.time(),
|
|
571
|
+
}
|
|
572
|
+
)
|
|
572
573
|
await self._notify_status(node.node_id, "error", {"error": str(e)})
|
|
573
|
-
logger.error("Parallel node exception",
|
|
574
|
-
node_id=node.node_id, error=str(e))
|
|
574
|
+
logger.error("Parallel node exception", node_id=node.node_id, error=str(e))
|
|
575
575
|
workflow_failed = True
|
|
576
576
|
|
|
577
577
|
# If workflow failed, cancel remaining pending tasks
|
|
578
578
|
# This prevents trigger nodes from blocking forever when a regular node fails
|
|
579
579
|
if workflow_failed and pending:
|
|
580
|
-
logger.info("Workflow failed, cancelling remaining tasks",
|
|
581
|
-
pending_count=len(pending))
|
|
580
|
+
logger.info("Workflow failed, cancelling remaining tasks", pending_count=len(pending))
|
|
582
581
|
|
|
583
582
|
for task in pending:
|
|
584
583
|
task.cancel()
|
|
585
584
|
|
|
586
585
|
# Wait for cancelled tasks to finish
|
|
587
586
|
if pending:
|
|
588
|
-
cancelled_done, _ = await asyncio.wait(
|
|
589
|
-
pending,
|
|
590
|
-
return_when=asyncio.ALL_COMPLETED
|
|
591
|
-
)
|
|
587
|
+
cancelled_done, _ = await asyncio.wait(pending, return_when=asyncio.ALL_COMPLETED)
|
|
592
588
|
|
|
593
589
|
# Mark cancelled nodes
|
|
594
590
|
for task in cancelled_done:
|
|
@@ -604,9 +600,7 @@ class WorkflowExecutor:
|
|
|
604
600
|
if workflow_failed:
|
|
605
601
|
ctx.status = WorkflowStatus.FAILED
|
|
606
602
|
|
|
607
|
-
async def _execute_single_node(self, ctx: ExecutionContext,
|
|
608
|
-
node: NodeExecution,
|
|
609
|
-
enable_caching: bool) -> None:
|
|
603
|
+
async def _execute_single_node(self, ctx: ExecutionContext, node: NodeExecution, enable_caching: bool) -> None:
|
|
610
604
|
"""Execute a single node with retry logic.
|
|
611
605
|
|
|
612
606
|
Args:
|
|
@@ -625,23 +619,27 @@ class WorkflowExecutor:
|
|
|
625
619
|
node.status = TaskStatus.FAILED
|
|
626
620
|
node.error = result.get("error", "Unknown error")
|
|
627
621
|
node.completed_at = time.time()
|
|
628
|
-
ctx.errors.append(
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
622
|
+
ctx.errors.append(
|
|
623
|
+
{
|
|
624
|
+
"node_id": node.node_id,
|
|
625
|
+
"error": node.error,
|
|
626
|
+
"retries_exhausted": True,
|
|
627
|
+
"timestamp": time.time(),
|
|
628
|
+
}
|
|
629
|
+
)
|
|
634
630
|
ctx.status = WorkflowStatus.FAILED
|
|
635
631
|
|
|
636
632
|
except Exception as e:
|
|
637
633
|
node.status = TaskStatus.FAILED
|
|
638
634
|
node.error = str(e)
|
|
639
635
|
node.completed_at = time.time()
|
|
640
|
-
ctx.errors.append(
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
636
|
+
ctx.errors.append(
|
|
637
|
+
{
|
|
638
|
+
"node_id": node.node_id,
|
|
639
|
+
"error": str(e),
|
|
640
|
+
"timestamp": time.time(),
|
|
641
|
+
}
|
|
642
|
+
)
|
|
645
643
|
await self._notify_status(node.node_id, "error", {"error": str(e)})
|
|
646
644
|
ctx.status = WorkflowStatus.FAILED
|
|
647
645
|
|
|
@@ -649,9 +647,7 @@ class WorkflowExecutor:
|
|
|
649
647
|
# RETRY LOGIC
|
|
650
648
|
# =========================================================================
|
|
651
649
|
|
|
652
|
-
async def _execute_node_with_retry(self, ctx: ExecutionContext,
|
|
653
|
-
node: NodeExecution,
|
|
654
|
-
enable_caching: bool) -> Dict[str, Any]:
|
|
650
|
+
async def _execute_node_with_retry(self, ctx: ExecutionContext, node: NodeExecution, enable_caching: bool) -> Dict[str, Any]:
|
|
655
651
|
"""Execute node with retry logic and DLQ on final failure.
|
|
656
652
|
|
|
657
653
|
Uses exponential backoff retry policy based on node type.
|
|
@@ -689,19 +685,25 @@ class WorkflowExecutor:
|
|
|
689
685
|
# Check if we should retry
|
|
690
686
|
if retry_policy.should_retry(error, attempt + 1):
|
|
691
687
|
delay = retry_policy.calculate_delay(attempt)
|
|
692
|
-
logger.info(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
"
|
|
704
|
-
|
|
688
|
+
logger.info(
|
|
689
|
+
"Retrying node after failure",
|
|
690
|
+
node_id=node.node_id,
|
|
691
|
+
attempt=attempt + 1,
|
|
692
|
+
max_attempts=retry_policy.max_attempts,
|
|
693
|
+
delay=delay,
|
|
694
|
+
error=error[:100],
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
await self._notify_status(
|
|
698
|
+
node.node_id,
|
|
699
|
+
"retrying",
|
|
700
|
+
{
|
|
701
|
+
"attempt": attempt + 1,
|
|
702
|
+
"max_attempts": retry_policy.max_attempts,
|
|
703
|
+
"delay": delay,
|
|
704
|
+
"error": error,
|
|
705
|
+
},
|
|
706
|
+
)
|
|
705
707
|
|
|
706
708
|
await asyncio.sleep(delay)
|
|
707
709
|
|
|
@@ -717,18 +719,12 @@ class WorkflowExecutor:
|
|
|
717
719
|
raise # Propagate cancellation
|
|
718
720
|
except Exception as e:
|
|
719
721
|
last_error = str(e)
|
|
720
|
-
logger.warning("Node execution exception",
|
|
721
|
-
node_id=node.node_id,
|
|
722
|
-
attempt=attempt + 1,
|
|
723
|
-
error=last_error)
|
|
722
|
+
logger.warning("Node execution exception", node_id=node.node_id, attempt=attempt + 1, error=last_error)
|
|
724
723
|
|
|
725
724
|
# Check if we should retry
|
|
726
725
|
if retry_policy.should_retry(last_error, attempt + 1):
|
|
727
726
|
delay = retry_policy.calculate_delay(attempt)
|
|
728
|
-
logger.info("Retrying node after exception",
|
|
729
|
-
node_id=node.node_id,
|
|
730
|
-
attempt=attempt + 1,
|
|
731
|
-
delay=delay)
|
|
727
|
+
logger.info("Retrying node after exception", node_id=node.node_id, attempt=attempt + 1, delay=delay)
|
|
732
728
|
|
|
733
729
|
await asyncio.sleep(delay)
|
|
734
730
|
node.status = TaskStatus.PENDING
|
|
@@ -752,9 +748,7 @@ class WorkflowExecutor:
|
|
|
752
748
|
# CACHED NODE EXECUTION (Prefect pattern)
|
|
753
749
|
# =========================================================================
|
|
754
750
|
|
|
755
|
-
async def _execute_node_with_caching(self, ctx: ExecutionContext,
|
|
756
|
-
node: NodeExecution,
|
|
757
|
-
enable_caching: bool) -> Dict[str, Any]:
|
|
751
|
+
async def _execute_node_with_caching(self, ctx: ExecutionContext, node: NodeExecution, enable_caching: bool) -> Dict[str, Any]:
|
|
758
752
|
"""Execute node with result caching (Prefect pattern).
|
|
759
753
|
|
|
760
754
|
Args:
|
|
@@ -771,9 +765,7 @@ class WorkflowExecutor:
|
|
|
771
765
|
|
|
772
766
|
# Check cache first (Prefect pattern)
|
|
773
767
|
if enable_caching:
|
|
774
|
-
cached_result = await self.cache.get_cached_result(
|
|
775
|
-
ctx.execution_id, node.node_id, inputs
|
|
776
|
-
)
|
|
768
|
+
cached_result = await self.cache.get_cached_result(ctx.execution_id, node.node_id, inputs)
|
|
777
769
|
if cached_result:
|
|
778
770
|
logger.info("Cache hit", node_id=node.node_id)
|
|
779
771
|
node.status = TaskStatus.CACHED
|
|
@@ -781,11 +773,14 @@ class WorkflowExecutor:
|
|
|
781
773
|
node.input_hash = hash_inputs(inputs)
|
|
782
774
|
node.completed_at = time.time()
|
|
783
775
|
ctx.outputs[node.node_id] = cached_result
|
|
784
|
-
await self._notify_status(node.node_id, "success",
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
"
|
|
788
|
-
|
|
776
|
+
await self._notify_status(node.node_id, "success", {"cached": True, **cached_result})
|
|
777
|
+
await self.cache.add_event(
|
|
778
|
+
ctx.execution_id,
|
|
779
|
+
"node_cached",
|
|
780
|
+
{
|
|
781
|
+
"node_id": node.node_id,
|
|
782
|
+
},
|
|
783
|
+
)
|
|
789
784
|
return cached_result
|
|
790
785
|
|
|
791
786
|
# Execute node
|
|
@@ -793,10 +788,14 @@ class WorkflowExecutor:
|
|
|
793
788
|
node.started_at = time.time()
|
|
794
789
|
node.input_hash = hash_inputs(inputs)
|
|
795
790
|
await self._notify_status(node.node_id, "executing", {})
|
|
796
|
-
await self.cache.add_event(
|
|
797
|
-
|
|
798
|
-
"
|
|
799
|
-
|
|
791
|
+
await self.cache.add_event(
|
|
792
|
+
ctx.execution_id,
|
|
793
|
+
"node_started",
|
|
794
|
+
{
|
|
795
|
+
"node_id": node.node_id,
|
|
796
|
+
"node_type": node.node_type,
|
|
797
|
+
},
|
|
798
|
+
)
|
|
800
799
|
|
|
801
800
|
# Update heartbeat (for crash detection)
|
|
802
801
|
await self.cache.update_heartbeat(ctx.execution_id, node.node_id)
|
|
@@ -816,12 +815,7 @@ class WorkflowExecutor:
|
|
|
816
815
|
logger.info(f"[Executor] exec_context['outputs'] keys: {list(exec_context['outputs'].keys())}")
|
|
817
816
|
|
|
818
817
|
# Call the actual node executor
|
|
819
|
-
result = await self.node_executor(
|
|
820
|
-
node.node_id,
|
|
821
|
-
node.node_type,
|
|
822
|
-
node_data.get("parameters", {}),
|
|
823
|
-
exec_context
|
|
824
|
-
)
|
|
818
|
+
result = await self.node_executor(node.node_id, node.node_type, node_data.get("parameters", {}), exec_context)
|
|
825
819
|
|
|
826
820
|
# Process result
|
|
827
821
|
if result.get("success"):
|
|
@@ -832,30 +826,38 @@ class WorkflowExecutor:
|
|
|
832
826
|
|
|
833
827
|
# Cache result (Prefect pattern)
|
|
834
828
|
if enable_caching:
|
|
835
|
-
await self.cache.set_cached_result(
|
|
836
|
-
ctx.execution_id, node.node_id, inputs, node.output
|
|
837
|
-
)
|
|
829
|
+
await self.cache.set_cached_result(ctx.execution_id, node.node_id, inputs, node.output)
|
|
838
830
|
|
|
839
831
|
await self._notify_status(node.node_id, "success", node.output)
|
|
840
|
-
await self.cache.add_event(
|
|
841
|
-
|
|
842
|
-
"
|
|
843
|
-
|
|
832
|
+
await self.cache.add_event(
|
|
833
|
+
ctx.execution_id,
|
|
834
|
+
"node_completed",
|
|
835
|
+
{
|
|
836
|
+
"node_id": node.node_id,
|
|
837
|
+
"execution_time": node.completed_at - node.started_at,
|
|
838
|
+
},
|
|
839
|
+
)
|
|
844
840
|
else:
|
|
845
841
|
node.status = TaskStatus.FAILED
|
|
846
842
|
node.error = result.get("error", "Unknown error")
|
|
847
843
|
node.completed_at = time.time()
|
|
848
|
-
ctx.errors.append(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
844
|
+
ctx.errors.append(
|
|
845
|
+
{
|
|
846
|
+
"node_id": node.node_id,
|
|
847
|
+
"error": node.error,
|
|
848
|
+
"timestamp": time.time(),
|
|
849
|
+
}
|
|
850
|
+
)
|
|
853
851
|
|
|
854
852
|
await self._notify_status(node.node_id, "error", {"error": node.error})
|
|
855
|
-
await self.cache.add_event(
|
|
856
|
-
|
|
857
|
-
"
|
|
858
|
-
|
|
853
|
+
await self.cache.add_event(
|
|
854
|
+
ctx.execution_id,
|
|
855
|
+
"node_failed",
|
|
856
|
+
{
|
|
857
|
+
"node_id": node.node_id,
|
|
858
|
+
"error": node.error,
|
|
859
|
+
},
|
|
860
|
+
)
|
|
859
861
|
|
|
860
862
|
# Mark workflow as failed
|
|
861
863
|
ctx.status = WorkflowStatus.FAILED
|
|
@@ -866,8 +868,7 @@ class WorkflowExecutor:
|
|
|
866
868
|
# DAG ANALYSIS
|
|
867
869
|
# =========================================================================
|
|
868
870
|
|
|
869
|
-
def _compute_execution_layers(self, nodes: List[Dict],
|
|
870
|
-
edges: List[Dict]) -> List[List[str]]:
|
|
871
|
+
def _compute_execution_layers(self, nodes: List[Dict], edges: List[Dict]) -> List[List[str]]:
|
|
871
872
|
"""Compute execution layers for parallel execution.
|
|
872
873
|
|
|
873
874
|
Nodes in the same layer have no dependencies on each other
|
|
@@ -891,9 +892,7 @@ class WorkflowExecutor:
|
|
|
891
892
|
from constants import CONFIG_NODE_TYPES, TOOLKIT_NODE_TYPES, AI_AGENT_TYPES
|
|
892
893
|
|
|
893
894
|
# Build node type lookup for trigger detection
|
|
894
|
-
node_types: Dict[str, str] = {
|
|
895
|
-
node["id"]: node.get("type", "unknown") for node in nodes
|
|
896
|
-
}
|
|
895
|
+
node_types: Dict[str, str] = {node["id"]: node.get("type", "unknown") for node in nodes}
|
|
897
896
|
|
|
898
897
|
# Find toolkit sub-nodes (nodes that connect TO a toolkit)
|
|
899
898
|
toolkit_node_ids = {n.get("id") for n in nodes if n.get("type") in TOOLKIT_NODE_TYPES}
|
|
@@ -914,7 +913,7 @@ class WorkflowExecutor:
|
|
|
914
913
|
# Nodes connected to AI Agent config handles are sub-nodes
|
|
915
914
|
# These handles: input-memory, input-tools, input-skill, input-teammates
|
|
916
915
|
if target in ai_agent_node_ids and source and target_handle:
|
|
917
|
-
if target_handle in (
|
|
916
|
+
if target_handle in ("input-memory", "input-tools", "input-skill", "input-teammates"):
|
|
918
917
|
subnode_ids.add(source)
|
|
919
918
|
|
|
920
919
|
# Filter out config nodes and sub-nodes from execution
|
|
@@ -949,15 +948,11 @@ class WorkflowExecutor:
|
|
|
949
948
|
|
|
950
949
|
while remaining:
|
|
951
950
|
# Find all nodes with in-degree 0 (no dependencies)
|
|
952
|
-
layer = [
|
|
953
|
-
node_id for node_id in remaining
|
|
954
|
-
if in_degree[node_id] == 0
|
|
955
|
-
]
|
|
951
|
+
layer = [node_id for node_id in remaining if in_degree[node_id] == 0]
|
|
956
952
|
|
|
957
953
|
if not layer:
|
|
958
954
|
# Cycle detected or stuck
|
|
959
|
-
logger.warning("Cycle detected or no start nodes",
|
|
960
|
-
remaining=list(remaining))
|
|
955
|
+
logger.warning("Cycle detected or no start nodes", remaining=list(remaining))
|
|
961
956
|
# Add remaining as single layer to avoid infinite loop
|
|
962
957
|
layers.append(list(remaining))
|
|
963
958
|
break
|
|
@@ -977,7 +972,7 @@ class WorkflowExecutor:
|
|
|
977
972
|
"Non-trigger node found at graph entry point",
|
|
978
973
|
node_id=node_id,
|
|
979
974
|
node_type=node_type,
|
|
980
|
-
expected_types=list(WORKFLOW_TRIGGER_TYPES)
|
|
975
|
+
expected_types=list(WORKFLOW_TRIGGER_TYPES),
|
|
981
976
|
)
|
|
982
977
|
|
|
983
978
|
# Log trigger node identification
|
|
@@ -985,10 +980,7 @@ class WorkflowExecutor:
|
|
|
985
980
|
logger.info(
|
|
986
981
|
"Identified trigger nodes as workflow starting points",
|
|
987
982
|
trigger_count=len(trigger_nodes),
|
|
988
|
-
trigger_nodes=[
|
|
989
|
-
f"{nid[:8]}({node_types.get(nid)})"
|
|
990
|
-
for nid in trigger_nodes
|
|
991
|
-
]
|
|
983
|
+
trigger_nodes=[f"{nid[:8]}({node_types.get(nid)})" for nid in trigger_nodes],
|
|
992
984
|
)
|
|
993
985
|
|
|
994
986
|
is_first_layer = False
|
|
@@ -1001,9 +993,7 @@ class WorkflowExecutor:
|
|
|
1001
993
|
for successor in adjacency[node_id]:
|
|
1002
994
|
in_degree[successor] -= 1
|
|
1003
995
|
|
|
1004
|
-
logger.debug("Computed execution layers",
|
|
1005
|
-
layer_count=len(layers),
|
|
1006
|
-
layers=[[n[:8] for n in layer] for layer in layers])
|
|
996
|
+
logger.debug("Computed execution layers", layer_count=len(layers), layers=[[n[:8] for n in layer] for layer in layers])
|
|
1007
997
|
|
|
1008
998
|
return layers
|
|
1009
999
|
|
|
@@ -1077,22 +1067,18 @@ class WorkflowExecutor:
|
|
|
1077
1067
|
# Check conditional edges for this node
|
|
1078
1068
|
if node_id in conditional_edges:
|
|
1079
1069
|
# Has conditional incoming edges - evaluate them
|
|
1080
|
-
conditions_met = self._evaluate_incoming_conditions(
|
|
1081
|
-
ctx, node_id, conditional_edges[node_id]
|
|
1082
|
-
)
|
|
1070
|
+
conditions_met = self._evaluate_incoming_conditions(ctx, node_id, conditional_edges[node_id])
|
|
1083
1071
|
if not conditions_met:
|
|
1084
1072
|
# Mark as SKIPPED if conditions not met and all deps done
|
|
1085
1073
|
node_exec.status = TaskStatus.SKIPPED
|
|
1086
|
-
logger.info("Node skipped due to unmet conditions",
|
|
1087
|
-
node_id=node_id)
|
|
1074
|
+
logger.info("Node skipped due to unmet conditions", node_id=node_id)
|
|
1088
1075
|
continue
|
|
1089
1076
|
|
|
1090
1077
|
ready.append(node_exec)
|
|
1091
1078
|
|
|
1092
1079
|
return ready
|
|
1093
1080
|
|
|
1094
|
-
def _evaluate_incoming_conditions(self, ctx: ExecutionContext, target_node_id: str,
|
|
1095
|
-
edges: List[Dict]) -> bool:
|
|
1081
|
+
def _evaluate_incoming_conditions(self, ctx: ExecutionContext, target_node_id: str, edges: List[Dict]) -> bool:
|
|
1096
1082
|
"""Evaluate conditions on incoming edges to determine if node should run.
|
|
1097
1083
|
|
|
1098
1084
|
Args:
|
|
@@ -1115,16 +1101,11 @@ class WorkflowExecutor:
|
|
|
1115
1101
|
|
|
1116
1102
|
# Evaluate condition
|
|
1117
1103
|
if evaluate_condition(condition, source_output):
|
|
1118
|
-
logger.debug("Conditional edge matched",
|
|
1119
|
-
source=source_id,
|
|
1120
|
-
target=target_node_id,
|
|
1121
|
-
condition=condition)
|
|
1104
|
+
logger.debug("Conditional edge matched", source=source_id, target=target_node_id, condition=condition)
|
|
1122
1105
|
return True
|
|
1123
1106
|
|
|
1124
1107
|
# No conditions matched
|
|
1125
|
-
logger.debug("No conditional edges matched",
|
|
1126
|
-
target=target_node_id,
|
|
1127
|
-
edge_count=len(edges))
|
|
1108
|
+
logger.debug("No conditional edges matched", target=target_node_id, edge_count=len(edges))
|
|
1128
1109
|
return False
|
|
1129
1110
|
|
|
1130
1111
|
def _get_node_data(self, ctx: ExecutionContext, node_id: str) -> Dict[str, Any]:
|
|
@@ -1167,8 +1148,7 @@ class WorkflowExecutor:
|
|
|
1167
1148
|
# STATUS NOTIFICATIONS
|
|
1168
1149
|
# =========================================================================
|
|
1169
1150
|
|
|
1170
|
-
async def _notify_status(self, node_id: str, status: str,
|
|
1171
|
-
data: Dict[str, Any]) -> None:
|
|
1151
|
+
async def _notify_status(self, node_id: str, status: str, data: Dict[str, Any]) -> None:
|
|
1172
1152
|
"""Send status notification via callback.
|
|
1173
1153
|
|
|
1174
1154
|
Args:
|
|
@@ -1186,9 +1166,7 @@ class WorkflowExecutor:
|
|
|
1186
1166
|
# RECOVERY
|
|
1187
1167
|
# =========================================================================
|
|
1188
1168
|
|
|
1189
|
-
async def recover_execution(self, execution_id: str,
|
|
1190
|
-
nodes: List[Dict],
|
|
1191
|
-
edges: List[Dict]) -> Optional[Dict[str, Any]]:
|
|
1169
|
+
async def recover_execution(self, execution_id: str, nodes: List[Dict], edges: List[Dict]) -> Optional[Dict[str, Any]]:
|
|
1192
1170
|
"""Recover and resume an interrupted execution.
|
|
1193
1171
|
|
|
1194
1172
|
Args:
|
|
@@ -1205,8 +1183,7 @@ class WorkflowExecutor:
|
|
|
1205
1183
|
return None
|
|
1206
1184
|
|
|
1207
1185
|
if ctx.status != WorkflowStatus.RUNNING:
|
|
1208
|
-
logger.info("Execution already complete", execution_id=execution_id,
|
|
1209
|
-
status=ctx.status.value)
|
|
1186
|
+
logger.info("Execution already complete", execution_id=execution_id, status=ctx.status.value)
|
|
1210
1187
|
return {
|
|
1211
1188
|
"success": ctx.status == WorkflowStatus.COMPLETED,
|
|
1212
1189
|
"execution_id": execution_id,
|
|
@@ -1214,9 +1191,7 @@ class WorkflowExecutor:
|
|
|
1214
1191
|
"recovered": False,
|
|
1215
1192
|
}
|
|
1216
1193
|
|
|
1217
|
-
logger.info("Recovering execution",
|
|
1218
|
-
execution_id=execution_id,
|
|
1219
|
-
checkpoints=ctx.checkpoints)
|
|
1194
|
+
logger.info("Recovering execution", execution_id=execution_id, checkpoints=ctx.checkpoints)
|
|
1220
1195
|
|
|
1221
1196
|
# Reset any RUNNING nodes to PENDING (they were interrupted)
|
|
1222
1197
|
for node_exec in ctx.node_executions.values():
|
|
@@ -1262,9 +1237,7 @@ class WorkflowExecutor:
|
|
|
1262
1237
|
# DLQ REPLAY
|
|
1263
1238
|
# =========================================================================
|
|
1264
1239
|
|
|
1265
|
-
async def replay_dlq_entry(self, entry_id: str,
|
|
1266
|
-
nodes: List[Dict],
|
|
1267
|
-
edges: List[Dict]) -> Dict[str, Any]:
|
|
1240
|
+
async def replay_dlq_entry(self, entry_id: str, nodes: List[Dict], edges: List[Dict]) -> Dict[str, Any]:
|
|
1268
1241
|
"""Replay a failed node from the Dead Letter Queue.
|
|
1269
1242
|
|
|
1270
1243
|
Creates a new execution context and attempts to re-execute the failed node.
|
|
@@ -1285,11 +1258,13 @@ class WorkflowExecutor:
|
|
|
1285
1258
|
"error": f"DLQ entry not found: {entry_id}",
|
|
1286
1259
|
}
|
|
1287
1260
|
|
|
1288
|
-
logger.info(
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1261
|
+
logger.info(
|
|
1262
|
+
"Replaying DLQ entry",
|
|
1263
|
+
entry_id=entry_id,
|
|
1264
|
+
node_id=entry.node_id,
|
|
1265
|
+
node_type=entry.node_type,
|
|
1266
|
+
original_execution=entry.execution_id,
|
|
1267
|
+
)
|
|
1293
1268
|
|
|
1294
1269
|
# Create new execution context for replay
|
|
1295
1270
|
ctx = ExecutionContext.create(
|
|
@@ -1321,9 +1296,7 @@ class WorkflowExecutor:
|
|
|
1321
1296
|
if node_exec.status == TaskStatus.COMPLETED:
|
|
1322
1297
|
# Success - remove from DLQ
|
|
1323
1298
|
await self.cache.remove_from_dlq(entry_id)
|
|
1324
|
-
logger.info("DLQ replay succeeded",
|
|
1325
|
-
entry_id=entry_id,
|
|
1326
|
-
node_id=entry.node_id)
|
|
1299
|
+
logger.info("DLQ replay succeeded", entry_id=entry_id, node_id=entry.node_id)
|
|
1327
1300
|
|
|
1328
1301
|
return {
|
|
1329
1302
|
"success": True,
|
|
@@ -1334,11 +1307,7 @@ class WorkflowExecutor:
|
|
|
1334
1307
|
}
|
|
1335
1308
|
else:
|
|
1336
1309
|
# Still failing - update DLQ entry
|
|
1337
|
-
await self.cache.update_dlq_entry(
|
|
1338
|
-
entry_id,
|
|
1339
|
-
entry.retry_count + 1,
|
|
1340
|
-
node_exec.error or "Unknown error"
|
|
1341
|
-
)
|
|
1310
|
+
await self.cache.update_dlq_entry(entry_id, entry.retry_count + 1, node_exec.error or "Unknown error")
|
|
1342
1311
|
|
|
1343
1312
|
return {
|
|
1344
1313
|
"success": False,
|