Enable Tracing

Synwire supports OpenTelemetry-based tracing via the tracing feature flag on synwire-core.

Enable the feature

[dependencies]
synwire-core = { version = "0.1", features = ["tracing"] }

Setup tracing subscriber

use tracing_subscriber::prelude::*;

fn init_tracing() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .init();
}

Callbacks for custom observability

Implement CallbackHandler to receive events during execution:

use synwire_core::callbacks::CallbackHandler;
use synwire_core::BoxFuture;

struct MetricsCallback;

impl CallbackHandler for MetricsCallback {
    fn on_llm_start<'a>(
        &'a self,
        model_type: &'a str,
        messages: &'a [synwire_core::messages::Message],
    ) -> BoxFuture<'a, ()> {
        Box::pin(async move {
            // Record metrics, emit spans, etc.
        })
    }

    fn on_llm_end<'a>(
        &'a self,
        response: &'a serde_json::Value,
    ) -> BoxFuture<'a, ()> {
        Box::pin(async move {
            // Record latency, token usage, etc.
        })
    }
}

Attaching callbacks to invocations

Pass callbacks via RunnableConfig:

use synwire_core::runnables::RunnableConfig;

let config = RunnableConfig {
    callbacks: vec![Box::new(MetricsCallback)],
    ..Default::default()
};

let result = model.invoke(&messages, Some(&config)).await?;

Filtering callback events

Override the ignore_* methods to skip categories:

impl CallbackHandler for MetricsCallback {
    fn ignore_tool(&self) -> bool { true }  // Skip tool events
    fn ignore_llm(&self) -> bool { false }  // Keep LLM events
}