@@ -621,16 +621,20 @@ async fn handle_text_message(
621621 return ;
622622 }
623623
624+ // Strip <think>...</think> blocks from model output
625+ // (e.g. MiniMax, DeepSeek reasoning tokens)
626+ let cleaned_response = strip_think_tags ( & result. response ) ;
627+
624628 // Guard: ensure we never send an empty response
625- let content = if result . response . trim ( ) . is_empty ( ) {
629+ let content = if cleaned_response . trim ( ) . is_empty ( ) {
626630 format ! (
627631 "[The agent completed processing but returned no text response. ({} in / {} out | {} iter)]" ,
628632 result. total_usage. input_tokens,
629633 result. total_usage. output_tokens,
630634 result. iterations,
631635 )
632636 } else {
633- result . response
637+ cleaned_response
634638 } ;
635639
636640 // Estimate context pressure from last call
@@ -1156,6 +1160,27 @@ fn extract_status_code(s: &str) -> Option<u16> {
11561160 None
11571161}
11581162
1163+ /// Strip `<think>...</think>` blocks from model output.
1164+ ///
1165+ /// Some models (MiniMax, DeepSeek, etc.) wrap their reasoning in `<think>` tags.
1166+ /// These are internal chain-of-thought and shouldn't be shown to the user.
1167+ pub fn strip_think_tags ( text : & str ) -> String {
1168+ let mut result = String :: with_capacity ( text. len ( ) ) ;
1169+ let mut remaining = text;
1170+ while let Some ( start) = remaining. find ( "<think>" ) {
1171+ result. push_str ( & remaining[ ..start] ) ;
1172+ if let Some ( end) = remaining[ start..] . find ( "</think>" ) {
1173+ remaining = & remaining[ ( start + end + 8 ) ..] ; // 8 = "</think>".len()
1174+ } else {
1175+ // Unclosed <think> tag — strip to end
1176+ remaining = "" ;
1177+ break ;
1178+ }
1179+ }
1180+ result. push_str ( remaining) ;
1181+ result
1182+ }
1183+
11591184// ---------------------------------------------------------------------------
11601185// Tests
11611186// ---------------------------------------------------------------------------
@@ -1232,4 +1257,18 @@ mod tests {
12321257 fn test_sanitize_trims_whitespace ( ) {
12331258 assert_eq ! ( sanitize_user_input( " hello " ) , "hello" ) ;
12341259 }
1260+
1261+ #[ test]
1262+ fn test_strip_think_tags ( ) {
1263+ assert_eq ! (
1264+ strip_think_tags( "<think>reasoning here</think>The answer is 42." ) ,
1265+ "The answer is 42."
1266+ ) ;
1267+ assert_eq ! (
1268+ strip_think_tags( "Hello <think>\n some thinking\n </think> world" ) ,
1269+ "Hello world"
1270+ ) ;
1271+ assert_eq ! ( strip_think_tags( "No thinking here" ) , "No thinking here" ) ;
1272+ assert_eq ! ( strip_think_tags( "<think>all thinking</think>" ) , "" ) ;
1273+ }
12351274}
0 commit comments