|
| 1 | +use log::info; |
| 2 | +use rmcp::{ |
| 3 | + ErrorData as McpError, |
| 4 | + handler::server::wrapper::Parameters, |
| 5 | + model::{CallToolResult, Content}, |
| 6 | + tool, tool_router, |
| 7 | +}; |
| 8 | + |
| 9 | +use super::{ |
| 10 | + ai_config::AiConfig, |
| 11 | + ai_manager::ChipmunkAI, |
| 12 | + parameters::{FilterParameter, SearchFilter}, |
| 13 | +}; |
| 14 | + |
| 15 | +#[tool_router] |
| 16 | +impl ChipmunkAI { |
| 17 | + #[allow(dead_code)] |
| 18 | + pub fn new(config: AiConfig) -> Self { |
| 19 | + Self { |
| 20 | + config, |
| 21 | + tool_router: Self::tool_router(), |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + #[tool(description = r#"Generate SearchFilter objects for filtering logs. |
| 26 | +
|
| 27 | +This tool accepts one or more filter specifications and returns a list of SearchFilter objects. |
| 28 | +Each filter can be customized with flags for regex matching, case sensitivity, and word boundaries. |
| 29 | +
|
| 30 | +**Input Parameters:** |
| 31 | +- `filters`: An array of filter objects, where each object contains: |
| 32 | + - `filter` (string): The text or pattern to search for |
| 33 | + - `is_regex` (boolean): true if the filter is a regular expression pattern |
| 34 | + - `ignore_case` (boolean): true for case-insensitive matching |
| 35 | + - `is_word` (boolean): true to match whole words only (word boundary matching) |
| 36 | +
|
| 37 | +**Usage Examples:** |
| 38 | +
|
| 39 | +Single filter: |
| 40 | +- Input: [{"filter": "error", "is_regex": false, "ignore_case": false, "is_word": false}] |
| 41 | +- Use case: Find exact matches of "error" |
| 42 | +
|
| 43 | +Multiple filters: |
| 44 | +- Input: [ |
| 45 | + {"filter": "ERROR", "is_regex": false, "ignore_case": true, "is_word": false}, |
| 46 | + {"filter": "\\d{4}-\\d{2}-\\d{2}", "is_regex": true, "ignore_case": false, "is_word": false} |
| 47 | + ] |
| 48 | +- Use case: Find "ERROR" (any case) OR date patterns |
| 49 | +
|
| 50 | +Common patterns: |
| 51 | +- Case-insensitive word: {"filter": "warning", "is_regex": false, "ignore_case": true, "is_word": true} |
| 52 | +- Regex pattern: {"filter": "\\b(error|fail|exception)\\b", "is_regex": true, "ignore_case": false, "is_word": false} |
| 53 | +- Exact match: {"filter": "timeout", "is_regex": false, "ignore_case": false, "is_word": false} |
| 54 | +
|
| 55 | +**Natural Language Interpretation:** |
| 56 | +When the user provides natural language instructions, interpret them as follows: |
| 57 | +- "error" → single filter for "error" |
| 58 | +- "error or warning" → two filters, one for "error" and one for "warning" |
| 59 | +- "case-insensitive ERROR" → set ignore_case: true |
| 60 | +- "match the word 'timeout'" → set is_word: true |
| 61 | +- "regex pattern \\d+" → set is_regex: true |
| 62 | +- "find ERROR, WARNING, and CRITICAL" → three separate filters |
| 63 | +"#)] |
| 64 | + async fn apply_filters( |
| 65 | + &self, |
| 66 | + Parameters(param): Parameters<FilterParameter>, |
| 67 | + ) -> Result<CallToolResult, McpError> { |
| 68 | + let filters = param |
| 69 | + .filters |
| 70 | + .iter() |
| 71 | + .map(|f| SearchFilter { |
| 72 | + value: f.value.clone(), |
| 73 | + is_regex: f.is_regex, |
| 74 | + is_word: f.is_word, |
| 75 | + ignore_case: f.ignore_case, |
| 76 | + }) |
| 77 | + .collect::<Vec<SearchFilter>>(); |
| 78 | + info!("Received Filters from the LLM Agent: {filters:?}"); |
| 79 | + // TODO: Apply filter via channel |
| 80 | + Ok(CallToolResult::success(vec![Content::json( |
| 81 | + "Applied filters to the logs", |
| 82 | + )?])) |
| 83 | + } |
| 84 | +} |
0 commit comments