pub struct Conversation {
pub messages: Vec<Message>,
pub stream_buffer: Option<String>,
pub tool_call_buffer: Option<ToolCallBuffer>,
pub turn_tracker: TurnTracker,
pub cold_summaries: Vec<String>,
}Fields§
§messages: Vec<Message>§stream_buffer: Option<String>§tool_call_buffer: Option<ToolCallBuffer>§turn_tracker: TurnTracker§cold_summaries: Vec<String>Cold zone: FIFO queue of compressed history summaries (max 3). Each entry is an LLM-generated summary of older turns.
Implementations§
Source§impl Conversation
impl Conversation
pub fn new() -> Self
Sourcepub fn load(path: &Path) -> Self
pub fn load(path: &Path) -> Self
Load conversation history from disk. Never fails — returns empty on any error.
Sourcepub fn save(&self, path: &Path)
pub fn save(&self, path: &Path)
Save conversation history to disk atomically (write to temp, then rename).
Sourcepub fn history_path() -> PathBuf
pub fn history_path() -> PathBuf
Path to history file.
pub fn add_user_message(&mut self, content: &str)
Sourcepub fn cancel_current_turn(&mut self)
pub fn cancel_current_turn(&mut self)
Cancel the current active turn: save all conversation content up to the moment of cancel. The user cancelled because they want to redirect the model, not because they want to lose context — the LLM needs to see what it already did so it can adjust.
If the model issued tool calls that never got results, we append
(cancelled) ToolResult entries for them so the API doesn’t
reject the message sequence with “messages illegal”.
Sourcepub fn cancel_current_turn_including_user(&mut self)
pub fn cancel_current_turn_including_user(&mut self)
Cancel the current active turn AND remove the user message. Used on Error exits where leaving an orphan user message (no assistant reply) would cause weak models to return 0 tokens on the next turn — two consecutive User messages with no intervening Assistant confuses OpenAI-compatible APIs.
pub fn push_delta(&mut self, delta: &str)
Sourcepub fn clear_stream_buffer(&mut self)
pub fn clear_stream_buffer(&mut self)
Clear the stream buffer without finalizing (used when text output is actually a malformed tool call that will be re-processed).
pub fn finalize_stream(&mut self)
pub fn add_assistant_tool_calls( &mut self, text: Option<&str>, tool_calls: Vec<ToolCall>, reasoning: Option<&str>, )
Sourcepub fn add_assistant_tool_calls_with_thinking(
&mut self,
text: Option<&str>,
tool_calls: Vec<ToolCall>,
reasoning: Option<&str>,
thinking_blocks: Vec<ThinkingBlock>,
)
pub fn add_assistant_tool_calls_with_thinking( &mut self, text: Option<&str>, tool_calls: Vec<ToolCall>, reasoning: Option<&str>, thinking_blocks: Vec<ThinkingBlock>, )
Like add_assistant_tool_calls but additionally stores Anthropic
extended-thinking content blocks (text + signature pairs). The
blocks must be echoed verbatim on subsequent requests when the
upstream is Anthropic-style and thinking is enabled — otherwise
the next request gets 400 The content[].thinking in the thinking mode must be passed back to the API. Other provider paths
(OpenAI / Ollama) ignore this field via .. destructuring, so
leaving it populated is harmless across cross-provider switches.
pub fn add_tool_result(&mut self, result: ToolResult)
pub fn finalize_stream_with_tool_call( &mut self, tool_call: ToolCall, reasoning: Option<&str>, )
Sourcepub fn finalize_stream_with_tool_calls(
&mut self,
tool_calls: &[ToolCall],
reasoning: Option<&str>,
)
pub fn finalize_stream_with_tool_calls( &mut self, tool_calls: &[ToolCall], reasoning: Option<&str>, )
Finalize the current stream buffer with multiple tool calls at once (multi-tool support).
reasoning carries thinking-model reasoning_content accumulated during the stream;
it’s stored on the message so the send-side policy can echo it back when the
provider demands (see ReasoningPolicy).
Sourcepub fn finalize_stream_with_tool_calls_and_thinking(
&mut self,
tool_calls: &[ToolCall],
reasoning: Option<&str>,
thinking_blocks: Vec<ThinkingBlock>,
)
pub fn finalize_stream_with_tool_calls_and_thinking( &mut self, tool_calls: &[ToolCall], reasoning: Option<&str>, thinking_blocks: Vec<ThinkingBlock>, )
Variant that additionally records Anthropic extended-thinking
blocks for echo-back. See add_assistant_tool_calls_with_thinking.
pub fn to_provider_messages(&self, system_prompt: &str) -> Vec<Message>
Sourcepub fn to_provider_messages_windowed(
&self,
system_prompt: &str,
window: usize,
) -> Vec<Message>
pub fn to_provider_messages_windowed( &self, system_prompt: &str, window: usize, ) -> Vec<Message>
Like to_provider_messages but only sends the last window messages.
Ensures the window starts at a valid boundary — never in the middle
of a tool_call/tool_result pair (which causes API “messages illegal” errors).
Sourcepub fn apply_compression(&mut self, remove_count: usize, summary: String)
pub fn apply_compression(&mut self, remove_count: usize, summary: String)
Apply compression: store summary in cold zone, remove old messages.
remove_count = number of messages from the front to remove.
(Changed from turn-based to message-based to support single-user-message
sessions where turn_tracker has only 1-2 turns but 30+ messages.)
── CRITICAL INVARIANT ── After compression:
- All surviving turns must have: start_idx < new_messages.len()
- All surviving turns must have: end_idx() <= new_messages.len()
- All surviving turns must have: msg_count > 0 These invariants prevent underflow in on_user_message(msg_idx).
Trait Implementations§
Source§impl Debug for Conversation
impl Debug for Conversation
Auto Trait Implementations§
impl Freeze for Conversation
impl RefUnwindSafe for Conversation
impl Send for Conversation
impl Sync for Conversation
impl Unpin for Conversation
impl UnsafeUnpin for Conversation
impl UnwindSafe for Conversation
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more