Skip to main content

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}