Skip to content

feat: add Brave Search LLM Context API mode for web_search#33383

Closed
thirumaleshp wants to merge 1 commit intoopenclaw:mainfrom
thirumaleshp:feat/brave-llm-context-api
Closed

feat: add Brave Search LLM Context API mode for web_search#33383
thirumaleshp wants to merge 1 commit intoopenclaw:mainfrom
thirumaleshp:feat/brave-llm-context-api

Conversation

@thirumaleshp
Copy link
Copy Markdown
Contributor

Add support for Brave's LLM Context API endpoint (/res/v1/llm/context) as an optional mode for the web_search tool. When configured with tools.web.search.brave.mode: "llm-context", the tool returns pre-extracted page content (text chunks, tables, code blocks) optimized for LLM grounding instead of standard URL/snippet results.

Closes #14992

Summary

  • Problem: OpenClaw's Brave web search only uses the standard Web Search API which returns URLs and snippets — the LLM must then fetch pages separately to get actual content.
  • Why it matters: Brave's LLM Context API returns pre-extracted page content at the same price, eliminating extra fetch steps and giving LLMs richer grounding data.
  • What changed: Added a brave.mode config option ("web" or "llm-context"), new response types, runBraveLlmContextSearch() function, mode dispatch in runWebSearch(), Zod schema validation, help text, and 5 unit tests.
  • What did NOT change (scope boundary): Default behavior is unchanged — no config = standard "web" mode. No changes to other providers (perplexity, grok, gemini, kimi).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • New config option: tools.web.search.brave.mode accepts "web" (default) or "llm-context"
  • When "llm-context" is set, web_search tool description changes to mention LLM Context API
  • Response format changes: results include snippets[] (extracted text) instead of description (short snippet), plus sources metadata and a mode: "llm-context" field

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No — reuses existing X-Subscription-Token / BRAVE_API_KEY
  • New/changed network calls? Yes — new endpoint https://api.search.brave.com/res/v1/llm/context
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Mitigation: The new endpoint is on the same Brave API domain (api.search.brave.com), uses the same auth header, and all response content is wrapped through wrapWebContent() with externalContent.untrusted: true — same security posture as existing Brave search. Request goes through withTrustedWebSearchEndpoint().

Repro + Verification

Environment

  • OS: Windows 11
  • Runtime/container: Node.js v22 (pnpm workspace)
  • Model/provider: N/A (tool-level change)
  • Integration/channel: N/A
  • Relevant config:
{ "tools": { "web": { "search": { "provider": "brave", "brave": { "mode": "llm-context" } } } } }

Steps

  1. Set tools.web.search.brave.mode: "llm-context" in config
  2. Set BRAVE_API_KEY environment variable
  3. Invoke web_search tool with a query

Expected

  • Response contains mode: "llm-context", results[].snippets[] with extracted text content, and sources[] metadata

Actual

  • (Requires live Brave API key for manual verification)

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

All tests pass: 42/42 web-search, 29/29 config-misc, 29/29 web-tools enabled-defaults. Build succeeds. Formatting passes (oxfmt).

Human Verification (required)

  • Verified scenarios: All unit tests pass, resolveBraveMode handles default/undefined/explicit/invalid values correctly, config Zod validation accepts the new brave block, build compiles cleanly
  • Edge cases checked: Missing brave config (defaults to "web"), invalid mode value (falls back to "web"), cache key differentiation between modes
  • What I did not verify: Live API call to Brave LLM Context endpoint (requires paid API key)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? Yes — new optional tools.web.search.brave.mode field
  • Migration needed? No — no config = existing behavior

Failure Recovery (if this breaks)

  • How to disable/revert: Remove brave.mode from config or set it to "web" — falls back to standard search
  • Files/config to restore: Only tools.web.search.brave config block needs removal
  • Known bad symptoms: If Brave LLM Context endpoint returns unexpected format, grounding.generic extraction returns empty results gracefully

