use car_agents::*;
use car_agents::{coordinator, planner, researcher, summarizer, verifier};
use car_inference::{InferenceConfig, InferenceEngine};
use std::sync::Arc;
const TEST_MODEL: &str = "gpt-4.1-mini";
fn make_ctx() -> AgentContext {
let mut dir = std::env::current_dir().ok();
while let Some(d) = dir {
let env_file = d.join(".env");
if env_file.exists() {
if let Ok(content) = std::fs::read_to_string(&env_file) {
for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some((k, v)) = line.split_once('=') {
if std::env::var(k.trim()).is_err() {
std::env::set_var(k.trim(), v.trim());
}
}
}
}
break;
}
dir = d.parent().map(|p| p.to_path_buf());
}
let engine = InferenceEngine::new(InferenceConfig::default());
AgentContext {
inference: Arc::new(engine),
}
}
#[tokio::test]
#[ignore] async fn test_researcher() {
let ctx = make_ctx();
let r = Researcher::with_config(
ctx,
researcher::ResearchConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let result = r.research("What is Rust's ownership model?", None).await;
println!(
"Researcher: {} (model: {}, {}ms)",
&result.output[..100.min(result.output.len())],
result.model_used,
result.latency_ms
);
assert!(!result.output.is_empty());
assert!(result.confidence > 0.0);
}
#[tokio::test]
#[ignore]
async fn test_planner() {
let ctx = make_ctx();
let p = PlannerAgent::with_config(
ctx,
planner::PlanConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let result = p
.plan("Deploy a Rust web service to production", None)
.await;
println!(
"Planner: {} (model: {}, {}ms)",
&result.output[..100.min(result.output.len())],
result.model_used,
result.latency_ms
);
assert!(!result.output.is_empty());
assert!(result.confidence > 0.0);
}
#[tokio::test]
#[ignore]
async fn test_verifier() {
let ctx = make_ctx();
let v = Verifier::with_config(
ctx,
verifier::VerifyConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let result = v
.verify(
"fn add(a: i32, b: i32) -> i32 { a + b }",
"Function must add two integers and return the sum",
)
.await;
println!(
"Verifier: {} (model: {}, {}ms)",
&result.output[..100.min(result.output.len())],
result.model_used,
result.latency_ms
);
assert!(result.output.contains("PASS") || result.output.contains("FAIL"));
}
#[tokio::test]
#[ignore]
async fn test_summarizer() {
let ctx = make_ctx();
let s = Summarizer::with_config(
ctx,
summarizer::SummaryConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let long_text =
"The Common Agent Runtime (CAR) is a deterministic execution layer for AI agents. "
.repeat(50);
let result = s.summarize(&long_text, Some("architecture")).await;
println!(
"Summarizer: {} (model: {}, {}ms, compression: {:.0}%)",
&result.output[..100.min(result.output.len())],
result.model_used,
result.latency_ms,
(1.0 - result.output.len() as f64 / long_text.len() as f64) * 100.0
);
assert!(result.output.len() < long_text.len());
}
#[tokio::test]
#[ignore]
async fn test_coordinator() {
let ctx = make_ctx();
let c = Coordinator::with_config(
ctx,
coordinator::CoordinatorConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let (plan, result) = c
.coordinate("Review a pull request for security vulnerabilities")
.await;
println!(
"Coordinator: pattern={:?}, agents={:?} (model: {}, {}ms)",
plan.pattern, plan.agents, result.model_used, result.latency_ms
);
assert!(!plan.agents.is_empty());
assert!(!plan.reasoning.is_empty());
}
#[tokio::test]
#[ignore]
async fn test_full_pipeline() {
let ctx = make_ctx();
let c = Coordinator::with_config(
ctx.clone(),
coordinator::CoordinatorConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let (plan, coord_result) = c.coordinate("Explain how memory works in CAR").await;
println!("Coordinator: {:?} → {:?}", plan.pattern, plan.agents);
let r = Researcher::with_config(
ctx.clone(),
researcher::ResearchConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let research = r
.research("How does memory work in the Common Agent Runtime?", None)
.await;
println!(
"Researcher: {}... ({}ms)",
&research.output[..80.min(research.output.len())],
research.latency_ms
);
let s = Summarizer::with_config(
ctx.clone(),
summarizer::SummaryConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let summary = s
.summarize(&research.output, Some("key architecture decisions"))
.await;
println!(
"Summarizer: {}... ({}ms)",
&summary.output[..80.min(summary.output.len())],
summary.latency_ms
);
let v = Verifier::with_config(
ctx.clone(),
verifier::VerifyConfig {
model: Some(TEST_MODEL.into()),
..Default::default()
},
);
let check = v
.verify(
&summary.output,
"Must explain graph-based memory, spreading activation, and context assembly",
)
.await;
println!(
"Verifier: {} ({}ms)",
if check.output.contains("PASS") {
"PASS"
} else {
"FAIL"
},
check.latency_ms
);
assert!(coord_result.confidence > 0.0);
assert!(!research.output.is_empty());
assert!(!summary.output.is_empty());
assert!(!check.output.is_empty());
let total_ms =
coord_result.latency_ms + research.latency_ms + summary.latency_ms + check.latency_ms;
println!("\nFull pipeline: {}ms total", total_ms);
}