Skip to content

fix(config): default model empty string — no more unavailable OpenAI model for non-OpenAI users — closes #646 - #649

Merged
nesquena-hermes merged 2 commits into
masterfrom
fix/default-model-empty
Apr 18, 2026
Merged

fix(config): default model empty string — no more unavailable OpenAI model for non-OpenAI users — closes #646#649
nesquena-hermes merged 2 commits into
masterfrom
fix/default-model-empty

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Problem

The hardcoded fallback DEFAULT_MODEL = "openai/gpt-5.4-mini" caused the model dropdown to show "GPT-5.4 Mini (unavailable)" for any user whose configured provider isn't OpenAI — including custom providers, LM Studio, Anthropic-only setups, and third-party deployments like Agent37.

The (unavailable) label is intentional and correct behavior (it preserves stale session models). The problem is the server's own default triggering it on the very first session before the user has done anything wrong.

Fix

Changed the default from "openai/gpt-5.4-mini" to "" (empty string).

# Before
DEFAULT_MODEL = os.getenv("HERMES_WEBUI_DEFAULT_MODEL", "openai/gpt-5.4-mini")

# After
DEFAULT_MODEL = os.getenv("HERMES_WEBUI_DEFAULT_MODEL", "")

An empty default means the WebUI defers to the active provider's own default model, which is always valid. Users who want a pinned default can still set HERMES_WEBUI_DEFAULT_MODEL env var or choose a model in Preferences — both paths are unaffected.

Testing

  • 1362 tests passing (4 pre-existing failures in test_sprint34 unrelated to this change)

Closes #646

@nesquena-hermes

Copy link
Copy Markdown
Collaborator Author

Review: ✅ Approved — ready to merge

Verdict: LGTM

Single-line fix for a clear root-cause problem: DEFAULT_MODEL = os.getenv("HERMES_WEBUI_DEFAULT_MODEL", "openai/gpt-5.4-mini") hardcodes an OpenAI model that shows as "(unavailable)" for any non-OpenAI user before they've done anything wrong.

The fix:

# Before
DEFAULT_MODEL = os.getenv("HERMES_WEBUI_DEFAULT_MODEL", "openai/gpt-5.4-mini")

# After
DEFAULT_MODEL = os.getenv("HERMES_WEBUI_DEFAULT_MODEL", "")

Empty string default means the WebUI defers to the active provider's model — always valid. The HERMES_WEBUI_DEFAULT_MODEL env var override path is preserved unchanged.

Test count: 1362 passing, 4 pre-existing failures in test_sprint34 confirmed unrelated (same failures on master before this branch).

The fix is minimal, correct, and closes #646. Ready to merge.

@nesquena

Copy link
Copy Markdown
Owner

Independent End-to-End Review — PR #649

Independent second-pass review after the first approval. Pulled the branch, verified downstream empty-model handling, ran the full test suite.

TL;DR

Merge-ready. Minimal one-line fix for a real UX annoyance. Empty-string default is handled correctly by resolve_model_provider() which falls through to the configured provider's base_url/provider (api/config.py:640-642: if not model_id: return model_id, config_provider, config_base_url). No test regressions.

Security audit ✅

N/A — one-character data change (the default value for an env var fallback). No new code paths.

Downstream empty-model analysis ✅

Traced DEFAULT_MODEL = "" through every usage site:

Call site Behavior with ""
api/config.py:721 (/api/models builder) default_model starts "", then overwritten by cfg.model.default, HERMES_MODEL/OPENAI_MODEL/LLM_MODEL, or provider detection. Empty only survives if NO config at all.
api/config.py:1211 (_SETTINGS_DEFAULTS) Persisted default_model="" — existing or DEFAULT_MODEL fallbacks in downstream code handle empty
api/models.py:41, :137 (Session ctor) Session.model may be ""resolve_model_provider("") → returns ("", cfg_provider, cfg_base_url). Configured provider takes over.
api/onboarding.py:436 settings.get("default_model") or DEFAULT_MODELor chain resolves to ""; onboarding UI already handles this with model dropdown selection.
api/routes.py:2601 Imported session without model gets model="" — same flow as Session ctor.

