| Crates.io | praxis-observability |
| lib.rs | praxis-observability |
| version | 0.2.0 |
| created_at | 2025-11-11 00:40:41.584898+00 |
| updated_at | 2025-11-11 00:40:41.584898+00 |
| description | Observability and tracing abstraction for Praxis AI agents |
| homepage | |
| repository | https://github.com/your-org/praxis |
| max_upload_size | |
| id | 1926475 |
| size | 101,860 |
Observability and tracing abstraction layer for Praxis AI agent framework.
praxis-observability provides a trait-based abstraction for observing and tracing AI agent executions. It enables you to track LLM calls, tool executions, and overall agent behavior across different observability backends.
Add to your Cargo.toml:
[dependencies]
praxis-observability = "0.1"
use praxis_observability::LangfuseObserver;
// Initialize observer
let observer = LangfuseObserver::new(
"your-public-key",
"your-secret-key",
"https://cloud.langfuse.com",
)?;
// Use with Praxis graph
let graph = Graph::builder()
.llm_client(llm_client)
.mcp_executor(mcp_executor)
.with_observer(Arc::new(observer))
.build()?;
use async_trait::async_trait;
use praxis_observability::{Observer, NodeObservation};
struct MyCustomObserver {
// Your fields
}
#[async_trait]
impl Observer for MyCustomObserver {
async fn trace_start(&self, run_id: String, conversation_id: String) -> anyhow::Result<()> {
// Initialize trace
Ok(())
}
async fn trace_llm_node(&self, observation: NodeObservation) -> anyhow::Result<()> {
// Trace LLM execution
Ok(())
}
async fn trace_tool_node(&self, observation: NodeObservation) -> anyhow::Result<()> {
// Trace tool execution
Ok(())
}
async fn trace_end(&self, run_id: String, status: String, total_duration_ms: u64) -> anyhow::Result<()> {
// Finalize trace
Ok(())
}
}
LANGFUSE_PUBLIC_KEY=pk-xxx
LANGFUSE_SECRET_KEY=sk-xxx
LANGFUSE_HOST=https://cloud.langfuse.com
[observability]
enabled = true
provider = "langfuse"
[observability.langfuse]
public_key = "${LANGFUSE_PUBLIC_KEY}"
secret_key = "${LANGFUSE_SECRET_KEY}"
host = "https://cloud.langfuse.com"
When using Langfuse, traces are organized as:
Trace (per graph execution run)
├── Span: LLM Node #1
│ ├── Input: Messages sent to LLM
│ └── Output: AI response (text or tool calls)
├── Span: Tool Node #1
│ ├── Input: Tool calls
│ └── Output: Tool results
├── Span: LLM Node #2
│ └── ...
└── Status: Success/Error
The Observer trait defines the contract for tracing:
All tracing operations are executed asynchronously in background tasks:
if let Some(obs) = &observer {
let obs_clone = Arc::clone(obs);
tokio::spawn(async move {
let _ = obs_clone.trace_llm_node(observation).await;
});
}
This ensures tracing never blocks the main execution flow.
Observability is triggered immediately after each node exits in the graph execution loop:
node.execute() completesThis design ensures:
The Langfuse implementation uses the batch ingestion API for optimal performance:
// Each event is wrapped in a batch
{
"batch": [{
"id": "event-uuid",
"timestamp": "2025-11-10T21:00:00Z",
"type": "generation-create",
"body": {
"id": "span-uuid",
"traceId": "trace-uuid",
"input": [{"role": "user", "content": "..."}],
"output": {"role": "assistant", "content": "..."},
// ... other fields
}
}]
}
Event Types:
trace-create: Creates/updates a tracegeneration-create: Records an LLM generation (for LLM nodes)span-create: Records a span (for Tool nodes)Benefits:
Input Format (complete message history):
[
{"role": "user", "content": "What is the weather in SF?"},
{"role": "assistant", "content": "Let me check that for you."},
{"role": "user", "content": "Thanks!"}
]
Output Format (new AI message):
{
"role": "assistant",
"content": "The weather in SF is 72°F and sunny."
}
Additional Fields:
gpt-4o-mini)Input Format (tool calls):
{
"tool_calls": [
{
"id": "call_abc123",
"name": "get_weather",
"arguments": "{\"location\": \"San Francisco\"}"
}
]
}
Output Format (tool results):
{
"tool_results": [
{
"tool_call_id": "call_abc123",
"name": "get_weather",
"content": "{\"temperature\": 72, \"condition\": \"sunny\"}",
"status": "success"
}
]
}
Additional Fields:
Arc for sharing across async tasksSee the examples/ directory for:
simple_trace.rs: Basic observer usagecustom_observer.rs: Implementing a custom observermetadata.rs: Adding custom metadata to tracesMIT