borderless / apis /exa.py
spagestic's picture
Add country metadata and source quality signals for immigration research.
897a75f
Raw
History Blame Contribute Delete
3.59 kB
"""Exa API client for immigration-focused web search."""
from __future__ import annotations
import os
from functools import lru_cache
from typing import Any
from exa_py import Exa
from apis.official_sources import classify_source, infer_official_domains
HIGHLIGHT_CHAR_LIMIT = 2000
SUMMARY_CHAR_LIMIT = 500
DEFAULT_NUM_RESULTS = 8
MAX_NUM_RESULTS = 15
def _truncate(text: str | None, limit: int) -> str:
if not text:
return ""
if len(text) <= limit:
return text
return text[:limit] + "\n… (truncated)"
@lru_cache(maxsize=1)
def _client() -> Exa:
api_key = os.environ.get("EXA_API_KEY")
if not api_key:
raise RuntimeError("EXA_API_KEY is not set")
return Exa(api_key=api_key)
def _result_dict(result: Any) -> dict[str, Any]:
highlights = result.highlights or []
trimmed_highlights = [
_truncate(highlight, HIGHLIGHT_CHAR_LIMIT) for highlight in highlights
]
summary = getattr(result, "summary", None)
source_quality = classify_source(result.url or "")
return {
"title": result.title or "",
"url": result.url or "",
"published_date": result.published_date,
"score": result.score,
"source_quality": source_quality,
"highlights": trimmed_highlights,
"summary": _truncate(summary, SUMMARY_CHAR_LIMIT) if summary else None,
}
def search_immigration(
query: str,
*,
num_results: int = DEFAULT_NUM_RESULTS,
country: str | None = None,
include_domains: list[str] | None = None,
) -> dict[str, Any]:
"""
Search the web for immigration-related information using Exa.
Returns result titles, URLs, and query-relevant highlights so the agent
can follow up with Firecrawl for full official page content.
Args:
query: Natural-language search query describing the ideal page.
num_results: Number of results to return (capped at 15).
country: Optional ISO-2 country code for geo-targeted results.
include_domains: Optional list of domains to restrict results to.
"""
normalized_query = (query or "").strip()
if not normalized_query:
return {"error": "query is required", "query": query}
capped_results = max(1, min(num_results, MAX_NUM_RESULTS))
try:
kwargs: dict[str, Any] = {
"num_results": capped_results,
"type": "auto",
"contents": {
"highlights": {"maxCharacters": HIGHLIGHT_CHAR_LIMIT},
},
}
if country:
normalized_country = country.strip().upper()
if normalized_country:
kwargs["user_location"] = normalized_country
if include_domains:
kwargs["include_domains"] = include_domains
official_domain_hints = infer_official_domains(normalized_query, country)
response = _client().search(normalized_query, **kwargs)
results = [_result_dict(result) for result in (response.results or [])]
return {
"query": normalized_query,
"num_results": len(results),
"official_domain_hints": official_domain_hints,
"official_results": sum(
1
for result in results
if result.get("source_quality", {}).get("is_official")
),
"results": results,
}
except Exception as exc:
return {"error": str(exc), "query": normalized_query}