Merge branch 'simp/r3-07-C2' into simp/r3-07

This commit is contained in:
Teknium
2026-09-02 22:50:53 -07:00
4 changed files with 223 additions and 329 deletions
+55 -83
View File
@@ -56,7 +56,9 @@ class _ManagedAttempt:
cls, session_id: str | None, request: dict[str, Any], metadata: dict[str, Any] | None,
*, name: str, model_name: str,
) -> "_ManagedAttempt | None":
"""Return the managed attempt for ``session_id``, or None to run unmanaged."""
"""Return the managed attempt for ``session_id`` (None: the inherited turn's), or None to run unmanaged."""
if session_id is None:
session_id = _current_session_id()
if not session_id:
return None
runtime, session, parent = relay_runtime.resolve_execution_context(session_id)
@@ -68,12 +70,9 @@ class _ManagedAttempt:
self, runtime: relay_runtime.RelayRuntime, session: Any, parent: Any,
request: dict[str, Any], metadata: dict[str, Any] | None, *, name: str, model_name: str,
) -> None:
self.runtime = runtime
self.session = session
self.runtime, self.session, self.request, self.metadata = runtime, session, request, metadata
self.logical = _logical_parent(runtime, session, parent, metadata)
self.parent = self.logical[1] if self.logical is not None else parent
self.request = request
self.metadata = metadata
self.body = _relay_request_body(request, metadata)
self.relay_request = runtime.relay.LLMRequest({}, self.body)
self.codec_baseline = _codec_round_trip_request_body(
@@ -109,28 +108,28 @@ class _ManagedAttempt:
self.raw_response["json"] = _jsonable(raw)
return self.raw_response["json"]
def invoke(self, callback: Callable[..., Any], next_request: Any) -> Any:
"""Provider callback handed to Relay: run ``callback`` on Relay's (possibly rewritten) request."""
@contextlib.contextmanager
def _recording_errors(self) -> Iterator[None]:
try:
raw = self.run_callback(callback, self.provider_request(next_request))
yield
except BaseException as exc:
self.raw_response["error"] = exc
raise
def invoke(self, callback: Callable[..., Any], next_request: Any) -> Any:
"""Provider callback handed to Relay: run ``callback`` on Relay's (possibly rewritten) request."""
with self._recording_errors():
raw = self.run_callback(callback, self.provider_request(next_request))
return self._record(raw)
async def invoke_async(self, callback: Callable[..., Any], next_request: Any) -> Any:
try:
async def call_provider() -> Any:
with relay_runtime.managed_callback_guard(): # nested relay calls run unmanaged
return await callback(final_request)
with self._recording_errors():
final_request = self.provider_request(next_request)
async def call_provider() -> Any:
# Nested relay calls run unmanaged — see relay_runtime.managed_callback_guard.
with relay_runtime.managed_callback_guard():
return await callback(final_request)
raw = await self.context.copy().run(asyncio.create_task, call_provider())
except BaseException as exc:
self.raw_response["error"] = exc
raise
return self._record(raw)
def run_managed(self, relay_call: Callable[..., Any], *callbacks: Any) -> Any:
@@ -177,8 +176,6 @@ def execute(
) -> Any:
"""Run one non-streaming physical provider attempt through Relay.
``session_id`` defaults to the inherited Hermes turn's session (unmanaged when there is none)."""
if session_id is None:
session_id = _current_session_id()
attempt = _ManagedAttempt.resolve(session_id, request, metadata, name=name, model_name=model_name)
if attempt is None:
return callback(request)
@@ -196,8 +193,6 @@ async def execute_async(
session_id: str | None = None, metadata: dict[str, Any] | None = None, defer_logical_completion: bool = False,
) -> Any:
"""Async ``execute``."""
if session_id is None:
session_id = _current_session_id()
attempt = _ManagedAttempt.resolve(session_id, request, metadata, name=name, model_name=model_name)
if attempt is None:
return await callback(request)
@@ -214,11 +209,9 @@ execute_current_async = execute_async
def _has_running_event_loop() -> bool:
try:
asyncio.get_running_loop()
except RuntimeError:
return False
return True
with contextlib.suppress(RuntimeError):
return asyncio.get_running_loop() is not None
return False
def stream_current(
@@ -227,15 +220,13 @@ def stream_current(
defer_logical_completion: bool = False, completed_response_predicate: Callable[[Any], bool] | None = None,
) -> Any:
"""Run a provider stream under the inherited Hermes turn when present.
With ``completed_response_predicate`` set, a factory that ignores ``stream=True`` and
returns a complete response is unwrapped and returned directly (pre-Relay behavior)
instead of staying trapped as ``final_response``. Detecting that primes the lazy
pipeline: a genuine first chunk is buffered, but provider latency and pre-first-yield
With ``completed_response_predicate`` set, a factory that ignores ``stream=True`` and returns a
complete response is unwrapped and returned directly (pre-Relay behavior). Detecting that primes
the lazy pipeline: a genuine first chunk is buffered, but provider latency and pre-first-yield
errors may surface before this returns."""
session_id = _current_session_id()
# On the Relay session's loop (inside a managed callback) a nested ManagedLlmStream would
# be iterated synchronously on that loop, which asyncio forbids; the outer managed stream
# already tracks this attempt.
# Inside a managed callback (on the Relay session's loop) a nested ManagedLlmStream would be
# iterated synchronously on that loop, which asyncio forbids; the outer stream tracks this attempt.
if session_id is None or _has_running_event_loop():
return stream_factory(request)
managed = stream(
@@ -244,12 +235,9 @@ def stream_current(
completed_response_predicate=completed_response_predicate,
)
if completed_response_predicate is not None:
# Relay may defer the provider callback until the first pull; prime once so a
# completed response surfaces (a real first chunk is buffered).
# Relay may defer the provider callback until the first pull; prime once (a real first chunk is buffered).
managed._prime_completed_response()
if managed.final_response is not None:
return managed.final_response
return managed
return managed.final_response if managed.final_response is not None else managed
def _aclose_on_loop(loop: asyncio.AbstractEventLoop, stream: Any) -> None:
@@ -258,7 +246,7 @@ def _aclose_on_loop(loop: asyncio.AbstractEventLoop, stream: Any) -> None:
if not callable(close):
return
async def close_stream() -> None:
async def close_stream() -> None: # create the coroutine on ``loop``, not the caller's thread
await close()
loop.run_until_complete(close_stream())
@@ -287,17 +275,12 @@ class ManagedLlmStream(Iterator[Any]):
self._defer_logical_completion = defer_logical_completion
# Only auxiliary calls report model/provider on their logical scope.
auxiliary = str((metadata or {}).get("call_role") or "").startswith("auxiliary:")
self._logical_model_name: str | None = model_name if auxiliary else None
self._logical_provider_name: str | None = name if auxiliary else None
self._on_chunk = on_chunk
self._chunk_adapter = chunk_adapter or _namespace
self._accept_chunk = accept_chunk
self._logical_model_name, self._logical_provider_name = (model_name, name) if auxiliary else (None, None)
self._on_chunk, self._chunk_adapter, self._accept_chunk = on_chunk, chunk_adapter or _namespace, accept_chunk
self._stream_factory, self._on_stream_created, self._finalizer = stream_factory, on_stream_created, finalizer
self._completed_response_predicate = completed_response_predicate
self._raw_chunks: list[tuple[Any, Any]] = []
self._prefetched_chunks: list[Any] = []
self._stream_factory = stream_factory
self._on_stream_created = on_stream_created
self._completed_response_predicate = completed_response_predicate
self._finalizer = finalizer
attempt = _ManagedAttempt.resolve(session_id, request, metadata, name=name, model_name=model_name)
if attempt is None:
self._start_unmanaged(request)
@@ -355,8 +338,8 @@ class ManagedLlmStream(Iterator[Any]):
raise
def _relay_finalizer(self, attempt: _ManagedAttempt) -> Any:
# Relay may call this while unwinding a provider-stream failure; keep the
# original error instead of a secondary "missing terminal response".
# Relay may call this while unwinding a provider-stream failure; keep the original
# error instead of a secondary "missing terminal response".
if self._callback_error is not None:
return None
try:
@@ -410,7 +393,7 @@ class ManagedLlmStream(Iterator[Any]):
def _recoverable_relay_failure(self, exc: BaseException) -> bool:
"""Relay post-processing failed after the provider already succeeded."""
recoverable = (isinstance(exc, Exception) and self._provider_completed and self._callback_error is None)
recoverable = isinstance(exc, Exception) and self._provider_completed and self._callback_error is None
if recoverable:
logger.warning(
"NeMo Relay stream post-processing failed after provider success; preserving the provider result",
@@ -435,12 +418,8 @@ class ManagedLlmStream(Iterator[Any]):
if self._prefetched_chunks:
return self._prefetched_chunks.pop()
if self._loop is None:
try:
chunk = next(self._stream)
except StopIteration:
self._close(logical_outcome="cancelled")
raise
if self._accept_chunk is not None and not self._accept_chunk(chunk):
chunk = next(self._stream, self) # self: exhausted sentinel
if chunk is self or (self._accept_chunk is not None and not self._accept_chunk(chunk)):
self._close(logical_outcome="cancelled")
raise StopIteration
return chunk
@@ -510,10 +489,9 @@ class ManagedLlmStream(Iterator[Any]):
self._raw_stream_resource = None
for resource in resources.values():
close = getattr(resource, "close", None)
if not callable(close):
continue
try:
close()
if callable(close):
close()
except Exception as exc:
self._keep_first_close_error(exc)
logger.debug("Provider stream cleanup failed", exc_info=True)
@@ -524,18 +502,17 @@ class ManagedLlmStream(Iterator[Any]):
self._closed = True
self._prefetched_chunks.clear()
try:
loop = self._loop
self._loop = None
loop, self._loop = self._loop, None
if loop is None:
self._close_provider_resources()
self._finish_logical(logical_outcome)
return
try:
_aclose_on_loop(loop, self._stream)
except Exception as exc:
self._keep_first_close_error(exc)
else:
try:
_aclose_on_loop(loop, self._stream)
except Exception as exc:
self._keep_first_close_error(exc)
self._finish_logical(logical_outcome)
loop.close()
if loop is not None:
loop.close()
finally:
self._release_runtime_lease()
@@ -612,10 +589,9 @@ class AnthropicStreamAccumulator:
for block in blocks:
partial = block.pop("_partial_json", None)
if partial is not None:
try:
block["input"] = json.loads(partial)
except (TypeError, ValueError):
block["input"] = partial
with contextlib.suppress(TypeError, ValueError):
partial = json.loads(partial)
block["input"] = partial
return {**self._message, "content": blocks}
def response(self, base: Any = None) -> Any:
@@ -724,9 +700,8 @@ def _provider_request(
if codec_baseline_body is not None and not _json_equal(content, relay_request_body):
baseline = codec_baseline_body
intercepted = _provider_request_body(content, metadata)
# Typed codecs may not represent provider-specific fields: overlay only values
# that changed from the codec-facing baseline so unrelated intercepts cannot
# delete or normalize unknown provider arguments.
# Typed codecs may not represent provider-specific fields: overlay only values that changed
# from the codec-facing baseline so unrelated intercepts cannot delete/normalize unknown arguments.
for key in baseline.keys() | intercepted.keys():
if key not in intercepted:
final.pop(key, None)
@@ -805,11 +780,11 @@ def _codec_round_trip_request_body(
try:
encoded = codec.encode(codec.decode(relay_request), relay_request)
content = getattr(encoded, "content", encoded)
if isinstance(content, dict):
return _provider_request_body(content, metadata)
except Exception:
logger.warning("NeMo Relay request codec baseline failed; ignoring request rewrites", exc_info=True)
return None
if isinstance(content, dict):
return _provider_request_body(content, metadata)
logger.warning("NeMo Relay request codec returned an unsupported baseline; ignoring request rewrites")
return None
@@ -826,10 +801,7 @@ def _provider_request_body(content: dict[str, Any], metadata: dict[str, Any] | N
def _codec(relay: Any, metadata: dict[str, Any] | None) -> Any:
protocol = _RELAY_PROTOCOL_BY_API_MODE.get(_api_mode(metadata))
codecs = getattr(relay, "codecs", None)
if protocol is None or codecs is None:
return None
codec = getattr(codecs, protocol[1], None)
codec = getattr(getattr(relay, "codecs", None), protocol[1], None) if protocol is not None else None
return codec() if callable(codec) else None
+158 -227
View File
@@ -6,6 +6,7 @@ import atexit
import asyncio
import contextlib
import contextvars
import functools
import importlib
import inspect
import logging
@@ -15,7 +16,7 @@ import tomllib
import uuid
from concurrent.futures import TimeoutError as FuturesTimeoutError
from dataclasses import dataclass, field
from enum import Enum, auto
from enum import Enum
from pathlib import Path
from typing import Any, Callable
@@ -37,8 +38,34 @@ _PROFILE_KEY_CACHE: dict[str, str] = {}
# lost span, never a blocked agent.
_SCOPE_OP_TIMEOUT = 10.0
_SCOPE_OP_EXECUTOR: Any = None
_SCOPE_OP_EXECUTOR_LOCK = threading.Lock()
class _Lazy:
"""Double-checked, thread-safe once-only factory; ``reset()`` re-arms it (tests)."""
def __init__(self, factory: Callable[[], Any]) -> None:
self._factory, self._value, self._lock = factory, None, threading.Lock()
def get(self) -> Any:
if self._value is None:
with self._lock:
if self._value is None:
self._value = self._factory()
return self._value
def reset(self) -> None:
self._value = None
def _new_scope_op_executor() -> Any:
# Daemon workers: a wedged call abandoned at timeout cannot block interpreter exit;
# ``Future.result(timeout=...)`` still bounds callers when every worker is wedged.
from tools.daemon_pool import DaemonThreadPoolExecutor
return DaemonThreadPoolExecutor(max_workers=8, thread_name_prefix="relay-scope-op")
_SCOPE_OP_EXECUTOR = _Lazy(_new_scope_op_executor)
_scope_op_executor = _SCOPE_OP_EXECUTOR.get
def runtime_metadata(runtime_id: str, **extra: Any) -> dict[str, Any]:
@@ -46,25 +73,11 @@ def runtime_metadata(runtime_id: str, **extra: Any) -> dict[str, Any]:
return {RUNTIME_SCHEMA_KEY: RUNTIME_SCHEMA_VERSION, RUNTIME_INSTANCE_KEY: runtime_id, **extra}
def _scope_op_executor():
"""Shared daemon executor for bounded native scope ops.
Daemon workers so a wedged call abandoned at timeout cannot block interpreter exit;
``Future.result(timeout=...)`` still bounds callers when every worker is wedged."""
global _SCOPE_OP_EXECUTOR
if _SCOPE_OP_EXECUTOR is None:
with _SCOPE_OP_EXECUTOR_LOCK:
if _SCOPE_OP_EXECUTOR is None:
from tools.daemon_pool import DaemonThreadPoolExecutor
_SCOPE_OP_EXECUTOR = DaemonThreadPoolExecutor(max_workers=8, thread_name_prefix="relay-scope-op")
return _SCOPE_OP_EXECUTOR
def _run_on_daemon_thread(
fn: Callable[[], Any], *, name: str, timeout: float | None = None, timeout_message: str = ""
) -> Any:
"""Run ``fn`` on a fresh daemon thread; re-raise its error or return its result.
With ``timeout`` a still-running worker is abandoned with ``TimeoutError`` (daemon:
cannot block interpreter exit)."""
With ``timeout`` a still-running worker is abandoned with ``TimeoutError`` (daemon: cannot block exit)."""
outcome: dict[str, Any] = {}
def _target() -> None:
@@ -87,8 +100,7 @@ def pop_relay_scope(relay: Any, handle: Any, *, output: Any = None, metadata: An
"""Pop a Relay scope, forwarding only the kwargs the live binding accepts.
``scope.pop`` gained ``metadata`` in nemo-relay 0.4+; older wheels raise TypeError."""
pop = relay.scope.pop
candidates = (("output", output), ("metadata", metadata), ("timestamp", timestamp))
kwargs = {key: value for key, value in candidates if value is not None}
kwargs = {k: v for k, v in (("output", output), ("metadata", metadata), ("timestamp", timestamp)) if v is not None}
try:
params = inspect.signature(pop).parameters
except (TypeError, ValueError):
@@ -100,8 +112,7 @@ def pop_relay_scope(relay: Any, handle: Any, *, output: Any = None, metadata: An
def _current_top(relay: Any) -> Any:
"""Return the current top-of-stack scope handle, or None."""
# Prefer scope.get_handle(): get_scope_stack() may return a native ScopeStack
# object that scope.pop rejects, so never treat it as a handle.
# Prefer scope.get_handle(): get_scope_stack() may return a native ScopeStack that scope.pop rejects.
get_handle = getattr(getattr(relay, "scope", None), "get_handle", None)
if callable(get_handle):
with contextlib.suppress(Exception):
@@ -119,14 +130,8 @@ def _same_handle(a: Any, b: Any) -> bool:
return a is b or a == b or (a_uuid is not None and a_uuid == getattr(b, "uuid", None))
class _RelayPluginConfigurationState(Enum):
"""Process-wide result shared by every currently hosted profile."""
UNINITIALIZED = auto()
DISABLED = auto()
ACTIVE = auto()
FOREIGN = auto()
FAILED = auto()
# Process-wide plugin-configuration result shared by every currently hosted profile.
_RelayPluginConfigurationState = Enum("_RelayPluginConfigurationState", "UNINITIALIZED DISABLED ACTIVE FOREIGN FAILED")
class _RelayPluginConfigurationLoadError(RuntimeError):
@@ -143,23 +148,16 @@ class RelaySession:
closing: bool = False
handle: Any = None
context: contextvars.Context | None = None
# Session-span segmentation: rotation closes the current session scope and pushes
# segment N+1 at a turn boundary (the only LIFO-safe point).
# Segmentation: rotation closes the session scope and pushes segment N+1 at a turn
# boundary (the only LIFO-safe point). Flags are set by compaction, consumed at that boundary.
segment: int = 0 # index of the CURRENT session scope (0 = first)
segment_turns: int = 0 # turns completed within the current segment
rotate_pending: bool = False # set by compaction; consumed at next begin_turn
# Rotating compaction landed while a turn was live here; closing now would pop the
# session scope under the live turn, so end_turn consumes this instead.
close_pending: bool = False
# gateway.telemetry.session_segments, cached at first read. Both defaults OFF =>
# rotation never fires and the scope lifecycle is unchanged.
_SEGMENTS_CONFIG: dict[str, Any] | None = None
_SEGMENTS_CONFIG_LOCK = threading.Lock()
rotate_pending: bool = False # consumed at next begin_turn
close_pending: bool = False # rotating compaction hit a live turn; end_turn consumes it
def _load_segments_config() -> dict[str, Any]:
"""gateway.telemetry.session_segments; both defaults OFF => rotation never fires."""
segments: dict[str, Any] = {}
with contextlib.suppress(Exception): # config absence must not crash
from gateway.run import _load_gateway_config # late import
@@ -172,27 +170,16 @@ def _load_segments_config() -> dict[str, Any]:
return {"on_compaction": bool(segments.get("on_compaction", False)), "max_turns": max_turns}
def _segments_config() -> dict[str, Any]:
"""Resolve session-segmentation settings; inert defaults when unset."""
global _SEGMENTS_CONFIG
if _SEGMENTS_CONFIG is None:
with _SEGMENTS_CONFIG_LOCK:
if _SEGMENTS_CONFIG is None:
_SEGMENTS_CONFIG = _load_segments_config()
return _SEGMENTS_CONFIG
def _reset_segments_config_for_tests() -> None:
global _SEGMENTS_CONFIG
_SEGMENTS_CONFIG = None
_SEGMENTS_CONFIG = _Lazy(_load_segments_config) # cached at first read
_segments_config = _SEGMENTS_CONFIG.get
_reset_segments_config_for_tests = _SEGMENTS_CONFIG.reset
class RelayOperationLease:
"""Keep process-wide Relay plugins alive across a deferred operation."""
def __init__(self, runtime: "RelayRuntime") -> None:
self._lock = threading.Lock()
self._runtime: RelayRuntime | None = runtime
self._lock, self._runtime = threading.Lock(), runtime
def run_in_session(self, session: RelaySession, callback: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
"""Run cleanup while this lease still owns the runtime lifetime."""
@@ -254,16 +241,14 @@ class _ProcessRelayPluginConfiguration:
existing_report = relay.plugin.report()
except Exception:
logger.warning(
"Hermes could not determine whether a process-global Relay "
"plugin configuration is already active; refusing to replace it",
exc_info=True,
"Hermes could not determine whether a process-global Relay plugin configuration is already "
"active; refusing to replace it", exc_info=True,
)
return _RelayPluginConfigurationState.FAILED
if existing_report is not None:
logger.warning(
"A process-global Relay plugin configuration is already active "
"outside Hermes native ownership; leaving it unchanged and "
"disabling Hermes-managed Relay middleware for this process"
"A process-global Relay plugin configuration is already active outside Hermes native ownership; "
"leaving it unchanged and disabling Hermes-managed Relay middleware for this process"
)
return _RelayPluginConfigurationState.FOREIGN
return None
@@ -291,10 +276,9 @@ class _ProcessRelayPluginConfiguration:
def release(self, owner: Any) -> None:
"""Release one host and clear Relay after the final host exits."""
with self._lock:
if id(owner) not in self._owners:
return
self._owners.remove(id(owner))
self.retry_pending_cleanup()
if id(owner) in self._owners:
self._owners.remove(id(owner))
self.retry_pending_cleanup()
def reset_for_tests(self) -> None:
"""Clear process-global state left by directly constructed test hosts."""
@@ -312,21 +296,23 @@ class _ProcessRelayPluginConfiguration:
relay, activation = self._relay, self._activation
if relay is None:
return True
try:
_resolve_plugin_awaitable(relay.subscribers.flush_async())
except Exception:
logger.warning("Hermes Relay plugin subscriber flush failed", exc_info=True)
return False
try:
def close_configuration() -> Any:
if activation is None:
_resolve_plugin_awaitable(relay.plugin.clear_async())
elif callable(close := getattr(activation, "close", None)):
_resolve_plugin_awaitable(close())
else:
raise RuntimeError("NeMo Relay dynamic plugin activation has no close method")
except Exception:
logger.warning("Hermes Relay plugin configuration cleanup failed", exc_info=True)
return False
return _resolve_plugin_awaitable(relay.plugin.clear_async())
if callable(close := getattr(activation, "close", None)):
return _resolve_plugin_awaitable(close())
raise RuntimeError("NeMo Relay dynamic plugin activation has no close method")
for what, step in (
("subscriber flush", lambda: _resolve_plugin_awaitable(relay.subscribers.flush_async())),
("configuration cleanup", close_configuration),
):
try:
step()
except Exception:
logger.warning("Hermes Relay plugin %s failed", what, exc_info=True)
return False
self._relay = self._activation = None
return True
@@ -342,17 +328,15 @@ class RelayRuntime:
self.relay = relay or _load_nemo_relay()
self.profile_key = profile_key or current_profile_key()
self.runtime_id = uuid.uuid4().hex
self._sessions_lock = threading.RLock()
self._sessions_lock, self._execution_consumers_lock = threading.RLock(), threading.RLock()
self._sessions: dict[str, RelaySession] = {}
self._subagent_parents: dict[str, str] = {}
self._subagent_parent_handles: dict[str, Any] = {}
self._execution_consumers: set[str] = set()
self._closing = self._shutdown_started = False
self._shutdown_complete = threading.Event()
self._operations_idle = threading.Event()
self._shutdown_complete, self._operations_idle = threading.Event(), threading.Event()
self._operations_idle.set()
self._active_operations = 0
self._execution_consumers_lock = threading.RLock()
self._execution_consumers: set[str] = set()
self._plugin_configuration_state = _PLUGIN_CONFIGURATION.acquire(self, self.relay)
# Cleared (with the atexit hook) by the first successful _finish_shutdown.
self._plugin_configuration_registered = True
@@ -385,9 +369,8 @@ class RelayRuntime:
exit_fallback: bool = False, **push_kwargs: Any,
) -> None:
"""Push a fresh SESSION_SCOPE for ``session`` (bounded by ``_SCOPE_OP_TIMEOUT``); record handle + context.
Subagents parent under their spawning turn/session handle; ``resolve_parent`` creates the parent
session when its handle is unknown. ``exit_fallback``: at interpreter shutdown the executor refuses
new futures; push synchronously instead (no agent turn waits at exit)."""
Subagents parent under their spawning turn/session handle (``resolve_parent`` creates the parent when
unknown). ``exit_fallback``: at interpreter shutdown the executor refuses futures; push synchronously."""
parent_handle = None
if session.parent_session_id:
with self._sessions_lock:
@@ -421,28 +404,22 @@ class RelayRuntime:
return None
session = self._sessions.get(session_id)
if session is None:
parent_session_id = self._subagent_parents.get(session_id, "")
session = RelaySession(session_id=session_id, parent_session_id=parent_session_id)
self._sessions[session_id] = session
session = self._sessions[session_id] = RelaySession(
session_id=session_id, parent_session_id=self._subagent_parents.get(session_id, "")
)
with session.lock:
if session.closing:
return None
if session.handle is None:
try:
self._open_session_scope(
session, {**(metadata or {}), **runtime_metadata(self.runtime_id)},
resolve_parent=True, data=data, exit_fallback=True,
)
except Exception:
session.context = None
raise
self._open_session_scope(
session, {**(metadata or {}), **runtime_metadata(self.runtime_id)},
resolve_parent=True, data=data, exit_fallback=True,
)
return session
def rotate_session_scope(self, session: RelaySession, *, reason: str) -> None:
"""Close the current session scope and open the next segment.
Called ONLY at a turn boundary: the stack is LIFO and rotating under a live child
would close a parent out of order. Bookkeeping advances even when a native call
fails so a degraded rotation cannot retry on every turn."""
"""Close the current session scope and open the next segment (turn boundary ONLY: LIFO).
Bookkeeping advances even when a native call fails so a degraded rotation cannot retry every turn."""
with session.lock:
if session.closing or session.handle is None:
return
@@ -482,12 +459,8 @@ class RelayRuntime:
return None
parent = self.ensure_session({"session_id": parent_session_id})
parent_handle = None if parent is None else parent.handle
turn = active_turn(parent_session_id)
# active_turn() already proved liveness and, for a RelayRuntime host, an open session.
if (
turn is not None and turn.handle is not None and turn.lease.host is self
and turn.lease.session.session_id == parent_session_id
):
turn = active_turn(parent_session_id) # already proved liveness and (RelayRuntime host) an open session
if turn is not None and turn.handle is not None and turn.lease.host is self:
parent_handle = turn.handle
with self._sessions_lock:
if self._closing:
@@ -524,14 +497,13 @@ class RelayRuntime:
return None if session.closing else session
def _session_context(self, session: RelaySession, *, allow_closing: bool) -> contextvars.Context:
"""Copy the current context and overlay the session's saved Relay vars."""
"""Copy the current context and overlay the session's saved Relay vars (a copy: re-entrant from callbacks)."""
with session.lock:
if session.closing and not allow_closing:
raise RuntimeError("Hermes Relay session is closing")
if session.context is None or session.handle is None:
raise RuntimeError("Hermes Relay session context is unavailable")
relay_context = session.context.copy()
# A copy lets a helper inside a Relay callback re-enter the session's Context.
context = contextvars.copy_context()
for variable, value in relay_context.items():
context.run(variable.set, value)
@@ -542,9 +514,8 @@ class RelayRuntime:
allow_closing: bool = False, timeout: float | None = None, **kwargs: Any,
) -> Any:
"""Run a Relay operation against a session's isolated scope stack.
``timeout`` bounds the native call on the daemon executor (``TimeoutError`` on
breach); ``None`` runs synchronously. Lifecycle ops gating turn/session completion
pass ``_SCOPE_OP_TIMEOUT``: a wedged pipeline must cost one span, never the agent."""
``timeout`` bounds the native call on the daemon executor (``TimeoutError`` on breach); ``None``
runs synchronously. Lifecycle ops pass ``_SCOPE_OP_TIMEOUT``: a wedged pipeline costs one span."""
with self._operation():
return self._run_in_session_untracked(
session, callback, *args, allow_closing=allow_closing, timeout=timeout, **kwargs
@@ -567,8 +538,8 @@ class RelayRuntime:
try:
future = _scope_op_executor().submit(context.run, invoke)
except RuntimeError:
# Interpreter shutdown: the executor refuses new futures, but the atexit close
# path must still flush — still bounded so a wedged call cannot block exit.
# Interpreter shutdown: the executor refuses futures but the atexit close path must
# still flush — still bounded so a wedged call cannot block exit.
return _run_on_daemon_thread(
lambda: context.run(invoke), name="relay-scope-op-exit", timeout=timeout,
timeout_message=f"{exceeded} during interpreter shutdown; abandoning the native "
@@ -628,9 +599,8 @@ class RelayRuntime:
def apply_tool_request_intercepts(self, *, session_id: str, tool_name: str, args: dict[str, Any]) -> dict[str, Any]:
"""Apply Relay request rewriting before Hermes authorizes a tool call."""
request_intercepts = getattr(getattr(self.relay, "tools", None), "request_intercepts", None)
if not self.managed_execution_enabled() or not callable(request_intercepts):
return args
session = self.ensure_session({"session_id": session_id})
managed = self.managed_execution_enabled() and callable(request_intercepts)
session = self.ensure_session({"session_id": session_id}) if managed else None
if session is None:
return args
result = self.run_in_session(session, request_intercepts, tool_name, args)
@@ -639,9 +609,8 @@ class RelayRuntime:
def _pop_with_drain(
self, handle: Any, *, output: dict[str, Any], metadata: dict[str, Any], session_root: Any, drain_limit: int,
) -> BaseException | None:
"""Pop ``handle``; if that fails, drain orphans above it and retry once.
Returns the retry's error (None on success). Must run inside ONE ``run_in_session``
callback so ContextVar stack views stay consistent."""
"""Pop ``handle``; if that fails, drain orphans above it and retry once; return the retry's error.
Must run inside ONE ``run_in_session`` callback so ContextVar stack views stay consistent."""
with contextlib.suppress(Exception):
pop_relay_scope(self.relay, handle, output=output, metadata=metadata)
return None
@@ -672,10 +641,9 @@ class RelayRuntime:
self, session: RelaySession, handle: Any, *, output: dict[str, Any] | None = None, allow_closing: bool = False,
failure_label: str = "scope close failed", drain_limit: int = 32, operation_already_held: bool = False,
) -> str | None:
"""Pop ``handle``, draining orphaned children in the same session context.
Relay scopes are strict LIFO; empty-stream retries + interrupt can abandon a
physical LLM scope above TURN/SESSION. Drain+close is bounded so a wedged pipeline
never blocks turn/session completion. Returns a failure string or None."""
"""Pop ``handle``, draining orphaned children in the same session context; failure string or None.
Relay scopes are strict LIFO; empty-stream retries + interrupt can abandon a physical LLM scope
above TURN/SESSION. Drain+close is bounded so a wedged pipeline never blocks completion."""
if handle is None:
return None
run_in_session = (self._run_in_session_untracked if operation_already_held else self.run_in_session)
@@ -690,15 +658,9 @@ class RelayRuntime:
return None if failure is None else f"{failure_label}: {failure}"
def close_session(self, event: dict[str, Any]) -> None:
"""Close one session scope and remove it from the core registry."""
try:
self._begin_operation()
except RuntimeError:
return
try:
"""Close one session scope and remove it from the core registry (no-op once shutting down)."""
with contextlib.suppress(RuntimeError), self._operation(): # _close_session itself never raises
self._close_session(event)
finally:
self._end_operation()
def _close_session(self, event: dict[str, Any]) -> None:
"""Close one session already admitted by the host lifecycle gate."""
@@ -717,8 +679,8 @@ class RelayRuntime:
session, session.handle, output={}, allow_closing=True,
failure_label="session scope close failed", operation_already_held=True,
)
# No subscriber flush here: it is process-wide, may wait on other sessions'
# publications and can deadlock an asyncio loop; final plugin teardown flushes once.
# No subscriber flush here: process-wide, may wait on other sessions and deadlock an asyncio
# loop; final plugin teardown flushes once.
with self._sessions_lock:
if self._sessions.get(session_id) is session:
del self._sessions[session_id]
@@ -814,8 +776,7 @@ class RelayHostRegistry:
def shutdown_all(self) -> None:
with self._lock:
hosts = list(self._hosts.values())
self._hosts.clear()
hosts, self._hosts = list(self._hosts.values()), {}
for host in hosts:
host.shutdown()
@@ -837,8 +798,7 @@ class ConversationLease:
def live_runtime(self) -> RelayRuntime | None:
"""Return the real Relay host when this lease owns an open session."""
host = self.host
return host if isinstance(host, RelayRuntime) and self.session is not None else None
return self.host if isinstance(self.host, RelayRuntime) and self.session is not None else None
@dataclass
@@ -862,28 +822,24 @@ _CURRENT_TURN: contextvars.ContextVar[RelayTurnContext | None] = contextvars.Con
"hermes_relay_turn", default=None
)
# Depth of managed Relay callbacks on the current call path (>0 while the native pipeline
# is mid-dispatch of a Hermes tool/LLM callback). Nested managed execution there is
# structurally broken: the pipeline binds its Futures to the OUTER call's event loop, which
# is blocked inside the synchronous callback (wrong loop / deadlock / "Event loop is
# closed"), so resolve_execution_context() bypasses Relay while set. A ContextVar so the
# marker follows copy_context() into worker threads / per-thread loops.
# >0 while the native pipeline is mid-dispatch of a Hermes tool/LLM callback. Nested managed
# execution there is structurally broken (the pipeline binds its Futures to the OUTER call's
# loop, blocked inside the synchronous callback), so resolve_execution_context() bypasses Relay.
# A ContextVar so the marker follows copy_context() into worker threads / per-thread loops.
_MANAGED_CALLBACK_DEPTH: contextvars.ContextVar[int] = contextvars.ContextVar(
"hermes_relay_managed_callback_depth", default=0
)
class managed_callback_guard:
"""Mark the current context as inside a managed Relay callback.
Wrap the ``invoke()`` callbacks handed to the native pipeline; everything they
transitively call (incl. work forwarded via copy_context()) runs unmanaged."""
def __enter__(self) -> "managed_callback_guard":
self._token = _MANAGED_CALLBACK_DEPTH.set(_MANAGED_CALLBACK_DEPTH.get() + 1)
return self
def __exit__(self, *exc_info: Any) -> None:
_MANAGED_CALLBACK_DEPTH.reset(self._token)
@contextlib.contextmanager
def managed_callback_guard():
"""Mark the current context as inside a managed Relay callback: everything the wrapped ``invoke()``
transitively calls (incl. work forwarded via copy_context()) runs unmanaged."""
token = _MANAGED_CALLBACK_DEPTH.set(_MANAGED_CALLBACK_DEPTH.get() + 1)
try:
yield
finally:
_MANAGED_CALLBACK_DEPTH.reset(token)
def _warn_on_error(what: str, callback: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
@@ -895,6 +851,16 @@ def _warn_on_error(what: str, callback: Callable[..., Any], *args: Any, **kwargs
return None
def _fail_open(what: str) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Decorator form of ``_warn_on_error`` for telemetry hooks that must never block the caller."""
def wrap(fn: Callable[..., Any]) -> Callable[..., Any]:
@functools.wraps(fn)
def guarded(*args: Any, **kwargs: Any) -> Any:
return _warn_on_error(what, fn, *args, **kwargs)
return guarded
return wrap
def _flag_open_session(session: RelaySession, flag: str) -> None:
"""Set a pending-rotation/close flag unless the session is already closing."""
with session.lock:
@@ -929,9 +895,7 @@ class RelaySessionCoordinator:
def acquire_conversation(
self, *, profile_key: str, session_id: str, platform: str, parent_session_id: str = "", model: str = "",
) -> ConversationLease:
host = self.registry.for_profile(profile_key) or NoopRelayRuntime(
profile_key, "Relay host creation was disabled"
)
host = self.registry.for_profile(profile_key) or NoopRelayRuntime(profile_key, "Relay host creation was disabled")
session = None
if isinstance(host, RelayRuntime):
context = {
@@ -949,9 +913,8 @@ class RelaySessionCoordinator:
session_id, parent_session_id = context["session_id"], context["parent_session_id"]
metadata = {"hermes.execution_surface": context["platform"] or "unknown"}
if parent_session_id and parent_session_id != session_id:
return host.register_subagent(
{"parent_session_id": parent_session_id, "child_session_id": session_id}, metadata=metadata,
)
event = {"parent_session_id": parent_session_id, "child_session_id": session_id}
return host.register_subagent(event, metadata=metadata)
return host.ensure_session({"session_id": session_id}, metadata=metadata)
def begin_turn(self, lease: ConversationLease, *, turn_id: str, task_id: str) -> RelayTurnContext:
@@ -961,8 +924,7 @@ class RelaySessionCoordinator:
key = (lease.profile_key, lease.session_id)
with self._active_turns_lock:
if self._active_turns.get(key):
# One physical scope stack per session; concurrent turns would create
# sibling scopes whose completion order is not LIFO.
# One physical scope stack per session; concurrent turns' sibling scopes would not close LIFO.
turn.relay_enabled = False
logger.warning(
"Skipping Relay instrumentation for concurrent Hermes turn %s in session %s",
@@ -973,8 +935,7 @@ class RelaySessionCoordinator:
turn._active_registered = True
host = lease.live_runtime() if turn.relay_enabled else None
if host is not None:
# Segment rotation happens HERE — the only point with no live turn scope on
# the stack, so the session scope can close/reopen without breaking LIFO.
# Rotation happens HERE: no live turn scope on the stack, so the session scope can close/reopen LIFO.
_warn_on_error("segment rotation", self._maybe_rotate_segment, host, lease.session)
turn.handle = _warn_on_error(
"turn initialization", host.run_in_session, lease.session, host.relay.scope.push,
@@ -1011,8 +972,8 @@ class RelaySessionCoordinator:
with contextlib.suppress(Exception), lease.session.lock: # accounting never blocks
lease.session.segment_turns += 1 # max_turns rotation trigger
try:
# Delegated agents own one turn: close their conversation while the
# active-turn guard is held so a parent timeout fallback cannot race it.
# Delegated agents own one turn: close their conversation while the active-turn
# guard is held so a parent timeout fallback cannot race it.
if lease.parent_session_id and isinstance(lease.host, RelayRuntime):
_warn_on_error(
"child conversation finalization", lease.host.unregister_subagent,
@@ -1032,15 +993,10 @@ class RelaySessionCoordinator:
if failure:
logger.warning("Hermes Relay turn finalization failed: %s", failure)
def _consume_deferred_close(self, lease: Any) -> None:
"""Close a session whose rotating-compaction close was deferred.
``notify_session_compacted`` sets ``close_pending`` when the old session had a live
turn (closing then breaks LIFO). The last live turn consumes it here after its own
scope popped and it left the active-turn table."""
# Telemetry must never block end_turn.
_warn_on_error("deferred session close", self._consume_deferred_close_unguarded, lease)
def _consume_deferred_close_unguarded(self, lease: ConversationLease) -> None:
@_fail_open("deferred session close")
def _consume_deferred_close(self, lease: ConversationLease) -> None:
"""Close a session whose rotating-compaction close was deferred (``close_pending``).
The last live turn consumes it after its own scope popped and it left the active-turn table."""
host = lease.live_runtime()
if host is None:
return
@@ -1049,19 +1005,12 @@ class RelaySessionCoordinator:
if pending and not self.has_active_turn(profile_key=lease.profile_key, session_id=lease.session_id):
host.close_session({"session_id": lease.session_id})
@_fail_open("compaction notification")
def notify_session_compacted(self, *, profile_key: str, session_id: str, old_session_id: str = "") -> None:
"""React to a completed compaction, per compaction mode.
In-place (``old_session_id`` empty/equal): flag rotation for the next turn boundary
— never rotate immediately, a turn may be live and rotating under it breaks LIFO.
Rotating (ids differ): the next turn gets a fresh session under the new id, so close
the OLD session now or its scope stays an unexported orphan. Unknown sessions and
disabled config are silent no-ops."""
# Telemetry must never block compaction.
_warn_on_error(
"compaction notification", self._notify_session_compacted_unguarded, profile_key, session_id, old_session_id
)
def _notify_session_compacted_unguarded(self, profile_key: str, session_id: str, old_session_id: str) -> None:
"""React to a completed compaction, per compaction mode; unknown sessions / disabled config are no-ops.
In-place (``old_session_id`` empty/equal): flag rotation for the next turn boundary — never rotate
immediately, a live turn under it breaks LIFO. Rotating (ids differ): the next turn gets a fresh
session under the new id, so close the OLD session now or its scope stays an unexported orphan."""
if not _segments_config()["on_compaction"]:
return
host = self.registry.for_profile(profile_key)
@@ -1121,8 +1070,7 @@ class RelaySessionCoordinator:
logical_calls.pop()
continue
with turn.logical_llm_lock:
# Stack-owned scopes: if the newest handle cannot close even after orphan
# drain, older ones cannot either — retain the unclosed prefix.
# Stack-owned: if the newest handle cannot close even after drain, older ones cannot either.
for pending_request_id, pending_handle in logical_calls:
turn.logical_llm_calls.setdefault(pending_request_id, pending_handle)
logger.warning("Hermes Relay logical LLM finalization failed: %s", failure)
@@ -1183,11 +1131,9 @@ def active_turn(session_id: str | None = None) -> RelayTurnContext | None:
def resolve_execution_context(session_id: str) -> tuple[RelayRuntime | None, RelaySession | None, Any]:
"""Resolve one active turn/session parent for managed Relay execution."""
if _MANAGED_CALLBACK_DEPTH.get() > 0:
# Nested managed execution is impossible (see _MANAGED_CALLBACK_DEPTH); the
# outer scope still records the tool-level event.
return None, None, None
if not relay_instrumentation_enabled():
# Nested managed execution is impossible (see _MANAGED_CALLBACK_DEPTH); the outer scope
# still records the tool-level event.
if _MANAGED_CALLBACK_DEPTH.get() > 0 or not relay_instrumentation_enabled():
return None, None, None
turn = active_turn(session_id)
host = turn.lease.live_runtime() if turn is not None else None
@@ -1205,9 +1151,7 @@ def resolve_execution_context(session_id: str) -> tuple[RelayRuntime | None, Rel
def apply_tool_request_intercepts(*, session_id: str, tool_name: str, args: dict[str, Any]) -> dict[str, Any]:
"""Return Relay-rewritten arguments at Hermes's authorization boundary."""
if not session_id:
return args
runtime = get_runtime(create=False)
runtime = get_runtime(create=False) if session_id else None
if runtime is None:
return args
return runtime.apply_tool_request_intercepts(session_id=session_id, tool_name=tool_name, args=args)
@@ -1219,12 +1163,9 @@ def _is_relay_wrapped_callback_error(relay_error: BaseException, callback_error:
return True
if not isinstance(relay_error, RuntimeError):
return False
callback_type = callback_error.__class__
type_names = {
callback_type.__name__, callback_type.__qualname__, f"{callback_type.__module__}.{callback_type.__qualname__}",
}
message = str(relay_error)
return any(message.startswith(f"internal error: {type_name}: {callback_error}") for type_name in type_names)
kind = type(callback_error)
type_names = {kind.__name__, kind.__qualname__, f"{kind.__module__}.{kind.__qualname__}"}
return any(str(relay_error).startswith(f"internal error: {name}: {callback_error}") for name in type_names)
def get_runtime(*, create: bool = True, profile_key: str | None = None) -> RelayRuntime | None:
@@ -1238,9 +1179,7 @@ def current_profile_key() -> str:
home = get_hermes_home().expanduser()
if not home.is_absolute():
return str(home.resolve())
raw = str(home)
cached = _PROFILE_KEY_CACHE.get(raw)
return cached if cached is not None else _PROFILE_KEY_CACHE.setdefault(raw, str(home.resolve()))
return _PROFILE_KEY_CACHE.get(str(home)) or _PROFILE_KEY_CACHE.setdefault(str(home), str(home.resolve()))
def _load_nemo_relay() -> Any:
@@ -1254,11 +1193,9 @@ def _configured_plugin_inputs(relay: Any) -> tuple[dict[str, Any], list[Any]] |
if not configured:
if legacy_vars := configured_legacy_relay_env_vars(os.environ):
logger.warning(
"Legacy NeMo Relay exporter variables are set but no %s was "
"provided. %s no longer activate Relay exporters; migrate the "
"exporter configuration to a Relay plugins.toml file.",
RELAY_PLUGINS_CONFIG_ENV,
", ".join(legacy_vars),
"Legacy NeMo Relay exporter variables are set but no %s was provided. %s no longer activate "
"Relay exporters; migrate the exporter configuration to a Relay plugins.toml file.",
RELAY_PLUGINS_CONFIG_ENV, ", ".join(legacy_vars),
)
return None
config_path = Path(configured).expanduser()
@@ -1266,17 +1203,12 @@ def _configured_plugin_inputs(relay: Any) -> tuple[dict[str, Any], list[Any]] |
with config_path.open("rb") as config_file:
config = tomllib.load(config_file)
if "dynamic_plugins" in config:
raise ValueError(
"Hermes [[dynamic_plugins]] records are unsupported; use Relay [[plugins.dynamic]] records"
)
dynamic_plugins: list[Any] = []
if "plugins" in config:
dynamic_plugins = relay.plugin.load_dynamic_plugin_activation_specs(config_path)
raise ValueError("Hermes [[dynamic_plugins]] records are unsupported; use Relay [[plugins.dynamic]] records")
dynamic_plugins = relay.plugin.load_dynamic_plugin_activation_specs(config_path) if "plugins" in config else []
return {k: v for k, v in config.items() if k != "plugins"}, dynamic_plugins
except Exception as exc:
raise _RelayPluginConfigurationLoadError(
"Hermes Relay plugin configuration could not be loaded from "
f"{config_path}; continuing without Relay plugins"
f"Hermes Relay plugin configuration could not be loaded from {config_path}; continuing without Relay plugins"
) from exc
@@ -1284,11 +1216,10 @@ def _resolve_plugin_awaitable(value: Any) -> Any:
"""Resolve Relay's async plugin API from synchronous host construction."""
if not inspect.isawaitable(value):
return value
try:
with contextlib.suppress(RuntimeError):
asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(value)
return _run_on_daemon_thread(lambda: asyncio.run(value), name="hermes-nemo-relay-plugin-lifecycle")
return _run_on_daemon_thread(lambda: asyncio.run(value), name="hermes-nemo-relay-plugin-lifecycle")
return asyncio.run(value)
def _session_id(event: dict[str, Any]) -> str:
+1 -2
View File
@@ -41,8 +41,7 @@ def execute(
except BaseException as exc:
callback_error = exc
raise
raw_result["value"] = result
raw_result["json"] = _jsonable(result)
raw_result.update(value=result, json=_jsonable(result))
return raw_result["json"]
try:
+9 -17
View File
@@ -82,26 +82,18 @@ class AnthropicTransport(ProviderTransport):
elif block.type in _THINKING_TYPES:
if block.type == "thinking":
reasoning_parts.append(block.thinking)
# Sanitized block preferred; raw only if sanitize dropped it.
if isinstance(clean_block, dict):
reasoning_details.append(clean_block)
elif isinstance(block_dict, dict):
reasoning_details.append(block_dict)
detail = clean_block if clean_block is not None else block_dict # raw only if sanitize dropped it
if isinstance(detail, dict):
reasoning_details.append(detail)
elif block.type == "tool_use":
name = block.name
if strip_tool_prefix and name.startswith(_MCP_PREFIX):
name = _unprefix_oauth_tool_name(name)
tool_calls.append(ToolCall(id=block.id, name=name, arguments=json.dumps(block.input)))
provider_data = {}
if reasoning_details:
provider_data["reasoning_details"] = reasoning_details
provider_data = {"reasoning_details": reasoning_details} if reasoning_details else {}
# Ordered channel only for the shape the parallel lists reconstruct wrongly.
kinds = {b.get("type") for b in ordered_blocks if isinstance(b, dict)}
signed = any(
b.get("type") in _THINKING_TYPES and (b.get("signature") or b.get("data"))
for b in ordered_blocks if isinstance(b, dict)
)
if signed and "tool_use" in kinds:
signed = any(b.get("type") in _THINKING_TYPES and (b.get("signature") or b.get("data")) for b in ordered_blocks)
if signed and any(b.get("type") == "tool_use" for b in ordered_blocks):
provider_data["anthropic_content_blocks"] = ordered_blocks
return NormalizedResponse(
content="\n".join(text_parts) if text_parts else None, tool_calls=tool_calls or None,
@@ -114,9 +106,9 @@ class AnthropicTransport(ProviderTransport):
"""Structural check; empty content is legitimate for ``end_turn``/``refusal`` (retrying
either would loop forever)."""
content_blocks = getattr(response, "content", None)
if not isinstance(content_blocks, list):
return False
return bool(content_blocks) or getattr(response, "stop_reason", None) in {"end_turn", "refusal"}
return isinstance(content_blocks, list) and (
bool(content_blocks) or getattr(response, "stop_reason", None) in {"end_turn", "refusal"}
)
def extract_cache_stats(self, response: Any) -> Optional[Dict[str, int]]:
"""Anthropic cache_read / cache_creation token counts."""