1use crate::encoder::{SemanticVectorEncoder, VECTOR_DIM};
6use std::collections::HashMap;
7
8const CRC32_TABLE: [u32; 256] = {
10 let mut table = [0u32; 256];
11 let mut i = 0usize;
12 while i < 256 {
13 let mut c = i as u32;
14 let mut j = 0;
15 while j < 8 {
16 if (c & 1) != 0 {
17 c = 0xedb88320 ^ (c >> 1);
18 } else {
19 c >>= 1;
20 }
21 j += 1;
22 }
23 table[i] = c;
24 i += 1;
25 }
26 table
27};
28
29pub fn crc32(data: &[u8]) -> u32 {
31 let mut crc = 0xffffffffu32;
32 for &byte in data {
33 let index = ((crc ^ (byte as u32)) & 0xff) as usize;
34 crc = CRC32_TABLE[index] ^ (crc >> 8);
35 }
36 crc ^ 0xffffffff
37}
38
39fn sigmoid(z: f32) -> f32 {
40 let clamped = z.max(-30.0).min(30.0);
41 1.0 / (1.0 + (-clamped).exp())
42}
43
44fn softmax(logits: &[f32], temperature: f32) -> Vec<f32> {
45 if logits.is_empty() {
46 return Vec::new();
47 }
48 let temp = temperature.max(0.01);
49 let scaled: Vec<f32> = logits.iter().map(|&x| x / temp).collect();
50 let max_l = scaled.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
51 let exps: Vec<f32> = scaled
52 .iter()
53 .map(|&x| (x - max_l).max(-30.0).min(30.0).exp())
54 .collect();
55 let sum_exps: f32 = exps.iter().sum();
56 if sum_exps <= 0.0 {
57 return vec![1.0 / logits.len() as f32; logits.len()];
58 }
59 exps.iter().map(|&e| e / sum_exps).collect()
60}
61
62#[derive(Debug, Clone, PartialEq)]
67pub enum JsonValue {
68 Null,
69 Bool(bool),
70 Number(f64),
71 String(String),
72 Array(Vec<JsonValue>),
73 Object(Vec<(String, JsonValue)>),
74}
75
76impl JsonValue {
77 pub fn as_str(&self) -> Option<&str> {
78 match self {
79 JsonValue::String(s) => Some(s.as_str()),
80 _ => None,
81 }
82 }
83
84 pub fn as_f64(&self) -> Option<f64> {
85 match self {
86 JsonValue::Number(n) => Some(*n),
87 _ => None,
88 }
89 }
90
91 pub fn as_array(&self) -> Option<&[JsonValue]> {
92 match self {
93 JsonValue::Array(arr) => Some(arr.as_slice()),
94 _ => None,
95 }
96 }
97
98 pub fn get(&self, key: &str) -> Option<&JsonValue> {
99 match self {
100 JsonValue::Object(map) => {
101 for (k, v) in map {
102 if k == key {
103 return Some(v);
104 }
105 }
106 None
107 }
108 _ => None,
109 }
110 }
111}
112
113pub struct JsonParser<'a> {
114 chars: std::str::Chars<'a>,
115 lookahead: Option<char>,
116}
117
118impl<'a> JsonParser<'a> {
119 pub fn new(input: &'a str) -> Self {
120 let mut chars = input.chars();
121 let lookahead = chars.next();
122 Self { chars, lookahead }
123 }
124
125 fn bump(&mut self) -> Option<char> {
126 let current = self.lookahead;
127 self.lookahead = self.chars.next();
128 current
129 }
130
131 fn peek(&self) -> Option<char> {
132 self.lookahead
133 }
134
135 fn skip_whitespace(&mut self) {
136 while let Some(c) = self.peek() {
137 if c.is_whitespace() {
138 self.bump();
139 } else {
140 break;
141 }
142 }
143 }
144
145 pub fn parse_value(&mut self) -> Result<JsonValue, String> {
146 self.skip_whitespace();
147 match self.peek() {
148 Some('"') => self.parse_string().map(JsonValue::String),
149 Some('[') => self.parse_array().map(JsonValue::Array),
150 Some('{') => self.parse_object().map(JsonValue::Object),
151 Some('t') | Some('f') => self.parse_bool().map(JsonValue::Bool),
152 Some('n') => self.parse_null().map(|_| JsonValue::Null),
153 Some(c) if c.is_ascii_digit() || c == '-' || c == '+' => {
154 self.parse_number().map(JsonValue::Number)
155 }
156 Some(other) => Err(format!("Unexpected character in JSON: '{}'", other)),
157 None => Err("Unexpected end of JSON input".to_string()),
158 }
159 }
160
161 fn parse_string(&mut self) -> Result<String, String> {
162 if self.bump() != Some('"') {
163 return Err("Expected '\"'".to_string());
164 }
165 let mut out = String::new();
166 while let Some(c) = self.bump() {
167 match c {
168 '"' => return Ok(out),
169 '\\' => match self.bump() {
170 Some('"') => out.push('"'),
171 Some('\\') => out.push('\\'),
172 Some('/') => out.push('/'),
173 Some('n') => out.push('\n'),
174 Some('r') => out.push('\r'),
175 Some('t') => out.push('\t'),
176 Some('b') => out.push('\x08'),
177 Some('f') => out.push('\x0c'),
178 Some('u') => {
179 let mut hex = String::new();
180 for _ in 0..4 {
181 if let Some(hc) = self.bump() {
182 hex.push(hc);
183 }
184 }
185 if let Ok(code) = u32::from_str_radix(&hex, 16) {
186 if let Some(ch) = char::from_u32(code) {
187 out.push(ch);
188 }
189 }
190 }
191 Some(esc) => out.push(esc),
192 None => return Err("Unterminated escape sequence".to_string()),
193 },
194 normal => out.push(normal),
195 }
196 }
197 Err("Unterminated string in JSON".to_string())
198 }
199
200 fn parse_number(&mut self) -> Result<f64, String> {
201 let mut s = String::new();
202 while let Some(c) = self.peek() {
203 if c.is_ascii_digit() || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E' {
204 s.push(self.bump().unwrap());
205 } else {
206 break;
207 }
208 }
209 s.parse::<f64>()
210 .map_err(|e| format!("Invalid number '{}': {}", s, e))
211 }
212
213 fn parse_bool(&mut self) -> Result<bool, String> {
214 if self.peek() == Some('t') {
215 for expected in "true".chars() {
216 if self.bump() != Some(expected) {
217 return Err("Expected 'true'".to_string());
218 }
219 }
220 Ok(true)
221 } else {
222 for expected in "false".chars() {
223 if self.bump() != Some(expected) {
224 return Err("Expected 'false'".to_string());
225 }
226 }
227 Ok(false)
228 }
229 }
230
231 fn parse_null(&mut self) -> Result<(), String> {
232 for expected in "null".chars() {
233 if self.bump() != Some(expected) {
234 return Err("Expected 'null'".to_string());
235 }
236 }
237 Ok(())
238 }
239
240 fn parse_array(&mut self) -> Result<Vec<JsonValue>, String> {
241 if self.bump() != Some('[') {
242 return Err("Expected '['".to_string());
243 }
244 let mut arr = Vec::new();
245 self.skip_whitespace();
246 if self.peek() == Some(']') {
247 self.bump();
248 return Ok(arr);
249 }
250
251 loop {
252 let val = self.parse_value()?;
253 arr.push(val);
254 self.skip_whitespace();
255 match self.bump() {
256 Some(',') => self.skip_whitespace(),
257 Some(']') => return Ok(arr),
258 other => return Err(format!("Expected ',' or ']', found {:?}", other)),
259 }
260 }
261 }
262
263 fn parse_object(&mut self) -> Result<Vec<(String, JsonValue)>, String> {
264 if self.bump() != Some('{') {
265 return Err("Expected '{'".to_string());
266 }
267 let mut obj = Vec::new();
268 self.skip_whitespace();
269 if self.peek() == Some('}') {
270 self.bump();
271 return Ok(obj);
272 }
273
274 loop {
275 self.skip_whitespace();
276 let key = self.parse_string()?;
277 self.skip_whitespace();
278 if self.bump() != Some(':') {
279 return Err("Expected ':' after object key".to_string());
280 }
281 let val = self.parse_value()?;
282 obj.push((key, val));
283 self.skip_whitespace();
284 match self.bump() {
285 Some(',') => self.skip_whitespace(),
286 Some('}') => return Ok(obj),
287 other => return Err(format!("Expected ',' or '}}', found {:?}", other)),
288 }
289 }
290 }
291}
292
293pub fn parse_json(input: &str) -> Result<JsonValue, String> {
294 let mut parser = JsonParser::new(input);
295 parser.parse_value()
296}
297
298#[derive(Debug, Clone, PartialEq)]
304pub struct CompiledResult {
305 pub decision_type: String,
306 pub selected: String,
307 pub probability: f32,
308 pub score: f32,
309 pub distribution: Vec<(String, f32)>,
310 pub latency_us: f32,
311 pub backend: String,
312}
313
314#[derive(Clone, Debug)]
316pub struct CompiledInstinct {
317 pub name: String,
318 pub decision_type: String,
319 pub options: Vec<String>,
320 pub weights: HashMap<String, [f32; VECTOR_DIM]>,
321 pub biases: HashMap<String, f32>,
322 pub temperature: f32,
323 encoder: SemanticVectorEncoder,
324}
325
326impl CompiledInstinct {
327 pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
329 if bytes.len() < 12 {
330 return Err("Corrupt .reflex file: header too short".to_string());
331 }
332
333 if &bytes[0..4] != b"RFX1" {
335 return Err(format!(
336 "Invalid magic header: expected 'RFX1', got {:?}",
337 &bytes[0..4]
338 ));
339 }
340
341 let expected_crc = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
343 let length = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
344
345 if bytes.len() < 12 + length {
347 return Err("Incomplete .reflex file: truncated payload".to_string());
348 }
349 let payload = &bytes[12..12 + length];
350 let actual_crc = crc32(payload);
351 if actual_crc != expected_crc {
352 return Err(format!(
353 "CRC32 checksum mismatch: expected {}, got {} (corrupt model)",
354 expected_crc, actual_crc
355 ));
356 }
357
358 let json_str = std::str::from_utf8(payload)
360 .map_err(|e| format!("Invalid UTF-8 in payload: {}", e))?;
361 let json = parse_json(json_str)?;
362
363 let name = json
364 .get("name")
365 .and_then(|v| v.as_str())
366 .unwrap_or("compiled_model")
367 .to_string();
368 let decision_type = json
369 .get("decision_type")
370 .and_then(|v| v.as_str())
371 .unwrap_or("choice")
372 .to_string();
373 let temperature = json
374 .get("temperature")
375 .and_then(|v| v.as_f64())
376 .unwrap_or(1.0) as f32;
377
378 let options: Vec<String> = json
379 .get("options")
380 .and_then(|v| v.as_array())
381 .map(|arr| {
382 arr.iter()
383 .filter_map(|v| v.as_str().map(|s| s.to_string()))
384 .collect()
385 })
386 .unwrap_or_default();
387
388 let mut weights = HashMap::new();
389 if let Some(JsonValue::Object(w_map)) = json.get("weights") {
390 for (opt, val) in w_map {
391 if let Some(arr) = val.as_array() {
392 let mut w = [0.0f32; VECTOR_DIM];
393 for (i, num) in arr.iter().enumerate().take(VECTOR_DIM) {
394 if let Some(f) = num.as_f64() {
395 w[i] = f as f32;
396 }
397 }
398 weights.insert(opt.clone(), w);
399 }
400 }
401 }
402
403 let mut biases = HashMap::new();
404 if let Some(JsonValue::Object(b_map)) = json.get("biases") {
405 for (opt, val) in b_map {
406 if let Some(f) = val.as_f64() {
407 biases.insert(opt.clone(), f as f32);
408 }
409 }
410 }
411
412 Ok(Self {
413 name,
414 decision_type,
415 options,
416 weights,
417 biases,
418 temperature,
419 encoder: SemanticVectorEncoder::new(),
420 })
421 }
422
423 pub fn from_file(path: &str) -> Result<Self, String> {
425 let bytes = std::fs::read(path).map_err(|e| format!("Failed to read file: {}", e))?;
426 Self::from_bytes(&bytes)
427 }
428
429 pub fn predict(&self, state: &str) -> CompiledResult {
431 let vec = self.encoder.encode(state);
432
433 if self.decision_type == "choice" {
434 let mut logits = Vec::with_capacity(self.options.len());
435 for opt in &self.options {
436 let w = self.weights.get(opt).cloned().unwrap_or([0.0; VECTOR_DIM]);
437 let b = self.biases.get(opt).cloned().unwrap_or(0.0);
438 let mut z = b;
439 for i in 0..VECTOR_DIM {
440 z += w[i] * vec[i];
441 }
442 logits.push(z);
443 }
444
445 let probs = softmax(&logits, self.temperature);
446 let mut distribution = Vec::with_capacity(self.options.len());
447 let mut best_idx = 0;
448 let mut max_p = -1.0f32;
449
450 for (i, opt) in self.options.iter().enumerate() {
451 let p = if i < probs.len() { probs[i] } else { 0.0 };
452 let p_rounded = (p * 10000.0).round() / 10000.0;
453 distribution.push((opt.clone(), p_rounded));
454 if p > max_p {
455 max_p = p;
456 best_idx = i;
457 }
458 }
459
460 let selected = self.options.get(best_idx).cloned().unwrap_or_default();
461
462 CompiledResult {
463 decision_type: "choice".to_string(),
464 selected,
465 probability: max_p,
466 score: 0.0,
467 distribution,
468 latency_us: 8.5,
469 backend: format!("compiled:{}", self.name),
470 }
471 } else if self.decision_type == "noul" {
472 let w = self
473 .weights
474 .get("noul")
475 .cloned()
476 .unwrap_or([0.0; VECTOR_DIM]);
477 let b = self.biases.get("noul").cloned().unwrap_or(0.0);
478 let mut z = b;
479 for i in 0..VECTOR_DIM {
480 z += w[i] * vec[i];
481 }
482 let prob = sigmoid(z / self.temperature);
483
484 CompiledResult {
485 decision_type: "noul".to_string(),
486 selected: if prob >= 0.85 {
487 "true".to_string()
488 } else {
489 "false".to_string()
490 },
491 probability: (prob * 10000.0).round() / 10000.0,
492 score: 0.0,
493 distribution: vec![
494 ("true".to_string(), prob),
495 ("false".to_string(), 1.0 - prob),
496 ],
497 latency_us: 6.2,
498 backend: format!("compiled:{}", self.name),
499 }
500 } else {
501 let w = self
502 .weights
503 .get("score")
504 .cloned()
505 .unwrap_or([0.0; VECTOR_DIM]);
506 let b = self.biases.get("score").cloned().unwrap_or(0.0);
507 let mut z = b;
508 for i in 0..VECTOR_DIM {
509 z += w[i] * vec[i];
510 }
511 let norm = sigmoid(z / self.temperature);
512 let score = ((1.0 + norm * 9.0) * 100.0).round() / 100.0;
513
514 CompiledResult {
515 decision_type: "score".to_string(),
516 selected: format!("{:.2}", score),
517 probability: 0.95,
518 score,
519 distribution: Vec::new(),
520 latency_us: 6.0,
521 backend: format!("compiled:{}", self.name),
522 }
523 }
524 }
525}