rig-tap
Backend-agnostic observability event schema and taps for Rig
agents. Defines a stable, versioned ObservabilityEvent stream emitted via
tracing so any consumer (Phoenix, Langfuse, a custom dashboard, a log
shipper) can subscribe without crate-specific glue.
This crate is the contract — it does not ship a UI. See the schema and tap points below.
What it provides
ObservabilityEvent+EventKind— the wire schema. Ten event kinds covering the prompt / tool / context / memory lifecycle.TelemetryHook<M>— implementsrig::agent::PromptHook<M>and emitsprompt.*andtool.*events from the fivePromptHooklifecycle methods.DispatchObserveHook(featurecompose) — implementsrig_compose::ToolDispatchHookand emitstool.invoked/tool.completed/tool.terminatedfrom the kernel-direct dispatch path.ObservedMemory<M>— decorator that wraps anyrig::memory::ConversationMemoryand emitscontext.sampledon everyload.ChainedHook<A, B>— compose twoPromptHooks on a single agent (e.g. pairMemvidPersistHookwithTelemetryHook).
Quick start
Wire a tracing subscriber that keeps the dedicated rig_tap target, then
attach the hooks at the lifecycle boundary you want to observe:
use ;
use ;
#
For kernel-direct tool dispatch, enable the compose feature and register
DispatchObserveHook with dispatch_tool_invocations_with_hooks. For
deterministic tests or examples, enable subscriber and use CapturingLayer to
collect typed ObservabilityEvent values in-process.
Architecture
rig-tap acts as a tap, listening to various hooks in the Rig lifecycle and writing uniform JSON payloads into the tracing ecosystem under a dedicated rig_tap target.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ Host Agent │──────►│ TelemetryHook ├──────►│ │
│ (rig::pipeline) │ │ │ │ │
└─────────────────┘ └─────────────────┘ │ │
│ │
┌─────────────────┐ ┌─────────────────┐ │ tracing::info! │
│ │ │ │ │ (target: │
│ Host Runtime │──────►│DispatchObserve..├──────►│ "rig_tap") │
│ (rig_compose) │ │ │ │ │
└─────────────────┘ └─────────────────┘ │ │
│ │
┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ │ │ │
│ ConversationMem ├──────►│ ObservedMemory ├──────►│ │
│ (rig::memory) │ │ │ └────────┬────────┘
└─────────────────┘ └─────────────────┘ │
▼
┌─────────────────┐
│ │
│ Telemetry Sink │
│ (OTEL/Langfuse/ │
│ Phoenix/etc.) │
└─────────────────┘
Wire format
All events are flat JSON serialized via tracing::info!(target: "rig_tap", event = %json):
tick is a monotonic per-process counter so consumers can order events
without clock skew. version is the schema version (currently 1).
Event kinds (v1)
kind |
Producer |
|---|---|
prompt.started |
TelemetryHook::on_completion_call |
prompt.completed |
TelemetryHook::on_completion_response |
tool.invoked |
TelemetryHook::on_tool_call / DispatchObserveHook |
tool.completed |
TelemetryHook::on_tool_result / DispatchObserveHook |
tool.skipped |
Producer crate (kernel hook with Skip semantics) |
tool.terminated |
DispatchObserveHook (kernel gate / runtime error) |
context.sampled |
ObservedMemory::load |
context.compacted |
Producer crate (e.g. rig-memvid) |
memory.demoted |
Producer crate |
memory.frame_written |
Producer crate |
prompt.*, tool.* (via TelemetryHook/DispatchObserveHook), and
context.sampled are emitted by this crate. The remaining tool.* and
memory.* / context.compacted events are emitted by producer crates
(e.g. rig-memvid) using the same schema — construct an EventKind
variant and pass it through ObservabilityEvent::new(conversation_id, kind)
or the emit_kind helper.
Consumer example
use ;
A consumer wanting typed events can attach a custom tracing_subscriber::Layer
that parses the event field via serde_json::from_str::<ObservabilityEvent>.
Coexistence with rig-core::telemetry
This crate is additive to Rig's existing GenAI span conventions
(gen_ai.input.messages, gen_ai.usage.input_tokens, etc.). Consumers using
tracing-opentelemetry for Phoenix / Langfuse keep their existing setup;
rig_tap events live under a separate target and can be filtered
independently.
Status
Crate version: 0.1.0. Rust edition: 2024. MSRV: 1.89. The library is
runtime-agnostic and emits through tracing; production consumers should use a
non-blocking tracing sink when exporting events off-host. The optional
subscriber feature is for tests/examples, while the optional compose
feature adds the rig-compose dispatch tap.
License
Licensed under either of Apache-2.0 or MIT at your option.