#![allow(deprecated)]
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use crate::stream::StreamEvent;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GraphEvent {
RunStart {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
graph_name: Option<String>,
},
RunEnd {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
},
NodeStart {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
node_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
attempt: u32,
#[serde(skip_serializing_if = "Option::is_none", default)]
attempt_id: Option<stack_ids::AttemptId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
trial_id: Option<stack_ids::TrialId>,
},
NodeEnd {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
node_id: String,
outcome: NodeOutcomeKind,
#[serde(skip_serializing_if = "Option::is_none", default)]
attempt_id: Option<stack_ids::AttemptId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
trial_id: Option<stack_ids::TrialId>,
},
Token {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
node_id: String,
token: String,
},
CheckpointWritten {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
#[serde(alias = "attempt_id")]
checkpoint_attempt_id: String,
},
InterruptRaised {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
node_id: String,
kind: String,
payload: Value,
},
StateUpdate {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
node_id: String,
updates: HashMap<String, Value>,
},
SuperstepStart {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
step: usize,
nodes: Vec<String>,
},
SuperstepEnd {
run_id: String,
#[deprecated(
note = "Use trace_ctx/attempt_id/trial_id instead. Will be removed when all consumers migrate."
)]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
step: usize,
},
ParallelCancellation {
run_id: String,
#[deprecated(note = "Use trace_ctx instead.")]
trace_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
trace_ctx: Option<stack_ids::TraceCtx>,
external_effects_may_have_escaped: bool,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NodeOutcomeKind {
Success,
Failed,
Interrupted,
}
pub trait EventSink: Send + Sync {
fn emit(&self, event: GraphEvent);
}
pub struct NoopEventSink;
impl EventSink for NoopEventSink {
fn emit(&self, _event: GraphEvent) {}
}
pub struct ChannelEventSink {
sender: tokio::sync::mpsc::Sender<StreamEvent>,
}
impl ChannelEventSink {
pub fn new(sender: tokio::sync::mpsc::Sender<StreamEvent>) -> Self {
Self { sender }
}
}
impl EventSink for ChannelEventSink {
fn emit(&self, event: GraphEvent) {
let stream_event = match event {
GraphEvent::RunStart { graph_name, .. } => StreamEvent::GraphStart { graph_name },
GraphEvent::RunEnd { .. } => StreamEvent::GraphEnd { graph_name: None },
GraphEvent::NodeStart { node_id, .. } => StreamEvent::NodeStart { node: node_id },
GraphEvent::NodeEnd { node_id, .. } => StreamEvent::NodeEnd { node: node_id },
GraphEvent::Token {
run_id,
node_id,
token,
..
} => StreamEvent::Custom(serde_json::json!({
"type": "token",
"run_id": run_id,
"node": node_id,
"token": token,
})),
GraphEvent::InterruptRaised {
node_id, payload, ..
} => StreamEvent::Interrupt {
node: node_id,
value: Some(payload),
},
GraphEvent::StateUpdate {
node_id, updates, ..
} => StreamEvent::StateUpdate {
node: node_id,
updates,
},
GraphEvent::SuperstepStart { step, nodes, .. } => {
StreamEvent::SuperstepStart { step, nodes }
}
GraphEvent::SuperstepEnd { step, .. } => StreamEvent::SuperstepEnd { step },
GraphEvent::ParallelCancellation { .. } => return,
GraphEvent::CheckpointWritten { .. } => return, };
let _ = self.sender.try_send(stream_event);
}
}
pub struct CallbackEventSink<F: Fn(GraphEvent) + Send + Sync> {
callback: F,
}
impl<F: Fn(GraphEvent) + Send + Sync> CallbackEventSink<F> {
pub fn new(callback: F) -> Self {
Self { callback }
}
}
impl<F: Fn(GraphEvent) + Send + Sync> EventSink for CallbackEventSink<F> {
fn emit(&self, event: GraphEvent) {
(self.callback)(event);
}
}
pub struct CompositeEventSink {
sinks: Vec<Arc<dyn EventSink>>,
}
impl CompositeEventSink {
pub fn new(sinks: Vec<Arc<dyn EventSink>>) -> Self {
Self { sinks }
}
}
impl EventSink for CompositeEventSink {
fn emit(&self, event: GraphEvent) {
for sink in &self.sinks {
sink.emit(event.clone());
}
}
}