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//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use crate::error::IdValidationError;
13
14const PREFIX: &str = "ctx_";
15
16fn validate(value: &str) -> Result<(), IdValidationError> {
17    if value.len() != PREFIX.len() + 16 {
18        return Err(IdValidationError::invalid(
19            "GatewayConversationId",
20            "must be 'ctx_' followed by 16 hex characters",
21        ));
22    }
23    if !value.starts_with(PREFIX) {
24        return Err(IdValidationError::invalid(
25            "GatewayConversationId",
26            "missing 'ctx_' prefix",
27        ));
28    }
29    if !value[PREFIX.len()..]
30        .bytes()
31        .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
32    {
33        return Err(IdValidationError::invalid(
34            "GatewayConversationId",
35            "suffix must be lowercase hex",
36        ));
37    }
38    Ok(())
39}
40
41crate::define_id!(GatewayConversationId, validated, schema, validate);
42
43impl GatewayConversationId {
44    #[must_use]
45    pub fn from_prefix_hash(hash: u64) -> Self {
46        Self::new(format!("{PREFIX}{hash:016x}"))
47    }
48}