feat: checkpoint device agent runtime milestones
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from core.models import Bounds, Scene, SceneElement
|
||||
from semantic.config import SemanticConfig, load_config
|
||||
from semantic.enricher import enrich_scene
|
||||
from semantic.llm_client import EnrichmentUnavailable
|
||||
|
||||
|
||||
class FakeClient:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
response: dict[str, Any] | None = None,
|
||||
error: Exception | None = None,
|
||||
) -> None:
|
||||
self.response = response
|
||||
self.error = error
|
||||
self.calls: list[tuple[dict[str, Any], float]] = []
|
||||
|
||||
def enrich(self, scene_json: dict[str, Any], *, timeout: float) -> dict[str, Any]:
|
||||
self.calls.append((scene_json, timeout))
|
||||
if self.error:
|
||||
raise self.error
|
||||
assert self.response is not None
|
||||
return self.response
|
||||
|
||||
|
||||
def _scene() -> Scene:
|
||||
return Scene(
|
||||
width=10,
|
||||
height=20,
|
||||
elements=[
|
||||
SceneElement(
|
||||
id="input",
|
||||
type="input",
|
||||
bounds=Bounds(1, 2, 3, 4),
|
||||
text="Message",
|
||||
),
|
||||
SceneElement(
|
||||
id="send",
|
||||
type="button",
|
||||
bounds=Bounds(5, 2, 3, 4),
|
||||
text="Send",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def _semantic_payload() -> dict[str, Any]:
|
||||
return {
|
||||
"page": "Chat",
|
||||
"intents": ["send a message"],
|
||||
"widgets": [
|
||||
{"element_id": "input", "purpose": "message text input"},
|
||||
{"element_id": "send", "purpose": "send message"},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_enrichment_config_is_disabled_by_default() -> None:
|
||||
assert load_config({}).enabled is False
|
||||
|
||||
|
||||
def test_enrich_scene_returns_semantic_scene_when_enabled() -> None:
|
||||
client = FakeClient(response=_semantic_payload())
|
||||
|
||||
result = enrich_scene(
|
||||
_scene(),
|
||||
client=client,
|
||||
config=SemanticConfig(enabled=True, timeout=2.5),
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.to_dict() == _semantic_payload()
|
||||
assert len(client.calls) == 1
|
||||
assert client.calls[0][1] == 2.5
|
||||
|
||||
|
||||
def test_enrich_scene_disabled_short_circuits_without_client_call() -> None:
|
||||
client = FakeClient(response=_semantic_payload())
|
||||
|
||||
result = enrich_scene(
|
||||
_scene(),
|
||||
client=client,
|
||||
config=SemanticConfig(enabled=False),
|
||||
)
|
||||
|
||||
assert result is None
|
||||
assert client.calls == []
|
||||
|
||||
|
||||
def test_enrich_scene_degrades_to_none_on_client_failure() -> None:
|
||||
client = FakeClient(error=EnrichmentUnavailable("rate limited"))
|
||||
|
||||
result = enrich_scene(
|
||||
_scene(),
|
||||
client=client,
|
||||
config=SemanticConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_enrich_scene_filters_dangling_element_ids() -> None:
|
||||
payload = _semantic_payload()
|
||||
payload["widgets"] = [
|
||||
{"element_id": "send", "purpose": "send message"},
|
||||
{"element_id": "missing", "purpose": "unknown control"},
|
||||
]
|
||||
client = FakeClient(response=payload)
|
||||
|
||||
result = enrich_scene(
|
||||
_scene(),
|
||||
client=client,
|
||||
config=SemanticConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.to_dict()["widgets"] == [
|
||||
{"element_id": "send", "purpose": "send message"}
|
||||
]
|
||||
|
||||
|
||||
def test_enrich_scene_returns_none_when_all_widgets_are_dangling() -> None:
|
||||
client = FakeClient(
|
||||
response={
|
||||
"page": "Chat",
|
||||
"intents": ["send a message"],
|
||||
"widgets": [{"element_id": "missing", "purpose": "unknown control"}],
|
||||
}
|
||||
)
|
||||
|
||||
result = enrich_scene(
|
||||
_scene(),
|
||||
client=client,
|
||||
config=SemanticConfig(enabled=True),
|
||||
)
|
||||
|
||||
assert result is None
|
||||
Reference in New Issue
Block a user