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 /// Budget limit in micro USD.
27 pub budget: Option<u64>,
28 /// Whether the budget applies per-session or per-account.
29 pub budget_scope: Option<BudgetScope>,
30 /// The range over which the budget is measured.
31 pub budget_range: Option<BudgetRange>,
32}
33
34/// Context about the request available to observation callbacks.
35#[derive(Debug, Clone)]
36pub struct RequestContext {
37 /// The incoming model name (route key).
38 pub route: String,
39 /// The resolved provider name.
40 pub provider: String,
41 /// The resolved model ID sent to the provider.
42 pub model: String,
43 /// Authenticated caller context (account ID, budget claims).
44 pub caller: CallerContext,
45 /// End-to-end request latency in milliseconds.
46 pub latency_ms: u64,
47}
48
49/// Event emitted when a request completes successfully.
50#[derive(Debug, Clone)]
51pub struct RequestSuccessEvent {
52 /// Request context.
53 pub ctx: RequestContext,
54 /// Token usage reported by the model.
55 pub usage: LanguageModelUsage,
56}
57
58/// Event emitted when a request fails.
59#[derive(Debug, Clone)]
60pub struct RequestFailureEvent {
61 /// Request context.
62 pub ctx: RequestContext,
63 /// The error that caused the failure.
64 pub error: BitrouterError,
65}
66
67/// Callback trait for observing completed API requests.
68///
69/// Implementations receive rich, typed events with full request context
70/// and can perform side effects such as spend logging, metrics aggregation,
71/// or alerting. Errors in callbacks are expected to be handled internally
72/// (e.g. logged and swallowed) — they must never break request serving.
73///
74/// Methods return boxed futures for dyn-compatibility, allowing multiple
75/// observers to be composed via [`CompositeObserver`](see bitrouter-observe).
76pub trait ObserveCallback: Send + Sync {
77 /// Called after a request completes successfully.
78 fn on_request_success(
79 &self,
80 event: RequestSuccessEvent,
81 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
82
83 /// Called after a request fails.
84 fn on_request_failure(
85 &self,
86 event: RequestFailureEvent,
87 ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
88}