1use std::collections::HashMap;
8
9use leviath_core::{
10 Blueprint, ContextLayout, EvictionStrategy, Region, RegionKind, truncate_at_boundary,
11};
12
13use crate::ContextWindow;
14
15pub fn init_window_seeded(
23 window: &mut ContextWindow,
24 blueprint: &Blueprint,
25 seeds: &HashMap<String, String>,
26) {
27 for region_def in &blueprint.context_layout.regions {
28 let region = Region::new(
29 region_def.name.clone(),
30 region_def.kind.clone(),
31 region_def.max_tokens,
32 );
33 window.add_region(region);
34 }
35
36 if window.get_region("tool_results").is_none() {
37 let tool_region = Region::new("tool_results".to_string(), RegionKind::Temporary, 5000);
38 window.add_region(tool_region);
39 }
40
41 if window.get_region("conversation").is_none() {
42 let conv_region = Region::new(
43 "conversation".to_string(),
44 RegionKind::SlidingWindow {
45 max_items: 50,
46 eviction_strategy: EvictionStrategy::PerItem,
47 },
48 10000,
49 );
50 window.add_region(conv_region);
51 }
52
53 for (name, content) in seeds {
54 let target = if name == "task" {
59 task_region_name(blueprint)
60 } else {
61 blueprint
62 .context_layout
63 .regions
64 .iter()
65 .find(|r| &r.name == name)
66 .map(|r| r.name.clone())
67 };
68 if let Some(region_name) = target {
69 let budget = window
74 .get_region(®ion_name)
75 .map(|r| r.max_tokens)
76 .unwrap_or(0);
77 let fitted = fit_seed_to_budget(content, budget);
78 let tokens = leviath_core::estimate_tokens(&fitted);
79 let _ = window.add_to_region(®ion_name, fitted, tokens);
80 }
81 }
82}
83
84const SEED_TRUNCATION_MARKER: &str =
86 "\n[...truncated by leviath: seed exceeded this region's budget]";
87
88fn fit_seed_to_budget(content: &str, max_tokens: usize) -> String {
92 let allowed = max_tokens.saturating_sub(1).saturating_mul(4);
95 if content.len() <= allowed {
96 return content.to_string();
97 }
98 let Some(room) = allowed.checked_sub(SEED_TRUNCATION_MARKER.len()) else {
101 return String::new();
102 };
103 format!(
104 "{}{SEED_TRUNCATION_MARKER}",
105 truncate_at_boundary(content, room)
106 )
107}
108
109fn task_region_name(blueprint: &Blueprint) -> Option<String> {
112 blueprint
113 .context_layout
114 .regions
115 .iter()
116 .find(|r| r.name == "task" && matches!(r.kind, RegionKind::Pinned))
117 .or_else(|| {
118 blueprint
119 .context_layout
120 .regions
121 .iter()
122 .find(|r| matches!(r.kind, RegionKind::Pinned))
123 })
124 .map(|r| r.name.clone())
125}
126
127pub fn init_window(window: &mut ContextWindow, blueprint: &Blueprint, task: &str) {
131 let seeds = HashMap::from([("task".to_string(), task.to_string())]);
132 init_window_seeded(window, blueprint, &seeds);
133}
134
135pub fn apply_layout(window: &mut ContextWindow, layout: &ContextLayout) {
140 let mut new_regions = Vec::new();
141 let mut kept: std::collections::HashSet<&str> = std::collections::HashSet::new();
142 for region_def in &layout.regions {
143 let mut new_region = Region::new(
144 region_def.name.clone(),
145 region_def.kind.clone(),
146 region_def.max_tokens,
147 );
148
149 if let Some(existing) = window.get_region(®ion_def.name) {
150 for entry in &existing.content {
156 let _ = new_region.carry_entry(entry.clone());
157 }
158 new_region.taint = existing.taint.clone();
161 }
162
163 kept.insert(region_def.name.as_str());
164 new_regions.push(new_region);
165 }
166
167 for infra in ["conversation", "tool_results"] {
174 if !kept.contains(infra)
175 && let Some(existing) = window.get_region(infra)
176 {
177 let mut carried = Region::new(
178 existing.name.clone(),
179 existing.kind.clone(),
180 existing.max_tokens,
181 );
182 for entry in &existing.content {
185 let _ = carried.carry_entry(entry.clone());
186 }
187 carried.taint = existing.taint.clone();
188 new_regions.push(carried);
189 }
190 }
191
192 window.regions = new_regions;
193 window.current_tokens = window.calculate_tokens();
194}
195
196#[cfg(test)]
197mod tests {
198 use super::{
199 SEED_TRUNCATION_MARKER, apply_layout, fit_seed_to_budget, init_window, init_window_seeded,
200 };
201 use crate::ContextWindow;
202 use leviath_core::{
203 Blueprint, ContextLayout, EvictionStrategy, RegionKind, Stage, blueprint::ModelConfig,
204 layout::RegionDefinition,
205 };
206 use std::collections::HashMap;
207
208 fn blueprint_with(regions: Vec<RegionDefinition>) -> Blueprint {
209 let layout = ContextLayout::new(regions, 100_000);
210 let stages = vec![Stage::new(
211 "main".to_string(),
212 ModelConfig::new("anthropic".to_string(), "claude-sonnet-4".to_string()),
213 )];
214 Blueprint::new("bp".to_string(), "desc".to_string(), stages, layout)
215 }
216
217 fn seeded_window(bp: &Blueprint, task: &str) -> ContextWindow {
218 let mut window = ContextWindow::new(100_000);
219 init_window(&mut window, bp, task);
220 window
221 }
222
223 #[test]
224 fn init_window_seeded_fills_multiple_named_regions_and_ignores_unknown() {
225 let bp = blueprint_with(vec![
226 RegionDefinition::new("task".to_string(), RegionKind::Pinned, 5000),
227 RegionDefinition::new("criteria".to_string(), RegionKind::Pinned, 5000),
228 ]);
229 let seeds = HashMap::from([
230 ("task".to_string(), "build a parser".to_string()),
231 ("criteria".to_string(), "focus on safety".to_string()),
232 ("ghost".to_string(), "no such region".to_string()),
233 ]);
234 let mut window = ContextWindow::new(100_000);
235 init_window_seeded(&mut window, &bp, &seeds);
236
237 assert!(
238 window
239 .get_region("task")
240 .unwrap()
241 .content
242 .iter()
243 .any(|e| e.content.contains("build a parser"))
244 );
245 assert!(
246 window
247 .get_region("criteria")
248 .unwrap()
249 .content
250 .iter()
251 .any(|e| e.content.contains("focus on safety"))
252 );
253 assert!(window.get_region("ghost").is_none());
255 }
256
257 #[test]
258 fn fit_seed_to_budget_leaves_a_fitting_seed_untouched() {
259 assert_eq!(fit_seed_to_budget("hello", 100), "hello");
260 let exact = "x".repeat(36);
262 assert_eq!(fit_seed_to_budget(&exact, 10), exact);
263 }
264
265 fn estimated_tokens(fitted: &str) -> usize {
268 leviath_core::estimate_tokens(fitted)
269 }
270
271 #[test]
272 fn fit_seed_to_budget_truncates_and_marks_an_oversized_seed() {
273 let big = "x".repeat(10_000);
274 let fitted = fit_seed_to_budget(&big, 100);
275 assert!(fitted.ends_with(SEED_TRUNCATION_MARKER));
276 let estimate = estimated_tokens(&fitted);
278 assert!(estimate <= 100, "estimate was {estimate}");
279 }
280
281 #[test]
282 fn fit_seed_to_budget_cuts_on_a_char_boundary() {
283 const MAX_TOKENS: usize = 60;
286 let room = (MAX_TOKENS - 1) * 4 - SEED_TRUNCATION_MARKER.len();
287 let mut s = "a".repeat(room - 1);
288 s.push('é'); s.push_str(&"b".repeat(500));
290 assert!(!s.is_char_boundary(room), "test must straddle the cut");
291
292 let fitted = fit_seed_to_budget(&s, MAX_TOKENS);
293 assert!(fitted.ends_with(SEED_TRUNCATION_MARKER));
294 assert!(estimated_tokens(&fitted) <= MAX_TOKENS);
295 assert_eq!(
297 fitted,
298 format!("{}{SEED_TRUNCATION_MARKER}", "a".repeat(room - 1))
299 );
300 }
301
302 #[test]
303 fn fit_seed_to_budget_yields_nothing_when_even_the_marker_cannot_fit() {
304 assert_eq!(fit_seed_to_budget("some content here", 2), "");
307 assert_eq!(fit_seed_to_budget("x", 0), "");
309 }
310
311 #[test]
312 fn init_window_seeded_truncates_a_seed_larger_than_its_region() {
313 let bp = blueprint_with(vec![RegionDefinition::new(
317 "facts".to_string(),
318 RegionKind::Pinned,
319 50,
320 )]);
321 let seeds = HashMap::from([("facts".to_string(), "y".repeat(10_000))]);
322 let mut window = ContextWindow::new(100_000);
323 init_window_seeded(&mut window, &bp, &seeds);
324
325 let region = window.get_region("facts").unwrap();
326 assert!(
327 !region.content.is_empty(),
328 "an oversized seed must be trimmed, not dropped"
329 );
330 assert!(region.content[0].content.ends_with(SEED_TRUNCATION_MARKER));
331 }
332
333 #[test]
334 fn init_window_seeded_task_key_falls_back_to_first_pinned() {
335 let bp = blueprint_with(vec![RegionDefinition::new(
338 "system".to_string(),
339 RegionKind::Pinned,
340 5000,
341 )]);
342 let seeds = HashMap::from([("task".to_string(), "fallback text".to_string())]);
343 let mut window = ContextWindow::new(100_000);
344 init_window_seeded(&mut window, &bp, &seeds);
345 assert!(
346 window
347 .get_region("system")
348 .unwrap()
349 .content
350 .iter()
351 .any(|e| e.content.contains("fallback text"))
352 );
353 }
354
355 #[test]
356 fn init_prefers_named_task_region_and_keeps_existing_infra_regions() {
357 let bp = blueprint_with(vec![
358 RegionDefinition::new("task".to_string(), RegionKind::Pinned, 5000),
359 RegionDefinition::new("tool_results".to_string(), RegionKind::Temporary, 5000),
360 RegionDefinition::new(
361 "conversation".to_string(),
362 RegionKind::SlidingWindow {
363 max_items: 10,
364 eviction_strategy: EvictionStrategy::PerItem,
365 },
366 10_000,
367 ),
368 ]);
369
370 let window = seeded_window(&bp, "do the thing");
371 assert!(
373 window
374 .get_region("task")
375 .unwrap()
376 .content
377 .iter()
378 .any(|e| e.content.contains("do the thing"))
379 );
380 assert_eq!(
382 window
383 .regions
384 .iter()
385 .filter(|r| r.name == "tool_results")
386 .count(),
387 1
388 );
389 assert_eq!(
390 window
391 .regions
392 .iter()
393 .filter(|r| r.name == "conversation")
394 .count(),
395 1
396 );
397 }
398
399 #[test]
400 fn init_adds_infra_regions_and_falls_back_to_first_pinned() {
401 let bp = blueprint_with(vec![RegionDefinition::new(
404 "system".to_string(),
405 RegionKind::Pinned,
406 5000,
407 )]);
408
409 let window = seeded_window(&bp, "seed task");
410 assert!(window.get_region("tool_results").is_some());
411 assert!(window.get_region("conversation").is_some());
412 assert!(
413 window
414 .get_region("system")
415 .unwrap()
416 .content
417 .iter()
418 .any(|e| e.content.contains("seed task"))
419 );
420 }
421
422 #[test]
423 fn init_without_pinned_region_does_not_seed_task() {
424 let bp = blueprint_with(vec![RegionDefinition::new(
425 "scratch".to_string(),
426 RegionKind::Temporary,
427 5000,
428 )]);
429
430 let window = seeded_window(&bp, "unseeded task");
431 assert!(window.get_region("scratch").unwrap().content.is_empty());
434 assert!(window.get_region("tool_results").is_some());
436 assert!(window.get_region("conversation").is_some());
437 }
438
439 #[test]
440 fn init_task_named_region_that_is_not_pinned_falls_back_to_first_pinned() {
441 let bp = blueprint_with(vec![
445 RegionDefinition::new("task".to_string(), RegionKind::Temporary, 5000),
446 RegionDefinition::new("system".to_string(), RegionKind::Pinned, 5000),
447 ]);
448
449 let window = seeded_window(&bp, "fallback seed");
450 assert!(window.get_region("task").unwrap().content.is_empty());
452 assert!(
454 window
455 .get_region("system")
456 .unwrap()
457 .content
458 .iter()
459 .any(|e| e.content.contains("fallback seed"))
460 );
461 }
462
463 #[test]
464 fn apply_layout_preserves_overlapping_content_and_creates_new_regions() {
465 let bp = blueprint_with(vec![RegionDefinition::new(
466 "system".to_string(),
467 RegionKind::Pinned,
468 5000,
469 )]);
470 let mut window = seeded_window(&bp, "carried content");
471
472 let new_layout = ContextLayout::new(
475 vec![
476 RegionDefinition::new("system".to_string(), RegionKind::Pinned, 5000),
477 RegionDefinition::new("scratch".to_string(), RegionKind::Temporary, 3000),
478 ],
479 8000,
480 );
481
482 apply_layout(&mut window, &new_layout);
483
484 assert_eq!(window.regions.len(), 4);
488 assert!(window.get_region("conversation").is_some());
489 assert!(window.get_region("tool_results").is_some());
490 assert!(
491 window
492 .get_region("system")
493 .unwrap()
494 .content
495 .iter()
496 .any(|e| e.content.contains("carried content"))
497 );
498 assert!(window.get_region("scratch").unwrap().content.is_empty());
499 assert_eq!(window.current_tokens, window.calculate_tokens());
501 assert!(window.current_tokens > 0);
502 }
503
504 #[test]
505 fn apply_layout_preserves_entry_kinds_and_taint_across_swap() {
506 let bp = blueprint_with(vec![RegionDefinition::new(
510 "task".to_string(),
511 RegionKind::Pinned,
512 5000,
513 )]);
514 let mut window = seeded_window(&bp, "the task");
515 window
516 .add_typed_entry(
517 "conversation",
518 leviath_core::EntryKind::AssistantTurn {
519 tool_calls: vec![leviath_core::SerializedToolCall {
520 id: "call_9".to_string(),
521 name: "shell".to_string(),
522 arguments: serde_json::json!({"command": "ls"}),
523 thought_signature: None,
524 }],
525 },
526 "running ls".to_string(),
527 10,
528 )
529 .unwrap();
530 window
531 .add_typed_entry(
532 "conversation",
533 leviath_core::EntryKind::ToolResult {
534 tool_call_id: "call_9".to_string(),
535 tool_name: "shell".to_string(),
536 is_error: false,
537 },
538 "file_a\nfile_b".to_string(),
539 10,
540 )
541 .unwrap();
542 window
543 .get_region_mut("conversation")
544 .unwrap()
545 .enable_taint_tracking();
546
547 let omitting = ContextLayout::new(
549 vec![RegionDefinition::new(
550 "task".to_string(),
551 RegionKind::Pinned,
552 5000,
553 )],
554 8000,
555 );
556 apply_layout(&mut window, &omitting);
557
558 let declaring = ContextLayout::new(
560 vec![
561 RegionDefinition::new("task".to_string(), RegionKind::Pinned, 5000),
562 RegionDefinition::new(
563 "conversation".to_string(),
564 RegionKind::SlidingWindow {
565 max_items: 10,
566 eviction_strategy: EvictionStrategy::PerItem,
567 },
568 10_000,
569 ),
570 ],
571 20_000,
572 );
573 apply_layout(&mut window, &declaring);
574
575 let conv = window.get_region("conversation").unwrap();
576 assert!(
577 conv.content.iter().any(|e| matches!(
578 &e.kind,
579 leviath_core::EntryKind::AssistantTurn { tool_calls }
580 if tool_calls.iter().any(|c| c.id == "call_9")
581 )),
582 "assistant turn must keep its typed tool_calls through both carry paths"
583 );
584 assert!(
585 conv.content.iter().any(|e| matches!(
586 &e.kind,
587 leviath_core::EntryKind::ToolResult { tool_call_id, .. }
588 if tool_call_id == "call_9"
589 )),
590 "tool result must keep its typed pairing through both carry paths"
591 );
592 assert!(
593 conv.taint.is_some(),
594 "region-level taint state must carry across layout swaps"
595 );
596 }
597
598 #[test]
599 fn apply_layout_carries_conversation_when_new_layout_omits_it() {
600 let bp = blueprint_with(vec![RegionDefinition::new(
604 "task".to_string(),
605 RegionKind::Pinned,
606 5000,
607 )]);
608 let mut window = seeded_window(&bp, "the task");
609 window
610 .add_typed_entry(
611 "conversation",
612 leviath_core::EntryKind::UserMessage,
613 "hello from stage 0".to_string(),
614 10,
615 )
616 .unwrap();
617
618 let next = ContextLayout::new(
620 vec![RegionDefinition::new(
621 "task".to_string(),
622 RegionKind::Pinned,
623 5000,
624 )],
625 8000,
626 );
627 apply_layout(&mut window, &next);
628
629 let conv = window
630 .get_region("conversation")
631 .expect("conversation carried across transition");
632 assert!(
633 conv.content
634 .iter()
635 .any(|e| e.content.contains("hello from stage 0")),
636 "carried conversation must retain its history"
637 );
638 }
639}