use std::sync::Arc;
use async_trait::async_trait;
use serde_json::Value as JsonValue;
use langgraph_checkpoint::config::RunnableConfig;
use super::base::{Runnable, RunnableError};
pub struct RunnableSeq {
name: String,
steps: Vec<Arc<dyn Runnable>>,
}
impl RunnableSeq {
pub fn new(name: impl Into<String>, steps: Vec<Arc<dyn Runnable>>) -> Self {
assert!(steps.len() >= 2, "RunnableSeq requires at least 2 steps");
Self {
name: name.into(),
steps,
}
}
pub fn new_relaxed(name: impl Into<String>, steps: Vec<Arc<dyn Runnable>>) -> Self {
Self {
name: name.into(),
steps,
}
}
pub fn len(&self) -> usize {
self.steps.len()
}
pub fn is_empty(&self) -> bool {
self.steps.is_empty()
}
}
#[async_trait]
impl Runnable for RunnableSeq {
fn invoke(&self, input: &JsonValue, config: &RunnableConfig) -> Result<JsonValue, RunnableError> {
let mut current = input.clone();
for step in &self.steps {
current = step.invoke(¤t, config)?;
}
Ok(current)
}
async fn ainvoke(&self, input: &JsonValue, config: &RunnableConfig) -> Result<JsonValue, RunnableError> {
let mut current = input.clone();
for step in &self.steps {
current = step.ainvoke(¤t, config).await?;
}
Ok(current)
}
fn name(&self) -> &str {
&self.name
}
}
pub fn pipe(first: Arc<dyn Runnable>, second: Arc<dyn Runnable>) -> RunnableSeq {
let name = format!("{}|{}", first.name(), second.name());
RunnableSeq::new_relaxed(name, vec![first, second])
}