Cersei

Cookbook: Embedding in Apps

Embed Cersei agents inside existing Rust applications.

Cookbook: Embedding Agents

In a Tauri Desktop App

use cersei::prelude::*;
use tauri::Manager;

#[tauri::command]
async fn ask_agent(prompt: String) -> Result<String, String> {
    let output = Agent::builder()
        .provider(Anthropic::from_env().map_err(|e| e.to_string())?)
        .tools(cersei::tools::filesystem())
        .permission_policy(AllowReadOnly)
        .max_turns(3)
        .run_with(&prompt)
        .await
        .map_err(|e| e.to_string())?;

    Ok(output.text().to_string())
}

In an Actix-web Server

use actix_web::{web, App, HttpResponse, HttpServer};
use cersei::prelude::*;
use std::sync::Arc;

struct AppState {
    provider: Arc<dyn Provider>,
}

async fn agent_endpoint(
    body: web::Json<serde_json::Value>,
    state: web::Data<AppState>,
) -> HttpResponse {
    let prompt = body["prompt"].as_str().unwrap_or("");
    let output = Agent::builder()
        .provider(Arc::clone(&state.provider))
        .tools(cersei::tools::coding())
        .permission_policy(AllowReadOnly)
        .run_with(prompt)
        .await;

    match output {
        Ok(o) => HttpResponse::Ok().json(serde_json::json!({ "response": o.text() })),
        Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ "error": e.to_string() })),
    }
}

Streaming to WebSocket

let agent = Agent::builder()
    .provider(Anthropic::from_env()?)
    .tools(cersei::tools::coding())
    .enable_broadcast(256)
    .build()?;

let mut rx = agent.subscribe().unwrap();

// Forward events to WebSocket
tokio::spawn(async move {
    while let Ok(event) = rx.recv().await {
        match event {
            AgentEvent::TextDelta(t) => ws_send(&t).await,
            AgentEvent::ToolStart { name, .. } => ws_send(&format!("[{name}]")).await,
            AgentEvent::Complete(_) => break,
            _ => {}
        }
    }
});

agent.run("Fix the tests").await?;

With Custom Memory Backend

struct PostgresMemory { pool: PgPool }

#[async_trait]
impl Memory for PostgresMemory {
    async fn store(&self, session_id: &str, messages: &[Message]) -> Result<()> {
        let json = serde_json::to_string(messages)?;
        sqlx::query("INSERT INTO sessions (id, messages) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET messages = $2")
            .bind(session_id)
            .bind(&json)
            .execute(&self.pool)
            .await?;
        Ok(())
    }

    async fn load(&self, session_id: &str) -> Result<Vec<Message>> {
        let row = sqlx::query_scalar::<_, String>("SELECT messages FROM sessions WHERE id = $1")
            .bind(session_id)
            .fetch_optional(&self.pool)
            .await?;
        match row {
            Some(json) => Ok(serde_json::from_str(&json)?),
            None => Ok(vec![]),
        }
    }

    // ... implement search, sessions, delete
}

Extension Points

WhatHow
Custom providerimpl Provider
Custom tool#[derive(Tool)] or impl Tool
Custom permissionsimpl PermissionPolicy
Custom memoryimpl Memory
Custom hooksimpl Hook
Custom reportersimpl Reporter
MCP serversMcpServerConfig
Skills.claude/commands/*.md
Graph memoryfeatures = ["graph"]

On this page