Web Search
The `web_search` tool offered to models: the provider pool, two-level opt-in, and the agent loop.
Web Search offers models a local tool named `web_search` — it runs inside the SAME server-side agent loop that runs MCP bindings (see Orchestration → Agent Loop). The search itself is answered from a provider pool that is priority- and quota-aware.
#Provider pool
Several search providers (e.g. Ollama's web-search endpoint, SearXNG) are ranked by priority; the highest-priority healthy one is tried first. A provider that errors (429/5xx/timeout/other 4xx) is put on a cooldown whose length depends on the error type, and the next-ranked provider takes over — these cooldown lengths are managed from the panel. A provider whose daily/monthly usage cap is full is skipped the same way. Providers that share a priority are rotated round-robin so the quota isn't always spent from the same one.
This layer never raises under any circumstance: if every provider fails, it returns an empty result and the caller (the agent loop) relies on that contract. The same query is cached briefly; the cache key is provider-agnostic, so a result produced by one provider also protects another provider's quota.
#How it is declared to the model
The `web_search` name is NOT namespaced — Trellis MCP tools arrive as `server<SEP>tool`, so a flat name never collides with them. The tool takes a required `query` and an optional `count`. If a group has both an MCP binding and web search, the model sees both in one combined tool list.
#Two-level opt-in
Declaring the tool to the model requires BOTH switches to be on together: the global switch (Settings → Web Search, `search.tool_enabled`, off by default) AND the resolved model group's own toggle (`web_search_enabled`, also off by default). Even with the global switch on, a group is off by default; with the global switch off, no group gets the tool at all. A request that never resolves to a model group (a direct model name) only checks the global switch.
#The agent loop and Judge
The search is triggered the same way as any other tool: when the model emits a `tool_call`, its query arguments (`query`, `count`) also pass through Judge's `tool_call` surface BEFORE the search runs — exactly like an MCP tool's arguments (see Platform → Judge). Search results are then treated like any other tool's result: right before handing it back to the model, the agent loop runs it through Judge's `context` surface. This matters — text coming back from an external search is a classic prompt-injection vector that a model can mistake for instructions, and a policy on the `context` surface can inspect/sanitize/block it.
#Web search without a binding
`binding=None` with `web_search=True` is a valid setup: a model group can have web search toggled on with no MCP binding at all. In that case no Trellis session is opened — the loop is built with only the search tool. If neither a binding nor web search is enabled, the agent loop is never set up at all (so it never wastes an extra upstream turn).