codetether_agent/tool/
search_router.rs1use std::sync::Arc;
7
8use anyhow::Result;
9use async_trait::async_trait;
10use serde_json::{Value, json};
11
12use super::{Tool, ToolResult};
13use crate::provider::ProviderRegistry;
14use crate::search::{model::DEFAULT_ROUTER_MODEL, run_router_search};
15
16pub struct SearchTool {
19 registry: Arc<ProviderRegistry>,
20}
21
22impl SearchTool {
23 pub fn new(registry: Arc<ProviderRegistry>) -> Self {
24 Self { registry }
25 }
26}
27
28#[async_trait]
29impl Tool for SearchTool {
30 fn id(&self) -> &str {
31 "search"
32 }
33 fn name(&self) -> &str {
34 "Search Router"
35 }
36 fn description(&self) -> &str {
37 "search(query: string, top_n?: int, router_model?: string) — LLM-routed search. Picks grep/glob/websearch/webfetch/memory/rlm based on the query and returns normalized JSON."
38 }
39 fn parameters(&self) -> Value {
40 json!({
41 "type": "object",
42 "properties": {
43 "query": {"type": "string", "description": "Natural-language search query"},
44 "top_n": {"type": "integer", "description": "Max backends to run (default 1)"},
45 "router_model": {"type": "string", "description": "Override router model (default zai/glm-5.1)"}
46 },
47 "required": ["query"]
48 })
49 }
50 async fn execute(&self, args: Value) -> Result<ToolResult> {
51 let query = match args["query"].as_str() {
52 Some(q) if !q.is_empty() => q,
53 _ => {
54 return Ok(ToolResult::structured_error(
55 "INVALID_ARGUMENT",
56 "search",
57 "query is required",
58 Some(vec!["query"]),
59 Some(json!({"query": "where is fn main"})),
60 ));
61 }
62 };
63 let top_n = args["top_n"].as_u64().unwrap_or(1).max(1) as usize;
64 let router_model = args["router_model"]
65 .as_str()
66 .unwrap_or(DEFAULT_ROUTER_MODEL);
67 match run_router_search(Arc::clone(&self.registry), router_model, query, top_n).await {
68 Ok(result) => {
69 let payload = serde_json::to_string_pretty(&result)?;
70 Ok(ToolResult::success(payload)
71 .with_metadata("backends", json!(result.runs.len()))
72 .with_metadata("router_model", json!(result.router_model)))
73 }
74 Err(err) => Ok(ToolResult::error(format!("search router failed: {err}"))),
75 }
76 }
77}