fix(gateway): don't restore a bare billing provider as the resumed session's provider

`_stored_session_runtime_overrides` restored the session provider from
`billing_provider` when `model_config` had no explicit provider. For a
`custom:<name>` endpoint that only ran normal turns (no `/model` switch), the
persisted `billing_provider` is the bare billing bucket `"custom"`, which
`agent_init` treats as non-routable, so `session.resume` failed with
"No LLM provider configured" even though new chats and CLI `--resume` work.

Only restore an explicit `model_config.provider`; skip a bare billing bucket
(`auto`/`openrouter`/`custom`) so resume falls back to the configured default,
matching the CLI path.

Fixes #44022
This commit is contained in:
Haozhe Zhang
2026-06-13 05:51:05 -07:00
committed by Teknium
parent cb125c2b3f
commit e256f4aae4
2 changed files with 45 additions and 5 deletions
+16 -5
View File
@@ -1477,6 +1477,11 @@ def _resolve_startup_runtime() -> tuple[str, str | None]:
return model, None
# Bare billing buckets are not routable provider identities (kept in parity with the
# provider gate in agent_init). Restoring one as a session provider override breaks resume.
_BARE_BILLING_PROVIDERS = {"auto", "openrouter", "custom"}
def _stored_session_runtime_overrides(row: dict | None) -> dict:
"""Return runtime fields persisted with a stored session.
@@ -1503,12 +1508,18 @@ def _stored_session_runtime_overrides(row: dict | None) -> dict:
overrides: dict = {}
model = str(row.get("model") or model_config.get("model") or "").strip()
provider = str(
model_config.get("provider")
or model_config.get("billing_provider")
or row.get("billing_provider")
or ""
# ``billing_provider`` is only the billing bucket — for a custom endpoint it is the
# bare class ``"custom"``, which agent_init treats as non-routable, so restoring it as
# the provider override makes ``session.resume`` fail with "No LLM provider configured".
# Only restore an explicit provider; otherwise leave it unset so resume falls back to
# the configured default, matching the working CLI path.
explicit_provider = str(model_config.get("provider") or "").strip()
billing_provider = str(
model_config.get("billing_provider") or row.get("billing_provider") or ""
).strip()
provider = explicit_provider
if not provider and billing_provider.lower() not in _BARE_BILLING_PROVIDERS:
provider = billing_provider
base_url = str(model_config.get("base_url") or "").strip()
api_mode = str(model_config.get("api_mode") or "").strip()
reasoning_config = model_config.get("reasoning_config")