Skip to main content

mesh_llm_api_client/
client.rs

1use crate::events::{Event, EventListener};
2use crate::{InviteToken, OwnerKeypair};
3use mesh_client::ClientError;
4use std::sync::Arc;
5use std::time::Duration;
6use thiserror::Error;
7
8pub const MAX_RECONNECT_ATTEMPTS: u32 = mesh_client::client::builder::MAX_RECONNECT_ATTEMPTS;
9
10#[derive(Debug, Error)]
11pub enum MeshApiError {
12    #[error(transparent)]
13    Client(#[from] ClientError),
14    #[error("public mesh discovery failed: {message}")]
15    Discovery { message: String },
16    #[error("no public mesh matched the requested criteria")]
17    NoPublicMeshFound,
18    #[error("invalid invite token: {message}")]
19    InvalidInviteToken { message: String },
20    #[error("invalid Mesh SDK configuration: {message}")]
21    InvalidConfig { message: &'static str },
22    #[error("model management failed: {message}")]
23    ModelManagement { message: String },
24    #[error("serving failed: {message}")]
25    Serving { message: String },
26    #[error("{feature} is not implemented in the Mesh SDK yet")]
27    Unsupported { feature: &'static str },
28}
29
30#[derive(Clone, Debug)]
31pub struct ClientConfig {
32    pub owner_keypair: OwnerKeypair,
33    pub invite_token: InviteToken,
34    pub user_agent: String,
35    pub connect_timeout: Duration,
36}
37
38pub struct ClientBuilder {
39    config: ClientConfig,
40}
41
42impl ClientBuilder {
43    pub fn new(owner_keypair: OwnerKeypair, invite_token: InviteToken) -> Self {
44        Self {
45            config: ClientConfig {
46                owner_keypair,
47                invite_token,
48                user_agent: format!("mesh-llm-api-client/{}", env!("CARGO_PKG_VERSION")),
49                connect_timeout: Duration::from_secs(30),
50            },
51        }
52    }
53
54    pub fn with_user_agent(mut self, ua: String) -> Self {
55        self.config.user_agent = ua;
56        self
57    }
58
59    pub fn with_connect_timeout(mut self, d: Duration) -> Self {
60        self.config.connect_timeout = d;
61        self
62    }
63
64    pub fn build(self) -> Result<MeshClient, MeshApiError> {
65        let inner = mesh_client::ClientBuilder::new(
66            self.config.owner_keypair.into_inner(),
67            self.config.invite_token.into_inner(),
68        )
69        .with_user_agent(self.config.user_agent.clone())
70        .with_connect_timeout(self.config.connect_timeout)
71        .build()?;
72
73        Ok(MeshClient { inner })
74    }
75}
76
77pub struct MeshClient {
78    inner: mesh_client::MeshClient,
79}
80
81impl MeshClient {
82    pub async fn join(&mut self) -> Result<(), MeshApiError> {
83        self.inner.join().await?;
84        Ok(())
85    }
86
87    pub async fn list_models(&self) -> Result<Vec<Model>, MeshApiError> {
88        Ok(self
89            .inner
90            .list_models()
91            .await?
92            .into_iter()
93            .map(Model::from)
94            .collect())
95    }
96
97    pub fn chat(&self, request: ChatRequest, listener: Arc<dyn EventListener>) -> RequestId {
98        let request_id = self.inner.chat(
99            mesh_client::ChatRequest::from(request),
100            Arc::new(EventListenerAdapter { inner: listener }),
101        );
102        RequestId(request_id.0)
103    }
104
105    pub fn responses(
106        &self,
107        request: ResponsesRequest,
108        listener: Arc<dyn EventListener>,
109    ) -> RequestId {
110        let request_id = self.inner.responses(
111            mesh_client::ResponsesRequest::from(request),
112            Arc::new(EventListenerAdapter { inner: listener }),
113        );
114        RequestId(request_id.0)
115    }
116
117    pub fn cancel(&self, request_id: RequestId) {
118        self.inner.cancel(mesh_client::RequestId(request_id.0));
119    }
120
121    pub async fn status(&self) -> Status {
122        Status::from(self.inner.status().await)
123    }
124
125    pub async fn disconnect(&mut self) {
126        self.inner.disconnect().await;
127    }
128
129    pub async fn reconnect(&mut self) -> Result<(), MeshApiError> {
130        self.inner.reconnect().await?;
131        Ok(())
132    }
133
134    pub fn add_event_listener(&self, listener: Arc<dyn EventListener>) -> String {
135        self.inner
136            .add_event_listener(Arc::new(EventListenerAdapter { inner: listener }))
137    }
138
139    pub fn remove_event_listener(&self, listener_id: &str) {
140        self.inner.remove_event_listener(listener_id);
141    }
142}
143
144#[derive(Clone, Debug)]
145pub struct ChatRequest {
146    pub model: String,
147    pub messages: Vec<ChatMessage>,
148}
149
150impl From<ChatRequest> for mesh_client::ChatRequest {
151    fn from(value: ChatRequest) -> Self {
152        Self {
153            model: value.model,
154            messages: value.messages.into_iter().map(Into::into).collect(),
155        }
156    }
157}
158
159#[derive(Clone, Debug)]
160pub struct ChatMessage {
161    pub role: String,
162    pub content: String,
163}
164
165impl From<ChatMessage> for mesh_client::ChatMessage {
166    fn from(value: ChatMessage) -> Self {
167        Self {
168            role: value.role,
169            content: value.content,
170        }
171    }
172}
173
174#[derive(Clone, Debug)]
175pub struct ResponsesRequest {
176    pub model: String,
177    pub input: String,
178}
179
180impl From<ResponsesRequest> for mesh_client::ResponsesRequest {
181    fn from(value: ResponsesRequest) -> Self {
182        Self {
183            model: value.model,
184            input: value.input,
185        }
186    }
187}
188
189#[derive(Debug, Clone)]
190pub struct Model {
191    pub id: String,
192    pub name: String,
193}
194
195impl From<mesh_client::Model> for Model {
196    fn from(value: mesh_client::Model) -> Self {
197        Self {
198            id: value.id,
199            name: value.name,
200        }
201    }
202}
203
204pub struct Status {
205    pub connected: bool,
206    pub peer_count: usize,
207}
208
209impl From<mesh_client::Status> for Status {
210    fn from(value: mesh_client::Status) -> Self {
211        Self {
212            connected: value.connected,
213            peer_count: value.peer_count,
214        }
215    }
216}
217
218pub struct RequestId(pub String);
219
220impl RequestId {
221    pub fn new() -> Self {
222        Self(mesh_client::RequestId::new().0)
223    }
224}
225
226impl Default for RequestId {
227    fn default() -> Self {
228        Self::new()
229    }
230}
231
232struct EventListenerAdapter {
233    inner: Arc<dyn EventListener>,
234}
235
236impl mesh_client::events::EventListener for EventListenerAdapter {
237    fn on_event(&self, event: mesh_client::events::Event) {
238        self.inner.on_event(match event {
239            mesh_client::events::Event::Connecting => Event::Connecting,
240            mesh_client::events::Event::Joined { node_id } => Event::Joined { node_id },
241            mesh_client::events::Event::ModelsUpdated { models } => Event::ModelsUpdated {
242                models: models.into_iter().map(Model::from).collect(),
243            },
244            mesh_client::events::Event::TokenDelta { request_id, delta } => {
245                Event::TokenDelta { request_id, delta }
246            }
247            mesh_client::events::Event::Completed { request_id } => Event::Completed { request_id },
248            mesh_client::events::Event::Failed { request_id, error } => {
249                Event::Failed { request_id, error }
250            }
251            mesh_client::events::Event::Disconnected { reason } => Event::Disconnected { reason },
252        });
253    }
254}