Skip to content

Commit 0bc26b2

Browse files
committed
refactor(chat): remove message linearization
Remove linearize.rs module and its usage from chat preparation pipeline. The linearization logic (merging consecutive user-like messages, folding tool-appendable content into tool/diff messages) is no longer needed as the underlying message processing has evolved. This simplifies the chat preparation flow while preserving cache hits through other mechanisms. Includes minor fixes: - gui: fix coin display logic in UsageCounter - queue: handle null boost_reasoning in patches - caps: add Default impl for CodeAssistantCaps - session: improve skill deactivation compaction - cache_guard: strict tools array equality for cache invalidation - other: snapshot param handling, tool sorting, vecdb scope filtering
1 parent 58fbd82 commit 0bc26b2

18 files changed

Lines changed: 426 additions & 1408 deletions

File tree

refact-agent/engine/src/caps/caps.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ impl ChatModelRecord {
151151
Some("anthropic_effort".to_string())
152152
} else if self.supports_thinking_budget {
153153
Some("anthropic_budget".to_string())
154+
} else if self.reasoning_effort_options.is_some() {
155+
Some("effort".to_string())
154156
} else {
155157
None
156158
}
@@ -264,7 +266,7 @@ impl Default for CapsMetadata {
264266
}
265267
}
266268

267-
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
269+
#[derive(Debug, Serialize, Deserialize, Clone)]
268270
pub struct CodeAssistantCaps {
269271
#[serde(deserialize_with = "normalize_string")]
270272
pub cloud_name: String,
@@ -303,6 +305,26 @@ pub struct CodeAssistantCaps {
303305
pub user_defaults: ProviderDefaults,
304306
}
305307

308+
impl Default for CodeAssistantCaps {
309+
fn default() -> Self {
310+
Self {
311+
cloud_name: String::new(),
312+
telemetry_basic_dest: default_telemetry_basic_dest(),
313+
telemetry_basic_retrieve_my_own: default_telemetry_retrieve_my_own(),
314+
completion_models: IndexMap::new(),
315+
chat_models: IndexMap::new(),
316+
embedding_model: EmbeddingModelRecord::default(),
317+
defaults: DefaultModels::default(),
318+
caps_version: 0,
319+
customization: String::new(),
320+
hf_tokenizer_template: default_hf_tokenizer_template(),
321+
metadata: CapsMetadata::default(),
322+
model_caps: Arc::new(std::collections::HashMap::new()),
323+
user_defaults: crate::providers::config::ProviderDefaults::default(),
324+
}
325+
}
326+
}
327+
306328
fn default_telemetry_retrieve_my_own() -> String {
307329
"https://www.smallcloud.ai/v1/telemetry-retrieve-my-own-stats".to_string()
308330
}
@@ -972,9 +994,6 @@ pub async fn load_caps(
972994
(caps, vec![server_provider])
973995
}
974996
Err(e) => {
975-
if is_refact {
976-
return Err(format!("Cloud model catalog fetch failed: {}", e));
977-
}
978997
warn!("Cloud caps fetch failed ({}), falling back to local providers only", e);
979998
(CodeAssistantCaps::default(), vec![])
980999
}

refact-agent/engine/src/chat/cache_guard.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ fn is_append_only_prefix_inner(
6767
| (Value::Number(_), Value::Number(_))
6868
| (Value::String(_), Value::String(_)) => prev == next,
6969
(Value::Array(a), Value::Array(b)) => {
70+
// The "tools" array is part of the prompt prefix — any change (including
71+
// appending a new tool) invalidates the LLM cache. Require strict equality.
72+
if parent_key == Some("tools") {
73+
return a == b;
74+
}
7075
if a.len() > b.len() {
7176
return false;
7277
}
@@ -300,6 +305,31 @@ mod tests {
300305
assert!(!is_append_only_prefix(&prev, &next));
301306
}
302307

308+
#[test]
309+
fn test_tools_array_strict_equality() {
310+
let tool_a = json!({"type": "function", "function": {"name": "tool_a", "description": "A"}});
311+
let tool_b = json!({"type": "function", "function": {"name": "tool_b", "description": "B"}});
312+
313+
// Identical tools → OK
314+
let prev = json!({"messages": [1], "tools": [tool_a.clone()]});
315+
let next = json!({"messages": [1, 2], "tools": [tool_a.clone()]});
316+
assert!(is_append_only_prefix(&prev, &next));
317+
318+
// New tool appended mid-session → violation (breaks LLM cache prefix)
319+
let next_extra = json!({"messages": [1, 2], "tools": [tool_a.clone(), tool_b.clone()]});
320+
assert!(!is_append_only_prefix(&prev, &next_extra));
321+
322+
// Tool removed mid-session → violation
323+
let prev2 = json!({"messages": [1], "tools": [tool_a.clone(), tool_b.clone()]});
324+
let next_removed = json!({"messages": [1, 2], "tools": [tool_a.clone()]});
325+
assert!(!is_append_only_prefix(&prev2, &next_removed));
326+
327+
// Tool description changed mid-session → violation
328+
let tool_a_changed = json!({"type": "function", "function": {"name": "tool_a", "description": "Changed"}});
329+
let next_changed = json!({"messages": [1, 2], "tools": [tool_a_changed]});
330+
assert!(!is_append_only_prefix(&prev, &next_changed));
331+
}
332+
303333
#[test]
304334
fn test_append_only_prefix_messages_keys_strict() {
305335
let prev = json!({

0 commit comments

Comments
 (0)