iron-core 0.1.39

Core AgentIron loop, session state, and tool registry
# Model Switching Examples

This guide shows common model switching patterns with `iron-core`.

## Basic Model Switch

Switch from the current model to GPT-4o mid-conversation:

```rust
use iron_core::{IronAgent, PromptEvent, ModelSwitchRequest};

let agent = IronAgent::new(config, provider);
let connection = agent.connect();
let session = connection.create_session()?;

// Start a conversation
let (handle, mut events) = session.prompt_stream("Explain quantum computing.");
// ... consume events ...

// Switch to a different model for the next prompt
let request = ModelSwitchRequest::Managed {
    provider_slug: "openai".into(),
    model: "gpt-4o".into(),
};
if let Err(e) = session.switch_model(request) {
    eprintln!("Failed to switch model: {}", e);
}

// Continue the same session with the new model
let (handle, mut events) = session.prompt_stream("Now explain it to a 5-year-old.");
```

## Handling Switch Events

Listen for switch events in your event loop:

```rust
use iron_core::PromptEvent;

while let Some(event) = events.next().await {
    match event {
        PromptEvent::Output { text } => print!("{}", text),
        
        PromptEvent::ModelSwitchPending { target_model, target_provider } => {
            println!("\n[Switch queued: will use {} after this turn]", target_model);
        }
        
        PromptEvent::ModelSwitched { from_model, to_model, adapted, capability_diff } => {
            println!("\n[Switched from {} to {}]", from_model, to_model);
            if adapted {
                println!("[Context was adapted for the new model]");
            }
            if let Some(diff) = capability_diff {
                if !diff.hidden_tools.is_empty() {
                    println!("[Hidden tools: {}]", diff.hidden_tools.join(", "));
                }
            }
        }
        
        PromptEvent::Complete { outcome } => break,
        _ => {}
    }
}
```

## Switching During Active Turns

When a turn is active, the switch is queued and applied at the boundary:

```rust
// Start a long-running generation
let (handle, mut events) = session.prompt_stream("Write a novel chapter...");

// While it's generating, queue a switch for the next turn
let request = ModelSwitchRequest::Managed {
    provider_slug: "anthropic".into(),
    model: "claude-opus-4".into(),
};
session.switch_model(request)?; // Returns Ok(()) immediately, queues the switch

// The current generation continues with the old model
// The switch applies when this turn completes
```

## Provider Change with Context Adaptation

Switching to a smaller context window triggers automatic compaction:

```rust
// Session has been running with a large-context model
let request = ModelSwitchRequest::Managed {
    provider_slug: "google".into(),
    model: "gemini-2.0-flash".into(), // 32K window vs 200K
};
match session.switch_model(request) {
    Ok(()) => {
        // Context was compacted automatically
        println!("Switch succeeded with context adaptation");
    }
    Err(e) => {
        // Context couldn't fit even after compaction
        eprintln!("Switch failed: {}", e);
    }
}
```

## Unmanaged Provider Switch

Use a custom provider that you manage yourself:

```rust
use iron_core::ModelSwitchRequest;
use iron_providers::{Provider, ProviderProfile, ApiFamily};

// Your custom provider implementation
let my_provider = MyCustomProvider::new();

let request = ModelSwitchRequest::Unmanaged {
    provider: Box::new(my_provider),
};
session.switch_model(request)?;
```

## Checking Model History

Inspect the session's model switch history:

```rust
// After several switches
let durable = session.durable();
let history = &durable.model_switch_history;
for record in history {
    println!("{} -> {} at index {}", 
        record.from_model, 
        record.to_model, 
        record.timeline_index
    );
}
```

## Conditional Switch Based on Capability

Check capabilities before switching:

```rust
use iron_core::capability::{ModelCapabilityRegistry, compare_capabilities};

let registry = ModelCapabilityRegistry::default();
let source = registry.get("openai", "gpt-4o").unwrap();
let target = registry.get("google", "gemini-2.0-flash").unwrap();

let diff = compare_capabilities(source, target);

if diff.window_shrink > 0.5 {
    println!("Warning: context window will shrink by {}%", 
        (diff.window_shrink * 100.0) as u32
    );
}

if !diff.hidden_tools.is_empty() {
    println!("These tools won't be available: {:?}", diff.hidden_tools);
}

// Proceed with the switch anyway
let request = ModelSwitchRequest::Managed {
    provider_slug: "google".into(),
    model: "gemini-2.0-flash".into(),
};
session.switch_model(request)?;
```

## Complete Example: Multi-Model Session

```rust
use iron_core::{IronAgent, PromptEvent, ModelSwitchRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let agent = IronAgent::new(config, provider);
    let connection = agent.connect();
    let session = connection.create_session()?;
    
    // Step 1: Use fast model for brainstorming
    let (handle, mut events) = session.prompt_stream(
        "Generate 10 ideas for a Rust CLI tool."
    );
    while let Some(event) = events.next().await {
        if let PromptEvent::Complete { .. } = event { break; }
    }
    
    // Step 2: Switch to reasoning model for implementation
    session.switch_model(ModelSwitchRequest::Managed {
        provider_slug: "anthropic".into(),
        model: "claude-opus-4".into(),
    })?;
    
    let (handle, mut events) = session.prompt_stream(
        "Implement the best idea from the list above."
    );
    while let Some(event) = events.next().await {
        match event {
            PromptEvent::Output { text } => print!("{}", text),
            PromptEvent::ModelSwitched { from_model, to_model, .. } => {
                println!("\n[Switched {} -> {}]", from_model, to_model);
            }
            PromptEvent::Complete { .. } => break,
            _ => {}
        }
    }
    
    Ok(())
}
```