#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
reason = "test code"
)]
use kernel::{
Address, AxError, Ceiling, ChatMessage, ContentBlock, Locator, Model, ModelRequest,
ModelReturn, Payload, Retries, RunId, Seq, TimeMs, ToolCall, ToolName, ToolOutcome,
};
use crate::conversation::Opening;
use crate::prefix::{FrozenPrefix, FrozenSegment, SegmentSlot};
use crate::turn::{CallShape, Interrupt};
use crate::{Handoff, RunHooks, RunPlan, SafePoint, drive};
struct Recorded {
replies: Vec<ModelReturn>,
sent: Vec<Vec<String>>,
}
impl Model for Recorded {
fn call(&mut self, req: &ModelRequest) -> Result<ModelReturn, AxError> {
self.sent.push(
req.chat
.messages
.iter()
.map(|message| serde_json::to_string(message).unwrap())
.collect(),
);
Ok(self.replies.remove(0))
}
}
fn says(text: &str, calls: Vec<ToolCall>) -> ModelReturn {
let mut blocks = vec![ContentBlock::Text {
text: text.to_owned(),
}];
blocks.extend(calls.iter().map(|call| ContentBlock::ToolUse {
id: call.id.clone(),
name: call.name.clone(),
input: call.args.clone(),
}));
ModelReturn::bare(kernel::model::message_payload(&blocks).unwrap(), calls)
}
#[test]
fn a_branch_first_request_opens_with_the_bytes_of_the_mothers_last() {
let dir = tempfile::tempdir().unwrap();
let (mut ledger, _) = storage::JsonlLedger::open(dir.path(), TimeMs::new(0)).unwrap();
let status = ToolCall {
id: "tu_1".to_owned(),
name: ToolName::parse("status").unwrap(),
args: Payload::empty(),
};
let mut mother = Recorded {
replies: vec![
says("reading it now", vec![status]),
says("it reads 42", Vec::new()),
],
sent: Vec::new(),
};
run(
&mut ledger,
&mut mother,
plan(RunId::from_bytes([1; 16]), "measure the meter", Vec::new()),
);
let mut index = storage::LedgerIndex::empty();
index.refresh(dir.path()).unwrap();
let tail = Seq::new(u64::try_from(index.len()).unwrap() - 1);
let inherited = super::inherited_indexed(&index, dir.path(), tail).unwrap();
let mut daughter = Recorded {
replies: vec![says("in feet it is 138", Vec::new())],
sent: Vec::new(),
};
run(
&mut ledger,
&mut daughter,
plan(
RunId::from_bytes([2; 16]),
"and in feet?",
inherited.messages,
),
);
let last = mother.sent.last().unwrap();
let first = &daughter.sent[0];
assert_eq!(first.get(..last.len()), Some(&last[..]));
}
fn run(ledger: &mut storage::JsonlLedger, model: &mut Recorded, plan: RunPlan) {
let mut clock = 0u64;
let mut now = || {
clock = clock.saturating_add(1);
Ok(TimeMs::new(clock))
};
let mut interrupt = |_: SafePoint| Interrupt::None;
let mut invoke = |_: &ToolCall, _: TimeMs| {
Ok(ToolOutcome {
result: Payload::of(&serde_json::json!({ "said": "42" })).unwrap(),
attachments: Vec::new(),
})
};
let mut hooks = RunHooks {
now: &mut now,
monotonic_us: &mut || 0,
interrupt: &mut interrupt,
checkpoint: None,
writes: &|_: &ToolCall| kernel::Writes::Nothing,
invoke: &mut invoke,
wait: &mut |_: TimeMs| crate::NextCall::Allowed,
deltas: None,
};
let handoff = Handoff::new(
vec![job(&room())],
"overview".to_owned(),
"progress".to_owned(),
"context".to_owned(),
"next".to_owned(),
)
.unwrap();
drive(plan, ledger, model, &mut hooks, &handoff).unwrap();
}
fn room() -> Address {
Address::parse("lab/room1").unwrap()
}
fn job(addr: &Address) -> Locator {
Locator::parse(&format!("file:{}/JOB.md@{}", addr.as_str(), "a".repeat(40))).unwrap()
}
fn plan(run: RunId, task: &str, inherited: Vec<ChatMessage>) -> RunPlan {
RunPlan {
run,
who: "lab/room1".to_owned(),
addr: room(),
task: task.to_owned(),
goal: "a number is written down".to_owned(),
opening: Opening::WithPerson,
job: job(&room()),
parent: None,
predecessor: None,
dispatched_by: kernel::event::Who::Person,
inherited,
shape: CallShape {
model: "script".to_owned(),
max_tokens: Ceiling::new(4096),
effort: None,
context_tokens: 0,
},
second_threshold: None,
context: crate::ContextReading::default(),
prefix: FrozenPrefix::assemble(
FrozenSegment::new(SegmentSlot::City, b"city".to_vec()),
FrozenSegment::new(SegmentSlot::Building, b"building".to_vec()),
FrozenSegment::new(SegmentSlot::Resident, b"resident".to_vec()),
FrozenSegment::new(SegmentSlot::Run, b"run".to_vec()),
)
.unwrap(),
policy: kernel::BuildingPolicy::default(),
tools: Vec::new(),
skills: Vec::new(),
retries: Retries::UntilHalted,
run_policy: crate::PolicyCell::new(kernel::RunPolicy::of(kernel::Mode::Work)),
naming: None,
}
}