netget/server/
peer_support.rs1use std::sync::Arc;
30
31use tokio::io::{AsyncWrite, AsyncWriteExt};
32use tokio::sync::{mpsc, Mutex};
33use tracing::{debug, warn};
34
35use crate::llm::actions::protocol_trait::{ActionResult, Server};
36use crate::state::client_handles::{
37 ClientCommand, ClientHandle, ClientSendOutcome, CLIENT_COMMAND_CAPACITY,
38};
39use crate::state::{AccessLogOwner, AppState, ServerId};
40
41pub async fn register_peer_channel(
43 state: &AppState,
44 server_id: ServerId,
45 connection_id: u32,
46) -> mpsc::Receiver<ClientCommand> {
47 let (command_tx, command_rx) = mpsc::channel(CLIENT_COMMAND_CAPACITY);
48 state
49 .register_peer_handle(server_id, connection_id, ClientHandle { command_tx })
50 .await;
51 command_rx
52}
53
54pub fn spawn_peer_command_task<W>(
62 mut command_rx: mpsc::Receiver<ClientCommand>,
63 protocol: Arc<dyn Server>,
64 state: Arc<AppState>,
65 server_id: ServerId,
66 connection_id: u32,
67 write_half: Arc<Mutex<W>>,
68 status_tx: mpsc::UnboundedSender<String>,
69) where
70 W: AsyncWrite + Unpin + Send + 'static,
71{
72 let task_registrar = state.clone();
73 let worker = tokio::spawn(async move {
74 while let Some(command) = command_rx.recv().await {
75 handle_peer_command(
76 protocol.as_ref(),
77 &state,
78 server_id,
79 connection_id,
80 &write_half,
81 command,
82 &status_tx,
83 )
84 .await;
85 }
86 debug!(
87 "peer command task for server #{} connection #{} ended",
88 server_id.as_u32(),
89 connection_id
90 );
91 });
92 tokio::spawn(async move {
96 task_registrar
97 .register_peer_task(server_id, connection_id, worker)
98 .await;
99 });
100}
101
102async fn handle_peer_command<W>(
104 protocol: &dyn Server,
105 state: &AppState,
106 server_id: ServerId,
107 connection_id: u32,
108 write_half: &Arc<Mutex<W>>,
109 command: ClientCommand,
110 status_tx: &mpsc::UnboundedSender<String>,
111) where
112 W: AsyncWrite + Unpin,
113{
114 let action = command.action.clone();
115 let outcome = execute_peer_action(protocol, state, server_id, write_half, &action).await;
116
117 let outcome_json = match &outcome {
120 Ok(outcome) => serde_json::to_value(outcome).unwrap_or(serde_json::Value::Null),
121 Err(e) => serde_json::json!({"error": e.to_string()}),
122 };
123 state
124 .record_access_log(
125 AccessLogOwner::Server(server_id.as_u32()),
126 protocol.protocol_name(),
127 Some(connection_id),
128 "injected_action",
129 action,
130 vec![outcome_json],
131 )
132 .await;
133
134 match &outcome {
135 Err(e) => warn!(
136 "injected action on server #{} connection #{} failed: {}",
137 server_id.as_u32(),
138 connection_id,
139 e
140 ),
141 Ok(ClientSendOutcome::Disconnected) => {
148 state.remove_peer_handle(server_id, connection_id).await;
149 state
150 .close_connection_on_server(
151 server_id,
152 crate::server::connection::ConnectionId::new(connection_id),
153 )
154 .await;
155 }
156 Ok(ClientSendOutcome::Sent { bytes_sent }) => {
160 state
161 .update_connection_stats(
162 server_id,
163 crate::server::connection::ConnectionId::new(connection_id),
164 None,
165 Some(*bytes_sent as u64),
166 None,
167 Some(1),
168 )
169 .await;
170 }
171 Ok(_) => {}
172 }
173 let _ = status_tx.send("__UPDATE_UI__".to_string());
174 let _ = command.reply_tx.send(outcome);
175}
176
177async fn execute_peer_action<W>(
179 protocol: &dyn Server,
180 state: &AppState,
181 server_id: ServerId,
182 write_half: &Arc<Mutex<W>>,
183 action: &serde_json::Value,
184) -> anyhow::Result<ClientSendOutcome>
185where
186 W: AsyncWrite + Unpin,
187{
188 let result = crate::llm::actions::executor::execute_actions(
189 vec![action.clone()],
190 state,
191 Some(protocol),
192 Some(server_id),
193 None,
194 )
195 .await?;
196
197 let mut bytes_sent = 0usize;
198 let mut closed = false;
199 let mut details: Vec<String> = Vec::new();
200 let mut stack: Vec<ActionResult> = result.protocol_results;
201 stack.reverse();
202 while let Some(item) = stack.pop() {
203 match item {
204 ActionResult::Output(bytes) => {
205 let mut write = write_half.lock().await;
206 write.write_all(&bytes).await?;
207 write.flush().await?;
208 bytes_sent += bytes.len();
209 }
210 ActionResult::Multiple(items) => {
211 for inner in items.into_iter().rev() {
212 stack.push(inner);
213 }
214 }
215 ActionResult::CloseConnection => {
220 let mut write = write_half.lock().await;
221 write.shutdown().await?;
222 closed = true;
223 }
224 ActionResult::WaitForMore | ActionResult::NoAction => {}
225 other => details.push(format!("{other:?}")),
226 }
227 }
228
229 if closed {
230 Ok(ClientSendOutcome::Disconnected)
231 } else if bytes_sent > 0 {
232 Ok(ClientSendOutcome::Sent { bytes_sent })
233 } else if details.is_empty() {
234 Ok(ClientSendOutcome::Executed {
235 detail: "executed (nothing to write)".to_string(),
236 })
237 } else {
238 Ok(ClientSendOutcome::Executed {
239 detail: crate::utils::truncate_for_log(&details.join("; "), 160),
240 })
241 }
242}