hermes - ✅(Solved) Fix Bug: Duplicate provider entries in /model picker [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#14057Fetched 2026-04-23 07:47:07
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×4commented ×1cross-referenced ×1

When a provider exists in both:

  1. Built-in provider list (HERMES_OVERLAYS, CANONICAL_PROVIDERS, or _PROVIDER_MODELS)
  2. User config (providers: section in config.yaml)

The /model picker shows duplicate entries with different names and model counts.

Root Cause

The bug is in hermes_cli/model_switch.py::list_authenticated_providers():

  1. Section 2 (HERMES_OVERLAYS) adds built-in providers (e.g., slug="deepseek", name="DeepSeek")
  2. Section 3 (user-defined endpoints) tries to add user config providers but skips if slug already in seen_slugs
  3. Section 4 (custom providers) adds providers from custom_providers list with custom: prefix
    • Checks if slug.lower() in seen_slugs: where slug="custom:deepseek"
    • seen_slugs has "deepseek" but not "custom:deepseek"
    • Result: Adds duplicate custom:deepseek

The deduplication logic fails because:

  • Section 4 only checks exact slug match, not base slug (without custom: prefix)
  • _pair_key deduplication assumes Section 3 adds all user providers, but Section 3 skips providers already in seen_slugs

Fix Action

Fixed

PR fix notes

PR #14068: fix(model-picker): dedupe built-in compatibility providers

Description (problem / solution / changelog)

Summary

  • stop section 4 from re-adding built-in providers as custom:* compatibility rows
  • keep /model provider lists stable when providers: entries mirror built-in providers like deepseek
  • add a regression test that exercises the real providers -> get_compatible_custom_providers() -> list_authenticated_providers() path

Problem

When a built-in provider also exists in providers:, the compatibility layer can still emit a duplicate custom:* row. On current main, a config-backed deepseek entry produced both deepseek and custom:deepseek in the picker.

Closes #14057.

Testing

  • pytest -o addopts= tests/hermes_cli/test_user_providers_model_switch.py
  • pytest -o addopts= tests/hermes_cli/test_model_switch_custom_providers.py -k "includes_custom_providers or groups_same_name_custom_providers_into_one_row or dedupes_dict_model_matching_singular_default"
  • manual repro: built-in deepseek + compatibility-merged config entry now yields only one deepseek row

Changed files

  • hermes_cli/model_switch.py (modified, +6/-1)
  • tests/hermes_cli/test_user_providers_model_switch.py (modified, +37/-0)

Code Example

Current: deepseek-chat on DeepSeek
DeepSeek (2 models) ‹ current
OpenRouter (32 models)
Anthropic (8 models)
Z.AI (7 models)
Kimi For Coding (7 models)
OpenAI (0 models) 
openai (0 models)
 deepseek (0 models)
kimi-coding (0 models)

---

providers:
  openai:
    type: openai
    base_url: https://api.openai.com/v1
    api_key_env: OPENAI_API_KEY
    default_flex: True
    models: ['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano']
  deepseek:
    type: deepseek
    base_url: https://api.deepseek.com
    api_key_env: DEEPSEEK_API_KEY
  kimi-coding:
    type: kimi-coding
    base_url: https://api.moonshot.ai/v1
    api_key_env: KIMI_API_KEY

---

if slug.lower() in seen_slugs:
    continue

---

# Check both the full slug and base slug (without "custom:" prefix)
base_slug = slug[7:] if slug.startswith("custom:") else slug
if base_slug.lower() in seen_slugs:
    continue
RAW_BUFFERClick to expand / collapse

Bug: Duplicate provider entries in /model picker when providers exist in both built-in and user config

Description

When a provider exists in both:

  1. Built-in provider list (HERMES_OVERLAYS, CANONICAL_PROVIDERS, or _PROVIDER_MODELS)
  2. User config (providers: section in config.yaml)

The /model picker shows duplicate entries with different names and model counts.

Current Behavior

Example output from Hermes CLI TUI /model command:

Current: deepseek-chat on DeepSeek
› DeepSeek (2 models) ‹ current
OpenRouter (32 models)
Anthropic (8 models)
Z.AI (7 models)
Kimi For Coding (7 models)
OpenAI (0 models) 
openai (0 models)
 deepseek (0 models)
kimi-coding (0 models)

Note the duplicates:

  • DeepSeek (built-in) and deepseek (user config)
  • Kimi For Coding (canonical) and kimi-coding (user config)
  • OpenAI (?) and openai (user config)

Root Cause

The bug is in hermes_cli/model_switch.py::list_authenticated_providers():

  1. Section 2 (HERMES_OVERLAYS) adds built-in providers (e.g., slug="deepseek", name="DeepSeek")
  2. Section 3 (user-defined endpoints) tries to add user config providers but skips if slug already in seen_slugs
  3. Section 4 (custom providers) adds providers from custom_providers list with custom: prefix
    • Checks if slug.lower() in seen_slugs: where slug="custom:deepseek"
    • seen_slugs has "deepseek" but not "custom:deepseek"
    • Result: Adds duplicate custom:deepseek

The deduplication logic fails because:

  • Section 4 only checks exact slug match, not base slug (without custom: prefix)
  • _pair_key deduplication assumes Section 3 adds all user providers, but Section 3 skips providers already in seen_slugs

Configuration

Relevant config.yaml sections:

providers:
  openai:
    type: openai
    base_url: https://api.openai.com/v1
    api_key_env: OPENAI_API_KEY
    default_flex: True
    models: ['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano']
  deepseek:
    type: deepseek
    base_url: https://api.deepseek.com
    api_key_env: DEEPSEEK_API_KEY
  kimi-coding:
    type: kimi-coding
    base_url: https://api.moonshot.ai/v1
    api_key_env: KIMI_API_KEY

Expected Behavior

Each provider should appear only once in the /model picker:

  • Built-in providers should show with their proper display names
  • User config providers that duplicate built-in providers should be suppressed
  • Providers should show correct model counts (not 0 for configured providers)

Proposed Fix

In list_authenticated_providers(), Section 4 (lines 1161-1185):

Change from:

if slug.lower() in seen_slugs:
    continue

To:

# Check both the full slug and base slug (without "custom:" prefix)
base_slug = slug[7:] if slug.startswith("custom:") else slug
if base_slug.lower() in seen_slugs:
    continue

Additionally, consider improving the _pair_key logic to handle cases where Section 3 skips providers.

Environment

  • Hermes Agent version: (latest main branch)
  • Python: 3.11+
  • OS: Linux

Steps to Reproduce

  1. Add a provider to config.yaml that already exists as a built-in provider (e.g., deepseek, kimi-coding)
  2. Run Hermes CLI TUI
  3. Type /model
  4. Observe duplicate entries

Additional Notes

  • The "OpenAI" vs "openai" duplicate might have a different root cause (possibly from Section 1 or label formatting)
  • Telegram /model picker works correctly because it uses different code path (_build_model_keyboard in telegram.py)
  • This affects user experience by cluttering the model picker with confusing duplicates

Disclaimer: Issue written by Hermes Agent using DeepSeek ⚕️

extent analysis

TL;DR

The most likely fix is to modify the list_authenticated_providers() function to correctly handle duplicate provider entries by checking both the full slug and base slug (without "custom:" prefix) in the seen_slugs set.

Guidance

  • Modify the list_authenticated_providers() function in hermes_cli/model_switch.py to check for both the full slug and base slug (without "custom:" prefix) in the seen_slugs set, as proposed in the issue.
  • Review the _pair_key logic to ensure it correctly handles cases where Section 3 skips providers.
  • Verify the fix by running the Hermes CLI TUI, typing /model, and checking for duplicate entries.
  • Consider investigating the "OpenAI" vs "openai" duplicate issue separately, as it may have a different root cause.

Example

# Check both the full slug and base slug (without "custom:" prefix)
base_slug = slug[7:] if slug.startswith("custom:") else slug
if base_slug.lower() in seen_slugs:
    continue

Notes

  • The proposed fix only addresses the duplicate entries caused by the incorrect handling of custom provider slugs.
  • The "OpenAI" vs "openai" duplicate issue may require additional investigation and fixes.
  • The Telegram /model picker is not affected by this issue due to its different code path.

Recommendation

Apply the proposed workaround by modifying the list_authenticated_providers() function to correctly handle duplicate provider entries. This should resolve the issue of duplicate entries in the /model picker.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix Bug: Duplicate provider entries in /model picker [1 pull requests, 1 comments, 2 participants]