197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from cognitive_agent.application.seat_service import SeatDemoService
|
|
from cognitive_agent.infrastructure.speech.understanding import (
|
|
UnderstandingTranscriptProcessor,
|
|
)
|
|
|
|
REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("utterance", "capability", "target", "parameters"),
|
|
(
|
|
(
|
|
"打开一半车窗",
|
|
"window.set_position",
|
|
{"device": "window", "zone": "front_left"},
|
|
{"position_pct": 50},
|
|
),
|
|
(
|
|
"把副驾腿托向上调整到最大位置",
|
|
"seat.set_legrest_height",
|
|
{"device": "seat", "zone": "front_right", "instance": "passenger"},
|
|
{"position_pct": 100},
|
|
),
|
|
(
|
|
"把遮阳帘打开",
|
|
"roof.set_shade_motion",
|
|
{"device": "roof", "zone": "vehicle"},
|
|
{"motion": "open"},
|
|
),
|
|
(
|
|
"遮阳帘打开一半",
|
|
"roof.set_shade_motion",
|
|
{"device": "roof", "zone": "vehicle"},
|
|
{"motion": "half"},
|
|
),
|
|
(
|
|
"把遮阳帘打开到四分之一",
|
|
"roof.set_shade_position",
|
|
{"device": "roof", "zone": "vehicle"},
|
|
{"position_pct": 25},
|
|
),
|
|
),
|
|
)
|
|
def test_server_owned_speech_semantics_bypass_second_language_parse(
|
|
utterance: str,
|
|
capability: str,
|
|
target: dict[str, str],
|
|
parameters: dict[str, int],
|
|
) -> None:
|
|
processor = UnderstandingTranscriptProcessor(REPOSITORY_ROOT)
|
|
understanding = processor.process(utterance).to_dict()
|
|
understanding["utterance_id"] = "bridge-regression"
|
|
|
|
with tempfile.TemporaryDirectory() as directory, patch.dict(
|
|
os.environ,
|
|
{
|
|
"COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false",
|
|
"COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false",
|
|
"COGNITIVE_AGENT_VEHICLE_MQTT_ENABLED": "false",
|
|
},
|
|
clear=False,
|
|
):
|
|
service = SeatDemoService(REPOSITORY_ROOT)
|
|
service.artifact_root = Path(directory) / "artifacts"
|
|
with patch.object(
|
|
service,
|
|
"_interpret",
|
|
side_effect=AssertionError("cleanText must not be parsed a second time"),
|
|
):
|
|
result = service.process(
|
|
understanding["clean_text"],
|
|
provider="local",
|
|
execution_mode="dry_run",
|
|
_speech_understanding=understanding,
|
|
)
|
|
|
|
assert result["status"] == "succeeded"
|
|
assert result["provider_used"] == "speech_semantic"
|
|
assert result["speech_understanding"]["raw_text"] == utterance
|
|
actions = result["intent_ir"]["spec"]["actions"]
|
|
assert len(actions) == 1
|
|
assert actions[0]["capability"] == capability
|
|
assert actions[0]["target"] == target
|
|
assert actions[0]["parameters"] == parameters
|
|
assert actions[0]["provenance"]["parser"] == "speech_semantic"
|
|
|
|
|
|
def test_non_executable_speech_cannot_enter_orchestration_bridge() -> None:
|
|
processor = UnderstandingTranscriptProcessor(REPOSITORY_ROOT)
|
|
understanding = processor.process("打开前大灯").to_dict()
|
|
understanding["utterance_id"] = "bridge-unsupported"
|
|
|
|
with patch.dict(
|
|
os.environ,
|
|
{
|
|
"COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false",
|
|
"COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false",
|
|
},
|
|
clear=False,
|
|
):
|
|
service = SeatDemoService(REPOSITORY_ROOT)
|
|
context = service._context_for_vehicle(
|
|
service.conversations.snapshot("conv-speech-bridge"),
|
|
service.vehicle_profiles.get(None),
|
|
)
|
|
with pytest.raises(ValueError, match="not execution eligible"):
|
|
service._adapt_speech_understanding(
|
|
understanding,
|
|
context,
|
|
service.vehicle_profiles.get(None),
|
|
)
|
|
|
|
|
|
def test_every_registered_native_runtime_capability_keeps_native_bridge_binding() -> None:
|
|
def valid_value(spec: dict[str, object]) -> object:
|
|
allowed = spec.get("allowed_values")
|
|
if isinstance(allowed, list) and allowed:
|
|
return allowed[0]
|
|
value_type = str(spec.get("type", "string"))
|
|
if value_type in {"integer", "number"}:
|
|
return spec.get("minimum", 0)
|
|
if value_type == "boolean":
|
|
return True
|
|
if value_type == "object":
|
|
return {}
|
|
if value_type == "array":
|
|
return []
|
|
return "test"
|
|
|
|
with patch.dict(
|
|
os.environ,
|
|
{
|
|
"COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false",
|
|
"COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false",
|
|
},
|
|
clear=False,
|
|
):
|
|
service = SeatDemoService(REPOSITORY_ROOT)
|
|
context = service._context_for_vehicle(
|
|
service.conversations.snapshot("conv-native-speech-matrix"),
|
|
service.vehicle_profiles.get(None),
|
|
)
|
|
registered = {
|
|
definition.name: definition
|
|
for definition in service.capability_registry.definitions()
|
|
if definition.name in service.native_can_runtime.supported_capabilities
|
|
}
|
|
assert registered
|
|
for capability, definition in registered.items():
|
|
parameters = {
|
|
str(spec["name"]): valid_value(spec)
|
|
for spec in definition.parameters
|
|
if spec.get("required")
|
|
}
|
|
target = definition.target_scopes[0]
|
|
understanding = {
|
|
"raw_text": f"native bridge matrix: {capability}",
|
|
"execution_eligible": True,
|
|
"commands": [
|
|
{
|
|
"status": "active",
|
|
"negated": False,
|
|
"resolution_status": "executable",
|
|
"validation_issues": [],
|
|
"confidence": 1.0,
|
|
"source_text": capability,
|
|
"executor_payload": {
|
|
"domain": definition.domain,
|
|
"capability": capability,
|
|
"target": target,
|
|
"operation": "set",
|
|
"parameters": parameters,
|
|
},
|
|
}
|
|
],
|
|
}
|
|
parsed = service._adapt_speech_understanding(
|
|
understanding,
|
|
context,
|
|
service.vehicle_profiles.get(None),
|
|
)
|
|
assert parsed["deferred_requests"] == [], capability
|
|
assert parsed["actions"] == [], capability
|
|
assert parsed["domain_actions"] == [], capability
|
|
assert [
|
|
action["capability"] for action in parsed["native_can_actions"]
|
|
] == [capability]
|