llm_agent_runtime/
types.rs1use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
11pub struct AgentId(pub String);
12
13impl AgentId {
14 pub fn new(id: impl Into<String>) -> Self {
22 let id = id.into();
23 if id.is_empty() {
24 debug_assert!(false, "AgentId must not be empty");
25 tracing::warn!("AgentId::new called with an empty string — agent IDs should be non-empty to avoid lookup ambiguity");
26 }
27 Self(id)
28 }
29
30 pub fn try_new(id: impl Into<String>) -> Result<Self, crate::error::AgentRuntimeError> {
35 let id = id.into();
36 if id.is_empty() {
37 return Err(crate::error::AgentRuntimeError::Memory(
38 "AgentId must not be empty".into(),
39 ));
40 }
41 Ok(Self(id))
42 }
43
44 pub fn random() -> Self {
46 Self(Uuid::new_v4().to_string())
47 }
48
49 pub fn as_str(&self) -> &str {
51 &self.0
52 }
53
54 pub fn len(&self) -> usize {
56 self.0.len()
57 }
58
59 pub fn is_empty(&self) -> bool {
64 self.0.is_empty()
65 }
66
67 pub fn starts_with(&self, prefix: &str) -> bool {
69 self.0.starts_with(prefix)
70 }
71}
72
73impl AsRef<str> for AgentId {
74 fn as_ref(&self) -> &str {
75 &self.0
76 }
77}
78
79impl std::fmt::Display for AgentId {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", self.0)
82 }
83}
84
85impl From<String> for AgentId {
86 fn from(s: String) -> Self {
90 Self::new(s)
91 }
92}
93
94impl From<&str> for AgentId {
95 fn from(s: &str) -> Self {
99 Self::new(s)
100 }
101}
102
103impl std::str::FromStr for AgentId {
104 type Err = crate::error::AgentRuntimeError;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
111 Self::try_new(s)
112 }
113}
114
115impl std::ops::Deref for AgentId {
116 type Target = str;
117
118 fn deref(&self) -> &Self::Target {
122 &self.0
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
128pub struct MemoryId(pub String);
129
130impl MemoryId {
131 pub fn new(id: impl Into<String>) -> Self {
139 let id = id.into();
140 if id.is_empty() {
141 debug_assert!(false, "MemoryId must not be empty");
142 tracing::warn!("MemoryId::new called with an empty string — memory IDs should be non-empty to avoid lookup ambiguity");
143 }
144 Self(id)
145 }
146
147 pub fn try_new(id: impl Into<String>) -> Result<Self, crate::error::AgentRuntimeError> {
149 let id = id.into();
150 if id.is_empty() {
151 return Err(crate::error::AgentRuntimeError::Memory(
152 "MemoryId must not be empty".into(),
153 ));
154 }
155 Ok(Self(id))
156 }
157
158 pub fn random() -> Self {
160 Self(Uuid::new_v4().to_string())
161 }
162
163 pub fn as_str(&self) -> &str {
165 &self.0
166 }
167
168 pub fn len(&self) -> usize {
170 self.0.len()
171 }
172
173 pub fn is_empty(&self) -> bool {
175 self.0.is_empty()
176 }
177
178 pub fn starts_with(&self, prefix: &str) -> bool {
180 self.0.starts_with(prefix)
181 }
182}
183
184impl AsRef<str> for MemoryId {
185 fn as_ref(&self) -> &str {
186 &self.0
187 }
188}
189
190impl std::fmt::Display for MemoryId {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 write!(f, "{}", self.0)
193 }
194}
195
196impl From<String> for MemoryId {
197 fn from(s: String) -> Self {
201 Self::new(s)
202 }
203}
204
205impl From<&str> for MemoryId {
206 fn from(s: &str) -> Self {
210 Self::new(s)
211 }
212}
213
214impl std::str::FromStr for MemoryId {
215 type Err = crate::error::AgentRuntimeError;
216
217 fn from_str(s: &str) -> Result<Self, Self::Err> {
222 Self::try_new(s)
223 }
224}
225
226impl std::ops::Deref for MemoryId {
227 type Target = str;
228
229 fn deref(&self) -> &Self::Target {
233 &self.0
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_agent_id_new_stores_value() {
243 let id = AgentId::new("agent-1");
244 assert_eq!(id.as_str(), "agent-1");
245 }
246
247 #[test]
248 fn test_agent_id_try_new_rejects_empty() {
249 assert!(AgentId::try_new("").is_err());
250 }
251
252 #[test]
253 fn test_agent_id_try_new_accepts_nonempty() {
254 let id = AgentId::try_new("ok").unwrap();
255 assert_eq!(id.as_str(), "ok");
256 }
257
258 #[test]
259 fn test_agent_id_random_generates_unique_ids() {
260 let a = AgentId::random();
261 let b = AgentId::random();
262 assert_ne!(a, b);
263 }
264
265 #[test]
266 fn test_agent_id_len_and_is_empty() {
267 let id = AgentId::new("abc");
268 assert_eq!(id.len(), 3);
269 assert!(!id.is_empty());
270 }
271
272 #[test]
273 fn test_agent_id_display() {
274 let id = AgentId::new("my-agent");
275 assert_eq!(id.to_string(), "my-agent");
276 }
277
278 #[test]
279 fn test_memory_id_new_stores_value() {
280 let id = MemoryId::new("mem-42");
281 assert_eq!(id.as_str(), "mem-42");
282 }
283
284 #[test]
285 fn test_memory_id_try_new_rejects_empty() {
286 assert!(MemoryId::try_new("").is_err());
287 }
288
289 #[test]
290 fn test_memory_id_len_and_is_empty() {
291 let id = MemoryId::new("hello");
292 assert_eq!(id.len(), 5);
293 assert!(!id.is_empty());
294 }
295
296 #[test]
297 fn test_memory_id_display() {
298 let id = MemoryId::new("mem-id");
299 assert_eq!(id.to_string(), "mem-id");
300 }
301
302 #[test]
303 fn test_memory_id_random_generates_unique_ids() {
304 let a = MemoryId::random();
305 let b = MemoryId::random();
306 assert_ne!(a, b);
307 }
308
309 #[test]
312 fn test_agent_id_starts_with_matching_prefix() {
313 let id = AgentId::new("agent-001");
314 assert!(id.starts_with("agent-"));
315 assert!(!id.starts_with("user-"));
316 }
317
318 #[test]
319 fn test_agent_id_starts_with_empty_prefix_always_true() {
320 let id = AgentId::new("anything");
321 assert!(id.starts_with(""));
322 }
323
324 #[test]
325 fn test_memory_id_starts_with_matching_prefix() {
326 let id = MemoryId::new("mem-42");
327 assert!(id.starts_with("mem-"));
328 assert!(!id.starts_with("ep-"));
329 }
330
331 #[test]
334 fn test_agent_id_ord_allows_sorting() {
335 let mut ids = vec![AgentId::new("c"), AgentId::new("a"), AgentId::new("b")];
336 ids.sort();
337 assert_eq!(ids[0].as_str(), "a");
338 assert_eq!(ids[2].as_str(), "c");
339 }
340
341 #[test]
342 fn test_agent_id_ord_allows_btreemap_key() {
343 use std::collections::BTreeMap;
344 let mut map: BTreeMap<AgentId, u32> = BTreeMap::new();
345 map.insert(AgentId::new("agent-2"), 2);
346 map.insert(AgentId::new("agent-1"), 1);
347 let keys: Vec<_> = map.keys().map(|k| k.as_str()).collect();
348 assert_eq!(keys, vec!["agent-1", "agent-2"]);
349 }
350
351 #[test]
352 fn test_agent_id_from_string() {
353 let id = AgentId::from("my-agent".to_owned());
354 assert_eq!(id.as_str(), "my-agent");
355 }
356
357 #[test]
358 fn test_agent_id_from_str_ref() {
359 let id = AgentId::from("my-agent");
360 assert_eq!(id.as_str(), "my-agent");
361 }
362
363 #[test]
364 fn test_agent_id_from_str_parse_rejects_empty() {
365 let result: Result<AgentId, _> = "".parse();
366 assert!(result.is_err());
367 }
368
369 #[test]
370 fn test_agent_id_from_str_parse_accepts_nonempty() {
371 let id: AgentId = "worker-1".parse().unwrap();
372 assert_eq!(id.as_str(), "worker-1");
373 }
374
375 #[test]
376 fn test_agent_id_deref_to_str() {
377 let id = AgentId::new("deref-test");
378 let s: &str = &id;
379 assert_eq!(s, "deref-test");
380 }
381
382 #[test]
383 fn test_agent_id_deref_enables_str_methods() {
384 let id = AgentId::new("hello-world");
385 assert!(id.contains('-'));
387 assert_eq!(id.len(), 11);
388 }
389
390 #[test]
391 fn test_memory_id_ord_allows_sorting() {
392 let mut ids = vec![MemoryId::new("z"), MemoryId::new("a"), MemoryId::new("m")];
393 ids.sort();
394 assert_eq!(ids[0].as_str(), "a");
395 assert_eq!(ids[2].as_str(), "z");
396 }
397
398 #[test]
399 fn test_memory_id_from_string() {
400 let id = MemoryId::from("mem-x".to_owned());
401 assert_eq!(id.as_str(), "mem-x");
402 }
403
404 #[test]
405 fn test_memory_id_from_str_ref() {
406 let id = MemoryId::from("mem-y");
407 assert_eq!(id.as_str(), "mem-y");
408 }
409
410 #[test]
411 fn test_memory_id_from_str_parse_rejects_empty() {
412 let result: Result<MemoryId, _> = "".parse();
413 assert!(result.is_err());
414 }
415
416 #[test]
417 fn test_memory_id_from_str_parse_accepts_nonempty() {
418 let id: MemoryId = "ep-001".parse().unwrap();
419 assert_eq!(id.as_str(), "ep-001");
420 }
421
422 #[test]
423 fn test_memory_id_deref_to_str() {
424 let id = MemoryId::new("deref-mem");
425 let s: &str = &id;
426 assert_eq!(s, "deref-mem");
427 }
428}