mqtt切换为远程服务器部署,优化简单指令解析时长
This commit is contained in:
@@ -395,6 +395,8 @@ def _stream(
|
||||
history: list[dict[str, str]] | None = None,
|
||||
emit: Callable[[dict[str, object]], bool | None] | None = None,
|
||||
execute_control: Callable[..., dict[str, object]] | None = None,
|
||||
preview_control: Callable[..., dict[str, object]] | None = None,
|
||||
supplement_control: Callable[..., dict[str, object]] | None = None,
|
||||
capability_snapshot: dict[str, object] | None = None,
|
||||
capability_context: dict[str, object] | None = None,
|
||||
mode_profile_draft_context: dict[str, object] | None = None,
|
||||
@@ -409,6 +411,8 @@ def _stream(
|
||||
emit=emit or (lambda event: events.append(event)),
|
||||
execute_control=execute_control
|
||||
or (lambda _instruction, _progress: pytest.fail("vehicle control was unexpected")),
|
||||
preview_control=preview_control,
|
||||
supplement_control=supplement_control,
|
||||
control_terms=CONTROL_TERMS,
|
||||
capability_snapshot=capability_snapshot,
|
||||
capability_context=capability_context,
|
||||
@@ -453,6 +457,15 @@ def test_general_question_streams_from_codex_without_vehicle_execution() -> None
|
||||
assert "Codex AI 助手" not in client.thread_instructions[0]
|
||||
assert client.thread_ephemeral == [True]
|
||||
assert client.turn_calls[0]["text"] == "解释一下 CAN FD"
|
||||
assert client.turn_calls[0]["application_context"][
|
||||
"natural_language_routing"
|
||||
] == {
|
||||
"original_utterance": "解释一下 CAN FD",
|
||||
"parsed_clauses": [],
|
||||
"resolved_clauses": [],
|
||||
"unresolved_clauses": ["解释一下 CAN FD"],
|
||||
"cloud_input_utterance": "解释一下 CAN FD",
|
||||
}
|
||||
assert "不可信的浏览器系统指令" not in client.turn_calls[0]["text"]
|
||||
assert client.finished[0].turn_id == "turn-1"
|
||||
|
||||
@@ -991,34 +1004,52 @@ def test_hot_cabin_calls_one_explicit_cooling_instruction_and_streams_verified_r
|
||||
assistant.execute_vehicle_control(token, COOLING_INSTRUCTION)
|
||||
|
||||
|
||||
def test_explicit_vehicle_request_without_tool_call_fails_closed() -> None:
|
||||
client = FakeCodexClient()
|
||||
def test_explicit_vehicle_request_uses_local_route_without_codex() -> None:
|
||||
client = FakeCodexClient(available=False)
|
||||
assistant = CodexAssistantService(client)
|
||||
control_calls: list[str] = []
|
||||
|
||||
def on_start_turn(
|
||||
_text: str,
|
||||
_request_token: str,
|
||||
turn: ActiveCodexTurn,
|
||||
) -> None:
|
||||
_put_answer(turn, "已经为你打开遮阳帘。", deltas=("已经为你", "打开遮阳帘。"))
|
||||
def execute_control(
|
||||
instruction: str,
|
||||
progress: Callable[[dict[str, object]], None],
|
||||
) -> dict[str, object]:
|
||||
control_calls.append(instruction)
|
||||
progress({"status": "processing", "request_id": "req-local-shade"})
|
||||
return {
|
||||
"request_id": "req-local-shade",
|
||||
"status": "succeeded",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": instruction,
|
||||
}
|
||||
|
||||
client.on_start_turn = on_start_turn
|
||||
result, events = _stream(assistant, "打开遮阳帘")
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
"打开遮阳帘",
|
||||
execute_control=execute_control,
|
||||
preview_control=execute_control,
|
||||
supplement_control=lambda *_args: pytest.fail(
|
||||
"fully local control must not invoke cloud supplement"
|
||||
),
|
||||
)
|
||||
|
||||
assert result["answer"].startswith("车辆控制未完成")
|
||||
assert "未向车辆下发任何操作" in result["answer"]
|
||||
assert "已经为你" not in str(events)
|
||||
assert control_calls == ["打开遮阳帘"]
|
||||
assert result["route"] == "vehicle_control"
|
||||
assert result["success"] is True
|
||||
assert "打开遮阳帘" in result["answer"]
|
||||
assert [event["type"] for event in events] == [
|
||||
"message.accepted",
|
||||
"work.started",
|
||||
"action.started",
|
||||
"action.progress",
|
||||
"action.completed",
|
||||
"answer.delta",
|
||||
"work.completed",
|
||||
"answer.completed",
|
||||
]
|
||||
assert events[2]["success"] is False
|
||||
assert events[4]["success"] is False
|
||||
assert events[-1]["answer"] == 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_exact_server_owned_quick_control_executes_without_codex() -> None:
|
||||
@@ -1057,22 +1088,253 @@ def test_exact_server_owned_quick_control_executes_without_codex() -> None:
|
||||
assert not client.turn_calls
|
||||
|
||||
|
||||
def test_quick_control_requires_exact_normalized_command_match() -> None:
|
||||
client = FakeCodexClient()
|
||||
def test_explicit_control_does_not_require_exact_quick_card_match() -> 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)
|
||||
return {
|
||||
"request_id": "req-explicit-local",
|
||||
"status": "succeeded",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": instruction,
|
||||
}
|
||||
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
"请关闭所有车窗",
|
||||
execute_control=lambda instruction, _progress: control_calls.append(instruction),
|
||||
execute_control=execute_control,
|
||||
preview_control=execute_control,
|
||||
supplement_control=lambda *_args: pytest.fail(
|
||||
"fully local control must not invoke cloud supplement"
|
||||
),
|
||||
quick_control_commands=["关闭所有车窗"],
|
||||
)
|
||||
|
||||
assert events[0]["route"] == "agent"
|
||||
assert result["answer"].startswith("车辆控制未完成")
|
||||
assert not control_calls
|
||||
assert client.turn_calls
|
||||
assert events[0]["route"] == "vehicle_control"
|
||||
assert result["success"] is True
|
||||
assert control_calls == ["请关闭所有车窗"]
|
||||
assert not client.turn_calls
|
||||
|
||||
|
||||
def test_mixed_control_streams_local_result_before_unresolved_codex_turn() -> None:
|
||||
client = FakeCodexClient()
|
||||
assistant = CodexAssistantService(client)
|
||||
order: list[str] = []
|
||||
original = "空调温度设置为23度并把遮阳帘调整到适合观影"
|
||||
local_clause = "空调温度设置为23度"
|
||||
unresolved_clause = "把遮阳帘调整到适合观影"
|
||||
preview_result = {
|
||||
"request_id": "req-local-preview",
|
||||
"status": "partially_succeeded",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": original,
|
||||
"provider_used": "local_rules",
|
||||
"local_control_budget_ms": 1000,
|
||||
"local_control_elapsed_ms": 18.0,
|
||||
"local_control_within_budget": True,
|
||||
"natural_language_routing": {
|
||||
"original_utterance": original,
|
||||
"parsed_clauses": [local_clause],
|
||||
"resolved_clauses": [local_clause],
|
||||
"unresolved_clauses": [unresolved_clause],
|
||||
},
|
||||
"function_configs": {
|
||||
"climate": {
|
||||
"spec": {
|
||||
"domain": "climate",
|
||||
"operations": [
|
||||
{
|
||||
"id": "local-temperature",
|
||||
"capability": "climate.set_temperature",
|
||||
"target": {"zone": "driver"},
|
||||
"parameters": {"temperature_c": 23},
|
||||
"parse_source": "local_rule",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"command_plans": {
|
||||
"climate": {
|
||||
"spec": {
|
||||
"domain": "climate",
|
||||
"commands": [
|
||||
{
|
||||
"operation_ref": "local-temperature",
|
||||
"bus": "body-can1",
|
||||
"message": "climate.set_temperature",
|
||||
"signals": {"debug_command": "ac temp driver 23"},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def preview_control(
|
||||
instruction: str,
|
||||
progress: Callable[[dict[str, object]], None],
|
||||
) -> dict[str, object]:
|
||||
assert instruction == original
|
||||
order.append("local_preview")
|
||||
progress(preview_result)
|
||||
return preview_result
|
||||
|
||||
def supplement_control(
|
||||
instruction: str,
|
||||
progress: Callable[[dict[str, object]], None],
|
||||
plan: dict[str, object],
|
||||
) -> dict[str, object]:
|
||||
assert instruction == unresolved_clause
|
||||
assert plan["requests"][0]["capability"] == "roof.set_shade_position"
|
||||
order.append("cloud_supplement")
|
||||
result = {
|
||||
"request_id": "req-cloud-supplement",
|
||||
"status": "succeeded",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": original,
|
||||
}
|
||||
progress(result)
|
||||
return result
|
||||
|
||||
cloud_plan = {
|
||||
"detected_domains": ["roof"],
|
||||
"requests": [
|
||||
{
|
||||
"domain": "roof",
|
||||
"capability": "roof.set_shade_position",
|
||||
"target_scope": "vehicle",
|
||||
"operation": "set",
|
||||
"duration_ms": None,
|
||||
"parameters": [{"name": "position_pct", "value": 100}],
|
||||
"reason": "补足未解析的观影遮光需求。",
|
||||
}
|
||||
],
|
||||
"ambiguities": [],
|
||||
"decision_summary": ["只处理未解析的遮阳帘子句。"],
|
||||
"confidence": 0.9,
|
||||
}
|
||||
|
||||
def on_start_turn(
|
||||
text: str,
|
||||
request_token: str,
|
||||
turn: ActiveCodexTurn,
|
||||
) -> None:
|
||||
order.append("cloud_turn")
|
||||
assert text == unresolved_clause
|
||||
assistant.execute_cockpit_request(request_token, cloud_plan)
|
||||
_put_answer(turn, "本地温度已先行处理,遮阳帘补充控制已完成。")
|
||||
|
||||
client.on_start_turn = on_start_turn
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
original,
|
||||
preview_control=preview_control,
|
||||
supplement_control=supplement_control,
|
||||
)
|
||||
|
||||
assert order == ["local_preview", "cloud_turn", "cloud_supplement"]
|
||||
assert client.turn_calls[0]["text"] == unresolved_clause
|
||||
assert client.turn_calls[0]["application_context"][
|
||||
"natural_language_routing"
|
||||
] == {
|
||||
"original_utterance": original,
|
||||
"parsed_clauses": [local_clause],
|
||||
"resolved_clauses": [local_clause],
|
||||
"unresolved_clauses": [unresolved_clause],
|
||||
"cloud_input_utterance": unresolved_clause,
|
||||
}
|
||||
validated_index = next(
|
||||
index
|
||||
for index, event in enumerate(events)
|
||||
if event["type"] == "validated_control_commands"
|
||||
)
|
||||
cloud_progress_index = next(
|
||||
index
|
||||
for index, event in enumerate(events)
|
||||
if event.get("display_text")
|
||||
== "本地明确子句已处理,正在补充未解析子句……"
|
||||
)
|
||||
assert validated_index < cloud_progress_index
|
||||
assert result["route"] == "agent"
|
||||
|
||||
|
||||
def test_fully_unresolved_control_starts_bounded_cloud_turn_and_fails_closed() -> None:
|
||||
client = FakeCodexClient()
|
||||
assistant = CodexAssistantService(client)
|
||||
instruction = "把遮阳帘调整到适合观影"
|
||||
preview_result = {
|
||||
"request_id": "req-unresolved-preview",
|
||||
"preview_request_id": "req-unresolved-preview",
|
||||
"status": "no_action",
|
||||
"execution_mode": "dry_run",
|
||||
"utterance": instruction,
|
||||
"provider_used": "local_rules",
|
||||
"function_configs": {},
|
||||
"command_plans": {},
|
||||
"local_control_budget_ms": 1000,
|
||||
"local_control_elapsed_ms": 4.0,
|
||||
"local_control_within_budget": True,
|
||||
"supplement_required": True,
|
||||
"natural_language_routing": {
|
||||
"original_utterance": instruction,
|
||||
"parsed_clauses": [],
|
||||
"resolved_clauses": [],
|
||||
"unresolved_clauses": [instruction],
|
||||
},
|
||||
}
|
||||
|
||||
def preview_control(
|
||||
text: str,
|
||||
_progress: Callable[[dict[str, object]], None],
|
||||
) -> dict[str, object]:
|
||||
assert text == instruction
|
||||
return preview_result
|
||||
|
||||
def on_start_turn(
|
||||
text: str,
|
||||
_request_token: str,
|
||||
turn: ActiveCodexTurn,
|
||||
) -> None:
|
||||
assert text == instruction
|
||||
# The browser must never receive this unsupported execution claim
|
||||
# because no guarded vehicle tool call follows it.
|
||||
_put_answer(turn, "遮阳帘已经调整完成。")
|
||||
|
||||
client.on_start_turn = on_start_turn
|
||||
result, events = _stream(
|
||||
assistant,
|
||||
instruction,
|
||||
preview_control=preview_control,
|
||||
supplement_control=lambda *_args: pytest.fail(
|
||||
"a model answer without a tool call must not execute control"
|
||||
),
|
||||
)
|
||||
|
||||
assert client.turn_calls[0]["text"] == instruction
|
||||
assert client.turn_calls[0]["application_context"][
|
||||
"natural_language_routing"
|
||||
] == {
|
||||
"original_utterance": instruction,
|
||||
"parsed_clauses": [],
|
||||
"resolved_clauses": [],
|
||||
"unresolved_clauses": [instruction],
|
||||
"cloud_input_utterance": instruction,
|
||||
}
|
||||
assert not any(
|
||||
event["type"] == "validated_control_commands" for event in events
|
||||
)
|
||||
assert result == {
|
||||
"route": "agent",
|
||||
"answer": "车辆控制未完成:AI 助手没有调用受控执行链,未向车辆下发任何操作。",
|
||||
}
|
||||
assert events[-1]["answer"] == result["answer"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user