use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::Duration;
pub mod rquickjs;
#[derive(Debug, Clone)]
pub struct WorkflowModuleInput {
pub source: String,
pub source_name: String,
pub args: Value,
pub budget: WorkflowBudgetSnapshot,
pub sandbox: SandboxOptions,
}
impl WorkflowModuleInput {
pub fn new(source: impl Into<String>, source_name: impl Into<String>, args: Value) -> Self {
Self {
source: source.into(),
source_name: source_name.into(),
args,
budget: WorkflowBudgetSnapshot::default(),
sandbox: SandboxOptions::default(),
}
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
pub struct WorkflowBudgetSnapshot {
pub total: Option<u64>,
pub spent: u64,
}
#[derive(Debug, Clone)]
pub struct SandboxOptions {
pub memory_limit_bytes: usize,
pub max_stack_size_bytes: usize,
pub timeout: Duration,
pub import_policy: ImportPolicy,
}
impl Default for SandboxOptions {
fn default() -> Self {
Self {
memory_limit_bytes: 128 * 1024 * 1024,
max_stack_size_bytes: 1024 * 1024,
timeout: Duration::from_secs(5),
import_policy: ImportPolicy::DenyAll,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ImportPolicy {
DenyAll,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct WorkflowModuleOutput {
pub result: Value,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum WorkflowRef {
Name(String),
ScriptPath {
#[serde(rename = "scriptPath")]
script_path: String,
},
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum WorkflowRuntimeCall {
#[serde(rename = "log")]
Log { values: Vec<Value> },
#[serde(rename = "phase")]
Phase {
name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
options: Option<Value>,
},
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum WorkflowRuntimeRequest {
#[serde(rename = "agent")]
Agent {
id: String,
prompt: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
options: Option<Value>,
},
#[serde(rename = "workflow")]
Workflow {
id: String,
#[serde(rename = "ref")]
workflow_ref: WorkflowRef,
#[serde(default, skip_serializing_if = "Option::is_none")]
args: Option<Value>,
},
#[serde(rename = "sleep")]
Sleep {
id: String,
#[serde(rename = "durationMs")]
duration_ms: u64,
},
}
impl WorkflowRuntimeRequest {
pub fn id(&self) -> &str {
match self {
Self::Agent { id, .. } | Self::Workflow { id, .. } | Self::Sleep { id, .. } => id,
}
}
pub fn kind(&self) -> &'static str {
match self {
Self::Agent { .. } => "agent",
Self::Workflow { .. } => "workflow",
Self::Sleep { .. } => "sleep",
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub enum WorkflowRuntimeRequestResolution {
Ok(Value),
OkUndefined,
OkWithBudget {
value: Value,
budget: WorkflowBudgetSnapshot,
},
Err {
message: String,
},
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub enum WorkflowRuntimePoll {
Call(WorkflowRuntimeCall),
Request(WorkflowRuntimeRequest),
Complete(WorkflowModuleOutput),
Pending,
}
pub trait WorkflowRuntimeExecution {
fn poll(&mut self) -> anyhow::Result<WorkflowRuntimePoll>;
fn take_pending_requests(&mut self) -> anyhow::Result<Vec<WorkflowRuntimeRequest>>;
fn resolve_request(
&mut self,
id: &str,
resolution: WorkflowRuntimeRequestResolution,
) -> anyhow::Result<()>;
}
pub trait WorkflowJSRuntime {
fn start_module(
&self,
input: WorkflowModuleInput,
) -> anyhow::Result<Box<dyn WorkflowRuntimeExecution>>;
}