1use std::collections::{HashMap, VecDeque};
4use std::io::{Read, Write};
5use std::os::unix::net::UnixStream;
6use std::time::Duration;
7
8use superfluid_proto::envelope::FrameClass;
9use superfluid_proto::handshake::{Hello, HelloAckW, LinkRole};
10use superfluid_proto::linkw::{self, msg_type};
11use superfluid_proto::{encode_frame, Frame, FrameDecoder};
12use superfluid_shm::{
13 fdpass, ControlSegment, RingRole, SharedRing, SharedRings, ShmSegment, CONTROL_BYTES, CONTROL_TAG,
14 LOGITS_RING, TOKEN_RING_IN, TOKEN_RING_OUT,
15};
16
17use crate::AgentError;
18
19pub const PROTO_VERSION: u16 = 8;
20
21pub struct WorkerClient {
22 frames: UnixStream,
23 fd_channel: UnixStream,
24 decoder: FrameDecoder<fn(u16) -> bool>,
25 seq: u64,
26 pub hello: HelloAckW,
27 rings: SharedRings,
28 bufs: HashMap<u64, ShmSegment>,
29 next_buf_id: u64,
30 pub op_done: VecDeque<linkw::StateOpDoneMsg>,
31}
32
33impl WorkerClient {
34 pub fn connect(frames: UnixStream, fd_channel: UnixStream) -> Result<WorkerClient, AgentError> {
35 frames.set_read_timeout(Some(Duration::from_millis(50)))?;
36 let mut client = WorkerClient {
37 frames,
38 fd_channel,
39 decoder: FrameDecoder::new(linkw::is_known_type as fn(u16) -> bool),
40 seq: 1,
41 hello: HelloAckW {
42 chosen_version: 0,
43 engine_bundle_hash: [0; 32],
44 capabilities: String::new(),
45 state_space_descriptors: Vec::new(),
46 registered_strategies: Vec::new(),
47 limits: linkw::LinkWLimits {
48 max_frame: 0,
49 op_window: 0,
50 ring_specs: Vec::new(),
51 },
52 kv_bits: 0,
53 },
54 rings: SharedRings {
55 token_in: SharedRing::create(TOKEN_RING_IN, 1, 32, RingRole::Writer)?,
56 token_out: SharedRing::create(TOKEN_RING_OUT, 1, 32, RingRole::Reader)?,
57 logits: SharedRing::create(LOGITS_RING, 1, 32, RingRole::Reader)?,
58 control: None,
59 },
60 bufs: HashMap::new(),
61 next_buf_id: 1,
62 op_done: VecDeque::new(),
63 };
64
65 let hello = Hello {
66 proto_versions: (PROTO_VERSION, PROTO_VERSION),
67 link_role: LinkRole::NodeAgent,
68 auth: Vec::new(),
69 };
70 let ack: HelloAckW = client.request(msg_type::HELLO, &hello, msg_type::HELLO_ACK_W)?;
71 client.hello = ack;
72
73 for spec in client.hello.limits.ring_specs.clone() {
74 let role = if spec.ring_id == TOKEN_RING_IN {
75 RingRole::Writer
76 } else {
77 RingRole::Reader
78 };
79 let ring = SharedRing::create(spec.ring_id, spec.slots, spec.slot_bytes, role)?;
80 let attach = linkw::RingAttachMsg {
81 ring_id: spec.ring_id,
82 layout: spec,
83 };
84 let payload = postcard::to_stdvec(&attach)?;
85 let sent_seq = client.send(msg_type::RING_ATTACH, 0, &payload)?;
86 fdpass::send_fd(
87 client.frames_fd_channel(),
88 ring.raw_fd(),
89 spec.ring_id as u64,
90 )?;
91 let ack_frame = client.wait_correlated(sent_seq)?;
92 let _ack: linkw::RingAckMsg = postcard::from_bytes(&ack_frame.payload)?;
93 match spec.ring_id {
94 TOKEN_RING_IN => client.rings.token_in = ring,
95 TOKEN_RING_OUT => client.rings.token_out = ring,
96 LOGITS_RING => client.rings.logits = ring,
97 _ => return Err(AgentError::Protocol("worker advertised unknown ring id")),
98 }
99 }
100
101 let control = ControlSegment::create()?;
102 let payload = postcard::to_stdvec(&linkw::ControlAttachMsg { bytes: CONTROL_BYTES as u32 })?;
103 let sent_seq = client.send(msg_type::CONTROL_ATTACH, 0, &payload)?;
104 fdpass::send_fd(client.frames_fd_channel(), control.raw_fd(), CONTROL_TAG)?;
105 let ack_frame = client.wait_correlated(sent_seq)?;
106 let _ack: linkw::ControlAckMsg = postcard::from_bytes(&ack_frame.payload)?;
107 client.rings.control = Some(control);
108 Ok(client)
109 }
110
111 pub fn yield_signal(&self) -> Option<superfluid_shm::YieldSignal> {
115 self.rings.control.as_ref()?.signal().ok()
116 }
117
118 fn frames_fd_channel(&self) -> &UnixStream {
119 &self.fd_channel
120 }
121
122 pub fn raw_frames_stream(&mut self) -> &mut UnixStream {
123 &mut self.frames
124 }
125
126 fn send(&mut self, msg_type: u16, correlation: u64, payload: &[u8]) -> Result<u64, AgentError> {
127 let class = if msg_type & 0x8000 != 0 {
128 FrameClass::Droppable
129 } else {
130 FrameClass::Required
131 };
132 let seq = self.seq;
133 let bytes = encode_frame(PROTO_VERSION, msg_type, seq, correlation, class, payload)?;
134 self.seq += 1;
135 self.frames.write_all(&bytes)?;
136 Ok(seq)
137 }
138
139 fn wait_correlated(&mut self, sent_seq: u64) -> Result<Frame, AgentError> {
140 self.wait_correlated_streaming(sent_seq, &mut |_| true)
141 }
142
143 fn wait_correlated_emitting(
144 &mut self,
145 sent_seq: u64,
146 on_emit: &mut dyn FnMut(linkw::TickEmitMsg),
147 ) -> Result<Frame, AgentError> {
148 let mut buf = [0u8; 64 * 1024];
149 loop {
150 while let Some(frame) = self.decoder.decode_next()? {
151 if self.hello.chosen_version != 0
152 && frame.proto_version != self.hello.chosen_version
153 {
154 return Err(AgentError::Protocol(
155 "frame version differs from the negotiated version",
156 ));
157 }
158 if frame.correlation_id == sent_seq {
159 return Ok(frame);
160 }
161 if frame.msg_type == msg_type::TICK_EMIT {
162 match postcard::from_bytes(&frame.payload) {
163 Ok(em) => on_emit(em),
164 Err(e) => {
165 tracing::warn!(error = %e, "dropping malformed TICK_EMIT frame")
166 }
167 }
168 continue;
169 }
170 self.note_async(frame)?;
171 }
172 match self.frames.read(&mut buf) {
173 Ok(0) => return Err(AgentError::Closed),
174 Ok(n) => self.decoder.feed(&buf[..n]),
175 Err(e)
176 if e.kind() == std::io::ErrorKind::WouldBlock
177 || e.kind() == std::io::ErrorKind::TimedOut =>
178 {
179 continue;
180 }
181 Err(e) => return Err(e.into()),
182 }
183 }
184 }
185
186 fn wait_correlated_streaming(
187 &mut self,
188 sent_seq: u64,
189 on_segment: &mut dyn FnMut(linkw::TranscribeSegmentMsg) -> bool,
190 ) -> Result<Frame, AgentError> {
191 let mut buf = [0u8; 64 * 1024];
192 let mut cancelled = false;
193 loop {
194 while let Some(frame) = self.decoder.decode_next()? {
195 if self.hello.chosen_version != 0
196 && frame.proto_version != self.hello.chosen_version
197 {
198 return Err(AgentError::Protocol(
199 "frame version differs from the negotiated version",
200 ));
201 }
202 if frame.correlation_id == sent_seq {
203 return Ok(frame);
204 }
205 if frame.msg_type == msg_type::TRANSCRIBE_SEGMENT {
206 if !on_segment(postcard::from_bytes(&frame.payload)?) && !cancelled {
207 cancelled = true;
208 let payload = postcard::to_stdvec(&linkw::TranscribeCancelMsg {})?;
209 self.send(msg_type::TRANSCRIBE_CANCEL, 0, &payload)?;
210 }
211 continue;
212 }
213 self.note_async(frame)?;
214 }
215 match self.frames.read(&mut buf) {
216 Ok(0) => return Err(AgentError::Closed),
217 Ok(n) => self.decoder.feed(&buf[..n]),
218 Err(e)
219 if e.kind() == std::io::ErrorKind::WouldBlock
220 || e.kind() == std::io::ErrorKind::TimedOut => {}
221 Err(e) => return Err(e.into()),
222 }
223 }
224 }
225
226 fn note_async(&mut self, frame: Frame) -> Result<(), AgentError> {
227 if frame.msg_type == msg_type::STATE_OP_DONE {
228 let done: linkw::StateOpDoneMsg = postcard::from_bytes(&frame.payload)?;
229 self.op_done.push_back(done);
230 }
231 Ok(())
232 }
233
234 fn request<Req: serde::Serialize, Res: serde::de::DeserializeOwned>(
235 &mut self,
236 req_type: u16,
237 req: &Req,
238 res_type: u16,
239 ) -> Result<Res, AgentError> {
240 let payload = postcard::to_stdvec(req)?;
241 let sent = self.send(req_type, 0, &payload)?;
242 let frame = self.wait_correlated(sent)?;
243 if frame.msg_type != res_type {
244 return Err(AgentError::Protocol("unexpected response type"));
245 }
246 Ok(postcard::from_bytes(&frame.payload)?)
247 }
248
249 pub fn stage_prompt(&mut self, tokens: &[u32]) -> Result<linkw::TokenRefMsg, AgentError> {
250 let r = self.rings.stage_prompt(tokens)?;
251 Ok(linkw::TokenRefMsg {
252 ring_id: r.ring_id,
253 index: r.index,
254 count: r.count,
255 generation: r.generation,
256 })
257 }
258
259 pub fn read_tokens(&self, r: &linkw::TokenRefMsg) -> Result<Vec<u32>, AgentError> {
260 use superfluid_engine::Rings;
261 self.rings
262 .read_tokens(&superfluid_abi::TokenRef {
263 ring_id: r.ring_id,
264 index: r.index,
265 count: r.count,
266 _pad0: 0,
267 generation: r.generation,
268 })
269 .map_err(|_| AgentError::Protocol("stale emitted-token reference"))
270 }
271
272 pub fn read_logits(&self, r: &linkw::RingRefMsg) -> Result<Vec<f32>, AgentError> {
273 use superfluid_engine::Rings;
274 self.rings
275 .read_logits_row(&superfluid_abi::RingRef {
276 ring_id: r.ring_id,
277 index: r.index,
278 generation: r.generation,
279 })
280 .map_err(|_| AgentError::Protocol("stale logits reference"))
281 }
282
283 pub fn tick(&mut self, plan: linkw::TickPlanMsg) -> Result<linkw::TickEventsMsg, AgentError> {
284 let res: linkw::TickResultMsg = self.request(
285 msg_type::TICK_SUBMIT,
286 &linkw::TickSubmitMsg { plan },
287 msg_type::TICK_RESULT,
288 )?;
289 match res {
290 linkw::TickResultMsg::Events(ev) => Ok(ev),
291 linkw::TickResultMsg::Rejected { status } => Err(AgentError::Rejected(status)),
292 }
293 }
294
295 pub fn tick_streaming(
296 &mut self,
297 plan: linkw::TickPlanMsg,
298 on_emit: &mut dyn FnMut(linkw::TickEmitMsg),
299 ) -> Result<linkw::TickEventsMsg, AgentError> {
300 let payload = postcard::to_stdvec(&linkw::TickSubmitMsg { plan })?;
301 let sent = self.send(msg_type::TICK_SUBMIT, 0, &payload)?;
302 let frame = self.wait_correlated_emitting(sent, on_emit)?;
303 if frame.msg_type != msg_type::TICK_RESULT {
304 return Err(AgentError::Protocol("unexpected response type"));
305 }
306 match postcard::from_bytes::<linkw::TickResultMsg>(&frame.payload)? {
307 linkw::TickResultMsg::Events(ev) => Ok(ev),
308 linkw::TickResultMsg::Rejected { status } => Err(AgentError::Rejected(status)),
309 }
310 }
311
312 pub fn match_prefix(
313 &mut self,
314 space_id: u32,
315 span: linkw::TokenRefMsg,
316 ) -> Result<linkw::MatchResMsg, AgentError> {
317 self.request(
318 msg_type::MATCH_REQ,
319 &linkw::MatchReqMsg {
320 space_id,
321 span,
322 media_deps: Vec::new(),
323 },
324 msg_type::MATCH_RES,
325 )
326 }
327
328 pub fn seed_release(&mut self, seed_handle: u64) -> Result<(), AgentError> {
329 let payload = postcard::to_stdvec(&linkw::SeedReleaseMsg { seed_handle })?;
330 self.send(msg_type::SEED_RELEASE, 0, &payload)?;
331 Ok(())
332 }
333
334 pub fn seed_acquire(
335 &mut self,
336 span: linkw::TokenRefMsg,
337 prefix_len: u64,
338 determinism_class: u8,
339 ) -> Result<u64, AgentError> {
340 self.seed_acquire_leased(span, prefix_len, determinism_class)
341 .map(|(handle, _)| handle)
342 }
343
344 pub fn seed_acquire_leased(
345 &mut self,
346 span: linkw::TokenRefMsg,
347 prefix_len: u64,
348 determinism_class: u8,
349 ) -> Result<(u64, Option<u64>), AgentError> {
350 let payload = postcard::to_stdvec(&linkw::SeedAcquireMsg {
351 span,
352 prefix_len,
353 determinism_class,
354 })?;
355 let sent = self.send(msg_type::SEED_ACQUIRE, 0, &payload)?;
356 let frame = self.wait_correlated(sent)?;
357 if frame.msg_type != msg_type::SEED_GRANT {
358 return Err(AgentError::Protocol("unexpected response type"));
359 }
360 match linkw::SeedGrantMsg::decode(&frame.payload)? {
361 linkw::SeedGrantMsg::Granted {
362 seed_handle,
363 ttl_ticks,
364 ..
365 } => Ok((seed_handle, ttl_ticks)),
366 linkw::SeedGrantMsg::Refused { status } => Err(AgentError::Rejected(status)),
367 }
368 }
369
370 pub fn buf_create(&mut self, len: u64) -> Result<u64, AgentError> {
371 let buf_id = self.next_buf_id;
372 self.next_buf_id += 1;
373 let seg = ShmSegment::create(len as usize)?;
374 let alloc = postcard::to_stdvec(&linkw::BufAllocMsg { buf_id, len })?;
375 self.send(msg_type::BUF_ALLOC, 0, &alloc)?;
376 let attach = postcard::to_stdvec(&linkw::BufAttachMsg {
377 buf_id,
378 len,
379 generation: 1,
380 })?;
381 self.send(msg_type::BUF_ATTACH, 0, &attach)?;
382 fdpass::send_fd(&self.fd_channel, seg.raw_fd(), buf_id)?;
383 self.bufs.insert(buf_id, seg);
384 Ok(buf_id)
385 }
386
387 pub fn buf_read(&self, buf_id: u64, len: usize) -> Result<Vec<u8>, AgentError> {
388 let seg = self
389 .bufs
390 .get(&buf_id)
391 .ok_or(AgentError::Protocol("unknown buffer"))?;
392 let mut out = vec![0u8; len.min(seg.len())];
393 seg.read_at(0, &mut out)?;
394 Ok(out)
395 }
396
397 pub fn buf_write(&self, buf_id: u64, data: &[u8]) -> Result<(), AgentError> {
398 let seg = self
399 .bufs
400 .get(&buf_id)
401 .ok_or(AgentError::Protocol("unknown buffer"))?;
402 seg.write_at(0, data)?;
403 Ok(())
404 }
405
406 pub fn buf_release(&mut self, buf_id: u64) -> Result<(), AgentError> {
407 let payload = postcard::to_stdvec(&linkw::BufReleaseMsg { buf_id })?;
408 self.send(msg_type::BUF_RELEASE, 0, &payload)?;
409 self.bufs.remove(&buf_id);
410 Ok(())
411 }
412
413 pub fn state_op(&mut self, op: linkw::StateOpKind) -> Result<u64, AgentError> {
414 let res: linkw::StateOpAcceptedMsg = self.request(
415 msg_type::STATE_OP_SUBMIT,
416 &linkw::StateOpSubmitMsg { op },
417 msg_type::STATE_OP_ACCEPTED,
418 )?;
419 match res {
420 linkw::StateOpAcceptedMsg::Accepted { op_handle } => Ok(op_handle),
421 linkw::StateOpAcceptedMsg::Rejected { status } => Err(AgentError::Rejected(status)),
422 }
423 }
424
425 pub fn wait_op_done(&mut self, op: u64) -> Result<linkw::StateOpDoneMsg, AgentError> {
426 loop {
427 if let Some(pos) = self.op_done.iter().position(|d| d.op_handle == op) {
428 return Ok(self.op_done.remove(pos).expect("indexed"));
429 }
430 let mut buf = [0u8; 64 * 1024];
431 while let Some(frame) = self.decoder.decode_next()? {
432 self.note_async(frame)?;
433 }
434 match self.frames.read(&mut buf) {
435 Ok(0) => return Err(AgentError::Closed),
436 Ok(n) => self.decoder.feed(&buf[..n]),
437 Err(e)
438 if e.kind() == std::io::ErrorKind::WouldBlock
439 || e.kind() == std::io::ErrorKind::TimedOut => {}
440 Err(e) => return Err(e.into()),
441 }
442 }
443 }
444
445 pub fn state_sync(
446 &mut self,
447 req: linkw::StateSyncReqMsg,
448 ) -> Result<linkw::StateSyncOkMsg, AgentError> {
449 let res: linkw::StateSyncResMsg =
450 self.request(msg_type::STATE_SYNC_REQ, &req, msg_type::STATE_SYNC_RES)?;
451 match res {
452 linkw::StateSyncResMsg::Ok(ok) => Ok(ok),
453 linkw::StateSyncResMsg::Err { status } => Err(AgentError::Rejected(status)),
454 }
455 }
456
457 pub fn send_transcribe_cancel(&mut self) -> Result<(), AgentError> {
458 let payload = postcard::to_stdvec(&linkw::TranscribeCancelMsg {})?;
459 self.send(msg_type::TRANSCRIBE_CANCEL, 0, &payload)?;
460 Ok(())
461 }
462
463 pub fn state_sync_streaming(
464 &mut self,
465 req: linkw::StateSyncReqMsg,
466 on_segment: &mut dyn FnMut(linkw::TranscribeSegmentMsg) -> bool,
467 ) -> Result<linkw::StateSyncOkMsg, AgentError> {
468 let payload = postcard::to_stdvec(&req)?;
469 let sent = self.send(msg_type::STATE_SYNC_REQ, 0, &payload)?;
470 let frame = self.wait_correlated_streaming(sent, on_segment)?;
471 if frame.msg_type != msg_type::STATE_SYNC_RES {
472 return Err(AgentError::Protocol("unexpected response type"));
473 }
474 match postcard::from_bytes::<linkw::StateSyncResMsg>(&frame.payload)? {
475 linkw::StateSyncResMsg::Ok(ok) => Ok(ok),
476 linkw::StateSyncResMsg::Err { status } => Err(AgentError::Rejected(status)),
477 }
478 }
479
480 pub fn register_strategy(
481 &mut self,
482 reg: linkw::StrategyRegisterMsg,
483 ) -> Result<linkw::StrategyGrantOkMsg, AgentError> {
484 let res: linkw::StrategyGrantMsg =
485 self.request(msg_type::STRATEGY_REGISTER, ®, msg_type::STRATEGY_GRANT)?;
486 match res {
487 linkw::StrategyGrantMsg::Granted(ok) => Ok(ok),
488 linkw::StrategyGrantMsg::Refused { status } => Err(AgentError::Rejected(status)),
489 }
490 }
491
492 pub fn ping(&mut self) -> Result<linkw::PongMsg, AgentError> {
493 let ping = linkw::PingMsg {
494 queue_depth: 0,
495 mem: linkw::MemCountersMsg {
496 allocated_bytes: 0,
497 host_retained_bytes: 0,
498 pool_blocks_total: 0,
499 pool_blocks_used: 0,
500 pool_bytes_evictable: 0,
501 tentative_bytes: 0,
502 },
503 };
504 self.request(msg_type::PING, &ping, msg_type::PONG)
505 }
506
507 pub fn drain(&mut self) -> Result<(), AgentError> {
508 let _: linkw::DrainedMsg =
509 self.request(msg_type::DRAIN_REQ, &linkw::DrainReqMsg, msg_type::DRAINED)?;
510 Ok(())
511 }
512}