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}