feat: make mode creation preview-only and cache-safe
This commit is contained in:
@@ -264,7 +264,7 @@ def test_unsupported_or_out_of_range_addition_rejects_all_changes_atomically(add
|
||||
assert not _hashes(user_root)
|
||||
|
||||
|
||||
def test_adding_devices_to_full_twelve_action_draft_never_truncates_original_actions(addition_service):
|
||||
def test_adding_devices_to_full_twenty_action_draft_never_truncates_original_actions(addition_service):
|
||||
service, _, user_root = addition_service
|
||||
operations = [
|
||||
("climate.set_power", "vehicle", {"state": "on"}),
|
||||
@@ -279,6 +279,14 @@ def test_adding_devices_to_full_twelve_action_draft_never_truncates_original_act
|
||||
("ambient_light.set_power", "vehicle", {"state": "off"}),
|
||||
("interior_light.set_indoor_power", "vehicle", {"state": "off"}),
|
||||
("interior_light.set_reading_power", "all", {"state": "off"}),
|
||||
("window.set_position", "rear_left", {"position_pct": 0}),
|
||||
("window.set_position", "rear_right", {"position_pct": 0}),
|
||||
("ambient_light.set_mode", "vehicle", {"mode": "romantic"}),
|
||||
("ambient_light.set_brightness_level", "vehicle", {"brightness_level": 2}),
|
||||
("ambient_light.set_color_index", "vehicle", {"color_index": 6}),
|
||||
("seat.set_heating", "passenger", {"state": "on", "level": 1}),
|
||||
("seat.set_backrest_recline", "passenger", {"position_pct": 45}),
|
||||
("seat.set_legrest_height", "passenger", {"position_pct": 35}),
|
||||
]
|
||||
plan = {
|
||||
"detected_domains": sorted({cap.split(".", 1)[0] for cap, _, _ in operations}),
|
||||
@@ -287,11 +295,11 @@ def test_adding_devices_to_full_twelve_action_draft_never_truncates_original_act
|
||||
"operation": "set", "parameters": [{"name": name, "value": value} for name, value in params.items()],
|
||||
"duration_ms": None, "reason": "固定目标边界回归。",
|
||||
} for cap, target, params in operations],
|
||||
"ambiguities": [], "confidence": 0.95, "decision_summary": ["验证12项原动作保留。"],
|
||||
"ambiguities": [], "confidence": 0.95, "decision_summary": ["验证20项原动作保留。"],
|
||||
}
|
||||
original = _preview(service, "构建一个满载参数模式", plan=plan)
|
||||
_assert_draft(original)
|
||||
assert len(_actions(original)) == 12
|
||||
assert len(_actions(original)) == 20
|
||||
source_path = service.artifact_root / original["request_id"] / "result.json"
|
||||
source_hash = hashlib.sha256(source_path.read_bytes()).hexdigest()
|
||||
try:
|
||||
|
||||
@@ -1193,26 +1193,34 @@ MAXIMUM_SCENE_REQUESTS = (
|
||||
_scene_request("ambient_light.set_power", "vehicle", {"state": "off"}),
|
||||
_scene_request("interior_light.set_indoor_power", "vehicle", {"state": "off"}),
|
||||
_scene_request("interior_light.set_reading_power", "all", {"state": "off"}),
|
||||
_scene_request("window.set_position", "rear_left", {"position_pct": 0}),
|
||||
_scene_request("window.set_position", "rear_right", {"position_pct": 0}),
|
||||
_scene_request("ambient_light.set_mode", "vehicle", {"mode": "romantic"}),
|
||||
_scene_request("ambient_light.set_brightness_level", "vehicle", {"brightness_level": 2}),
|
||||
_scene_request("ambient_light.set_color_index", "vehicle", {"color_index": 6}),
|
||||
_scene_request("seat.set_heating", "passenger", {"state": "on", "level": 1}),
|
||||
_scene_request("seat.set_backrest_recline", "passenger", {"position_pct": 45}),
|
||||
_scene_request("seat.set_legrest_height", "passenger", {"position_pct": 35}),
|
||||
)
|
||||
|
||||
|
||||
def test_scene_request_limit_accepts_exactly_twelve_verified_actions(
|
||||
def test_scene_request_limit_accepts_exactly_twenty_verified_actions(
|
||||
mode_service: tuple[SeatDemoService, Path],
|
||||
) -> None:
|
||||
service, user_root = mode_service
|
||||
assert len(MAXIMUM_SCENE_REQUESTS) == MAX_PLAN_REQUESTS == 12
|
||||
assert len(MAXIMUM_SCENE_REQUESTS) == MAX_PLAN_REQUESTS == 20
|
||||
result = _preview_without_hardware(
|
||||
service,
|
||||
"构建一个十二动作模式",
|
||||
conversation_id="conv-twelve-scene-actions",
|
||||
"构建一个二十动作模式",
|
||||
conversation_id="conv-twenty-scene-actions",
|
||||
assistant_plan=_scene_plan(*MAXIMUM_SCENE_REQUESTS),
|
||||
)
|
||||
|
||||
_assert_successful_preview(result, name="十二动作模式", operation_count=12)
|
||||
_assert_successful_preview(result, name="二十动作模式", operation_count=20)
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
|
||||
|
||||
def test_scene_request_limit_rejects_thirteen_before_any_hardware_or_persistence(
|
||||
def test_scene_request_limit_rejects_twenty_one_before_any_hardware_or_persistence(
|
||||
mode_service: tuple[SeatDemoService, Path],
|
||||
) -> None:
|
||||
service, user_root = mode_service
|
||||
@@ -1225,8 +1233,8 @@ def test_scene_request_limit_rejects_thirteen_before_any_hardware_or_persistence
|
||||
with pytest.raises(ValueError, match="too many requests"):
|
||||
_preview_without_hardware(
|
||||
service,
|
||||
"构建一个十三动作模式",
|
||||
conversation_id="conv-thirteen-scene-actions",
|
||||
"构建一个二十一动作模式",
|
||||
conversation_id="conv-twenty-one-scene-actions",
|
||||
assistant_plan=_scene_plan(*oversized),
|
||||
)
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
|
||||
@@ -26,17 +26,18 @@ CALLBACK_URL = "http://127.0.0.1:8765/api/internal/codex/vehicle-control"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("message", "expected_name"),
|
||||
("message", "expected_name", "expected_operation_count"),
|
||||
(
|
||||
("构建一个星夜模式", "星夜模式"),
|
||||
("创建学习模式", "学习模式"),
|
||||
("创建中秋欢快模式", "中秋欢快模式"),
|
||||
("构建一个星夜模式", "星夜模式", 13),
|
||||
("创建学习模式", "学习模式", 13),
|
||||
("创建中秋欢快模式", "中秋欢快模式", 13),
|
||||
),
|
||||
)
|
||||
def test_structured_scene_turn_reaches_the_complete_local_dry_run_pipeline(
|
||||
tmp_path: Path,
|
||||
message: str,
|
||||
expected_name: str,
|
||||
expected_operation_count: int,
|
||||
) -> None:
|
||||
service = SeatDemoService(REPOSITORY_ROOT)
|
||||
service.artifact_root = tmp_path / "runs"
|
||||
@@ -107,20 +108,37 @@ def test_structured_scene_turn_reaches_the_complete_local_dry_run_pipeline(
|
||||
assert result["orchestration_route"] == "mode_profile_create_preview"
|
||||
assert draft["name"] == expected_name
|
||||
assert draft["generation_source"] == "codex_scene_composition"
|
||||
assert draft["operation_count"] == 2
|
||||
assert draft["operation_count"] == expected_operation_count
|
||||
capabilities = {
|
||||
action["capability"] for action in result["actions"]
|
||||
}
|
||||
assert capabilities == {
|
||||
expected_capabilities = {
|
||||
"climate.set_power",
|
||||
"climate.set_temperature",
|
||||
"climate.set_fan_level",
|
||||
"climate.set_cycle_mode",
|
||||
"window.set_position",
|
||||
"roof.set_shade_position",
|
||||
"seat.set_ventilation",
|
||||
"seat.set_backrest_recline",
|
||||
"seat.set_legrest_height",
|
||||
"ambient_light.set_power",
|
||||
}
|
||||
assert capabilities == expected_capabilities
|
||||
window_actions = [
|
||||
action for action in result["actions"]
|
||||
if action["capability"] == "window.set_position"
|
||||
]
|
||||
assert {
|
||||
action["target"]["zone"] for action in window_actions
|
||||
} == {"front_left", "front_right", "rear_left", "rear_right"}
|
||||
assert all(action["parameters"] == {"position_pct": 0} for action in window_actions)
|
||||
assert not list((tmp_path / "user-modes").rglob("*.yaml"))
|
||||
assert any(event.get("type") == "action.completed" for event in events)
|
||||
assert all("request_token" not in str(event) for event in events)
|
||||
|
||||
|
||||
def test_mid_autumn_cloud_scene_requires_authorized_flashing_light(
|
||||
def test_mid_autumn_cloud_scene_requires_native_and_decorative_light_controls(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
service = SeatDemoService(REPOSITORY_ROOT)
|
||||
@@ -161,6 +179,45 @@ def test_mid_autumn_cloud_scene_requires_authorized_flashing_light(
|
||||
"risk": "low",
|
||||
"slot": 3,
|
||||
},
|
||||
{
|
||||
"id": "ir-slot:light-strip:21",
|
||||
"status": "learned",
|
||||
"enabled": True,
|
||||
"device_id": "light-strip",
|
||||
"device_name": "灯带",
|
||||
"object_names": ["灯带", "LED 灯条"],
|
||||
"card_uid": "A1B2C3D4",
|
||||
"action": "打开",
|
||||
"action_names": ["打开", "开启"],
|
||||
"risk": "low",
|
||||
"slot": 21,
|
||||
},
|
||||
{
|
||||
"id": "ir-slot:light-strip:23",
|
||||
"status": "learned",
|
||||
"enabled": True,
|
||||
"device_id": "light-strip",
|
||||
"device_name": "灯带",
|
||||
"object_names": ["灯带", "LED 灯条"],
|
||||
"card_uid": "A1B2C3D4",
|
||||
"action": "切换为红色",
|
||||
"action_names": ["切换为红色", "设置为红色"],
|
||||
"risk": "low",
|
||||
"slot": 23,
|
||||
},
|
||||
{
|
||||
"id": "ir-slot:ambient-light:31",
|
||||
"status": "learned",
|
||||
"enabled": True,
|
||||
"device_id": "ambient-light",
|
||||
"device_name": "外设氛围灯",
|
||||
"object_names": ["外设氛围灯", "氛围彩灯玩具"],
|
||||
"card_uid": "B1C2D3E4",
|
||||
"action": "打开",
|
||||
"action_names": ["打开", "开启"],
|
||||
"risk": "low",
|
||||
"slot": 31,
|
||||
},
|
||||
]
|
||||
)
|
||||
handler = object.__new__(SeatDemoHandler)
|
||||
@@ -235,5 +292,29 @@ def test_mid_autumn_cloud_scene_requires_authorized_flashing_light(
|
||||
assert response["success"] is True
|
||||
assert result["status"] == "succeeded"
|
||||
assert result["execution_mode"] == "dry_run"
|
||||
assert [item["slot"] for item in result["infrared_actions"]] == [2, 3]
|
||||
assert [item["slot"] for item in result["infrared_actions"]] == [
|
||||
2,
|
||||
31,
|
||||
21,
|
||||
]
|
||||
capabilities = {
|
||||
action["capability"]
|
||||
for action in result["actions"]
|
||||
}
|
||||
assert "climate.set_temperature" in capabilities
|
||||
assert "ambient_light.set_power" in capabilities
|
||||
assert "roof.set_shade_position" in capabilities
|
||||
assert {
|
||||
"climate.set_power",
|
||||
"climate.set_temperature",
|
||||
"climate.set_fan_level",
|
||||
"climate.set_cycle_mode",
|
||||
"window.set_position",
|
||||
"seat.set_ventilation",
|
||||
"seat.set_backrest_recline",
|
||||
"seat.set_legrest_height",
|
||||
"ambient_light.set_power",
|
||||
"infrared.emit",
|
||||
} <= capabilities
|
||||
assert result["mode_profile_draft"]["operation_count"] == 16
|
||||
assert result["mode_profile_draft"]["domains"][-1] == "self_extension"
|
||||
|
||||
@@ -15,7 +15,13 @@ from cognitive_agent.application.codex_assistant import (
|
||||
_catalog_exact_scene_template_plan,
|
||||
)
|
||||
from cognitive_agent.application.mode_profile import ModeProfileRegistry
|
||||
from cognitive_agent.application.scene_composer import normalize_scene_output
|
||||
from cognitive_agent.application.scene_composer import (
|
||||
normalize_scene_output,
|
||||
scene_context_for_mode_creation,
|
||||
scene_required_native_fallbacks,
|
||||
scene_required_peripheral_refs,
|
||||
scene_semantic_guidance,
|
||||
)
|
||||
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
|
||||
from cognitive_agent.application.self_extension import (
|
||||
ExtensionIntegrationError,
|
||||
@@ -212,6 +218,33 @@ def test_scene_composer_mixed_can_ir_preview_save_and_revalidate(
|
||||
*template_external["requests"],
|
||||
],
|
||||
}
|
||||
template_context = scene_context_for_mode_creation(
|
||||
context,
|
||||
"创建一个浪漫模式",
|
||||
)
|
||||
template_semantics = scene_semantic_guidance("浪漫模式")
|
||||
template_native_fallbacks = scene_required_native_fallbacks(
|
||||
"浪漫模式",
|
||||
template_context,
|
||||
include_default_bundle=True,
|
||||
)
|
||||
template_semantics["required_native_domains"] = [
|
||||
str(item["domain"])
|
||||
for item in template_native_fallbacks
|
||||
]
|
||||
template_semantics["native_fallback_requests"] = template_native_fallbacks
|
||||
template_semantics["peripheral_policy"] = {
|
||||
"required_refs": scene_required_peripheral_refs(
|
||||
"浪漫模式",
|
||||
template_context,
|
||||
include_default_bundle=True,
|
||||
),
|
||||
}
|
||||
template_context["scene_semantics"] = template_semantics
|
||||
augmented_template = CodexAssistantService._with_required_scene_peripherals(
|
||||
augmented_template,
|
||||
template_context,
|
||||
)
|
||||
template_preview = handler._preview_mode_with_scene_peripherals(
|
||||
"/api/requests/preview",
|
||||
{
|
||||
@@ -224,11 +257,30 @@ def test_scene_composer_mixed_can_ir_preview_save_and_revalidate(
|
||||
on_commands_validated=None,
|
||||
)
|
||||
assert template_preview["status"] == "succeeded"
|
||||
assert template_preview["mode_profile_draft"]["source_template_id"] == "romantic"
|
||||
assert template_preview["mode_profile_draft"]["operation_count"] == (
|
||||
len(template_preview["intent_ir"]["spec"]["actions"])
|
||||
assert template_preview["mode_profile_draft"]["generation_source"] == (
|
||||
"codex_scene_composition"
|
||||
)
|
||||
assert template_preview["mode_profile_draft"]["domains"][-1] == "self_extension"
|
||||
template_actions = template_preview["intent_ir"]["spec"]["actions"]
|
||||
assert template_preview["mode_profile_draft"]["operation_count"] == (
|
||||
len(template_actions)
|
||||
)
|
||||
assert {
|
||||
"climate",
|
||||
"ambient_light",
|
||||
"roof",
|
||||
"self_extension",
|
||||
}.issubset(template_preview["mode_profile_draft"]["domains"])
|
||||
template_capabilities = {
|
||||
action["capability"]
|
||||
for action in template_actions
|
||||
}
|
||||
assert any(
|
||||
capability.startswith("climate.")
|
||||
for capability in template_capabilities
|
||||
)
|
||||
assert "ambient_light.set_power" in template_capabilities
|
||||
assert "roof.set_shade_position" in template_capabilities
|
||||
assert "infrared.emit" in template_capabilities
|
||||
|
||||
explicit = handler._execute_request_with_peripherals(
|
||||
"/api/requests/preview",
|
||||
|
||||
@@ -60,6 +60,27 @@ def _action_map(
|
||||
}
|
||||
|
||||
|
||||
def _assert_default_control_bundle(result: Mapping[str, object]) -> None:
|
||||
actions = _action_map(result)
|
||||
assert actions["climate.set_power", "vehicle"] == {"state": "on"}
|
||||
assert ("climate.set_temperature", "vehicle") in actions
|
||||
assert ("climate.set_fan_level", "vehicle") in actions
|
||||
assert ("climate.set_cycle_mode", "vehicle") in actions
|
||||
for target in ("front_left", "front_right", "rear_left", "rear_right"):
|
||||
assert actions["window.set_position", target] == {"position_pct": 0}
|
||||
assert ("roof.set_shade_position", "vehicle") in actions
|
||||
assert actions["seat.set_ventilation", "passenger"] == {
|
||||
"state": "on",
|
||||
"level": 1,
|
||||
}
|
||||
assert ("seat.set_backrest_recline", "passenger") in actions
|
||||
assert ("seat.set_legrest_height", "passenger") in actions
|
||||
assert any(
|
||||
capability.startswith("ambient_light.")
|
||||
for capability, _target in actions
|
||||
)
|
||||
|
||||
|
||||
def _draft_reference(result: Mapping[str, object]) -> dict[str, str]:
|
||||
draft = result.get("mode_profile_draft")
|
||||
assert isinstance(draft, Mapping)
|
||||
@@ -225,7 +246,7 @@ def test_all_production_scene_templates_complete_the_full_local_dry_run(
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
|
||||
|
||||
def test_viewing_template_creates_an_unbound_audited_copy_without_codex(
|
||||
def test_viewing_template_seeds_a_complete_unbound_preview_without_codex(
|
||||
hybrid_service: tuple[SeatDemoService, AssistantCapabilityCatalog, Path],
|
||||
) -> None:
|
||||
service, catalog, user_root = hybrid_service
|
||||
@@ -249,15 +270,16 @@ def test_viewing_template_creates_an_unbound_audited_copy_without_codex(
|
||||
assert raw_result["execution_mode"] == "dry_run"
|
||||
assert raw_result["hardware_execution_allowed"] is False
|
||||
assert draft["name"] == "观影模式"
|
||||
assert draft["generation_source"] == "scene_template_copy"
|
||||
assert draft["source_template_id"] == "cinema"
|
||||
assert draft["source_template_revision"]
|
||||
assert draft["generation_source"] == "codex_scene_composition"
|
||||
assert "source_template_id" not in draft
|
||||
assert "source_template_revision" not in draft
|
||||
assert "existing_mode_id" not in draft
|
||||
assert "existing_mode_revision" not in draft
|
||||
assert raw_result["mode_creation"]["source_template_id"] == "cinema"
|
||||
assert passed_plans[0]["metadata"]["source"] == "scene_template"
|
||||
assert "source_template_id" not in raw_result["mode_creation"]
|
||||
assert "metadata" not in passed_plans[0]
|
||||
_assert_default_control_bundle(raw_result)
|
||||
assert events[0]["engine"] == "n7web-scene-template"
|
||||
assert "独立草稿" in response["answer"]
|
||||
assert "创建阶段不会下发" in response["answer"]
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
client.start_structured_turn.assert_not_called()
|
||||
client.start_turn.assert_not_called()
|
||||
@@ -319,7 +341,6 @@ def test_purpose_descriptions_use_a_concise_name_and_zero_model_template_route(
|
||||
conversation_id=f"conv-template-{case_id}",
|
||||
)
|
||||
|
||||
template = service.scene_templates.get_template(template_id)
|
||||
draft = raw_result["mode_profile_draft"]
|
||||
assert response["route"] == "scene_template"
|
||||
assert response["success"] is True
|
||||
@@ -328,13 +349,11 @@ def test_purpose_descriptions_use_a_concise_name_and_zero_model_template_route(
|
||||
assert raw_result["execution_mode"] == "dry_run"
|
||||
assert raw_result["hardware_execution_allowed"] is False
|
||||
assert draft["name"] == expected_name
|
||||
assert draft["generation_source"] == "scene_template_copy"
|
||||
assert draft["source_template_id"] == template_id
|
||||
assert draft["operation_count"] == sum(
|
||||
operation.domain != "seat" or operation.target_scope == "passenger"
|
||||
for operation in template.operations
|
||||
)
|
||||
assert passed_plans[0]["metadata"]["template_id"] == template_id
|
||||
assert draft["generation_source"] == "codex_scene_composition"
|
||||
assert "source_template_id" not in draft
|
||||
assert draft["operation_count"] == len(_actions(raw_result))
|
||||
assert "metadata" not in passed_plans[0]
|
||||
_assert_default_control_bundle(raw_result)
|
||||
assert events[0]["engine"] == "n7web-scene-template"
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
client.start_structured_turn.assert_not_called()
|
||||
@@ -396,7 +415,14 @@ def test_seating_comfort_requests_preview_only_passenger_with_no_model_or_hardwa
|
||||
assert result["execution_mode"] == "dry_run"
|
||||
assert result["hardware_execution_allowed"] is False
|
||||
assert result["mode_profile_draft"]["name"] == expected_name
|
||||
assert result["mode_profile_draft"]["source_template_id"] == template_id
|
||||
if "座椅" in message:
|
||||
assert result["mode_profile_draft"]["source_template_id"] == template_id
|
||||
else:
|
||||
assert result["mode_profile_draft"]["generation_source"] == (
|
||||
"codex_scene_composition"
|
||||
)
|
||||
assert "source_template_id" not in result["mode_profile_draft"]
|
||||
_assert_default_control_bundle(result)
|
||||
seat_actions = {
|
||||
(capability, target): parameters
|
||||
for (capability, target), parameters in _action_map(result).items()
|
||||
@@ -524,8 +550,10 @@ def test_template_alias_keeps_the_user_requested_draft_name(
|
||||
draft = raw_result["mode_profile_draft"]
|
||||
assert response["route"] == "scene_template"
|
||||
assert draft["name"] == "影院模式"
|
||||
assert draft["source_template_id"] == "cinema"
|
||||
assert passed_plans[0]["metadata"]["template_id"] == "cinema"
|
||||
assert draft["generation_source"] == "codex_scene_composition"
|
||||
assert "source_template_id" not in draft
|
||||
assert "metadata" not in passed_plans[0]
|
||||
_assert_default_control_bundle(raw_result)
|
||||
client.start_structured_turn.assert_not_called()
|
||||
client.start_turn.assert_not_called()
|
||||
|
||||
@@ -574,7 +602,7 @@ def test_template_copy_can_be_revised_twice_without_losing_sibling_actions(
|
||||
assert shade_revised["execution_mode"] == final["execution_mode"] == "dry_run"
|
||||
assert shade_revised["hardware_execution_allowed"] is False
|
||||
assert final["hardware_execution_allowed"] is False
|
||||
assert len(built_actions) == len(shade_actions) == len(final_actions) == 10
|
||||
assert len(built_actions) == len(shade_actions) == len(final_actions) == 16
|
||||
assert built_actions[shade_key] == {"position_pct": 0}
|
||||
assert shade_actions[shade_key] == final_actions[shade_key] == {"position_pct": 100}
|
||||
assert built_actions[temperature_key] == shade_actions[temperature_key] == {"temperature_c": 24}
|
||||
@@ -592,7 +620,8 @@ def test_template_copy_can_be_revised_twice_without_losing_sibling_actions(
|
||||
assert final_draft["draft_revision"] == 3
|
||||
assert second_draft["generation_source"] == "draft_revision"
|
||||
assert final_draft["generation_source"] == "draft_revision"
|
||||
assert second_draft["source_template_id"] == final_draft["source_template_id"] == ("cinema")
|
||||
assert "source_template_id" not in second_draft
|
||||
assert "source_template_id" not in final_draft
|
||||
assert "existing_mode_id" not in final_draft
|
||||
assert not list(user_root.rglob("*.yaml"))
|
||||
|
||||
@@ -626,7 +655,8 @@ def test_saving_a_template_copy_never_mutates_template_and_registered_mode_wins_
|
||||
assert list(user_root.rglob(f"{saved_profile['id']}.yaml"))
|
||||
assert _template_hashes() == hashes_before
|
||||
assert service.scene_templates.get_template("cinema").revision == template_revision
|
||||
assert first_plans[0]["metadata"]["template_id"] == "cinema"
|
||||
assert "metadata" not in first_plans[0]
|
||||
_assert_default_control_bundle(built)
|
||||
first_client.start_structured_turn.assert_not_called()
|
||||
|
||||
response, registered, events, second_client, passed_plans = _assistant_preview(
|
||||
|
||||
Reference in New Issue
Block a user