machinaos 0.0.1
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 +71 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Recovery sweeper for crash recovery.
|
|
2
|
+
|
|
3
|
+
Runs as background task to:
|
|
4
|
+
- Detect abandoned executions (running but no heartbeat)
|
|
5
|
+
- Resume interrupted workflows
|
|
6
|
+
- Clean up stale data
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import time
|
|
11
|
+
from typing import Dict, Any, List, Optional, Callable, Awaitable
|
|
12
|
+
|
|
13
|
+
from core.logging import get_logger
|
|
14
|
+
from .models import TaskStatus, WorkflowStatus
|
|
15
|
+
from .cache import ExecutionCache
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RecoverySweeper:
|
|
21
|
+
"""Background task that recovers abandoned workflow executions.
|
|
22
|
+
|
|
23
|
+
Conductor's sweeper pattern:
|
|
24
|
+
- Periodically scans for stuck executions
|
|
25
|
+
- Detects nodes with stale heartbeats
|
|
26
|
+
- Resets stuck nodes to PENDING for retry
|
|
27
|
+
- Triggers workflow_decide to resume
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, cache: ExecutionCache,
|
|
31
|
+
heartbeat_timeout: int = 300, # 5 minutes
|
|
32
|
+
sweep_interval: int = 60, # 1 minute
|
|
33
|
+
max_retries: int = 3):
|
|
34
|
+
"""Initialize recovery sweeper.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
cache: ExecutionCache for Redis access
|
|
38
|
+
heartbeat_timeout: Seconds before node is considered stuck
|
|
39
|
+
sweep_interval: Seconds between sweep runs
|
|
40
|
+
max_retries: Max retry attempts per node
|
|
41
|
+
"""
|
|
42
|
+
self.cache = cache
|
|
43
|
+
self.heartbeat_timeout = heartbeat_timeout
|
|
44
|
+
self.sweep_interval = sweep_interval
|
|
45
|
+
self.max_retries = max_retries
|
|
46
|
+
self._running = False
|
|
47
|
+
self._task: Optional[asyncio.Task] = None
|
|
48
|
+
|
|
49
|
+
# Recovery callbacks (set by workflow service)
|
|
50
|
+
self._on_recovery: Optional[Callable[[str], Awaitable[None]]] = None
|
|
51
|
+
|
|
52
|
+
def set_recovery_callback(self, callback: Callable[[str], Awaitable[None]]) -> None:
|
|
53
|
+
"""Set callback to invoke when execution needs recovery.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
callback: Async function that takes execution_id
|
|
57
|
+
"""
|
|
58
|
+
self._on_recovery = callback
|
|
59
|
+
|
|
60
|
+
async def start(self) -> None:
|
|
61
|
+
"""Start the recovery sweeper background task."""
|
|
62
|
+
if self._running:
|
|
63
|
+
logger.warning("Recovery sweeper already running")
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
self._running = True
|
|
67
|
+
self._task = asyncio.create_task(self._sweep_loop())
|
|
68
|
+
logger.info("Recovery sweeper started",
|
|
69
|
+
heartbeat_timeout=self.heartbeat_timeout,
|
|
70
|
+
sweep_interval=self.sweep_interval)
|
|
71
|
+
|
|
72
|
+
async def stop(self) -> None:
|
|
73
|
+
"""Stop the recovery sweeper."""
|
|
74
|
+
self._running = False
|
|
75
|
+
if self._task:
|
|
76
|
+
self._task.cancel()
|
|
77
|
+
try:
|
|
78
|
+
await self._task
|
|
79
|
+
except asyncio.CancelledError:
|
|
80
|
+
pass
|
|
81
|
+
logger.info("Recovery sweeper stopped")
|
|
82
|
+
|
|
83
|
+
async def _sweep_loop(self) -> None:
|
|
84
|
+
"""Main sweep loop - runs continuously."""
|
|
85
|
+
while self._running:
|
|
86
|
+
try:
|
|
87
|
+
await self._sweep_once()
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error("Sweep iteration failed", error=str(e))
|
|
90
|
+
|
|
91
|
+
# Wait before next sweep
|
|
92
|
+
await asyncio.sleep(self.sweep_interval)
|
|
93
|
+
|
|
94
|
+
async def _sweep_once(self) -> None:
|
|
95
|
+
"""Single sweep iteration - check all active executions."""
|
|
96
|
+
# Get all active executions
|
|
97
|
+
active_ids = await self.cache.get_active_executions()
|
|
98
|
+
|
|
99
|
+
if not active_ids:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
logger.debug("Sweeping active executions", count=len(active_ids))
|
|
103
|
+
|
|
104
|
+
for execution_id in active_ids:
|
|
105
|
+
try:
|
|
106
|
+
await self._check_execution(execution_id)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error("Failed to check execution",
|
|
109
|
+
execution_id=execution_id, error=str(e))
|
|
110
|
+
|
|
111
|
+
async def _check_execution(self, execution_id: str) -> None:
|
|
112
|
+
"""Check single execution for stuck nodes.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
execution_id: Execution to check
|
|
116
|
+
"""
|
|
117
|
+
# Load execution state
|
|
118
|
+
ctx = await self.cache.load_execution_state(execution_id)
|
|
119
|
+
if not ctx:
|
|
120
|
+
# Execution not found - remove from active set
|
|
121
|
+
logger.warning("Orphan execution in active set",
|
|
122
|
+
execution_id=execution_id)
|
|
123
|
+
if self.cache.cache.is_redis_available():
|
|
124
|
+
await self.cache.cache.redis.srem("executions:active", execution_id)
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
# Check if already complete
|
|
128
|
+
if ctx.status in (WorkflowStatus.COMPLETED, WorkflowStatus.FAILED,
|
|
129
|
+
WorkflowStatus.CANCELLED):
|
|
130
|
+
# Should not be in active set
|
|
131
|
+
if self.cache.cache.is_redis_available():
|
|
132
|
+
await self.cache.cache.redis.srem("executions:active", execution_id)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
# Check for stuck nodes
|
|
136
|
+
needs_recovery = False
|
|
137
|
+
current_time = time.time()
|
|
138
|
+
|
|
139
|
+
for node_id, node_exec in ctx.node_executions.items():
|
|
140
|
+
if node_exec.status == TaskStatus.RUNNING:
|
|
141
|
+
# Check heartbeat
|
|
142
|
+
last_heartbeat = await self.cache.get_heartbeat(execution_id, node_id)
|
|
143
|
+
|
|
144
|
+
if last_heartbeat is None:
|
|
145
|
+
# No heartbeat - node is stuck
|
|
146
|
+
stuck_duration = current_time - (node_exec.started_at or current_time)
|
|
147
|
+
if stuck_duration > self.heartbeat_timeout:
|
|
148
|
+
logger.warning("Node stuck (no heartbeat)",
|
|
149
|
+
execution_id=execution_id,
|
|
150
|
+
node_id=node_id,
|
|
151
|
+
stuck_seconds=stuck_duration)
|
|
152
|
+
needs_recovery = True
|
|
153
|
+
|
|
154
|
+
elif current_time - last_heartbeat > self.heartbeat_timeout:
|
|
155
|
+
# Heartbeat too old
|
|
156
|
+
logger.warning("Node stuck (stale heartbeat)",
|
|
157
|
+
execution_id=execution_id,
|
|
158
|
+
node_id=node_id,
|
|
159
|
+
heartbeat_age=current_time - last_heartbeat)
|
|
160
|
+
needs_recovery = True
|
|
161
|
+
|
|
162
|
+
if needs_recovery and self._on_recovery:
|
|
163
|
+
logger.info("Triggering recovery", execution_id=execution_id)
|
|
164
|
+
try:
|
|
165
|
+
await self._on_recovery(execution_id)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error("Recovery callback failed",
|
|
168
|
+
execution_id=execution_id, error=str(e))
|
|
169
|
+
|
|
170
|
+
async def scan_on_startup(self) -> List[str]:
|
|
171
|
+
"""Scan for executions that need recovery on server startup.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
List of execution IDs that need recovery
|
|
175
|
+
"""
|
|
176
|
+
needs_recovery = []
|
|
177
|
+
|
|
178
|
+
# Get all active executions
|
|
179
|
+
active_ids = await self.cache.get_active_executions()
|
|
180
|
+
|
|
181
|
+
logger.info("Startup scan for incomplete executions",
|
|
182
|
+
active_count=len(active_ids))
|
|
183
|
+
|
|
184
|
+
for execution_id in active_ids:
|
|
185
|
+
ctx = await self.cache.load_execution_state(execution_id)
|
|
186
|
+
if not ctx:
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
# Check if execution was interrupted
|
|
190
|
+
if ctx.status == WorkflowStatus.RUNNING:
|
|
191
|
+
# Check how long it's been stuck
|
|
192
|
+
if ctx.updated_at:
|
|
193
|
+
age = time.time() - ctx.updated_at
|
|
194
|
+
if age > self.heartbeat_timeout:
|
|
195
|
+
logger.info("Found interrupted execution",
|
|
196
|
+
execution_id=execution_id,
|
|
197
|
+
age_seconds=age)
|
|
198
|
+
needs_recovery.append(execution_id)
|
|
199
|
+
|
|
200
|
+
return needs_recovery
|
|
201
|
+
|
|
202
|
+
async def cleanup_old_executions(self, max_age_hours: int = 24) -> int:
|
|
203
|
+
"""Clean up completed executions older than max_age.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
max_age_hours: Maximum age in hours
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Number of executions cleaned up
|
|
210
|
+
"""
|
|
211
|
+
cleaned = 0
|
|
212
|
+
max_age_seconds = max_age_hours * 3600
|
|
213
|
+
current_time = time.time()
|
|
214
|
+
|
|
215
|
+
# This would need a scan of all execution keys
|
|
216
|
+
# For now, we rely on Redis TTL for cleanup
|
|
217
|
+
logger.info("Cleanup skipped - using Redis TTL",
|
|
218
|
+
max_age_hours=max_age_hours)
|
|
219
|
+
|
|
220
|
+
return cleaned
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# Global sweeper instance (initialized by main.py)
|
|
224
|
+
_sweeper: Optional[RecoverySweeper] = None
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def get_recovery_sweeper() -> Optional[RecoverySweeper]:
|
|
228
|
+
"""Get global recovery sweeper instance."""
|
|
229
|
+
return _sweeper
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def set_recovery_sweeper(sweeper: RecoverySweeper) -> None:
|
|
233
|
+
"""Set global recovery sweeper instance."""
|
|
234
|
+
global _sweeper
|
|
235
|
+
_sweeper = sweeper
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Node handlers package - extracted from workflow.py for maintainability.
|
|
2
|
+
|
|
3
|
+
This package contains all node execution handlers organized by category:
|
|
4
|
+
- ai.py: AI Agent, AI Chat Model, Simple Memory
|
|
5
|
+
- android.py: Android Device Setup, Android Services
|
|
6
|
+
- code.py: Python Executor, JavaScript Executor
|
|
7
|
+
- document.py: HTTP Scraper, File Downloader, Document Parser, Text Chunker, Embedding Generator, Vector Store
|
|
8
|
+
- http.py: HTTP Request, Webhook Response
|
|
9
|
+
- tools.py: Tool execution handlers for AI Agent tool calling
|
|
10
|
+
- triggers.py: Generic trigger node handler
|
|
11
|
+
- utility.py: Maps, Text, Chat, Cron, Start
|
|
12
|
+
- whatsapp.py: WhatsApp Send, WhatsApp Connect
|
|
13
|
+
- polyglot.py: Polyglot server integration (standalone, not auto-imported)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# AI handlers
|
|
17
|
+
from .ai import (
|
|
18
|
+
handle_ai_agent,
|
|
19
|
+
handle_chat_agent,
|
|
20
|
+
handle_ai_chat_model,
|
|
21
|
+
handle_simple_memory,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Android handlers
|
|
25
|
+
from .android import (
|
|
26
|
+
handle_android_device_setup,
|
|
27
|
+
handle_android_service,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Code execution handlers
|
|
31
|
+
from .code import (
|
|
32
|
+
handle_python_executor,
|
|
33
|
+
handle_javascript_executor,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# HTTP handlers
|
|
37
|
+
from .http import (
|
|
38
|
+
handle_http_request,
|
|
39
|
+
handle_webhook_response,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Trigger handlers
|
|
43
|
+
from .triggers import (
|
|
44
|
+
handle_trigger_node,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Utility handlers
|
|
48
|
+
from .utility import (
|
|
49
|
+
handle_create_map,
|
|
50
|
+
handle_add_locations,
|
|
51
|
+
handle_nearby_places,
|
|
52
|
+
handle_text_generator,
|
|
53
|
+
handle_file_handler,
|
|
54
|
+
handle_chat_send,
|
|
55
|
+
handle_chat_history,
|
|
56
|
+
handle_start,
|
|
57
|
+
handle_cron_scheduler,
|
|
58
|
+
handle_timer,
|
|
59
|
+
handle_console,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# WhatsApp handlers
|
|
63
|
+
from .whatsapp import (
|
|
64
|
+
handle_whatsapp_send,
|
|
65
|
+
handle_whatsapp_connect,
|
|
66
|
+
handle_whatsapp_db,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Document processing handlers
|
|
70
|
+
from .document import (
|
|
71
|
+
handle_http_scraper,
|
|
72
|
+
handle_file_downloader,
|
|
73
|
+
handle_document_parser,
|
|
74
|
+
handle_text_chunker,
|
|
75
|
+
handle_embedding_generator,
|
|
76
|
+
handle_vector_store,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Tool execution handlers (for AI Agent tool calling)
|
|
80
|
+
from .tools import (
|
|
81
|
+
execute_tool,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
# AI
|
|
86
|
+
'handle_ai_agent',
|
|
87
|
+
'handle_chat_agent',
|
|
88
|
+
'handle_ai_chat_model',
|
|
89
|
+
'handle_simple_memory',
|
|
90
|
+
# Android
|
|
91
|
+
'handle_android_device_setup',
|
|
92
|
+
'handle_android_service',
|
|
93
|
+
# Code
|
|
94
|
+
'handle_python_executor',
|
|
95
|
+
'handle_javascript_executor',
|
|
96
|
+
# HTTP
|
|
97
|
+
'handle_http_request',
|
|
98
|
+
'handle_webhook_response',
|
|
99
|
+
# Triggers
|
|
100
|
+
'handle_trigger_node',
|
|
101
|
+
# Utility
|
|
102
|
+
'handle_create_map',
|
|
103
|
+
'handle_add_locations',
|
|
104
|
+
'handle_nearby_places',
|
|
105
|
+
'handle_text_generator',
|
|
106
|
+
'handle_file_handler',
|
|
107
|
+
'handle_chat_send',
|
|
108
|
+
'handle_chat_history',
|
|
109
|
+
'handle_start',
|
|
110
|
+
'handle_cron_scheduler',
|
|
111
|
+
'handle_timer',
|
|
112
|
+
'handle_console',
|
|
113
|
+
# WhatsApp
|
|
114
|
+
'handle_whatsapp_send',
|
|
115
|
+
'handle_whatsapp_connect',
|
|
116
|
+
'handle_whatsapp_db',
|
|
117
|
+
# Document processing
|
|
118
|
+
'handle_http_scraper',
|
|
119
|
+
'handle_file_downloader',
|
|
120
|
+
'handle_document_parser',
|
|
121
|
+
'handle_text_chunker',
|
|
122
|
+
'handle_embedding_generator',
|
|
123
|
+
'handle_vector_store',
|
|
124
|
+
# Tools
|
|
125
|
+
'execute_tool',
|
|
126
|
+
]
|