Skip to main content

systemprompt_identifiers/
gateway_conversation.rs

1//! Deterministic gateway conversation cache key.
2//!
3//! Distinct from [`crate::ContextId`] (a user-owned agent context, UUID v4)
4//! and [`crate::ProviderRequestId`] (an opaque upstream provider trace).
5//! `GatewayConversationId` is **always** `ctx_<16 lowercase hex>` derived
6//! from an FNV-1a hash of a conversation prefix, so the same opening turn
7//! maps to the same id across processes, hosts, and Rust versions.
8
9use crate::error::IdValidationError;
10
11const PREFIX: &str = "ctx_";
12
13fn validate(value: &str) -> Result<(), IdValidationError> {
14    if value.len() != PREFIX.len() + 16 {
15        return Err(IdValidationError::invalid(
16            "GatewayConversationId",
17            "must be 'ctx_' followed by 16 hex characters",
18        ));
19    }
20    if !value.starts_with(PREFIX) {
21        return Err(IdValidationError::invalid(
22            "GatewayConversationId",
23            "missing 'ctx_' prefix",
24        ));
25    }
26    if !value[PREFIX.len()..]
27        .bytes()
28        .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
29    {
30        return Err(IdValidationError::invalid(
31            "GatewayConversationId",
32            "suffix must be lowercase hex",
33        ));
34    }
35    Ok(())
36}
37
38crate::define_id!(GatewayConversationId, validated, schema, validate);
39
40impl GatewayConversationId {
41    /// Mint a deterministic id from a 64-bit prefix hash.
42    ///
43    /// The hash itself is computed by `systemprompt_models::gateway_hash`
44    /// helpers; see `conversation_prefix_hash`.
45    #[must_use]
46    pub fn from_prefix_hash(hash: u64) -> Self {
47        Self::new(format!("{PREFIX}{hash:016x}"))
48    }
49}