2.0 KiB
SPEC-015 — Web search (Exa)
A web_search function tool for general "look it up on the internet"
questions the other tools do not cover: IGDB is games, the Codex is
Adeptus Mechanicus lore, the news store is the configured feeds, and
fetch_url needs a URL the user already has. Web search fills the gap
and pairs with fetch_url (search → pick a link → read it). Results are
external text and are sanitized (SAF-03); the Exa key is a host secret,
never in the repo. Active only when enable-web-search = true and a key
is present.
WEB-01 — web_search is offered as a tool (coverage: test)
When enable-web-search is true and an Exa key is available
(exa-api-key in config, else EXA_API_KEY env), the chat call's
tools list includes a web_search function (query string, optional
num_results). With the flag off or no key it is absent.
WEB-02 — Results are reduced and sanitized (coverage: test)
Each Exa result becomes {title, url, snippet, published}; title and
snippet pass through sanitize_external_text (snippet capped at
web-snippet-chars, default 400) so a web page can neither inject an
@everyone nor smuggle control characters into the prompt.
WEB-03 — Result count is bounded (coverage: test)
num_results is clamped to 1..MAX_RESULTS (10) before the request, so
neither a huge fan-out nor a zero/negative count reaches the API.
WEB-04 — Missing key and API failure are reported, not raised (coverage: test)
With no key the tool returns an {error: ...} result without a network
call. A request that raises (network, non-2xx, bad JSON) is logged and
returns an {error: ...} dict — search never raises into the loop.
WEB-05 — Searches are metered per user (coverage: test)
Each web_search increments a per-user daily counter; over
web-daily-per-user (default 30) the tool refuses with an error result
without calling the API. The budget gate (SAF-04) still applies to the
surrounding model calls.