kimetsu_brain/
delivery.rs1use super::{ContextCapsule, memory_revision_bindings};
4use serde_json::{Value, json};
5
6pub struct Delivery {
7 pub payload: Value,
8 pub capsules: Vec<ContextCapsule>,
9}
10
11pub fn serialized_output_tokens(payload: &Value) -> u32 {
16 let bytes = json!({"content": [{"type": "text", "text": payload.to_string()}]})
17 .to_string()
18 .len();
19 u32::try_from(bytes).unwrap_or(u32::MAX)
20}
21
22fn account(payload: &mut Value) -> u32 {
23 payload["used_tokens"] = json!(0);
24 loop {
25 let bound = serialized_output_tokens(payload);
26 if payload["used_tokens"].as_u64() == Some(u64::from(bound)) {
27 return bound;
28 }
29 payload["used_tokens"] = json!(bound);
30 }
31}
32
33pub fn compact_capsules(capsules: &[ContextCapsule]) -> Vec<Value> {
34 capsules
35 .iter()
36 .map(|c| {
37 json!({
38 "id": c.id, "kind": c.kind, "summary": c.summary,
39 "expansion_handle": c.expansion_handle, "score": c.score,
40 })
41 })
42 .collect()
43}
44
45pub fn fit_json(
50 mut capsules: Vec<ContextCapsule>,
51 budget: u32,
52 render: impl Fn(&[ContextCapsule]) -> Value,
53) -> Delivery {
54 loop {
55 let mut payload = render(&capsules);
56 payload["budget_tokens"] = json!(budget);
57 payload["token_accounting"] = json!("utf8_byte_upper_bound");
58 if account(&mut payload) <= budget {
59 return Delivery { payload, capsules };
60 }
61 if capsules.pop().is_none() {
62 let mut payload = json!({"ok": false, "error": "budget_too_small",
63 "budget_tokens": budget, "capsules": [], "capsule_count": 0,
64 "token_accounting": "utf8_byte_upper_bound"});
65 account(&mut payload);
66 return Delivery { payload, capsules };
67 }
68 }
69}
70
71pub fn add_optional_field(delivery: &mut Delivery, key: &str, value: Value, budget: u32) {
74 let mut payload = delivery.payload.clone();
75 payload[key] = value;
76 if account(&mut payload) <= budget {
77 delivery.payload = payload;
78 }
79}
80
81pub fn injected_payload(capsules: &[ContextCapsule], used_tokens: u32) -> Value {
84 json!({
85 "memory_ids": capsules.iter().filter_map(|c| c.expansion_handle.strip_prefix("memory:")).collect::<Vec<_>>(),
86 "memory_revisions": memory_revision_bindings(capsules),
87 "capsule_handles": capsules.iter().map(|c| c.expansion_handle.as_str()).collect::<Vec<_>>(),
88 "capsule_count": capsules.len(), "used_tokens": used_tokens,
89 })
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 fn capsule(id: &str, text: &str) -> ContextCapsule {
97 let mut c = ContextCapsule::wire_minimal(text.into(), "memory".into(), 0.9);
98 c.id = id.into();
99 c.expansion_handle = format!("memory:{id}");
100 c.claim_revision = Some(format!("revision-{id}"));
101 c
102 }
103 fn render(c: &[ContextCapsule]) -> Value {
104 json!({"ok":true,"capsules":compact_capsules(c),"capsule_count":c.len()})
105 }
106
107 #[test]
108 fn final_serialization_bounds_unicode_identifiers_and_escaping() {
109 for text in [
110 "字".repeat(1000),
111 "no_space_identifier".repeat(1000),
112 "\"\\\n".repeat(1000),
113 ] {
114 let delivery = fit_json(
115 vec![capsule("kept", "short"), capsule("dropped", &text)],
116 800,
117 render,
118 );
119 assert_eq!(delivery.capsules.len(), 1);
120 assert_eq!(
121 delivery.payload["used_tokens"].as_u64(),
122 Some(u64::from(serialized_output_tokens(&delivery.payload)))
123 );
124 assert!(serialized_output_tokens(&delivery.payload) <= 800);
125 let event = injected_payload(&delivery.capsules, 0);
126 assert_eq!(event["memory_ids"], json!(["kept"]));
127 assert_eq!(event["memory_revisions"], json!({"kept":"revision-kept"}));
128 }
129 }
130
131 #[test]
132 fn tiny_budget_reports_actual_error_cost_and_no_exposure() {
133 let delivery = fit_json(vec![capsule("secret", "secret")], 1, render);
134 assert_eq!(delivery.payload["error"], "budget_too_small");
135 assert!(delivery.payload["used_tokens"].as_u64().unwrap() > 1);
136 assert!(delivery.capsules.is_empty());
137 assert_eq!(injected_payload(&[], 0)["memory_revisions"], json!({}));
138 }
139
140 #[test]
141 fn repeated_playbook_text_is_included_in_final_bound() {
142 let delivery = fit_json(
143 vec![capsule("large", &"x".repeat(500))],
144 900,
145 |c| json!({"capsules":compact_capsules(c),"playbook_markdown":c.iter().map(|c|c.summary.as_str()).collect::<Vec<_>>().join("\n")}),
146 );
147 assert!(delivery.capsules.is_empty());
148 assert_eq!(delivery.payload["playbook_markdown"], "");
149 assert!(serialized_output_tokens(&delivery.payload) <= 900);
150 }
151}