import { withRetry } from "./retry.js"; export interface SearchResult { title: string; url: string; description: string; } export interface SearchProvider { search(query: string, count: number, signal?: AbortSignal): Promise; } export class BraveSearchProvider implements SearchProvider { private apiKey: string; constructor(apiKey: string) { this.apiKey = apiKey; } async search(query: string, count: number, signal?: AbortSignal): Promise { const url = new URL("https://api.search.brave.com/res/v1/web/search"); url.searchParams.set("q", query); url.searchParams.set("count", String(Math.min(count, 20))); const doSearch = async () => { const response = await fetch(url.toString(), { headers: { "Accept": "application/json", "X-Subscription-Token": this.apiKey, "User-Agent": "pi-web-research/1.0", }, signal, }); if (response.status === 429) { const retryAfter = response.headers.get("Retry-After") ?? "unknown"; throw new Error(`Brave Search rate limited. Retry after ${retryAfter} seconds.`); } if (!response.ok) { const text = await response.text(); throw new Error(`Brave Search failed (${response.status}): ${text}`); } const data = (await response.json()) as any; const results = (data.web?.results ?? []).map((r: any) => ({ title: r.title ?? "", url: r.url ?? "", description: r.description ?? "", })); return results; }; return withRetry(doSearch, { maxRetries: 3, baseDelayMs: 1000 }); } }