Module: Protege::IntrospectionBroadcaster
- Extended by:
- IntrospectionBroadcaster
- Included in:
- IntrospectionBroadcaster
- Defined in:
- app/subscribers/protege/introspection_broadcaster.rb
Overview
Drives the console's introspection panel from inference events — an app-level subscriber for the
console experience, not part of the engine's inference core. A peer of Subscribers::HookDispatcher
and Subscribers::Tracing, installed once at boot.
It subscribes to Protege events and translates streaming + tool-call notifications into Turbo Stream
broadcasts, building the panel's live activity feed: one chronological stream where the model's
reasoning tokens and its tool calls are interleaved in the order they happen. Installed via the
install! contract (see the engine initializer) and holds process-local state for the lifetime of
the process; reset! detaches its subscriptions and clears that state.
What each event does (all broadcast to the thread's Turbo channel, target dom_id(thread, :activity)
— the feed container in _introspection.html.slim):
InferenceChunkEvent— stream the token into the run's current reasoning block, opening a fresh block after each tool call so reasoning is grouped into spaced paragraphs. Interleaving is automatic: every append lands at the feed's end, so reasoning blocks and cards stay in chronological order.ToolCallStartedEvent— append a pending tool card the instant the model requests the call.ToolCallCompletedEvent/ToolCallFailedEvent— replace that card in place with the outcome (a response preview, or the error message).
Routing. Only InferenceChunkEvent carries the thread; the tool-call events carry just the
correlation_id that Protege::Event.emit injects into every event. So the broadcaster keeps a
correlation_id → thread map, populated from InferenceStartedEvent (which carries the message, hence
its thread) and cleared on +InferenceCompletedEvent+/+InferenceFailedEvent+. This is the single stream-
routing convention — no emit_* signature carries the thread just for the panel. @mutex guards
the map because callbacks may run concurrently across threads. Runs with no thread (scheduled
+ResponsibilityRun+s) never populate the map, so their tool events broadcast nothing.
The feed is live-only by design: it is not rehydrated from persistence, so a page reload shows an empty feed until the next run streams in.
Instance Method Summary collapse
-
#install! ⇒ void
Wire the event listeners — the subscriber's
install!contract. -
#reset! ⇒ void
Detach every subscription and drop all per-run state — the subscriber's
reset!contract, for boot re-wiring and tests.
Instance Method Details
#install! ⇒ void
This method returns an undefined value.
Wire the event listeners — the subscriber's install! contract. Idempotent: retains the
subscription handles and no-ops until #reset! runs, so a repeat call never double-subscribes.
Called once at boot from the engine's after_initialize.
59 60 61 62 63 64 65 66 67 68 |
# File 'app/subscribers/protege/introspection_broadcaster.rb', line 59 def install! return unless @handles.empty? @handles = [ install_start_listener, install_chunk_listener, *install_tool_listeners, *install_completion_listeners ] end |
#reset! ⇒ void
This method returns an undefined value.
Detach every subscription and drop all per-run state — the subscriber's reset! contract, for
boot re-wiring and tests.
74 75 76 77 78 79 80 81 82 |
# File 'app/subscribers/protege/introspection_broadcaster.rb', line 74 def reset! @handles.each { |handle| ActiveSupport::Notifications.unsubscribe(handle) } @handles = [] @mutex.synchronize do @threads.clear @reasoning.clear @reasoning_seq.clear end end |