use anyhow::Result;
use std::path::Path;
use std::sync::Arc;
use super::AgentOutput;
use crate::runtime::llm::LlmClient;
use crate::runtime::model_registry::ModelRegistry;
use crate::runtime::tracer::{TraceCtx, Tracer};
use crate::skill::manifest::ModelConfig;
use crate::skill::parser::parse_step;
pub struct OneshotStep {
pub body: String,
pub model: Option<ModelConfig>,
}
pub struct ReactStep {
pub body: String,
pub uses: Vec<String>,
pub max_iter: u32,
pub model: Option<ModelConfig>,
}
pub enum Step {
Oneshot(OneshotStep),
React(ReactStep),
}
impl Step {
pub fn from_file(path: &Path) -> Result<Self> {
let cfg = parse_step(path)?;
let model = cfg.call.as_ref().and_then(|c| c.model.clone());
if cfg.react {
let max_iter = cfg.max_iter();
Ok(Step::React(ReactStep {
body: cfg.body,
uses: cfg.call.map(|c| c.uses).unwrap_or_default(),
max_iter,
model,
}))
} else {
Ok(Step::Oneshot(OneshotStep {
body: cfg.body,
model,
}))
}
}
pub fn pattern_name(&self) -> &'static str {
match self {
Step::Oneshot(_) => "oneshot",
Step::React(_) => "react",
}
}
pub async fn run(
&self,
input: &str,
name: &str,
registry: &Arc<ModelRegistry>,
fallback_client: &LlmClient,
tracer: &mut dyn Tracer,
ctx: &TraceCtx,
crumb: &str,
) -> Result<AgentOutput> {
let owned_client;
let client: &LlmClient = {
let resolved = registry.resolve(self.model(), self.pattern_name())?;
let c = LlmClient::from_resolved(&resolved, Some(fallback_client.debug_hook()))?
.with_agent_name(name);
owned_client = c;
&owned_client
};
eprintln!(" → {name}…");
match self {
Step::Oneshot(s) => {
let value =
super::oneshot::call(&s.body, input, name, client, tracer, ctx, crumb).await?;
Ok(AgentOutput { key: "done".to_string(), value, span_id: String::new() })
}
Step::React(s) => {
super::react::react_loop(
&s.body,
&s.uses,
s.max_iter,
client,
input,
tracer,
ctx,
&[],
crumb,
)
.await
}
}
}
fn model(&self) -> Option<&ModelConfig> {
match self {
Step::Oneshot(s) => s.model.as_ref(),
Step::React(s) => s.model.as_ref(),
}
}
}