use std::io::Write;
use ds_api::{AgentEvent, DeepseekAgent, tool};
use futures::StreamExt;
use serde_json::json;
use tokio::time::{Duration, sleep};
struct SlowCounter;
#[tool]
impl Tool for SlowCounter {
async fn count_to(&self, n: u32) -> Value {
for i in 1..=n {
sleep(Duration::from_millis(200)).await;
eprintln!(" [tool] counting… {i}/{n}");
}
json!({ "final_count": n, "message": format!("counted to {n}") })
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let token = std::env::var("DEEPSEEK_API_KEY").expect("DEEPSEEK_API_KEY must be set");
let agent = DeepseekAgent::new(&token)
.with_streaming()
.with_system_prompt(
"You are a helpful assistant. \
When the user asks you to count, use the count_to tool. \
Always acknowledge any follow-up messages from the user.",
)
.add_tool(SlowCounter);
let tx_clone = agent.interrupt_sender();
tokio::spawn(async move {
sleep(Duration::from_millis(500)).await;
println!("\n[user injects] \"Actually, please also tell me the square of that number.\"\n");
tx_clone
.send("Actually, please also tell me the square of that number.".into())
.expect("channel closed unexpectedly");
});
println!("Asking the agent to count to 5 (tool takes ~1 s)…\n");
let mut stream = agent.chat("Please count to 5 using the count_to tool.");
while let Some(event) = stream.next().await {
match event {
Err(e) => {
eprintln!("\nError: {e}");
break;
}
Ok(AgentEvent::Token(fragment)) => {
print!("{fragment}");
std::io::stdout().flush().ok();
}
Ok(AgentEvent::ToolCall(c)) => {
if c.delta.is_empty() {
println!("\n[calling {}]", c.name);
}
}
Ok(AgentEvent::ToolResult(r)) => {
println!("\n[tool result] {} -> {}", r.name, r.result);
println!("\n(injected message will be picked up before the next API turn)\n");
}
Ok(_) => todo!(),
}
}
println!("\n\n--- conversation complete ---");
if let Some(recovered) = stream.into_agent() {
let history = recovered.history();
println!("\nFinal history ({} messages):", history.len());
for msg in history {
let role = format!("{:?}", msg.role).to_lowercase();
let preview = msg
.content
.as_deref()
.unwrap_or("<no content>")
.chars()
.take(80)
.collect::<String>();
println!(" [{role}] {preview}");
}
}
Ok(())
}