The key insight: api/config.py:640-642 in resolve_model_provider() explicitly handles empty-string — it returns (model_id, config_provider, config_base_url) so the configured provider's base_url and provider take over. This is exactly what the PR description promises: "defers to the active provider's own default model."

Edge case: no provider configured AND empty default

If a user somehow reaches a state with no config.yaml provider, no env vars, and no saved settings, /api/models would return a dropdown with one empty entry ({id: "", label: ""}) at config.py:1120. This is a UX regression for that specific edge case — but it's only reachable by a user who hasn't completed onboarding, at which point they can't chat anyway. Low real-world impact. Worth a follow-up to show a friendlier empty state, but not a blocker for this PR.

Test results ✅

  • 1331 passed, 42 skipped, 0 failed (full suite, isolated worktree)
  • test_models_default_model_not_empty still passes (conftest.py forces HERMES_WEBUI_DEFAULT_MODEL=openai/gpt-5.4-mini for test isolation, so the test asserts the env-var path — NOT the empty-default path this PR introduces in production)
  • CI green on Python 3.11/3.12/3.13 ✅

Coverage gap worth noting: There's no test that exercises the "no env var set, no config" path — all tests run with the env var forced. Not a regression from this PR, but a test gap that predates it. Worth a future PR to add a conftest fixture that unsets the env var for a specific test to verify empty-default behavior end-to-end.

Markdown + version

Recommendation

Approved, ready to merge. No changes needed from me. The prior reviewer's approval stands.

One future improvement (not for this PR):

  • Add a conftest-isolated test that asserts DEFAULT_MODEL empty-string path produces a sensible dropdown/session.model flow when no config/env is present.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator Author

Review follow-up: latent bug fixed + tests added

The core DEFAULT_MODEL = os.getenv(..., "") change is correct and necessary. Good catch on #646.

Bug found during review: empty model creates blank dropdown entries

Two code paths in api/config.py were constructing model entries unconditionally from default_model without checking whether it was empty:

  1. Unknown provider, no auto-detected models (lines 1102-1113): inserted {"id": "", "label": ""} into the provider group when default_model=""
  2. No providers detected (lines 1114-1121): inserted {"provider": "Default", "models": [{"id": "", "label": ""}]} when default_model=""

The if default_model: guard at line 1129 only protected the "ensure default appears in dropdown" section — not these two paths. Any user with no provider configured would get a broken dropdown with a blank model entry.

What was pushed in the fix commit (fdf5fe6):

  • api/config.py: wrapped both affected code paths with if default_model: guards
  • tests/test_sprint11.py: updated the docstring for test_models_default_model_not_empty to clarify it only holds because conftest sets the env var — the new behavior (empty = valid) is now documented
  • tests/test_issue646.py (new): 3 tests verifying the empty-model guard logic

Test results: 3/3 new tests pass. Full suite: 4 failed (pre-existing test_sprint34.py OAuth stubs), 1372 passed.

The fix is pushed to fix/default-model-empty. Ready for independent review.

@nesquena-hermes
nesquena-hermes force-pushed the fix/default-model-empty branch from fdf5fe6 to 7e06e97 Compare April 18, 2026 06:46
@nesquena-hermes
nesquena-hermes merged commit ec48c48 into master Apr 18, 2026
3 checks passed
@nesquena-hermes
nesquena-hermes deleted the fix/default-model-empty branch April 21, 2026 02:38
JKJameson pushed a commit to JKJameson/hermes-webui that referenced this pull request Apr 25, 2026
… for non-OpenAI users — closes nesquena#646 (PR nesquena#649)

DEFAULT_MODEL now defaults to "" instead of "openai/gpt-5.4-mini". Guards added in model-list builder so empty default does not create blank model entries. Adds 3 tests in test_issue646.py. Independent review by @nesquena.
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
… for non-OpenAI users — closes nesquena#646 (PR nesquena#649)

DEFAULT_MODEL now defaults to "" instead of "openai/gpt-5.4-mini". Guards added in model-list builder so empty default does not create blank model entries. Adds 3 tests in test_issue646.py. Independent review by @nesquena.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ux(models): hardcoded fallback default model (openai/gpt-5.4-mini) shown as unavailable for non-OpenAI users

2 participants