use crate::scenario::{
AgentsConfig, AppConfigTemplate, EnvironmentConfig, EvalConditions, EvalScenario, LlmConfig,
ManagerConfig, ScenarioActions, ScenarioMeta, TaskConfig,
};
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct RuntimeTaskSpec {
pub meta: ScenarioMeta,
pub task: TaskConfig,
pub llm: LlmConfig,
pub manager: ManagerConfig,
pub actions: ScenarioActions,
pub app_config: AppConfigTemplate,
pub environment: EnvironmentConfig,
pub agents: AgentsConfig,
pub conditions: EvalConditions,
pub working_dir: Option<PathBuf>,
}
impl RuntimeTaskSpec {
pub fn with_task(mut self, goal: impl Into<String>) -> Self {
self.task.goal = goal.into();
self
}
pub fn with_env_type(mut self, env_type: impl Into<String>) -> Self {
self.environment.env_type = env_type.into();
self
}
pub fn with_working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.working_dir = Some(dir.into());
self
}
pub fn with_max_ticks(mut self, max_ticks: u64) -> Self {
self.app_config.max_ticks = max_ticks;
self
}
pub fn into_eval_scenario(mut self) -> EvalScenario {
if let Some(ref dir) = self.working_dir {
let params = self.environment.params.as_object_mut();
if let Some(obj) = params {
obj.insert(
"working_dir".to_string(),
serde_json::Value::String(dir.display().to_string()),
);
} else {
let mut obj = serde_json::Map::new();
obj.insert(
"working_dir".to_string(),
serde_json::Value::String(dir.display().to_string()),
);
self.environment.params = serde_json::Value::Object(obj);
}
}
EvalScenario {
meta: self.meta,
task: self.task,
llm: self.llm,
manager: self.manager,
batch_processor: Default::default(),
dependency_graph: None,
actions: self.actions,
app_config: self.app_config,
environment: self.environment,
agents: self.agents,
conditions: self.conditions,
milestones: vec![],
variants: vec![],
}
}
pub fn resolved_working_dir(&self) -> PathBuf {
self.working_dir
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
}
impl From<EvalScenario> for RuntimeTaskSpec {
fn from(scenario: EvalScenario) -> Self {
Self {
meta: scenario.meta,
task: scenario.task,
llm: scenario.llm,
manager: scenario.manager,
actions: scenario.actions,
app_config: scenario.app_config,
environment: scenario.environment,
agents: scenario.agents,
conditions: scenario.conditions,
working_dir: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn minimal_scenario() -> EvalScenario {
let toml_str = r#"
[meta]
name = "Test"
id = "test:minimal:v1"
[task]
goal = "Original goal"
[llm]
provider = "llama-server"
model = "test-model"
[app_config]
max_ticks = 100
[environment]
env_type = "default"
[agents]
[[agents.workers]]
id_pattern = "worker_{i}"
count = 1
[conditions]
on_timeout = "fail"
"#;
toml::from_str(toml_str).unwrap()
}
#[test]
fn test_from_eval_scenario() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario);
assert_eq!(spec.task.goal, "Original goal");
assert_eq!(spec.environment.env_type, "default");
}
#[test]
fn test_with_task() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario).with_task("New custom goal");
assert_eq!(spec.task.goal, "New custom goal");
}
#[test]
fn test_with_env_type() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario).with_env_type("realworld");
assert_eq!(spec.environment.env_type, "realworld");
}
#[test]
fn test_into_eval_scenario() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario)
.with_task("Modified goal")
.with_max_ticks(50);
let converted = spec.into_eval_scenario();
assert_eq!(converted.task.goal, "Modified goal");
assert_eq!(converted.app_config.max_ticks, 50);
assert!(converted.milestones.is_empty());
assert!(converted.variants.is_empty());
}
#[test]
fn test_working_dir_propagates_to_env_params() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario).with_working_dir("/tmp/test_workspace");
let converted = spec.into_eval_scenario();
let params = converted.environment.params.as_object().unwrap();
assert_eq!(
params.get("working_dir").and_then(|v| v.as_str()),
Some("/tmp/test_workspace")
);
}
#[test]
fn test_working_dir_without_override() {
let scenario = minimal_scenario();
let spec = RuntimeTaskSpec::from(scenario);
assert!(spec.working_dir.is_none());
let converted = spec.into_eval_scenario();
let params = converted.environment.params.as_object();
assert!(params.is_none() || params.unwrap().get("working_dir").is_none());
}
}