1use crate::interrupt::Interrupt;
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, GraphError>;
8
9#[derive(Error, Debug)]
11pub enum GraphError {
12 #[error("Invalid graph structure: {0}")]
14 InvalidGraph(String),
15
16 #[error("Node not found: {0}")]
18 NodeNotFound(String),
19
20 #[error("Edge target not found: {0}")]
22 EdgeTargetNotFound(String),
23
24 #[error("No entry point defined (missing edge from START)")]
26 NoEntryPoint,
27
28 #[error("Recursion limit exceeded: {0} steps")]
30 RecursionLimitExceeded(usize),
31
32 #[error("Execution interrupted: {0:?}")]
34 Interrupted(Box<InterruptedExecution>),
35
36 #[error("Node '{node}' execution failed: {message}")]
38 NodeExecutionFailed { node: String, message: String },
39
40 #[error("State serialization error: {0}")]
42 SerializationError(String),
43
44 #[error("Checkpoint error: {0}")]
46 CheckpointError(String),
47
48 #[error("Router returned unknown target: {0}")]
50 UnknownRouteTarget(String),
51
52 #[error("IO error: {0}")]
54 IoError(#[from] std::io::Error),
55
56 #[error("JSON error: {0}")]
58 JsonError(#[from] serde_json::Error),
59
60 #[cfg(feature = "sqlite")]
62 #[error("Database error: {0}")]
63 DatabaseError(#[from] sqlx::Error),
64}
65
66#[derive(Debug, Clone)]
68pub struct InterruptedExecution {
69 pub thread_id: String,
71 pub checkpoint_id: String,
73 pub interrupt: Interrupt,
75 pub state: crate::state::State,
77 pub step: usize,
79}
80
81impl InterruptedExecution {
82 pub fn new(
84 thread_id: String,
85 checkpoint_id: String,
86 interrupt: Interrupt,
87 state: crate::state::State,
88 step: usize,
89 ) -> Self {
90 Self { thread_id, checkpoint_id, interrupt, state, step }
91 }
92}
93
94impl From<GraphError> for adk_core::AdkError {
95 fn from(err: GraphError) -> Self {
96 use adk_core::{ErrorCategory, ErrorComponent};
97 let (category, code) = match &err {
98 GraphError::InvalidGraph(_) => (ErrorCategory::InvalidInput, "graph.invalid"),
99 GraphError::NodeNotFound(_) => (ErrorCategory::NotFound, "graph.node_not_found"),
100 GraphError::EdgeTargetNotFound(_) => {
101 (ErrorCategory::NotFound, "graph.edge_target_not_found")
102 }
103 GraphError::NoEntryPoint => (ErrorCategory::InvalidInput, "graph.no_entry_point"),
104 GraphError::RecursionLimitExceeded(_) => {
105 (ErrorCategory::Internal, "graph.recursion_limit")
106 }
107 GraphError::Interrupted(_) => (ErrorCategory::Cancelled, "graph.interrupted"),
108 GraphError::NodeExecutionFailed { .. } => {
109 (ErrorCategory::Internal, "graph.node_execution_failed")
110 }
111 GraphError::SerializationError(_) => (ErrorCategory::Internal, "graph.serialization"),
112 GraphError::CheckpointError(_) => (ErrorCategory::Internal, "graph.checkpoint"),
113 GraphError::UnknownRouteTarget(_) => {
114 (ErrorCategory::NotFound, "graph.unknown_route_target")
115 }
116 GraphError::IoError(_) => (ErrorCategory::Internal, "graph.io"),
117 GraphError::JsonError(_) => (ErrorCategory::Internal, "graph.json"),
118 #[cfg(feature = "sqlite")]
119 GraphError::DatabaseError(_) => (ErrorCategory::Internal, "graph.database"),
120 };
121 adk_core::AdkError::new(ErrorComponent::Graph, category, code, err.to_string())
122 .with_source(err)
123 }
124}