Spaces:
Sleeping
Sleeping
File size: 4,919 Bytes
ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c 1fee1c2 ab4952c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | # COST: ZERO β BM25 keyword scoring (rank-bm25, runs locally).
# Searches across ALL scraped AgentRax pages (home, pricing, about, help, blog, contact).
# Falls back to single-page snapshot if multi-page store is missing.
# A live HTTP fetch only occurs when cached content is stale (>60 min).
# No embedding model or LLM is invoked at any point in this tool.
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from dotenv import load_dotenv
load_dotenv()
from rank_bm25 import BM25Okapi
from scraper.content_store import (
is_content_stale,
is_multi_page_stale,
load_all_pages,
load_latest_content,
save_all_pages,
save_site_content,
)
from scraper.web_scraper import AGENTRAX_URL, scrape_all_pages, scrape_site
_TOP_K = 8 # results returned across all pages
_MIN_CHARS = 30 # minimum length to include a text fragment
def _page_label(url: str) -> str:
"""Return a short human-readable label for a page URL."""
slug = url.rstrip("/").split("/")[-1]
return slug if slug else "home"
def _build_corpus(pages: dict[str, dict]) -> tuple[list[str], list[str]]:
"""Return (corpus_texts, corpus_labels) from a {url: page_data} dict."""
texts: list[str] = []
labels: list[str] = []
for url, content in pages.items():
label = _page_label(url)
headings = content.get("headings") or []
body_text = content.get("body_text") or ""
for h in headings:
h = h.strip()
if len(h) >= 5:
texts.append(f"[{label}] {h}")
labels.append(label)
for para in body_text.split("\n"):
para = para.strip()
if len(para) >= _MIN_CHARS:
texts.append(f"[{label}] {para}")
labels.append(label)
return texts, labels
async def search_agentrax_website(query: str) -> str:
"""Search all AgentRax website pages for information relevant to the user query.
Searches across home, pricing, about, help, blog, and contact pages using BM25.
Content is refreshed automatically every 60 minutes. Falls back to single-page
snapshot if the multi-page store is unavailable.
Args:
query: The question or topic to search on the AgentRax website.
Returns:
Relevant extracted sections annotated with which page they came from.
"""
# ββ 1. Load / refresh multi-page content βββββββββββββββββββββββββββββββββ
pages = load_all_pages()
if is_multi_page_stale() or not pages:
fresh = await scrape_all_pages()
if fresh:
save_all_pages(fresh)
pages = {p["url"]: p for p in fresh}
# ββ 2. Fall back to single-page homepage snapshot βββββββββββββββββββββββββ
if not pages:
single = load_latest_content()
if is_content_stale() or not single:
single = await scrape_site(AGENTRAX_URL)
if "error" not in single:
save_site_content(single)
if single and "error" not in single:
pages = {single.get("url", AGENTRAX_URL): single}
if not pages:
return (
"Error: could not retrieve AgentRax website content. "
"Please try again in a moment."
)
# ββ 3. Build cross-page searchable corpus βββββββββββββββββββββββββββββββββ
corpus, labels = _build_corpus(pages)
if not corpus:
# Last resort: return description of first available page
first = next(iter(pages.values()))
title = first.get("title") or "AgentRax"
desc = first.get("description") or ""
return f"## {title}\n\n{desc}" if desc else f"## {title}\n\n(No content available.)"
# ββ 4. BM25 ranking βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
tokenized = [text.lower().split() for text in corpus]
bm25 = BM25Okapi(tokenized)
scores = bm25.get_scores(query.lower().split())
top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:_TOP_K]
# Preserve document order for readability
top_indices_sorted = sorted(top_indices)
# ββ 5. Format output ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
lines = []
for i in top_indices_sorted:
lines.append(f"- {corpus[i]}")
pages_scraped = ", ".join(sorted({_page_label(u) for u in pages}))
header = f"## AgentRax β Pages searched: {pages_scraped}"
return f"{header}\n\n### Most Relevant Content\n" + "\n".join(lines)
|