cockpit-agent/tests/test_ui_understanding.py

345 lines
12 KiB
Python
Raw Normal View History

2026-08-24 16:49:44 +08:00
import unittest
from cockpit_agent.common.enums import (
Domain,
Operation,
SelectionState,
Unit,
Zone,
)
from cockpit_agent.grounding.adapter import GroundingResult
from cockpit_agent.intent.schemas import SemanticAction
from cockpit_agent.perception.schemas import UIState
from cockpit_agent.perception.ui_understanding import ModelUIUnderstanding
class FakeVisionGenerator:
def __init__(self, output: str) -> None:
self.output = output
self.prompt = ""
self.image_path = ""
def generate(
self,
image_path: str,
prompt: str,
max_new_tokens: int = 256,
) -> str:
del max_new_tokens
self.image_path = image_path
self.prompt = prompt
return self.output
def temperature_intent() -> SemanticAction:
return SemanticAction(
domain=Domain.CLIMATE,
function="temperature",
zone=Zone.DRIVER,
operation=Operation.SET,
state=None,
value=23,
unit=Unit.CELSIUS,
)
def function_grounding(query: str = "主驾驶温度控制") -> GroundingResult:
return GroundingResult(
semantic_query=query,
bbox_relative=(100, 200, 400, 600),
bbox_pixel=(20, 20, 80, 60),
center_pixel=(50, 40),
image_width=200,
image_height=100,
raw_output='{"bbox_2d": [100, 200, 400, 600]}',
)
def understand(model: FakeVisionGenerator, intent: SemanticAction):
return ModelUIUnderstanding(model).understand(
"/tmp/function_roi.jpg",
intent,
function_grounding(),
)
class UIUnderstandingTest(unittest.TestCase):
def test_slider_schema_allows_nullable_observations(self) -> None:
state = UIState.from_dict(
{
"function": "tailgate_max_opening",
"zone": None,
"control_type": "slider",
"current_value": None,
"step_value": None,
"current_state": None,
"selection_state": None,
"min_value": None,
"max_value": None,
"options": None,
"track_visible": True,
"knob_visible": None,
"orientation": None,
}
)
self.assertIsNone(state.current_value)
self.assertIsNone(state.min_value)
self.assertIsNone(state.max_value)
self.assertTrue(state.track_visible)
self.assertIsNone(state.knob_visible)
def test_selection_state_schema_accepts_selected_and_unknown(self) -> None:
for selection_state in ("selected", "unknown"):
with self.subTest(selection_state=selection_state):
state = UIState.from_dict(
{
"function": "unlock_mode",
"zone": None,
"control_type": "segmented_control",
"current_value": "all_doors",
"step_value": None,
"current_state": None,
"selection_state": selection_state,
"min_value": None,
"max_value": None,
"options": ["driver_door", "all_doors"],
"track_visible": None,
"knob_visible": None,
"orientation": None,
}
)
self.assertEqual(state.selection_state.value, selection_state)
def test_partial_options_do_not_invalidate_observed_selection(self) -> None:
state = UIState.from_dict(
{
"function": "unlock_mode",
"zone": None,
"control_type": "segmented_control",
"current_value": "all_doors",
"step_value": None,
"current_state": None,
"selection_state": "selected",
"min_value": None,
"max_value": None,
"options": ["driver_door"],
"track_visible": None,
"knob_visible": None,
"orientation": None,
}
)
self.assertEqual(state.current_value, "all_doors")
self.assertEqual(state.options, ("driver_door",))
self.assertEqual(state.selection_state, SelectionState.SELECTED)
def test_clears_fields_that_do_not_apply_to_stepper(self) -> None:
model = FakeVisionGenerator(
"""{
"control_type": "stepper",
"current_value": 26.0,
"step_value": 1.0,
"current_state": null,
"min_value": 26.0,
"max_value": 26.0,
"options": null
}"""
)
state = understand(model, temperature_intent())
self.assertEqual(state.current_value, 26.0)
self.assertEqual(state.function, "temperature")
self.assertEqual(state.zone, Zone.DRIVER)
self.assertIsNone(state.min_value)
self.assertIsNone(state.max_value)
def test_slider_allows_unobservable_current_value(self) -> None:
intent = SemanticAction.from_dict(
{
"domain": "body",
"function": "tailgate_max_opening",
"zone": None,
"operation": "set",
"state": None,
"value": 80,
"unit": "percent",
}
)
model = FakeVisionGenerator(
"""{
"control_type": "slider",
"current_value": null,
"step_value": null,
"current_state": null,
"min_value": "50%",
"max_value": "100%",
"options": null,
"selection_state": null,
"track_visible": true,
"knob_visible": true
,"orientation": "horizontal"
}"""
)
state = understand(model, intent)
self.assertIsNone(state.current_value)
self.assertEqual(state.min_value, 50)
self.assertEqual(state.max_value, 100)
self.assertTrue(state.track_visible)
self.assertTrue(state.knob_visible)
self.assertEqual(state.orientation.value, "horizontal")
self.assertIsNone(state.selection_state)
def test_normalizes_unlock_option_labels(self) -> None:
intent = SemanticAction.from_dict(
{
"domain": "body",
"function": "unlock_mode",
"zone": None,
"operation": "set",
"state": None,
"value": "all_doors",
"unit": None,
}
)
model = FakeVisionGenerator(
"""{
"control_type": "segmented_control",
"current_value": "所有车门",
"step_value": null,
"current_state": "active",
"min_value": "主驾车门",
"max_value": "所有车门",
"options": ["主驾车门", "所有车门"]
}"""
)
state = understand(model, intent)
self.assertEqual(state.current_value, "all_doors")
self.assertEqual(state.options, ("driver_door", "all_doors"))
self.assertIsNone(state.current_state)
self.assertEqual(state.selection_state, SelectionState.SELECTED)
def test_normalizes_common_selection_state_language(self) -> None:
intent = SemanticAction.from_dict(
{
"domain": "body",
"function": "unlock_mode",
"zone": None,
"operation": "set",
"state": None,
"value": "all_doors",
"unit": None,
}
)
cases = (
("active", SelectionState.SELECTED),
("inactive", SelectionState.UNSELECTED),
("selected", SelectionState.SELECTED),
("unknown", SelectionState.UNKNOWN),
)
for raw_state, expected in cases:
with self.subTest(raw_state=raw_state):
model = FakeVisionGenerator(
"""{
"control_type": "segmented_control",
"current_value": "所有车门",
"step_value": null,
"current_state": null,
"selection_state": "%s",
"min_value": null,
"max_value": null,
"options": ["主驾车门", "所有车门"],
"track_visible": null,
"knob_visible": null
}""" % raw_state
)
state = understand(model, intent)
self.assertEqual(state.selection_state, expected)
def test_model_control_type_is_not_repaired_for_unlock_mode(self) -> None:
intent = SemanticAction.from_dict(
{
"domain": "body",
"function": "unlock_mode",
"zone": None,
"operation": "set",
"state": None,
"value": "all_doors",
"unit": None,
}
)
model = FakeVisionGenerator(
"""{
"control_type": "button",
"current_value": "所有车门",
"step_value": null,
"current_state": "active",
"min_value": null,
"max_value": null,
"options": null
}"""
)
state = understand(model, intent)
self.assertEqual(state.control_type.value, "button")
self.assertEqual(state.current_value, "all_doors")
self.assertEqual(state.selection_state, SelectionState.SELECTED)
self.assertIsNone(state.current_state)
def test_preserves_model_control_type_without_function_specific_repair(self) -> None:
model = FakeVisionGenerator(
"""{
"control_type": "slider",
"current_value": null,
"step_value": null,
"current_state": null,
"min_value": 0,
"max_value": 100,
"options": null
}"""
)
state = understand(model, temperature_intent())
self.assertEqual(state.control_type.value, "slider")
def test_all_observation_fields_may_be_null(self) -> None:
model = FakeVisionGenerator(
"""{
"control_type": null,
"current_value": null,
"step_value": null,
"current_state": null,
"min_value": null,
"max_value": null,
"options": null
}"""
)
state = understand(model, temperature_intent())
self.assertIsNone(state.control_type)
self.assertIsNone(state.current_value)
self.assertIsNone(state.current_state)
self.assertIsNone(state.selection_state)
self.assertIsNone(state.track_visible)
self.assertIsNone(state.knob_visible)
def test_prompt_describes_roi_and_forbids_guessing(self) -> None:
model = FakeVisionGenerator(
"""{
"control_type": "stepper",
"current_value": 26,
"step_value": 1,
"current_state": null,
"min_value": null,
"max_value": null,
"options": null
}"""
)
understand(model, temperature_intent())
self.assertEqual(model.image_path, "/tmp/function_roi.jpg")
self.assertIn("ROI", model.prompt)
self.assertIn("绝不能", model.prompt)
self.assertIn("端点旁的数值", model.prompt)
self.assertIn("selection_state", model.prompt)
self.assertNotIn("available", model.prompt)
self.assertNotIn("空调控制栏", model.prompt)
self.assertNotIn("页面中央", model.prompt)
if __name__ == "__main__":
unittest.main()