Skip to main content

daimon_core/
model.rs

1//! The [`Model`] trait — the plugin interface for LLM providers.
2
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use crate::error::Result;
8use crate::stream::ResponseStream;
9use crate::types::{ChatRequest, ChatResponse};
10
11/// Trait for LLM providers. Supports both synchronous and streaming generation.
12///
13/// Implement this trait to add a new model provider to Daimon. The trait is
14/// object-safe via [`ErasedModel`], so providers can be stored behind
15/// `Arc<dyn ErasedModel>`.
16pub trait Model: Send + Sync {
17    /// Generates a complete response for the given request.
18    fn generate(&self, request: &ChatRequest) -> impl Future<Output = Result<ChatResponse>> + Send;
19
20    /// Returns a stream of response chunks for token-by-token output.
21    fn generate_stream(
22        &self,
23        request: &ChatRequest,
24    ) -> impl Future<Output = Result<ResponseStream>> + Send;
25
26    /// The provider-side model identifier (e.g. `"claude-sonnet-5"`), used
27    /// for cost attribution. Defaults to `"default"` so existing
28    /// implementations keep compiling; providers should override it with the
29    /// configured model name.
30    fn model_id(&self) -> &str {
31        "default"
32    }
33}
34
35/// Shared ownership of a model via `Arc<dyn ErasedModel>`.
36pub type SharedModel = Arc<dyn ErasedModel>;
37
38/// Object-safe wrapper for [`Model`], enabling dynamic dispatch via `Arc<dyn ErasedModel>`.
39pub trait ErasedModel: Send + Sync {
40    /// Object-safe version of [`Model::generate`].
41    fn generate_erased<'a>(
42        &'a self,
43        request: &'a ChatRequest,
44    ) -> Pin<Box<dyn Future<Output = Result<ChatResponse>> + Send + 'a>>;
45
46    /// Object-safe version of [`Model::generate_stream`].
47    fn generate_stream_erased<'a>(
48        &'a self,
49        request: &'a ChatRequest,
50    ) -> Pin<Box<dyn Future<Output = Result<ResponseStream>> + Send + 'a>>;
51
52    /// Object-safe version of [`Model::model_id`].
53    fn model_id_erased(&self) -> &str;
54}
55
56impl<T: Model> ErasedModel for T {
57    fn generate_erased<'a>(
58        &'a self,
59        request: &'a ChatRequest,
60    ) -> Pin<Box<dyn Future<Output = Result<ChatResponse>> + Send + 'a>> {
61        Box::pin(self.generate(request))
62    }
63
64    fn generate_stream_erased<'a>(
65        &'a self,
66        request: &'a ChatRequest,
67    ) -> Pin<Box<dyn Future<Output = Result<ResponseStream>> + Send + 'a>> {
68        Box::pin(self.generate_stream(request))
69    }
70
71    fn model_id_erased(&self) -> &str {
72        self.model_id()
73    }
74}