Skip to main content

opendev_repl/handlers/
thinking_handler.rs

1//! Handler for thinking/reasoning content.
2//!
3//! Mirrors `opendev/core/context_engineering/tools/handlers/thinking_handler.py`.
4//!
5//! Responsibilities:
6//! - Capture thinking blocks from model responses
7//! - Format thinking traces for display
8//! - Blend self-critique into responses at HIGH thinking level
9
10use std::collections::HashMap;
11
12use serde_json::Value;
13
14use super::traits::{HandlerResult, PreCheckResult, ToolHandler};
15
16/// Handler for the Think tool.
17pub struct ThinkingHandler;
18
19impl ThinkingHandler {
20    /// Create a new thinking handler.
21    pub fn new() -> Self {
22        Self
23    }
24
25    /// Format thinking content for display.
26    pub fn format_thinking(content: &str) -> String {
27        if content.is_empty() {
28            return String::new();
29        }
30
31        let mut result = String::with_capacity(content.len() + 40);
32        result.push_str("--- thinking ---\n");
33        result.push_str(content.trim());
34        result.push_str("\n--- end thinking ---");
35        result
36    }
37
38    /// Extract a summary line from thinking content.
39    pub fn summarize(content: &str, max_words: usize) -> String {
40        let words: Vec<&str> = content.split_whitespace().collect();
41        if words.len() <= max_words {
42            words.join(" ")
43        } else {
44            let mut summary: String = words[..max_words].join(" ");
45            summary.push_str("...");
46            summary
47        }
48    }
49}
50
51impl Default for ThinkingHandler {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57impl ToolHandler for ThinkingHandler {
58    fn handles(&self) -> &[&str] {
59        &["Think", "think"]
60    }
61
62    fn pre_check(&self, _tool_name: &str, _args: &HashMap<String, Value>) -> PreCheckResult {
63        PreCheckResult::Allow
64    }
65
66    fn post_process(
67        &self,
68        _tool_name: &str,
69        _args: &HashMap<String, Value>,
70        output: Option<&str>,
71        error: Option<&str>,
72        success: bool,
73    ) -> HandlerResult {
74        // Format thinking output with delimiters.
75        let formatted = output.map(Self::format_thinking);
76
77        HandlerResult {
78            output: formatted,
79            error: error.map(|s| s.to_string()),
80            success,
81            meta: Default::default(),
82        }
83    }
84}
85
86#[cfg(test)]
87#[path = "thinking_handler_tests.rs"]
88mod tests;