use std::time::Duration;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResourceBudget {
pub fuel: u64,
pub mem_pages: u32,
pub wall: Duration,
}
impl Default for ResourceBudget {
fn default() -> Self {
Self {
fuel: 10_000_000,
mem_pages: 64,
wall: Duration::from_millis(50),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RecallStep {
Recall { query: String, k: u32 },
Score { memory_id: String },
Cite { memory_id: String },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GuestProgram {
pub steps: Vec<RecallStep>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RecallBundle {
pub recalled: Vec<RecallHit>,
pub final_answer: String,
pub guest_token_cost: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RecallHit {
pub id: String,
pub content: String,
pub score: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CodeModeRecall {
pub program: GuestProgram,
pub budget: ResourceBudget,
}
#[derive(Debug, Error, PartialEq)]
pub enum CodeModeError {
#[error("guest fuel exhausted ({budget} units consumed)")]
Halted { budget: u64 },
#[error("guest exceeded wall-time budget {budget:?}")]
WallTimeExceeded { budget: Duration },
#[error("guest tried to access {capability} which is stripped from the sandbox")]
SandboxViolation { capability: &'static str },
#[error("guest emitted no recall steps — refusing an empty bundle")]
EmptyProgram,
}
pub trait HostStore: Send + Sync {
fn recall(&self, query: &str, k: u32) -> Vec<RecallHit>;
fn score(&self, memory_id: &str) -> f32;
fn cite(&self, memory_id: &str) -> String;
}
pub fn run_code_mode_host(
program: &CodeModeRecall,
store: &dyn HostStore,
) -> Result<RecallBundle, CodeModeError> {
if program.program.steps.is_empty() {
return Err(CodeModeError::EmptyProgram);
}
let start = std::time::Instant::now();
let mut fuel_used = 0u64;
let mut recalled = Vec::new();
let mut answer_parts = Vec::new();
for step in &program.program.steps {
fuel_used = fuel_used.saturating_add(1_000_000);
if fuel_used > program.budget.fuel {
return Err(CodeModeError::Halted {
budget: program.budget.fuel,
});
}
if start.elapsed() > program.budget.wall {
return Err(CodeModeError::WallTimeExceeded {
budget: program.budget.wall,
});
}
match step {
RecallStep::Recall { query, k } => {
let hits = store.recall(query, *k);
for h in &hits {
answer_parts.push(format!("- {}", h.content));
}
recalled.extend(hits);
}
RecallStep::Score { memory_id } => {
let _ = store.score(memory_id);
}
RecallStep::Cite { memory_id } => {
let _ = store.cite(memory_id);
}
}
}
let final_answer = if answer_parts.is_empty() {
"(no relevant memories)".to_string()
} else {
answer_parts.join("\n")
};
let guest_token_cost =
crate::token::estimate_tokens(&final_answer) + program.program.steps.len() * 4; Ok(RecallBundle {
recalled,
final_answer,
guest_token_cost,
})
}
#[cfg(test)]
mod tests {
use super::*;
struct StubStore;
impl HostStore for StubStore {
fn recall(&self, q: &str, k: u32) -> Vec<RecallHit> {
(0..k.min(3))
.map(|i| RecallHit {
id: format!("m{i}"),
content: format!("answer to '{q}' #{i}"),
score: 1.0 - (i as f32) * 0.1,
})
.collect()
}
fn score(&self, _: &str) -> f32 {
0.5
}
fn cite(&self, mid: &str) -> String {
format!("receipt-for-{mid}")
}
}
#[test]
fn empty_program_is_rejected() {
let req = CodeModeRecall {
program: GuestProgram { steps: vec![] },
budget: ResourceBudget::default(),
};
let err = run_code_mode_host(&req, &StubStore).unwrap_err();
assert_eq!(err, CodeModeError::EmptyProgram);
}
#[test]
fn fuel_exhaust_halts() {
let req = CodeModeRecall {
program: GuestProgram {
steps: vec![
RecallStep::Recall {
query: "x".into(),
k: 1,
};
12
],
},
budget: ResourceBudget::default(),
};
let err = run_code_mode_host(&req, &StubStore).unwrap_err();
assert!(matches!(err, CodeModeError::Halted { .. }));
}
#[test]
fn happy_path_returns_bundle() {
let req = CodeModeRecall {
program: GuestProgram {
steps: vec![RecallStep::Recall {
query: "patient fatigue".into(),
k: 3,
}],
},
budget: ResourceBudget::default(),
};
let bundle = run_code_mode_host(&req, &StubStore).unwrap();
assert_eq!(bundle.recalled.len(), 3);
assert!(bundle.final_answer.contains("answer to"));
}
#[test]
fn wall_time_budget_can_be_exceeded() {
let req = CodeModeRecall {
program: GuestProgram {
steps: vec![
RecallStep::Recall {
query: "x".into(),
k: 1,
};
2
],
},
budget: ResourceBudget {
wall: Duration::from_nanos(0),
..ResourceBudget::default()
},
};
std::thread::sleep(Duration::from_millis(1));
let err = run_code_mode_host(&req, &StubStore).unwrap_err();
assert!(matches!(err, CodeModeError::WallTimeExceeded { .. }));
}
}