agent-orchestrator-sdk 0.1.1

Rust SDK for orchestrating LLM-powered agents, shared task execution, and teammate coordination
Documentation
use async_trait::async_trait;
use reqwest::Url;
use scraper::{Html, Selector};
use serde_json::json;

use crate::error::SdkResult;
use crate::traits::tool::{Tool, ToolDefinition};

pub struct WebSearchTool;

#[async_trait]
impl Tool for WebSearchTool {
    fn definition(&self) -> ToolDefinition {
        ToolDefinition {
            name: "web_search".to_string(),
            description: "Search the public web for up-to-date information and return a compact list of result titles and URLs.".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "query": { "type": "string", "description": "Search query" },
                    "max_results": { "type": "integer", "description": "Maximum number of results to return (default: 5)" }
                },
                "required": ["query"]
            }),
        }
    }

    async fn execute(&self, arguments: serde_json::Value) -> SdkResult<serde_json::Value> {
        let query = arguments["query"].as_str().unwrap_or("").trim();
        let max_results = arguments["max_results"].as_u64().unwrap_or(5) as usize;

        if query.is_empty() {
            return Ok(json!({ "error": "Missing 'query' argument" }));
        }

        let mut url = Url::parse("https://html.duckduckgo.com/html/")
            .expect("hardcoded DuckDuckGo HTML URL should be valid");
        url.query_pairs_mut().append_pair("q", query);

        let client = reqwest::Client::builder()
            .user_agent("agent-sdk-web-search/0.1")
            .build()?;

        let html = client.get(url).send().await?.text().await?;
        let document = Html::parse_document(&html);
        let selector = Selector::parse("a.result__a")
            .expect("hardcoded search result selector should be valid");

        let mut results = Vec::new();

        for link in document.select(&selector).take(max_results) {
            let title = link.text().collect::<Vec<_>>().join(" ").trim().to_string();
            let href = link.value().attr("href").unwrap_or("").to_string();

            if title.is_empty() || href.is_empty() {
                continue;
            }

            results.push(json!({
                "title": title,
                "url": href,
            }));
        }

        Ok(json!({
            "query": query,
            "results": results,
            "count": results.len(),
        }))
    }
}