use std::io::Write;
use std::time::{Duration, Instant};
use futures::StreamExt;
use tkach::{Agent, AgentError, CancellationToken, Message, StreamEvent, providers::Anthropic};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = dotenvy::dotenv_override();
let agent = Agent::builder()
.provider(Anthropic::from_env())
.model(tkach::model::claude::HAIKU_20251001)
.system("You are verbose and detailed.")
.max_turns(1)
.max_tokens(2048)
.build()
.unwrap();
let cancel = CancellationToken::new();
let cancel_handle = cancel.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(1500)).await;
eprintln!("\n\n[firing cancel after 1500ms]");
cancel_handle.cancel();
});
let started = Instant::now();
let mut stream = agent.stream(
vec![Message::user_text(
"Write a detailed 500-word essay on the history of the Rust programming \
language. Include sections on its origins at Mozilla, the design goals, \
notable milestones in its release history, the role of the Rust Foundation, \
and its current state. Be thorough.",
)],
cancel,
);
print!("> ");
std::io::stdout().flush()?;
let mut delta_count = 0usize;
let mut text_seen = String::new();
while let Some(event) = stream.next().await {
match event {
Ok(StreamEvent::ContentDelta(text)) => {
delta_count += 1;
print!("{text}");
text_seen.push_str(&text);
std::io::stdout().flush()?;
}
Ok(_) => {}
Err(e) => {
eprintln!("\n[stream error: {e}]");
break;
}
}
}
println!();
let outcome = stream.into_result().await;
let elapsed = started.elapsed();
eprintln!();
eprintln!("--- summary ---");
eprintln!("delta count : {delta_count}");
eprintln!("text seen : {} chars", text_seen.len());
eprintln!("wall time : {elapsed:?}");
eprintln!(
"outcome : {}",
match &outcome {
Ok(_) => "Ok (unexpected — should have been cancelled)".to_string(),
Err(AgentError::Cancelled { partial }) => format!(
"Cancelled with partial.text len={} chars",
partial.text.len()
),
Err(other) => format!("Err: {other:?}"),
}
);
eprintln!();
let err = outcome.expect_err("expected cancellation, got Ok");
let AgentError::Cancelled { partial } = &err else {
panic!("expected AgentError::Cancelled, got {err:?}");
};
assert!(
delta_count >= 1,
"expected to receive at least one ContentDelta before cancel fired"
);
assert_eq!(
partial.stop_reason,
tkach::StopReason::Cancelled,
"partial should carry StopReason::Cancelled"
);
assert!(
elapsed < Duration::from_secs(5),
"expected prompt cancellation, took {elapsed:?}"
);
assert!(
!text_seen.is_empty(),
"expected non-empty text before cancel, got empty"
);
eprintln!("✓ all assertions passed");
Ok(())
}