Class: Pikuri::Tool::Search::Brave
- Inherits:
-
Object
- Object
- Pikuri::Tool::Search::Brave
- Defined in:
- lib/pikuri/tool/search/brave.rb
Overview
Performs a Brave Search via the official Web Search API, returning hits as Result rows. Split into a thin HTTP fetch (#search) and a pure parser (.parse) so tests exercise the parser against fixture JSON. Engines#search owns the final Markdown rendering.
Constructed with its API key (+Brave.new(api_key:)+); Engines builds one only when a Brave key was configured. pikuri reads no key from the environment (CLAUDE.md "Environment is not a secret store"). Key at https://api-dashboard.search.brave.com — the free "Data for Search" tier allows 1 query/sec, ~2k/month.
Privacy posture
Brave's API notice retains Search Query Logs 90 days and collects no identifiers linking a query to a person; it publicly commits not to train on query data, and offers Zero Data Retention — but only on Enterprise, not the free tier pikuri defaults to. Bottom line: the cleanest API-level posture of pikuri's three providers (no training, no IP linkage, capped 90-day retention), but a logged 90-day window on the cheap tier, so not a ZDR substitute for genuinely sensitive queries.
Constant Summary collapse
- ENDPOINT =
Returns Web Search endpoint.
'https://api.search.brave.com/res/v1/web/search'- DEFAULT_MAX_RESULTS =
Returns default number of results returned.
10- LIMITER =
Returns free-tier Brave caps at 1 req/sec; the 5-minute cooldown protects the monthly quota from doomed retries on a 429.
RateLimiter.new(min_interval: 1.0, cooldown: 300.0)
Class Method Summary collapse
-
.parse(json, max_results: DEFAULT_MAX_RESULTS) ⇒ Array<Result>
Parse a Brave JSON response into Result rows (++ highlights stripped).
Instance Method Summary collapse
-
#initialize(api_key:) ⇒ Brave
constructor
A new instance of Brave.
-
#label ⇒ String
Short provider label for Engines.
-
#search(query, max_results: DEFAULT_MAX_RESULTS, cancellable: Pikuri::Agent::Control::Cancellable::NEVER) ⇒ Array<Result>
Fetch results for
queryas anArray<Result>.
Constructor Details
#initialize(api_key:) ⇒ Brave
Returns a new instance of Brave.
44 45 46 47 48 |
# File 'lib/pikuri/tool/search/brave.rb', line 44 def initialize(api_key:) raise ArgumentError, 'Brave Search API key is blank' if api_key.to_s.strip.empty? @api_key = api_key end |
Class Method Details
.parse(json, max_results: DEFAULT_MAX_RESULTS) ⇒ Array<Result>
Parse a Brave JSON response into Result rows (++ highlights
stripped). A genuine "no results" payload (recognized search shape,
empty mixed.main/top/side) returns [] so Engines#search renders
its stub; any other zero-result shape raises.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/pikuri/tool/search/brave.rb', line 99 def self.parse(json, max_results: DEFAULT_MAX_RESULTS) data = JSON.parse(json) results = Array(data.dig('web', 'results')).take(max_results).filter_map do |r| href = r['url'].to_s next nil if href.empty? Result.new( url: href, title: strip_html(r['title']), body: strip_html(r['description']) ) end if results.empty? return [] if genuine_no_results?(data) raise diagnose_empty(data, json) end results end |
Instance Method Details
#label ⇒ String
Returns short provider label for Engines.
51 52 53 |
# File 'lib/pikuri/tool/search/brave.rb', line 51 def label 'Brave' end |
#search(query, max_results: DEFAULT_MAX_RESULTS, cancellable: Pikuri::Agent::Control::Cancellable::NEVER) ⇒ Array<Result>
Fetch results for query as an Array<Result>. Throttled to 1/sec and
circuit-broken 5 min on rate-limit/quota (see LIMITER). The caller
(Engines#search) normalizes the query and wraps this in a cache.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/pikuri/tool/search/brave.rb', line 69 def search(query, max_results: DEFAULT_MAX_RESULTS, cancellable: Pikuri::Agent::Control::Cancellable::NEVER) LIMITER.call(cancellable: cancellable) do response = Faraday.get( ENDPOINT, { q: query, count: max_results }, { 'X-Subscription-Token' => @api_key, 'Accept' => 'application/json' } ) unless response.success? if response.status == 429 || response.status >= 500 raise Engines::Unavailable, "HTTP #{response.status}" end raise "Brave Search request failed: #{response.status} #{response.body}" end self.class.parse(response.body, max_results: max_results) end end |