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,148 @@
|
|
|
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 json
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ModelService:
|
|
24
|
+
"""Service handling agent architecture profile detection and metadata formatting."""
|
|
25
|
+
|
|
26
|
+
# parse_llm_config re-reads and re-validates the config file on every call;
|
|
27
|
+
# /api/sessions calls this once per session row, so cache (provider, model_id)
|
|
28
|
+
# for a short TTL. Config edits take effect within _LLM_INFO_TTL seconds.
|
|
29
|
+
_LLM_INFO_TTL = 10.0
|
|
30
|
+
_llm_info_cache: tuple[float, str, str] | None = None
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def _get_llm_provider_and_model(cls) -> tuple[str, str]:
|
|
34
|
+
cached = cls._llm_info_cache
|
|
35
|
+
now = time.monotonic()
|
|
36
|
+
if cached and now - cached[0] < cls._LLM_INFO_TTL:
|
|
37
|
+
return cached[1], cached[2]
|
|
38
|
+
provider = "google"
|
|
39
|
+
model_id = "gemini-3.7-flash"
|
|
40
|
+
try:
|
|
41
|
+
from artemis.config import parse_llm_config
|
|
42
|
+
|
|
43
|
+
llm_cfg = parse_llm_config()
|
|
44
|
+
if llm_cfg and llm_cfg.operator:
|
|
45
|
+
provider = str(llm_cfg.operator.provider)
|
|
46
|
+
model_id = str(llm_cfg.operator.model)
|
|
47
|
+
elif llm_cfg and llm_cfg.default:
|
|
48
|
+
provider = str(llm_cfg.default.provider)
|
|
49
|
+
model_id = str(llm_cfg.default.model)
|
|
50
|
+
except Exception as exc:
|
|
51
|
+
# Unreadable/invalid LLM config: fall back to display defaults
|
|
52
|
+
# (cached for the TTL, so this logs at most once per window).
|
|
53
|
+
logger.warning("Could not resolve LLM config for display: %s", exc)
|
|
54
|
+
cls._llm_info_cache = (now, provider, model_id)
|
|
55
|
+
return provider, model_id
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def get_active_model_info(cls, profile: str | None = None) -> dict[str, str]:
|
|
59
|
+
"""Return active architecture and underlying LLM model configuration."""
|
|
60
|
+
# 1. Determine underlying LLM model and provider from config (cached)
|
|
61
|
+
provider, model_id = cls._get_llm_provider_and_model()
|
|
62
|
+
|
|
63
|
+
# 2. Determine agent architecture name (Flash vs Pro)
|
|
64
|
+
arch_name = "Flash"
|
|
65
|
+
if profile:
|
|
66
|
+
p_lower = str(profile).lower()
|
|
67
|
+
if "pro" in p_lower:
|
|
68
|
+
arch_name = "Pro"
|
|
69
|
+
elif "flash" in p_lower:
|
|
70
|
+
arch_name = "Flash"
|
|
71
|
+
else:
|
|
72
|
+
arch_name = str(profile).capitalize()
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
"name": arch_name,
|
|
76
|
+
"id": model_id,
|
|
77
|
+
"provider": provider,
|
|
78
|
+
"architecture": f"ARTEMIS {arch_name}",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def resolve_session_profile(
|
|
83
|
+
row_dict: dict[str, Any],
|
|
84
|
+
llm_trace_payloads: list[str] | None = None,
|
|
85
|
+
running_profile: str | None = None,
|
|
86
|
+
agent_names: list[str] | None = None,
|
|
87
|
+
) -> str | None:
|
|
88
|
+
"""Resolve the Artemis agent architecture profile ('flash' or 'pro') for a session."""
|
|
89
|
+
sess_profile = None
|
|
90
|
+
d_info_raw = row_dict.get("device_info")
|
|
91
|
+
if d_info_raw:
|
|
92
|
+
try:
|
|
93
|
+
d_info = json.loads(d_info_raw) if isinstance(d_info_raw, str) else d_info_raw
|
|
94
|
+
if isinstance(d_info, dict):
|
|
95
|
+
p = d_info.get("profile")
|
|
96
|
+
if p:
|
|
97
|
+
p_str = str(p).lower()
|
|
98
|
+
if "pro" in p_str:
|
|
99
|
+
return "pro"
|
|
100
|
+
elif "flash" in p_str:
|
|
101
|
+
return "flash"
|
|
102
|
+
except (ValueError, TypeError):
|
|
103
|
+
# Malformed device_info JSON: try the other profile sources.
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
if (row_dict.get("status") == "running") and running_profile:
|
|
107
|
+
p_str = str(running_profile).lower()
|
|
108
|
+
if "pro" in p_str:
|
|
109
|
+
return "pro"
|
|
110
|
+
elif "flash" in p_str:
|
|
111
|
+
return "flash"
|
|
112
|
+
|
|
113
|
+
# Check Agent/Trace names - the most definitive indicator of architecture
|
|
114
|
+
if agent_names:
|
|
115
|
+
agent_names_lower = [str(a).lower() for a in agent_names]
|
|
116
|
+
if any(
|
|
117
|
+
name in ("planner", "validator", "summarizer", "operator", "checker", "diagnoser")
|
|
118
|
+
for name in agent_names_lower
|
|
119
|
+
):
|
|
120
|
+
return "pro"
|
|
121
|
+
if any("flashrunner" in name for name in agent_names_lower):
|
|
122
|
+
return "flash"
|
|
123
|
+
|
|
124
|
+
# Check LLM traces for agent name
|
|
125
|
+
if llm_trace_payloads:
|
|
126
|
+
for tr_payload in llm_trace_payloads:
|
|
127
|
+
if tr_payload:
|
|
128
|
+
try:
|
|
129
|
+
p_obj = (
|
|
130
|
+
json.loads(tr_payload) if isinstance(tr_payload, str) else tr_payload
|
|
131
|
+
)
|
|
132
|
+
if isinstance(p_obj, dict):
|
|
133
|
+
agent_name = str(p_obj.get("agent") or p_obj.get("name") or "").lower()
|
|
134
|
+
if "flashrunner" in agent_name:
|
|
135
|
+
return "flash"
|
|
136
|
+
if any(
|
|
137
|
+
x in agent_name
|
|
138
|
+
for x in ("planner", "validator", "operator", "checker")
|
|
139
|
+
):
|
|
140
|
+
return "pro"
|
|
141
|
+
except (ValueError, TypeError):
|
|
142
|
+
# Malformed trace payload JSON: skip this trace.
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
model_service = ModelService()
|
|
@@ -0,0 +1,514 @@
|
|
|
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
|
+
"""Preset Task Catalog & Dynamic Recommendation Engine for Artemis."""
|
|
16
|
+
|
|
17
|
+
from typing import Any, Literal
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AppInfo(BaseModel):
|
|
22
|
+
"""Application metadata."""
|
|
23
|
+
|
|
24
|
+
name: str
|
|
25
|
+
icon: str
|
|
26
|
+
pkg: str
|
|
27
|
+
category: str = "general"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TaskPreset(BaseModel):
|
|
31
|
+
"""Smart suggestion task definition."""
|
|
32
|
+
|
|
33
|
+
id: str
|
|
34
|
+
title: str
|
|
35
|
+
description: str
|
|
36
|
+
goal: str
|
|
37
|
+
profile: Literal["flash", "pro"]
|
|
38
|
+
category: Literal["flash", "pro", "cross_app", "monitor"]
|
|
39
|
+
tag: str
|
|
40
|
+
apps: list[AppInfo]
|
|
41
|
+
required_packages: list[str] = Field(default_factory=list)
|
|
42
|
+
match_mode: Literal["any", "all"] = "any"
|
|
43
|
+
priority: int = 50
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ============================================================================
|
|
47
|
+
# APP PACKAGE REGISTRY
|
|
48
|
+
# ============================================================================
|
|
49
|
+
|
|
50
|
+
APP_REGISTRY: dict[str, dict[str, str]] = {
|
|
51
|
+
# Google Suite & System
|
|
52
|
+
"com.google.android.apps.maps": {"name": "Maps", "icon": "explore", "category": "navigation"},
|
|
53
|
+
"com.google.android.gm": {"name": "Gmail", "icon": "mail", "category": "productivity"},
|
|
54
|
+
"com.android.chrome": {"name": "Chrome", "icon": "public", "category": "browser"},
|
|
55
|
+
"com.google.android.youtube": {
|
|
56
|
+
"name": "YouTube",
|
|
57
|
+
"icon": "smart_display",
|
|
58
|
+
"category": "entertainment",
|
|
59
|
+
},
|
|
60
|
+
"com.android.settings": {"name": "Settings", "icon": "settings", "category": "system"},
|
|
61
|
+
"com.google.android.deskclock": {"name": "Clock", "icon": "timer", "category": "utility"},
|
|
62
|
+
"com.android.deskclock": {"name": "Clock", "icon": "timer", "category": "utility"},
|
|
63
|
+
"com.google.android.calculator": {
|
|
64
|
+
"name": "Calculator",
|
|
65
|
+
"icon": "calculate",
|
|
66
|
+
"category": "utility",
|
|
67
|
+
},
|
|
68
|
+
"com.android.calculator2": {"name": "Calculator", "icon": "calculate", "category": "utility"},
|
|
69
|
+
"com.google.android.apps.photos": {
|
|
70
|
+
"name": "Photos",
|
|
71
|
+
"icon": "photo_library",
|
|
72
|
+
"category": "media",
|
|
73
|
+
},
|
|
74
|
+
"com.google.android.calendar": {
|
|
75
|
+
"name": "Calendar",
|
|
76
|
+
"icon": "calendar_month",
|
|
77
|
+
"category": "productivity",
|
|
78
|
+
},
|
|
79
|
+
"com.google.android.keep": {
|
|
80
|
+
"name": "Keep Notes",
|
|
81
|
+
"icon": "note_alt",
|
|
82
|
+
"category": "productivity",
|
|
83
|
+
},
|
|
84
|
+
"com.android.vending": {"name": "Play Store", "icon": "storefront", "category": "tools"},
|
|
85
|
+
"com.google.android.apps.messaging": {
|
|
86
|
+
"name": "Messages",
|
|
87
|
+
"icon": "chat",
|
|
88
|
+
"category": "communication",
|
|
89
|
+
},
|
|
90
|
+
# Popular Ecosystem Apps
|
|
91
|
+
"com.tencent.mm": {"name": "WeChat", "icon": "forum", "category": "social"},
|
|
92
|
+
"com.xingin.xhs": {"name": "Xiaohongshu", "icon": "auto_stories", "category": "social"},
|
|
93
|
+
"com.sankuai.meituan": {"name": "Meituan", "icon": "restaurant", "category": "lifestyle"},
|
|
94
|
+
"com.dianping.v1": {"name": "Dianping", "icon": "star", "category": "lifestyle"},
|
|
95
|
+
"tv.danmaku.bili": {"name": "Bilibili", "icon": "video_library", "category": "entertainment"},
|
|
96
|
+
"com.eg.android.AlipayGphone": {
|
|
97
|
+
"name": "Alipay",
|
|
98
|
+
"icon": "account_balance_wallet",
|
|
99
|
+
"category": "finance",
|
|
100
|
+
},
|
|
101
|
+
"com.netease.cloudmusic": {
|
|
102
|
+
"name": "NetEase Music",
|
|
103
|
+
"icon": "headphones",
|
|
104
|
+
"category": "entertainment",
|
|
105
|
+
},
|
|
106
|
+
"com.spotify.music": {"name": "Spotify", "icon": "music_note", "category": "entertainment"},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ============================================================================
|
|
111
|
+
# CURATED TASK PRESET LIBRARY (1-2 curated tasks per common app)
|
|
112
|
+
# ============================================================================
|
|
113
|
+
|
|
114
|
+
PRESET_TASK_CATALOG: list[TaskPreset] = [
|
|
115
|
+
# 1. Google Maps
|
|
116
|
+
TaskPreset(
|
|
117
|
+
id="maps_coffee",
|
|
118
|
+
title="Find Specialty Coffee",
|
|
119
|
+
description="Search nearby top-rated cafes in Google Maps",
|
|
120
|
+
goal="Open Google Maps, search for top-rated specialty coffee shops nearby, and view the top result details.",
|
|
121
|
+
profile="flash",
|
|
122
|
+
category="flash",
|
|
123
|
+
tag="Maps",
|
|
124
|
+
apps=[
|
|
125
|
+
AppInfo(
|
|
126
|
+
name="Maps",
|
|
127
|
+
icon="explore",
|
|
128
|
+
pkg="com.google.android.apps.maps",
|
|
129
|
+
category="navigation",
|
|
130
|
+
)
|
|
131
|
+
],
|
|
132
|
+
required_packages=["com.google.android.apps.maps"],
|
|
133
|
+
priority=95,
|
|
134
|
+
),
|
|
135
|
+
TaskPreset(
|
|
136
|
+
id="pro_commute_share",
|
|
137
|
+
title="Commute ETA & Message Draft",
|
|
138
|
+
description="Check transit time on Maps and draft arrival ETA in Messages",
|
|
139
|
+
goal="Open Google Maps to check commute time to the International Airport, calculate arrival time, then open Messages and draft an ETA text message.",
|
|
140
|
+
profile="pro",
|
|
141
|
+
category="cross_app",
|
|
142
|
+
tag="Maps + Messages",
|
|
143
|
+
apps=[
|
|
144
|
+
AppInfo(
|
|
145
|
+
name="Maps",
|
|
146
|
+
icon="explore",
|
|
147
|
+
pkg="com.google.android.apps.maps",
|
|
148
|
+
category="navigation",
|
|
149
|
+
),
|
|
150
|
+
AppInfo(
|
|
151
|
+
name="Messages",
|
|
152
|
+
icon="chat",
|
|
153
|
+
pkg="com.google.android.apps.messaging",
|
|
154
|
+
category="communication",
|
|
155
|
+
),
|
|
156
|
+
],
|
|
157
|
+
required_packages=["com.google.android.apps.maps", "com.google.android.apps.messaging"],
|
|
158
|
+
match_mode="all",
|
|
159
|
+
priority=92,
|
|
160
|
+
),
|
|
161
|
+
# 2. Gmail
|
|
162
|
+
TaskPreset(
|
|
163
|
+
id="gmail_receipts",
|
|
164
|
+
title="Search Order Receipts",
|
|
165
|
+
description="Find recent flight or delivery confirmation emails in Gmail",
|
|
166
|
+
goal="Open Gmail and search for recent flight or package delivery confirmation emails.",
|
|
167
|
+
profile="flash",
|
|
168
|
+
category="flash",
|
|
169
|
+
tag="Gmail",
|
|
170
|
+
apps=[
|
|
171
|
+
AppInfo(name="Gmail", icon="mail", pkg="com.google.android.gm", category="productivity")
|
|
172
|
+
],
|
|
173
|
+
required_packages=["com.google.android.gm"],
|
|
174
|
+
priority=90,
|
|
175
|
+
),
|
|
176
|
+
TaskPreset(
|
|
177
|
+
id="pro_email_to_calendar",
|
|
178
|
+
title="Email Itinerary to Calendar",
|
|
179
|
+
description="Extract flight or event dates from Gmail and schedule in Calendar",
|
|
180
|
+
goal="Open Gmail to find the latest event invitation or itinerary, extract dates and location, then open Google Calendar and create a corresponding calendar event.",
|
|
181
|
+
profile="pro",
|
|
182
|
+
category="cross_app",
|
|
183
|
+
tag="Gmail + Calendar",
|
|
184
|
+
apps=[
|
|
185
|
+
AppInfo(
|
|
186
|
+
name="Gmail", icon="mail", pkg="com.google.android.gm", category="productivity"
|
|
187
|
+
),
|
|
188
|
+
AppInfo(
|
|
189
|
+
name="Calendar",
|
|
190
|
+
icon="calendar_month",
|
|
191
|
+
pkg="com.google.android.calendar",
|
|
192
|
+
category="productivity",
|
|
193
|
+
),
|
|
194
|
+
],
|
|
195
|
+
required_packages=["com.google.android.gm"],
|
|
196
|
+
priority=94,
|
|
197
|
+
),
|
|
198
|
+
# 3. Chrome
|
|
199
|
+
TaskPreset(
|
|
200
|
+
id="chrome_research",
|
|
201
|
+
title="Search AI News Breakthroughs",
|
|
202
|
+
description="Search latest multimodal AI developments in Chrome browser",
|
|
203
|
+
goal="Open Chrome browser and search for latest breakthroughs in multimodal mobile AI agents.",
|
|
204
|
+
profile="flash",
|
|
205
|
+
category="flash",
|
|
206
|
+
tag="Chrome",
|
|
207
|
+
apps=[AppInfo(name="Chrome", icon="public", pkg="com.android.chrome", category="browser")],
|
|
208
|
+
required_packages=["com.android.chrome"],
|
|
209
|
+
priority=88,
|
|
210
|
+
),
|
|
211
|
+
TaskPreset(
|
|
212
|
+
id="pro_research_keep",
|
|
213
|
+
title="Product Research & Notes Note",
|
|
214
|
+
description="Compare top 3 headphones on Chrome and record comparison in Keep",
|
|
215
|
+
goal="Open Chrome, research top 3 noise-cancelling headphones comparing price and battery life, then write a structured comparison summary note in Keep Notes.",
|
|
216
|
+
profile="pro",
|
|
217
|
+
category="pro",
|
|
218
|
+
tag="Chrome + Keep",
|
|
219
|
+
apps=[
|
|
220
|
+
AppInfo(name="Chrome", icon="public", pkg="com.android.chrome", category="browser"),
|
|
221
|
+
AppInfo(
|
|
222
|
+
name="Keep Notes",
|
|
223
|
+
icon="note_alt",
|
|
224
|
+
pkg="com.google.android.keep",
|
|
225
|
+
category="productivity",
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
required_packages=["com.android.chrome"],
|
|
229
|
+
priority=91,
|
|
230
|
+
),
|
|
231
|
+
# 4. YouTube
|
|
232
|
+
TaskPreset(
|
|
233
|
+
id="youtube_lofi",
|
|
234
|
+
title="Play Lo-Fi Music Radio",
|
|
235
|
+
description="Search and play a Lo-Fi hip hop live stream on YouTube",
|
|
236
|
+
goal='Open YouTube, search for "Lofi hip hop beats relaxing radio" and tap on the live stream.',
|
|
237
|
+
profile="flash",
|
|
238
|
+
category="flash",
|
|
239
|
+
tag="YouTube",
|
|
240
|
+
apps=[
|
|
241
|
+
AppInfo(
|
|
242
|
+
name="YouTube",
|
|
243
|
+
icon="smart_display",
|
|
244
|
+
pkg="com.google.android.youtube",
|
|
245
|
+
category="entertainment",
|
|
246
|
+
)
|
|
247
|
+
],
|
|
248
|
+
required_packages=["com.google.android.youtube"],
|
|
249
|
+
priority=85,
|
|
250
|
+
),
|
|
251
|
+
# 5. Settings
|
|
252
|
+
TaskPreset(
|
|
253
|
+
id="settings_display_wifi",
|
|
254
|
+
title="Dark Mode & Wi-Fi Check",
|
|
255
|
+
description="Toggle dark theme and verify network connection in Settings",
|
|
256
|
+
goal="Open Settings app, navigate to Display settings, ensure Dark theme is enabled, and check Wi-Fi connection status.",
|
|
257
|
+
profile="flash",
|
|
258
|
+
category="flash",
|
|
259
|
+
tag="Settings",
|
|
260
|
+
apps=[
|
|
261
|
+
AppInfo(name="Settings", icon="settings", pkg="com.android.settings", category="system")
|
|
262
|
+
],
|
|
263
|
+
required_packages=["com.android.settings"],
|
|
264
|
+
priority=87,
|
|
265
|
+
),
|
|
266
|
+
TaskPreset(
|
|
267
|
+
id="pro_settings_qa",
|
|
268
|
+
title="Subsystem Health & Crash Probe",
|
|
269
|
+
description="Traverse Settings submenus to verify screens and check for crash dialogs",
|
|
270
|
+
goal="Explore Settings submenus (Network, Connected devices, Apps, Battery, Storage), verify each screen loads properly without ANR or crash dialogs, and summarize results.",
|
|
271
|
+
profile="pro",
|
|
272
|
+
category="monitor",
|
|
273
|
+
tag="Settings QA",
|
|
274
|
+
apps=[
|
|
275
|
+
AppInfo(name="Settings", icon="settings", pkg="com.android.settings", category="system")
|
|
276
|
+
],
|
|
277
|
+
required_packages=["com.android.settings"],
|
|
278
|
+
priority=93,
|
|
279
|
+
),
|
|
280
|
+
# 6. Clock
|
|
281
|
+
TaskPreset(
|
|
282
|
+
id="clock_timer",
|
|
283
|
+
title="25-Min Pomodoro Timer",
|
|
284
|
+
description="Start a 25-minute focus countdown timer in Clock app",
|
|
285
|
+
goal="Open Clock app, switch to Timer tab, set 25 minutes and start the countdown timer.",
|
|
286
|
+
profile="flash",
|
|
287
|
+
category="flash",
|
|
288
|
+
tag="Clock",
|
|
289
|
+
apps=[
|
|
290
|
+
AppInfo(
|
|
291
|
+
name="Clock", icon="timer", pkg="com.google.android.deskclock", category="utility"
|
|
292
|
+
)
|
|
293
|
+
],
|
|
294
|
+
required_packages=["com.google.android.deskclock", "com.android.deskclock"],
|
|
295
|
+
priority=86,
|
|
296
|
+
),
|
|
297
|
+
# 7. Calculator
|
|
298
|
+
TaskPreset(
|
|
299
|
+
id="calc_gratuity",
|
|
300
|
+
title="Split Bill & Calculate Tip",
|
|
301
|
+
description="Calculate 18% gratuity on $186.40 for 3 people in Calculator",
|
|
302
|
+
goal="Open Calculator and calculate 18% tip on a bill of $186.40, then divide by 3 people.",
|
|
303
|
+
profile="flash",
|
|
304
|
+
category="flash",
|
|
305
|
+
tag="Calculator",
|
|
306
|
+
apps=[
|
|
307
|
+
AppInfo(
|
|
308
|
+
name="Calculator",
|
|
309
|
+
icon="calculate",
|
|
310
|
+
pkg="com.google.android.calculator",
|
|
311
|
+
category="utility",
|
|
312
|
+
)
|
|
313
|
+
],
|
|
314
|
+
required_packages=["com.google.android.calculator", "com.android.calculator2"],
|
|
315
|
+
priority=84,
|
|
316
|
+
),
|
|
317
|
+
# 8. Photos
|
|
318
|
+
TaskPreset(
|
|
319
|
+
id="photos_inspect",
|
|
320
|
+
title="Inspect Recent Screenshot",
|
|
321
|
+
description="Open Google Photos and review the latest screenshot taken",
|
|
322
|
+
goal="Open Google Photos and view the most recent screenshot in the screenshots album.",
|
|
323
|
+
profile="flash",
|
|
324
|
+
category="flash",
|
|
325
|
+
tag="Photos",
|
|
326
|
+
apps=[
|
|
327
|
+
AppInfo(
|
|
328
|
+
name="Photos",
|
|
329
|
+
icon="photo_library",
|
|
330
|
+
pkg="com.google.android.apps.photos",
|
|
331
|
+
category="media",
|
|
332
|
+
)
|
|
333
|
+
],
|
|
334
|
+
required_packages=["com.google.android.apps.photos"],
|
|
335
|
+
priority=82,
|
|
336
|
+
),
|
|
337
|
+
# 9. WeChat
|
|
338
|
+
TaskPreset(
|
|
339
|
+
id="wechat_browse",
|
|
340
|
+
title="Check WeChat Messages",
|
|
341
|
+
description="Open WeChat and view top recent chat conversations",
|
|
342
|
+
goal="Open WeChat and view the top recent chat messages.",
|
|
343
|
+
profile="flash",
|
|
344
|
+
category="flash",
|
|
345
|
+
tag="WeChat",
|
|
346
|
+
apps=[AppInfo(name="WeChat", icon="forum", pkg="com.tencent.mm", category="social")],
|
|
347
|
+
required_packages=["com.tencent.mm"],
|
|
348
|
+
priority=89,
|
|
349
|
+
),
|
|
350
|
+
TaskPreset(
|
|
351
|
+
id="pro_wechat_to_calendar",
|
|
352
|
+
title="WeChat Notice to Calendar",
|
|
353
|
+
description="Extract meeting notice from WeChat chat and add to Calendar",
|
|
354
|
+
goal="Open WeChat, locate the latest meeting announcement or event message in the top chat, extract the time and topic, then open Calendar and schedule an event.",
|
|
355
|
+
profile="pro",
|
|
356
|
+
category="cross_app",
|
|
357
|
+
tag="WeChat + Calendar",
|
|
358
|
+
apps=[
|
|
359
|
+
AppInfo(name="WeChat", icon="forum", pkg="com.tencent.mm", category="social"),
|
|
360
|
+
AppInfo(
|
|
361
|
+
name="Calendar",
|
|
362
|
+
icon="calendar_month",
|
|
363
|
+
pkg="com.google.android.calendar",
|
|
364
|
+
category="productivity",
|
|
365
|
+
),
|
|
366
|
+
],
|
|
367
|
+
required_packages=["com.tencent.mm"],
|
|
368
|
+
priority=93,
|
|
369
|
+
),
|
|
370
|
+
# 10. Xiaohongshu
|
|
371
|
+
TaskPreset(
|
|
372
|
+
id="xhs_coffee_guide",
|
|
373
|
+
title="RED Cafe Guide Search",
|
|
374
|
+
description="Search trending specialty cafe reviews on Xiaohongshu",
|
|
375
|
+
goal="Open Xiaohongshu, search for top-rated specialty coffee shops, and view the top post.",
|
|
376
|
+
profile="flash",
|
|
377
|
+
category="flash",
|
|
378
|
+
tag="Xiaohongshu",
|
|
379
|
+
apps=[
|
|
380
|
+
AppInfo(
|
|
381
|
+
name="Xiaohongshu", icon="auto_stories", pkg="com.xingin.xhs", category="social"
|
|
382
|
+
)
|
|
383
|
+
],
|
|
384
|
+
required_packages=["com.xingin.xhs"],
|
|
385
|
+
priority=87,
|
|
386
|
+
),
|
|
387
|
+
# 11. Meituan / Dianping
|
|
388
|
+
TaskPreset(
|
|
389
|
+
id="meituan_ramen_search",
|
|
390
|
+
title="Meituan Food Search",
|
|
391
|
+
description="Search top-rated Ramen nearby on Meituan or Dianping",
|
|
392
|
+
goal="Open Meituan or Dianping, search for top-rated Ramen nearby, and view top restaurant rating.",
|
|
393
|
+
profile="flash",
|
|
394
|
+
category="flash",
|
|
395
|
+
tag="Meituan",
|
|
396
|
+
apps=[
|
|
397
|
+
AppInfo(
|
|
398
|
+
name="Meituan", icon="restaurant", pkg="com.sankuai.meituan", category="lifestyle"
|
|
399
|
+
)
|
|
400
|
+
],
|
|
401
|
+
required_packages=["com.sankuai.meituan", "com.dianping.v1"],
|
|
402
|
+
priority=86,
|
|
403
|
+
),
|
|
404
|
+
# 12. Bilibili
|
|
405
|
+
TaskPreset(
|
|
406
|
+
id="bilibili_stream",
|
|
407
|
+
title="Bilibili Tech Video",
|
|
408
|
+
description="Search and play an AI Agent tutorial video on Bilibili",
|
|
409
|
+
goal='Open Bilibili, search for "AI Agent Architecture", and play the top matching video.',
|
|
410
|
+
profile="flash",
|
|
411
|
+
category="flash",
|
|
412
|
+
tag="Bilibili",
|
|
413
|
+
apps=[
|
|
414
|
+
AppInfo(
|
|
415
|
+
name="Bilibili",
|
|
416
|
+
icon="video_library",
|
|
417
|
+
pkg="tv.danmaku.bili",
|
|
418
|
+
category="entertainment",
|
|
419
|
+
)
|
|
420
|
+
],
|
|
421
|
+
required_packages=["tv.danmaku.bili"],
|
|
422
|
+
priority=85,
|
|
423
|
+
),
|
|
424
|
+
# 13. Play Store
|
|
425
|
+
TaskPreset(
|
|
426
|
+
id="pro_playstore_review",
|
|
427
|
+
title="Play Store App Review Study",
|
|
428
|
+
description="Compare top task management apps and ratings on Google Play",
|
|
429
|
+
goal="Open Google Play Store, search for top rated task management apps, compare ratings and latest user reviews of the top 2 candidates, and record recommendations.",
|
|
430
|
+
profile="pro",
|
|
431
|
+
category="pro",
|
|
432
|
+
tag="Play Store",
|
|
433
|
+
apps=[
|
|
434
|
+
AppInfo(
|
|
435
|
+
name="Play Store", icon="storefront", pkg="com.android.vending", category="tools"
|
|
436
|
+
)
|
|
437
|
+
],
|
|
438
|
+
required_packages=["com.android.vending"],
|
|
439
|
+
priority=88,
|
|
440
|
+
),
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
# ============================================================================
|
|
445
|
+
# RECOMMENDATION ENGINE
|
|
446
|
+
# ============================================================================
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class TaskRecommendationEngine:
|
|
450
|
+
"""Intelligent recommendation engine matching device capabilities."""
|
|
451
|
+
|
|
452
|
+
def __init__(self, catalog: list[TaskPreset] | None = None):
|
|
453
|
+
self.catalog = catalog or PRESET_TASK_CATALOG
|
|
454
|
+
|
|
455
|
+
def get_all_tasks(self) -> list[dict[str, Any]]:
|
|
456
|
+
return [task.model_dump() for task in self.catalog]
|
|
457
|
+
|
|
458
|
+
def recommend_tasks(
|
|
459
|
+
self, installed_packages: list[str] | set[str], category: str = "all", limit: int = 12
|
|
460
|
+
) -> list[dict[str, Any]]:
|
|
461
|
+
pkgs_set = (
|
|
462
|
+
set(installed_packages) if isinstance(installed_packages, list) else installed_packages
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
scored_tasks: list[tuple[int, TaskPreset, bool]] = []
|
|
466
|
+
|
|
467
|
+
for task in self.catalog:
|
|
468
|
+
# Check package presence
|
|
469
|
+
is_matched = False
|
|
470
|
+
if pkgs_set:
|
|
471
|
+
if not task.required_packages:
|
|
472
|
+
is_matched = True
|
|
473
|
+
elif task.match_mode == "all":
|
|
474
|
+
is_matched = all(pkg in pkgs_set for pkg in task.required_packages)
|
|
475
|
+
else:
|
|
476
|
+
is_matched = any(pkg in pkgs_set for pkg in task.required_packages)
|
|
477
|
+
else:
|
|
478
|
+
is_matched = False
|
|
479
|
+
|
|
480
|
+
# Calculate score
|
|
481
|
+
score = task.priority
|
|
482
|
+
if pkgs_set:
|
|
483
|
+
if is_matched:
|
|
484
|
+
score += 100
|
|
485
|
+
if len(task.required_packages) > 1 and task.match_mode == "all":
|
|
486
|
+
score += 30
|
|
487
|
+
else:
|
|
488
|
+
score -= 40
|
|
489
|
+
|
|
490
|
+
# Filter by category
|
|
491
|
+
if category == "flash" and task.profile != "flash":
|
|
492
|
+
continue
|
|
493
|
+
elif category == "pro" and task.profile != "pro":
|
|
494
|
+
continue
|
|
495
|
+
elif category == "cross_app" and task.category != "cross_app":
|
|
496
|
+
continue
|
|
497
|
+
elif category == "monitor" and task.category != "monitor":
|
|
498
|
+
continue
|
|
499
|
+
|
|
500
|
+
scored_tasks.append((score, task, is_matched))
|
|
501
|
+
|
|
502
|
+
# Sort descending by score
|
|
503
|
+
scored_tasks.sort(key=lambda item: item[0], reverse=True)
|
|
504
|
+
|
|
505
|
+
results: list[dict[str, Any]] = []
|
|
506
|
+
for _, task, matched in scored_tasks[:limit]:
|
|
507
|
+
d = task.model_dump()
|
|
508
|
+
d["is_device_matched"] = matched
|
|
509
|
+
results.append(d)
|
|
510
|
+
|
|
511
|
+
return results
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
task_recommendation_engine = TaskRecommendationEngine()
|