Skip to main content

synwire_core/language_models/
traits.rs

1//! Language model trait definitions.
2
3use crate::BoxFuture;
4use crate::BoxStream;
5use crate::error::SynwireError;
6use crate::language_models::types::{ChatChunk, ChatResult};
7use crate::messages::Message;
8use crate::runnables::RunnableConfig;
9use crate::tools::ToolSchema;
10
11/// Base trait for chat language models.
12///
13/// All chat models must implement this trait. Methods use manual
14/// `BoxFuture` desugaring for dyn-compatibility.
15///
16/// # Cancel safety
17///
18/// The futures returned by [`invoke`](Self::invoke), [`batch`](Self::batch),
19/// and [`stream`](Self::stream) are **not cancel-safe** in general.
20/// Dropping a future mid-execution may leave the underlying HTTP connection
21/// in an indeterminate state. If you need cancellation, use
22/// [`tokio::time::timeout`] and create a fresh request on timeout.
23/// The [`BoxStream`] returned by `stream` can be safely dropped at any
24/// point; unread chunks are simply discarded.
25pub trait BaseChatModel: Send + Sync {
26    /// Invoke the model with a list of messages.
27    fn invoke<'a>(
28        &'a self,
29        messages: &'a [Message],
30        config: Option<&'a RunnableConfig>,
31    ) -> BoxFuture<'a, Result<ChatResult, SynwireError>>;
32
33    /// Invoke the model on multiple inputs concurrently.
34    fn batch<'a>(
35        &'a self,
36        inputs: &'a [Vec<Message>],
37        config: Option<&'a RunnableConfig>,
38    ) -> BoxFuture<'a, Result<Vec<ChatResult>, SynwireError>> {
39        Box::pin(async move {
40            let mut results = Vec::with_capacity(inputs.len());
41            for messages in inputs {
42                results.push(self.invoke(messages, config).await?);
43            }
44            Ok(results)
45        })
46    }
47
48    /// Stream model responses as incremental chunks.
49    fn stream<'a>(
50        &'a self,
51        messages: &'a [Message],
52        config: Option<&'a RunnableConfig>,
53    ) -> BoxFuture<'a, Result<BoxStream<'a, Result<ChatChunk, SynwireError>>, SynwireError>>;
54
55    /// Returns the model type identifier.
56    fn model_type(&self) -> &str;
57
58    /// Returns a new model instance with tools bound.
59    fn bind_tools(&self, _tools: &[ToolSchema]) -> Result<Box<dyn BaseChatModel>, SynwireError> {
60        Err(SynwireError::Prompt {
61            message: "bind_tools not supported by this model".into(),
62        })
63    }
64}
65
66/// Base trait for text completion language models.
67///
68/// For non-chat (completion-style) LLMs.
69pub trait BaseLLM: Send + Sync {
70    /// Invoke the model with a text prompt.
71    fn invoke<'a>(
72        &'a self,
73        prompt: &'a str,
74        config: Option<&'a RunnableConfig>,
75    ) -> BoxFuture<'a, Result<String, SynwireError>>;
76
77    /// Invoke on multiple prompts.
78    fn batch<'a>(
79        &'a self,
80        prompts: &'a [String],
81        config: Option<&'a RunnableConfig>,
82    ) -> BoxFuture<'a, Result<Vec<String>, SynwireError>> {
83        Box::pin(async move {
84            let mut results = Vec::with_capacity(prompts.len());
85            for prompt in prompts {
86                results.push(self.invoke(prompt, config).await?);
87            }
88            Ok(results)
89        })
90    }
91
92    /// Stream responses as incremental text chunks.
93    fn stream<'a>(
94        &'a self,
95        prompt: &'a str,
96        config: Option<&'a RunnableConfig>,
97    ) -> BoxFuture<'a, Result<BoxStream<'a, Result<String, SynwireError>>, SynwireError>>;
98
99    /// Returns the model type identifier.
100    fn model_type(&self) -> &str;
101}