Risks and Mitigations

  • Risk: Brave LLM Context API response format changes
    • Mitigation: Response parsing uses optional chaining and fallback defaults (entry.url ?? "", entry.snippets ?? []), so unexpected shapes degrade gracefully to empty results rather than crashes.

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Mar 3, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR adds a new "llm-context" mode for Brave Search that calls Brave's /res/v1/llm/context endpoint and returns pre-extracted page content (text chunks, tables, code blocks) instead of URL/snippet results. The implementation is well-scoped: it follows existing patterns for trusted endpoints, content wrapping, and config resolution, and default behavior is fully preserved.

Key changes:

  • New BRAVE_LLM_CONTEXT_ENDPOINT constant and runBraveLlmContextSearch() function with graceful optional-chaining fallbacks
  • resolveBraveMode() / resolveBraveConfig() helpers wired into createWebSearchTool() and runWebSearch()
  • braveMode added to the cache key to differentiate mode-specific results
  • Config type, Zod schema, help text, and UI label all updated consistently
  • 5 unit tests covering default, explicit, fallback, and invalid-mode cases

Issue found:

  • The Brave cache key unconditionally includes count and ui_lang for both modes (line 1229), but runBraveLlmContextSearch does not accept or forward either parameter to the API. Two identical llm-context requests differing only in count or ui_lang will always miss each other's cache and trigger redundant API calls. The cache key should omit these two fields when effectiveBraveMode === "llm-context".

Confidence Score: 3/5

  • Safe to merge with the cache key optimization noted — the bug causes unnecessary API calls but not incorrect results or security concerns.
  • The feature is well-implemented and backward-compatible. The only identified issue (cache key including count/ui_lang that are unused by the LLM Context API) leads to avoidable cache misses and redundant Brave API calls but does not produce wrong results, expose security risks, or break existing functionality. All other aspects — auth reuse, untrusted content wrapping, Zod validation, graceful fallbacks, and test coverage — are solid.
  • src/agents/tools/web-search.ts (line 1229): Brave cache key should exclude count and ui_lang parameters when effectiveBraveMode === "llm-context"

Last reviewed commit: 6bdc347

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

