think stripping

This commit is contained in:
jaberjaber23
2026-03-05 15:41:08 +03:00
parent eafeb6a012
commit 06df0795c8
3 changed files with 47 additions and 5 deletions
+1 -1
View File
@@ -335,7 +335,7 @@ pub async fn chat_completions(
index: 0,
message: ChoiceMessage {
role: "assistant",
content: Some(result.response),
content: Some(crate::ws::strip_think_tags(&result.response)),
tool_calls: None,
},
finish_reason: "stop",
+5 -2
View File
@@ -321,8 +321,11 @@ pub async fn send_message(
.await
{
Ok(result) => {
// Strip <think>...</think> blocks from model output
let cleaned = crate::ws::strip_think_tags(&result.response);
// Guard: ensure we never return an empty response to the client
let response = if result.response.trim().is_empty() {
let response = if cleaned.trim().is_empty() {
format!(
"[The agent completed processing but returned no text response. ({} in / {} out | {} iter)]",
result.total_usage.input_tokens,
@@ -330,7 +333,7 @@ pub async fn send_message(
result.iterations,
)
} else {
result.response
cleaned
};
(
StatusCode::OK,
+41 -2
View File
@@ -621,8 +621,12 @@ async fn handle_text_message(
return;
}
// Strip <think>...</think> blocks from model output
// (e.g. MiniMax, DeepSeek reasoning tokens)
let cleaned_response = strip_think_tags(&result.response);
// Guard: ensure we never send an empty response
let content = if result.response.trim().is_empty() {
let content = if cleaned_response.trim().is_empty() {
format!(
"[The agent completed processing but returned no text response. ({} in / {} out | {} iter)]",
result.total_usage.input_tokens,
@@ -630,7 +634,7 @@ async fn handle_text_message(
result.iterations,
)
} else {
result.response
cleaned_response
};
// Estimate context pressure from last call
@@ -1156,6 +1160,27 @@ fn extract_status_code(s: &str) -> Option<u16> {
None
}
/// Strip `<think>...</think>` blocks from model output.
///
/// Some models (MiniMax, DeepSeek, etc.) wrap their reasoning in `<think>` tags.
/// These are internal chain-of-thought and shouldn't be shown to the user.
pub fn strip_think_tags(text: &str) -> String {
let mut result = String::with_capacity(text.len());
let mut remaining = text;
while let Some(start) = remaining.find("<think>") {
result.push_str(&remaining[..start]);
if let Some(end) = remaining[start..].find("</think>") {
remaining = &remaining[(start + end + 8)..]; // 8 = "</think>".len()
} else {
// Unclosed <think> tag — strip to end
remaining = "";
break;
}
}
result.push_str(remaining);
result
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
@@ -1232,4 +1257,18 @@ mod tests {
fn test_sanitize_trims_whitespace() {
assert_eq!(sanitize_user_input(" hello "), "hello");
}
#[test]
fn test_strip_think_tags() {
assert_eq!(
strip_think_tags("<think>reasoning here</think>The answer is 42."),
"The answer is 42."
);
assert_eq!(
strip_think_tags("Hello <think>\nsome thinking\n</think> world"),
"Hello world"
);
assert_eq!(strip_think_tags("No thinking here"), "No thinking here");
assert_eq!(strip_think_tags("<think>all thinking</think>"), "");
}
}