android-midscene-automation 0.2.4 → 1.1.0
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/CHANGELOG.md +7 -0
- package/README.md +35 -0
- package/artemis-main/.dockerignore +15 -0
- package/artemis-main/.env.example +34 -0
- package/artemis-main/CONTRIBUTING.md +35 -0
- package/artemis-main/Dockerfile +68 -0
- package/artemis-main/LICENSE +202 -0
- package/artemis-main/Makefile +115 -0
- package/artemis-main/README.md +322 -0
- package/artemis-main/README_CN.md +320 -0
- package/artemis-main/apps/__init__.py +15 -0
- package/artemis-main/apps/admin_console/__init__.py +19 -0
- package/artemis-main/apps/admin_console/core/__init__.py +13 -0
- package/artemis-main/apps/admin_console/core/config.py +43 -0
- package/artemis-main/apps/admin_console/core/security.py +178 -0
- package/artemis-main/apps/admin_console/core/state.py +243 -0
- package/artemis-main/apps/admin_console/database/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/connection.py +60 -0
- package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
- package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
- package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
- package/artemis-main/apps/admin_console/index.html +3787 -0
- package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
- package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
- package/artemis-main/apps/admin_console/routers/media.py +198 -0
- package/artemis-main/apps/admin_console/routers/replay.py +91 -0
- package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
- package/artemis-main/apps/admin_console/routers/steps.py +109 -0
- package/artemis-main/apps/admin_console/routers/stream.py +56 -0
- package/artemis-main/apps/admin_console/routers/system.py +761 -0
- package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
- package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
- package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
- package/artemis-main/apps/admin_console/server.py +508 -0
- package/artemis-main/apps/admin_console/services/__init__.py +13 -0
- package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
- package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
- package/artemis-main/apps/admin_console/services/media_service.py +623 -0
- package/artemis-main/apps/admin_console/services/model_service.py +148 -0
- package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
- package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
- package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
- package/artemis-main/apps/showcase_ui/README.md +31 -0
- package/artemis-main/apps/showcase_ui/angular.json +132 -0
- package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
- package/artemis-main/apps/showcase_ui/package.json +39 -0
- package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
- package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
- package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
- package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
- package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
- package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
- package/artemis-main/apps/showcase_ui/src/index.html +37 -0
- package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
- package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
- package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
- package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
- package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
- package/artemis-main/artemis/__init__.py +44 -0
- package/artemis-main/artemis/__main__.py +18 -0
- package/artemis-main/artemis/_legacy_imports.py +94 -0
- package/artemis-main/artemis/_version.py +30 -0
- package/artemis-main/artemis/agents/__init__.py +17 -0
- package/artemis-main/artemis/agents/checker/checker.json +9 -0
- package/artemis-main/artemis/agents/checker/checker.py +859 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
- package/artemis-main/artemis/agents/explorer/constants.py +52 -0
- package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
- package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
- package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
- package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
- package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
- package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
- package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
- package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
- package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
- package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
- package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
- package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
- package/artemis-main/artemis/agents/flash/runner.py +1167 -0
- package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
- package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
- package/artemis-main/artemis/agents/operator/operator.json +3 -0
- package/artemis-main/artemis/agents/operator/operator.py +1394 -0
- package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
- package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
- package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
- package/artemis-main/artemis/agents/planner/planner.json +32 -0
- package/artemis-main/artemis/agents/planner/planner.py +436 -0
- package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
- package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
- package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
- package/artemis-main/artemis/agents/validator/categories.py +33 -0
- package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
- package/artemis-main/artemis/agents/validator/incidents.py +120 -0
- package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
- package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
- package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
- package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
- package/artemis-main/artemis/agents/validator/validator.py +188 -0
- package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
- package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
- package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
- package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
- package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
- package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
- package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
- package/artemis-main/artemis/clients/accessibility_client.py +438 -0
- package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
- package/artemis-main/artemis/config/__init__.py +309 -0
- package/artemis-main/artemis/config/agent.py +1098 -0
- package/artemis-main/artemis/config/constants.py +151 -0
- package/artemis-main/artemis/config/core/__init__.py +24 -0
- package/artemis-main/artemis/config/core/engine.py +118 -0
- package/artemis-main/artemis/config/core/interpolator.py +78 -0
- package/artemis-main/artemis/config/llm.py +267 -0
- package/artemis-main/artemis/config/paths.py +244 -0
- package/artemis-main/artemis/config/runtime.py +197 -0
- package/artemis-main/artemis/config/settings.py +315 -0
- package/artemis-main/artemis/constants.py +47 -0
- package/artemis-main/artemis/context.py +289 -0
- package/artemis-main/artemis/controllers/__init__.py +41 -0
- package/artemis-main/artemis/controllers/controller_factory.py +41 -0
- package/artemis-main/artemis/controllers/device_controller.py +29 -0
- package/artemis-main/artemis/controllers/types.py +37 -0
- package/artemis-main/artemis/controllers/unified_controller.py +649 -0
- package/artemis-main/artemis/core/__init__.py +21 -0
- package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
- package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
- package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
- package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
- package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
- package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
- package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
- package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
- package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
- package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
- package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
- package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
- package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
- package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
- package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
- package/artemis-main/artemis/core/tool_declaration.py +43 -0
- package/artemis-main/artemis/core/tool_failure.py +56 -0
- package/artemis-main/artemis/data_engine/context_vars.py +24 -0
- package/artemis-main/artemis/data_engine/engine.py +1615 -0
- package/artemis-main/artemis/data_engine/history_reader.py +142 -0
- package/artemis-main/artemis/data_engine/models.py +131 -0
- package/artemis-main/artemis/data_engine/storage.py +1245 -0
- package/artemis-main/artemis/data_engine/trace.py +727 -0
- package/artemis-main/artemis/drivers/__init__.py +48 -0
- package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
- package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
- package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
- package/artemis-main/artemis/drivers/android/recorder.py +57 -0
- package/artemis-main/artemis/drivers/base.py +290 -0
- package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
- package/artemis-main/artemis/drivers/factory.py +80 -0
- package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
- package/artemis-main/artemis/drivers/types.py +67 -0
- package/artemis-main/artemis/graph/checkpoints.py +1243 -0
- package/artemis-main/artemis/graph/graph.py +1055 -0
- package/artemis-main/artemis/graph/perception.py +234 -0
- package/artemis-main/artemis/graph/state.py +151 -0
- package/artemis-main/artemis/graph/visibility.py +238 -0
- package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
- package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
- package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
- package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
- package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
- package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
- package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
- package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
- package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
- package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
- package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
- package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
- package/artemis-main/artemis/interfaces/cli/main.py +109 -0
- package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
- package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
- package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
- package/artemis-main/artemis/llm/__init__.py +25 -0
- package/artemis-main/artemis/llm/fake_model.py +179 -0
- package/artemis-main/artemis/llm/google/__init__.py +57 -0
- package/artemis-main/artemis/llm/google/provider.py +185 -0
- package/artemis-main/artemis/llm/google/usage.py +148 -0
- package/artemis-main/artemis/llm/parser.py +32 -0
- package/artemis-main/artemis/llm/reliability.py +267 -0
- package/artemis-main/artemis/llm/router.py +389 -0
- package/artemis-main/artemis/llm/schemas.py +36 -0
- package/artemis-main/artemis/llm/structured.py +196 -0
- package/artemis-main/artemis/main.py +58 -0
- package/artemis-main/artemis/mcp/__init__.py +20 -0
- package/artemis-main/artemis/mcp/action_executor.py +760 -0
- package/artemis-main/artemis/mcp/action_manifest.py +320 -0
- package/artemis-main/artemis/mcp/action_names.py +172 -0
- package/artemis-main/artemis/mcp/action_server.py +202 -0
- package/artemis-main/artemis/mcp/action_session.py +245 -0
- package/artemis-main/artemis/mcp/action_specs.py +868 -0
- package/artemis-main/artemis/mcp/action_types.py +86 -0
- package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
- package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
- package/artemis-main/artemis/mcp/actuators/base.py +113 -0
- package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
- package/artemis-main/artemis/mcp/adb_server.py +490 -0
- package/artemis-main/artemis/mcp/observation.py +133 -0
- package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
- package/artemis-main/artemis/memory/__init__.py +90 -0
- package/artemis-main/artemis/memory/chunking.py +1843 -0
- package/artemis-main/artemis/memory/context_policy.py +226 -0
- package/artemis-main/artemis/memory/step_capsule.md +73 -0
- package/artemis-main/artemis/memory/step_memory.py +281 -0
- package/artemis-main/artemis/memory/transcript.py +798 -0
- package/artemis-main/artemis/platform/__init__.py +61 -0
- package/artemis-main/artemis/platform/base.py +104 -0
- package/artemis-main/artemis/platform/darwin.py +184 -0
- package/artemis-main/artemis/platform/linux.py +207 -0
- package/artemis-main/artemis/platform/windows.py +193 -0
- package/artemis-main/artemis/resources/__init__.py +46 -0
- package/artemis-main/artemis/runtime/__init__.py +108 -0
- package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
- package/artemis-main/artemis/runtime/awake_lease.py +283 -0
- package/artemis-main/artemis/runtime/awake_service.py +343 -0
- package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
- package/artemis-main/artemis/runtime/daemon_client.py +422 -0
- package/artemis-main/artemis/runtime/device_lock.py +1013 -0
- package/artemis-main/artemis/runtime/device_pool.py +478 -0
- package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
- package/artemis-main/artemis/runtime/process_probe.py +98 -0
- package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
- package/artemis-main/artemis/runtime/supervisor.py +121 -0
- package/artemis-main/artemis/runtime/trace_store.py +375 -0
- package/artemis-main/artemis/sdk/__init__.py +26 -0
- package/artemis-main/artemis/sdk/agent.py +485 -0
- package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
- package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
- package/artemis-main/artemis/sdk/constants.py +15 -0
- package/artemis-main/artemis/sdk/run_outcome.py +58 -0
- package/artemis-main/artemis/sdk/types/__init__.py +61 -0
- package/artemis-main/artemis/sdk/types/agent.py +89 -0
- package/artemis-main/artemis/sdk/utils.py +28 -0
- package/artemis-main/artemis/services/llm.py +1040 -0
- package/artemis-main/artemis/services/token_meter.py +267 -0
- package/artemis-main/artemis/telemetry/__init__.py +35 -0
- package/artemis-main/artemis/telemetry/models.py +64 -0
- package/artemis-main/artemis/telemetry/replay.py +34 -0
- package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
- package/artemis-main/artemis/telemetry/tracer.py +85 -0
- package/artemis-main/artemis/toolchain/__init__.py +52 -0
- package/artemis-main/artemis/toolchain/descriptors.py +92 -0
- package/artemis-main/artemis/toolchain/resolver.py +185 -0
- package/artemis-main/artemis/tools/base.py +232 -0
- package/artemis-main/artemis/tools/command_tool.py +1240 -0
- package/artemis-main/artemis/tools/committee_prompts.json +5 -0
- package/artemis-main/artemis/tools/committee_tool.py +422 -0
- package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
- package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
- package/artemis-main/artemis/tools/explorer_tool.py +581 -0
- package/artemis-main/artemis/tools/history/__init__.py +398 -0
- package/artemis-main/artemis/tools/history/replay.py +120 -0
- package/artemis-main/artemis/tools/history/screenshot.py +139 -0
- package/artemis-main/artemis/tools/history/search.py +563 -0
- package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
- package/artemis-main/artemis/tools/index.py +47 -0
- package/artemis-main/artemis/tools/log_tool.py +108 -0
- package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
- package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
- package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
- package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
- package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
- package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
- package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
- package/artemis-main/artemis/tools/scratchpad.py +819 -0
- package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
- package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
- package/artemis-main/artemis/tools/video_tool.py +261 -0
- package/artemis-main/artemis/tools/wait_tool.py +97 -0
- package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
- package/artemis-main/artemis/utils/coordinates.py +606 -0
- package/artemis-main/artemis/utils/credentials_validator.py +201 -0
- package/artemis-main/artemis/utils/cv_canvas.py +189 -0
- package/artemis-main/artemis/utils/cython_compat.py +45 -0
- package/artemis-main/artemis/utils/element_hit_test.py +80 -0
- package/artemis-main/artemis/utils/image_diff.py +335 -0
- package/artemis-main/artemis/utils/image_hash.py +80 -0
- package/artemis-main/artemis/utils/notes.py +470 -0
- package/artemis-main/artemis/utils/ocr_api.py +105 -0
- package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
- package/artemis-main/artemis/utils/plan_grammar.py +513 -0
- package/artemis-main/artemis/utils/python_executor.py +213 -0
- package/artemis-main/artemis/utils/startup_progress.py +60 -0
- package/artemis-main/artemis/utils/task_tree.py +1174 -0
- package/artemis-main/artemis/utils/text.py +37 -0
- package/artemis-main/artemis/utils/ui_filter.py +651 -0
- package/artemis-main/artemis/utils/video.py +685 -0
- package/artemis-main/artemis/utils/visualization.py +802 -0
- package/artemis-main/config/artemis.example.jsonc +12 -0
- package/artemis-main/mcp_server/README.md +111 -0
- package/artemis-main/mcp_server/__init__.py +21 -0
- package/artemis-main/mcp_server/__main__.py +20 -0
- package/artemis-main/mcp_server/background/__init__.py +15 -0
- package/artemis-main/mcp_server/background/task_runner.py +453 -0
- package/artemis-main/mcp_server/base.py +49 -0
- package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
- package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
- package/artemis-main/mcp_server/notifiers/base.py +45 -0
- package/artemis-main/mcp_server/notifiers/composite.py +87 -0
- package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
- package/artemis-main/mcp_server/notifiers/file.py +69 -0
- package/artemis-main/mcp_server/notifiers/script.py +90 -0
- package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
- package/artemis-main/mcp_server/rules.md +68 -0
- package/artemis-main/mcp_server/server.py +106 -0
- package/artemis-main/mcp_server/tools/__init__.py +29 -0
- package/artemis-main/mcp_server/tools/device_state.py +107 -0
- package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
- package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
- package/artemis-main/mcp_server/tools/task_manager.py +627 -0
- package/artemis-main/mcp_server/tools/task_runner.py +548 -0
- package/artemis-main/mcp_server/utils/__init__.py +15 -0
- package/artemis-main/mcp_server/utils/device_utils.py +180 -0
- package/artemis-main/mcp_server/utils/env_utils.py +65 -0
- package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
- package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
- package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
- package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
- package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
- package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
- package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
- package/artemis-main/packages/artemis-client/README.md +112 -0
- package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
- package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
- package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
- package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
- package/artemis-main/pyproject.toml +259 -0
- package/artemis-main/pyright-core.json +17 -0
- package/artemis-main/scripts/check_dependency_sources.py +68 -0
- package/artemis-main/scripts/install_deps.ps1 +520 -0
- package/artemis-main/scripts/install_deps.sh +533 -0
- package/artemis-main/scripts/quality_ratchet.py +97 -0
- package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
- package/artemis-main/scripts/run_edge_case_part.py +327 -0
- package/artemis-main/scripts/run_ui.py +242 -0
- package/artemis-main/scripts/start.ps1 +500 -0
- package/artemis-main/scripts/test_all_edge_cases.py +269 -0
- package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
- package/artemis-main/setup.py +164 -0
- package/artemis-main/start.bat +35 -0
- package/artemis-main/start.sh +423 -0
- package/artemis-main/third_party/mobile_use/LICENSE +190 -0
- package/artemis-main/third_party/mobile_use/METADATA +27 -0
- package/artemis-main/third_party/mobile_use/NOTICE +12 -0
- package/artemis-main/third_party/mobile_use/__init__.py +19 -0
- package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
- package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
- package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
- package/artemis-main/third_party/mobile_use/config/output.py +184 -0
- package/artemis-main/third_party/mobile_use/context.py +119 -0
- package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
- package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
- package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
- package/artemis-main/third_party/mobile_use/graph.py +54 -0
- package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
- package/artemis-main/third_party/mobile_use/main.py +130 -0
- package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
- package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
- package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
- package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
- package/artemis-main/third_party/mobile_use/testing.py +59 -0
- package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
- package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
- package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
- package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
- package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
- package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
- package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
- package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
- package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
- package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
- package/artemis-main/uv.lock +4908 -0
- package/bin/android-midscene-automation.js +1 -5
- package/bin/artemis-config.mjs +15 -0
- package/bin/artemis-embedded.py +56 -0
- package/bin/start-artemis.mjs +30 -0
- package/bin/start-dev.mjs +112 -0
- package/bin/test_artemis_embedded.py +44 -0
- package/package.json +4 -2
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +12 -0
- package/src/App.vue +10 -5
- package/src/api.ts +5 -0
- package/src/appium-recorder/AppiumPage.vue +1 -1
- package/src/appium-recorder/components/ComponentInspector.vue +1 -1
- package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
- package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
- package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
- package/src/appium-recorder/flow-appearance.ts +4 -4
- package/src/components/AppSidebar.vue +8 -7
- package/src/components/MidsceneMaintenanceNotice.vue +17 -0
- package/src/components/analytics/AnalysisCharts.vue +4 -4
- package/src/components/analytics/ReportAnalysis.vue +1 -1
- package/src/components/generator/TestCaseFileUpload.vue +1 -1
- package/src/components/help/HelpCenter.vue +56 -37
- package/src/pages/AiGeneratorPage.vue +1 -1
- package/src/pages/ArtemisPage.vue +72 -0
- package/src/pages/ConfigPage.vue +18 -19
- package/src/style.css +340 -121
- package/src/types.ts +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from artemis.tools.command_tool import (
|
|
16
|
+
RunAdbCommandArgs,
|
|
17
|
+
RunAdbCommandTool,
|
|
18
|
+
RunAdbCommand,
|
|
19
|
+
run_adb_command,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Backward-compatibility alias
|
|
23
|
+
AdbShellArgs = RunAdbCommandArgs
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"RunAdbCommandArgs",
|
|
27
|
+
"RunAdbCommandTool",
|
|
28
|
+
"RunAdbCommand",
|
|
29
|
+
"run_adb_command",
|
|
30
|
+
"AdbShellArgs",
|
|
31
|
+
]
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
import functools
|
|
17
|
+
import inspect
|
|
18
|
+
import re
|
|
19
|
+
import time
|
|
20
|
+
from typing import Annotated, Any, Literal, get_args, get_origin
|
|
21
|
+
import uuid
|
|
22
|
+
|
|
23
|
+
from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage
|
|
24
|
+
from langchain_core.tools import BaseTool
|
|
25
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
26
|
+
from langgraph.prebuilt import InjectedState
|
|
27
|
+
|
|
28
|
+
from artemis.core.tool_failure import is_tool_failure
|
|
29
|
+
from artemis.data_engine import engine as engine_mod
|
|
30
|
+
from artemis.data_engine.trace import CURRENT_TRACE_ID, smart_serialize
|
|
31
|
+
from artemis.graph.state import State
|
|
32
|
+
from third_party.mobile_use.tools.tool_wrapper import CompositeToolWrapper, ToolWrapper
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"CompositeToolWrapper",
|
|
36
|
+
"ToolWrapper",
|
|
37
|
+
"get_tool_result_content",
|
|
38
|
+
"invoke_tool_with_injection",
|
|
39
|
+
"resolve_image_carrier",
|
|
40
|
+
"split_multimodal_result",
|
|
41
|
+
"tool_result_messages",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# pylint: disable=too-many-branches,too-many-locals,too-many-statements
|
|
46
|
+
async def invoke_tool_with_injection(
|
|
47
|
+
tool: BaseTool,
|
|
48
|
+
args: dict,
|
|
49
|
+
tool_call_id: str,
|
|
50
|
+
state: State | None = None,
|
|
51
|
+
record_trace: bool = True,
|
|
52
|
+
) -> Any:
|
|
53
|
+
"""Invokes a LangChain tool, manually injecting arguments marked with
|
|
54
|
+
|
|
55
|
+
InjectedState or InjectedToolCallId if calling the underlying function
|
|
56
|
+
directly.
|
|
57
|
+
Automatically records type='tool' trace in DataEngine.
|
|
58
|
+
"""
|
|
59
|
+
final_args = dict(args)
|
|
60
|
+
|
|
61
|
+
def _real_callable(candidate: Any) -> Callable | None:
|
|
62
|
+
"""Return a routine or partial whose signature supports direct injection."""
|
|
63
|
+
target = candidate.func if isinstance(candidate, functools.partial) else candidate
|
|
64
|
+
if inspect.isroutine(target):
|
|
65
|
+
return candidate
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
# For StructuredTool, the original function might be in tool.func or tool.coroutine
|
|
69
|
+
func = None
|
|
70
|
+
if getattr(tool, "coroutine", None):
|
|
71
|
+
func = _real_callable(tool.coroutine)
|
|
72
|
+
if func is None and getattr(tool, "func", None):
|
|
73
|
+
func = _real_callable(tool.func)
|
|
74
|
+
|
|
75
|
+
if func:
|
|
76
|
+
try:
|
|
77
|
+
sig = inspect.signature(func)
|
|
78
|
+
for param_name, param in sig.parameters.items():
|
|
79
|
+
annotation = param.annotation
|
|
80
|
+
# Handle Annotated types
|
|
81
|
+
if get_origin(annotation) is Annotated:
|
|
82
|
+
args_list = get_args(annotation)
|
|
83
|
+
if InjectedState in args_list:
|
|
84
|
+
final_args[param_name] = state
|
|
85
|
+
elif InjectedToolCallId in args_list:
|
|
86
|
+
final_args[param_name] = tool_call_id
|
|
87
|
+
except (ValueError, TypeError):
|
|
88
|
+
# If inspection fails, fall back to just using provided args
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
trace_id = uuid.uuid4()
|
|
92
|
+
parent_id = CURRENT_TRACE_ID.get()
|
|
93
|
+
token = CURRENT_TRACE_ID.set(trace_id)
|
|
94
|
+
start_time = time.time()
|
|
95
|
+
|
|
96
|
+
live_engine = getattr(engine_mod, "_CURRENT_DATA_ENGINE", None)
|
|
97
|
+
|
|
98
|
+
if record_trace and live_engine:
|
|
99
|
+
step_id = getattr(live_engine, "current_step_id", None)
|
|
100
|
+
live_engine.record_trace(
|
|
101
|
+
type="tool",
|
|
102
|
+
name=tool.name,
|
|
103
|
+
payload={"args": {k: smart_serialize(v) for k, v in args.items()}},
|
|
104
|
+
status="running",
|
|
105
|
+
parent_trace_id=parent_id,
|
|
106
|
+
step_id=step_id,
|
|
107
|
+
trace_id=trace_id,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
execution_status = "running"
|
|
111
|
+
execution_result = None
|
|
112
|
+
execution_error = None
|
|
113
|
+
try:
|
|
114
|
+
# Call the function
|
|
115
|
+
if inspect.iscoroutinefunction(func):
|
|
116
|
+
result = await func(**final_args)
|
|
117
|
+
else:
|
|
118
|
+
result = func(**final_args)
|
|
119
|
+
execution_result = result
|
|
120
|
+
if is_tool_failure(result):
|
|
121
|
+
# A structural failure is traced as one; the text is the error.
|
|
122
|
+
execution_status = "failed"
|
|
123
|
+
execution_error = result
|
|
124
|
+
else:
|
|
125
|
+
execution_status = "success"
|
|
126
|
+
return result
|
|
127
|
+
except Exception as e:
|
|
128
|
+
execution_status = "failed"
|
|
129
|
+
execution_error = e
|
|
130
|
+
raise e
|
|
131
|
+
finally:
|
|
132
|
+
duration = time.time() - start_time
|
|
133
|
+
if record_trace and live_engine:
|
|
134
|
+
step_id = getattr(live_engine, "current_step_id", None)
|
|
135
|
+
payload = {"args": {k: smart_serialize(v) for k, v in args.items()}}
|
|
136
|
+
|
|
137
|
+
if execution_status == "success":
|
|
138
|
+
payload["result"] = smart_serialize(get_tool_result_content(execution_result))
|
|
139
|
+
elif execution_status == "failed":
|
|
140
|
+
payload["error"] = str(execution_error)
|
|
141
|
+
else:
|
|
142
|
+
execution_status = "terminated"
|
|
143
|
+
payload["error"] = "Execution terminated unexpectedly."
|
|
144
|
+
|
|
145
|
+
live_engine.record_trace(
|
|
146
|
+
type="tool",
|
|
147
|
+
name=tool.name,
|
|
148
|
+
payload=payload,
|
|
149
|
+
status=execution_status,
|
|
150
|
+
duration=duration,
|
|
151
|
+
parent_trace_id=parent_id,
|
|
152
|
+
step_id=step_id,
|
|
153
|
+
trace_id=trace_id,
|
|
154
|
+
)
|
|
155
|
+
CURRENT_TRACE_ID.reset(token)
|
|
156
|
+
else:
|
|
157
|
+
# Fallback to ainvoke if no direct func available; tools that declare
|
|
158
|
+
# a "state" argument still receive the graph state.
|
|
159
|
+
tool_args = getattr(tool, "args", None)
|
|
160
|
+
if state is not None and isinstance(tool_args, dict) and "state" in tool_args:
|
|
161
|
+
final_args["state"] = state
|
|
162
|
+
return await tool.ainvoke(final_args)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_tool_result_content(result: Any) -> Any:
|
|
166
|
+
"""Extracts the content from a tool result, handling ToolMessage returns."""
|
|
167
|
+
if isinstance(result, ToolMessage):
|
|
168
|
+
return result.content
|
|
169
|
+
return result
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# --- Multimodal tool results ------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
ImageCarrier = Literal["tool", "human"]
|
|
175
|
+
|
|
176
|
+
#: Providers whose tool-result messages may carry image parts directly.
|
|
177
|
+
_TOOL_IMAGE_PROVIDERS = frozenset({"google", "anthropic"})
|
|
178
|
+
|
|
179
|
+
_STEP_RE = re.compile(r"\bstep\s+(\d+)", re.IGNORECASE)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def split_multimodal_result(result: Any) -> tuple[str, list[dict[str, Any]]]:
|
|
183
|
+
"""Splits a tool result into its text and its image content blocks.
|
|
184
|
+
|
|
185
|
+
Plain strings have no images; content-block lists contribute their text
|
|
186
|
+
blocks (joined by newlines) and their ``image_url`` / ``image`` blocks.
|
|
187
|
+
``ToolMessage`` results are unwrapped first.
|
|
188
|
+
"""
|
|
189
|
+
content = get_tool_result_content(result)
|
|
190
|
+
if content is None:
|
|
191
|
+
return "", []
|
|
192
|
+
if isinstance(content, list):
|
|
193
|
+
texts: list[str] = []
|
|
194
|
+
images: list[dict[str, Any]] = []
|
|
195
|
+
for block in content:
|
|
196
|
+
if isinstance(block, dict) and block.get("type") in ("image_url", "image"):
|
|
197
|
+
images.append(block)
|
|
198
|
+
elif isinstance(block, dict) and block.get("type") == "text":
|
|
199
|
+
texts.append(str(block.get("text") or ""))
|
|
200
|
+
else:
|
|
201
|
+
texts.append(str(block))
|
|
202
|
+
return "\n".join(t for t in texts if t), images
|
|
203
|
+
return content if isinstance(content, str) else str(content), []
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def resolve_image_carrier(llm_or_provider: Any) -> ImageCarrier:
|
|
207
|
+
"""Which message carries images returned by a tool for this LLM.
|
|
208
|
+
|
|
209
|
+
Gemini (direct API) and Anthropic accept image parts inside the tool
|
|
210
|
+
result message itself (``"tool"``). Vertex AI and the OpenAI-compatible
|
|
211
|
+
providers (openai / openrouter / xai / ...) reject them there, so the
|
|
212
|
+
image travels in a ``HumanMessage`` that immediately follows the textual
|
|
213
|
+
tool result (``"human"``). Accepts a provider name, a provider enum, or an
|
|
214
|
+
LLM whose ``endpoint.provider`` names the provider that actually issues
|
|
215
|
+
the request (a fallback model is judged by its own provider).
|
|
216
|
+
"""
|
|
217
|
+
provider: Any = llm_or_provider
|
|
218
|
+
if provider is not None and not isinstance(provider, str):
|
|
219
|
+
# An LLM (RobustChatModelWrapper) names its provider on ``endpoint``;
|
|
220
|
+
# anything without an endpoint is taken as the provider itself.
|
|
221
|
+
endpoint = getattr(provider, "endpoint", None)
|
|
222
|
+
if endpoint is not None:
|
|
223
|
+
provider = getattr(endpoint, "provider", None)
|
|
224
|
+
value = str(getattr(provider, "value", provider) or "").lower()
|
|
225
|
+
return "tool" if value in _TOOL_IMAGE_PROVIDERS else "human"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def tool_result_messages(
|
|
229
|
+
tool_call_id: str,
|
|
230
|
+
result: Any,
|
|
231
|
+
*,
|
|
232
|
+
name: str | None = None,
|
|
233
|
+
status: str = "success",
|
|
234
|
+
image_carrier: ImageCarrier | None = None,
|
|
235
|
+
llm: Any = None,
|
|
236
|
+
) -> list[BaseMessage]:
|
|
237
|
+
"""The conversation messages that deliver one tool result to the model.
|
|
238
|
+
|
|
239
|
+
Text-only results become a single ``ToolMessage``. Results carrying images
|
|
240
|
+
(e.g. ``get_step_screenshot``) are delivered according to the image
|
|
241
|
+
carrier — given explicitly or resolved from ``llm`` — so every agent loop
|
|
242
|
+
handles multimodal tool results the same way and no loop ever pastes
|
|
243
|
+
base64 into a text field. The tool call id, name and status are preserved.
|
|
244
|
+
"""
|
|
245
|
+
text, images = split_multimodal_result(result)
|
|
246
|
+
extra = {"name": name} if name else {}
|
|
247
|
+
if not images:
|
|
248
|
+
content = get_tool_result_content(result)
|
|
249
|
+
if not isinstance(content, (str, list)):
|
|
250
|
+
content = text
|
|
251
|
+
return [ToolMessage(tool_call_id=tool_call_id, content=content, status=status, **extra)]
|
|
252
|
+
|
|
253
|
+
carrier = image_carrier or resolve_image_carrier(llm)
|
|
254
|
+
if carrier == "tool":
|
|
255
|
+
blocks: list[dict[str, Any]] = [{"type": "text", "text": text}] if text else []
|
|
256
|
+
return [
|
|
257
|
+
ToolMessage(
|
|
258
|
+
tool_call_id=tool_call_id, content=[*blocks, *images], status=status, **extra
|
|
259
|
+
)
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
match = _STEP_RE.search(text)
|
|
263
|
+
caption = f"[Screenshot returned by {name or 'the tool'}"
|
|
264
|
+
if match:
|
|
265
|
+
caption += f" for step {match.group(1)}"
|
|
266
|
+
caption += "]"
|
|
267
|
+
return [
|
|
268
|
+
ToolMessage(tool_call_id=tool_call_id, content=text, status=status, **extra),
|
|
269
|
+
HumanMessage(content=[{"type": "text", "text": caption}, *images]),
|
|
270
|
+
]
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Video Analyzer utility for analyzing video content using Gemini models.
|
|
16
|
+
|
|
17
|
+
This utility sends video files to video-capable Gemini models for analysis
|
|
18
|
+
and returns text descriptions based on the provided prompt.
|
|
19
|
+
It also exposes a tool for agents to perform structured video analysis.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import Any, Literal
|
|
23
|
+
|
|
24
|
+
from langchain_core.messages import ToolMessage
|
|
25
|
+
from langchain_core.tools import BaseTool
|
|
26
|
+
from pydantic import BaseModel, Field
|
|
27
|
+
|
|
28
|
+
from artemis.core.tool_failure import ToolFailure
|
|
29
|
+
from artemis.agents.video_analyzer.video_analyzer import VideoAnalyzer
|
|
30
|
+
from artemis.context import ArtemisContext
|
|
31
|
+
from artemis.core.tool_declaration import ToolDeclaration
|
|
32
|
+
from artemis.data_engine.trace import trace_langchain_tool
|
|
33
|
+
from artemis.drivers.base import BaseDeviceDriver
|
|
34
|
+
from artemis.graph.state import State
|
|
35
|
+
from artemis.tools.base import ArtemisTool, ToolCategory
|
|
36
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
37
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
38
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
39
|
+
|
|
40
|
+
logger = get_logger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class VideoAnalyzerArgs(BaseModel):
|
|
44
|
+
"""Arguments schema for video analyzer tool."""
|
|
45
|
+
|
|
46
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
47
|
+
time_description: str = Field(
|
|
48
|
+
...,
|
|
49
|
+
description=(
|
|
50
|
+
"The specific time range to analyze, relative to the start of the"
|
|
51
|
+
" recording (e.g., 'from 5s to 12s', 'from 20s to the end')."
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
purpose: str = Field(
|
|
55
|
+
...,
|
|
56
|
+
description=(
|
|
57
|
+
"The specific query, intent, or question you want the video"
|
|
58
|
+
" analyzer to answer about the visual or audio content in this"
|
|
59
|
+
" segment."
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
OPERATOR_VIDEO_ANALYZER_DOCSTRING = (
|
|
65
|
+
"[VIDEO] Use this tool to analyze any specific time range of the video"
|
|
66
|
+
" recording. This tool delegates to a video analysis subagent"
|
|
67
|
+
" with full access to the continuous screen recording of your entire"
|
|
68
|
+
" execution session.\n\n"
|
|
69
|
+
"- This tool overcomes your limitation of only receiving discrete"
|
|
70
|
+
" screenshots. Common use cases include (but are not limited to):\n"
|
|
71
|
+
" * Watching media files: Play the video on the device screen, use"
|
|
72
|
+
" `wait_for_delay` until the video finishes playing, and then call this"
|
|
73
|
+
" tool to analyze the recording.\n"
|
|
74
|
+
" * Bridging the gap: Inspecting what happened between your last action"
|
|
75
|
+
" and the current screenshot.\n"
|
|
76
|
+
" * Memory retrieval: Recalling fast-moving or transient information from"
|
|
77
|
+
" a previous step.\n"
|
|
78
|
+
" * Catching rapid changes: Investigating on-screen information that"
|
|
79
|
+
" changes too quickly for static screenshots to capture.\n"
|
|
80
|
+
" - Please specify a narrow time range when possible to ensure faster"
|
|
81
|
+
" processing.\n"
|
|
82
|
+
" - If only audio analysis is needed, state it clearly in your purpose.\n"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
#: Flash-profile declaration of the same tool (FlashRunner binds JSON-schema
|
|
86
|
+
#: declarations rather than LangChain tools). Description and parameters are
|
|
87
|
+
#: derived from the operator contract above so the two never drift.
|
|
88
|
+
VIDEO_ANALYZER_TOOL = ToolDeclaration(
|
|
89
|
+
name="video_analyzer",
|
|
90
|
+
description=OPERATOR_VIDEO_ANALYZER_DOCSTRING,
|
|
91
|
+
parameters={
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
field_name: {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": field_info.description or "",
|
|
97
|
+
}
|
|
98
|
+
for field_name, field_info in VideoAnalyzerArgs.model_fields.items()
|
|
99
|
+
},
|
|
100
|
+
"required": list(VideoAnalyzerArgs.model_fields),
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
DIAGNOSER_VIDEO_ANALYZER_DOCSTRING = (
|
|
105
|
+
"[DIAGNOSTIC] Analyzes a video segment for failure diagnosis and"
|
|
106
|
+
" forensics.\n\n"
|
|
107
|
+
"Use this to verify if an action succeeded, check for error popups, or"
|
|
108
|
+
" investigate visual anomalies when a step fails.\n"
|
|
109
|
+
"Provide a narrow time range (e.g., 'from 10s to 15s') to ensure speed.\n"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class VideoAnalyzerTool(ArtemisTool):
|
|
114
|
+
"""Universal tool for analyzing video content using Gemini models."""
|
|
115
|
+
|
|
116
|
+
def __init__(
|
|
117
|
+
self,
|
|
118
|
+
role: Literal["operator", "diagnoser"] = "operator",
|
|
119
|
+
description: str | None = None,
|
|
120
|
+
category: ToolCategory = "perception",
|
|
121
|
+
):
|
|
122
|
+
chosen_description = description or (
|
|
123
|
+
OPERATOR_VIDEO_ANALYZER_DOCSTRING
|
|
124
|
+
if role == "operator"
|
|
125
|
+
else DIAGNOSER_VIDEO_ANALYZER_DOCSTRING
|
|
126
|
+
)
|
|
127
|
+
super().__init__(
|
|
128
|
+
name="video_analyzer",
|
|
129
|
+
description=chosen_description,
|
|
130
|
+
args_schema=VideoAnalyzerArgs,
|
|
131
|
+
category=category,
|
|
132
|
+
)
|
|
133
|
+
self.role = role
|
|
134
|
+
|
|
135
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
|
|
136
|
+
async def execute(
|
|
137
|
+
self,
|
|
138
|
+
driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
|
|
139
|
+
ctx: ArtemisContext | None = None,
|
|
140
|
+
time_description: str | None = None,
|
|
141
|
+
purpose: str | None = None,
|
|
142
|
+
tool_call_id: str | None = None,
|
|
143
|
+
state: State | None = None,
|
|
144
|
+
**kwargs: Any,
|
|
145
|
+
) -> Any:
|
|
146
|
+
time_desc = (
|
|
147
|
+
time_description
|
|
148
|
+
if time_description is not None
|
|
149
|
+
else (kwargs.get("time_description") or kwargs.get("TimeDescription") or "")
|
|
150
|
+
)
|
|
151
|
+
purp = (
|
|
152
|
+
purpose
|
|
153
|
+
if purpose is not None
|
|
154
|
+
else (kwargs.get("purpose") or kwargs.get("Purpose") or "")
|
|
155
|
+
)
|
|
156
|
+
tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
|
|
157
|
+
st = state if state is not None else kwargs.get("state")
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
logger.info(f"video_analyzer called for range: {time_desc}")
|
|
161
|
+
if ctx is None:
|
|
162
|
+
raise ValueError("ArtemisContext is required for VideoAnalyzer.")
|
|
163
|
+
agent = VideoAnalyzer(ctx)
|
|
164
|
+
agent_outcome, status = await agent.run(time_desc, purp)
|
|
165
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
166
|
+
logger.error(f"Error running video analyzer: {e}")
|
|
167
|
+
agent_outcome = f"Error running video analyzer: {e}"
|
|
168
|
+
status = "error"
|
|
169
|
+
|
|
170
|
+
if st is not None:
|
|
171
|
+
return ToolMessage(
|
|
172
|
+
tool_call_id=tcid or "",
|
|
173
|
+
content=agent_outcome,
|
|
174
|
+
status=status,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if status == "failed":
|
|
178
|
+
return f"Video analysis failed: {agent_outcome}"
|
|
179
|
+
return agent_outcome
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Universal tool instance & aliases
|
|
183
|
+
video_analyzer = VideoAnalyzerTool()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class VideoAnalyzerPureTool(ArtemisTool):
|
|
187
|
+
"""Universal pure tool for video analysis returning text results directly."""
|
|
188
|
+
|
|
189
|
+
def __init__(self):
|
|
190
|
+
super().__init__(
|
|
191
|
+
name="video_analyzer_pure",
|
|
192
|
+
description=OPERATOR_VIDEO_ANALYZER_DOCSTRING,
|
|
193
|
+
args_schema=VideoAnalyzerArgs,
|
|
194
|
+
category="perception",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
198
|
+
async def execute(
|
|
199
|
+
self,
|
|
200
|
+
driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
|
|
201
|
+
ctx: ArtemisContext | None = None,
|
|
202
|
+
time_description: str | None = None,
|
|
203
|
+
purpose: str | None = None,
|
|
204
|
+
**kwargs: Any,
|
|
205
|
+
) -> str:
|
|
206
|
+
time_desc = (
|
|
207
|
+
time_description
|
|
208
|
+
if time_description is not None
|
|
209
|
+
else (kwargs.get("time_description") or kwargs.get("TimeDescription") or "")
|
|
210
|
+
)
|
|
211
|
+
purp = (
|
|
212
|
+
purpose
|
|
213
|
+
if purpose is not None
|
|
214
|
+
else (kwargs.get("purpose") or kwargs.get("Purpose") or "")
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
logger.info(f"video_analyzer (pure) called for range: {time_desc}")
|
|
219
|
+
if ctx is None:
|
|
220
|
+
raise ValueError("ArtemisContext is required for VideoAnalyzer.")
|
|
221
|
+
agent = VideoAnalyzer(ctx)
|
|
222
|
+
agent_outcome, status = await agent.run(time_desc, purp)
|
|
223
|
+
if status == "failed":
|
|
224
|
+
return ToolFailure(f"Video analysis failed: {agent_outcome}")
|
|
225
|
+
return agent_outcome
|
|
226
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
227
|
+
logger.error(f"Error in pure video_analyzer tool: {e}")
|
|
228
|
+
return ToolFailure(f"Error running video analyzer: {e}")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Universal pure tool instance & aliases
|
|
232
|
+
video_analyzer_pure = VideoAnalyzerPureTool()
|
|
233
|
+
VideoAnalyzerPure = VideoAnalyzerPureTool
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_video_analyzer_tool(
|
|
237
|
+
ctx: ArtemisContext, role: Literal["operator", "diagnoser"] = "operator"
|
|
238
|
+
) -> BaseTool:
|
|
239
|
+
"""Creates a tool that allows agents to perform structured video analysis.
|
|
240
|
+
|
|
241
|
+
The tool acts as a Main Agent following the Dynamic Video Analyzer plan.
|
|
242
|
+
"""
|
|
243
|
+
tool_inst = VideoAnalyzerTool(role=role)
|
|
244
|
+
return trace_langchain_tool(tool_inst.to_langchain_tool(ctx), ctx)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def get_video_analyzer_tool_pure(ctx: ArtemisContext) -> BaseTool:
|
|
248
|
+
"""Creates a pure LangChain tool for video analysis that returns the text result directly.
|
|
249
|
+
|
|
250
|
+
Suitable for agents running outside of LangGraph (like the outputter).
|
|
251
|
+
"""
|
|
252
|
+
return trace_langchain_tool(
|
|
253
|
+
video_analyzer_pure.to_langchain_tool(ctx, name="video_analyzer"), ctx
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
video_analyzer_wrapper = ToolWrapper(
|
|
258
|
+
tool_fn_getter=get_video_analyzer_tool,
|
|
259
|
+
on_success_fn=lambda output: f"Video Analyzer replied:\n{output}",
|
|
260
|
+
on_failure_fn=lambda error: f"Video Analyzer failed: {error}",
|
|
261
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from langchain_core.tools import BaseTool
|
|
19
|
+
from pydantic import BaseModel, Field
|
|
20
|
+
|
|
21
|
+
from artemis.context import ArtemisContext
|
|
22
|
+
from artemis.data_engine.trace import trace_langchain_tool
|
|
23
|
+
from artemis.drivers.base import BaseDeviceDriver
|
|
24
|
+
from artemis.tools.base import ArtemisTool, ToolCategory
|
|
25
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
26
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
27
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
28
|
+
|
|
29
|
+
logger = get_logger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class WaitArgs(BaseModel):
|
|
33
|
+
"""Arguments schema for wait tool."""
|
|
34
|
+
|
|
35
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
36
|
+
seconds: int = Field(
|
|
37
|
+
...,
|
|
38
|
+
description=("The duration to wait/sleep in seconds. Minimum is 1, maximum is 60."),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
WAIT_DOCSTRING = (
|
|
43
|
+
"[SHELL] Pauses execution for a specified duration in seconds.\n\n"
|
|
44
|
+
"Use this to wait for active background operations to progress."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class WaitTool(ArtemisTool):
|
|
49
|
+
"""Universal tool for pausing execution for a specified duration in seconds."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, category: ToolCategory = "system"):
|
|
52
|
+
super().__init__(
|
|
53
|
+
name="wait",
|
|
54
|
+
description=WAIT_DOCSTRING,
|
|
55
|
+
args_schema=WaitArgs,
|
|
56
|
+
category=category,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
60
|
+
async def execute(
|
|
61
|
+
self,
|
|
62
|
+
driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
|
|
63
|
+
ctx: ArtemisContext | None = None, # pylint: disable=unused-argument
|
|
64
|
+
seconds: int | None = None,
|
|
65
|
+
**kwargs: Any,
|
|
66
|
+
) -> str:
|
|
67
|
+
sec = (
|
|
68
|
+
seconds
|
|
69
|
+
if seconds is not None
|
|
70
|
+
else (kwargs.get("seconds") or kwargs.get("Seconds") or 1)
|
|
71
|
+
)
|
|
72
|
+
if sec < 1:
|
|
73
|
+
sec = 1
|
|
74
|
+
elif sec > 60:
|
|
75
|
+
sec = 60
|
|
76
|
+
|
|
77
|
+
logger.info(f"Diagnoser waiting for {sec} seconds...")
|
|
78
|
+
await asyncio.sleep(sec)
|
|
79
|
+
return f"Waited {sec} seconds."
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Universal tool instance & aliases
|
|
83
|
+
wait = WaitTool()
|
|
84
|
+
Wait = WaitTool
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_wait_tool(ctx: ArtemisContext) -> BaseTool:
|
|
88
|
+
"""Exports wait as a LangChain BaseTool."""
|
|
89
|
+
return trace_langchain_tool(wait.to_langchain_tool(ctx), ctx)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
wait_wrapper = ToolWrapper(
|
|
93
|
+
tool_fn_getter=get_wait_tool,
|
|
94
|
+
on_success_fn=lambda seconds: f"Waited {seconds}s",
|
|
95
|
+
on_failure_fn=lambda err: f"Wait failed: {err}",
|
|
96
|
+
)
|
|
97
|
+
export_wait_tool = get_wait_tool
|