heddle_object_model/object/
state_attribution.rs1use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8pub struct Principal {
9 #[serde(with = "serde_bytes")]
11 pub name: Vec<u8>,
12 #[serde(with = "serde_bytes")]
14 pub email: Vec<u8>,
15}
16
17impl Principal {
18 pub fn new(name: impl AsRef<[u8]>, email: impl AsRef<[u8]>) -> Self {
20 Self {
21 name: name.as_ref().to_vec(),
22 email: email.as_ref().to_vec(),
23 }
24 }
25
26 pub fn name_lossy(&self) -> std::borrow::Cow<'_, str> {
28 String::from_utf8_lossy(&self.name)
29 }
30
31 pub fn email_lossy(&self) -> std::borrow::Cow<'_, str> {
33 String::from_utf8_lossy(&self.email)
34 }
35
36 pub fn from_env() -> Option<Self> {
38 let name = std::env::var("HEDDLE_PRINCIPAL_NAME").ok()?;
39 let email = std::env::var("HEDDLE_PRINCIPAL_EMAIL").ok()?;
40 if name.trim().is_empty() || email.trim().is_empty() {
41 return None;
42 }
43 Some(Self {
44 name: name.into_bytes(),
45 email: email.into_bytes(),
46 })
47 }
48}
49
50impl std::fmt::Display for Principal {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(f, "{} <{}>", self.name_lossy(), self.email_lossy())
53 }
54}
55
56#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
58pub struct Agent {
59 pub provider: String,
61 pub model: String,
63 pub session_id: Option<String>,
65 pub segment_id: Option<String>,
67 pub policy_id: Option<String>,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub thought_level: Option<String>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub parent: Option<String>,
75}
76
77impl Agent {
78 pub fn new(provider: impl Into<String>, model: impl Into<String>) -> Self {
80 Self {
81 provider: provider.into(),
82 model: model.into(),
83 session_id: None,
84 segment_id: None,
85 policy_id: None,
86 thought_level: None,
87 parent: None,
88 }
89 }
90
91 pub fn with_session(
93 mut self,
94 session_id: impl Into<String>,
95 segment_id: impl Into<String>,
96 ) -> Self {
97 self.session_id = Some(session_id.into());
98 self.segment_id = Some(segment_id.into());
99 self
100 }
101
102 pub fn with_policy(mut self, policy_id: impl Into<String>) -> Self {
104 self.policy_id = Some(policy_id.into());
105 self
106 }
107
108 pub fn from_env() -> Option<Self> {
110 let provider = std::env::var("HEDDLE_AGENT_PROVIDER").ok()?;
111 let model = std::env::var("HEDDLE_AGENT_MODEL").ok()?;
112 let session_id = std::env::var("HEDDLE_SESSION_ID").ok();
113 let segment_id = std::env::var("HEDDLE_SESSION_SEGMENT").ok();
114 let policy_id = std::env::var("HEDDLE_AGENT_POLICY").ok();
115 Some(Self {
116 provider,
117 model,
118 session_id,
119 segment_id,
120 policy_id,
121 thought_level: None,
122 parent: None,
123 })
124 }
125
126 pub fn with_thought_level(mut self, thought_level: impl Into<String>) -> Self {
128 self.thought_level = Some(thought_level.into());
129 self
130 }
131
132 pub fn with_parent(mut self, parent: impl Into<String>) -> Self {
134 self.parent = Some(parent.into());
135 self
136 }
137}
138
139impl std::fmt::Display for Agent {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(f, "{}/{}", self.provider, self.model)
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147pub struct Attribution {
148 pub principal: Principal,
150 pub agent: Option<Agent>,
152}
153
154impl Attribution {
155 pub fn human(principal: Principal) -> Self {
157 Self {
158 principal,
159 agent: None,
160 }
161 }
162
163 pub fn with_agent(principal: Principal, agent: Agent) -> Self {
165 Self {
166 principal,
167 agent: Some(agent),
168 }
169 }
170}
171
172impl std::fmt::Display for Attribution {
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174 if let Some(agent) = &self.agent {
175 write!(f, "{} (via {})", self.principal, agent)
176 } else {
177 write!(f, "{}", self.principal)
178 }
179 }
180}