Fix new chats using profile default model

This commit is contained in:
dobby-d-elf committed 2026-05-20 10:57:04 -06:00
1 parent dc57d358eb
commit 19ad20afff
4 files changed
+59 -21

No files matched your search

+29 -2
View File
@@ -1340,6 +1340,27 @@ def get_session(sid, metadata_only=False):
return s
raise KeyError(sid)
def _profile_default_model_state(profile=None):
"""Return the default model/provider configured for *profile*."""
default_model = ""
default_provider = None
try:
from api.profiles import get_hermes_home_for_profile
config_path = Path(get_hermes_home_for_profile(profile)) / "config.yaml"
config_data = _cfg._load_yaml_config_file(config_path)
except Exception:
config_data = {}
model_cfg = config_data.get("model", {}) if isinstance(config_data, dict) else {}
if isinstance(model_cfg, str):
default_model = model_cfg.strip()
elif isinstance(model_cfg, dict):
default_model = str(model_cfg.get("default") or "").strip()
default_provider = str(model_cfg.get("provider") or "").strip() or None
return default_model or get_effective_default_model(), default_provider
def new_session(workspace=None, model=None, profile=None, model_provider=None, project_id=None, worktree_info=None):
"""Create a new in-memory session.
@@ -1372,13 +1393,19 @@ def new_session(workspace=None, model=None, profile=None, model_provider=None, p
profile = get_active_profile_name()
except ImportError:
profile = None
effective_model = model or get_effective_default_model()
if model:
effective_model = model
effective_model_provider = model_provider
else:
effective_model, effective_model_provider = _profile_default_model_state(profile)
if model_provider:
effective_model_provider = model_provider
wt = worktree_info if isinstance(worktree_info, dict) else None
workspace_path = (wt.get('path') if wt and wt.get('path') else workspace) if wt else workspace
s = Session(
workspace=workspace_path or get_last_workspace(),
model=effective_model,
model_provider=model_provider,
model_provider=effective_model_provider,
profile=profile,
project_id=project_id,
worktree_path=wt.get('path') if wt else None,
-16
View File
@@ -460,23 +460,7 @@ async function newSession(flash, options={}){
const switchWs=S._profileSwitchWorkspace;
S._profileSwitchWorkspace=null;
const inheritWs=switchWs||(S.session?S.session.workspace:null)||(S._profileDefaultWorkspace||null);
// Use the saved default model for new sessions (#872). The user's saved
// default_model (from Settings) takes priority over the chat-header dropdown
// value, which reflects the *previous* session's model. Fall back to the
// dropdown value only when no default_model is configured.
const modelSel=$('modelSelect');
const selectedDefaultModel=window._defaultModel||(modelSel&&modelSel.value)||'';
let defaultApplied=false;
if(window._defaultModel&&modelSel&&typeof _applyModelToDropdown==='function'){
defaultApplied=!!_applyModelToDropdown(window._defaultModel,modelSel,window._activeProvider||null);
}
const canQualify=!window._defaultModel||defaultApplied||(modelSel&&modelSel.value===selectedDefaultModel);
const newModelState=(canQualify&&typeof _modelStateForSelect==='function')
? _modelStateForSelect(modelSel,selectedDefaultModel)
: {model:selectedDefaultModel,model_provider:null};
const reqBody={
model:newModelState.model,
model_provider:newModelState.model_provider||null,
workspace:inheritWs,
profile:S.activeProfile||'default',
};
+24
View File
@@ -266,6 +266,30 @@ def test_sessions_js_sends_profile_in_new_session_post():
)
def test_new_session_uses_explicit_profile_default_model_and_provider(tmp_path, monkeypatch):
"""New chats must inherit the selected profile's config default."""
import api.profiles as p
import api.models as m
monkeypatch.setattr(p, "_DEFAULT_HERMES_HOME", tmp_path)
profile_home = tmp_path / "profiles" / "pepper"
profile_home.mkdir(parents=True)
(profile_home / "config.yaml").write_text(
"model:\n default: pepper-profile-model\n provider: pepper-provider\n",
encoding="utf-8",
)
with patch.object(m.Session, 'save', return_value=None):
s = m.new_session(workspace=str(tmp_path), profile="pepper")
try:
assert s.model == "pepper-profile-model"
assert s.model_provider == "pepper-provider"
assert s.profile == "pepper"
finally:
with m.LOCK:
m.SESSIONS.pop(s.session_id, None)
def test_get_hermes_home_for_profile_rejects_path_traversal():
"""R19j: get_hermes_home_for_profile() must reject names that don't match
_PROFILE_ID_RE (e.g. path traversal like '../../etc') and return the base
+6 -3
View File
@@ -1171,10 +1171,13 @@ class TestFrontendModelProviderState:
assert "_modelStateForSelect" in src
assert "model_provider:modelState.model_provider||null" in src
def test_new_session_sends_model_provider(self):
def test_new_session_lets_profile_config_choose_default_model_provider(self):
src = _read("static/sessions.js")
assert "_modelStateForSelect(modelSel,selectedDefaultModel)" in src
assert "model_provider:newModelState.model_provider||null" in src
start = src.index("async function newSession(")
body = src[start:src.index("const data=await api('/api/session/new'", start)]
assert "profile:S.activeProfile||'default'" in body
assert "model:newModelState.model" not in body
assert "model_provider:newModelState.model_provider||null" not in body
def test_ui_has_json_model_state_storage(self):
src = _read("static/ui.js")