android-midscene-automation 0.2.3 → 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 +11 -1
- package/README.md +63 -1
- 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 +10 -8
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +13 -1
- package/src/App.vue +72 -23
- 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 +369 -186
- package/src/types.ts +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
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 langchain_core.tools import BaseTool
|
|
16
|
+
|
|
17
|
+
from artemis.context import ArtemisContext
|
|
18
|
+
from artemis.data_engine.trace import trace_langchain_tool
|
|
19
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
20
|
+
from third_party.mobile_use.tools.index import (
|
|
21
|
+
get_tools_from_wrappers as build_tools_from_wrappers,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_tools_from_wrappers(
|
|
26
|
+
ctx: "ArtemisContext",
|
|
27
|
+
wrappers: list[ToolWrapper],
|
|
28
|
+
) -> list[BaseTool]:
|
|
29
|
+
"""Instantiate the available tools of ``wrappers``, traced into the data engine."""
|
|
30
|
+
return build_tools_from_wrappers(
|
|
31
|
+
ctx, wrappers, wrap_tool=lambda t: trace_langchain_tool(t, ctx)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_tool_by_name(name: str, tools: list[BaseTool]) -> BaseTool | None:
|
|
36
|
+
"""Get a tool by name, stripping any prefixes like 'default_api:'."""
|
|
37
|
+
normalized_name = name
|
|
38
|
+
if ":" in normalized_name:
|
|
39
|
+
normalized_name = normalized_name.split(":")[-1]
|
|
40
|
+
|
|
41
|
+
for t in tools:
|
|
42
|
+
t_name = t.name
|
|
43
|
+
if ":" in t_name:
|
|
44
|
+
t_name = t_name.split(":")[-1]
|
|
45
|
+
if t_name == normalized_name:
|
|
46
|
+
return t
|
|
47
|
+
return None
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
"""Universal Log Analyzer tool for inspecting and analyzing Android system logs."""
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from langchain_core.tools import BaseTool
|
|
20
|
+
from pydantic import BaseModel, Field
|
|
21
|
+
|
|
22
|
+
from artemis.core.tool_failure import ToolFailure
|
|
23
|
+
from artemis.agents.log_analyzer.log_analyzer import LogAnalyzerNode
|
|
24
|
+
from artemis.context import ArtemisContext
|
|
25
|
+
from artemis.data_engine.trace import trace_langchain_tool
|
|
26
|
+
from artemis.drivers.base import BaseDeviceDriver
|
|
27
|
+
from artemis.graph.state import State
|
|
28
|
+
from artemis.tools.base import ArtemisTool
|
|
29
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
30
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
31
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
32
|
+
|
|
33
|
+
logger = get_logger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# pylint: disable=too-few-public-methods
|
|
37
|
+
class AnalyzeLogsArgs(BaseModel):
|
|
38
|
+
"""Arguments schema for invoking the Log Analyzer subagent."""
|
|
39
|
+
|
|
40
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
41
|
+
specific_query: str = Field(
|
|
42
|
+
...,
|
|
43
|
+
description=(
|
|
44
|
+
"Instructions for the log analyzer, specifying what to search for or analyze."
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AnalyzeLogsTool(ArtemisTool):
|
|
50
|
+
"""Universal tool for analyzing Android logs using a dedicated hybrid log analyzer agent."""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
super().__init__(
|
|
54
|
+
name="analyze_logs",
|
|
55
|
+
description=(
|
|
56
|
+
"[DIAGNOSTIC] Analyzes Android logs using a dedicated hybrid log analyzer agent. "
|
|
57
|
+
"Use this when you need to investigate failures, check for specific error "
|
|
58
|
+
"logs, or understand system events."
|
|
59
|
+
),
|
|
60
|
+
args_schema=AnalyzeLogsArgs,
|
|
61
|
+
category="custom",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
65
|
+
async def execute(
|
|
66
|
+
self,
|
|
67
|
+
driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
|
|
68
|
+
ctx: ArtemisContext | None = None,
|
|
69
|
+
specific_query: str | None = None,
|
|
70
|
+
state: State | None = None,
|
|
71
|
+
**kwargs: Any,
|
|
72
|
+
) -> Any:
|
|
73
|
+
query = (
|
|
74
|
+
specific_query
|
|
75
|
+
if specific_query is not None
|
|
76
|
+
else (
|
|
77
|
+
kwargs.get("specific_query")
|
|
78
|
+
or kwargs.get("SpecificQuery")
|
|
79
|
+
or kwargs.get("query")
|
|
80
|
+
or kwargs.get("Query")
|
|
81
|
+
or ""
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
st = state if state is not None else kwargs.get("state")
|
|
85
|
+
logger.info(f"analyze_logs tool called with query: '{query}'")
|
|
86
|
+
if ctx is None:
|
|
87
|
+
return ToolFailure("Error: ArtemisContext is required for LogAnalyzer.")
|
|
88
|
+
analyst = LogAnalyzerNode(ctx)
|
|
89
|
+
result = await analyst.run(query, st)
|
|
90
|
+
return result
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Universal tool instance & aliases
|
|
94
|
+
analyze_logs = AnalyzeLogsTool()
|
|
95
|
+
AnalyzeLogs = AnalyzeLogsTool
|
|
96
|
+
LogTool = AnalyzeLogsTool
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_analyze_logs_tool(ctx: ArtemisContext) -> BaseTool:
|
|
100
|
+
"""Exports analyze_logs as a LangChain BaseTool."""
|
|
101
|
+
return trace_langchain_tool(analyze_logs.to_langchain_tool(ctx), ctx)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
analyze_logs_wrapper = ToolWrapper(
|
|
105
|
+
tool_fn_getter=get_analyze_logs_tool,
|
|
106
|
+
on_success_fn=lambda output: f"Log Analyzer replied:\n{output}",
|
|
107
|
+
on_failure_fn=lambda error: f"Log Analyzer failed: {error}",
|
|
108
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
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 typing import Any
|
|
16
|
+
|
|
17
|
+
from langchain_core.messages import ToolMessage
|
|
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.graph.state import State
|
|
25
|
+
from artemis.tools.base import ArtemisTool, ToolCategory
|
|
26
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
27
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
28
|
+
from third_party.mobile_use.tools.mobile.launch_app import LAUNCH_APP_DOCSTRING, find_package
|
|
29
|
+
from third_party.mobile_use.utils.app_launch_utils import launch_app_with_retries
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LaunchAppArgs(BaseModel):
|
|
33
|
+
"""Arguments schema for launching an application."""
|
|
34
|
+
|
|
35
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
36
|
+
app_name: str = Field(
|
|
37
|
+
...,
|
|
38
|
+
description="The natural language name of the application to launch.",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LaunchAppTool(ArtemisTool):
|
|
43
|
+
"""Universal tool for finding and launching an application on the device."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, category: ToolCategory = "action"):
|
|
46
|
+
super().__init__(
|
|
47
|
+
name="launch_app",
|
|
48
|
+
description=LAUNCH_APP_DOCSTRING,
|
|
49
|
+
args_schema=LaunchAppArgs,
|
|
50
|
+
category=category,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
|
|
54
|
+
async def execute(
|
|
55
|
+
self,
|
|
56
|
+
driver: BaseDeviceDriver | None = None,
|
|
57
|
+
ctx: ArtemisContext | None = None,
|
|
58
|
+
app_name: str | None = None,
|
|
59
|
+
tool_call_id: str | None = None,
|
|
60
|
+
state: State | None = None,
|
|
61
|
+
**kwargs: Any,
|
|
62
|
+
) -> Any:
|
|
63
|
+
app = (
|
|
64
|
+
app_name
|
|
65
|
+
if app_name is not None
|
|
66
|
+
else (kwargs.get("app_name") or kwargs.get("AppName") or "")
|
|
67
|
+
)
|
|
68
|
+
tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
|
|
69
|
+
st = state if state is not None else kwargs.get("state")
|
|
70
|
+
|
|
71
|
+
success = False
|
|
72
|
+
error_msg = None
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
if not app:
|
|
76
|
+
raise ValueError("app_name parameter is required.")
|
|
77
|
+
|
|
78
|
+
if ctx is not None:
|
|
79
|
+
package_name = await find_package(ctx=ctx, app_name=app)
|
|
80
|
+
if not package_name:
|
|
81
|
+
success = False
|
|
82
|
+
outcome = f"Failed to launch app '{app}': Package not found."
|
|
83
|
+
error_msg = "Package not found."
|
|
84
|
+
else:
|
|
85
|
+
success, error_msg = await launch_app_with_retries(
|
|
86
|
+
ctx=ctx, app_package=package_name
|
|
87
|
+
)
|
|
88
|
+
outcome = (
|
|
89
|
+
f"Launched app '{app}' ({package_name}); foreground confirmed."
|
|
90
|
+
if success
|
|
91
|
+
else f"Failed to launch app '{app}': {error_msg}"
|
|
92
|
+
)
|
|
93
|
+
elif driver is not None and hasattr(driver, "launch_app"):
|
|
94
|
+
success = await driver.launch_app(app)
|
|
95
|
+
outcome = f"Launched app '{app}'." if success else f"Failed to launch app '{app}'."
|
|
96
|
+
error_msg = None if success else "Launch failed."
|
|
97
|
+
else:
|
|
98
|
+
success = False
|
|
99
|
+
outcome = "Error during launch app: No driver or context provided."
|
|
100
|
+
error_msg = "No driver or context provided."
|
|
101
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
102
|
+
success = False
|
|
103
|
+
outcome = f"Error during launch app: {e}"
|
|
104
|
+
error_msg = str(e)
|
|
105
|
+
|
|
106
|
+
if st is not None:
|
|
107
|
+
additional_kwargs = {} if success else ({"error": error_msg} if error_msg else {})
|
|
108
|
+
return ToolMessage(
|
|
109
|
+
tool_call_id=tcid or "",
|
|
110
|
+
content=outcome,
|
|
111
|
+
additional_kwargs=additional_kwargs,
|
|
112
|
+
status="success" if success else "error",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return outcome
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Universal tool instance & aliases
|
|
119
|
+
launch_app = LaunchAppTool()
|
|
120
|
+
LaunchApp = LaunchAppTool
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_launch_app_tool(ctx: ArtemisContext) -> BaseTool:
|
|
124
|
+
"""Exports launch_app as a LangChain BaseTool."""
|
|
125
|
+
return trace_langchain_tool(launch_app.to_langchain_tool(ctx), ctx)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
launch_app_wrapper = ToolWrapper(
|
|
129
|
+
tool_fn_getter=get_launch_app_tool,
|
|
130
|
+
on_success_fn=lambda *args, **kwargs: "Launch dispatched",
|
|
131
|
+
on_failure_fn=lambda *args, **kwargs: "Failure",
|
|
132
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
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 datetime import datetime, timedelta
|
|
16
|
+
import time
|
|
17
|
+
from artemis.context import ArtemisContext
|
|
18
|
+
from third_party.mobile_use.controllers.platform_specific_commands_controller import get_adb_device
|
|
19
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parse_log_time(time_str: str) -> datetime:
|
|
25
|
+
"""Parses a log timestamp string into a datetime object."""
|
|
26
|
+
now = datetime.now()
|
|
27
|
+
current_year = now.year
|
|
28
|
+
|
|
29
|
+
def _try_parse(year: int) -> datetime | None:
|
|
30
|
+
for fmt in ("%Y-%m-%d %H:%M:%S.%f", "%Y-%m-%d %H:%M:%S"):
|
|
31
|
+
try:
|
|
32
|
+
return datetime.strptime(f"{year}-{time_str}", fmt)
|
|
33
|
+
except ValueError:
|
|
34
|
+
pass
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
dt = _try_parse(current_year)
|
|
38
|
+
if dt is None:
|
|
39
|
+
raise ValueError(f"Unsupported time format: {time_str}")
|
|
40
|
+
|
|
41
|
+
# If parsed time is in the future (allowing 1 hour grace for clock drift),
|
|
42
|
+
# we assume it belongs to the previous year.
|
|
43
|
+
if dt > now + timedelta(hours=1):
|
|
44
|
+
prev_dt = _try_parse(current_year - 1)
|
|
45
|
+
if prev_dt is not None:
|
|
46
|
+
dt = prev_dt
|
|
47
|
+
|
|
48
|
+
return dt
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def resolve_time(
|
|
52
|
+
time_input: str | float | None, session_start: float | None
|
|
53
|
+
) -> tuple[str | None, datetime | None]:
|
|
54
|
+
"""Resolves relative or absolute time input into formatted string and datetime."""
|
|
55
|
+
if time_input is None:
|
|
56
|
+
return None, None
|
|
57
|
+
|
|
58
|
+
if isinstance(time_input, (int, float)) or (
|
|
59
|
+
isinstance(time_input, str) and time_input.replace(".", "", 1).isdigit()
|
|
60
|
+
):
|
|
61
|
+
# It's relative seconds
|
|
62
|
+
if session_start is None:
|
|
63
|
+
raise ValueError("Cannot resolve relative time without session start time.")
|
|
64
|
+
rel_sec = float(time_input)
|
|
65
|
+
abs_ts = session_start + rel_sec
|
|
66
|
+
|
|
67
|
+
t_struct = time.localtime(abs_ts)
|
|
68
|
+
t_str = time.strftime("%m-%d %H:%M:%S", t_struct)
|
|
69
|
+
ms = int((abs_ts - int(abs_ts)) * 1000)
|
|
70
|
+
formatted_str = f"{t_str}.{ms:03d}"
|
|
71
|
+
|
|
72
|
+
dt = datetime.fromtimestamp(abs_ts)
|
|
73
|
+
return formatted_str, dt
|
|
74
|
+
|
|
75
|
+
# Assume it's absolute time string "MM-DD HH:MM:SS.ms"
|
|
76
|
+
try:
|
|
77
|
+
dt = parse_log_time(str(time_input))
|
|
78
|
+
except ValueError as e:
|
|
79
|
+
raise ValueError(f"Unsupported time format: {time_input}") from e
|
|
80
|
+
return str(time_input), dt
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# pylint: disable=too-many-locals
|
|
84
|
+
def fetch_and_filter_logs(
|
|
85
|
+
ctx: ArtemisContext,
|
|
86
|
+
lines: int = 1000,
|
|
87
|
+
since_time: str | None = None,
|
|
88
|
+
until_time: str | None = None,
|
|
89
|
+
) -> str:
|
|
90
|
+
"""Fetches logs from ADB device and filters them by time if needed."""
|
|
91
|
+
device = get_adb_device(ctx)
|
|
92
|
+
|
|
93
|
+
session_start = ctx.data_engine.session_start_time if ctx.data_engine else None
|
|
94
|
+
|
|
95
|
+
since_str, since_dt = resolve_time(since_time, session_start)
|
|
96
|
+
_, until_dt = resolve_time(until_time, session_start)
|
|
97
|
+
|
|
98
|
+
if since_str:
|
|
99
|
+
try:
|
|
100
|
+
logger.info(f"Attempting to fetch logs since {since_str}...")
|
|
101
|
+
logs = device.shell(f'logcat -v threadtime -t "{since_str}"')
|
|
102
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
103
|
+
logger.warning(
|
|
104
|
+
f"Failed to fetch logs since time directly: {e}. Falling back"
|
|
105
|
+
" to reading lines and filtering."
|
|
106
|
+
)
|
|
107
|
+
logs = device.shell(f"logcat -v threadtime -t {lines}")
|
|
108
|
+
else:
|
|
109
|
+
logger.info(f"Reading last {lines} lines of logs...")
|
|
110
|
+
logs = device.shell(f"logcat -v threadtime -t {lines}")
|
|
111
|
+
|
|
112
|
+
if since_dt or until_dt:
|
|
113
|
+
filtered_lines = []
|
|
114
|
+
for line in logs.splitlines():
|
|
115
|
+
if len(line) >= 18:
|
|
116
|
+
log_time_str = line[:18]
|
|
117
|
+
try:
|
|
118
|
+
log_dt = parse_log_time(log_time_str)
|
|
119
|
+
|
|
120
|
+
if since_dt and log_dt < since_dt:
|
|
121
|
+
continue
|
|
122
|
+
if until_dt and log_dt > until_dt:
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
filtered_lines.append(line)
|
|
126
|
+
except ValueError:
|
|
127
|
+
pass
|
|
128
|
+
# Cap to the requested line limit so timestamp fetches don't overflow context
|
|
129
|
+
if 0 < lines < len(filtered_lines):
|
|
130
|
+
filtered_lines = filtered_lines[-lines:]
|
|
131
|
+
logs = "\n".join(filtered_lines)
|
|
132
|
+
else:
|
|
133
|
+
# Ensure fallback capping even when no datetime parsing occurred
|
|
134
|
+
log_lines = logs.splitlines()
|
|
135
|
+
if 0 < lines < len(log_lines):
|
|
136
|
+
logs = "\n".join(log_lines[-lines:])
|
|
137
|
+
|
|
138
|
+
return logs
|
|
@@ -0,0 +1,202 @@
|
|
|
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 base64
|
|
16
|
+
import json
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from langchain_core.messages import ToolMessage
|
|
21
|
+
from langchain_core.tools import BaseTool
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
from artemis.context import ArtemisContext
|
|
25
|
+
from artemis.data_engine.trace import trace_langchain_tool
|
|
26
|
+
from artemis.drivers.base import BaseDeviceDriver
|
|
27
|
+
from artemis.graph.state import State
|
|
28
|
+
from artemis.tools.base import ArtemisTool, ToolCategory
|
|
29
|
+
from artemis.tools.tool_wrapper import ToolWrapper
|
|
30
|
+
from artemis.utils.ocr_api import is_ocr_configured, perform_ocr
|
|
31
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class OcrArgs(BaseModel):
|
|
35
|
+
"""Arguments schema for OCR recognition."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
OCR_DOCSTRING = (
|
|
41
|
+
"[EXPLORER] Performs OCR on the current screen and returns the detected"
|
|
42
|
+
" text along with their coordinates."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# pylint: disable=too-many-branches,too-many-locals,too-many-statements
|
|
47
|
+
async def run_ocr_core(
|
|
48
|
+
ctx: ArtemisContext | None = None,
|
|
49
|
+
state: State | None = None,
|
|
50
|
+
driver: BaseDeviceDriver | None = None,
|
|
51
|
+
) -> str:
|
|
52
|
+
"""Universal OCR core function: reads screenshot from state or driver and returns results."""
|
|
53
|
+
screenshot_b64 = None
|
|
54
|
+
screenshot_path = getattr(state, "latest_screenshot", None) if state else None
|
|
55
|
+
|
|
56
|
+
if screenshot_path:
|
|
57
|
+
if not Path(screenshot_path).exists():
|
|
58
|
+
logger.error(f"Screenshot file does not exist: {screenshot_path}")
|
|
59
|
+
raise FileNotFoundError(f"Screenshot file does not exist: {screenshot_path}")
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
with open(screenshot_path, "rb") as f:
|
|
63
|
+
screenshot_b64 = base64.b64encode(f.read()).decode("utf-8")
|
|
64
|
+
logger.info(f"OCR loaded screenshot from file: {screenshot_path}")
|
|
65
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
66
|
+
logger.error(f"Failed to read screenshot from {screenshot_path}: {e}")
|
|
67
|
+
raise e
|
|
68
|
+
elif driver is not None and hasattr(driver, "get_screen_data"):
|
|
69
|
+
try:
|
|
70
|
+
screen_data = await driver.get_screen_data()
|
|
71
|
+
if hasattr(screen_data, "screenshot_base64") and screen_data.screenshot_base64:
|
|
72
|
+
screenshot_b64 = screen_data.screenshot_base64
|
|
73
|
+
elif hasattr(screen_data, "screenshot_bytes") and screen_data.screenshot_bytes:
|
|
74
|
+
screenshot_b64 = base64.b64encode(screen_data.screenshot_bytes).decode("utf-8")
|
|
75
|
+
elif hasattr(screen_data, "base64") and screen_data.base64:
|
|
76
|
+
screenshot_b64 = screen_data.base64
|
|
77
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
78
|
+
logger.error(f"Failed to capture screenshot from driver: {e}")
|
|
79
|
+
raise e
|
|
80
|
+
else:
|
|
81
|
+
logger.error("No screenshot path found in state.latest_screenshot and no driver available")
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"No screenshot path found in state.latest_screenshot and no driver available"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if not screenshot_b64:
|
|
87
|
+
raise ValueError("Failed to obtain screenshot for OCR.")
|
|
88
|
+
|
|
89
|
+
width = 1080
|
|
90
|
+
height = 2400
|
|
91
|
+
try:
|
|
92
|
+
if ctx and hasattr(ctx, "device") and ctx.device:
|
|
93
|
+
w = getattr(ctx.device, "device_width", 1080)
|
|
94
|
+
h = getattr(ctx.device, "device_height", 2400)
|
|
95
|
+
if isinstance(w, int):
|
|
96
|
+
width = w
|
|
97
|
+
if isinstance(h, int):
|
|
98
|
+
height = h
|
|
99
|
+
elif driver and hasattr(driver, "screen_size") and driver.screen_size:
|
|
100
|
+
width, height = driver.screen_size
|
|
101
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
102
|
+
logger.warning(f"Failed to get device resolution for OCR normalization: {e}")
|
|
103
|
+
|
|
104
|
+
results = await perform_ocr(screenshot_b64)
|
|
105
|
+
if results:
|
|
106
|
+
normalized_results = []
|
|
107
|
+
for item in results:
|
|
108
|
+
text = item.get("text")
|
|
109
|
+
vertices = item.get("position") or []
|
|
110
|
+
|
|
111
|
+
# Calculate center point
|
|
112
|
+
x_coords = [v.get("x", 0) for v in vertices]
|
|
113
|
+
y_coords = [v.get("y", 0) for v in vertices]
|
|
114
|
+
|
|
115
|
+
if x_coords and y_coords:
|
|
116
|
+
min_x, max_x = min(x_coords), max(x_coords)
|
|
117
|
+
min_y, max_y = min(y_coords), max(y_coords)
|
|
118
|
+
|
|
119
|
+
center_x = (min_x + max_x) / 2
|
|
120
|
+
center_y = (min_y + max_y) / 2
|
|
121
|
+
|
|
122
|
+
# Normalize to 0-1000
|
|
123
|
+
norm_x = int(max(0, min(1000, center_x * 1000 / width)))
|
|
124
|
+
norm_y = int(max(0, min(1000, center_y * 1000 / height)))
|
|
125
|
+
|
|
126
|
+
normalized_results.append({"text": text, "coordinates": [norm_x, norm_y]})
|
|
127
|
+
else:
|
|
128
|
+
normalized_results.append({"text": text, "coordinates": None})
|
|
129
|
+
|
|
130
|
+
return json.dumps(normalized_results, ensure_ascii=False, indent=2)
|
|
131
|
+
return "No text detected on the screen."
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class OcrRecognitionTool(ArtemisTool):
|
|
135
|
+
"""Universal tool for performing OCR on the current screen."""
|
|
136
|
+
|
|
137
|
+
def __init__(self, category: ToolCategory = "explorer"):
|
|
138
|
+
super().__init__(
|
|
139
|
+
name="ocr_recognition",
|
|
140
|
+
description=OCR_DOCSTRING,
|
|
141
|
+
args_schema=OcrArgs,
|
|
142
|
+
category=category,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def is_available(self, ctx: Any = None) -> bool:
|
|
146
|
+
return is_ocr_configured()
|
|
147
|
+
|
|
148
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
149
|
+
async def execute(
|
|
150
|
+
self,
|
|
151
|
+
driver: BaseDeviceDriver | None = None,
|
|
152
|
+
ctx: ArtemisContext | None = None,
|
|
153
|
+
tool_call_id: str | None = None,
|
|
154
|
+
state: State | None = None,
|
|
155
|
+
**kwargs: Any,
|
|
156
|
+
) -> Any:
|
|
157
|
+
tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
|
|
158
|
+
st = state if state is not None else kwargs.get("state")
|
|
159
|
+
|
|
160
|
+
has_failed = False
|
|
161
|
+
output = ""
|
|
162
|
+
error_message = None
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
output = await run_ocr_core(ctx=ctx, state=st, driver=driver)
|
|
166
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
167
|
+
has_failed = True
|
|
168
|
+
error_message = f"Failed to perform OCR: {str(e)}"
|
|
169
|
+
|
|
170
|
+
if st is not None:
|
|
171
|
+
return ToolMessage(
|
|
172
|
+
tool_call_id=tcid or "",
|
|
173
|
+
content=ocr_recognition_wrapper.on_failure_fn(error_message)
|
|
174
|
+
if has_failed
|
|
175
|
+
else ocr_recognition_wrapper.on_success_fn(output),
|
|
176
|
+
additional_kwargs={"error": error_message} if has_failed else {},
|
|
177
|
+
status="error" if has_failed else "success",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if has_failed:
|
|
181
|
+
return ocr_recognition_wrapper.on_failure_fn(error_message)
|
|
182
|
+
return ocr_recognition_wrapper.on_success_fn(output)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# Universal tool instance & aliases
|
|
186
|
+
ocr_recognition = OcrRecognitionTool()
|
|
187
|
+
OcrRecognition = OcrRecognitionTool
|
|
188
|
+
OCRRecognition = OcrRecognitionTool
|
|
189
|
+
OcrTool = OcrRecognitionTool
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_ocr_tool(ctx: ArtemisContext) -> BaseTool:
|
|
193
|
+
"""Exports ocr_recognition as a LangChain BaseTool."""
|
|
194
|
+
return trace_langchain_tool(ocr_recognition.to_langchain_tool(ctx), ctx)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
ocr_recognition_wrapper = ToolWrapper(
|
|
198
|
+
tool_fn_getter=get_ocr_tool,
|
|
199
|
+
on_success_fn=lambda output: f"OCR Recognition successful. Results:\n{output}",
|
|
200
|
+
on_failure_fn=lambda error: f"OCR Recognition failed: {error}",
|
|
201
|
+
is_available_fn=lambda ctx: is_ocr_configured(),
|
|
202
|
+
)
|