Skip to content

web_context

web_context

Web search for chat messages.

When a chat request sets web_search, the chat route searches the web for the user's latest message before the model answers, reads the top pages, and adds the results as context the model cites as [1], [2], ... The sources are also sent to the client in a web_search SSE event so the UI can link the citations.

Searching sends the query to the configured engine (DuckDuckGo, or Tavily, You.com, Serply or Firecrawl with a key; see :class:locus.tools.web_search.WebSearchTool), so the client only asks for it when the user turns web search on. A message that contains a URL fetches that page instead of searching.

Classes

WebSource dataclass

WebSource(ref: int, title: str, url: str, snippet: str = '', text: str = '')
Functions
public
public() -> Dict[str, Any]

The fields sent to the client.

Source code in src/locus/server/web_context.py
def public(self) -> Dict[str, Any]:
    """The fields sent to the client."""
    return {"ref": self.ref, "title": self.title, "url": self.url}

WebSearchResult dataclass

WebSearchResult(query: str, engine: str = '', sources: List[WebSource] = list(), error: Optional[str] = None)
Functions
event
event() -> Dict[str, Any]

Payload of the web_search SSE event.

Source code in src/locus/server/web_context.py
def event(self) -> Dict[str, Any]:
    """Payload of the ``web_search`` SSE event."""
    payload: Dict[str, Any] = {
        "query": self.query,
        "engine": self.engine,
        "sources": [s.public() for s in self.sources],
    }
    if self.error:
        payload["error"] = self.error
    return payload

Functions

parse_results

parse_results(content: str) -> List[WebSource]

Parse :class:WebSearchTool's ### title / Source / Summary blocks.

Source code in src/locus/server/web_context.py
def parse_results(content: str) -> List[WebSource]:
    """Parse :class:`WebSearchTool`'s ``### title / Source / Summary`` blocks."""
    sources: List[WebSource] = []
    for block in content.split("\n\n---\n\n"):
        match = _RESULT_RE.match(block.strip())
        if not match:
            continue
        sources.append(
            WebSource(
                ref=len(sources) + 1,
                title=match["title"] or match["url"],
                url=match["url"],
                snippet=match["summary"].strip(),
            )
        )
    return sources

search_web async

search_web(query: str) -> WebSearchResult

Search for query and read the top pages. Never raises.

Source code in src/locus/server/web_context.py
async def search_web(query: str) -> WebSearchResult:
    """Search for ``query`` and read the top pages. Never raises."""
    from locus.tools.web_search import WebSearchTool

    result = WebSearchResult(query=query)
    tool = WebSearchTool(max_results=MAX_RESULTS)
    try:
        found = await asyncio.wait_for(
            asyncio.to_thread(tool.execute, query=query), SEARCH_TIMEOUT_S
        )
    except asyncio.TimeoutError:
        result.error = "The web search timed out."
        return result
    except Exception as exc:  # the tool reports most errors itself
        result.error = f"The web search failed ({type(exc).__name__})."
        return result

    metadata = found.metadata or {}
    result.engine = str(metadata.get("engine") or metadata.get("extractor") or "")
    if not found.success:
        result.error = found.content or "The web search failed."
        return result

    if metadata.get("mode") == "fetch":
        url = str(metadata.get("url", ""))
        result.sources = [
            WebSource(ref=1, title=url, url=url, text=found.content[: PAGE_CHARS * 2])
        ]
        return result

    result.sources = parse_results(found.content)
    if not result.sources:
        result.error = "The web search found no results."
        return result

    async def fetch(source: WebSource) -> None:
        try:
            text = await asyncio.wait_for(
                asyncio.to_thread(WebSearchTool._fetch_url, source.url, PAGE_CHARS),
                FETCH_TIMEOUT_S,
            )
            source.text = (text or "").strip()
        except Exception:
            logger.debug("Could not fetch %s", source.url, exc_info=True)

    await asyncio.gather(*(fetch(s) for s in result.sources[:FETCH_TOP]))
    return result

render_context

render_context(result: WebSearchResult, today: Optional[date] = None) -> str

The system prompt section that gives the model the search results.

Source code in src/locus/server/web_context.py
def render_context(result: WebSearchResult, today: Optional[_dt.date] = None) -> str:
    """The system prompt section that gives the model the search results."""
    today = today or _dt.date.today()
    if result.error or not result.sources:
        reason = result.error or "The web search found no results."
        return (
            "The user asked you to search the web, but the search didn't work: "
            f"{reason} Answer from your own knowledge, and say that you couldn't "
            "check the web, so recent details may be out of date."
        )
    lines = [
        "Web search results for the user's latest message, "
        f"retrieved {today.isoformat()}.",
        "Use them to answer. Cite the sources you rely on inline as [1], [2], "
        "and so on, using the numbers below. If the results don't answer the "
        "question, say so rather than guessing. Don't invent sources or URLs.",
        "",
    ]
    for source in result.sources:
        body = source.text or source.snippet
        lines.append(
            f"[{source.ref}] {source.title}\nURL: {source.url}\n{body}".rstrip()
        )
        lines.append("")
    return "\n".join(lines).rstrip()