codex-cli-sdk 0.0.1

Rust SDK for the OpenAI Codex CLI
Documentation
//! Cancellation — abort a streaming turn mid-flight.
//!
//! A `CancellationToken` is passed via `TurnOptions`. When cancelled, the
//! stream ends cleanly — no panic, no partial output corruption.
//!
//! This example cancels after 3 seconds to demonstrate the pattern.
//! In real use you'd cancel in response to a user signal or timeout.
//!
//! ```bash
//! cargo run --example 08_cancellation
//! ```

use codex_cli_sdk::config::TurnOptions;
use codex_cli_sdk::{Codex, CodexConfig, ThreadEvent, ThreadItem, ThreadOptions};
use tokio_stream::StreamExt;
use tokio_util::sync::CancellationToken;

#[tokio::main]
async fn main() -> codex_cli_sdk::Result<()> {
    let codex = Codex::new(CodexConfig::default())?;
    let mut thread = codex.start_thread(ThreadOptions::default());

    let cancel = CancellationToken::new();
    let turn_opts = TurnOptions {
        cancel: Some(cancel.clone()),
        ..Default::default()
    };

    // Cancel after 3 seconds
    let cancel_trigger = cancel.clone();
    tokio::spawn(async move {
        tokio::time::sleep(std::time::Duration::from_secs(3)).await;
        println!("\n[cancelling...]");
        cancel_trigger.cancel();
    });

    println!("Streaming (will cancel after 3s)...\n");

    let mut stream = thread
        .run_streamed(
            "Write a detailed explanation of every sorting algorithm, with examples in Rust",
            turn_opts,
        )
        .await?;

    let mut text_received = false;

    loop {
        match stream.next().await {
            Some(Ok(ThreadEvent::ItemUpdated {
                item: ThreadItem::AgentMessage { text, .. },
            })) => {
                print!("{text}");
                text_received = true;
            }
            Some(Ok(ThreadEvent::TurnCompleted { usage })) => {
                println!(
                    "\n\nCompleted normally. Tokens: {} in / {} out",
                    usage.input_tokens, usage.output_tokens
                );
                break;
            }
            Some(Ok(ThreadEvent::TurnFailed { error })) => {
                // A cancelled turn surfaces here as a failure
                eprintln!("\n\nTurn stopped: {}", error.message);
                break;
            }
            Some(Ok(_)) => {}
            Some(Err(e)) => {
                // Cancellation may also surface as a transport error
                eprintln!("\n\nStream ended: {e}");
                break;
            }
            None => {
                if text_received {
                    println!("\n\n[stream closed]");
                }
                break;
            }
        }
    }

    Ok(())
}