Switch Provider

All chat models implement the BaseChatModel trait, so switching providers requires only changing the constructor.

Using trait objects

use synwire_core::language_models::BaseChatModel;
use synwire_core::messages::Message;

async fn ask(
    model: &dyn BaseChatModel,
    question: &str,
) -> Result<String, synwire_core::error::SynwireError> {
    let messages = vec![Message::human(question)];
    let result = model.invoke(&messages, None).await?;
    Ok(result.message.content().as_text())
}

Selecting at runtime

use synwire_core::language_models::{FakeChatModel, BaseChatModel};
use synwire_llm_openai::ChatOpenAI;
use synwire_llm_ollama::ChatOllama;

fn create_model(provider: &str) -> Result<Box<dyn BaseChatModel>, Box<dyn std::error::Error>> {
    match provider {
        "openai" => Ok(Box::new(
            ChatOpenAI::builder()
                .model("gpt-4o-mini")
                .api_key_env("OPENAI_API_KEY")
                .build()?
        )),
        "ollama" => Ok(Box::new(
            ChatOllama::builder()
                .model("llama3.2")
                .build()?
        )),
        "fake" => Ok(Box::new(
            FakeChatModel::new(vec!["Fake response".into()])
        )),
        _ => Err("Unknown provider".into()),
    }
}

Feature flags for optional providers

Use Cargo feature flags to conditionally compile providers:

[dependencies]
synwire = "0.1"

[features]
default = []
openai = ["synwire/openai"]
ollama = ["synwire/ollama"]

Testing with FakeChatModel

Replace any real provider with FakeChatModel in tests:

#[cfg(test)]
mod tests {
    use synwire_core::language_models::{FakeChatModel, BaseChatModel};

    #[tokio::test]
    async fn test_my_agent() {
        let model: Box<dyn BaseChatModel> = Box::new(
            FakeChatModel::new(vec!["Test response".into()])
        );
        // Use model in your agent...
    }
}