Commit Graph

63 Commits

Author SHA1 Message Date
Teknium feed17bd75 refactor(agent/relay): unmanaged __next__ sentinel exit; codec baseline/_codec flattened; register_subagent turn check relies on active_turn invariant; small ctor/lookup collapses 2026-09-02 22:46:46 -07:00
Teknium 9b669ca507 refactor(agent/relay): _ManagedAttempt error-recording ctx manager; managed_callback_guard as contextmanager; plugin cleanup steps table; close_session via _operation; anthropic normalize predicates collapsed 2026-09-02 22:35:41 -07:00
Teknium 763cee5c20 refactor(agent/relay_runtime): _Lazy singleton for executor/segments config; _fail_open decorator replaces *_unguarded pairs; dead context reset dropped 2026-09-02 22:22:18 -07:00
Teknium 874843146b refactor(agent/relay_runtime): conversation initializer context built once 2026-09-02 21:22:33 -07:00
Teknium aca6425ee9 refactor(agent/relay): Noop host via staticmethod lambdas; grouped default-flag declarations 2026-09-02 21:17:48 -07:00
Teknium 5ecba78cd8 refactor(agent/relay_runtime): merge active_turn guards; small guard inversions 2026-09-02 21:07:53 -07:00
Teknium 11dab00851 refactor(agent/relay_runtime): plugin config tracks ownership via _relay alone; fold intercept/subagent guards 2026-09-02 21:05:57 -07:00
Teknium fa06ccf236 refactor(agent/relay_runtime): _operation() context manager for tracked Relay calls 2026-09-02 20:51:04 -07:00
Teknium 720a4d46ef refactor(agent/relay_runtime): end_turn accounting via suppress+lock; logical-call unwind pops from the tail 2026-09-02 20:37:43 -07:00
Teknium b121ac4a83 refactor(agent/relay_llm): execute/execute_async default to the inherited turn's session; execute_current(_async) become aliases 2026-09-02 20:29:43 -07:00
Teknium 5adb74fd24 refactor(agent/relay_runtime,transports): inline single-use _push_session_scope; get_transport rediscovery predicate 2026-09-02 20:23:21 -07:00
Teknium 38534b92a1 refactor(agent/relay_runtime): plugin configuration acquire = preflight-or-activate; drop _remember; reflow boolean spans 2026-09-02 20:07:59 -07:00
Teknium 9d0586c275 refactor(agent/relay_runtime): resolve_execution_context reuses relay_instrumentation_enabled; tighter turn unwind/profile key 2026-09-02 19:59:48 -07:00
Teknium c83de74cb7 refactor(agent/relay): pack call/signature spans to 120 cols 2026-09-02 19:55:45 -07:00
Teknium bc224e648d refactor(agent/relay): RelayRuntime._lookup for locked registry reads; first-close-error helper; subagent parent predicate leans on active_turn 2026-09-02 19:52:58 -07:00
Teknium ff4d896e50 refactor(agent/relay,transports): fold docstring whitespace (text unchanged) 2026-09-02 19:49:06 -07:00
Teknium b162ef97b1 refactor(agent/relay_runtime): contextlib.suppress for swallow-only excepts; shared timeout message; daemon-thread outcome box 2026-09-02 19:46:01 -07:00
Teknium 46f696c48b refactor(agent/relay,transports): anthropic build_kwargs default table + thinking-type set; compact accumulator/logical-scope plumbing 2026-09-02 19:43:35 -07:00
Teknium 552c5b62ca refactor(agent/relay_runtime): remove dead emit_mark (module fn + RelayRuntime method; only a test asserted its False path) 2026-09-02 19:31:18 -07:00
Teknium e58175139d refactor(agent/relay): drop _safe in favour of _warn_on_error/suppress; pack spans to 110 cols; strip intra-function separator blanks 2026-09-02 19:24:48 -07:00
Teknium 2ed4135398 refactor(agent/relay_runtime): _warn_on_error fail-open helper for telemetry paths; conversation/compaction bodies lifted out of try blocks 2026-09-02 19:12:37 -07:00
Teknium be3a659b14 refactor(agent/relay): compact relay/transport docstrings and comments (keep every invariant); reflow boolean spans; drop no-op helpers 2026-09-02 18:53:45 -07:00
Teknium a560fd840c refactor(agent/relay): fold execute/execute_async invoke into _ManagedAttempt; stream() = ManagedLlmStream; codec tool normalizer table; plugin acquire preflight/activate split; pack call/signature spans 2026-09-02 18:35:48 -07:00
Teknium 00cd12f7e4 refactor(agent/relay): fold relay_llm attempt scaffolding into _ManagedAttempt; dedupe runtime session/scope paths; drop dead relay_runtime module-level shims 2026-09-02 13:29:48 -07:00
Alex Fournier 31402f630b fix(relay): complete native plugin cutover
Signed-off-by: Alex Fournier <afournier@nvidia.com>
2026-08-19 08:52:03 -07:00
Bryan Bednarski 93bec27f66 fix(relay): preserve shutdown scope cleanup
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:03 -07:00
Bryan Bednarski 6ec2c0ba8b fix(relay): define process-wide profile policy
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:03 -07:00
Bryan Bednarski 3fad83df31 fix(relay): guard native plugin ownership cutover
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:03 -07:00
Bryan Bednarski ad9fb060c5 fix(relay): clarify opt-in plugin layering
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:02 -07:00
Bryan Bednarski c86fd74e28 refactor(relay): require 0.7 plugin APIs
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:02 -07:00
Bryan Bednarski 4e34dfc932 refactor(relay): use canonical dynamic plugin config
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:02 -07:00
Bryan Bednarski 0b7288ebcb fix(relay): require explicit plugin configuration
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:01 -07:00
Bryan Bednarski e7da915f67 fix(relay): defer subscriber flush to shutdown
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:01 -07:00
Bryan Bednarski 918dd8a265 feat(relay): load standard dynamic plugin records
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:01 -07:00
Bryan Bednarski 88300217c2 feat(relay): activate configured dynamic plugins
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:01 -07:00
Bryan Bednarski c4ae7f7a3b feat(relay): initialize discovered plugin components
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-19 08:52:01 -07:00
rainbowgits aba4934274 fix(agent): omit unsupported metadata on Relay scope.pop
Older nemo-relay bindings reject metadata= on scope.pop, which aborted
turn finalization and left scopes open. Filter kwargs to what the live
binding accepts so close paths can complete.
2026-08-15 00:33:01 -07:00
Teknium 0b988b0118 fix(relay): keep orphan drain bounded by the scope-op timeout
The salvaged _close_scope_handle replaced direct scope.pop calls that
were bounded by _SCOPE_OP_TIMEOUT with an unbounded run_in_session
callback, regressing the bounded-finalization contract (CI:
tests/agent/test_relay_runtime_bounded_scope_ops.py — end_turn /
close_session / finish_logical_calls hung when the native pop wedged).
Pass timeout=_SCOPE_OP_TIMEOUT so the whole drain+close costs at most
one span and never blocks turn or session completion.
2026-08-14 22:11:30 -07:00
Teknium 3537ef9d01 fix(streaming): widen #81521 interrupt join to sibling paths and use version-correct Relay top accessor
Follow-up to HexLab98's salvaged commits:

