Skip to main content

cognis_trace/
meta.rs

1//! Well-known keys in `RunnableConfig.metadata`. The bridge handler reads
2//! these on the trace root to populate trace-level fields. Users construct
3//! them with the builder helpers below.
4
5use serde_json::{Map, Value};
6
7/// Well-known metadata keys.
8pub mod keys {
9    /// Langfuse-compatible `sessionId`.
10    pub const SESSION_ID: &str = "session_id";
11    /// Langfuse-compatible `userId`.
12    pub const USER_ID: &str = "user_id";
13    /// Release version (git sha or semver tag).
14    pub const RELEASE: &str = "release";
15    /// Trace version.
16    pub const VERSION: &str = "version";
17    /// Environment (production / staging / dev).
18    pub const ENVIRONMENT: &str = "environment";
19    /// Make trace public.
20    pub const PUBLIC: &str = "public";
21}
22
23/// Builder of `RunnableConfig.metadata` entries with strongly-named helpers.
24pub struct TraceMeta;
25
26impl TraceMeta {
27    /// Build a `(key, value)` pair for `session_id`.
28    pub fn session(id: impl Into<String>) -> (&'static str, Value) {
29        (keys::SESSION_ID, Value::String(id.into()))
30    }
31
32    /// Build a `(key, value)` pair for `user_id`.
33    pub fn user(id: impl Into<String>) -> (&'static str, Value) {
34        (keys::USER_ID, Value::String(id.into()))
35    }
36
37    /// Build a `(key, value)` pair for `release`.
38    pub fn release(s: impl Into<String>) -> (&'static str, Value) {
39        (keys::RELEASE, Value::String(s.into()))
40    }
41
42    /// Build a `(key, value)` pair for `environment`.
43    pub fn environment(s: impl Into<String>) -> (&'static str, Value) {
44        (keys::ENVIRONMENT, Value::String(s.into()))
45    }
46
47    /// Build a `(key, value)` pair for `version`.
48    pub fn version(s: impl Into<String>) -> (&'static str, Value) {
49        (keys::VERSION, Value::String(s.into()))
50    }
51
52    /// Build a `(key, value)` pair for `public`.
53    pub fn public(b: bool) -> (&'static str, Value) {
54        (keys::PUBLIC, Value::Bool(b))
55    }
56}
57
58/// Insert a key/value into `metadata`, upgrading `Null` / non-object to an
59/// object. Returns the updated value.
60pub fn merge_into(metadata: Value, kv: (&str, Value)) -> Value {
61    let mut map = match metadata {
62        Value::Object(m) => m,
63        _ => Map::new(),
64    };
65    map.insert(kv.0.to_string(), kv.1);
66    Value::Object(map)
67}
68
69/// Read a string-typed well-known key from `metadata` if present.
70pub fn read_string(metadata: &Value, key: &str) -> Option<String> {
71    metadata.as_object()?.get(key)?.as_str().map(str::to_owned)
72}
73
74/// Read a bool-typed well-known key from `metadata` if present.
75pub fn read_bool(metadata: &Value, key: &str) -> Option<bool> {
76    metadata.as_object()?.get(key)?.as_bool()
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn merge_into_null_creates_object() {
85        let v = merge_into(Value::Null, TraceMeta::session("s1"));
86        assert_eq!(v.as_object().unwrap()["session_id"], "s1");
87    }
88
89    #[test]
90    fn merge_into_existing_object_inserts_key() {
91        let v = merge_into(serde_json::json!({"a": 1}), TraceMeta::user("u1"));
92        let obj = v.as_object().unwrap();
93        assert_eq!(obj["a"], 1);
94        assert_eq!(obj["user_id"], "u1");
95    }
96
97    #[test]
98    fn merge_into_overwrites_same_key() {
99        let v = merge_into(Value::Null, TraceMeta::session("first"));
100        let v = merge_into(v, TraceMeta::session("second"));
101        assert_eq!(v.as_object().unwrap()["session_id"], "second");
102    }
103
104    #[test]
105    fn read_string_returns_none_when_missing() {
106        assert_eq!(read_string(&Value::Null, keys::SESSION_ID), None);
107        assert_eq!(
108            read_string(&serde_json::json!({"a": 1}), keys::SESSION_ID),
109            None
110        );
111    }
112
113    #[test]
114    fn read_string_finds_well_known_key() {
115        let v = merge_into(Value::Null, TraceMeta::release("v1.2.3"));
116        assert_eq!(read_string(&v, keys::RELEASE).as_deref(), Some("v1.2.3"));
117    }
118
119    #[test]
120    fn read_bool_finds_public() {
121        let v = merge_into(Value::Null, TraceMeta::public(true));
122        assert_eq!(read_bool(&v, keys::PUBLIC), Some(true));
123    }
124}