From 0a8765a236ed1d253ef18ecc856cbe881fa3e52a Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:12:08 -0700 Subject: [PATCH] fix(gateway): model inheritance gated on the model section, not config.yaml existence (#86212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit profiles.create inherits the launch profile's provider+model when the caller doesn't pin one — but the gate was 'config.yaml doesn't exist yet'. Voice-section mirroring (#85755) runs FIRST and legitimately creates config.yaml (tts/stt), so inheritance silently skipped for every non-clone profile since: the bot's editor showed 'Inherit (launch profile)' while the profile actually had NO model section, and the first message failed with 'No inference provider configured' even though the main agent was authenticated and working (Bot Mode tester report, screenshots). Gate on what we actually care about: the profile's own raw config lacking a complete model section (provider+default). Clones bring their own section and stay untouched; explicit pins unchanged. E2E: create receipt now model_inherited=true and the fresh profile's config.yaml carries the launch profile's provider/model. --- tui_gateway/methods_profiles.py | 38 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/tui_gateway/methods_profiles.py b/tui_gateway/methods_profiles.py index b1450472c6..f2048d7787 100644 --- a/tui_gateway/methods_profiles.py +++ b/tui_gateway/methods_profiles.py @@ -357,20 +357,36 @@ def _(rid, params: dict) -> dict: model_set = True except Exception: pass - elif is_truthy_value(params.get("mirror_credentials", True)) and not (path / "config.yaml").exists(): - # No explicit pin and no cloned config: inherit the launch profile's - # provider+model so the first turn resolves. Same writer as the pin. + elif is_truthy_value(params.get("mirror_credentials", True)): + # No explicit pin: inherit the launch profile's provider+model so the + # first turn resolves. Gate on the MODEL SECTION being absent, not on + # config.yaml existing — earlier mirroring steps (voice sections, + # #85755) legitimately create the file first, and a file-existence + # gate silently skipped inheritance for every non-clone bot + # ("No inference provider configured" on first message, tester + # report). Clones bring their own model section and stay untouched. try: - from hermes_cli.config import load_config_readonly + from hermes_cli.config import load_config_readonly, read_user_config_raw from hermes_cli.web_routers.profiles import _write_profile_model + from hermes_constants import ( + reset_hermes_home_override, + set_hermes_home_override, + ) - cfg = load_config_readonly() or {} - model_cfg = cfg.get("model") or {} - inherited_provider = str(model_cfg.get("provider") or "") - inherited_model = str(model_cfg.get("default") or "") - if inherited_provider and inherited_model: - _write_profile_model(path, inherited_provider, inherited_model) - mirrored["model_inherited"] = True + token = set_hermes_home_override(str(path)) + try: + dst_model = (read_user_config_raw() or {}).get("model") or {} + finally: + reset_hermes_home_override(token) + + if not (dst_model.get("provider") and dst_model.get("default")): + cfg = load_config_readonly() or {} + model_cfg = cfg.get("model") or {} + inherited_provider = str(model_cfg.get("provider") or "") + inherited_model = str(model_cfg.get("default") or "") + if inherited_provider and inherited_model: + _write_profile_model(path, inherited_provider, inherited_model) + mirrored["model_inherited"] = True except Exception: pass