Skip to main content

mesh_api/
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}
15
16#[derive(Clone, Debug)]
17pub struct ClientConfig {
18    pub owner_keypair: OwnerKeypair,
19    pub invite_token: InviteToken,
20    pub user_agent: String,
21    pub connect_timeout: Duration,
22}
23
24pub struct ClientBuilder {
25    config: ClientConfig,
26}
27
28impl ClientBuilder {
29    pub fn new(owner_keypair: OwnerKeypair, invite_token: InviteToken) -> Self {
30        Self {
31            config: ClientConfig {
32                owner_keypair,
33                invite_token,
34                user_agent: format!("mesh-api/{}", env!("CARGO_PKG_VERSION")),
35                connect_timeout: Duration::from_secs(30),
36            },
37        }
38    }
39
40    pub fn with_user_agent(mut self, ua: String) -> Self {
41        self.config.user_agent = ua;
42        self
43    }
44
45    pub fn with_connect_timeout(mut self, d: Duration) -> Self {
46        self.config.connect_timeout = d;
47        self
48    }
49
50    pub fn build(self) -> Result<MeshClient, MeshApiError> {
51        let inner = mesh_client::ClientBuilder::new(
52            self.config.owner_keypair.into_inner(),
53            self.config.invite_token.into_inner(),
54        )
55        .with_user_agent(self.config.user_agent.clone())
56        .with_connect_timeout(self.config.connect_timeout)
57        .build()?;
58
59        Ok(MeshClient { inner })
60    }
61}
62
63pub struct MeshClient {
64    inner: mesh_client::MeshClient,
65}
66
67impl MeshClient {
68    pub async fn join(&mut self) -> Result<(), MeshApiError> {
69        self.inner.join().await?;
70        Ok(())
71    }
72
73    pub async fn list_models(&self) -> Result<Vec<Model>, MeshApiError> {
74        Ok(self
75            .inner
76            .list_models()
77            .await?
78            .into_iter()
79            .map(Model::from)
80            .collect())
81    }
82
83    pub fn chat(&self, request: ChatRequest, listener: Arc<dyn EventListener>) -> RequestId {
84        let request_id = self.inner.chat(
85            mesh_client::ChatRequest::from(request),
86            Arc::new(EventListenerAdapter { inner: listener }),
87        );
88        RequestId(request_id.0)
89    }
90
91    pub fn responses(
92        &self,
93        request: ResponsesRequest,
94        listener: Arc<dyn EventListener>,
95    ) -> RequestId {
96        let request_id = self.inner.responses(
97            mesh_client::ResponsesRequest::from(request),
98            Arc::new(EventListenerAdapter { inner: listener }),
99        );
100        RequestId(request_id.0)
101    }
102
103    pub fn cancel(&self, request_id: RequestId) {
104        self.inner.cancel(mesh_client::RequestId(request_id.0));
105    }
106
107    pub async fn status(&self) -> Status {
108        Status::from(self.inner.status().await)
109    }
110
111    pub async fn disconnect(&mut self) {
112        self.inner.disconnect().await;
113    }
114
115    pub async fn reconnect(&mut self) -> Result<(), MeshApiError> {
116        self.inner.reconnect().await?;
117        Ok(())
118    }
119}
120
121#[derive(Clone, Debug)]
122pub struct ChatRequest {
123    pub model: String,
124    pub messages: Vec<ChatMessage>,
125}
126
127impl From<ChatRequest> for mesh_client::ChatRequest {
128    fn from(value: ChatRequest) -> Self {
129        Self {
130            model: value.model,
131            messages: value.messages.into_iter().map(Into::into).collect(),
132        }
133    }
134}
135
136#[derive(Clone, Debug)]
137pub struct ChatMessage {
138    pub role: String,
139    pub content: String,
140}
141
142impl From<ChatMessage> for mesh_client::ChatMessage {
143    fn from(value: ChatMessage) -> Self {
144        Self {
145            role: value.role,
146            content: value.content,
147        }
148    }
149}
150
151#[derive(Clone, Debug)]
152pub struct ResponsesRequest {
153    pub model: String,
154    pub input: String,
155}
156
157impl From<ResponsesRequest> for mesh_client::ResponsesRequest {
158    fn from(value: ResponsesRequest) -> Self {
159        Self {
160            model: value.model,
161            input: value.input,
162        }
163    }
164}
165
166#[derive(Debug, Clone)]
167pub struct Model {
168    pub id: String,
169    pub name: String,
170}
171
172impl From<mesh_client::Model> for Model {
173    fn from(value: mesh_client::Model) -> Self {
174        Self {
175            id: value.id,
176            name: value.name,
177        }
178    }
179}
180
181pub struct Status {
182    pub connected: bool,
183    pub peer_count: usize,
184}
185
186impl From<mesh_client::Status> for Status {
187    fn from(value: mesh_client::Status) -> Self {
188        Self {
189            connected: value.connected,
190            peer_count: value.peer_count,
191        }
192    }
193}
194
195pub struct RequestId(pub String);
196
197impl RequestId {
198    pub fn new() -> Self {
199        Self(mesh_client::RequestId::new().0)
200    }
201}
202
203impl Default for RequestId {
204    fn default() -> Self {
205        Self::new()
206    }
207}
208
209struct EventListenerAdapter {
210    inner: Arc<dyn EventListener>,
211}
212
213impl mesh_client::events::EventListener for EventListenerAdapter {
214    fn on_event(&self, event: mesh_client::events::Event) {
215        self.inner.on_event(match event {
216            mesh_client::events::Event::Connecting => Event::Connecting,
217            mesh_client::events::Event::Joined { node_id } => Event::Joined { node_id },
218            mesh_client::events::Event::ModelsUpdated { models } => Event::ModelsUpdated {
219                models: models.into_iter().map(Model::from).collect(),
220            },
221            mesh_client::events::Event::TokenDelta { request_id, delta } => {
222                Event::TokenDelta { request_id, delta }
223            }
224            mesh_client::events::Event::Completed { request_id } => Event::Completed { request_id },
225            mesh_client::events::Event::Failed { request_id, error } => {
226                Event::Failed { request_id, error }
227            }
228            mesh_client::events::Event::Disconnected { reason } => Event::Disconnected { reason },
229        });
230    }
231}