ainl_memory/
trajectory_table.rs1use ainl_contracts::{TrajectoryOutcome, TrajectoryStep};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct TrajectoryDetailRecord {
12 pub id: Uuid,
13 pub episode_id: Uuid,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub graph_trajectory_node_id: Option<Uuid>,
17 pub agent_id: String,
18 pub session_id: String,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub project_id: Option<String>,
21 pub recorded_at: i64,
22 pub outcome: TrajectoryOutcome,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub ainl_source_hash: Option<String>,
25 pub duration_ms: u64,
26 pub steps: Vec<TrajectoryStep>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub frame_vars: Option<serde_json::Value>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub fitness_delta: Option<f32>,
31}
32
33impl TrajectoryDetailRecord {
34 #[must_use]
36 pub fn to_replay_line(&self) -> ainl_trajectory::TrajectoryReplayLine {
37 ainl_trajectory::trajectory_replay_line(
38 self.id,
39 self.episode_id,
40 self.graph_trajectory_node_id,
41 &self.agent_id,
42 &self.session_id,
43 self.project_id.as_deref(),
44 self.recorded_at,
45 self.outcome,
46 self.ainl_source_hash.as_deref(),
47 self.duration_ms,
48 self.steps.clone(),
49 self.frame_vars.clone(),
50 self.fitness_delta,
51 )
52 }
53
54 pub fn to_replay_jsonl(&self) -> Result<String, serde_json::Error> {
56 self.to_replay_line().to_jsonl_string()
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use uuid::Uuid;
64
65 #[test]
66 fn detail_record_replay_jsonl_roundtrip() {
67 let r = TrajectoryDetailRecord {
68 id: Uuid::nil(),
69 episode_id: Uuid::nil(),
70 graph_trajectory_node_id: None,
71 agent_id: "a".into(),
72 session_id: "s".into(),
73 project_id: None,
74 recorded_at: 42,
75 outcome: TrajectoryOutcome::PartialSuccess,
76 ainl_source_hash: Some("h1".into()),
77 duration_ms: 7,
78 steps: vec![],
79 frame_vars: None,
80 fitness_delta: None,
81 };
82 let line = r.to_replay_jsonl().unwrap();
83 let rows = ainl_trajectory::parse_jsonl(&line).unwrap();
84 assert_eq!(rows.len(), 1);
85 assert_eq!(rows[0].agent_id, "a");
86 assert_eq!(rows[0].outcome, TrajectoryOutcome::PartialSuccess);
87 }
88}