opentui(phase3): launcher integration — HERMES_TUI_ENGINE dual-engine
hermes --tui launches the native OpenTUI engine (Bun) when HERMES_TUI_ENGINE=opentui (env) or display.tui_engine=opentui (config); Ink stays the default and the shipping path is untouched. - _resolve_tui_engine() (env > config > ink); refuses opentui on Windows/Termux (no Bun) -> falls back to ink with a notice. - _make_opentui_argv() -> [bun, src/entry.real.tsx] (no build step). - _bun_bin() with HERMES_BUN override. - Branch at top of _make_tui_argv BEFORE _ensure_tui_node (Bun-only host must not bootstrap Node). - Gate _launch_tui NODE_OPTIONS/--max-old-space-size on engine==ink (Bun is JSC; the V8 flag errors/ignores). Verified end-to-end via tmux: real hermes --tui -> Bun -> OpenTUI -> real Python gateway streamed a real reply. No-flag default still ink.
This commit is contained in:
@@ -491,27 +491,15 @@ def _lift_max_output_tokens(entry: Dict[str, Any], result: Dict[str, Any]) -> No
|
||||
|
||||
def _get_named_custom_provider(requested_provider: str) -> Optional[Dict[str, Any]]:
|
||||
requested_norm = _normalize_custom_provider_name(requested_provider or "")
|
||||
if not requested_norm:
|
||||
if not requested_norm or requested_norm == "custom":
|
||||
return None
|
||||
|
||||
# Bare "custom" is normally an incomplete spec — the canonical form is
|
||||
# "custom:<name>" — and is otherwise owned by the model.base_url "bare
|
||||
# custom" trust path. BUT a user may literally name a ``providers:`` (or
|
||||
# legacy ``custom_providers:``) entry "custom" (e.g. ``providers.custom``
|
||||
# pointing at cliproxy). We used to return None here *before* scanning
|
||||
# config, so such an entry was never matched and resolution fell through to
|
||||
# the global default (Codex) — the cause of cron jobs with
|
||||
# ``provider: "custom"`` failing with ``auth_unavailable: providers=codex``.
|
||||
# Fall through to the config scan instead; if no entry is literally named
|
||||
# "custom" it still returns None at the end, preserving the trust path.
|
||||
|
||||
# Raw names should only map to custom providers when they are not already
|
||||
# valid built-in providers or aliases. Explicit menu keys like
|
||||
# ``custom:local`` always target the saved custom provider. Bare "custom"
|
||||
# is exempt from the shadow check — it is not a built-in to defer to.
|
||||
# ``custom:local`` always target the saved custom provider.
|
||||
if requested_norm == "auto":
|
||||
return None
|
||||
if requested_norm != "custom" and not requested_norm.startswith("custom:"):
|
||||
if not requested_norm.startswith("custom:"):
|
||||
try:
|
||||
canonical = auth_mod.resolve_provider(requested_norm)
|
||||
except AuthError:
|
||||
@@ -646,20 +634,6 @@ def _get_named_custom_provider(requested_provider: str) -> Optional[Dict[str, An
|
||||
return None
|
||||
|
||||
|
||||
def has_named_custom_provider(requested_provider: str) -> bool:
|
||||
"""Return True when config defines a custom provider matching the request.
|
||||
|
||||
Thin public wrapper around :func:`_get_named_custom_provider` so other
|
||||
modules (e.g. the cronjob tool) can decide whether a provider name will
|
||||
actually resolve to a configured ``providers:`` / ``custom_providers:``
|
||||
entry — without reaching into a private helper or duplicating the scan.
|
||||
"""
|
||||
try:
|
||||
return _get_named_custom_provider(requested_provider) is not None
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _custom_provider_request_overrides(custom_provider: Dict[str, Any]) -> Dict[str, Any]:
|
||||
extra_body = custom_provider.get("extra_body")
|
||||
if not isinstance(extra_body, dict) or not extra_body:
|
||||
|
||||
Reference in New Issue
Block a user