Watch
1
0
Fork
You've already forked souveraine
0

turn: the cache split reaches the message, not just a log line

a10ef5e added the counters and nothing read them — assistant_with_usage
had zero callers, so ConversationMessage.usage was always None and the
only proof caching worked was a grep. The turn now sums usage across its
rounds and commits it with the reply, and the per-round log carries the
split on every provider rather than only the Anthropic bridge.

memory: expose sliding_reflect. The tool offered sliding_window, whose
own doc comment says to use SlidingReflect unless you want blind
truncation, and hid the safe variant entirely.
This commit is contained in:
Fimeg 2026-08-10 19:35:34 -04:00
commit 6eda491496
2 changed files with 23 additions and 4 deletions

View file

@ -1347,8 +1347,8 @@ Paths are relative to my memory directory. Frontmatter description is required o
},
"strategy": {
"type": "string",
"enum": ["microcompact", "sliding_window", "summary", "cull"],
"description": "Compaction strategy (for compact subcommand). sliding_window (fast, drops middle), summary (LLM), microcompact (tool-result placeholder), cull (greetings)"
"enum": ["microcompact", "sliding_window", "sliding_reflect", "summary", "cull"],
"description": "Compaction strategy (for compact subcommand). microcompact (clears old tool output, drops nothing), cull (drops throwaways), sliding_window (fast, drops the middle blind), sliding_reflect (the same slide, but a pass reads the middle first and carries its threads forward), summary (LLM, highest fidelity)"
}
},
"required": ["command"]

View file

@ -10,7 +10,7 @@ use crate::bridge::bifrost::{
use crate::bridge::model_router::TokenCounter;
use crate::core::compact::CompactionEngine;
use crate::core::nervous::{EventBus, SensorEvent};
use crate::core::session::{ContentBlock, ConversationMessage, MessageRole};
use crate::core::session::{ContentBlock, ConversationMessage, MessageRole, TokenUsage};
use crate::core::tools::defs::ToolContext;
use crate::server::consciousness_engine::ConsciousnessEvent;
use crate::server::SouveraineServer;
@ -302,6 +302,11 @@ pub(crate) async fn run_turn(
let mut checkpoint_blocks: Vec<crate::server::consciousness_engine::CheckpointToolBlock> =
Vec::new();
// What the turn actually cost, summed over its rounds. Each round re-sends
// the prefix, so input and cache_read climb per round — that is what was
// billed, and the ratio between them is the only proof caching still works.
let mut turn_usage = TokenUsage::default();
// Announce this turn's lifecycle onto the nervous system so any
// sensorium (Matrix, mobile) can drive itself off the event stream.
let dispatcher = crate::core::nervous::turn_dispatcher::TurnEventDispatcher::new(
@ -394,11 +399,20 @@ pub(crate) async fn run_turn(
res = llm.chat_completion_with_strain(req) => res?,
};
if let Some(u) = &response.usage {
turn_usage.input_tokens += u.prompt_tokens;
turn_usage.output_tokens += u.completion_tokens;
turn_usage.cache_creation_input_tokens += u.cache_write_tokens;
turn_usage.cache_read_input_tokens += u.cache_read_tokens;
}
tracing::info!(
elapsed = ?turn_start.elapsed(),
tool_round = tool_round,
tool_calls = response.tool_calls.len(),
content_len = response.content.len(),
cache_read = turn_usage.cache_read_input_tokens,
cache_write = turn_usage.cache_creation_input_tokens,
"LLM call returned"
);
@ -878,7 +892,12 @@ pub(crate) async fn run_turn(
if !(halted_by_subconscious && committed_content.is_empty()) {
server.sessions.add_message(
&conversation_id,
ConversationMessage::assistant_text(&committed_content),
ConversationMessage::assistant_with_usage(
vec![ContentBlock::Text {
text: committed_content.clone(),
}],
Some(turn_usage),
),
)?;
}