2026-08-03 14:37:16 -04:00
|
|
|
//! The `Backend` trait — the wire-format-agnostic seam every model backend
|
|
|
|
|
//! (Ollama, Claude, Routstr) implements once. The loop and every tool are
|
|
|
|
|
//! written against this trait only; wire-format differences live entirely
|
|
|
|
|
//! inside each adapter.
|
|
|
|
|
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
|
|
|
|
|
use super::tools::{ChatMessage, ToolCall, ToolDef};
|
|
|
|
|
|
|
|
|
|
pub mod claude;
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub mod scripted;
|
|
|
|
|
|
|
|
|
|
pub enum BackendTurn {
|
|
|
|
|
Text(String),
|
|
|
|
|
ToolCalls(Vec<ToolCall>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
pub trait Backend: Send + Sync {
|
2026-08-05 11:34:31 -04:00
|
|
|
async fn send(
|
|
|
|
|
&self,
|
|
|
|
|
system: &str,
|
|
|
|
|
tools: &[ToolDef],
|
|
|
|
|
history: &[ChatMessage],
|
|
|
|
|
) -> Result<BackendTurn>;
|
2026-08-03 14:37:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// D-04's backend chain: local Ollama first (node data never leaves the
|
|
|
|
|
/// node when a local model is available), then Claude, then Routstr. Only
|
|
|
|
|
/// the Claude leg is implemented in this tracer — `backends/ollama.rs`
|
|
|
|
|
/// (13-10) and `backends/routstr.rs` (13-13) slot in ahead of and behind it
|
|
|
|
|
/// without changing the `Backend` trait; that is the architectural
|
|
|
|
|
/// commitment this tracer proves.
|
|
|
|
|
pub fn select_backend(data_dir: &Path) -> Box<dyn Backend> {
|
|
|
|
|
Box::new(claude::ClaudeBackend::new(data_dir.to_path_buf()))
|
|
|
|
|
}
|