Class: Kward::WebSearch
- Inherits:
-
Object
- Object
- Kward::WebSearch
- Defined in:
- lib/kward/tools/search/web.rb
Overview
Live web-search implementation with provider fallbacks.
Defined Under Namespace
Classes: NetHttpClient, Result, SearchResponse
Constant Summary collapse
- DEFAULT_MAX_RESULTS =
5- MAX_MAX_RESULTS =
20- MAX_QUERIES =
4- MAX_OUTPUT_BYTES =
8 * 1024
- MODEL_PROVIDER_MAX_TOKENS =
512- MAX_ANSWER_CHARS =
2_000- MAX_EXCERPT_CHARS =
300- HTTP_TIMEOUT_SECONDS =
10- DUCKDUCKGO_URL =
"https://html.duckduckgo.com/html/"- EXA_MCP_URL =
"https://mcp.exa.ai/mcp"- EXA_ANSWER_URL =
"https://api.exa.ai/answer"- EXA_SEARCH_URL =
"https://api.exa.ai/search"- PERPLEXITY_API_URL =
"https://api.perplexity.ai/chat/completions"- GEMINI_API_BASE =
"https://generativelanguage.googleapis.com/v1beta"- DEFAULT_GEMINI_MODEL =
"gemini-2.5-flash"- PUBLIC_SEARXNG_INSTANCES =
[ "https://searx.be", "https://search.inetol.net", "https://searx.tiekoetter.com" ].freeze
- PROVIDERS =
%w[auto exa perplexity gemini duckduckgo].freeze
Instance Method Summary collapse
- #available? ⇒ Boolean
-
#initialize(http_client: NetHttpClient.new, searxng_instances: PUBLIC_SEARXNG_INSTANCES, max_output_bytes: MAX_OUTPUT_BYTES, config: nil) ⇒ WebSearch
constructor
Creates an object for web search provider operations.
- #search(args = nil, cancellation: nil, **keyword_args) ⇒ Object
Constructor Details
#initialize(http_client: NetHttpClient.new, searxng_instances: PUBLIC_SEARXNG_INSTANCES, max_output_bytes: MAX_OUTPUT_BYTES, config: nil) ⇒ WebSearch
Creates an object for web search provider operations.
39 40 41 42 43 44 |
# File 'lib/kward/tools/search/web.rb', line 39 def initialize(http_client: NetHttpClient.new, searxng_instances: PUBLIC_SEARXNG_INSTANCES, max_output_bytes: MAX_OUTPUT_BYTES, config: nil) @http_client = http_client @searxng_instances = searxng_instances @max_output_bytes = max_output_bytes @config = config end |
Instance Method Details
#available? ⇒ Boolean
46 47 48 49 50 51 |
# File 'lib/kward/tools/search/web.rb', line 46 def available? enabled = boolean_config_value("enabled") return enabled unless enabled.nil? true end |
#search(args = nil, cancellation: nil, **keyword_args) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/kward/tools/search/web.rb', line 53 def search(args = nil, cancellation: nil, **keyword_args) args ||= keyword_args cancellation&.raise_if_cancelled! queries = args_value(args, "queries") return "Error: queries must be an array with 1-#{MAX_QUERIES} strings" unless valid_queries?(queries) max_results = bounded_max_results(args_value(args, "max_results") || args_value(args, "num_results")) provider_value = args_value(args, "provider") provider_value = config_value("provider") if provider_value.nil? provider_value = "auto" if provider_value.nil? || provider_value.to_s.empty? provider = normalize_provider(provider_value) return "Error: provider must be one of: #{PROVIDERS.join(", ")}" unless provider recency_value = args_value(args, "recency_filter") || args_value(args, "recencyFilter") recency = normalize_recency(recency_value) return "Error: recency_filter must be one of: day, week, month, year" if recency_value && !recency = { max_results: max_results, recency_filter: recency, domain_filter: normalize_domain_filter(args_value(args, "domain_filter") || args_value(args, "domainFilter")), provider: provider } sections = ["# Web search"] failures = [] succeeded = 0 queries.each do |query| cancellation&.raise_if_cancelled! response, error = search_query(query, , cancellation: cancellation) succeeded += 1 if successful_response?(response) failures << "#{query}: #{error}" if error && !successful_response?(response) sections << format_query_results(query, response, error) end if succeeded.zero? return "Error: web_search found no results\n#{failures.map { |failure| "- #{failure}" }.join("\n")}".strip end sections.insert(1, "- Queries: #{queries.length}\n- Succeeded: #{succeeded}\n- Failed: #{queries.length - succeeded}") sections.insert(2, "Use fetch_content with a result URL to verify human-readable pages, or fetch_raw for specs, JSON, YAML, XML, and other machine-readable resources.") truncate_output(sections.join("\n\n")) end |