Skip to main content

atomcode_core/ctx/
mod.rs

1//! Context construction strategies — one entry point per model/provider.
2//!
3//! This module owns the [`CtxBuilder`] trait (defined below) and the
4//! registry function [`for_provider`] (in [`resolver`]). Each concrete
5//! builder lives in its own file to keep [`mod.rs`](self) thin:
6//!
7//! | File          | Role                                                  |
8//! |---------------|-------------------------------------------------------|
9//! | `mod.rs`      | `CtxBuilder` trait definition + re-exports            |
10//! | `resolver.rs` | `for_provider` dispatch (add new models here)         |
11//! | `render.rs`   | Default render / compression-plan policy              |
12//! | `default.rs`  | `DefaultCtx` — thin wrapper over `render`             |
13//! | `ollama.rs`   | `OllamaCtx` — small-window local models               |
14//! | `truncate.rs` | Shared per-tool truncation helpers                    |
15//!
16//! Adding a new per-model ctx strategy:
17//!
18//! 1. Create `ctx/<name>.rs` with a `pub struct XxxCtx` and
19//!    `impl CtxBuilder for XxxCtx`. Keep all ctor + tests + impl
20//!    methods in that single file.
21//! 2. Declare `pub mod <name>;` below.
22//! 3. Register in `resolver::for_provider`.
23//!
24//! The trait is narrow on purpose: `build_messages` owns the full
25//! render path for its model, including any system-prompt variation,
26//! cold-zone handling, or tool-schema trimming. The shared
27//! [`render`] and [`truncate`] modules offer building blocks that
28//! impls may call or ignore at will.
29
30pub mod default;
31pub mod env;
32pub mod file_store;
33pub mod ollama;
34pub mod render;
35pub mod resolver;
36pub mod truncate;
37
38use crate::conversation::message::Message;
39use crate::conversation::{ContextStats, Conversation};
40use crate::tool::ToolResult;
41
42pub use default::DefaultCtx;
43pub use env::EnvSnapshot;
44pub use ollama::OllamaCtx;
45pub use resolver::for_provider;
46
47/// Per-session context construction strategy. Selected once at
48/// `AgentLoop::new` via [`for_provider`] and rebuilt on `ReloadConfig`.
49pub trait CtxBuilder: Send + Sync {
50    /// Build the messages array to send to the LLM for this turn.
51    ///
52    /// Implementations are free to:
53    /// - Transform `system_prompt` (strip tool schemas for small models,
54    ///   add cache-friendly markers for Claude, replace entirely for
55    ///   fine-tuned models)
56    /// - Choose any render pipeline (delegate to
57    ///   [`crate::ctx::render::build_messages`], call shared helpers
58    ///   directly, or roll their own)
59    /// - Decide how to handle `turn_reminder` — per-turn dynamic
60    ///   context (git status, current task, prev edited files). The
61    ///   default policy prepends it to the last User message for
62    ///   system-prompt cache stability; a small-window model might
63    ///   drop it to save tokens; a cache-sensitive model might insert
64    ///   it as its own System message to keep the stable prefix clean.
65    ///   Pass `""` when no reminder applies.
66    fn build_messages(
67        &self,
68        conv: &Conversation,
69        system_prompt: &str,
70        turn_reminder: &str,
71    ) -> (Vec<Message>, ContextStats);
72
73    /// Whether the conversation should be compressed. Default: never.
74    fn needs_compression(&self, _conv: &Conversation, _system_tokens: usize) -> bool {
75        false
76    }
77
78    /// Produce a compression plan `(content_to_summarize,
79    /// messages_to_remove)`. Default: `None` (no compression).
80    fn compression_plan(&self, _conv: &Conversation) -> Option<(String, usize)> {
81        None
82    }
83
84    /// Truncate a single tool output in place.
85    fn truncate_tool_output(&self, result: &mut ToolResult, tool_name: &str);
86
87    /// Effective token budget for this strategy.
88    ///
89    /// Reflects any defensive clamps the impl applies (e.g. `OllamaCtx`
90    /// floors at 4K even if `provider.context_window == 0`). Callers
91    /// that need the actual budget — `ctx_budget_hint` reset, datalog,
92    /// per-tool truncation — should use this instead of
93    /// `Config::default_context_window()`, which returns the raw,
94    /// unclamped value and may diverge for degenerate configs.
95    fn ctx_window(&self) -> usize;
96
97    /// Human-readable name for logging / debugging.
98    fn name(&self) -> &'static str;
99}