use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct GraphId(pub String);
impl GraphId {
pub fn new() -> Self {
Self(format!("g_{}", crate::server::security::random_token()))
}
}
impl Default for GraphId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NodeId(pub String);
impl NodeId {
pub fn new(label: impl Into<String>) -> Self {
Self(label.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GraphStatus {
Running,
Paused,
Done,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NodeKind {
Worker,
Tool,
Ai,
Transform,
Merge,
Checkpoint,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NodeStatus {
Pending,
Running,
Done,
Failed,
Paused,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExecutionStrategy {
Single,
Pipeline,
Parallel,
MapReduce,
Hybrid,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeChoice {
#[default]
Auto,
Backend,
#[serde(alias = "edge")]
Node,
Browser,
Hybrid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionPlan {
pub runtime: RuntimeChoice,
pub strategy: ExecutionStrategy,
pub chunks: u32,
pub parallel: bool,
pub use_ai: bool,
pub tools: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_cost: Option<String>,
}
impl Default for ExecutionPlan {
fn default() -> Self {
Self {
runtime: RuntimeChoice::Auto,
strategy: ExecutionStrategy::Single,
chunks: 1,
parallel: false,
use_ai: false,
tools: Vec::new(),
estimated_cost: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum GraphEdge {
DataFlow {
from: NodeId,
to: NodeId,
key: Option<String>,
},
ControlFlow {
from: NodeId,
to: NodeId,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNodeSnapshot {
pub id: NodeId,
pub kind: NodeKind,
pub label: String,
pub status: NodeStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphSnapshot {
pub id: GraphId,
pub worker: String,
pub intent: String,
pub plan: ExecutionPlan,
pub nodes: Vec<GraphNodeSnapshot>,
pub edges: Vec<GraphEdge>,
pub status: GraphStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum WorkerEvent {
Log {
message: String,
node: NodeId,
timestamp_ms: u64,
},
Progress {
value: u8,
node: NodeId,
timestamp_ms: u64,
},
AiThinking {
content: String,
node: NodeId,
timestamp_ms: u64,
},
ToolCall {
tool: String,
#[serde(skip_serializing_if = "Option::is_none")]
args: Option<Value>,
node: NodeId,
timestamp_ms: u64,
},
NodeStart {
node: NodeId,
kind: NodeKind,
timestamp_ms: u64,
},
NodeDone {
node: NodeId,
duration_ms: u64,
timestamp_ms: u64,
},
NodeFailed {
node: NodeId,
error: String,
timestamp_ms: u64,
},
Result {
data: Value,
timestamp_ms: u64,
},
GraphDone {
graph_id: GraphId,
timestamp_ms: u64,
},
}
impl WorkerEvent {
pub fn timestamp_ms(&self) -> u64 {
match self {
Self::Log { timestamp_ms, .. }
| Self::Progress { timestamp_ms, .. }
| Self::AiThinking { timestamp_ms, .. }
| Self::ToolCall { timestamp_ms, .. }
| Self::NodeStart { timestamp_ms, .. }
| Self::NodeDone { timestamp_ms, .. }
| Self::NodeFailed { timestamp_ms, .. }
| Self::Result { timestamp_ms, .. }
| Self::GraphDone { timestamp_ms, .. } => *timestamp_ms,
}
}
pub fn node_id(&self) -> Option<&NodeId> {
match self {
Self::Log { node, .. }
| Self::Progress { node, .. }
| Self::AiThinking { node, .. }
| Self::ToolCall { node, .. }
| Self::NodeStart { node, .. }
| Self::NodeDone { node, .. }
| Self::NodeFailed { node, .. } => Some(node),
Self::Result { .. } | Self::GraphDone { .. } => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StartWorkerResponse {
pub graph_id: GraphId,
pub plan: ExecutionPlan,
#[serde(skip_serializing_if = "Option::is_none")]
pub access_token: Option<String>,
}