[GH-ISSUE #24120] feat: Support Brave Search LLM Context API as web_search provider option #35727

Open
opened 2026-04-25 09:53:59 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @Fade78 on GitHub (Apr 25, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/24120

Check Existing Issues

  • I have searched for all existing open AND closed issues and discussions for similar requests. I have found none that is comparable to my request.

Verify Feature Scope

  • I have read through and understood the scope definition for feature requests in the Issues section. I believe my feature request meets the definition and belongs in the Issues section instead of the Discussions.

Problem Description

Open WebUI uses Brave Search to retrieve web results that are injected into the LLM's context for grounding. The current implementation calls /res/v1/web/search, which returns a list of URLs with titles and short text snippets (typically 1–3 sentences per result).

This creates a fundamental quality problem: the LLM receives almost nothing it can actually reason over. The pipeline then attempts to compensate by scraping each result URL individually — adding seconds of latency, failing on JavaScript-rendered pages, and getting blocked by anti-bot protections. Even when scraping succeeds, the extraction quality is inconsistent.

Since February 6, 2026, Brave provides the /res/v1/llm/context endpoint designed specifically for this use case. It returns pre-extracted, relevance-scored page content — text passages, tables, code blocks, structured data — ready for LLM consumption in a single API call. No scraping needed. Same API key. Same pricing. Open WebUI can't use it because the endpoint URL is hardcoded in backend/open_webui/retrieval/web/brave.py.

Desired Solution you'd like

Replace the /res/v1/web/search call with /res/v1/llm/context in the Brave Search integration.

What changes

1. Endpoint and authentication — Identical. Same X-Subscription-Token header, same API key, same pricing tier. Only the URL path changes:

- https://api.search.brave.com/res/v1/web/search
+ https://api.search.brave.com/res/v1/llm/context

2. Request parameters — The LLM Context endpoint accepts the same q and count parameters, plus context-size parameters that developers should be aware of:

Parameter Default Range Relevance
maximum_number_of_tokens 8192 1024–32768 Controls total extracted content volume. Directly impacts how much context the LLM receives per search. Must be tuned against the model's context window.
maximum_number_of_urls 20 1–50 Caps the number of source URLs. Maps naturally to the existing WEB_SEARCH_RESULT_COUNT setting.
maximum_number_of_tokens_per_url 4096 512–8192 Caps content per individual URL. Prevents one dominant page from consuming the entire budget.
maximum_number_of_snippets_per_url 50 1–100 Caps the number of extracted passages per URL.
context_threshold_mode balanced strict / balanced / lenient / disabled Relevance filter. balanced is a good default. strict reduces noise for factual queries. lenient maximizes coverage for research queries.
freshness (none) pd / pw / pm / py / date range Same as web search. Filters by page recency.

The maximum_number_of_tokens parameter is the most critical one to get right. At the default of 8192 tokens, a search returning 10 results sends roughly 800 tokens per source — far richer than current snippets (~50 tokens each) but well within the context window of most models. For models with small context windows (≤4K), this should be reduced to 1024–2048.

3. Response format — Structurally different. The web search response wraps results under web.results with flat description strings. The LLM Context response nests them under grounding.generic with snippets arrays:

// Current: /res/v1/web/search
{
  "web": {
    "results": [
      { "url": "...", "title": "...", "description": "A short snippet..." }
    ]
  }
}

// New: /res/v1/llm/context
{
  "grounding": {
    "generic": [
      { "url": "...", "title": "...", "snippets": ["Full paragraph from the page...", "Another relevant passage..."] }
    ]
  },
  "sources": {
    "https://example.com": { "title": "...", "hostname": "...", "age": ["Monday, January 15, 2024", "2024-01-15", "380 days ago"] }
  }
}

The snippets field contains full extracted passages (not 1-sentence summaries). The sources object provides metadata (hostname, page age) that could enrich citations. Both grounding.generic and sources use the URL as the linking key.

4. Mapping to existing SearchResult — The existing model has three fields:

class SearchResult(BaseModel):
    link: str
    title: Optional[str]
    snippet: Optional[str]

The simplest mapping is to join the snippets array:

SearchResult(
    link=result['url'],
    title=result.get('title'),
    snippet='\n\n'.join(result.get('snippets', [])),
)

This preserves backward compatibility — downstream code that reads SearchResult.snippet gets richer content without any changes. The sources metadata (hostname, age) could be appended to the snippet or ignored in a first implementation.

5. Rate limiting — The existing 429 retry logic (1 second sleep, one retry) works unchanged. Same rate limits apply.

6. POST method — The LLM Context endpoint also supports POST with the same parameters as a JSON body, which is useful for complex queries that exceed URL length limits. This could be adopted as a quality-of-life improvement but is not required for the initial implementation.

Alternatives Considered

  1. Post-search scraping pipeline — Use the existing /res/v1/web/search endpoint, then scrape each URL with the built-in get_web_loader to extract full page content. This is the current default behavior when "Web Search" + "RAG" is enabled in Open WebUI. Drawback: requires one extra HTTP request per result (latency × N), is fragile (sites block scrapers, JS-rendered pages fail), and the extraction quality is inconsistent. The LLM Context endpoint eliminates this entire extra step.

  2. Brave MCP Server — Brave publishes an official MCP Server that exposes both web_search and llm_context as tools. It could be configured as a separate tool in Open WebUI. Drawback: this requires the user to set up and maintain a separate MCP server process, configure it in Open WebUI's MCP integration, and it doesn't integrate with the native web search pipeline (no result count setting, no domain filter, no admin toggle). The experience is fragmented compared to a built-in option.

  3. Brave Answers API — Brave also offers an /res/v1/answers endpoint that returns a grounded AI-generated answer with citations, using an OpenAI-compatible interface. Drawback: this delegates answer generation to Brave's own model, which conflicts with Open WebUI's architecture where the user's chosen LLM generates the answer. It doesn't return raw extractable context for RAG.

  4. Forking the container image — Modify brave.py in a custom build. Drawback: defeats the purpose of using the official container; breaks on every update; doesn't benefit the community.

Additional Context

  • Launch date: The LLM Context API was launched on February 6, 2026 (per Brave's changelog). It is now a stable, production-ready endpoint.

  • Same API key, same pricing: The LLM Context endpoint uses the exact same X-Subscription-Token authentication and the same per-request pricing as the existing Web Search endpoint. No additional account setup or cost is required — only the URL and response parsing need to change. Pricing is $5 per 1,000 requests for both endpoints (Search includes LLM Context, Web, Images, News).

  • Community demand: The openclaw project has already received two feature requests for this same capability (#14992, #15028), confirming that other AI tool integrations see value in this endpoint.

  • SearchResult model impact: The current SearchResult model in backend/open_webui/retrieval/web/main.py has only three fields: link, title, and snippet. The LLM Context response provides richer data (multiple snippets per URL, hostname, page age). A minimal implementation could join the snippets array into a single snippet string, but extending SearchResult with an optional snippets: list[str] field would preserve the richer structure for downstream use.

  • Rate limiting: The current brave.py includes a 429 retry mechanism (1 second sleep, one retry). This should be preserved for the LLM Context endpoint, which follows the same rate limits. The existing pattern is adequate.

  • Token budget parameter: The LLM Context endpoint accepts a maximum_number_of_tokens parameter (default 8192, range 1024–32768). This could be exposed as an admin setting (e.g., BRAVE_SEARCH_CONTEXT_TOKENS) to let users control how much context is retrieved per query, balancing grounding quality against latency and cost.

Originally created by @Fade78 on GitHub (Apr 25, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/24120 ### Check Existing Issues - [x] I have searched for all existing **open AND closed** issues and discussions for similar requests. I have found none that is comparable to my request. ### Verify Feature Scope - [x] I have read through and understood the scope definition for feature requests in the Issues section. I believe my feature request meets the definition and belongs in the Issues section instead of the Discussions. ### Problem Description Open WebUI uses Brave Search to retrieve web results that are injected into the LLM's context for grounding. The current implementation calls `/res/v1/web/search`, which returns a list of URLs with titles and short text snippets (typically 1–3 sentences per result). This creates a fundamental quality problem: the LLM receives almost nothing it can actually reason over. The pipeline then attempts to compensate by scraping each result URL individually — adding seconds of latency, failing on JavaScript-rendered pages, and getting blocked by anti-bot protections. Even when scraping succeeds, the extraction quality is inconsistent. Since **February 6, 2026**, Brave provides the `/res/v1/llm/context` endpoint designed specifically for this use case. It returns pre-extracted, relevance-scored page content — text passages, tables, code blocks, structured data — ready for LLM consumption in a single API call. No scraping needed. Same API key. Same pricing. Open WebUI can't use it because the endpoint URL is hardcoded in `backend/open_webui/retrieval/web/brave.py`. ### Desired Solution you'd like Replace the `/res/v1/web/search` call with `/res/v1/llm/context` in the Brave Search integration. ### What changes **1. Endpoint and authentication** — Identical. Same `X-Subscription-Token` header, same API key, same pricing tier. Only the URL path changes: ``` - https://api.search.brave.com/res/v1/web/search + https://api.search.brave.com/res/v1/llm/context ``` **2. Request parameters** — The LLM Context endpoint accepts the same `q` and `count` parameters, plus context-size parameters that developers should be aware of: | Parameter | Default | Range | Relevance | |---|---|---|---| | `maximum_number_of_tokens` | 8192 | 1024–32768 | Controls total extracted content volume. Directly impacts how much context the LLM receives per search. Must be tuned against the model's context window. | | `maximum_number_of_urls` | 20 | 1–50 | Caps the number of source URLs. Maps naturally to the existing `WEB_SEARCH_RESULT_COUNT` setting. | | `maximum_number_of_tokens_per_url` | 4096 | 512–8192 | Caps content per individual URL. Prevents one dominant page from consuming the entire budget. | | `maximum_number_of_snippets_per_url` | 50 | 1–100 | Caps the number of extracted passages per URL. | | `context_threshold_mode` | `balanced` | `strict` / `balanced` / `lenient` / `disabled` | Relevance filter. `balanced` is a good default. `strict` reduces noise for factual queries. `lenient` maximizes coverage for research queries. | | `freshness` | (none) | `pd` / `pw` / `pm` / `py` / date range | Same as web search. Filters by page recency. | The `maximum_number_of_tokens` parameter is the most critical one to get right. At the default of 8192 tokens, a search returning 10 results sends roughly 800 tokens per source — far richer than current snippets (~50 tokens each) but well within the context window of most models. For models with small context windows (≤4K), this should be reduced to 1024–2048. **3. Response format** — Structurally different. The web search response wraps results under `web.results` with flat `description` strings. The LLM Context response nests them under `grounding.generic` with `snippets` arrays: ```json // Current: /res/v1/web/search { "web": { "results": [ { "url": "...", "title": "...", "description": "A short snippet..." } ] } } // New: /res/v1/llm/context { "grounding": { "generic": [ { "url": "...", "title": "...", "snippets": ["Full paragraph from the page...", "Another relevant passage..."] } ] }, "sources": { "https://example.com": { "title": "...", "hostname": "...", "age": ["Monday, January 15, 2024", "2024-01-15", "380 days ago"] } } } ``` The `snippets` field contains full extracted passages (not 1-sentence summaries). The `sources` object provides metadata (hostname, page age) that could enrich citations. Both `grounding.generic` and `sources` use the URL as the linking key. **4. Mapping to existing `SearchResult`** — The existing model has three fields: ```python class SearchResult(BaseModel): link: str title: Optional[str] snippet: Optional[str] ``` The simplest mapping is to join the snippets array: ```python SearchResult( link=result['url'], title=result.get('title'), snippet='\n\n'.join(result.get('snippets', [])), ) ``` This preserves backward compatibility — downstream code that reads `SearchResult.snippet` gets richer content without any changes. The `sources` metadata (hostname, age) could be appended to the snippet or ignored in a first implementation. **5. Rate limiting** — The existing 429 retry logic (1 second sleep, one retry) works unchanged. Same rate limits apply. **6. POST method** — The LLM Context endpoint also supports `POST` with the same parameters as a JSON body, which is useful for complex queries that exceed URL length limits. This could be adopted as a quality-of-life improvement but is not required for the initial implementation. ### Alternatives Considered 1. **Post-search scraping pipeline** — Use the existing `/res/v1/web/search` endpoint, then scrape each URL with the built-in `get_web_loader` to extract full page content. This is the current default behavior when "Web Search" + "RAG" is enabled in Open WebUI. **Drawback**: requires one extra HTTP request per result (latency × N), is fragile (sites block scrapers, JS-rendered pages fail), and the extraction quality is inconsistent. The LLM Context endpoint eliminates this entire extra step. 2. **Brave MCP Server** — Brave publishes an official [MCP Server](https://github.com/brave/brave-search-skills) that exposes both `web_search` and `llm_context` as tools. It could be configured as a separate tool in Open WebUI. **Drawback**: this requires the user to set up and maintain a separate MCP server process, configure it in Open WebUI's MCP integration, and it doesn't integrate with the native web search pipeline (no result count setting, no domain filter, no admin toggle). The experience is fragmented compared to a built-in option. 3. **Brave Answers API** — Brave also offers an `/res/v1/answers` endpoint that returns a grounded AI-generated answer with citations, using an OpenAI-compatible interface. **Drawback**: this delegates answer generation to Brave's own model, which conflicts with Open WebUI's architecture where the user's chosen LLM generates the answer. It doesn't return raw extractable context for RAG. 4. **Forking the container image** — Modify `brave.py` in a custom build. **Drawback**: defeats the purpose of using the official container; breaks on every update; doesn't benefit the community. ### Additional Context - **Launch date**: The LLM Context API was launched on **February 6, 2026** (per Brave's changelog). It is now a stable, production-ready endpoint. - **Same API key, same pricing**: The LLM Context endpoint uses the exact same `X-Subscription-Token` authentication and the same per-request pricing as the existing Web Search endpoint. No additional account setup or cost is required — only the URL and response parsing need to change. Pricing is $5 per 1,000 requests for both endpoints (Search includes LLM Context, Web, Images, News). - **Community demand**: The openclaw project has already received two feature requests for this same capability ([#14992](https://github.com/openclaw/openclaw/issues/14992), [#15028](https://github.com/openclaw/openclaw/issues/15028)), confirming that other AI tool integrations see value in this endpoint. - **`SearchResult` model impact**: The current `SearchResult` model in `backend/open_webui/retrieval/web/main.py` has only three fields: `link`, `title`, and `snippet`. The LLM Context response provides richer data (multiple snippets per URL, hostname, page age). A minimal implementation could join the `snippets` array into a single `snippet` string, but extending `SearchResult` with an optional `snippets: list[str]` field would preserve the richer structure for downstream use. - **Rate limiting**: The current `brave.py` includes a 429 retry mechanism (1 second sleep, one retry). This should be preserved for the LLM Context endpoint, which follows the same rate limits. The existing pattern is adequate. - **Token budget parameter**: The LLM Context endpoint accepts a `maximum_number_of_tokens` parameter (default 8192, range 1024–32768). This could be exposed as an admin setting (e.g., `BRAVE_SEARCH_CONTEXT_TOKENS`) to let users control how much context is retrieved per query, balancing grounding quality against latency and cost.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#35727