use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;
use crate::tensor::HostTensor;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct NeuronId(pub(crate) u32);
impl NeuronId {
pub(crate) const NONE: Self = Self(u32::MAX);
pub fn index(self) -> usize {
self.0 as usize
}
}
impl fmt::Display for NeuronId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "n{}", self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub(crate) struct PathwayId(pub(crate) u32);
impl PathwayId {
pub(crate) fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SignalId(pub u64);
static NEXT_SIGNAL: AtomicU64 = AtomicU64::new(1);
impl SignalId {
fn next() -> Self {
Self(NEXT_SIGNAL.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Feature {
pub kind: u16,
pub sketch: u64,
}
impl Feature {
pub const NONE: Self = Self { kind: 0, sketch: 0 };
pub const fn new(kind: u16, sketch: u64) -> Self {
Self { kind, sketch }
}
pub fn sketch(bytes: &[u8]) -> Self {
Self::new(1, sketch64(bytes))
}
pub fn sketch_as(kind: u16, bytes: &[u8]) -> Self {
Self::new(kind, sketch64(bytes))
}
pub fn with_kind(self, kind: u16) -> Self {
Self { kind, ..self }
}
pub fn is_none(self) -> bool {
self == Self::NONE
}
}
pub(crate) fn sketch64(bytes: &[u8]) -> u64 {
const K: u64 = 0x9E37_79B9_7F4A_7C15;
let mut h = (bytes.len() as u64).wrapping_mul(K) ^ 0x2545_F491_4F6C_DD1D;
let (chunks, rest) = bytes.as_chunks::<8>();
for chunk in chunks {
h = (h ^ u64::from_le_bytes(*chunk)).wrapping_mul(K);
h ^= h >> 29;
}
if !rest.is_empty() {
let mut word = [0u8; 8];
word[..rest.len()].copy_from_slice(rest);
h = (h ^ u64::from_le_bytes(word)).wrapping_mul(K);
h ^= h >> 29;
}
h = (h ^ (h >> 32)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
h ^ (h >> 31)
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Urgency {
pub deadline: Option<Instant>,
pub rate: f32,
}
impl Urgency {
pub const NONE: Self = Self {
deadline: None,
rate: 0.0,
};
pub fn within(d: Duration) -> Self {
Self {
deadline: Some(Instant::now() + d),
rate: 0.0,
}
}
pub fn at(deadline: Instant) -> Self {
Self {
deadline: Some(deadline),
rate: 0.0,
}
}
pub fn with_rate(self, rate: f32) -> Self {
Self { rate, ..self }
}
pub fn remaining(&self, now: Instant) -> Option<Duration> {
self.deadline.map(|d| d.saturating_duration_since(now))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Command {
pub effector: String,
#[serde(default)]
pub args: serde_json::Value,
}
impl Command {
pub fn new(effector: &str, args: serde_json::Value) -> Self {
Self {
effector: effector.to_string(),
args,
}
}
}
#[derive(Clone, Debug, Default)]
pub enum Payload {
#[default]
None,
Bytes(Bytes),
Text(Arc<str>),
Json(Arc<serde_json::Value>),
Tensors(Arc<Vec<HostTensor>>),
Command(Arc<Command>),
#[cfg(feature = "llm")]
Prompt(Arc<crate::llm::GenerateRequest>),
}
impl Payload {
pub fn text(s: &str) -> Self {
Self::Text(Arc::from(s))
}
pub fn bytes(b: impl Into<Bytes>) -> Self {
Self::Bytes(b.into())
}
pub fn json(v: serde_json::Value) -> Self {
Self::Json(Arc::new(v))
}
pub fn command(c: Command) -> Self {
Self::Command(Arc::new(c))
}
pub fn tensors(t: Vec<HostTensor>) -> Self {
Self::Tensors(Arc::new(t))
}
pub fn as_text(&self) -> Option<&str> {
match self {
Self::Text(s) => Some(s),
Self::Bytes(b) => std::str::from_utf8(b).ok(),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Self::Bytes(b) => Some(b),
Self::Text(s) => Some(s.as_bytes()),
_ => None,
}
}
pub fn as_command(&self) -> Option<&Command> {
match self {
Self::Command(c) => Some(c),
_ => None,
}
}
pub fn commands(&self, effector: &str) -> bool {
self.as_command().is_some_and(|c| c.effector == effector)
}
pub fn as_tensors(&self) -> Option<&[HostTensor]> {
match self {
Self::Tensors(t) => Some(t),
_ => None,
}
}
pub fn to_json(&self) -> Option<serde_json::Value> {
match self {
Self::Json(v) => Some((**v).clone()),
Self::Text(_) | Self::Bytes(_) => serde_json::from_slice(self.as_bytes()?).ok(),
Self::Command(c) => serde_json::to_value(&**c).ok(),
Self::None | Self::Tensors(_) => None,
#[cfg(feature = "llm")]
Self::Prompt(_) => None,
}
}
pub fn to_bytes(&self) -> Bytes {
match self {
Self::None => Bytes::new(),
Self::Bytes(b) => b.clone(),
Self::Text(s) => Bytes::copy_from_slice(s.as_bytes()),
Self::Json(v) => Bytes::from(v.to_string()),
Self::Command(c) => Bytes::from(serde_json::to_vec(&**c).unwrap_or_default()),
Self::Tensors(t) => Bytes::from(
t.iter()
.flat_map(|t| match &t.values {
crate::tensor::Values::F32(v) => {
v.iter().flat_map(|x| x.to_le_bytes()).collect::<Vec<u8>>()
}
crate::tensor::Values::I64(v) => {
v.iter().flat_map(|x| x.to_le_bytes()).collect()
}
})
.collect::<Vec<u8>>(),
),
#[cfg(feature = "llm")]
Self::Prompt(_) => Bytes::new(),
}
}
pub fn kind(&self) -> &'static str {
match self {
Self::None => "none",
Self::Bytes(_) => "bytes",
Self::Text(_) => "text",
Self::Json(_) => "json",
Self::Tensors(_) => "tensors",
Self::Command(_) => "command",
#[cfg(feature = "llm")]
Self::Prompt(_) => "prompt",
}
}
}
pub(crate) const TRACE_HOPS: usize = 8;
#[derive(Clone, Copy, Debug)]
pub struct Trace {
hops: [NeuronId; TRACE_HOPS],
len: u8,
overflow: u16,
brain: bool,
queued: bool,
}
impl Default for Trace {
fn default() -> Self {
Self {
hops: [NeuronId::NONE; TRACE_HOPS],
len: 0,
overflow: 0,
brain: false,
queued: false,
}
}
}
impl Trace {
pub(crate) fn push(&mut self, id: NeuronId) {
if (self.len as usize) < TRACE_HOPS {
self.hops[self.len as usize] = id;
self.len += 1;
} else {
self.overflow = self.overflow.saturating_add(1);
}
}
pub(crate) fn mark_brain(&mut self) {
self.brain = true;
}
pub(crate) fn mark_queued(&mut self) {
self.queued = true;
}
pub fn hops(&self) -> &[NeuronId] {
&self.hops[..self.len as usize]
}
pub fn len(&self) -> usize {
self.len as usize + self.overflow as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0 && self.overflow == 0
}
pub fn reached_brain(&self) -> bool {
self.brain
}
pub fn was_queued(&self) -> bool {
self.queued
}
pub fn last(&self) -> Option<NeuronId> {
self.hops().last().copied()
}
}
#[derive(Debug)]
pub enum Answer {
Http {
status: u16,
content_type: String,
body: Bytes,
},
Tensors(Vec<HostTensor>),
Failed(Box<crate::error::FurnaceError>),
#[cfg(feature = "llm")]
Generation(
std::result::Result<
tokio::sync::mpsc::Receiver<crate::llm::Event>,
crate::llm::EngineError,
>,
),
}
impl Answer {
pub fn text(status: u16, body: &str) -> Self {
Self::Http {
status,
content_type: "text/plain; charset=utf-8".to_string(),
body: Bytes::copy_from_slice(body.as_bytes()),
}
}
pub fn json(status: u16, value: &serde_json::Value) -> Self {
Self::Http {
status,
content_type: "application/json".to_string(),
body: Bytes::from(value.to_string()),
}
}
pub fn bytes(status: u16, content_type: &str, body: Bytes) -> Self {
Self::Http {
status,
content_type: content_type.to_string(),
body,
}
}
pub fn failed(error: crate::error::FurnaceError) -> Self {
Self::Failed(Box::new(error))
}
}
struct ReplyInner {
sender: Mutex<Option<oneshot::Sender<Answer>>>,
required: bool,
}
#[derive(Clone)]
pub struct ReplyHandle(Arc<ReplyInner>);
impl fmt::Debug for ReplyHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReplyHandle")
.field("open", &self.is_open())
.field("required", &self.0.required)
.finish()
}
}
impl ReplyHandle {
pub fn new() -> (Self, oneshot::Receiver<Answer>) {
Self::make(false)
}
pub fn required() -> (Self, oneshot::Receiver<Answer>) {
Self::make(true)
}
fn make(required: bool) -> (Self, oneshot::Receiver<Answer>) {
let (sender, receiver) = oneshot::channel();
let inner = ReplyInner {
sender: Mutex::new(Some(sender)),
required,
};
(Self(Arc::new(inner)), receiver)
}
pub fn answer(&self, answer: Answer) -> bool {
let sender = self
.0
.sender
.lock()
.unwrap_or_else(|e| e.into_inner())
.take();
sender.is_some_and(|s| s.send(answer).is_ok())
}
pub fn is_open(&self) -> bool {
self.0
.sender
.lock()
.unwrap_or_else(|e| e.into_inner())
.as_ref()
.is_some_and(|s| !s.is_closed())
}
pub fn wanted(&self) -> bool {
!self.0.required || self.is_open()
}
}
#[derive(Clone, Debug)]
pub struct Signal {
pub id: SignalId,
pub origin: NeuronId,
pub at: Instant,
pub urgency: Urgency,
pub feature: Feature,
pub intensity: f32,
pub payload: Payload,
pub trace: Trace,
pub reply: Option<ReplyHandle>,
}
impl Signal {
pub fn new(feature: Feature, payload: Payload) -> Self {
Self {
id: SignalId::next(),
origin: NeuronId::NONE,
at: Instant::now(),
urgency: Urgency::NONE,
feature,
intensity: 1.0,
payload,
trace: Trace::default(),
reply: None,
}
}
pub fn with_urgency(mut self, urgency: Urgency) -> Self {
self.urgency = urgency;
self
}
pub fn with_intensity(mut self, intensity: f32) -> Self {
self.intensity = intensity;
self
}
pub fn with_feature(mut self, feature: Feature) -> Self {
self.feature = feature;
self
}
pub fn with_reply(mut self, reply: ReplyHandle) -> Self {
self.reply = Some(reply);
self
}
pub fn derive(&self, payload: Payload) -> Self {
Self {
id: self.id,
origin: self.origin,
at: self.at,
urgency: self.urgency,
feature: self.feature,
intensity: self.intensity,
payload,
trace: self.trace,
reply: self.reply.clone(),
}
}
pub fn command(&self, effector: &str, args: serde_json::Value) -> Self {
self.derive(Payload::command(Command::new(effector, args)))
}
pub fn age(&self) -> Duration {
self.at.elapsed()
}
pub fn answer(&self, answer: Answer) -> bool {
self.reply.as_ref().is_some_and(|r| r.answer(answer))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sketches_tell_inputs_apart_and_repeat_for_equal_ones() {
assert_eq!(Feature::sketch(b"hello"), Feature::sketch(b"hello"));
assert_ne!(Feature::sketch(b"hello"), Feature::sketch(b"hellp"));
assert_ne!(Feature::sketch(b""), Feature::sketch(b"\0"));
assert_ne!(Feature::sketch(b"12345678"), Feature::sketch(b"123456789"));
assert_ne!(Feature::sketch(b"a"), Feature::sketch_as(2, b"a"));
assert!(!Feature::sketch(b"").is_none());
}
#[test]
fn trace_records_eight_hops_inline_and_counts_the_rest() {
let mut trace = Trace::default();
for i in 0..12 {
trace.push(NeuronId(i));
}
assert_eq!(trace.hops().len(), TRACE_HOPS);
assert_eq!(trace.len(), 12);
assert_eq!(trace.last(), Some(NeuronId(7)));
assert!(!trace.reached_brain());
}
#[test]
fn derived_signals_share_identity_and_reply() {
let (reply, mut receiver) = ReplyHandle::new();
let signal = Signal::new(Feature::sketch(b"x"), Payload::text("x"))
.with_reply(reply)
.with_urgency(Urgency::within(Duration::from_secs(1)));
let child = signal.command("log", serde_json::json!({"line": "x"}));
assert_eq!(child.id, signal.id);
assert_eq!(child.payload.as_command().unwrap().effector, "log");
assert!(child.urgency.remaining(Instant::now()).is_some());
assert!(child.answer(Answer::text(200, "ok")));
assert!(!signal.answer(Answer::text(200, "again")));
let Answer::Http { body, .. } = receiver.try_recv().unwrap() else {
panic!("an HTTP answer");
};
assert_eq!(body, Bytes::from_static(b"ok"));
}
#[test]
fn only_a_required_reply_makes_its_signal_unwanted_once_abandoned() {
let (optional, receiver) = ReplyHandle::new();
drop(receiver);
assert!(!optional.is_open());
assert!(optional.wanted());
let (required, receiver) = ReplyHandle::required();
assert!(required.wanted());
drop(receiver);
assert!(!required.wanted());
}
#[test]
fn payload_conversions() {
let json = Payload::text(r#"{"a":1}"#).to_json().unwrap();
assert_eq!(json["a"], 1);
assert_eq!(
Payload::json(json.clone()).to_bytes(),
Bytes::from(r#"{"a":1}"#)
);
let command = Payload::command(Command::new("w", serde_json::json!({"k": "v"})));
assert_eq!(command.to_json().unwrap()["effector"], "w");
assert!(Payload::None.as_text().is_none());
assert_eq!(Payload::bytes(&b"abc"[..]).as_text(), Some("abc"));
}
}