rig_tap/lib.rs
1//! Emits uniform telemetry for [Rig](https://crates.io/crates/rig-core) agents
2//! and companion crates.
3//!
4//! `rig-tap` defines a stable, versioned [`ObservabilityEvent`] stream for
5//! prompt, tool, context, memory, and dispatch lifecycle events. Producer
6//! crates can use the same event vocabulary whether the event came from a Rig
7//! agent hook, `rig-compose` dispatch, `rig-memvid` memory behavior, or host
8//! application code.
9//!
10//! See the crate [README](../README.md) for the full schema and consumer
11//! recipe. The two consumer-facing types are:
12//!
13//! - [`TelemetryHook`] — implements [`rig::agent::PromptHook`] and emits
14//! `prompt.*` and `tool.*` events.
15//! - [`ObservedMemory`] — wraps any [`rig::memory::ConversationMemory`] and
16//! emits `context.sampled` on every load.
17//!
18//! Plus [`ChainedHook`] for composing two `PromptHook`s on a single agent.
19//!
20//! # Wire format
21//!
22//! All events are emitted as a single `tracing::info!` event on the
23//! [`EVENT_TARGET`] target (`"rig_tap"`). The string field `event` carries the
24//! JSON-encoded [`ObservabilityEvent`], while scalar `rig_tap.*` fields expose
25//! `kind`, `conversation_id`, `version`, `tick`, and `occurred_at_millis` for
26//! OpenTelemetry collector routing and indexing without JSON parsing.
27//!
28//! # Subscriber sizing
29//!
30//! Emission is synchronous: every event runs serde + the registered
31//! layers on the calling task. Per-request hot paths (every prompt, every
32//! tool call, every memory load) call into the tracing dispatcher
33//! directly. For production deployments — especially ones that ship
34//! events off-host — wire a non-blocking sink (e.g.
35//! `tracing_appender::non_blocking` or a bounded channel feeding an
36//! async exporter) so a slow consumer can't backpressure the agent.
37//! In-process counters and the bundled doc-test layer are fine
38//! synchronous.
39//!
40//! # Example
41//!
42//! ```no_run
43//! use rig_tap::{TelemetryHook, ObservedMemory};
44//! use rig::memory::InMemoryConversationMemory;
45//!
46//! # fn build<M: rig::completion::CompletionModel>() -> TelemetryHook<M> {
47//! let memory = ObservedMemory::new(InMemoryConversationMemory::new());
48//! let hook = TelemetryHook::<M>::with_defaults("gpt-4o", "thread-1");
49//! // agent.memory(memory).with_hook(hook)
50//! # hook }
51//! ```
52
53#![deny(missing_docs)]
54
55pub mod extract;
56
57mod chained;
58#[cfg(feature = "compose")]
59mod dispatch;
60pub mod emit;
61mod error;
62mod event;
63mod hook;
64mod observed_memory;
65#[cfg(feature = "subscriber")]
66mod subscriber;
67
68pub use chained::ChainedHook;
69#[cfg(feature = "compose")]
70pub use dispatch::DispatchObserveHook;
71pub use emit::{EVENT_TARGET, build_event, emit, emit_kind, try_emit};
72pub use error::Error;
73pub use event::{
74 EventKind, ObservabilityEvent, PAYLOAD_TRUNCATE_BYTES, SCHEMA_VERSION, ScalarFields,
75 truncate_utf8,
76};
77pub use extract::extract_event;
78pub use hook::{ConversationIdResolver, ModelResolver, TelemetryHook, TelemetryHookConfig};
79pub use observed_memory::ObservedMemory;
80#[cfg(feature = "subscriber")]
81pub use subscriber::CapturingLayer;