Skip to content

Commit f0e3b1c

Browse files
committed
add conversation compaction to preserve context instead of trimming
1 parent 5468307 commit f0e3b1c

11 files changed

Lines changed: 492 additions & 27 deletions

File tree

.sofosrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ This prints:
363363

364364
### Anthropic API
365365
- Uses Claude Messages API (not legacy Completions)
366-
- Model: claude-sonnet-4-5 (default)
366+
- Model: claude-sonnet-4-6 (default)
367367
- Supports tool calling and server-side tools (web_search)
368368
- API version: 2023-06-01
369369
- Usage tracking: Automatic token counting and cost calculation

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to Sofos are documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
- Conversation compaction: replaces naive message trimming with intelligent context preservation
9+
- Two-phase approach: truncates large tool results first, then summarizes older messages via the LLM
10+
- Works with both Anthropic and OpenAI providers
11+
- Auto-triggers at 80% of token budget before sending the next request
12+
- `/compact` command for manual compaction
13+
- Shows "Compacting conversation..." animation during summarization
14+
- Falls back to trimming on failure or ESC interrupt
15+
716
### Changed
817
- Increase `MAX_TOOL_OUTPUT_TOKENS`
918

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Tested on macOS but should work on Linux and Windows as well.
4848
- **MCP Integration** - Connect to external tools via Model Context Protocol
4949
- **Visual Diffs** - Colored change display
5050
- **Iterative Tools** - Up to 200 tool calls per request
51+
- **Context Compaction** - Summarizes older messages instead of dropping them
5152
- **Cost Tracking** - Session token usage and cost estimates
5253
- **Safe Mode** - Read-only operation mode
5354

@@ -95,6 +96,7 @@ sofos
9596
- `/resume` - Resume previous session
9697
- `/clear` - Clear conversation history
9798
- `/think [on|off]` - Toggle extended thinking (shows status if no arg)
99+
- `/compact` - Compress conversation history to free up context (also triggers automatically at 80% token usage)
98100
- `/s` - Safe mode (read-only, prompt: **`λ:`**, blinking underscore (`_`) cursor)
99101
- `/n` - Normal mode (all tools, prompt: **`λ>`**, default cursor)
100102
- `/exit`, `/quit`, `/q`, `Ctrl+D` - Exit with cost summary
@@ -133,7 +135,7 @@ Exit summary shows token usage and estimated cost (based on official API pricing
133135
--api-key <KEY> Anthropic API key (overrides env var)
134136
--openai-api-key <KEY> OpenAI API key (overrides env var)
135137
--morph-api-key <KEY> Morph API key (overrides env var)
136-
--model <MODEL> Model to use (default: claude-sonnet-4-5)
138+
--model <MODEL> Model to use (default: claude-sonnet-4-6)
137139
--morph-model <MODEL> Morph model (default: morph-v3-fast)
138140
--max-tokens <N> Max response tokens (default: 8192)
139141
-t, --enable-thinking Enable extended thinking (default: false)

src/api/anthropic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ mod tests {
186186
fn test_request_with_thinking() {
187187
let thinking = Some(Thinking::enabled(3000));
188188
let request = CreateMessageRequest {
189-
model: "claude-sonnet-4-5".to_string(),
189+
model: "claude-sonnet-4-6".to_string(),
190190
max_tokens: 8192,
191191
messages: vec![],
192192
system: None,

src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct Cli {
3030
#[arg(long)]
3131
pub check_connection: bool,
3232

33-
#[arg(long, default_value = "claude-sonnet-4-5")]
33+
#[arg(long, default_value = "claude-sonnet-4-6")]
3434
pub model: String,
3535

3636
#[arg(long, default_value = "morph-v3-fast")]

src/commands/builtin.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ pub fn normal_mode_command(repl: &mut Repl) -> Result<CommandResult> {
4747
repl.disable_safe_mode();
4848
Ok(CommandResult::Continue)
4949
}
50+
51+
pub fn compact_command(repl: &mut Repl) -> Result<CommandResult> {
52+
repl.handle_compact_command()?;
53+
Ok(CommandResult::Continue)
54+
}

src/commands/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub enum Command {
2323
ThinkStatus,
2424
SafeMode,
2525
NormalMode,
26+
Compact,
2627
}
2728

2829
impl Command {
@@ -36,6 +37,7 @@ impl Command {
3637
"/think" => Some(Command::ThinkStatus),
3738
"/s" => Some(Command::SafeMode),
3839
"/n" => Some(Command::NormalMode),
40+
"/compact" => Some(Command::Compact),
3941
_ => None,
4042
}
4143
}
@@ -50,6 +52,7 @@ impl Command {
5052
Command::ThinkStatus => builtin::think_status_command(repl),
5153
Command::SafeMode => builtin::safe_mode_command(repl),
5254
Command::NormalMode => builtin::normal_mode_command(repl),
55+
Command::Compact => builtin::compact_command(repl),
5356
}
5457
}
5558
}
@@ -66,4 +69,5 @@ pub static COMMANDS: &[&str] = &[
6669
"/think",
6770
"/s",
6871
"/n",
72+
"/compact",
6973
];

src/config.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ pub struct SofosConfig {
88
pub max_file_size: usize,
99
#[allow(dead_code)]
1010
pub max_bash_output: usize,
11+
/// Auto-compact when token usage exceeds this ratio of max_context_tokens
12+
pub compaction_trigger_ratio: f64,
13+
/// Number of recent messages to preserve during compaction
14+
pub compaction_preserve_recent: usize,
15+
/// Truncate tool results longer than this (chars) during compaction
16+
pub tool_result_truncate_threshold: usize,
1117
}
1218

1319
impl Default for SofosConfig {
@@ -18,6 +24,9 @@ impl Default for SofosConfig {
1824
max_tool_iterations: 200,
1925
max_file_size: 10 * 1024 * 1024,
2026
max_bash_output: 50 * 1024 * 1024,
27+
compaction_trigger_ratio: 0.80,
28+
compaction_preserve_recent: 20,
29+
tool_result_truncate_threshold: 2000,
2130
}
2231
}
2332
}
@@ -77,5 +86,8 @@ mod tests {
7786
assert_eq!(config.max_tool_iterations, 200);
7887
assert_eq!(config.max_file_size, 10 * 1024 * 1024);
7988
assert_eq!(config.max_bash_output, 50 * 1024 * 1024);
89+
assert!((config.compaction_trigger_ratio - 0.80).abs() < f64::EPSILON);
90+
assert_eq!(config.compaction_preserve_recent, 20);
91+
assert_eq!(config.tool_result_truncate_threshold, 2000);
8092
}
8193
}

0 commit comments

Comments
 (0)