fix(#29872): cross-check custom provider pool key against requested name - #30107
Closed
zombi3butt wants to merge 13 commits into
Closed
fix(#29872): cross-check custom provider pool key against requested name#30107zombi3butt wants to merge 13 commits into
zombi3butt wants to merge 13 commits into
Conversation
…f GGUF metadata (issue NousResearch#29802)
…me dict (issue NousResearch#29872) When resolving named custom providers (custom:<name>), the returned runtime dict previously collapsed provider to bare 'custom', losing the specific sub-provider identity. This caused TUI display and credential pool lookups to show only 'custom' instead of 'custom:bobapi-deepseek' etc. Changes: - _try_resolve_from_custom_pool: preserve sub-provider name in returned provider field as 'custom:<name>' - _resolve_named_custom_runtime: non-pool return path uses 'custom:<name>' for the provider field - Bare 'custom' with explicit base_url remains unchanged Updated tests to reflect new expected provider values.
…and improving error visibility (issue NousResearch#29726)
fix: route cron job scripts through remote terminal backend (issue NousResearch#29849)
When provider hooks silently strip the role="system" message (known with some Ollama Cloud variants), re-inject from original input so SOUL.md is never lost mid-flight. Adds defensive verification in _build_kwargs_from_profile.
…esearch#29920) Two-layer fix for `HTTP 400: invalid message content type: map[string]interface{}`. 1. `_tool_result_content_for_active_model` in run_agent.py — serializes non-string, non-list results (Python dicts/lists from MCP tools or memory helpers) as JSON before appending to messages. Falls back to repr() on serialization failure. 2. `sanitize_api_messages` in agent_runtime_helpers.py — coerces tool role `content` to JSON string as a safety-net before every API call. Catches any tool results that bypass the first layer (e.g. from session restore or manual message manipulation). Fixes the 'model provider failed after retries' loop caused by a single bad tool result poisoning the entire message history.
Three-layer fix for Discord bot-to-bot silence token handling: 1. Entry filter (_handle_message): Drop Discord bot messages with content exactly "NO_REPLY" before they enter the agent loop. When DISCORD_ALLOW_BOTS=mentions, other bots' NO_REPLY must be ignored. 2. Backfill exclusion (_fetch_channel_context): Exclude NO_REPLY sentinel messages from channel history backfill so they don't contaminate session context. 3. Delivery suppression (send): Suppress literal NO_REPLY responses from being sent to Discord channels — it's a control/silence token, not user-facing content. Fixes noisy bot-to-bot loops caused by agent silence being surfaced as empty-response retries.
…onversation (issue NousResearch#29926) When auto-compression rotates the session mid-run, result["messages"] contains the inflated post-turn list (compressed baseline + this turn's growth). The CLI was overwriting conversation_history with this inflated list, causing the next turn to start from 130K+ tokens instead of the compressed ~24K baseline — wasting VRAM and API time. Fix: detect session rotation via _cli_last_run_old_session_id (captured before run_conversation in the agent thread) and use agent._session_messages instead of result["messages"] when rotation occurred. Falls back to result["messages"] for normal (non-rotated) runs. Mirrors the gateway path fix in PR NousResearch#29505.
…usResearch#29948) When users start multiple Telegram gateways via HERMES_PROFILE=alice hermes gateway --replace & HERMES_PROFILE=bob hermes gateway --replace & the _apply_profile_override() function now resolves HERMES_PROFILE to the correct profile-scoped PID file instead of colliding on the default ~/.hermes/gateway.pid. Root cause: _apply_profile_override() read --profile/-p argv flags and active_profile file but completely ignored HERMES_PROFILE env var. Both gateways resolved to the same default path → profile B's --replace SIGKILL'd profile A's gateway. Priority order (highest to lowest): 1. --profile/-p argv flag (explicit) 2. HERMES_PROFILE env var (session-level) 3. active_profile file (sticky default) 4. No override — falls back to default ~/.hermes Only honours HERMES_PROFILE if the profile directory exists, preventing crashes on stale/typos env vars. Falls through gracefully to active_profile. Fixes issue NousResearch#29948.
… requested name When is called with a named custom provider (e.g. ), it calls which may fall back to base_url matching and pick a DIFFERENT provider's pool if multiple entries share the same endpoint. This fix adds a cross-check: iterate in the caller to find the expected pool key for the given name, then compare against what GPCPK actually returns. If they differ (URL fallback picked wrong provider), reject and return None instead of loading wrong API key. Also added regression tests verifying rejection on mismatch and pass on match, plus no-cross-check path for provider_name=None. Fixes NousResearch#29872
Collaborator
|
This PR bundles many unrelated changes across 16 files (sanitize_api_messages, model_metadata llama.cpp context, chat_completions kwargs, etc.) beyond the described runtime_provider.py cross-check. The core fix looks correct for #29872. Recommend splitting unrelated changes into separate PRs. Also see #29893 which addresses the same issue more narrowly. |
Author
|
Closing per alt-glitch review. This PR bundles unrelated changes (16 files, 997 additions) and overlaps with #29893 which addresses #29872 with a cleaner 4-file fix that directly extracts the sub-name from custom: and passes it to _try_resolve_from_custom_pool(). Vanhci's approach fixes the root cause rather than adding defensive validation. |
This was referenced May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix for #29872
Problem
When a named custom provider (e.g.
custom:bobapi-deepseek) is resolved,_try_resolve_from_custom_pool()callsget_custom_provider_pool_key(base_url, provider_name)which may fall back to base_url matching and pick a DIFFERENTprovider's pool if multiple entries share the same endpoint URL. This causes
the wrong API key to be loaded from
auth.jsonpools.Root Cause
_try_resolve_from_custom_pool()callsget_custom_provider_pool_key(base_url, provider_name). Inside GPCPK, whenprovider_nameis given, it iterates throughconfig entries to find a name match. But if multiple custom providers share the
same base_url with different names, iteration order and matching logic may cause
a fallback match against the wrong provider entry — returning wrong credentials
from auth.json.
Fix
Add a cross-check in
_try_resolve_from_custom_pool(): before calling GPCPK forthe actual URL-based lookup, iterate
_iter_custom_providers()to find the expectedpool key for the given
provider_name. Then compare against what GPCPK actuallyreturns. If they differ (URL fallback picked a different provider), reject and
return None instead of loading wrong API key.
Changes
hermes_cli/runtime_provider.py: Added cross-check logic in_try_resolve_from_custom_pool()_iter_custom_providersfromagent.credential_pooltests/hermes_cli/test_runtime_provider_cross_check.py: New regression testsTesting