Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions hermes_cli/model_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,22 @@ def _record_builtin_endpoint(slug: str) -> None:
curated["lmstudio"] = live

# --- 1. Check Hermes-mapped providers ---
# Pre-scan custom_providers for base_urls so we can suppress built-in
# providers that would duplicate a user-defined endpoint (e.g. OpenAI
# native vs. user-defined OpenAI pointing at api.openai.com/v1).
_custom_base_urls: set[str] = set()
if custom_providers and isinstance(custom_providers, list):
for entry in custom_providers:
if isinstance(entry, dict):
bu = (entry.get("base_url") or "").strip().rstrip("/")
if bu:
_custom_base_urls.add(bu.lower())

def _custom_provider_has_base_url(base_url: str) -> bool:
"""Return True if a custom provider already claims this base_url."""
target = base_url.strip().rstrip("/").lower()
return target in _custom_base_urls

for hermes_id, mdev_id in PROVIDER_TO_MODELS_DEV.items():
# Skip aliases that map to the same models.dev provider (e.g.
# kimi-coding and kimi-coding-cn both → kimi-for-coding).
Expand Down Expand Up @@ -1146,6 +1162,16 @@ def _record_builtin_endpoint(slug: str) -> None:
pinfo = _mdev_pinfo(mdev_id)
display_name = pinfo.name if pinfo else mdev_id

# Suppress built-in `openai` when a custom provider already claims
# the same base_url (e.g. api.openai.com/v1). The custom provider
# typically has a richer model list and is the intended entry.
if slug == "openai" and _custom_provider_has_base_url(
"https://api.openai.com/v1"
):
seen_slugs.add(slug.lower())
seen_mdev_ids.add(mdev_id)
continue

results.append({
"slug": slug,
"name": display_name,
Expand Down
12 changes: 10 additions & 2 deletions hermes_cli/runtime_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,10 +521,18 @@ def _resolve_named_custom_runtime(
pool_result["model"] = model_name
return pool_result

configured_api_key = str(custom_provider.get("api_key", "") or "").strip()
configured_key_env = str(custom_provider.get("key_env", "") or "").strip()
configured_api_key_env = ""
if configured_api_key.startswith("env:"):
configured_api_key_env = configured_api_key.split(":", 1)[1].strip()
configured_api_key = ""

api_key_candidates = [
(explicit_api_key or "").strip(),
str(custom_provider.get("api_key", "") or "").strip(),
os.getenv(str(custom_provider.get("key_env", "") or "").strip(), "").strip(),
configured_api_key,
os.getenv(configured_key_env, "").strip(),
os.getenv(configured_api_key_env, "").strip(),
os.getenv("OPENAI_API_KEY", "").strip(),
os.getenv("OPENROUTER_API_KEY", "").strip(),
]
Expand Down
30 changes: 22 additions & 8 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7100,10 +7100,17 @@ def _call():
result["partial_tool_names"] = []
deltas_were_sent["yes"] = False
first_delta_fired["done"] = False
self._emit_status(
f"⚠️ Connection dropped mid tool-call "
f"({type(e).__name__}). Reconnecting… "
f"(attempt {_stream_attempt + 2}/{_max_stream_retries + 1})"
# Intermediate retry messages are logged (not emitted)
# to avoid cluttering the chat thread with transient
# warnings that remain even after successful retries.
# See: https://github.com/NousResearch/hermes-agent/issues/5151
logger.info(
"⚠️ Connection dropped mid tool-call "
"(%s). Reconnecting… "
"(attempt %s/%s)",
type(e).__name__,
_stream_attempt + 2,
_max_stream_retries + 1,
)
self._touch_activity(
f"stream retry {_stream_attempt + 2}/{_max_stream_retries + 1} "
Expand Down Expand Up @@ -7166,10 +7173,17 @@ def _call():
type(e).__name__,
e,
)
self._emit_status(
f"⚠️ Connection to provider dropped "
f"({type(e).__name__}). Reconnecting… "
f"(attempt {_stream_attempt + 2}/{_max_stream_retries + 1})"
# Intermediate retry messages are logged (not emitted)
# to avoid cluttering the chat thread with transient
# warnings that remain even after successful retries.
# See: https://github.com/NousResearch/hermes-agent/issues/5151
logger.info(
"⚠️ Connection to provider dropped "
"(%s). Reconnecting… "
"(attempt %s/%s)",
type(e).__name__,
_stream_attempt + 2,
_max_stream_retries + 1,
)
self._touch_activity(
f"stream retry {_stream_attempt + 2}/{_max_stream_retries + 1} "
Expand Down