Skip to main content

tt_shared/
context.rs

1//! RequestContext carries authenticated identity, trace IDs, and credentials
2//! through the request lifecycle. Adapters are stateless — every call gets a
3//! fresh context.
4
5use std::time::Duration;
6
7use uuid::Uuid;
8
9/// Subscription tier for the calling organisation, as surfaced by the cloud
10/// tier-resolution layer.
11///
12/// The tier drives cache TTL selection per spec §8.4 (24h / 7d / 30d bands).
13/// The `tier` field on [`crate::context::RequestContext`] and on
14/// `tt_auth::ApiKeyContext` is `Option<CallerTier>`: when `None`, the gateway
15/// falls back to the conservative 24h default. The cloud will inject the real
16/// tier once `rv-tier-limits-enforcement` is wired; until then all requests
17/// run as if Free.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum CallerTier {
20    /// Free tier — 24h cache TTL (spec §8.4).
21    Free,
22    /// Pro tier — 7d cache TTL (spec §8.4).
23    Pro,
24    /// Team tier — 7d cache TTL (spec §8.4, same band as Pro).
25    Team,
26    /// Scale tier — 30d cache TTL (spec §8.4).
27    Scale,
28}
29
30impl CallerTier {
31    /// Cache TTL in seconds for this tier, per spec §8.4.
32    ///
33    /// | Tier        | TTL    |
34    /// | ----------- | ------ |
35    /// | Free        | 24h    |
36    /// | Pro / Team  | 7d     |
37    /// | Scale       | 30d    |
38    #[must_use]
39    pub fn ttl_secs(self) -> u64 {
40        match self {
41            CallerTier::Free => 24 * 60 * 60,
42            CallerTier::Pro | CallerTier::Team => 7 * 24 * 60 * 60,
43            CallerTier::Scale => 30 * 24 * 60 * 60,
44        }
45    }
46}
47
48#[derive(Debug, Clone)]
49pub struct RequestContext {
50    pub trace_id: Uuid,
51    pub org_id: Uuid,
52    pub api_key_id: Uuid,
53    pub credentials: ProviderCredentials,
54    /// Free-form cost-attribution tag from `X-TokenTrimmer-Tag` header.
55    pub tag: Option<String>,
56    /// Deadline for the entire request. Adapters should respect this.
57    pub deadline: Option<Duration>,
58}
59
60#[derive(Debug, Clone)]
61pub struct ProviderCredentials {
62    pub api_key: SecretString,
63    /// Self-hosted endpoint override (used for Ollama, vLLM, LM Studio, OpenRouter, etc.).
64    pub base_url: Option<String>,
65    pub extra_headers: Vec<(String, String)>,
66}
67
68/// String wrapper whose `Debug` impl never prints the secret.
69#[derive(Clone)]
70pub struct SecretString(String);
71
72impl SecretString {
73    pub fn new(s: impl Into<String>) -> Self {
74        Self(s.into())
75    }
76
77    pub fn expose(&self) -> &str {
78        &self.0
79    }
80}
81
82impl std::fmt::Debug for SecretString {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        write!(f, "SecretString(****)")
85    }
86}