mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-19 14:12:01 +00:00
fix(#4536): gate service tier on effective send model, not saved default
This commit is contained in:
committed by
nesquena-hermes
parent
4d64c18832
commit
2f2d36d4eb
+21
-7
@@ -3544,19 +3544,33 @@ def _public_main_service_tier(model_cfg: dict) -> str:
|
||||
return "priority" if service_tier == "priority" else ""
|
||||
|
||||
|
||||
def _main_model_request_overrides(config_data: dict) -> dict:
|
||||
"""Return supported runtime request overrides for the main chat model."""
|
||||
def _main_model_request_overrides(
|
||||
config_data: dict,
|
||||
effective_model: str | None = None,
|
||||
effective_provider: str | None = None,
|
||||
) -> dict:
|
||||
"""Return supported runtime request overrides for the main chat model.
|
||||
|
||||
When *effective_model* / *effective_provider* are supplied, the
|
||||
service-tier gate checks those instead of the saved default model,
|
||||
so a per-session model switch to a non-OpenAI provider does not
|
||||
leak ``service_tier`` onto an unsupported request.
|
||||
"""
|
||||
if not isinstance(config_data, dict):
|
||||
return {}
|
||||
model_cfg = config_data.get("model", {})
|
||||
if not isinstance(model_cfg, dict):
|
||||
return {}
|
||||
overrides = {}
|
||||
model_id = str(model_cfg.get("default") or model_cfg.get("name") or "").strip()
|
||||
resolved_provider = str(model_cfg.get("provider") or "").strip().lower()
|
||||
if not resolved_provider:
|
||||
_, resolved_provider, _ = resolve_model_provider(model_id)
|
||||
if _main_model_supports_service_tier(model_id, resolved_provider):
|
||||
gate_model = effective_model
|
||||
gate_provider = effective_provider
|
||||
if not gate_model:
|
||||
gate_model = str(model_cfg.get("default") or model_cfg.get("name") or "").strip()
|
||||
if not gate_provider:
|
||||
gate_provider = str(model_cfg.get("provider") or "").strip().lower()
|
||||
if not gate_provider:
|
||||
_, gate_provider, _ = resolve_model_provider(gate_model)
|
||||
if _main_model_supports_service_tier(gate_model, gate_provider):
|
||||
service_tier = str(model_cfg.get("service_tier") or "").strip().lower()
|
||||
if service_tier == "priority":
|
||||
overrides["service_tier"] = "priority"
|
||||
|
||||
@@ -591,6 +591,15 @@ def _run_gateway_chat_streaming(
|
||||
prefill_messages = []
|
||||
base_url = _gateway_base_url(cfg)
|
||||
api_key = _gateway_api_key()
|
||||
try:
|
||||
from api.config import _main_model_request_overrides
|
||||
_gw_overrides = _main_model_request_overrides(
|
||||
cfg,
|
||||
effective_model=model,
|
||||
effective_provider=model_provider,
|
||||
)
|
||||
except Exception:
|
||||
_gw_overrides = {}
|
||||
# Capability gate: use runs API when gateway advertises approval support.
|
||||
_use_runs_api = _gateway_use_runs_api_enabled(cfg) and gateway_supports_approval(base_url, api_key)
|
||||
if _use_runs_api:
|
||||
@@ -599,6 +608,8 @@ def _run_gateway_chat_streaming(
|
||||
body_extras["provider"] = model_provider
|
||||
if reasoning_effort is not None:
|
||||
body_extras["reasoning_effort"] = reasoning_effort
|
||||
if _gw_overrides.get("service_tier"):
|
||||
body_extras["service_tier"] = _gw_overrides["service_tier"]
|
||||
try:
|
||||
final_text, usage = _run_gateway_runs_api_streaming(
|
||||
session_id, msg_text, model, workspace, stream_id,
|
||||
@@ -661,6 +672,8 @@ def _run_gateway_chat_streaming(
|
||||
body["provider"] = model_provider
|
||||
if reasoning_effort is not None:
|
||||
body["reasoning_effort"] = reasoning_effort
|
||||
if _gw_overrides.get("service_tier"):
|
||||
body["service_tier"] = _gw_overrides["service_tier"]
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(body).encode("utf-8"),
|
||||
|
||||
+5
-1
@@ -7026,7 +7026,11 @@ def _run_agent_streaming(
|
||||
_prefill_context = _load_webui_prefill_context(_cfg)
|
||||
_prefill_messages = _prefill_messages_with_webui_context(_prefill_context, _cfg)
|
||||
_prefill_messages = _normalize_prefill_messages_before_user_turn(_prefill_messages)
|
||||
_main_request_overrides = _main_model_request_overrides(_cfg)
|
||||
_main_request_overrides = _main_model_request_overrides(
|
||||
_cfg,
|
||||
effective_model=resolved_model,
|
||||
effective_provider=resolved_provider,
|
||||
)
|
||||
put('context_status', {
|
||||
'session_id': session_id,
|
||||
'prefill': _public_prefill_context_status(_prefill_context),
|
||||
|
||||
@@ -94,6 +94,41 @@ class TestIssue4536ServiceTier:
|
||||
|
||||
assert payload["main"]["service_tier"] == ""
|
||||
|
||||
def test_effective_model_gates_service_tier_forwarding(self):
|
||||
"""service_tier must be gated on the effective send model, not the saved default."""
|
||||
from api import config
|
||||
|
||||
cfg_priority = {"model": {"provider": "openai", "default": "gpt-5.5", "service_tier": "priority"}}
|
||||
|
||||
openai_effective = config._main_model_request_overrides(
|
||||
cfg_priority, effective_model="gpt-5.5", effective_provider="openai",
|
||||
)
|
||||
assert openai_effective.get("service_tier") == "priority"
|
||||
|
||||
openrouter_effective = config._main_model_request_overrides(
|
||||
cfg_priority, effective_model="meta-llama/llama-3.1", effective_provider="openrouter",
|
||||
)
|
||||
assert openrouter_effective == {}
|
||||
|
||||
custom_effective = config._main_model_request_overrides(
|
||||
cfg_priority, effective_model="my-local-model", effective_provider="custom:local",
|
||||
)
|
||||
assert custom_effective == {}
|
||||
|
||||
codex_effective = config._main_model_request_overrides(
|
||||
cfg_priority, effective_model="gpt-5.3-codex", effective_provider="openai-codex",
|
||||
)
|
||||
assert codex_effective == {}
|
||||
|
||||
def test_no_effective_params_falls_back_to_saved_default(self):
|
||||
"""Without effective_model/provider, the function still reads the saved config."""
|
||||
from api import config
|
||||
|
||||
result = config._main_model_request_overrides(
|
||||
{"model": {"provider": "openai", "default": "gpt-5.5", "service_tier": "priority"}},
|
||||
)
|
||||
assert result == {"service_tier": "priority"}
|
||||
|
||||
def test_switching_main_model_away_from_openai_clears_service_tier(self, monkeypatch, tmp_path):
|
||||
"""A non-OpenAI default-model save should remove stale OpenAI service-tier state."""
|
||||
from api import config
|
||||
|
||||
Reference in New Issue
Block a user