//! Context construction strategies — one entry point per model/provider.
//!
//! This module owns the [`CtxBuilder`] trait (defined below) and the
//! registry function [`for_provider`] (in [`resolver`]). Each concrete
//! builder lives in its own file to keep [`mod.rs`](self) thin:
//!
//! | File | Role |
//! |---------------|-------------------------------------------------------|
//! | `mod.rs` | `CtxBuilder` trait definition + re-exports |
//! | `resolver.rs` | `for_provider` dispatch (add new models here) |
//! | `render.rs` | Default render / compression-plan policy |
//! | `default.rs` | `DefaultCtx` — thin wrapper over `render` |
//! | `ollama.rs` | `OllamaCtx` — small-window local models |
//! | `truncate.rs` | Shared per-tool truncation helpers |
//!
//! Adding a new per-model ctx strategy:
//!
//! 1. Create `ctx/<name>.rs` with a `pub struct XxxCtx` and
//! `impl CtxBuilder for XxxCtx`. Keep all ctor + tests + impl
//! methods in that single file.
//! 2. Declare `pub mod <name>;` below.
//! 3. Register in `resolver::for_provider`.
//!
//! The trait is narrow on purpose: `build_messages` owns the full
//! render path for its model, including any system-prompt variation,
//! cold-zone handling, or tool-schema trimming. The shared
//! [`render`] and [`truncate`] modules offer building blocks that
//! impls may call or ignore at will.
pub mod default;
pub mod env;
pub mod file_store;
pub mod ollama;
pub mod render;
pub mod resolver;
pub mod truncate;
use crate::conversation::message::Message;
use crate::conversation::{ContextStats, Conversation};
use crate::tool::ToolResult;
pub use default::DefaultCtx;
pub use env::EnvSnapshot;
pub use ollama::OllamaCtx;
pub use resolver::for_provider;
/// Per-session context construction strategy. Selected once at
/// `AgentLoop::new` via [`for_provider`] and rebuilt on `ReloadConfig`.
pub trait CtxBuilder: Send + Sync {
/// Build the messages array to send to the LLM for this turn.
///
/// Implementations are free to:
/// - Transform `system_prompt` (strip tool schemas for small models,
/// add cache-friendly markers for Claude, replace entirely for
/// fine-tuned models)
/// - Choose any render pipeline (delegate to
/// [`crate::ctx::render::build_messages`], call shared helpers
/// directly, or roll their own)
/// - Decide how to handle `turn_reminder` — per-turn dynamic
/// context (git status, current task, prev edited files). The
/// default policy prepends it to the last User message for
/// system-prompt cache stability; a small-window model might
/// drop it to save tokens; a cache-sensitive model might insert
/// it as its own System message to keep the stable prefix clean.
/// Pass `""` when no reminder applies.
fn build_messages(
&self,
conv: &Conversation,
system_prompt: &str,
turn_reminder: &str,
) -> (Vec<Message>, ContextStats);
/// Whether the conversation should be compressed. Default: never.
fn needs_compression(&self, _conv: &Conversation, _system_tokens: usize) -> bool {
false
}
/// Produce a compression plan `(content_to_summarize,
/// messages_to_remove)`. `keep_ceiling` is the max tokens the kept tail
/// may occupy (window minus system/tools/cold-zone/output reserve), so
/// reasoning-heavy tails get drained to fit. Default: `None`.
fn compression_plan(
&self,
_conv: &Conversation,
_keep_ceiling: usize,
) -> Option<(String, usize)> {
None
}
/// Truncate a single tool output in place.
fn truncate_tool_output(&self, result: &mut ToolResult, tool_name: &str);
/// Effective token budget for this strategy.
///
/// Reflects any defensive clamps the impl applies (e.g. `OllamaCtx`
/// floors at 4K even if `provider.context_window == 0`). Callers
/// that need the actual budget — `ctx_budget_hint` reset, datalog,
/// per-tool truncation — should use this instead of
/// `Config::default_context_window()`, which returns the raw,
/// unclamped value and may diverge for degenerate configs.
fn ctx_window(&self) -> usize;
/// Human-readable name for logging / debugging.
fn name(&self) -> &'static str;
}