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,289 @@
|
|
|
1
|
+
"""Google Maps service for location operations."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import googlemaps
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
|
|
8
|
+
from core.config import Settings
|
|
9
|
+
from core.logging import get_logger, log_execution_time
|
|
10
|
+
from services.auth import AuthService
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MapsService:
|
|
16
|
+
"""Google Maps and Places API service."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, auth_service: AuthService, settings: Settings):
|
|
19
|
+
self.auth = auth_service
|
|
20
|
+
self.settings = settings
|
|
21
|
+
|
|
22
|
+
def validate_coordinates(self, lat: float, lng: float) -> bool:
|
|
23
|
+
"""Validate latitude and longitude."""
|
|
24
|
+
return -90 <= lat <= 90 and -180 <= lng <= 180
|
|
25
|
+
|
|
26
|
+
def validate_zoom_level(self, zoom: int) -> bool:
|
|
27
|
+
"""Validate Google Maps zoom level."""
|
|
28
|
+
return 0 <= zoom <= 21
|
|
29
|
+
|
|
30
|
+
async def create_map(self, node_id: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
|
|
31
|
+
"""Create Google Maps configuration."""
|
|
32
|
+
start_time = time.time()
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
api_key = parameters.get('api_key') or self.settings.google_maps_api_key
|
|
36
|
+
if not api_key:
|
|
37
|
+
raise ValueError("Google Maps API key is required")
|
|
38
|
+
|
|
39
|
+
# Extract and validate parameters (snake_case)
|
|
40
|
+
lat = float(parameters.get('lat', 40.7128))
|
|
41
|
+
lng = float(parameters.get('lng', -74.0060))
|
|
42
|
+
zoom = int(parameters.get('zoom', 13))
|
|
43
|
+
map_type = parameters.get('map_type_id', 'ROADMAP')
|
|
44
|
+
|
|
45
|
+
if not self.validate_coordinates(lat, lng):
|
|
46
|
+
raise ValueError("Invalid coordinates")
|
|
47
|
+
if not self.validate_zoom_level(zoom):
|
|
48
|
+
raise ValueError("Invalid zoom level")
|
|
49
|
+
if map_type not in ['ROADMAP', 'SATELLITE', 'HYBRID', 'TERRAIN']:
|
|
50
|
+
raise ValueError("Invalid map type")
|
|
51
|
+
|
|
52
|
+
result = {
|
|
53
|
+
"map_config": {
|
|
54
|
+
"center": {"lat": lat, "lng": lng},
|
|
55
|
+
"zoom": zoom,
|
|
56
|
+
"mapTypeId": map_type
|
|
57
|
+
},
|
|
58
|
+
"static_map_url": f"https://maps.googleapis.com/maps/api/staticmap?center={lat},{lng}&zoom={zoom}&size=600x400&maptype={map_type.lower()}&key={api_key}",
|
|
59
|
+
"status": "OK"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
log_execution_time(logger, "create_map", start_time, time.time())
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
"success": True,
|
|
66
|
+
"node_id": node_id,
|
|
67
|
+
"node_type": "createMap",
|
|
68
|
+
"operation": "map_initialization",
|
|
69
|
+
"result": result,
|
|
70
|
+
"execution_time": time.time() - start_time,
|
|
71
|
+
"timestamp": datetime.now().isoformat()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error("Create map failed", node_id=node_id, error=str(e))
|
|
76
|
+
return {
|
|
77
|
+
"success": False,
|
|
78
|
+
"node_id": node_id,
|
|
79
|
+
"node_type": "createMap",
|
|
80
|
+
"error": str(e),
|
|
81
|
+
"execution_time": time.time() - start_time,
|
|
82
|
+
"timestamp": datetime.now().isoformat()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async def geocode_location(self, node_id: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
|
|
86
|
+
"""Geocode addresses or reverse geocode coordinates."""
|
|
87
|
+
start_time = time.time()
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
api_key = parameters.get('api_key') or self.settings.google_maps_api_key
|
|
91
|
+
if not api_key:
|
|
92
|
+
raise ValueError("Google Maps API key is required")
|
|
93
|
+
|
|
94
|
+
gmaps = googlemaps.Client(key=api_key)
|
|
95
|
+
service_type = parameters.get('service_type', 'geocode')
|
|
96
|
+
|
|
97
|
+
if service_type == 'geocode':
|
|
98
|
+
address = parameters.get('address', '')
|
|
99
|
+
if not address:
|
|
100
|
+
raise ValueError("Address is required for geocoding")
|
|
101
|
+
|
|
102
|
+
geocode_result = gmaps.geocode(address=address)
|
|
103
|
+
result = {
|
|
104
|
+
"service_type": "geocoding",
|
|
105
|
+
"input": {"address": address},
|
|
106
|
+
"results": geocode_result,
|
|
107
|
+
"status": "OK" if geocode_result else "ZERO_RESULTS"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
elif service_type == 'reverse_geocode':
|
|
111
|
+
lat = float(parameters.get('lat', 0))
|
|
112
|
+
lng = float(parameters.get('lng', 0))
|
|
113
|
+
|
|
114
|
+
if not self.validate_coordinates(lat, lng):
|
|
115
|
+
raise ValueError("Invalid coordinates")
|
|
116
|
+
|
|
117
|
+
reverse_result = gmaps.reverse_geocode((lat, lng))
|
|
118
|
+
result = {
|
|
119
|
+
"service_type": "reverse_geocoding",
|
|
120
|
+
"input": {"lat": lat, "lng": lng},
|
|
121
|
+
"results": reverse_result,
|
|
122
|
+
"status": "OK" if reverse_result else "ZERO_RESULTS"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError(f"Unsupported service type: {service_type}")
|
|
127
|
+
|
|
128
|
+
log_execution_time(logger, f"geocoding_{service_type}", start_time, time.time())
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
"success": True,
|
|
132
|
+
"node_id": node_id,
|
|
133
|
+
"node_type": "addLocations",
|
|
134
|
+
"operation": service_type,
|
|
135
|
+
"result": result,
|
|
136
|
+
"execution_time": time.time() - start_time,
|
|
137
|
+
"timestamp": datetime.now().isoformat()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
except googlemaps.exceptions.ApiError as e:
|
|
141
|
+
logger.error("Google Maps API error", node_id=node_id, error=str(e))
|
|
142
|
+
return {
|
|
143
|
+
"success": False,
|
|
144
|
+
"node_id": node_id,
|
|
145
|
+
"node_type": "addLocations",
|
|
146
|
+
"error": f"Google Maps API error: {str(e)}",
|
|
147
|
+
"execution_time": time.time() - start_time,
|
|
148
|
+
"timestamp": datetime.now().isoformat()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error("Geocoding failed", node_id=node_id, error=str(e))
|
|
153
|
+
return {
|
|
154
|
+
"success": False,
|
|
155
|
+
"node_id": node_id,
|
|
156
|
+
"node_type": "addLocations",
|
|
157
|
+
"error": str(e),
|
|
158
|
+
"execution_time": time.time() - start_time,
|
|
159
|
+
"timestamp": datetime.now().isoformat()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async def find_nearby_places(self, node_id: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
|
|
163
|
+
"""Find nearby places using Google Places API."""
|
|
164
|
+
start_time = time.time()
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
api_key = parameters.get('api_key') or self.settings.google_maps_api_key
|
|
168
|
+
if not api_key:
|
|
169
|
+
raise ValueError("Google Maps API key is required")
|
|
170
|
+
|
|
171
|
+
gmaps = googlemaps.Client(key=api_key)
|
|
172
|
+
|
|
173
|
+
# Extract and validate parameters
|
|
174
|
+
lat = float(parameters.get('lat', 40.7484))
|
|
175
|
+
lng = float(parameters.get('lng', -73.9857))
|
|
176
|
+
radius = int(parameters.get('radius', 500))
|
|
177
|
+
|
|
178
|
+
if not self.validate_coordinates(lat, lng):
|
|
179
|
+
raise ValueError("Invalid coordinates")
|
|
180
|
+
if not (1 <= radius <= 50000):
|
|
181
|
+
raise ValueError("Radius must be between 1 and 50000 meters")
|
|
182
|
+
|
|
183
|
+
# Optional parameters (snake_case)
|
|
184
|
+
place_type = parameters.get('type', 'restaurant')
|
|
185
|
+
page_size = min(int(parameters.get('page_size', 20)), 20)
|
|
186
|
+
|
|
187
|
+
# Extract options (may contain keyword, name, min_price, max_price, open_now, language, rank_by)
|
|
188
|
+
options = parameters.get('options', {})
|
|
189
|
+
|
|
190
|
+
# Support both nested options and top-level parameters (snake_case)
|
|
191
|
+
keyword = options.get('keyword', '') if isinstance(options, dict) else parameters.get('keyword', '')
|
|
192
|
+
name_filter = options.get('name', '') if isinstance(options, dict) else parameters.get('name', '')
|
|
193
|
+
min_price = options.get('min_price') if isinstance(options, dict) else parameters.get('min_price')
|
|
194
|
+
max_price = options.get('max_price') if isinstance(options, dict) else parameters.get('max_price')
|
|
195
|
+
open_now = options.get('open_now', False) if isinstance(options, dict) else parameters.get('open_now', False)
|
|
196
|
+
language = options.get('language', 'en') if isinstance(options, dict) else parameters.get('language', 'en')
|
|
197
|
+
rank_by = options.get('rank_by', 'prominence') if isinstance(options, dict) else parameters.get('rank_by', 'prominence')
|
|
198
|
+
|
|
199
|
+
logger.debug("[Nearby Places] Extracted parameters",
|
|
200
|
+
keyword=keyword,
|
|
201
|
+
name_filter=name_filter,
|
|
202
|
+
min_price=min_price,
|
|
203
|
+
max_price=max_price,
|
|
204
|
+
open_now=open_now,
|
|
205
|
+
language=language,
|
|
206
|
+
rank_by=rank_by,
|
|
207
|
+
place_type=place_type)
|
|
208
|
+
|
|
209
|
+
# Build search request
|
|
210
|
+
search_params = {
|
|
211
|
+
'location': (lat, lng),
|
|
212
|
+
'type': place_type
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Add radius unless ranking by distance
|
|
216
|
+
if rank_by != 'distance':
|
|
217
|
+
search_params['radius'] = radius
|
|
218
|
+
else:
|
|
219
|
+
search_params['rank_by'] = rank_by
|
|
220
|
+
|
|
221
|
+
# Add optional parameters
|
|
222
|
+
if keyword:
|
|
223
|
+
search_params['keyword'] = keyword
|
|
224
|
+
if name_filter:
|
|
225
|
+
search_params['name'] = name_filter
|
|
226
|
+
if min_price is not None and min_price != '':
|
|
227
|
+
search_params['min_price'] = int(min_price)
|
|
228
|
+
if max_price is not None and max_price != '':
|
|
229
|
+
search_params['max_price'] = int(max_price)
|
|
230
|
+
if open_now:
|
|
231
|
+
search_params['open_now'] = True
|
|
232
|
+
if language:
|
|
233
|
+
search_params['language'] = language
|
|
234
|
+
|
|
235
|
+
nearby_result = gmaps.places_nearby(**search_params)
|
|
236
|
+
results = nearby_result.get('results', [])[:page_size]
|
|
237
|
+
|
|
238
|
+
result = {
|
|
239
|
+
"search_parameters": {
|
|
240
|
+
"location": {"lat": lat, "lng": lng},
|
|
241
|
+
"radius": radius if rank_by != 'distance' else None,
|
|
242
|
+
"type": place_type,
|
|
243
|
+
"keyword": keyword or None,
|
|
244
|
+
"name_filter": name_filter or None,
|
|
245
|
+
"min_price": min_price if min_price not in [None, ''] else None,
|
|
246
|
+
"max_price": max_price if max_price not in [None, ''] else None,
|
|
247
|
+
"open_now": open_now,
|
|
248
|
+
"language": language,
|
|
249
|
+
"rank_by": rank_by,
|
|
250
|
+
"page_size": page_size
|
|
251
|
+
},
|
|
252
|
+
"results": results,
|
|
253
|
+
"total_results": len(results),
|
|
254
|
+
"status": nearby_result.get('status', 'OK')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
log_execution_time(logger, "nearby_places", start_time, time.time())
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
"success": True,
|
|
261
|
+
"node_id": node_id,
|
|
262
|
+
"node_type": "showNearbyPlaces",
|
|
263
|
+
"operation": "nearby_search",
|
|
264
|
+
"result": result,
|
|
265
|
+
"execution_time": time.time() - start_time,
|
|
266
|
+
"timestamp": datetime.now().isoformat()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
except googlemaps.exceptions.ApiError as e:
|
|
270
|
+
logger.error("Google Places API error", node_id=node_id, error=str(e))
|
|
271
|
+
return {
|
|
272
|
+
"success": False,
|
|
273
|
+
"node_id": node_id,
|
|
274
|
+
"node_type": "showNearbyPlaces",
|
|
275
|
+
"error": f"Google Places API error: {str(e)}",
|
|
276
|
+
"execution_time": time.time() - start_time,
|
|
277
|
+
"timestamp": datetime.now().isoformat()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
logger.error("Nearby places search failed", node_id=node_id, error=str(e))
|
|
282
|
+
return {
|
|
283
|
+
"success": False,
|
|
284
|
+
"node_id": node_id,
|
|
285
|
+
"node_type": "showNearbyPlaces",
|
|
286
|
+
"error": str(e),
|
|
287
|
+
"execution_time": time.time() - start_time,
|
|
288
|
+
"timestamp": datetime.now().isoformat()
|
|
289
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Simple in-memory conversation storage for AI agents.
|
|
2
|
+
|
|
3
|
+
Uses standard Python data structures with LangChain message compatibility.
|
|
4
|
+
No deprecated APIs - follows LangChain 0.3+ recommendations.
|
|
5
|
+
"""
|
|
6
|
+
from typing import Dict, List, Optional, Callable
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Message:
|
|
16
|
+
"""Single conversation message."""
|
|
17
|
+
role: str # 'human' or 'ai'
|
|
18
|
+
content: str
|
|
19
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ConversationSession:
|
|
24
|
+
"""Conversation session with message history."""
|
|
25
|
+
session_id: str
|
|
26
|
+
messages: List[Message] = field(default_factory=list)
|
|
27
|
+
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Global in-memory store
|
|
31
|
+
_sessions: Dict[str, ConversationSession] = {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_session(session_id: str) -> ConversationSession:
|
|
35
|
+
"""Get or create a conversation session."""
|
|
36
|
+
if session_id not in _sessions:
|
|
37
|
+
_sessions[session_id] = ConversationSession(session_id=session_id)
|
|
38
|
+
logger.info(f"[Memory] Created new session: {session_id}")
|
|
39
|
+
return _sessions[session_id]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_message(session_id: str, role: str, content: str) -> None:
|
|
43
|
+
"""Add a message to a session."""
|
|
44
|
+
session = get_session(session_id)
|
|
45
|
+
session.messages.append(Message(role=role, content=content))
|
|
46
|
+
logger.info(f"[Memory] Added {role} message to session '{session_id}' (total: {len(session.messages)})")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_messages(session_id: str, window_size: Optional[int] = None) -> List[Dict]:
|
|
50
|
+
"""Get messages from a session, optionally limited to last N."""
|
|
51
|
+
session = get_session(session_id)
|
|
52
|
+
messages = session.messages
|
|
53
|
+
if window_size and window_size > 0:
|
|
54
|
+
messages = messages[-window_size:]
|
|
55
|
+
return [{"role": m.role, "content": m.content, "timestamp": m.timestamp} for m in messages]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def clear_session(session_id: str) -> int:
|
|
59
|
+
"""Clear a session's messages. Returns count of cleared messages."""
|
|
60
|
+
if session_id in _sessions:
|
|
61
|
+
count = len(_sessions[session_id].messages)
|
|
62
|
+
_sessions[session_id].messages = []
|
|
63
|
+
logger.info(f"[Memory] Cleared {count} messages from session '{session_id}'")
|
|
64
|
+
return count
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def delete_session(session_id: str) -> bool:
|
|
69
|
+
"""Delete a session entirely. Returns True if session existed."""
|
|
70
|
+
if session_id in _sessions:
|
|
71
|
+
del _sessions[session_id]
|
|
72
|
+
logger.info(f"[Memory] Deleted session: {session_id}")
|
|
73
|
+
return True
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_all_sessions() -> List[Dict]:
|
|
78
|
+
"""Get info about all sessions."""
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
"session_id": s.session_id,
|
|
82
|
+
"message_count": len(s.messages),
|
|
83
|
+
"created_at": s.created_at
|
|
84
|
+
}
|
|
85
|
+
for s in _sessions.values()
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_langchain_messages(session_id: str, window_size: Optional[int] = None):
|
|
90
|
+
"""Convert session messages to LangChain message format.
|
|
91
|
+
|
|
92
|
+
Uses langchain_core.messages (modern API, not deprecated).
|
|
93
|
+
"""
|
|
94
|
+
from langchain_core.messages import HumanMessage, AIMessage
|
|
95
|
+
|
|
96
|
+
messages = get_messages(session_id, window_size)
|
|
97
|
+
lc_messages = []
|
|
98
|
+
for m in messages:
|
|
99
|
+
if m['role'] == 'human':
|
|
100
|
+
lc_messages.append(HumanMessage(content=m['content']))
|
|
101
|
+
elif m['role'] == 'ai':
|
|
102
|
+
lc_messages.append(AIMessage(content=m['content']))
|
|
103
|
+
return lc_messages
|