harmonyrun 0.4.9__py3-none-any.whl
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.
- harmonyrun/__init__.py +186 -0
- harmonyrun/__main__.py +8 -0
- harmonyrun/agent/__init__.py +5 -0
- harmonyrun/agent/events/__init__.py +18 -0
- harmonyrun/agent/events/_render.py +258 -0
- harmonyrun/agent/events/handler.py +36 -0
- harmonyrun/agent/events/render_registry.py +41 -0
- harmonyrun/agent/execution/__init__.py +27 -0
- harmonyrun/agent/execution/anti_loop.py +410 -0
- harmonyrun/agent/execution/base_agent.py +991 -0
- harmonyrun/agent/execution/failure_classifier.py +332 -0
- harmonyrun/agent/execution/formatting.py +23 -0
- harmonyrun/agent/execution/handle.py +124 -0
- harmonyrun/agent/execution/interventions.py +89 -0
- harmonyrun/agent/execution/thresholds.py +99 -0
- harmonyrun/agent/fast_agent/__init__.py +3 -0
- harmonyrun/agent/fast_agent/fast_agent.py +972 -0
- harmonyrun/agent/harmony/__init__.py +29 -0
- harmonyrun/agent/harmony/harmony_agent.py +1413 -0
- harmonyrun/agent/harmony/resources.py +10 -0
- harmonyrun/agent/harmony/services.py +358 -0
- harmonyrun/agent/llm/__init__.py +70 -0
- harmonyrun/agent/llm/cache.py +72 -0
- harmonyrun/agent/llm/inference.py +359 -0
- harmonyrun/agent/llm/introspect.py +36 -0
- harmonyrun/agent/llm/loader.py +274 -0
- harmonyrun/agent/llm/messages.py +144 -0
- harmonyrun/agent/llm/picker.py +253 -0
- harmonyrun/agent/llm/usage.py +203 -0
- harmonyrun/agent/manager/__init__.py +9 -0
- harmonyrun/agent/manager/base_manager.py +215 -0
- harmonyrun/agent/manager/executor_agent.py +409 -0
- harmonyrun/agent/manager/manager_agent.py +487 -0
- harmonyrun/agent/memory/__init__.py +56 -0
- harmonyrun/agent/memory/distiller.py +164 -0
- harmonyrun/agent/memory/keys.py +21 -0
- harmonyrun/agent/memory/models.py +108 -0
- harmonyrun/agent/memory/recall.py +39 -0
- harmonyrun/agent/memory/store.py +158 -0
- harmonyrun/agent/oneflows/__init__.py +11 -0
- harmonyrun/agent/oneflows/app_matcher.py +85 -0
- harmonyrun/agent/oneflows/app_starter_workflow.py +177 -0
- harmonyrun/agent/oneflows/structured_output_agent.py +80 -0
- harmonyrun/agent/prompt/__init__.py +114 -0
- harmonyrun/agent/prompt/_env.py +45 -0
- harmonyrun/agent/prompt/constants.py +60 -0
- harmonyrun/agent/prompt/packing.py +367 -0
- harmonyrun/agent/prompt/resolver.py +68 -0
- harmonyrun/agent/prompt/state_user.py +402 -0
- harmonyrun/agent/prompt/system/__init__.py +27 -0
- harmonyrun/agent/prompt/system/executor.py +44 -0
- harmonyrun/agent/prompt/system/fast_agent.py +64 -0
- harmonyrun/agent/prompt/system/manager.py +194 -0
- harmonyrun/agent/prompt/templates/executor/README.md +39 -0
- harmonyrun/agent/prompt/templates/executor/system.en.j2 +63 -0
- harmonyrun/agent/prompt/templates/executor/system.zh.j2 +63 -0
- harmonyrun/agent/prompt/templates/executor/user.en.j2 +75 -0
- harmonyrun/agent/prompt/templates/executor/user.zh.j2 +75 -0
- harmonyrun/agent/prompt/templates/fast_agent/README.md +53 -0
- harmonyrun/agent/prompt/templates/fast_agent/system.en.j2 +157 -0
- harmonyrun/agent/prompt/templates/fast_agent/system.zh.j2 +156 -0
- harmonyrun/agent/prompt/templates/manager/README.md +26 -0
- harmonyrun/agent/prompt/templates/manager/system.en.j2 +164 -0
- harmonyrun/agent/prompt/templates/manager/system.zh.j2 +164 -0
- harmonyrun/agent/prompt/user_blocks/__init__.py +53 -0
- harmonyrun/agent/prompt/user_blocks/delta_trail.py +182 -0
- harmonyrun/agent/prompt/user_blocks/device_state.py +306 -0
- harmonyrun/agent/prompt/user_blocks/executor_user.py +57 -0
- harmonyrun/agent/prompt/user_blocks/goal.py +142 -0
- harmonyrun/agent/prompt/user_blocks/history.py +162 -0
- harmonyrun/agent/prompt/user_blocks/labels.py +24 -0
- harmonyrun/agent/prompt/user_blocks/next_step.py +32 -0
- harmonyrun/agent/prompt/user_blocks/task_plan.py +50 -0
- harmonyrun/agent/state/__init__.py +72 -0
- harmonyrun/agent/state/flow_states.py +85 -0
- harmonyrun/agent/state/observations.py +202 -0
- harmonyrun/agent/state/state.py +402 -0
- harmonyrun/agent/state/state_components.py +204 -0
- harmonyrun/agent/tools/__init__.py +49 -0
- harmonyrun/agent/tools/actions.py +653 -0
- harmonyrun/agent/tools/context.py +44 -0
- harmonyrun/agent/tools/dispatcher.py +85 -0
- harmonyrun/agent/tools/gestures.py +120 -0
- harmonyrun/agent/tools/registry.py +501 -0
- harmonyrun/agent/tools/result.py +16 -0
- harmonyrun/agent/tools/schemas.py +276 -0
- harmonyrun/app_cards/__init__.py +55 -0
- harmonyrun/app_cards/app_card_provider.py +26 -0
- harmonyrun/app_cards/loading.py +69 -0
- harmonyrun/app_cards/provider_factory.py +60 -0
- harmonyrun/app_cards/providers/__init__.py +7 -0
- harmonyrun/app_cards/providers/composite_provider.py +104 -0
- harmonyrun/app_cards/providers/local_provider.py +113 -0
- harmonyrun/app_cards/providers/server_provider.py +120 -0
- harmonyrun/app_cards/suite_resolve.py +112 -0
- harmonyrun/app_cards/writing.py +74 -0
- harmonyrun/batch/__init__.py +49 -0
- harmonyrun/batch/execution/__init__.py +34 -0
- harmonyrun/batch/execution/device_gate.py +73 -0
- harmonyrun/batch/execution/driver_factory.py +60 -0
- harmonyrun/batch/execution/phase.py +152 -0
- harmonyrun/batch/execution/resources.py +169 -0
- harmonyrun/batch/execution/runner.py +687 -0
- harmonyrun/batch/execution/scheduler.py +159 -0
- harmonyrun/batch/loading/__init__.py +29 -0
- harmonyrun/batch/loading/levels.py +23 -0
- harmonyrun/batch/loading/loader.py +285 -0
- harmonyrun/batch/loading/prompts/README.md +29 -0
- harmonyrun/batch/loading/prompts/__init__.py +114 -0
- harmonyrun/batch/loading/prompts/agent_goal/en.j2 +22 -0
- harmonyrun/batch/loading/prompts/agent_goal/zh.j2 +23 -0
- harmonyrun/batch/loading/prompts/postcondition/en.j2 +8 -0
- harmonyrun/batch/loading/prompts/postcondition/zh.j2 +8 -0
- harmonyrun/batch/loading/prompts/precondition/en.j2 +8 -0
- harmonyrun/batch/loading/prompts/precondition/zh.j2 +8 -0
- harmonyrun/batch/loading/prompts/test_execution/en.j2 +13 -0
- harmonyrun/batch/loading/prompts/test_execution/zh.j2 +13 -0
- harmonyrun/batch/loading/validation.py +60 -0
- harmonyrun/batch/models.py +149 -0
- harmonyrun/batch/reporting/__init__.py +49 -0
- harmonyrun/batch/reporting/_common.py +26 -0
- harmonyrun/batch/reporting/html_report.py +84 -0
- harmonyrun/batch/reporting/json_report.py +97 -0
- harmonyrun/batch/reporting/live.py +323 -0
- harmonyrun/batch/reporting/paths.py +37 -0
- harmonyrun/batch/reporting/templates/report.html +1191 -0
- harmonyrun/batch/reporting/viewer.py +309 -0
- harmonyrun/batch/schemas/report.schema.json +123 -0
- harmonyrun/batch/schemas/test_suite.schema.json +129 -0
- harmonyrun/batch/suite_runner.py +487 -0
- harmonyrun/cli/__init__.py +9 -0
- harmonyrun/cli/_capture.py +202 -0
- harmonyrun/cli/_logging.py +21 -0
- harmonyrun/cli/_runtime.py +55 -0
- harmonyrun/cli/feedback.py +60 -0
- harmonyrun/cli/main.py +332 -0
- harmonyrun/cli/mcp.py +219 -0
- harmonyrun/cli/memory_cmd.py +145 -0
- harmonyrun/cli/run.py +150 -0
- harmonyrun/cli/view.py +37 -0
- harmonyrun/config/__init__.py +88 -0
- harmonyrun/config/config_example.yaml +96 -0
- harmonyrun/config/manager.py +322 -0
- harmonyrun/config/paths.py +66 -0
- harmonyrun/config/schema/__init__.py +32 -0
- harmonyrun/config/schema/agent.py +182 -0
- harmonyrun/config/schema/llm.py +28 -0
- harmonyrun/config/schema/mcp.py +18 -0
- harmonyrun/config/schema/overrides.py +262 -0
- harmonyrun/config/schema/root.py +247 -0
- harmonyrun/config/schema/runtime.py +121 -0
- harmonyrun/diagnostics/__init__.py +13 -0
- harmonyrun/diagnostics/checks.py +498 -0
- harmonyrun/diagnostics/results.py +64 -0
- harmonyrun/diagnostics/runner.py +130 -0
- harmonyrun/env_keys.py +80 -0
- harmonyrun/events/__init__.py +34 -0
- harmonyrun/events/bus.py +83 -0
- harmonyrun/events/model/__init__.py +122 -0
- harmonyrun/events/model/agent.py +378 -0
- harmonyrun/events/model/base.py +73 -0
- harmonyrun/events/model/catalog.py +32 -0
- harmonyrun/events/model/device.py +129 -0
- harmonyrun/events/model/llm.py +53 -0
- harmonyrun/events/transports/__init__.py +15 -0
- harmonyrun/events/transports/langgraph.py +85 -0
- harmonyrun/farm/CLAUDE.md +280 -0
- harmonyrun/farm/__init__.py +27 -0
- harmonyrun/farm/_bootstrap.py +38 -0
- harmonyrun/farm/cli.py +397 -0
- harmonyrun/farm/endpoint.py +36 -0
- harmonyrun/farm/remote_driver.py +426 -0
- harmonyrun/farm/server/__init__.py +3 -0
- harmonyrun/farm/server/app.py +266 -0
- harmonyrun/farm/server/archive.py +141 -0
- harmonyrun/farm/server/auth.py +41 -0
- harmonyrun/farm/server/dispatch.py +155 -0
- harmonyrun/farm/server/pool.py +168 -0
- harmonyrun/farm/server/schemas.py +70 -0
- harmonyrun/farm/server/session.py +206 -0
- harmonyrun/feedback/__init__.py +13 -0
- harmonyrun/feedback/context.py +19 -0
- harmonyrun/feedback/discovery.py +85 -0
- harmonyrun/feedback/flow.py +111 -0
- harmonyrun/feedback/github.py +224 -0
- harmonyrun/feedback/issue.py +109 -0
- harmonyrun/feedback/packaging.py +76 -0
- harmonyrun/hdcutils/__init__.py +52 -0
- harmonyrun/hdcutils/agent/__init__.py +37 -0
- harmonyrun/hdcutils/agent/diagnostic.py +82 -0
- harmonyrun/hdcutils/agent/installer.py +189 -0
- harmonyrun/hdcutils/agent/spec.py +166 -0
- harmonyrun/hdcutils/agent/uitest_agent_1.1.12.so +0 -0
- harmonyrun/hdcutils/agent/uitest_agent_1.1.3.so +0 -0
- harmonyrun/hdcutils/agent/uitest_agent_1.1.5.so +0 -0
- harmonyrun/hdcutils/agent/uitest_agent_1.2.3.so +0 -0
- harmonyrun/hdcutils/agent/uitest_agent_x86_1.1.9.so +0 -0
- harmonyrun/hdcutils/client.py +108 -0
- harmonyrun/hdcutils/device/__init__.py +3 -0
- harmonyrun/hdcutils/device/apps.py +391 -0
- harmonyrun/hdcutils/device/core.py +181 -0
- harmonyrun/hdcutils/device/display.py +219 -0
- harmonyrun/hdcutils/device/display_state.py +589 -0
- harmonyrun/hdcutils/device/files.py +54 -0
- harmonyrun/hdcutils/device/forwards.py +90 -0
- harmonyrun/hdcutils/device/input.py +99 -0
- harmonyrun/hdcutils/device/recording.py +337 -0
- harmonyrun/hdcutils/device/screen.py +376 -0
- harmonyrun/hdcutils/device/system.py +82 -0
- harmonyrun/hdcutils/emulator/__init__.py +25 -0
- harmonyrun/hdcutils/emulator/scenario.py +297 -0
- harmonyrun/hdcutils/errors.py +41 -0
- harmonyrun/hdcutils/hilog.py +441 -0
- harmonyrun/hdcutils/keycode.py +361 -0
- harmonyrun/hdcutils/rpc/__init__.py +5 -0
- harmonyrun/hdcutils/rpc/client.py +462 -0
- harmonyrun/hdcutils/rpc/framing.py +103 -0
- harmonyrun/hdcutils/rpc/recovery.py +114 -0
- harmonyrun/hdcutils/transport/__init__.py +23 -0
- harmonyrun/hdcutils/transport/ports.py +128 -0
- harmonyrun/hdcutils/transport/recording.py +93 -0
- harmonyrun/hdcutils/transport/runner.py +204 -0
- harmonyrun/hdcutils/types.py +43 -0
- harmonyrun/hdcutils/version.py +12 -0
- harmonyrun/hdcutils/wake.py +258 -0
- harmonyrun/hypium/__init__.py +12 -0
- harmonyrun/hypium/action_mapper.py +159 -0
- harmonyrun/hypium/assertion_builder.py +280 -0
- harmonyrun/hypium/cli.py +140 -0
- harmonyrun/hypium/code_polisher.py +203 -0
- harmonyrun/hypium/component_resolver.py +221 -0
- harmonyrun/hypium/generator.py +306 -0
- harmonyrun/hypium/template.py +157 -0
- harmonyrun/hypium/trajectory_loader.py +280 -0
- harmonyrun/log_handlers.py +68 -0
- harmonyrun/mcp/__init__.py +28 -0
- harmonyrun/mcp/schemas.py +107 -0
- harmonyrun/mcp/server.py +96 -0
- harmonyrun/mcp/session.py +124 -0
- harmonyrun/mcp/tools/__init__.py +5 -0
- harmonyrun/mcp/tools/actions.py +218 -0
- harmonyrun/mcp/tools/connection.py +62 -0
- harmonyrun/mcp/tools/perception.py +233 -0
- harmonyrun/mcp/transport.py +19 -0
- harmonyrun/tools/__init__.py +79 -0
- harmonyrun/tools/contracts.py +39 -0
- harmonyrun/tools/driver/__init__.py +25 -0
- harmonyrun/tools/driver/app_catalog.py +113 -0
- harmonyrun/tools/driver/base.py +362 -0
- harmonyrun/tools/driver/factory.py +82 -0
- harmonyrun/tools/driver/harmony.py +1185 -0
- harmonyrun/tools/driver/perceiving.py +89 -0
- harmonyrun/tools/driver/proxy.py +228 -0
- harmonyrun/tools/driver/recording.py +603 -0
- harmonyrun/tools/driver/stealth.py +217 -0
- harmonyrun/tools/monitors/__init__.py +26 -0
- harmonyrun/tools/monitors/hilog_monitor.py +463 -0
- harmonyrun/tools/monitors/screen_recorder.py +208 -0
- harmonyrun/tools/perception/__init__.py +59 -0
- harmonyrun/tools/perception/processors/__init__.py +68 -0
- harmonyrun/tools/perception/processors/bundle_serde.py +139 -0
- harmonyrun/tools/perception/processors/constants.py +54 -0
- harmonyrun/tools/perception/processors/converters/__init__.py +5 -0
- harmonyrun/tools/perception/processors/converters/a11y_converter.py +116 -0
- harmonyrun/tools/perception/processors/extractors/__init__.py +5 -0
- harmonyrun/tools/perception/processors/extractors/nav_state.py +232 -0
- harmonyrun/tools/perception/processors/extractors/phone_state.py +399 -0
- harmonyrun/tools/perception/processors/filters/__init__.py +40 -0
- harmonyrun/tools/perception/processors/filters/base.py +24 -0
- harmonyrun/tools/perception/processors/filters/concise_filter.py +123 -0
- harmonyrun/tools/perception/processors/filters/detailed_filter.py +173 -0
- harmonyrun/tools/perception/processors/filters/passthrough_filter.py +23 -0
- harmonyrun/tools/perception/processors/formatters/__init__.py +10 -0
- harmonyrun/tools/perception/processors/formatters/base.py +39 -0
- harmonyrun/tools/perception/processors/formatters/indexed_formatter.py +941 -0
- harmonyrun/tools/perception/processors/pipeline.py +137 -0
- harmonyrun/tools/perception/processors/simplifiers/__init__.py +20 -0
- harmonyrun/tools/perception/processors/simplifiers/highlight_marker.py +58 -0
- harmonyrun/tools/perception/processors/simplifiers/system_ui_pruner.py +171 -0
- harmonyrun/tools/perception/processors/simplifiers/tree_simplifier.py +227 -0
- harmonyrun/tools/perception/processors/utils/__init__.py +42 -0
- harmonyrun/tools/perception/processors/utils/coordinate.py +25 -0
- harmonyrun/tools/perception/processors/utils/geometry.py +148 -0
- harmonyrun/tools/perception/processors/utils/semantic.py +58 -0
- harmonyrun/tools/perception/ui/__init__.py +47 -0
- harmonyrun/tools/perception/ui/config.py +80 -0
- harmonyrun/tools/perception/ui/offline.py +140 -0
- harmonyrun/tools/perception/ui/provider.py +341 -0
- harmonyrun/tools/perception/ui/screenshot.py +272 -0
- harmonyrun/tools/perception/ui/snapshot.py +178 -0
- harmonyrun/tools/perception/ui/state.py +261 -0
- harmonyrun/tools/perception/ui/stealth_state.py +106 -0
- harmonyrun/traces/__init__.py +50 -0
- harmonyrun/traces/case_report.py +274 -0
- harmonyrun/traces/folder.py +221 -0
- harmonyrun/traces/format.py +33 -0
- harmonyrun/traces/llm_log.py +212 -0
- harmonyrun/traces/recorder.py +293 -0
- harmonyrun/traces/serializers.py +138 -0
- harmonyrun/traces/sinks.py +60 -0
- harmonyrun/traces/telemetry/__init__.py +22 -0
- harmonyrun/traces/telemetry/tracker.py +180 -0
- harmonyrun/traces/templates/case_report.html +1908 -0
- harmonyrun/traces/writer.py +309 -0
- harmonyrun-0.4.9.dist-info/METADATA +401 -0
- harmonyrun-0.4.9.dist-info/RECORD +309 -0
- harmonyrun-0.4.9.dist-info/WHEEL +4 -0
- harmonyrun-0.4.9.dist-info/entry_points.txt +2 -0
- harmonyrun-0.4.9.dist-info/licenses/LICENSE +21 -0
harmonyrun/__init__.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HarmonyRun - A framework for automating HarmonyOS devices through LLM agents.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
from importlib import import_module
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
# Silence ``transformers`` "None of PyTorch, TensorFlow >= 2.0, or Flax have
|
|
14
|
+
# been found." advisory. ``langchain_core`` imports ``transformers`` purely to
|
|
15
|
+
# pull in ``GPT2TokenizerFast``; we never run model inference through it, so
|
|
16
|
+
# the advisory is noise. Must be set *before* the transitive transformers
|
|
17
|
+
# import (triggered by any langchain import), hence done here at package root.
|
|
18
|
+
os.environ.setdefault("TRANSFORMERS_NO_ADVISORY_WARNINGS", "1")
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
__version__ = version("harmonyrun")
|
|
22
|
+
except PackageNotFoundError:
|
|
23
|
+
# Running from a checkout without installing the distribution (e.g. scripts).
|
|
24
|
+
__version__ = "0.0.0+unknown"
|
|
25
|
+
|
|
26
|
+
# Attach a default CLILogHandler so that every consumer (CLI, SDK, tools-only)
|
|
27
|
+
# gets visible output without explicit setup. CLI may replace this via
|
|
28
|
+
# ``configure_logging()``.
|
|
29
|
+
_logger = logging.getLogger("harmonyrun")
|
|
30
|
+
try:
|
|
31
|
+
from harmonyrun.log_handlers import CLILogHandler
|
|
32
|
+
|
|
33
|
+
_logger.addHandler(CLILogHandler())
|
|
34
|
+
_logger.setLevel(logging.INFO)
|
|
35
|
+
_logger.propagate = False
|
|
36
|
+
except ImportError:
|
|
37
|
+
# e.g. ``rich`` not installed — skip; subpackages like ``hdcutils`` still import.
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Type checking imports for static analyzers
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from harmonyrun.agent.harmony import HarmonyAgent
|
|
43
|
+
from harmonyrun.events import ResultEvent
|
|
44
|
+
from harmonyrun.agent.llm import load_llm
|
|
45
|
+
from harmonyrun.agent.state import StepObservation
|
|
46
|
+
from harmonyrun.events import FastAgentToolCallEvent
|
|
47
|
+
from harmonyrun.config import (
|
|
48
|
+
AgentConfig,
|
|
49
|
+
AppCardConfig,
|
|
50
|
+
ConfigManager,
|
|
51
|
+
DeviceConfig,
|
|
52
|
+
HarmonyRunConfig,
|
|
53
|
+
ExecutorConfig,
|
|
54
|
+
FastAgentConfig,
|
|
55
|
+
LLMProfile,
|
|
56
|
+
LoggingConfig,
|
|
57
|
+
ManagerConfig,
|
|
58
|
+
ToolsConfig,
|
|
59
|
+
)
|
|
60
|
+
from harmonyrun.env_keys import load_runtime_dotenv
|
|
61
|
+
from harmonyrun.hdcutils import hdc
|
|
62
|
+
from harmonyrun.mcp import create_server as create_mcp_server
|
|
63
|
+
from harmonyrun.mcp import serve_stdio as serve_mcp_stdio
|
|
64
|
+
from harmonyrun.tools.driver.base import DeviceDriver
|
|
65
|
+
from harmonyrun.tools.driver.harmony import HarmonyDriver
|
|
66
|
+
from harmonyrun.tools.driver.recording import RecordingDriver
|
|
67
|
+
from harmonyrun.tools.perception.ui.provider import StateProvider as TreeStateProvider
|
|
68
|
+
from harmonyrun.tools.perception.ui.state import UIState
|
|
69
|
+
from harmonyrun.tools.perception.ui.offline import OfflineStateProvider
|
|
70
|
+
from harmonyrun.tools.perception.processors import load_capture_bundle, load_device_info
|
|
71
|
+
from harmonyrun.config.schema.overrides import apply_overrides
|
|
72
|
+
from harmonyrun.traces.case_report import render_case_html
|
|
73
|
+
from harmonyrun.traces.folder import TraceFolder
|
|
74
|
+
from harmonyrun.tools.driver.factory import build_harmony_driver
|
|
75
|
+
from harmonyrun.batch.loading.validation import iter_json_schema_errors
|
|
76
|
+
from harmonyrun.batch.reporting import find_latest_suite_run
|
|
77
|
+
from harmonyrun.batch.loading.loader import load_test_cases
|
|
78
|
+
from harmonyrun.batch.loading.prompts import build_test_execution_goal
|
|
79
|
+
from harmonyrun.batch.models import SuiteMeta, TestCase, TestCaseResult
|
|
80
|
+
from harmonyrun.batch.execution.runner import run_test_case
|
|
81
|
+
|
|
82
|
+
_LAZY_EXPORTS: dict[str, tuple[str, str]] = {
|
|
83
|
+
"ResultEvent": ("harmonyrun.events", "ResultEvent"),
|
|
84
|
+
"HarmonyAgent": ("harmonyrun.agent.harmony", "HarmonyAgent"),
|
|
85
|
+
"load_llm": ("harmonyrun.agent.llm", "load_llm"),
|
|
86
|
+
"StepObservation": ("harmonyrun.agent.state", "StepObservation"),
|
|
87
|
+
"FastAgentToolCallEvent": ("harmonyrun.events", "FastAgentToolCallEvent"),
|
|
88
|
+
"AgentConfig": ("harmonyrun.config", "AgentConfig"),
|
|
89
|
+
"AppCardConfig": ("harmonyrun.config", "AppCardConfig"),
|
|
90
|
+
"FastAgentConfig": ("harmonyrun.config", "FastAgentConfig"),
|
|
91
|
+
"DeviceConfig": ("harmonyrun.config", "DeviceConfig"),
|
|
92
|
+
"HarmonyRunConfig": ("harmonyrun.config", "HarmonyRunConfig"),
|
|
93
|
+
"ExecutorConfig": ("harmonyrun.config", "ExecutorConfig"),
|
|
94
|
+
"LLMProfile": ("harmonyrun.config", "LLMProfile"),
|
|
95
|
+
"LoggingConfig": ("harmonyrun.config", "LoggingConfig"),
|
|
96
|
+
"ManagerConfig": ("harmonyrun.config", "ManagerConfig"),
|
|
97
|
+
"ToolsConfig": ("harmonyrun.config", "ToolsConfig"),
|
|
98
|
+
"ConfigManager": ("harmonyrun.config", "ConfigManager"),
|
|
99
|
+
"load_runtime_dotenv": ("harmonyrun.env_keys", "load_runtime_dotenv"),
|
|
100
|
+
"hdc": ("harmonyrun.hdcutils", "hdc"),
|
|
101
|
+
"DeviceDriver": ("harmonyrun.tools", "DeviceDriver"),
|
|
102
|
+
"TreeStateProvider": ("harmonyrun.tools", "TreeStateProvider"),
|
|
103
|
+
"HarmonyDriver": ("harmonyrun.tools", "HarmonyDriver"),
|
|
104
|
+
"RecordingDriver": ("harmonyrun.tools", "RecordingDriver"),
|
|
105
|
+
"UIState": ("harmonyrun.tools.perception.ui.state", "UIState"),
|
|
106
|
+
"OfflineStateProvider": ("harmonyrun.tools.perception.ui.offline", "OfflineStateProvider"),
|
|
107
|
+
"load_capture_bundle": ("harmonyrun.tools.perception.processors", "load_capture_bundle"),
|
|
108
|
+
"load_device_info": ("harmonyrun.tools.perception.processors", "load_device_info"),
|
|
109
|
+
"apply_overrides": ("harmonyrun.config.schema.overrides", "apply_overrides"),
|
|
110
|
+
"create_mcp_server": ("harmonyrun.mcp", "create_server"),
|
|
111
|
+
"serve_mcp_stdio": ("harmonyrun.mcp", "serve_stdio"),
|
|
112
|
+
"render_case_html": ("harmonyrun.traces.case_report", "render_case_html"),
|
|
113
|
+
"TraceFolder": ("harmonyrun.traces.folder", "TraceFolder"),
|
|
114
|
+
"build_harmony_driver": ("harmonyrun.tools.driver.factory", "build_harmony_driver"),
|
|
115
|
+
"iter_json_schema_errors": ("harmonyrun.batch.loading.validation", "iter_json_schema_errors"),
|
|
116
|
+
"find_latest_suite_run": ("harmonyrun.batch.reporting", "find_latest_suite_run"),
|
|
117
|
+
"load_test_cases": ("harmonyrun.batch.loading.loader", "load_test_cases"),
|
|
118
|
+
"SuiteMeta": ("harmonyrun.batch.models", "SuiteMeta"),
|
|
119
|
+
"TestCase": ("harmonyrun.batch.models", "TestCase"),
|
|
120
|
+
"TestCaseResult": ("harmonyrun.batch.models", "TestCaseResult"),
|
|
121
|
+
"DeviceRequirements": ("harmonyrun.batch.models", "DeviceRequirements"),
|
|
122
|
+
"run_test_case": ("harmonyrun.batch.execution.runner", "run_test_case"),
|
|
123
|
+
"build_test_execution_goal": ("harmonyrun.batch.loading.prompts", "build_test_execution_goal"),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def __getattr__(name: str) -> object:
|
|
128
|
+
spec = _LAZY_EXPORTS.get(name)
|
|
129
|
+
if spec is None:
|
|
130
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
131
|
+
mod_name, attr = spec
|
|
132
|
+
return getattr(import_module(mod_name), attr)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def __dir__() -> list[str]:
|
|
136
|
+
return sorted({*globals().keys(), *_LAZY_EXPORTS.keys(), "__version__"})
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# Make main components available at package level
|
|
140
|
+
__all__ = [
|
|
141
|
+
# Agent
|
|
142
|
+
"HarmonyAgent",
|
|
143
|
+
"load_llm",
|
|
144
|
+
"ResultEvent",
|
|
145
|
+
# Tools / Drivers
|
|
146
|
+
"DeviceDriver",
|
|
147
|
+
"TreeStateProvider",
|
|
148
|
+
"HarmonyDriver",
|
|
149
|
+
"RecordingDriver",
|
|
150
|
+
# Configuration
|
|
151
|
+
"ConfigManager",
|
|
152
|
+
"HarmonyRunConfig",
|
|
153
|
+
"AgentConfig",
|
|
154
|
+
"FastAgentConfig",
|
|
155
|
+
"ManagerConfig",
|
|
156
|
+
"ExecutorConfig",
|
|
157
|
+
"AppCardConfig",
|
|
158
|
+
"DeviceConfig",
|
|
159
|
+
"LoggingConfig",
|
|
160
|
+
"ToolsConfig",
|
|
161
|
+
"LLMProfile",
|
|
162
|
+
# Env / secrets
|
|
163
|
+
"load_runtime_dotenv",
|
|
164
|
+
# Device enumeration / connection (async hdc client singleton)
|
|
165
|
+
"hdc",
|
|
166
|
+
# MCP
|
|
167
|
+
"create_mcp_server",
|
|
168
|
+
"serve_mcp_stdio",
|
|
169
|
+
# Agent internals (advanced users)
|
|
170
|
+
"FastAgentToolCallEvent",
|
|
171
|
+
# Perception facade (offline eval)
|
|
172
|
+
"OfflineStateProvider",
|
|
173
|
+
"load_capture_bundle",
|
|
174
|
+
"load_device_info",
|
|
175
|
+
# Config override engine
|
|
176
|
+
"apply_overrides",
|
|
177
|
+
# Traces
|
|
178
|
+
"render_case_html",
|
|
179
|
+
"TraceFolder",
|
|
180
|
+
# Driver factory
|
|
181
|
+
"build_harmony_driver",
|
|
182
|
+
# Schema validation
|
|
183
|
+
"iter_json_schema_errors",
|
|
184
|
+
# Batch helpers
|
|
185
|
+
"find_latest_suite_run",
|
|
186
|
+
]
|
harmonyrun/__main__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Agent-side event **consumption** + wiring.
|
|
2
|
+
|
|
3
|
+
The event *vocabulary*, the ``EventBus`` and the stream ``WorkflowEventEmitter``
|
|
4
|
+
all live in the ``harmonyrun.events`` leaf — import them from there. This
|
|
5
|
+
package holds only the agent-layer consumer side:
|
|
6
|
+
|
|
7
|
+
- ``handler`` — ``ConsoleSink``, the stream-transport console renderer.
|
|
8
|
+
- ``render_registry`` — the type→renderer dispatch table that each sub-agent's
|
|
9
|
+
``_render`` module self-registers into (IoC; the registry imports no concrete
|
|
10
|
+
event class).
|
|
11
|
+
- ``_render`` — coordination + ``traces`` device-event renderers.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from harmonyrun.agent.events.handler import ConsoleSink
|
|
17
|
+
|
|
18
|
+
__all__ = ["ConsoleSink"]
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Log renderers for every workflow event, centralized in one module.
|
|
2
|
+
|
|
3
|
+
The events themselves live in the ``harmonyrun.events`` vocabulary leaf; their
|
|
4
|
+
*consumer-side* CLI rendering belongs here in the agent layer. Renderers used to
|
|
5
|
+
be scattered across each sub-agent package ("co-located with the event"), but
|
|
6
|
+
once the event vocabulary sank to the ``events/`` leaf that justification went
|
|
7
|
+
away — the per-agent ``_render`` modules imported only the leaf event classes
|
|
8
|
+
plus the registry, never their own package. So all renderers now live here and
|
|
9
|
+
register on this module's import (triggered by ``handler.py``), which also makes
|
|
10
|
+
registration deterministic instead of depending on which sub-agent packages got
|
|
11
|
+
imported first.
|
|
12
|
+
|
|
13
|
+
Grouping below is by *publisher* (orchestrator/coordination + traces, then each
|
|
14
|
+
sub-agent's own telemetry). This module imports only ``harmonyrun.events`` event
|
|
15
|
+
classes and ``render_registry`` — never a sub-agent package — so the IoC rule
|
|
16
|
+
(registry/ConsoleSink never reach up into ``fast_agent``/``manager``/``executor``)
|
|
17
|
+
still holds.
|
|
18
|
+
|
|
19
|
+
Rendering strings are byte-identical to the historical isinstance chain — do not
|
|
20
|
+
reword without updating ``tests/unit/agent/events/test_handler_render.py`` and
|
|
21
|
+
running an on-device smoke.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
|
|
28
|
+
from harmonyrun.agent.events.render_registry import register
|
|
29
|
+
from harmonyrun.events import (
|
|
30
|
+
ActiveInterventionEvent,
|
|
31
|
+
ExecutorActionEvent,
|
|
32
|
+
ExecutorActionResultEvent,
|
|
33
|
+
ExecutorResponseEvent,
|
|
34
|
+
ExecutorResultEvent,
|
|
35
|
+
FastAgentEndEvent,
|
|
36
|
+
FastAgentExecuteEvent,
|
|
37
|
+
FastAgentInputEvent,
|
|
38
|
+
FastAgentOutputEvent,
|
|
39
|
+
FastAgentResponseEvent,
|
|
40
|
+
FastAgentResultEvent,
|
|
41
|
+
FastAgentToolCallEvent,
|
|
42
|
+
FinalizeEvent,
|
|
43
|
+
ManagerContextEvent,
|
|
44
|
+
ManagerPlanDetailsEvent,
|
|
45
|
+
ManagerResponseEvent,
|
|
46
|
+
RecordUIStateEvent,
|
|
47
|
+
ScreenshotEvent,
|
|
48
|
+
StructuredFailureEvent,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
logger = logging.getLogger("harmonyrun")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---- coordination (orchestrator-published) + traces/device ----
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _render_screenshot(event: ScreenshotEvent) -> None:
|
|
58
|
+
logger.debug("📸 Taking screenshot...")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _render_record_ui_state(event: RecordUIStateEvent) -> None:
|
|
62
|
+
logger.debug("✏️ Recording UI state")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _render_executor_result(event: ExecutorResultEvent) -> None:
|
|
66
|
+
logger.debug("Step complete", extra={"color": "magenta"})
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _render_fast_agent_execute(event: FastAgentExecuteEvent) -> None:
|
|
70
|
+
logger.debug("🔧 Starting task execution...", extra={"color": "magenta"})
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _render_fast_agent_result(event: FastAgentResultEvent) -> None:
|
|
74
|
+
if event.success:
|
|
75
|
+
logger.debug(f"Task result: {event.reason}", extra={"color": "green"})
|
|
76
|
+
else:
|
|
77
|
+
logger.debug(f"Task failed: {event.reason}", extra={"color": "red"})
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _render_finalize(event: FinalizeEvent) -> None:
|
|
81
|
+
if event.success:
|
|
82
|
+
logger.info(f"🎉 Goal achieved: {event.reason}", extra={"color": "green"})
|
|
83
|
+
else:
|
|
84
|
+
logger.info(f"❌ Goal failed: {event.reason}", extra={"color": "red"})
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ---- fast_agent telemetry ----
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _render_input(event: FastAgentInputEvent) -> None:
|
|
91
|
+
logger.debug("💬 Task input received...")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _render_response(event: FastAgentResponseEvent) -> None:
|
|
95
|
+
logger.debug("FastAgent response", extra={"color": "magenta"})
|
|
96
|
+
if event.thought:
|
|
97
|
+
preview = (
|
|
98
|
+
event.thought[:150] + "..."
|
|
99
|
+
if len(event.thought) > 150
|
|
100
|
+
else event.thought
|
|
101
|
+
)
|
|
102
|
+
logger.debug(f"🧠 Thinking: {preview}", extra={"color": "cyan"})
|
|
103
|
+
if event.code:
|
|
104
|
+
logger.debug("💻 Executing action code", extra={"color": "yellow"})
|
|
105
|
+
logger.debug(f"{event.code}", extra={"color": "blue"})
|
|
106
|
+
usage_line = event.usage.summary_line() if event.usage else ""
|
|
107
|
+
if usage_line:
|
|
108
|
+
logger.debug(
|
|
109
|
+
f"📊 fast_agent tokens — {usage_line}",
|
|
110
|
+
extra={"color": "cyan"},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _render_tool_call(event: FastAgentToolCallEvent) -> None:
|
|
115
|
+
logger.debug("⚡ Executing tool calls...", extra={"color": "yellow"})
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _render_output(event: FastAgentOutputEvent) -> None:
|
|
119
|
+
if event.output:
|
|
120
|
+
output = str(event.output)
|
|
121
|
+
preview = output[:100] + "..." if len(output) > 100 else output
|
|
122
|
+
if "Error" in output or "Exception" in output:
|
|
123
|
+
logger.debug(f"❌ Action error: {preview}", extra={"color": "red"})
|
|
124
|
+
else:
|
|
125
|
+
logger.debug(f"⚡ Action result: {preview}", extra={"color": "green"})
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _render_end(event: FastAgentEndEvent) -> None:
|
|
129
|
+
status = "done" if event.success else "failed"
|
|
130
|
+
color = "green" if event.success else "red"
|
|
131
|
+
logger.debug(
|
|
132
|
+
f"■ {status}: {event.reason} ({event.tool_call_count} runs)",
|
|
133
|
+
extra={"color": color},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _render_structured_failure(event: StructuredFailureEvent) -> None:
|
|
138
|
+
logger.warning(
|
|
139
|
+
f"🧩 Structured failure [{event.mode}/{event.severity}]: "
|
|
140
|
+
f"{event.conclusion} (target={event.target_ref})",
|
|
141
|
+
extra={"color": "red"},
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _render_active_intervention(event: ActiveInterventionEvent) -> None:
|
|
146
|
+
logger.warning(
|
|
147
|
+
f"🛠️ Active intervention [{event.kind}] → {event.target} "
|
|
148
|
+
f"(#{event.count}): {event.reason}",
|
|
149
|
+
extra={"color": "yellow"},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ---- manager telemetry ----
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _render_context(event: ManagerContextEvent) -> None:
|
|
157
|
+
logger.debug("🧠 Manager preparing context...")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _render_manager_response(event: ManagerResponseEvent) -> None:
|
|
161
|
+
logger.debug("📥 Manager received LLM response")
|
|
162
|
+
usage_line = event.usage.summary_line() if event.usage else ""
|
|
163
|
+
if usage_line:
|
|
164
|
+
logger.debug(f"📊 manager tokens — {usage_line}", extra={"color": "cyan"})
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _render_plan_details(event: ManagerPlanDetailsEvent) -> None:
|
|
168
|
+
if event.thought:
|
|
169
|
+
preview = (
|
|
170
|
+
event.thought[:120] + "..."
|
|
171
|
+
if len(event.thought) > 120
|
|
172
|
+
else event.thought
|
|
173
|
+
)
|
|
174
|
+
logger.debug(f"💭 Thought: {preview}", extra={"color": "cyan"})
|
|
175
|
+
if event.subgoal:
|
|
176
|
+
preview = (
|
|
177
|
+
event.subgoal[:150] + "..."
|
|
178
|
+
if len(event.subgoal) > 150
|
|
179
|
+
else event.subgoal
|
|
180
|
+
)
|
|
181
|
+
logger.debug(f"📋 Next step: {preview}", extra={"color": "yellow"})
|
|
182
|
+
if event.answer:
|
|
183
|
+
preview = (
|
|
184
|
+
event.answer[:200] + "..."
|
|
185
|
+
if len(event.answer) > 200
|
|
186
|
+
else event.answer
|
|
187
|
+
)
|
|
188
|
+
logger.debug(f"💬 Answer: {preview}", extra={"color": "green"})
|
|
189
|
+
if event.plan:
|
|
190
|
+
logger.debug(f"▸ {event.plan}", extra={"color": "yellow"})
|
|
191
|
+
if event.memory_update:
|
|
192
|
+
mem = event.memory_update
|
|
193
|
+
mem_preview = mem[:100] + "..." if len(mem) > 100 else mem
|
|
194
|
+
logger.debug(
|
|
195
|
+
f"🧠 Memory: {mem_preview}",
|
|
196
|
+
extra={"color": "cyan"},
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# ---- executor telemetry ----
|
|
201
|
+
# ``ExecutorContextEvent`` is intentionally NOT registered — the former
|
|
202
|
+
# ``EventHandler`` had no branch for it, so it falls through to the generic
|
|
203
|
+
# fallback exactly as before.
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _render_executor_response(event: ExecutorResponseEvent) -> None:
|
|
207
|
+
usage_line = event.usage.summary_line() if event.usage else ""
|
|
208
|
+
if usage_line:
|
|
209
|
+
logger.debug(
|
|
210
|
+
f"📊 executor tokens — {usage_line}",
|
|
211
|
+
extra={"color": "cyan"},
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _render_action(event: ExecutorActionEvent) -> None:
|
|
216
|
+
if event.description:
|
|
217
|
+
logger.debug(f"🎯 Action: {event.description}", extra={"color": "yellow"})
|
|
218
|
+
if event.thought:
|
|
219
|
+
preview = (
|
|
220
|
+
event.thought[:120] + "..."
|
|
221
|
+
if len(event.thought) > 120
|
|
222
|
+
else event.thought
|
|
223
|
+
)
|
|
224
|
+
logger.debug(f"💭 Reasoning: {preview}", extra={"color": "cyan"})
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _render_action_result(event: ExecutorActionResultEvent) -> None:
|
|
228
|
+
if event.success:
|
|
229
|
+
logger.debug(f"✅ {event.summary}", extra={"color": "green"})
|
|
230
|
+
else:
|
|
231
|
+
error_msg = event.error or "Unknown error"
|
|
232
|
+
logger.debug(f"❌ {event.summary} ({error_msg})", extra={"color": "red"})
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ---- registration (centralized; handler.py's import triggers all of it) ----
|
|
236
|
+
|
|
237
|
+
register(ScreenshotEvent, _render_screenshot)
|
|
238
|
+
register(RecordUIStateEvent, _render_record_ui_state)
|
|
239
|
+
register(ExecutorResultEvent, _render_executor_result)
|
|
240
|
+
register(FastAgentExecuteEvent, _render_fast_agent_execute)
|
|
241
|
+
register(FastAgentResultEvent, _render_fast_agent_result)
|
|
242
|
+
register(FinalizeEvent, _render_finalize)
|
|
243
|
+
|
|
244
|
+
register(FastAgentInputEvent, _render_input)
|
|
245
|
+
register(FastAgentResponseEvent, _render_response)
|
|
246
|
+
register(FastAgentToolCallEvent, _render_tool_call)
|
|
247
|
+
register(FastAgentOutputEvent, _render_output)
|
|
248
|
+
register(FastAgentEndEvent, _render_end)
|
|
249
|
+
register(StructuredFailureEvent, _render_structured_failure)
|
|
250
|
+
register(ActiveInterventionEvent, _render_active_intervention)
|
|
251
|
+
|
|
252
|
+
register(ManagerContextEvent, _render_context)
|
|
253
|
+
register(ManagerResponseEvent, _render_manager_response)
|
|
254
|
+
register(ManagerPlanDetailsEvent, _render_plan_details)
|
|
255
|
+
|
|
256
|
+
register(ExecutorResponseEvent, _render_executor_response)
|
|
257
|
+
register(ExecutorActionEvent, _render_action)
|
|
258
|
+
register(ExecutorActionResultEvent, _render_action_result)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Workflow event → logger translator shared by CLI and batch runner.
|
|
2
|
+
|
|
3
|
+
Pure dispatch: every renderer is centralized in ``_render.py`` (imported here
|
|
4
|
+
for its registration side-effect) and registered against an exact event type.
|
|
5
|
+
``handle`` just looks the renderer up and applies a generic fallback for unknown
|
|
6
|
+
events — so this module references no concrete event type and no sub-agent
|
|
7
|
+
package (the registry holds only ``type -> callable``).
|
|
8
|
+
|
|
9
|
+
Renderers emit ``logging`` calls with ``extra`` params (color, etc.); the
|
|
10
|
+
attached ``logging.Handler`` (typically CLILogHandler) does the rendering.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
# Import side-effect: registers every renderer (coordination/traces + all
|
|
16
|
+
# sub-agent telemetry, centralized in ``_render``).
|
|
17
|
+
import harmonyrun.agent.events._render # noqa: F401
|
|
18
|
+
from harmonyrun.agent.events.render_registry import render
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("harmonyrun")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConsoleSink:
|
|
24
|
+
"""Console-rendering sink (consumption ④) for the LangGraph stream transport.
|
|
25
|
+
|
|
26
|
+
Despite the name, this sink writes **no** console output itself — it only
|
|
27
|
+
translates workflow events into ``logging`` records (with ``extra`` params
|
|
28
|
+
such as ``color``) via the render registry, and the attached cli-layer
|
|
29
|
+
``CLILogHandler`` does the actual terminal rendering. No UI state tracking.
|
|
30
|
+
The CLI / batch runners consume ``AgentHandle.stream_events()`` and feed each
|
|
31
|
+
event to :meth:`handle`.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def handle(self, event) -> None:
|
|
35
|
+
if not render(event):
|
|
36
|
+
logger.debug(f"🔄 {event.__class__.__name__}")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Event → log-rendering dispatch registry (pure infra, knows no event types).
|
|
2
|
+
|
|
3
|
+
``EventHandler`` used to be a ~150-line ``isinstance`` chain that imported the
|
|
4
|
+
three sub-agents' telemetry events — an inversion (events/ is a shared *leaf*
|
|
5
|
+
infra layer, yet reached *up* into ``fast_agent`` / ``manager`` / ``executor``).
|
|
6
|
+
|
|
7
|
+
This registry inverts that control: all renderers live in ``_render.py`` (which
|
|
8
|
+
imports only ``harmonyrun.events`` event classes — never a sub-agent package)
|
|
9
|
+
and self-register here on import. The registry itself holds only
|
|
10
|
+
``type -> callable`` and imports no concrete event class, so neither it nor
|
|
11
|
+
``handler.py`` references any sub-agent package.
|
|
12
|
+
|
|
13
|
+
Lookup is exact-type (events are leaf classes; no subclass fan-out is needed),
|
|
14
|
+
matching the old ``isinstance`` chain's first-match-wins behaviour.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import Any, Callable
|
|
20
|
+
|
|
21
|
+
EventRenderer = Callable[[Any], None]
|
|
22
|
+
|
|
23
|
+
_RENDERERS: dict[type, EventRenderer] = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def register(event_type: type, renderer: EventRenderer) -> None:
|
|
27
|
+
"""Register the renderer for an exact event type (last registration wins)."""
|
|
28
|
+
_RENDERERS[event_type] = renderer
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def render(event: Any) -> bool:
|
|
32
|
+
"""Render ``event`` via its registered renderer.
|
|
33
|
+
|
|
34
|
+
Returns ``True`` if a renderer handled it, ``False`` otherwise (caller
|
|
35
|
+
applies the generic fallback). Exact-type lookup only.
|
|
36
|
+
"""
|
|
37
|
+
renderer = _RENDERERS.get(type(event))
|
|
38
|
+
if renderer is None:
|
|
39
|
+
return False
|
|
40
|
+
renderer(event)
|
|
41
|
+
return True
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Execution scaffolding shared by every agent that drives a LangGraph run.
|
|
2
|
+
|
|
3
|
+
Two public concepts live here:
|
|
4
|
+
|
|
5
|
+
- ``BaseExecutionAgent`` — the thin base class for ManagerAgent /
|
|
6
|
+
ExecutorAgent / FastAgent. Consolidates the cross-cutting code every
|
|
7
|
+
per-LLM-call agent needs: device-state capture, UI mirroring into the
|
|
8
|
+
shared state, hilog event draining, screenshot annotation, app-card
|
|
9
|
+
refresh, and usage recording. Anti-loop detection (``anti_loop`` /
|
|
10
|
+
``thresholds``) is part of this scaffolding.
|
|
11
|
+
- ``AgentHandle`` — the awaitable wrapper every agent's ``run()`` returns
|
|
12
|
+
over a compiled LangGraph (``await handle`` / ``handle.stream_events()``).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from harmonyrun.agent.execution.base_agent import (
|
|
16
|
+
BaseExecutionAgent,
|
|
17
|
+
build_record_ui_state_event,
|
|
18
|
+
)
|
|
19
|
+
from harmonyrun.agent.execution.formatting import compact_args_repr
|
|
20
|
+
from harmonyrun.agent.execution.handle import AgentHandle
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"BaseExecutionAgent",
|
|
24
|
+
"AgentHandle",
|
|
25
|
+
"compact_args_repr",
|
|
26
|
+
"build_record_ui_state_event",
|
|
27
|
+
]
|