fix: deduplicate model dropdown (hyphen vs dot) + README accuracy (#332)
Normalizes hyphens to dots in backend model-ID comparison so claude-sonnet-4-6 (hermes-agent format) matches claude-sonnet-4.6 (WebUI list) and no duplicate entry is injected. README line counts and test count corrected. 791 tests, all pass.
This commit is contained in:
@@ -6,6 +6,11 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## [v0.50.8] Model dropdown deduplication — hyphen vs dot separator fix (PR #332)
|
||||||
|
|
||||||
|
- **Model dropdown no longer shows duplicates for hyphen-format configs** (e.g. `claude-sonnet-4-6` from hermes-agent config): The server-side normalization in `api/config.py` now unifies hyphens and dots when checking whether the default model is already in the dropdown. Previously, `claude-sonnet-4-6` (hermes-agent format) and `claude-sonnet-4.6` (WebUI list format) were treated as different models, causing the same model to appear twice — once as a raw unlabelled entry and once with the correct display name. The raw entry is now suppressed and the labelled one is selected as default.
|
||||||
|
- **README updated**: test count corrected to 791 / 51 files; all module line counts updated to current values; `onboarding.py`, `state_sync.py`, `updates.py` added to the architecture listing.
|
||||||
|
|
||||||
## [v0.50.7] OAuth provider onboarding path — Codex/Copilot no longer blocks setup (PR #331, fixes #329 bug 2)
|
## [v0.50.7] OAuth provider onboarding path — Codex/Copilot no longer blocks setup (PR #331, fixes #329 bug 2)
|
||||||
|
|
||||||
- **OAuth providers now have a proper onboarding path** (closes bug 2): Users with `openai-codex`, `copilot`, `qwen-oauth`, or any other OAuth-authenticated provider now see a clear confirmation card instead of an unusable API key input form.
|
- **OAuth providers now have a proper onboarding path** (closes bug 2): Users with `openai-codex`, `copilot`, `qwen-oauth`, or any other OAuth-authenticated provider now see a clear confirmation card instead of an unusable API key input form.
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -339,8 +339,8 @@ Or using the agent venv explicitly:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Tests run against an isolated server on port 8788 with a separate state directory.
|
Tests run against an isolated server on port 8788 with a separate state directory.
|
||||||
Production data and real cron jobs are never touched. Current count: **433 tests**
|
Production data and real cron jobs are never touched. Current count: **791 tests**
|
||||||
across 23 test files.
|
across 51 test files.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -462,31 +462,33 @@ across 23 test files.
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
server.py HTTP routing shell + auth middleware (~83 lines)
|
server.py HTTP routing shell + auth middleware (~154 lines)
|
||||||
api/
|
api/
|
||||||
auth.py Optional password authentication, signed cookies (~149 lines)
|
auth.py Optional password authentication, signed cookies (~201 lines)
|
||||||
config.py Discovery, globals, model detection, reloadable config (~726 lines)
|
config.py Discovery, globals, model detection, reloadable config (~1110 lines)
|
||||||
helpers.py HTTP helpers, security headers (~71 lines)
|
helpers.py HTTP helpers, security headers (~175 lines)
|
||||||
models.py Session model + CRUD + CLI bridge (~338 lines)
|
models.py Session model + CRUD + CLI bridge (~377 lines)
|
||||||
profiles.py Profile state management, hermes_cli wrapper (~366 lines)
|
onboarding.py First-run onboarding wizard, OAuth provider support (~507 lines)
|
||||||
routes.py All GET + POST route handlers (~1314 lines)
|
profiles.py Profile state management, hermes_cli wrapper (~411 lines)
|
||||||
streaming.py SSE engine, run_agent, cancel support (~332 lines)
|
routes.py All GET + POST route handlers (~1996 lines)
|
||||||
upload.py Multipart parser, file upload handler (~78 lines)
|
state_sync.py /insights sync — message_count to state.db (~113 lines)
|
||||||
|
streaming.py SSE engine, run_agent, cancel support (~545 lines)
|
||||||
|
updates.py Self-update check and release notes (~257 lines)
|
||||||
|
upload.py Multipart parser, file upload handler (~82 lines)
|
||||||
workspace.py File ops, workspace helpers, git detection (~288 lines)
|
workspace.py File ops, workspace helpers, git detection (~288 lines)
|
||||||
static/
|
static/
|
||||||
index.html HTML template (~600 lines)
|
index.html HTML template (~600 lines)
|
||||||
style.css All CSS incl. mobile responsive, themes (~855 lines)
|
style.css All CSS incl. mobile responsive, themes (~1050 lines)
|
||||||
ui.js DOM helpers, renderMd, tool cards, context ring (~1090 lines)
|
ui.js DOM helpers, renderMd, tool cards, context indicator (~1496 lines)
|
||||||
workspace.js File preview, file ops, git badge (~247 lines)
|
workspace.js File preview, file ops, git badge (~286 lines)
|
||||||
sessions.js Session CRUD, ⋯ dropdown, collapsible groups, search (~600 lines)
|
sessions.js Session CRUD, collapsible groups, search (~752 lines)
|
||||||
messages.js send(), SSE handlers, rAF throttle (~352 lines)
|
messages.js send(), SSE handlers, rAF throttle (~487 lines)
|
||||||
panels.js Cron, skills, memory, profiles, control center (~1200 lines)
|
panels.js Cron, skills, memory, profiles, settings (~1438 lines)
|
||||||
commands.js Slash command autocomplete (~170 lines)
|
commands.js Slash command autocomplete (~267 lines)
|
||||||
boot.js Mobile nav, workspace state machine, composer chips, boot IIFE (~420 lines)
|
boot.js Mobile nav, voice input, boot IIFE (~524 lines)
|
||||||
tests/
|
tests/
|
||||||
conftest.py Isolated test server (port 8788)
|
conftest.py Isolated test server (port 8788)
|
||||||
test_sprint{1-36}.py 36 test files, 742 test functions
|
51 test files 791 test functions
|
||||||
test_regressions.py Permanent regression gate
|
|
||||||
Dockerfile python:3.12-slim container image
|
Dockerfile python:3.12-slim container image
|
||||||
docker-compose.yml Compose with named volume and optional auth
|
docker-compose.yml Compose with named volume and optional auth
|
||||||
.github/workflows/ CI: multi-arch Docker build + GitHub Release on tag
|
.github/workflows/ CI: multi-arch Docker build + GitHub Release on tag
|
||||||
|
|||||||
@@ -913,10 +913,11 @@ def get_available_models() -> dict:
|
|||||||
# Ensure the user's configured default_model always appears in the dropdown.
|
# Ensure the user's configured default_model always appears in the dropdown.
|
||||||
# It may be missing if the model isn't in any hardcoded list (e.g. openrouter/free,
|
# It may be missing if the model isn't in any hardcoded list (e.g. openrouter/free,
|
||||||
# a custom local model, or any model.default not in _FALLBACK_MODELS).
|
# a custom local model, or any model.default not in _FALLBACK_MODELS).
|
||||||
# Normalize before comparing: strip provider prefix so 'anthropic/claude-opus-4.6'
|
# Normalize before comparing: strip provider prefix and unify separators so
|
||||||
# matches 'claude-opus-4.6' already in the list and avoids a duplicate entry.
|
# 'anthropic/claude-opus-4.6' matches 'claude-opus-4.6' and 'claude-sonnet-4-6'
|
||||||
|
# matches 'claude-sonnet-4.6' (hermes-agent uses hyphens, webui uses dots).
|
||||||
if default_model:
|
if default_model:
|
||||||
_norm = lambda mid: mid.split("/", 1)[-1] if "/" in mid else mid
|
_norm = lambda mid: (mid.split("/", 1)[-1] if "/" in mid else mid).replace("-", ".")
|
||||||
all_ids_norm = {_norm(m["id"]) for g in groups for m in g.get("models", [])}
|
all_ids_norm = {_norm(m["id"]) for g in groups for m in g.get("models", [])}
|
||||||
if _norm(default_model) not in all_ids_norm:
|
if _norm(default_model) not in all_ids_norm:
|
||||||
# Determine which group to inject into. Compare against the
|
# Determine which group to inject into. Compare against the
|
||||||
|
|||||||
@@ -526,7 +526,7 @@
|
|||||||
<div class="settings-section-title">System</div>
|
<div class="settings-section-title">System</div>
|
||||||
<div class="settings-section-meta">Instance version and access controls.</div>
|
<div class="settings-section-meta">Instance version and access controls.</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="settings-version-badge">v0.50.7</span>
|
<span class="settings-version-badge">v0.50.8</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
||||||
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
|
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user