feat: integrate production cockpit AI assistant
This commit is contained in:
@@ -35,6 +35,7 @@ CONTROL_TERMS = (
|
||||
"创建一个宝宝睡眠模式",
|
||||
"请帮我创建一个宝宝睡眠模式:副驾半躺、空调24度、关闭阅读灯",
|
||||
"新增冥想空间模式,打开氛围灯并把温度调到24度",
|
||||
"创建一个禁止打扰模式",
|
||||
),
|
||||
)
|
||||
def test_named_mode_creation_is_authorized_as_bounded_configuration(message: str) -> None:
|
||||
@@ -342,6 +343,7 @@ def _stream(
|
||||
capability_snapshot: dict[str, object] | None = None,
|
||||
capability_context: dict[str, object] | None = None,
|
||||
mode_profile_draft_context: dict[str, object] | None = None,
|
||||
quick_control_commands: list[str] | None = None,
|
||||
) -> tuple[dict[str, object], list[dict[str, object]]]:
|
||||
events: list[dict[str, object]] = []
|
||||
result = assistant.stream(
|
||||
@@ -356,6 +358,7 @@ def _stream(
|
||||
capability_snapshot=capability_snapshot,
|
||||
capability_context=capability_context,
|
||||
mode_profile_draft_context=mode_profile_draft_context,
|
||||
quick_control_commands=quick_control_commands or [],
|
||||
)
|
||||
return result, events
|
||||
|
||||
@@ -384,7 +387,15 @@ def test_general_question_streams_from_codex_without_vehicle_execution() -> None
|
||||
]
|
||||
assert events[0]["engine"] == "codex-app-server"
|
||||
assert events[-1]["answer"] == "这是 Codex 的流式回答。"
|
||||
assert events[1]["display_text"] == "小迪正在理解并处理……"
|
||||
assert events[-2]["display_text"] == "小迪已完成"
|
||||
assert all(
|
||||
"codex" not in str(event.get("display_text") or "").lower()
|
||||
for event in events
|
||||
)
|
||||
assert len(client.thread_instructions) == 1
|
||||
assert "“小迪”AI 助手" in client.thread_instructions[0]
|
||||
assert "Codex AI 助手" not in client.thread_instructions[0]
|
||||
assert client.thread_ephemeral == [True]
|
||||
assert client.turn_calls[0]["text"] == "解释一下 CAN FD"
|
||||
assert "不可信的浏览器系统指令" not in client.turn_calls[0]["text"]
|
||||
@@ -582,8 +593,8 @@ def test_nonmatching_draft_context_does_not_claim_another_mode_update() -> None:
|
||||
)
|
||||
|
||||
# The stale card reference has been ignored. The sentence is still an
|
||||
# explicit registered control under the legacy saved-mode update route.
|
||||
assert authorization.reason == "explicit_registered_control"
|
||||
# explicit saved-mode mutation under the deterministic management route.
|
||||
assert authorization.reason == "explicit_mode_profile_update"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -902,6 +913,60 @@ def test_explicit_vehicle_request_without_tool_call_fails_closed() -> None:
|
||||
assert events[-1]["answer"] == result["answer"]
|
||||
|
||||
|
||||
def test_exact_server_owned_quick_control_executes_without_codex() -> None:
|
||||
client = FakeCodexClient(available=False)
|
||||
assistant = CodexAssistantService(client)
|
||||
control_calls: list[str] = []
|
||||
|
||||
def execute_control(
|
||||
instruction: str,
|
||||
progress: Callable[[dict[str, object]], None],
|
||||
) -> dict[str, object]:
|
||||
control_calls.append(instruction)
|
||||
progress({"status": "processing", "request_id": "req-quick-control"})
|
||||
return {
|
||||
"request_id": "req-quick-control",
|
||||
"status": "succeeded",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": instruction,
|
||||
}
|
||||
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
"关闭所有车窗",
|
||||
execute_control=execute_control,
|
||||
quick_control_commands=["关闭所有车窗"],
|
||||
)
|
||||
|
||||
assert control_calls == ["关闭所有车窗"]
|
||||
assert result["route"] == "vehicle_control"
|
||||
assert result["success"] is True
|
||||
assert "关闭所有车窗" in result["answer"]
|
||||
assert "升温" not in result["answer"]
|
||||
assert events[0]["engine"] == "n7web-control-router"
|
||||
assert events[1]["display_text"] == "正在执行快捷车控……"
|
||||
assert not client.thread_instructions
|
||||
assert not client.turn_calls
|
||||
|
||||
|
||||
def test_quick_control_requires_exact_normalized_command_match() -> None:
|
||||
client = FakeCodexClient()
|
||||
assistant = CodexAssistantService(client)
|
||||
control_calls: list[str] = []
|
||||
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
"请关闭所有车窗",
|
||||
execute_control=lambda instruction, _progress: control_calls.append(instruction),
|
||||
quick_control_commands=["关闭所有车窗"],
|
||||
)
|
||||
|
||||
assert events[0]["route"] == "agent"
|
||||
assert result["answer"].startswith("车辆控制未完成")
|
||||
assert not control_calls
|
||||
assert client.turn_calls
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("message", "expected_instruction", "temperature"),
|
||||
[
|
||||
@@ -1265,7 +1330,11 @@ def test_codex_work_items_are_forwarded_without_exposing_reasoning() -> None:
|
||||
event_types = [event["type"] for event in events]
|
||||
assert event_types.count("work.started") == 3
|
||||
assert event_types.count("work.completed") == 3
|
||||
assert any(event.get("display_text") == "Codex 正在处理……" for event in events)
|
||||
assert any(event.get("display_text") == "小迪正在处理……" for event in events)
|
||||
assert all(
|
||||
"codex" not in str(event.get("display_text") or "").lower()
|
||||
for event in events
|
||||
)
|
||||
assert "不得显示" not in str(events)
|
||||
assert result["answer"] == "相关测试已通过,工作已完成。"
|
||||
|
||||
@@ -1371,6 +1440,46 @@ def test_non_retryable_app_server_error_fails_without_waiting_for_turn_timeout()
|
||||
assert client.interrupts == []
|
||||
|
||||
|
||||
def test_retry_progress_uses_xiaodi_public_branding() -> None:
|
||||
client = FakeCodexClient()
|
||||
assistant = CodexAssistantService(client)
|
||||
|
||||
def on_start_turn(
|
||||
_text: str,
|
||||
_request_token: str,
|
||||
turn: ActiveCodexTurn,
|
||||
) -> None:
|
||||
turn.events.put(
|
||||
{
|
||||
"method": "error",
|
||||
"params": {
|
||||
"willRetry": True,
|
||||
"error": {"message": "internal transport retry"},
|
||||
},
|
||||
}
|
||||
)
|
||||
_put_answer(turn, "重连后已完成。")
|
||||
|
||||
client.on_start_turn = on_start_turn
|
||||
result, events = _stream(assistant, "解释 CAN FD")
|
||||
|
||||
retry_event = next(event for event in events if event["type"] == "work.progress")
|
||||
assert retry_event["display_text"] == "小迪正在重新连接……"
|
||||
assert "codex" not in retry_event["display_text"].lower()
|
||||
assert result["answer"] == "重连后已完成。"
|
||||
|
||||
|
||||
def test_unknown_work_item_uses_xiaodi_public_branding() -> None:
|
||||
event = CodexAssistantService._item_started_event(
|
||||
{"type": "futureWorkItem"},
|
||||
"msg-public-brand",
|
||||
)
|
||||
|
||||
assert event is not None
|
||||
assert event["display_text"] == "小迪正在处理……"
|
||||
assert "codex" not in event["display_text"].lower()
|
||||
|
||||
|
||||
def test_same_principal_reuses_thread_and_different_principal_is_isolated() -> None:
|
||||
client = FakeCodexClient()
|
||||
assistant = CodexAssistantService(client)
|
||||
@@ -1486,7 +1595,9 @@ def test_structured_plan_is_forwarded_with_the_server_bound_original_request() -
|
||||
{"name": "transmittance_pct", "value": 30}
|
||||
]
|
||||
assert forwarded_plan["requests"][0]["reason"] == "原始用户请求的结构化候选动作。"
|
||||
assert forwarded_plan["decision_summary"] == ["Codex 结构化计划已提交应用安全链复核。"]
|
||||
assert forwarded_plan["decision_summary"] == [
|
||||
"小迪生成的结构化计划已提交应用安全链复核。"
|
||||
]
|
||||
|
||||
|
||||
def test_structured_plan_rejects_non_json_values_before_control_execution() -> None:
|
||||
|
||||
Reference in New Issue
Block a user