Skip to main content

synapto_interface/
llm.rs

1pub use genai;
2
3pub trait LLMSafe {}
4
5impl<T: LLMSafe> LLMSafe for Vec<T> {}
6impl<T: LLMSafe> LLMSafe for Option<T> {}
7impl<T: LLMSafe> LLMSafe for Box<T> {}
8impl<T: LLMSafe> LLMSafe for &T {}
9impl<T: LLMSafe> LLMSafe for [T] {}
10impl<T: LLMSafe, const N: usize> LLMSafe for [T; N] {}
11use std::collections::HashMap;
12impl<K: LLMSafe, V: LLMSafe> LLMSafe for HashMap<K, V> {}
13
14pub use synapto_derive::LLMSafe;
15
16#[derive(
17    Clone,
18    Copy,
19    Debug,
20    serde::Serialize,
21    serde::Deserialize,
22    schemars::JsonSchema,
23    PartialEq,
24    Eq,
25    Default,
26)]
27pub enum ReasoningEffort {
28    #[default]
29    None,
30    Minimal,
31    Low,
32    Medium,
33    High,
34}
35
36#[derive(
37    Clone, Debug, serde::Serialize, serde::Deserialize, schemars::JsonSchema, PartialEq, Eq,
38)]
39pub struct ModelConfig {
40    pub model: String,
41    #[serde(default)]
42    pub reasoning_effort: ReasoningEffort,
43}
44
45#[derive(Debug, Default, Clone)]
46pub struct RawLlmOptions {
47    pub reasoning_effort: Option<genai::chat::ReasoningEffort>,
48    pub tools: Option<Vec<genai::chat::Tool>>,
49    pub resolved_tools: Option<Vec<(genai::chat::ToolCall, String)>>,
50    pub output_schema: Option<schemars::Schema>,
51    pub messages: Option<Vec<genai::chat::ChatMessage>>,
52}
53
54/// Internal raw executor contract, completely decoupled from any specific client library.
55#[doc(hidden)]
56#[async_trait::async_trait]
57pub trait RawLlmExecutor: Send + Sync + 'static {
58    async fn execute_raw(
59        &self,
60        model: &str,
61        system_prompt: &str,
62        prompt: &str,
63        options: RawLlmOptions,
64    ) -> Result<genai::chat::ChatResponse, String>;
65}
66
67#[doc(hidden)]
68#[async_trait::async_trait]
69impl<T: RawLlmExecutor + ?Sized> RawLlmExecutor for std::sync::Arc<T> {
70    async fn execute_raw(
71        &self,
72        model: &str,
73        system_prompt: &str,
74        prompt: &str,
75        options: RawLlmOptions,
76    ) -> Result<genai::chat::ChatResponse, String> {
77        (**self)
78            .execute_raw(model, system_prompt, prompt, options)
79            .await
80    }
81}
82
83/// Opaque handle to the LLM execution runtime.
84///
85/// This struct wraps the execution backend and exposes zero public execution methods.
86/// Pass this handle to `LLM::create_client` to construct a typed, structured LLM client.
87#[derive(Clone)]
88pub struct LlmExecutor {
89    backend: std::sync::Arc<dyn RawLlmExecutor>,
90}
91
92impl std::fmt::Debug for LlmExecutor {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        f.debug_struct("LlmExecutor").finish_non_exhaustive()
95    }
96}
97
98impl LlmExecutor {
99    pub fn new<B: RawLlmExecutor + 'static>(backend: B) -> Self {
100        Self {
101            backend: std::sync::Arc::new(backend),
102        }
103    }
104
105    pub fn from_arc(backend: std::sync::Arc<dyn RawLlmExecutor>) -> Self {
106        Self { backend }
107    }
108
109    #[doc(hidden)]
110    pub async fn execute_internal(
111        &self,
112        model: &str,
113        system_prompt: &str,
114        prompt: &str,
115        options: RawLlmOptions,
116    ) -> Result<genai::chat::ChatResponse, String> {
117        self.backend
118            .execute_raw(model, system_prompt, prompt, options)
119            .await
120    }
121}