#![allow(dead_code)]
use std::sync::{Arc, OnceLock};
use tokio::sync::mpsc;
use yoagent::provider::mock::{MockResponse, MockToolCall};
use yoagent::provider::{MockProvider, ModelConfig};
use yoagent::sub_agent::SubAgentTool;
use yoagent::*;
pub fn live_model() -> Option<ModelConfig> {
static LIVE: OnceLock<Option<ModelConfig>> = OnceLock::new();
LIVE.get_or_init(|| {
if !std::env::args().any(|a| a == "--live") {
return None;
}
let has = |var: &str| std::env::var(var).is_ok_and(|v| !v.trim().is_empty());
if has("DEEPSEEK_API_KEY") {
Some(ModelConfig::deepseek("deepseek-flash", "DeepSeek Flash"))
} else if has("ANTHROPIC_API_KEY") {
Some(ModelConfig::claude_haiku_4_5())
} else {
eprintln!("--live needs DEEPSEEK_API_KEY or ANTHROPIC_API_KEY; running offline");
None
}
})
.clone()
}
pub fn is_live() -> bool {
live_model().is_some()
}
pub fn new_agent(script: Vec<MockResponse>) -> Agent {
match live_model() {
Some(model) => Agent::from_config(model),
None => Agent::from_provider(MockProvider::new(script), ModelConfig::mock()),
}
}
pub fn sub_agent(name: &str, script: Vec<MockResponse>) -> SubAgentTool {
match live_model() {
Some(model) => SubAgentTool::from_config(name, model),
None => SubAgentTool::from_provider(
name,
Arc::new(MockProvider::new(script)),
ModelConfig::mock(),
),
}
}
pub fn call(name: &str, args: serde_json::Value) -> MockResponse {
calls(&[(name, args)])
}
pub fn calls(list: &[(&str, serde_json::Value)]) -> MockResponse {
MockResponse::ToolCalls(
list.iter()
.map(|(name, args)| MockToolCall {
name: name.to_string(),
arguments: args.clone(),
provider_metadata: None,
})
.collect(),
)
}
pub fn answer(text: &str) -> MockResponse {
MockResponse::Text(text.into())
}
pub async fn run(agent: &mut Agent, prompt: &str) -> Vec<AgentEvent> {
let (tx, mut rx) = mpsc::unbounded_channel();
agent.prompt_with_sender(prompt, tx).await;
let mut events = Vec::new();
while let Ok(event) = rx.try_recv() {
events.push(event);
}
events
}
pub fn text_of(content: &[Content]) -> String {
content
.iter()
.filter_map(|c| match c {
Content::Text { text } => Some(text.as_str()),
_ => None,
})
.collect::<Vec<_>>()
.join("")
}
pub fn tool_results(events: &[AgentEvent]) -> Vec<(String, String, bool)> {
events
.iter()
.filter_map(|e| match e {
AgentEvent::ToolExecutionEnd {
tool_name,
result,
is_error,
..
} => Some((tool_name.clone(), text_of(&result.content), *is_error)),
_ => None,
})
.collect()
}
pub fn last_text(events: &[AgentEvent]) -> String {
let Some(AgentEvent::AgentEnd { messages, .. }) = events.last() else {
return String::new();
};
match messages.last() {
Some(AgentMessage::Llm(Message::Assistant { content, .. }))
| Some(AgentMessage::Llm(Message::User { content, .. })) => text_of(content),
_ => String::new(),
}
}
pub fn check(ok: bool, what: &str) {
if is_live() {
println!(" (live run: not checking that {what})");
} else {
assert!(ok, "regression: expected {what}");
println!(" ok: {what}");
}
}