1use std::collections::HashMap;
4use std::fmt;
5
6use ainl_graph_extractor::ExtractionReport;
7use ainl_memory::{AgentGraphSnapshot, AinlMemoryNode, GraphValidationReport, SqliteGraphStore};
8use ainl_persona::PersonaSnapshot;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13pub const EMIT_TO_EDGE: &str = "EMIT_TO";
15
16#[derive(Debug, Clone)]
18pub struct PatchDispatchResult {
19 pub label: String,
20 pub patch_version: u32,
21 pub fitness_before: f32,
22 pub fitness_after: f32,
23 pub dispatched: bool,
24 pub skip_reason: Option<PatchSkipReason>,
25 pub adapter_output: Option<serde_json::Value>,
27 pub adapter_name: Option<String>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum PatchSkipReason {
33 MissingDeclaredRead(String),
34 Retired,
35 ZeroVersion,
36 NotProcedural,
38 PersistFailed(String),
40}
41
42impl fmt::Display for PatchSkipReason {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 PatchSkipReason::MissingDeclaredRead(s) => write!(f, "missing_declared_read:{s}"),
46 PatchSkipReason::Retired => write!(f, "retired"),
47 PatchSkipReason::ZeroVersion => write!(f, "zero_version"),
48 PatchSkipReason::NotProcedural => write!(f, "not_procedural"),
49 PatchSkipReason::PersistFailed(s) => write!(f, "persist_failed:{s}"),
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct AinlGraphArtifact {
57 pub agent_id: String,
58 pub snapshot: AgentGraphSnapshot,
59 pub validation: GraphValidationReport,
60}
61
62impl AinlGraphArtifact {
63 pub fn load(store: &SqliteGraphStore, agent_id: &str) -> Result<Self, String> {
65 let snapshot = store.export_graph(agent_id)?;
66 let validation = store.validate_graph(agent_id)?;
67 if !validation.is_valid {
68 let mut msg = String::from("graph validation failed: dangling edges");
69 for d in &validation.dangling_edge_details {
70 msg.push_str(&format!(
71 "; {} -> {} [{}]",
72 d.source_id, d.target_id, d.edge_type
73 ));
74 }
75 return Err(msg);
76 }
77 Ok(Self {
78 agent_id: agent_id.to_string(),
79 snapshot,
80 validation,
81 })
82 }
83
84 pub fn from_snapshot(snapshot: AgentGraphSnapshot) -> Self {
86 let agent_id = snapshot.agent_id.clone();
87 let node_count = snapshot.nodes.len();
88 let edge_count = snapshot.edges.len();
89 let validation = GraphValidationReport {
90 agent_id: agent_id.clone(),
91 node_count,
92 edge_count,
93 dangling_edges: Vec::new(),
94 dangling_edge_details: Vec::new(),
95 cross_agent_boundary_edges: 0,
96 orphan_nodes: Vec::new(),
97 is_valid: true,
98 };
99 Self {
100 agent_id,
101 snapshot,
102 validation,
103 }
104 }
105}
106
107#[derive(Debug, Clone, Default, Serialize, Deserialize)]
109#[serde(default)]
110pub struct TurnInput {
111 pub user_message: String,
112 pub tools_invoked: Vec<String>,
113 pub trace_event: Option<serde_json::Value>,
114 pub depth: u32,
116 pub frame: HashMap<String, serde_json::Value>,
118 pub emit_targets: Vec<Uuid>,
121}
122
123#[derive(Debug, Clone)]
125pub struct MemoryContext {
126 pub recent_episodes: Vec<AinlMemoryNode>,
127 pub relevant_semantic: Vec<AinlMemoryNode>,
128 pub active_patches: Vec<AinlMemoryNode>,
129 pub persona_snapshot: Option<PersonaSnapshot>,
130 pub compiled_at: DateTime<Utc>,
131}
132
133impl Default for MemoryContext {
134 fn default() -> Self {
135 Self {
136 recent_episodes: Vec::new(),
137 relevant_semantic: Vec::new(),
138 active_patches: Vec::new(),
139 persona_snapshot: None,
140 compiled_at: Utc::now(),
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
147pub struct TurnOutput {
148 pub episode_id: Uuid,
149 pub persona_prompt_contribution: Option<String>,
150 pub memory_context: MemoryContext,
151 pub extraction_report: Option<ExtractionReport>,
152 pub steps_executed: u32,
153 pub outcome: TurnOutcome,
154 pub patch_dispatch_results: Vec<PatchDispatchResult>,
155}
156
157impl Default for TurnOutput {
158 fn default() -> Self {
159 Self {
160 episode_id: Uuid::nil(),
161 persona_prompt_contribution: None,
162 memory_context: MemoryContext::default(),
163 extraction_report: None,
164 steps_executed: 0,
165 outcome: TurnOutcome::Success,
166 patch_dispatch_results: Vec::new(),
167 }
168 }
169}
170
171#[derive(Debug, Clone)]
173pub enum TurnOutcome {
174 Success,
175 DepthLimitExceeded,
176 StepLimitExceeded { steps_executed: u32 },
177 GraphMemoryDisabled,
178 PartialSuccess {
179 episode_recorded: bool,
180 extraction_failed: bool,
181 patches_failed: Vec<String>,
182 warnings: Vec<String>,
183 },
184 Error(String),
185}