const cacheKey = normalizeCacheKey(
params.provider === "brave"
? `${params.provider}:${params.query}:${params.count}:${params.country || "default"}:${params.search_lang || "default"}:${params.ui_lang || "default"}:${params.freshness || "default"}`
? `${params.provider}:${effectiveBraveMode}:${params.query}:${params.count}:${params.country || "default"}:${params.search_lang || "default"}:${params.ui_lang || "default"}:${params.freshness || "default"}`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache key includes unused parameters for llm-context mode

The Brave cache key (line 1229) unconditionally includes ${params.count} and ${params.ui_lang}, but runBraveLlmContextSearch does not accept or forward either of these parameters to the API (lines 1359–1366). This means two llm-context requests with the same query but differing count or ui_lang values will always miss each other's cache and trigger redundant API calls, even though the Brave LLM Context endpoint would return identical results for both.

The llm-context cache key should only include the parameters that are actually forwarded to the API: country, search_lang, and freshness. Consider branching the cache key expression based on effectiveBraveMode to exclude count and ui_lang when mode is "llm-context".

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/web-search.ts
Line: 1229

Comment:
**Cache key includes unused parameters for `llm-context` mode**

The Brave cache key (line 1229) unconditionally includes `${params.count}` and `${params.ui_lang}`, but `runBraveLlmContextSearch` does not accept or forward either of these parameters to the API (lines 1359–1366). This means two `llm-context` requests with the same query but differing `count` or `ui_lang` values will always miss each other's cache and trigger redundant API calls, even though the Brave LLM Context endpoint would return identical results for both.

The `llm-context` cache key should only include the parameters that are actually forwarded to the API: `country`, `search_lang`, and `freshness`. Consider branching the cache key expression based on `effectiveBraveMode` to exclude `count` and `ui_lang` when mode is `"llm-context"`.

How can I resolve this? If you propose a fix, please make it concise.

const cacheKey = normalizeCacheKey(
params.provider === "brave"
? `${params.provider}:${params.query}:${params.count}:${params.country || "default"}:${params.search_lang || "default"}:${params.ui_lang || "default"}:${params.freshness || "default"}`
? `${params.provider}:${effectiveBraveMode}:${params.query}:${params.count}:${params.country || "default"}:${params.search_lang || "default"}:${params.ui_lang || "default"}:${params.freshness || "default"}`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache key includes unused parameters for llm-context mode

The Brave cache key (line 1229) unconditionally includes ${params.count} and ${params.ui_lang}, but runBraveLlmContextSearch does not accept or forward either of these parameters to the API (lines 1359–1366). This means two llm-context requests with the same query but differing count or ui_lang values will always miss each other's cache and trigger redundant API calls, even though the Brave LLM Context endpoint would return identical results for both.

The llm-context cache key should only include the parameters that are actually forwarded to the API: country, search_lang, and freshness. Consider branching the cache key expression based on effectiveBraveMode to exclude count and ui_lang when mode is "llm-context".

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/web-search.ts
Line: 1229

Comment:
**Cache key includes unused parameters for `llm-context` mode**

The Brave cache key (line 1229) unconditionally includes `${params.count}` and `${params.ui_lang}`, but `runBraveLlmContextSearch` does not accept or forward either of these parameters to the API (lines 1359–1366). This means two `llm-context` requests with the same query but differing `count` or `ui_lang` values will always miss each other's cache and trigger redundant API calls, even though the Brave LLM Context endpoint would return identical results for both.

The `llm-context` cache key should only include the parameters that are actually forwarded to the API: `country`, `search_lang`, and `freshness`. Consider branching the cache key expression based on `effectiveBraveMode` to exclude `count` and `ui_lang` when mode is `"llm-context"`.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Additional Comments (1)

src/agents/tools/web-search.ts, line 1229
Cache key includes unused parameters for llm-context mode

The Brave cache key (line 1229) unconditionally includes ${params.count} and ${params.ui_lang}, but runBraveLlmContextSearch does not accept or forward either of these parameters to the API (lines 1359–1366). This means two llm-context requests with the same query but differing count or ui_lang values will always miss each other's cache and trigger redundant API calls, even though the Brave LLM Context endpoint would return identical results for both.

The llm-context cache key should only include the parameters that are actually forwarded to the API: country, search_lang, and freshness. Consider branching the cache key expression based on effectiveBraveMode to exclude count and ui_lang when mode is "llm-context".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa1fbb98da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1361 to +1365
const { results: llmResults, sources } = await runBraveLlmContextSearch({
query: params.query,
apiKey: params.apiKey,
timeoutSeconds: params.timeoutSeconds,
country: params.country,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor count limit in Brave llm-context mode

When tools.web.search.brave.mode is set to "llm-context", this branch calls runBraveLlmContextSearch without passing the requested count, and the returned llmResults are not truncated before being returned/cached. In that mode, web_search can return more results/snippets than the caller asked for, which breaks the documented count (1-10) contract and can materially increase response size/token cost for callers trying to constrain output.

Useful? React with 👍 / 👎.

Add support for Brave's LLM Context API endpoint (/res/v1/llm/context)
as an optional mode for the web_search tool. When configured with
tools.web.search.brave.mode set to llm-context, the tool returns
pre-extracted page content optimized for LLM grounding instead of
standard URL/snippet results.

The llm-context cache key excludes count and ui_lang parameters that
the LLM Context API does not accept, preventing unnecessary cache
misses.

Closes openclaw#14992

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@thirumaleshp thirumaleshp force-pushed the feat/brave-llm-context-api branch from aa1fbb9 to ade890a Compare March 4, 2026 05:52
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ade890a8a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

timeoutSeconds: params.timeoutSeconds,
country: params.country,
search_lang: params.search_lang,
freshness: params.freshness,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve date range filters in llm-context requests

In Brave llm-context mode, this call forwards only params.freshness and never translates date_after/date_before into a freshness range, unlike the standard Brave branch. As a result, queries that specify date filters are accepted but executed without any date constraint, so users can get stale/unbounded results while believing the filter was applied. Please either derive freshness from dateAfter/dateBefore here as well, or reject date filters in this mode.

Useful? React with 👍 / 👎.

@cgdusek

This comment was marked as spam.

@steipete
Copy link
Copy Markdown
Contributor

steipete commented Mar 8, 2026

Superseded by #39906, which landed this feature on March 8, 2026.

Thanks for the implementation and prior art here.

@steipete steipete closed this Mar 8, 2026
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 8, 2026
* main: (70 commits)
  Refactor release hardening follow-ups (openclaw#39959)
  docs: clarify bot review conversation ownership (openclaw#39942)
  fix: harden talk silence timeout parsing (openclaw#39607) (thanks @danodoesdesign)
  talk: add configurable silence timeout
  transcript-policy: use named Set for anthropic signature-excluded providers
  transcript-policy: don't preserve thinking signatures for kimi-coding (openclaw#39798)
  fix: land mac universal release defaults (openclaw#33891) (thanks @cgdusek)
  Docs: clarify notarization handoff in mac release flow
  Docs: mark basic mac dist example as non-notarized
  Docs: clarify release build arch defaults for mac packaging
  macOS: default release app builds to universal binaries
  fix(issue-39839): address tool-call extra params parsing for kimi anthropic-messages
  docs: use alphabetical provider ordering
  fix: follow up openclaw#39321 and openclaw#38445 landings
  docs: note /landpr merge process
  fix: land Brave llm-context gaps (openclaw#33383) (thanks @thirumaleshp)
  feat: add Brave Search LLM Context API mode for web_search
  fix(feishu): restore @larksuiteoapi/node-sdk in root dependencies
  refactor: tighten codex inline api fallback follow-up
  macOS: set speech recognition taskHint for Talk Mode mic capture
  ...
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
GordonSH-oss pushed a commit to GordonSH-oss/openclaw that referenced this pull request Mar 9, 2026
hugs42 pushed a commit to hugs42/openclaw that referenced this pull request Mar 10, 2026
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
Get-windy pushed a commit to Get-windy/JieZi-ai-PS that referenced this pull request Mar 10, 2026
上游更新摘要(abb8f6310 → bda63c3,164 commits):

### 新功能
- ACP: 新增 resumeSessionId 支持 ACP session 恢复(openclaw#41847)
- CLI: 新增 openclaw backup create/verify 本地状态归档命令(openclaw#40163)
- Talk: 新增 talk.silenceTimeoutMs 配置项,可自定义静默超时(openclaw#39607)
- ACP Provenance: 新增 ACP 入站溯源元数据和回执注入(openclaw#40473)
- Brave 搜索: 新增 llm-context 模式,返回 AI 精炼摘要(openclaw#33383)
- browser.relayBindHost: Chrome relay 可绑定非 loopback 地址(WSL2 支持)(openclaw#39364)
- node-pending-work: 新增 node.pending.pull/ack RPC 接口
- Telegram: 新增 exec-approvals 处理器,支持 Telegram 内命令执行审批
- Mattermost: 新增 target-resolution,修复 markdown 保留和 DM media 上传
- MS Teams: 修复 Bot Framework General channel 对话 ID 兼容性(openclaw#41838)
- secrets/runtime-web-tools: 全新 web runtime secrets 工具模块
- cron: 新增 store-migration,isolated-agent 直送核心通道,delivery failure notify
- TUI: 自动检测浅色终端主题(COLORFGBG),支持 OPENCLAW_THEME 覆盖(openclaw#38636)

### 修复
- macOS: launchd 重启前重启已禁用服务,修复 openclaw update 卡死问题
- Telegram DM: 按 agent 去重入站 DM,防止同一条消息触发重复回复(openclaw#40519)
- Matrix DM: 修复 m.direct homeserver 检测,保留房间绑定优先级(openclaw#19736)
- 飞书: 清理插件发现缓存,修复 onboarding 安装后重复弹窗(openclaw#39642)
- config/runtime snapshots: 修复 config 写入后 secret 快照丢失问题(openclaw#37313)
- browser/CDP: 修复 ws:// CDP URL 反向代理和 wildcard 地址重写
- agents/failover: 识别 Bedrock tokens per day 限额为 rate limit

### 版本
- ACPX 0.1.16
- iOS/macOS 版本号更新
- Android: 精简后台权限

构建验证:待执行
sauerdaniel pushed a commit to sauerdaniel/openclaw that referenced this pull request Mar 11, 2026
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Support Brave Search LLM Context API as web_search provider option

3 participants