- Apply the same bounded worker join before raising InterruptedError at
  the two sibling interrupt sites that share the raise-without-join
  shape: the non-streaming API poll loop and the Bedrock streaming poll
  loop. Both workers run Relay-managed physical attempts, so raising
  immediately allowed turn teardown to race a still-open physical scope
  exactly as in the streaming path.

- Address the #81601 review finding (egilewski): the pinned nemo-relay
  binding's get_scope_stack() returns a native ScopeStack object which
  scope.pop rejects with TypeError, so the orphan drain never drained
  under the real binding. current_top() now prefers the version-correct
  scope.get_handle() accessor and falls back to the old list-unwrap for
  fake/legacy shapes. Handle comparisons go through same_handle(),
  comparing by uuid, because native ScopeHandle instances do not
  implement value equality.

- Add a real-binding regression test that reproduces the orphaned-scope
  session close against the pinned native wheel (skips where the native
  binding is unavailable), alongside the existing fake-based coverage.
2026-08-14 22:11:30 -07:00
HexLab98 0c9d8ab0ce fix(streaming): join stream worker and drain Relay scopes on interrupt
Empty-stream stalls that trip interrupt were raising InterruptedError
before the stream worker closed its physical LLM scope, corrupting the
Relay LIFO stack and cascading into a CLI EIO redraw storm (#81521).
2026-08-14 22:11:30 -07:00
Teknium 85e08110fb fix(relay): defer rotating-compaction session close while a turn is live
notify_session_compacted closed the old session scope immediately on a
legacy rotating compaction. A compaction can complete while a turn is
still live on the old session; closing then pops the session scope under
the live turn scope, violating the stack's LIFO order — the exact
invariant the rest of the segmentation feature protects.

Now: when the old session has an active turn, set close_pending instead;
that turn's end_turn consumes the flag after its own turn scope pops and
it unregisters from the active-turn table. Sabotage-verified: the new
test fails without the fix.
2026-08-13 10:45:15 -07:00
Victor Kyriazakos 11c74beffa feat(relay): session-span segmentation for continuous sessions
Continuous gateway sessions keep the Relay session scope open for days;
close-driven export means the session root span and out-of-turn marks
never export until /new or idle-end, and a crash loses the open segment
entirely.

Opt-in segmentation (both defaults OFF => scope lifecycle byte-identical
to today):

  gateway.telemetry.session_segments.on_compaction: false
  gateway.telemetry.session_segments.max_turns: 0

Rotation closes the current session scope and pushes the next segment
(same session_id attribute, plus hermes.session.segment=N and
segment_reason=compaction|max_turns) ONLY at a turn boundary in
begin_turn — never mid-turn (scope stack is LIFO). Compaction completion
just flags rotate_pending (observer semantics, nothing on the compaction
critical path); legacy rotating compaction closes the orphaned old
session scope so its segment exports. Both native calls ride the
existing bounded scope-op executor: a wedged rotation costs one segment
span, never the agent. Segment bookkeeping advances even on native
failure so a degraded rotation cannot retry every turn.
2026-08-13 10:45:15 -07:00
Victor Kyriazakos 24be384bb8 fix(relay): bound the interpreter-shutdown fallback lane; unwedge test fakes at teardown
CI caught the file hanging AFTER '6 passed in 4.32s' until the runner's
300s SIGKILL. Two defects, same class the PR fixes:

1. The executor-refused (interpreter shutdown) fallback ran the native
   call UNBOUNDED on the calling thread — a wedged pipeline would block
   process exit forever. Now runs on a bounded daemon exit-thread with
   the same timeout/abandon semantics as the executor lane.
2. The wedge tests left daemon workers parked on Event.wait() and live
   sessions registered on the atexit shutdown hook; exit re-ran the
   wedged pops (bounded, 10s each) and the per-file runner timed out.
   Autouse teardown now releases every wedge and drains each runtime.

Canonical runner: 4.4s (was 300s file-timeout kill). Bare pytest was a
false green for this class — it exits before atexit replay cost shows.
2026-08-12 19:19:54 -07:00
Victor Kyriazakos d607f0cafb fix(relay): bound native scope lifecycle operations so a wedged pipeline cannot block the agent
The NeMo Relay native binding's scope.pop/push are synchronous and
unbounded ('returns after the scope is closed successfully'). When the
native pipeline cannot make progress, the session coordinator's turn and
session finalization block forever inside run_conversation: delegated
children finish their turns but never return, and delegation batches die
on the stall watchdog. Proven live 2026-08-10 on the staging fleet — a
falsification probe (plugin disabled, identical config) completed the
same delegation batch that wedged with the plugin active.

Bound every scope lifecycle operation that gates turn/session completion
(session push, turn push, turn pop, logical-LLM pops, session pop,
subscriber flush) by running the native call on a shared
DaemonThreadPoolExecutor and honoring a 10s result timeout. On breach a
TimeoutError propagates into each call site's existing exception
handling — warn, retain the unclosed-prefix diagnostics, continue — so
the worst case is one lost span, never a blocked agent. timeout=None
preserves byte-identical synchronous behavior for all other callers, and
interpreter-shutdown paths fall back to the synchronous call so the
atexit flush still exports.

Observability must never block the product.
2026-08-12 19:19:54 -07:00
Teknium 961f7481a7 fix(relay): bypass managed execution for nested calls inside managed callbacks
The native Relay pipeline binds its Futures to the event loop that
entered run_in_session_async. While a managed tool callback executes,
that loop is blocked until the callback returns — so a nested managed
relay call made from inside the callback (vision_analyze's auxiliary
LLM call on a worker-thread loop) awaits a Future that can never
complete: 'RuntimeError: Future attached to a different loop', or a
deadlock, plus 'Event loop is closed' at shutdown when the orphaned
future completes late. (#77244)

Fix: managed_callback_guard, a ContextVar depth marker set around every
Hermes callback the relay adapters hand to the native pipeline
(relay_tools.execute invoke, relay_llm execute/execute_async invoke,
ManagedLlmStream run_callback). resolve_execution_context returns the
no-relay triple while the marker is set, so nested calls run unmanaged.
The marker propagates through contextvars.copy_context() into the
worker threads tools use for their internal async work.

Top-level turn LLM calls and tool wraps stay fully managed — verified
live: vision_analyze works under active shared metrics while the main
turn still records managed llm.execute events.

Alternative fixes considered and rejected: removing
retain_managed_execution (kills the shared-metrics managed pipeline)
and gating on main-thread identity (managed tool wraps legitimately
run on the run_agent thread, so that gate disables relay everywhere).
2026-08-08 23:46:17 -07:00
Bryan Bednarski e1caa611bf fix(relay): preserve skipped turn context
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-03 13:43:17 -06:00
Bryan Bednarski a2a08fe147 fix(relay): gate skipped turn metrics
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-03 13:43:17 -06:00
Bryan Bednarski 9a9b670e29 fix(relay): avoid concurrent turn scope corruption
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-03 13:43:16 -06:00
Alex Fournier 3b55419cb1 fix(relay): close logical calls in stack order
Signed-off-by: Alex Fournier <afournier@nvidia.com>
2026-07-28 10:19:53 -07:00
Alex Fournier 14bed44c8c Reapply "feat(observability): integrate NeMo Relay runtime and shared metrics"
Signed-off-by: Alex Fournier <afournier@nvidia.com>
2026-07-27 21:10:51 -07:00