Source code for ralph.mcp.websearch.backends.brave

"""Brave Search web-search backend."""

from __future__ import annotations

from collections.abc import Iterable, Mapping
from dataclasses import dataclass
from importlib import import_module
from typing import cast

import httpx

from ..secrets import resolve_secret
from .base import SearchResult, WebSearchError

_DEFAULT_URL = "https://api.search.brave.com/res/v1/web/search"
_TIMEOUT_SECONDS = 10.0


[docs] @dataclass(frozen=True) class BraveBackend: """Backend powered by Brave Search's HTTP API.""" api_key: str | None = None api_key_env: str | None = None url: str = _DEFAULT_URL def search(self, query: str, *, limit: int = 10) -> list[SearchResult]: _ensure_brave_extra_installed() resolved_key = resolve_secret(self.api_key, self.api_key_env) try: response = httpx.get( self.url, headers={ "Accept": "application/json", "X-Subscription-Token": resolved_key, }, params={"q": query, "count": limit}, timeout=_TIMEOUT_SECONDS, ) response.raise_for_status() payload = cast("object", response.json()) except Exception as exc: raise WebSearchError("brave search failed") from exc return _normalize_results(payload)
def _ensure_brave_extra_installed() -> None: try: import_module("brave_search_python_client") except ImportError as exc: raise WebSearchError( "backend 'brave' requires 'pip install ralph-workflow[web-search]'" ) from exc def _normalize_results(payload: object) -> list[SearchResult]: if not isinstance(payload, Mapping): raise WebSearchError("brave returned an invalid response") web_payload = payload.get("web") if not isinstance(web_payload, Mapping): return [] raw_results = web_payload.get("results") if not isinstance(raw_results, Iterable): return [] results: list[SearchResult] = [] for item in raw_results: if not isinstance(item, Mapping): continue title = _string_value(item, "title") url = _string_value(item, "url") snippet = _string_value(item, "description") if not title or not url: continue results.append(SearchResult(title=title, url=url, snippet=snippet)) return results def _string_value(payload: Mapping[str, object], *keys: str) -> str: for key in keys: value = payload.get(key) if isinstance(value, str) and value: return value return "" __all__ = ["BraveBackend"]