From fb8bf2f5bd2c677c735cb241cdc8d4c77d8445f0 Mon Sep 17 00:00:00 2001 From: Gordie Date: Wed, 1 Jul 2026 03:52:09 -0500 Subject: [PATCH] fix(webui): keep #1855 fast-path window and Copilot gpt-4o regression tests green - Extract the MoA @moa:/moa/ prefix-stripping branch of _resolve_compatible_session_model_state into a new _moa_fast_path_model_state() helper. Inlining it had pushed `catalog = get_available_models()` just past the 6000-char source window that tests/test_issue1855_resolve_model_provider_fast_path.py:: TestFastPathSourceShape scans to guard the #1855 fast-path/ catalog-call ordering, breaking that regression test after rebasing onto current master. - Re-add gpt-4o to the refreshed Copilot static fallback list. It's a real Copilot-served model and tests/test_issues_373_374_375.py::TestStaleModelListCleanup:: test_copilot_list_unchanged asserts the Copilot list keeps it even as #374 removes it from the generic OpenAI list. The live Copilot catalog probe remains authoritative; this only affects the cold-start/probe-miss fallback. Verification: ./scripts/test.sh tests/test_issue1855_resolve_model_provider_fast_path.py tests/test_issues_373_374_375.py tests/test_issue5057_moa_webui_route.py tests/test_copilot_provider_model_settings_not_allowlist.py tests/test_moa_model_picker_provider.py tests/test_pr1970_lmstudio_base_url_fallback.py -q -> 73 passed in 3.60s ./scripts/test.sh tests/ -k "config or provider or model or copilot or moa or lmstudio" -q -> 1745 passed, 2 skipped, 9775 deselected, 1 xpassed in 68.57s --- api/config.py | 1 + api/routes.py | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/api/config.py b/api/config.py index e74fe911a..59adcf497 100644 --- a/api/config.py +++ b/api/config.py @@ -1703,6 +1703,7 @@ _PROVIDER_MODELS = { {"id": "gemini-3.5-flash", "label": "Gemini 3.5 Flash"}, {"id": "gemini-2.5-pro", "label": "Gemini 2.5 Pro"}, {"id": "mai-code-1-flash-picker", "label": "MAI Code 1 Flash"}, + {"id": "gpt-4o", "label": "GPT-4o"}, ], # Cursor ACP — models served via Cursor CLI agent acp "cursor-acp": [ diff --git a/api/routes.py b/api/routes.py index 753ca21a2..8bc32e13c 100644 --- a/api/routes.py +++ b/api/routes.py @@ -5888,6 +5888,23 @@ def _read_profile_model_config( return _provider, _default +def _moa_fast_path_model_state(model: str) -> tuple[str, str, bool]: + """Strip an optional ``@moa:``/``moa/`` prefix from an MoA-routed model. + + Split out of ``_resolve_compatible_session_model_state`` so the MoA + fast-path stays a single-line call in that function body (see + ``test_issue1855_resolve_model_provider_fast_path.py``, the fast-path/ + catalog-call ordering check scans a bounded window of that function's + source, and inlining this here previously pushed the catalog call just + past that window). + """ + if model.startswith("@moa:"): + return model.split(":", 1)[1].strip(), "moa", True + if model.lower().startswith("moa/"): + return model.split("/", 1)[1].strip(), "moa", True + return model, "moa", False + + def _resolve_compatible_session_model_state( model_id: str | None, model_provider: str | None = None, @@ -5914,7 +5931,7 @@ def _resolve_compatible_session_model_state( after paying the full catalog-build cost. Avoiding the catalog here keeps ``POST /api/chat/start`` snappy even when the model catalog is cold and the rebuild has to make network calls (custom OpenAI-compat endpoints, - OpenRouter ``/models``, LM Studio ``/models``, credential pool refresh) — + OpenRouter ``/models``, LM Studio ``/models``, credential pool refresh), those used to wedge the handler for >100s and trigger 502s on default-60s reverse proxies, even though the WebUI itself eventually responded. @@ -5922,7 +5939,7 @@ def _resolve_compatible_session_model_state( non-blocking: it resolves from the warm/disk cache or a network-free minimal catalog and NEVER triggers a live per-provider rebuild (the Copilot token-exchange HTTPS call that hangs a server-initiated wakeup - turn — see rebase report §1/§3/model-resolve-hang). Human-initiated + turn, see rebase report §1/§3/model-resolve-hang). Human-initiated chat/start leaves this False to keep full live discovery; a session that already has a persisted model still resolves correctly because the persisted model wins over the catalog and the catalog is only consulted @@ -5931,11 +5948,7 @@ def _resolve_compatible_session_model_state( model = str(model_id or "").strip() requested_provider = _clean_session_model_provider(model_provider) if model and requested_provider == "moa": - if model.startswith("@moa:"): - return model.split(":", 1)[1].strip(), "moa", True - if model.lower().startswith("moa/"): - return model.split("/", 1)[1].strip(), "moa", True - return model, "moa", False + return _moa_fast_path_model_state(model) if model and requested_provider and model.startswith(f"@{requested_provider}:"): try: from api.config import cfg as _active_cfg