diff --git a/api/routes.py b/api/routes.py index ee14690c..235e857d 100644 --- a/api/routes.py +++ b/api/routes.py @@ -19367,11 +19367,15 @@ def _handle_chat_start(handler, body, diag=None): if "model_provider" in body else getattr(s, "model_provider", None) ) +<<<<<<< ours _pp_provider, _pp_default, _pp_cfg = _read_profile_model_config(s, requested_provider) +======= +>>>>>>> theirs explicit_model_pick = bool(body.get("explicit_model_pick")) moa_config = None + gateway_chat_enabled = webui_gateway_chat_enabled(get_config()) if body.get("moa_config"): - if webui_gateway_chat_enabled(get_config()): + if gateway_chat_enabled: return bad(handler, "MoA override is unavailable on gateway-backed sessions", 409) from api.commands import resolve_moa_config @@ -19379,6 +19383,7 @@ def _handle_chat_start(handler, body, diag=None): moa_config = resolve_moa_config() except RuntimeError as e: return bad(handler, str(e), 503) + _pp_provider, _pp_default = _read_profile_model_config(s, requested_provider) diag.stage("resolve_model_provider") if diag else None model, model_provider, normalized_model = _resolve_compatible_session_model_state( requested_model, @@ -19401,18 +19406,22 @@ def _handle_chat_start(handler, body, diag=None): # with start_session_turn so both entry points behave identically # under runtime_adapter_enabled() / runtime_adapter_runner_enabled() # — Q-2979-A2 / Copilot discussion_r3305864087/r3305864173). + start_run_kwargs = { + "msg": msg, + "attachments": attachments, + "workspace": workspace, + "model": model, + "model_provider": model_provider, + "normalized_model": normalized_model, + "source": "webui", + "route": "/api/chat/start", + "diag": diag, + } + if not gateway_chat_enabled and moa_config is not None: + start_run_kwargs["moa_config"] = moa_config response = _start_run( s, - msg=msg, - attachments=attachments, - workspace=workspace, - model=model, - model_provider=model_provider, - normalized_model=normalized_model, - source="webui", - route="/api/chat/start", - diag=diag, - moa_config=moa_config, + **start_run_kwargs, ) # Map adapter-selection NotImplementedError (501) onto the legacy # bad-request response shape that this route exposed historically diff --git a/tests/test_issue5057_moa_webui_route.py b/tests/test_issue5057_moa_webui_route.py index d516de8d..391852bb 100644 --- a/tests/test_issue5057_moa_webui_route.py +++ b/tests/test_issue5057_moa_webui_route.py @@ -127,3 +127,64 @@ def test_moa_config_is_per_turn_not_persisted(): js_source = js_path.read_text(encoding="utf-8") assert "moa_config:_pendingMoaConfig?true:undefined" in js_source assert "_pendingMoaConfig=null" in js_source + + +def test_moa_gateway_chat_start_fails_closed(monkeypatch, tmp_path): + """Gateway-backed WebUI sessions must reject /moa until the gateway consumes runtime overrides.""" + import api.commands as commands + import api.routes as routes + + class _Handler: + def __init__(self): + import io + + self.status = None + self.response_headers = [] + self.wfile = io.BytesIO() + + def send_response(self, status): + self.status = status + + def send_header(self, key, value): + self.response_headers.append((key, value)) + + def end_headers(self): + self.response_headers.append(("__end__", "")) + + class _Session: + session_id = "sess-moa-gateway" + workspace = str(tmp_path) + model = "gpt-5.5" + model_provider = "openai-codex" + profile = "default" + messages = [] + context_messages = [] + pending_user_message = None + + def start_run(*_args, **_kwargs): # pragma: no cover - should fail before run start + raise AssertionError("gateway-backed /moa must fail closed before starting a run") + + def resolve_moa_config(): # pragma: no cover - should fail before resolving MoA + raise AssertionError("gateway-backed /moa must fail before resolving MoA config") + + monkeypatch.setattr(routes, "get_session", lambda _sid: _Session()) + monkeypatch.setattr(routes, "_resolve_chat_workspace_with_recovery", lambda _s, _w: str(tmp_path)) + monkeypatch.setattr(routes, "_start_run", start_run) + monkeypatch.setattr(routes, "get_config", lambda: {"chat_backend": "gateway"}) + monkeypatch.setattr(routes, "webui_gateway_chat_enabled", lambda _cfg: True) + monkeypatch.setattr(commands, "resolve_moa_config", resolve_moa_config) + + handler = _Handler() + routes._handle_chat_start( + handler, + { + "session_id": "sess-moa-gateway", + "message": "diagnose issue", + "workspace": str(tmp_path), + "moa_config": True, + }, + ) + + body = json.loads(handler.wfile.getvalue().decode("utf-8")) + assert handler.status == 409 + assert body["error"] == "MoA override is unavailable on gateway-backed sessions"