SimpleChatbot / scripts /patch_auxiliary_client.py
Amin
Fix auxiliary fallback selection and enforce free OpenRouter models.
cebe6c4
Raw
History Blame Contribute Delete
7.68 kB
"""Apply HermesFace patches to upstream ``agent/auxiliary_client.py``."""
from __future__ import annotations
from pathlib import Path
from sync_hf import _replace_exact_once, _replace_required_once
_PATCH_MARKER = "# HermesFace: configured fallback respects unhealthy cache"
def _patch_payment_fallback_block(
code: str,
*,
log_prefix: str,
label: str,
) -> tuple[str, bool]:
old = (
f"{log_prefix}\n"
" task or \"call\", reason, resolved_provider, first_err)\n"
"\n"
" # Fallback order (#26882, #26803):\n"
" # 1. User-configured fallback_chain (per-task) if set\n"
" # 2. For auto: top-level main fallback_providers/fallback_model\n"
" # 3. For auto: built-in auxiliary discovery chain\n"
" # 4. For explicit aux providers: main agent model safety net\n"
" fb_client, fb_model, fb_label = (None, None, \"\")\n"
" if is_auto:\n"
" fb_client, fb_model, fb_label = _try_configured_fallback_chain(\n"
" task, resolved_provider or \"auto\", reason=reason)\n"
" if fb_client is None:\n"
" fb_client, fb_model, fb_label = _try_main_fallback_chain(\n"
" task, resolved_provider or \"auto\", reason=reason)\n"
" if fb_client is None:\n"
" fb_client, fb_model, fb_label = _try_payment_fallback(\n"
" resolved_provider, task, reason=reason)\n"
" else:\n"
" fb_client, fb_model, fb_label = _try_configured_fallback_chain(\n"
" task, resolved_provider or \"auto\", reason=reason)\n"
)
new = (
f"{log_prefix}\n"
" task or \"call\", reason, resolved_provider, first_err)\n"
"\n"
" # Fallback order (#26882, #26803):\n"
" # 1. User-configured fallback_chain (per-task) if set\n"
" # 2. For auto: top-level main fallback_providers/fallback_model\n"
" # 3. For auto: built-in auxiliary discovery chain\n"
" # 4. For explicit aux providers: main agent model safety net\n"
" fb_client, fb_model, fb_label = (None, None, \"\")\n"
" _failed_chain_provider = (\n"
" _recoverable_pool_provider(resolved_provider, client, main_runtime=main_runtime)\n"
" or resolved_provider\n"
" or \"auto\"\n"
" )\n"
" if is_auto:\n"
" fb_client, fb_model, fb_label = _try_configured_fallback_chain(\n"
" task, _failed_chain_provider, reason=reason)\n"
" if fb_client is None:\n"
" fb_client, fb_model, fb_label = _try_main_fallback_chain(\n"
" task, _failed_chain_provider, reason=reason)\n"
" if fb_client is None:\n"
" fb_client, fb_model, fb_label = _try_payment_fallback(\n"
" resolved_provider, task, reason=reason)\n"
" else:\n"
" fb_client, fb_model, fb_label = _try_configured_fallback_chain(\n"
" task, _failed_chain_provider, reason=reason)\n"
)
return _replace_exact_once(code, old, new, label=label)
def apply_auxiliary_client_fallback_patch(aux_path: Path) -> list[str]:
"""Patch fallback selection to honor unhealthy providers and concrete failures."""
if not aux_path.is_file():
raise RuntimeError(f"required upstream auxiliary client missing: {aux_path}")
code = aux_path.read_text(encoding="utf-8")
if _PATCH_MARKER in code:
return ["auxiliary_client fallback patch already applied"]
changes: list[str] = []
unhealthy_block = (
' label = f"fallback_chain[{i}]({fb_provider})"\n\n'
" try:\n"
)
unhealthy_replacement = (
' label = f"fallback_chain[{i}]({fb_provider})"\n'
f" {_PATCH_MARKER}\n"
" fb_norm = fb_provider.lower()\n"
" if _is_provider_unhealthy(fb_norm):\n"
" _log_skip_unhealthy(fb_norm, task)\n"
' tried.append(f"{label} (unhealthy)")\n'
" continue\n\n"
" try:\n"
)
code = _replace_required_once(
code,
unhealthy_block,
unhealthy_replacement,
label="auxiliary configured fallback unhealthy skip",
)
changes.append("configured fallback_chain skips unhealthy providers")
code, changed = _patch_payment_fallback_block(
code,
log_prefix=' logger.info("Auxiliary %s: %s on %s (%s), trying fallback",',
label="auxiliary sync payment fallback failed-provider label",
)
if changed:
changes.append("sync payment fallback passes concrete failed provider")
code, changed = _patch_payment_fallback_block(
code,
log_prefix=' logger.info("Auxiliary %s (async): %s on %s (%s), trying fallback",',
label="auxiliary async payment fallback failed-provider label",
)
if changed:
changes.append("async payment fallback passes concrete failed provider")
openrouter_guard_old = (
' or_key = explicit_api_key or os.getenv("OPENROUTER_API_KEY")\n'
" if not or_key:\n"
)
openrouter_guard_new = (
' or_key = explicit_api_key or os.getenv("OPENROUTER_API_KEY")\n'
" chosen_model = model or _OPENROUTER_MODEL\n"
' if chosen_model and ":free" not in str(chosen_model).lower():\n'
" logger.error(\n"
' "Auxiliary OpenRouter call refused: model %r lacks :free suffix "\n'
' "(set OPENROUTER_ANALYSIS_MODEL to a :free model)",\n'
" chosen_model,\n"
" )\n"
" return None, None\n"
" if not or_key:\n"
)
code = _replace_required_once(
code,
openrouter_guard_old,
openrouter_guard_new,
label="auxiliary OpenRouter :free guard",
)
changes.append("OpenRouter auxiliary calls require :free model suffix")
resolve_openrouter_old = (
" if provider == \"openrouter\":\n"
" client, default = _try_openrouter(explicit_api_key=explicit_api_key)\n"
)
resolve_openrouter_new = (
" if provider == \"openrouter\":\n"
" chosen_model = model or _OPENROUTER_MODEL\n"
' if chosen_model and ":free" not in str(chosen_model).lower():\n'
" logger.error(\n"
' "resolve_provider_client: refusing paid OpenRouter model %r "\n'
' "(model id must include :free)",\n'
" chosen_model,\n"
" )\n"
" return None, None\n"
" client, default = _try_openrouter(explicit_api_key=explicit_api_key, model=model)\n"
)
code = _replace_required_once(
code,
resolve_openrouter_old,
resolve_openrouter_new,
label="resolve_provider_client OpenRouter :free guard",
)
changes.append("resolve_provider_client refuses paid OpenRouter models")
aux_path.write_text(code, encoding="utf-8")
return changes