411 lines
15 KiB
Python
411 lines
15 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import threading
|
|
import unittest
|
|
from copy import deepcopy
|
|
|
|
from cognitive_agent.application.codex_assistant import (
|
|
CodexAssistantService,
|
|
_VehicleAuthorization,
|
|
_VehicleTurnContext,
|
|
)
|
|
|
|
|
|
UTTERANCE = "空调温度设置到23度,打开闪烁灯"
|
|
|
|
|
|
def compiled_mixed_result() -> dict[str, object]:
|
|
return {
|
|
"request_id": "req-assistant-local-mixed",
|
|
"utterance": UTTERANCE,
|
|
"status": "processing",
|
|
"phase": "commands_validated",
|
|
"provider_used": "local_rules+infrared_local_rules",
|
|
"execution_mode": "dry_run",
|
|
"control_transport": "wired_can",
|
|
"function_configs": {
|
|
"native_climate": {
|
|
"spec": {
|
|
"domain": "climate",
|
|
"operations": [
|
|
{
|
|
"id": "climate-temperature-1",
|
|
"capability": "climate.set_temperature",
|
|
"target": {"zone": "driver"},
|
|
"parameters": {"temperature_c": 23},
|
|
"parse_source": "local_rule",
|
|
}
|
|
],
|
|
}
|
|
},
|
|
"self_extension": {
|
|
"spec": {
|
|
"domain": "self_extension",
|
|
"operations": [
|
|
{
|
|
"id": "infrared-1",
|
|
"capability": "infrared.emit",
|
|
"target": {"device": "toy-001", "name": "闪灯玩具"},
|
|
"parameters": {"action": "开启", "slot": 1},
|
|
"parse_source": "local_rule",
|
|
}
|
|
],
|
|
}
|
|
},
|
|
},
|
|
"command_plans": {
|
|
"self_extension": {
|
|
"spec": {
|
|
"domain": "self_extension",
|
|
"commands": [
|
|
{
|
|
"operation_ref": "unrelated-operation",
|
|
"message": "Native_0xBAD",
|
|
"signals": {
|
|
"debug_command": "must-not-cross-tool-boundary",
|
|
"frame_id": "0xBAD",
|
|
"data_hex": "DE AD BE EF",
|
|
},
|
|
},
|
|
{
|
|
"operation_ref": "infrared-1",
|
|
"bus": "TCP",
|
|
"message": "infrared.emit",
|
|
"signals": {"slot": 1},
|
|
},
|
|
],
|
|
}
|
|
},
|
|
"native_climate": {
|
|
"spec": {
|
|
"domain": "climate",
|
|
"commands": [
|
|
{
|
|
"operation_ref": "climate-temperature-1",
|
|
"bus": "body_can",
|
|
"message": "Native_0x1DE",
|
|
"signals": {
|
|
"debug_command": "ac temp driver 23",
|
|
"frame_id": "0x1DE",
|
|
"data_hex": "00 00 00 00 17 00 00 00",
|
|
},
|
|
}
|
|
],
|
|
}
|
|
},
|
|
},
|
|
"trace_events": [
|
|
{"stage": "intent_parse", "duration_ms": 2},
|
|
{"stage": "context_resolve", "duration_ms": 1},
|
|
{"stage": "domain_route", "duration_ms": 1},
|
|
{"stage": "function_generate", "duration_ms": 2},
|
|
{"stage": "schema_validate", "duration_ms": 1},
|
|
{"stage": "compile", "duration_ms": 2},
|
|
{"stage": "execute", "duration_ms": 999},
|
|
],
|
|
"raw_can": "must-not-cross-tool-boundary",
|
|
}
|
|
|
|
|
|
class ValidatedControlProjectionTest(unittest.TestCase):
|
|
def test_projection_exposes_only_exact_display_commands(self) -> None:
|
|
projection = CodexAssistantService._validated_control_payload(
|
|
compiled_mixed_result()
|
|
)
|
|
|
|
self.assertIsNotNone(projection)
|
|
assert projection is not None
|
|
commands = projection["validated_control_commands"]
|
|
assert isinstance(commands, list)
|
|
self.assertEqual(len(commands), 2)
|
|
by_capability = {
|
|
command["operation"]["capability"]: command for command in commands
|
|
}
|
|
climate = by_capability["climate.set_temperature"]
|
|
infrared = by_capability["infrared.emit"]
|
|
self.assertEqual(climate["actual_command"], "ac temp driver 23")
|
|
self.assertEqual(
|
|
climate["frames"],
|
|
[
|
|
{
|
|
"label": "CAN1 帧",
|
|
"value": "0x1DE · 00 00 00 00 17 00 00 00",
|
|
}
|
|
],
|
|
)
|
|
self.assertEqual(infrared["actual_command"], "infrared emit 1")
|
|
self.assertEqual(
|
|
infrared["frames"],
|
|
[
|
|
{
|
|
"label": "GSCU1B TCP",
|
|
"value": '{"action":"emit","data":{"no":1},"type":"infrared"}',
|
|
}
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
[event["stage"] for event in projection["trace_events"]],
|
|
[
|
|
"intent_parse",
|
|
"context_resolve",
|
|
"domain_route",
|
|
"function_generate",
|
|
"schema_validate",
|
|
"compile",
|
|
],
|
|
)
|
|
serialized = json.dumps(projection, ensure_ascii=False)
|
|
self.assertNotIn("command_plans", serialized)
|
|
self.assertNotIn("data_hex", serialized)
|
|
self.assertNotIn("raw_can", serialized)
|
|
self.assertNotIn("must-not-cross-tool-boundary", serialized)
|
|
|
|
def test_projection_drops_secrets_and_sensitive_nested_keys(self) -> None:
|
|
result = compiled_mixed_result()
|
|
secret = "projection-secret-token"
|
|
climate_config = result["function_configs"]["native_climate"]
|
|
climate_config["spec"]["domain"] = f"climate-{secret}"
|
|
operation = climate_config["spec"]["operations"][0]
|
|
operation["target"].update(
|
|
{
|
|
"api_key": "API-LEAK",
|
|
"cookie": "COOKIE-LEAK",
|
|
}
|
|
)
|
|
operation["parameters"].update(
|
|
{
|
|
"credential": "CRED-LEAK",
|
|
"access_key": "ACCESS-LEAK",
|
|
}
|
|
)
|
|
operation["parse_source"] = f"local-{secret}"
|
|
result["command_plans"]["native_climate"]["spec"]["commands"][0][
|
|
"signals"
|
|
]["debug_command"] = f"ac temp driver 23 {secret}"
|
|
|
|
projection = CodexAssistantService._validated_control_payload(
|
|
result,
|
|
secret=secret,
|
|
)
|
|
|
|
self.assertIsNotNone(projection)
|
|
assert projection is not None
|
|
serialized = json.dumps(projection, ensure_ascii=False)
|
|
for forbidden in (
|
|
secret,
|
|
"API-LEAK",
|
|
"COOKIE-LEAK",
|
|
"CRED-LEAK",
|
|
"ACCESS-LEAK",
|
|
"api_key",
|
|
"credential",
|
|
"access_key",
|
|
):
|
|
self.assertNotIn(forbidden, serialized)
|
|
climate = next(
|
|
item
|
|
for item in projection["validated_control_commands"]
|
|
if item["operation"]["capability"] == "climate.set_temperature"
|
|
)
|
|
self.assertEqual(climate["domain"], "")
|
|
self.assertNotIn("parse_source", climate["operation"])
|
|
self.assertEqual(climate["operation"]["target"], {"zone": "driver"})
|
|
self.assertEqual(
|
|
climate["operation"]["parameters"],
|
|
{"temperature_c": 23},
|
|
)
|
|
self.assertEqual(climate["actual_command"], "")
|
|
|
|
def test_projection_drops_operation_when_identity_contains_secret(self) -> None:
|
|
result = compiled_mixed_result()
|
|
secret = "projection-secret-token"
|
|
operation = result["function_configs"]["native_climate"]["spec"][
|
|
"operations"
|
|
][0]
|
|
operation["id"] = f"climate-{secret}"
|
|
result["command_plans"]["native_climate"]["spec"]["commands"][0][
|
|
"operation_ref"
|
|
] = operation["id"]
|
|
|
|
projection = CodexAssistantService._validated_control_payload(
|
|
result,
|
|
secret=secret,
|
|
)
|
|
|
|
self.assertIsNotNone(projection)
|
|
assert projection is not None
|
|
serialized = json.dumps(projection, ensure_ascii=False)
|
|
self.assertNotIn(secret, serialized)
|
|
self.assertEqual(
|
|
[
|
|
item["operation"]["capability"]
|
|
for item in projection["validated_control_commands"]
|
|
],
|
|
["infrared.emit"],
|
|
)
|
|
|
|
def test_duplicate_operation_refs_never_cross_configuration_boundaries(self) -> None:
|
|
result = {
|
|
"function_configs": {
|
|
"native_climate": {
|
|
"spec": {
|
|
"domain": "climate",
|
|
"operations": [
|
|
{
|
|
"id": "op-1",
|
|
"capability": "climate.set_temperature",
|
|
"target": {"zone": "driver"},
|
|
"parameters": {"temperature_c": 23},
|
|
}
|
|
],
|
|
}
|
|
},
|
|
"native_seat": {
|
|
"spec": {
|
|
"domain": "seat",
|
|
"operations": [
|
|
{
|
|
"id": "op-1",
|
|
"capability": "seat.set_heating",
|
|
"target": {"zone": "passenger"},
|
|
"parameters": {"level": 2},
|
|
}
|
|
],
|
|
}
|
|
},
|
|
},
|
|
"command_plans": {
|
|
"native_seat": {
|
|
"spec": {
|
|
"domain": "seat",
|
|
"commands": [
|
|
{
|
|
"operation_ref": "op-1",
|
|
"bus": "seat_can0",
|
|
"signals": {
|
|
"debug_command": "seat passenger heat 2"
|
|
},
|
|
}
|
|
],
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
projection = CodexAssistantService._validated_control_payload(result)
|
|
|
|
self.assertIsNotNone(projection)
|
|
assert projection is not None
|
|
commands = projection["validated_control_commands"]
|
|
self.assertEqual(len(commands), 1)
|
|
self.assertEqual(
|
|
commands[0]["operation"]["capability"],
|
|
"seat.set_heating",
|
|
)
|
|
self.assertEqual(commands[0]["actual_command"], "seat passenger heat 2")
|
|
|
|
def test_compiled_semantic_operation_is_kept_without_debug_command(self) -> None:
|
|
result = deepcopy(compiled_mixed_result())
|
|
climate_command = result["command_plans"]["native_climate"]["spec"][
|
|
"commands"
|
|
][0]
|
|
climate_command["signals"].pop("debug_command")
|
|
|
|
projection = CodexAssistantService._validated_control_payload(result)
|
|
|
|
self.assertIsNotNone(projection)
|
|
assert projection is not None
|
|
climate = next(
|
|
item
|
|
for item in projection["validated_control_commands"]
|
|
if item["operation"]["capability"] == "climate.set_temperature"
|
|
)
|
|
self.assertEqual(climate["actual_command"], "")
|
|
self.assertEqual(
|
|
climate["frames"][0]["value"],
|
|
"0x1DE · 00 00 00 00 17 00 00 00",
|
|
)
|
|
|
|
def test_direct_stream_emits_projection_before_progress_and_completion(self) -> None:
|
|
client = type("UnavailableClient", (), {"available": False})()
|
|
assistant = CodexAssistantService(client)
|
|
events: list[dict[str, object]] = []
|
|
|
|
def execute_control(instruction, progress):
|
|
self.assertEqual(instruction, UTTERANCE)
|
|
result = compiled_mixed_result()
|
|
progress(result)
|
|
return {**result, "status": "succeeded"}
|
|
|
|
result = assistant.stream(
|
|
UTTERANCE,
|
|
conversation_id="conv-validated-projection",
|
|
principal="local-test",
|
|
history=[],
|
|
emit=events.append,
|
|
execute_control=execute_control,
|
|
control_terms=("空调", "闪烁灯"),
|
|
quick_control_commands=[UTTERANCE],
|
|
)
|
|
|
|
event_types = [event["type"] for event in events]
|
|
validated_index = event_types.index("validated_control_commands")
|
|
self.assertLess(validated_index, event_types.index("action.progress"))
|
|
self.assertLess(validated_index, event_types.index("action.completed"))
|
|
self.assertLess(validated_index, event_types.index("answer.completed"))
|
|
self.assertEqual(result["route"], "vehicle_control")
|
|
self.assertEqual(result["success"], True)
|
|
|
|
safe_result = CodexAssistantService._safe_vehicle_result(
|
|
compiled_mixed_result()
|
|
)
|
|
self.assertNotIn("command_plans", safe_result)
|
|
self.assertNotIn("validated_control_commands", safe_result)
|
|
|
|
def test_agent_tool_path_queues_projection_for_the_browser_thread(self) -> None:
|
|
client = type("UnavailableClient", (), {"available": False})()
|
|
assistant = CodexAssistantService(client)
|
|
request_token = "projection-secret-token"
|
|
|
|
def execute_control(instruction, progress):
|
|
self.assertEqual(instruction, UTTERANCE)
|
|
result = compiled_mixed_result()
|
|
progress(result)
|
|
return {**result, "status": "succeeded"}
|
|
|
|
context = _VehicleTurnContext(
|
|
original_message=UTTERANCE,
|
|
execute_control=execute_control,
|
|
authorization=_VehicleAuthorization(
|
|
instruction=UTTERANCE,
|
|
reason="explicit_registered_control",
|
|
),
|
|
capability_snapshot={},
|
|
query_capabilities=lambda _snapshot, _query: {},
|
|
lock=threading.Lock(),
|
|
)
|
|
assistant._tool_contexts[request_token] = context
|
|
|
|
safe_result = assistant.execute_vehicle_control(request_token, UTTERANCE)
|
|
|
|
queued_events = []
|
|
while not context.events.empty():
|
|
queued_events.append(context.events.get_nowait())
|
|
event_types = [event["type"] for event in queued_events]
|
|
self.assertEqual(event_types[0], "action.started")
|
|
self.assertLess(
|
|
event_types.index("validated_control_commands"),
|
|
event_types.index("action.progress"),
|
|
)
|
|
self.assertLess(
|
|
event_types.index("validated_control_commands"),
|
|
event_types.index("action.completed"),
|
|
)
|
|
self.assertNotIn("command_plans", safe_result)
|
|
self.assertNotIn(request_token, json.dumps(queued_events, ensure_ascii=False))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|