bitrouter_core/observe.rs
1//! Handler-level observation callback for request lifecycle events.
2//!
3//! [`ObserveCallback`] fires from the API handler layer with full request
4//! context (route, provider, model, account, latency, usage/error).
5//! This is complementary to [`GenerationHook`](crate::hooks::GenerationHook)
6//! which fires at the model layer with no request context.
7
8use std::future::Future;
9use std::pin::Pin;
10
11use crate::auth::claims::{BudgetRange, BudgetScope};
12use crate::errors::BitrouterError;
13use crate::models::language::usage::LanguageModelUsage;
14
15/// Authenticated caller context extracted from JWT claims.
16///
17/// Carries the account identifier and any claim-based permissions (budget,
18/// model allowlist) through the API handler layer. Constructed in the auth
19/// filter and consumed by observers and (in the future) enforcement middleware.
20#[derive(Debug, Clone, Default)]
21pub struct CallerContext {
22 /// The account that made the request, if authentication is enabled.
23 pub account_id: Option<String>,
24 /// Optional model-name patterns this caller may access.
25 pub models: Option<Vec<String>>,
26 /// Optional tool-name patterns this caller may access.
27 pub tools: Option<Vec<String>>,
28 /// Budget limit in micro USD.
29 pub budget: Option<u64>,
30 /// Whether the budget applies per-session or per-account.
31 pub budget_scope: Option<BudgetScope>,
32 /// The range over which the budget is measured.
33 pub budget_range: Option<BudgetRange>,
34 /// CAIP-2 chain identifier from JWT claims (e.g. `"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"`).
35 pub chain: Option<String>,
36}
37
38/// Context about the request available to observation callbacks.
39#[derive(Debug, Clone)]
40pub struct RequestContext {
41 /// The incoming model name (route key).
42 pub route: String,
43 /// The resolved provider name.
44 pub provider: String,
45 /// The resolved model ID sent to the provider.
46 pub model: String,
47 /// Authenticated caller context (account ID, budget claims).
48 pub caller: CallerContext,
49 /// End-to-end request latency in milliseconds.
50 pub latency_ms: u64,
51}
52
53/// Event emitted when a request completes successfully.
54#[derive(Debug, Clone)]
55pub struct RequestSuccessEvent {
56 /// Request context.
57 pub ctx: RequestContext,
58 /// Token usage reported by the model.
59 pub usage: LanguageModelUsage,
60}
61
62/// Event emitted when a request fails.
63#[derive(Debug, Clone)]
64pub struct RequestFailureEvent {
65 /// Request context.
66 pub ctx: RequestContext,
67 /// The error that caused the failure.
68 pub error: BitrouterError,
69}
70
71/// Callback trait for observing completed API requests.
72///
73/// Implementations receive rich, typed events with full request context
74/// and can perform side effects such as spend logging, metrics aggregation,
75/// or alerting. Errors in callbacks are expected to be handled internally
76/// (e.g. logged and swallowed) — they must never break request serving.
77///
78/// Methods return boxed futures for dyn-compatibility, allowing multiple
79/// observers to be composed via [`CompositeObserver`](see bitrouter-observe).
80pub trait ObserveCallback: Send + Sync {
81 /// Called after a request completes successfully.
82 fn on_request_success(
83 &self,
84 event: RequestSuccessEvent,
85 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
86
87 /// Called after a request fails.
88 fn on_request_failure(
89 &self,
90 event: RequestFailureEvent,
91 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
92}
93
94// ── MCP tool call observation ────────────────────────────────────────
95
96/// Context about a tool call request available to observation callbacks.
97#[derive(Debug, Clone)]
98pub struct ToolRequestContext {
99 /// The upstream MCP server name.
100 pub server: String,
101 /// The un-namespaced tool name.
102 pub tool: String,
103 /// Authenticated caller context (account ID, tool allowlist, budget claims).
104 pub caller: CallerContext,
105 /// End-to-end tool call latency in milliseconds.
106 pub latency_ms: u64,
107}
108
109/// Event emitted when an MCP tool call completes successfully.
110#[derive(Debug, Clone)]
111pub struct ToolCallSuccessEvent {
112 /// Tool call context.
113 pub ctx: ToolRequestContext,
114}
115
116/// Event emitted when an MCP tool call fails.
117#[derive(Debug, Clone)]
118pub struct ToolCallFailureEvent {
119 /// Tool call context.
120 pub ctx: ToolRequestContext,
121 /// Error description.
122 pub error: String,
123}
124
125/// Callback trait for observing completed MCP tool calls.
126///
127/// Parallel to [`ObserveCallback`] but for tool invocations rather than
128/// LLM requests. Implementations persist tool spend logs or emit metrics.
129pub trait ToolObserveCallback: Send + Sync {
130 /// Called after a tool call completes successfully.
131 fn on_tool_call_success(
132 &self,
133 event: ToolCallSuccessEvent,
134 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
135
136 /// Called after a tool call fails.
137 fn on_tool_call_failure(
138 &self,
139 event: ToolCallFailureEvent,
140 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
141}
142
143// ── A2A agent call observation ───────────────────────────────────────
144
145/// Context about an agent call request available to observation callbacks.
146#[derive(Debug, Clone)]
147pub struct AgentRequestContext {
148 /// The upstream agent name.
149 pub agent: String,
150 /// The A2A method dispatched (e.g. `"message/send"`, `"tasks/get"`).
151 pub method: String,
152 /// Authenticated caller context.
153 pub caller: CallerContext,
154 /// End-to-end agent call latency in milliseconds.
155 pub latency_ms: u64,
156}
157
158/// Event emitted when an A2A agent call completes successfully.
159#[derive(Debug, Clone)]
160pub struct AgentCallSuccessEvent {
161 /// Agent call context.
162 pub ctx: AgentRequestContext,
163}
164
165/// Event emitted when an A2A agent call fails.
166#[derive(Debug, Clone)]
167pub struct AgentCallFailureEvent {
168 /// Agent call context.
169 pub ctx: AgentRequestContext,
170 /// Error description.
171 pub error: String,
172}
173
174/// Callback trait for observing completed A2A agent calls.
175///
176/// Parallel to [`ToolObserveCallback`] but for agent invocations.
177/// Implementations persist agent spend logs or emit metrics.
178pub trait AgentObserveCallback: Send + Sync {
179 /// Called after an agent call completes successfully.
180 fn on_agent_call_success(
181 &self,
182 event: AgentCallSuccessEvent,
183 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
184
185 /// Called after an agent call fails.
186 fn on_agent_call_failure(
187 &self,
188 event: AgentCallFailureEvent,
189 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
190}