Repository navigation
feat: enable duckduckgo web search in Open WebUI chat - #414
Conversation
Wire ENABLE_WEB_SEARCH, WEB_SEARCH_ENGINE=duckduckgo, and both bypass flags (web loader + embedding/retrieval) into the open-webui service so chat can do live web search with zero API key or account signup. The bypass flags keep search snippet-only, decoupling this feature from the RAG embedding path (which routes through the same model-provider stack as chat completions). Verified live: retrieval config API confirms the running container reports ENABLE_WEB_SEARCH=true/engine=duckduckgo (what the chat UI reads to render the search toggle), and a direct call to /api/v1/retrieval/process/web/search returns real, current DuckDuckGo results end to end.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
Next review available in: 45 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review on PR #414 flagged that the four web-search env vars landed in the open-webui service block shared by profiles [local, enterprise, chat]. docker-compose.enterprise.yml does not override that block, so Hive Enterprise (data-sovereign, customer-hosted posture) would have silently inherited live outbound DuckDuckGo calls with no opt-out. Switch to the same env-var-with-default pattern already used for OWUI_E2E_MODE in this block: ${VAR:-false}. Default is off; a local/demo .env sets ENABLE_WEB_SEARCH=true + WEB_SEARCH_ENGINE=duckduckgo to opt in. Documents the default-off convention in .env.example next to OWUI_E2E_MODE. Re-verified live: local/demo container still resolves ENABLE_WEB_SEARCH= true/duckduckgo (confirmed via a fresh /api/v1/retrieval/process/web/search call returning live results), and docker-compose.enterprise.yml has zero open-webui overrides, so an enterprise .env without these vars set resolves ENABLE_WEB_SEARCH to false by the same interpolation default.
|
Live verification, this session, against the running local demo stack (OWUI at localhost:3003):
|
# feat: turn on DuckDuckGo web search for the chat demo deployment ## Summary The parity audit lists web search as a missing Claude-defining surface: Hive Chat could not search the web. The wiring for it has actually existed since #414, which added the compose passthrough for four environment keys on the open-webui service, but every default resolves to off and the demo box's own .env is untracked and hand-maintained, so the running demo deployment has had the feature sitting disabled behind defaults nobody sets. This change turns it on for the demo deployment only, through the versioned deploy definition instead of an untracked file. ## What changed 1. `.github/workflows/deploy-demo-box.yml`: the workflow-level env block that already carries HIVE_COMPOSE_FLAGS now also carries ENABLE_WEB_SEARCH=true, WEB_SEARCH_ENGINE=duckduckgo, BYPASS_WEB_SEARCH_WEB_LOADER=true, BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL=true. 2. `.env.example`: the comment above the four keys now points at where demo enablement lives, so the documented story matches reality. No application code changed. No frontend change was needed: the fork's composer keeps upstream's native web-search entry (IntegrationsMenu toggle, active pill, `features.web_search` in the completion payload), which renders as soon as `/api/config` reports `features.enable_web_search`, gated on admin role or the default-on `USER_PERMISSIONS_FEATURES_WEB_SEARCH`. ## Why this mechanism Shell environment overrides --env-file during compose interpolation, so workflow env wins over anything the untracked box .env carries for these keys, while the shared compose defaults stay off. That preserves the opt-in ruling from #414's review round: docker-compose.yml is shared with the enterprise profile, which must not silently inherit live outbound search calls. Enterprise deployments do not read this workflow and stay off unless their operator opts in. Local dev stacks are equally unaffected. Validated both resolution states locally with the exact demo flag set: ``` $ docker compose ... config # without workflow env ENABLE_WEB_SEARCH: "false" WEB_SEARCH_ENGINE: "" BYPASS_WEB_SEARCH_WEB_LOADER: "false" BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: "false" $ docker compose ... config # with workflow env ENABLE_WEB_SEARCH: "true" WEB_SEARCH_ENGINE: duckduckgo BYPASS_WEB_SEARCH_WEB_LOADER: "true" BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: "true" ``` ## Engine choice DuckDuckGo. The pinned v0.10.2 backend ships `retrieval/web/duckduckgo.py`; it needs no API key and no account signup, so it works tonight with zero new infrastructure. SearXNG (the old Phase 26 plan) would need a container plus an engine URL and buys nothing for five users on one box. If DDG ever rate limits or breaks, every other engine in the same menu is reachable by changing one env value, no code. ## Metering note Web search itself is not model spend and this change does not pretend to meter it. DuckDuckGo snippets are free and unmetered; only the chat completion that consumes the injected results is metered, exactly like any other completion through the gateway. Nothing new flows into billing from this feature. ## Data flow honesty Search runs inside the open-webui container and sends the derived query text to duckduckgo.com over outbound HTTPS. For the demo deployment (five users, controlled box) that is the accepted posture already verified live back in #414. The enterprise profile stays off precisely because a customer-hosted data-sovereign deployment should not gain outbound calls by default. ## Live verification (this branch's image) Throwaway stack: stub OpenAI-compatible LLM + the chat image built from this branch (`hive-open-webui:websearch-proof`), env enabled exactly as the workflow sets it. 1. `/api/config` reports features.enable_web_search true. 2. `POST /api/v1/retrieval/process/web/search` returns live DuckDuckGo results for a real query. 3. In-chat end to end: toggle Web Search in the composer, send a current-events question, receive a reply citing retrieved sources, captured as screenshots posted below via the visual-proof script. Follow-up (not this PR): post-deploy-verify.yml could assert the four values resolve true in the resolved open-webui config so a future compose invocation without the workflow env cannot silently drop the feature. Left out of this PR deliberately: that file is heavily contended this week. Buglog entry: none. No bug fixed here, this is enablement of existing capability. ## Rev 2: the persisted-config trap, closed Review verdict on rev 1 was right: web.search.* are DB-backed config in the pinned image. Config.seed_defaults "inserts keys that don't yet exist in the DB. Existing DB values take precedence over defaults" (models/config.py), so the demo box's chat database has carried web.search.enable = false, an empty engine, and both bypass flags false since its first boot. Read-only inspection of the box's config table (61 web.search rows) confirmed it before any code was written. The rev 1 workflow-env flip alone would have been a silent no-op in production. ### The reconcile The four keys now ride the existing first-boot-wins reconcile that #722 built for exactly this trap (owui-patches/hive_rag_env_config.py, spliced into seed_registered_defaults at build time): - web.search.enable <- ENABLE_WEB_SEARCH (boolean, env wins when set) - web.search.engine <- WEB_SEARCH_ENGINE (string; blank env never clobbers) - web.search.bypass_web_loader <- BYPASS_WEB_SEARCH_WEB_LOADER - web.search.bypass_embedding_and_retrieval <- BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL The demo's workflow env sets all four, so the next deploy writes true/duckduckgo/true/true over the stale false rows. The enterprise profile resolves them off, so its opt-in ruling stands. An operator who leaves a variable unset keeps the persisted value (blank never clobbers for the string key; the boolean keys always follow compose resolution, same as the existing product-surface flags). ### Manual step taken on the demo box (2026-08-24) Because the reconcile ships only in this branch's image and the box runs main's image until merge, the feature was enabled on the box by hand today, the same effect the PR produces: the four rows were updated in the box's webui.db config table (backup taken first), and the open-webui container was recreated with the four env vars exported. The next merged deploy rebuilds the image with the reconcile and re-asserts the same values from workflow env, so the manual state and the PR's end state agree. ### LOW: rate limits are no longer silent The pinned engine wrapper swallowed RatelimitException into an empty list, making a rate-limited DuckDuckGo indistinguishable from "the web has nothing": the native search_web builtin returned [] and the model answered "no results" with no signal that search itself was down. A build-time patch (owui-patches/apply_web_search_ratelimit_patch.py) re-raises it instead, which lands the error where both paths surface it: the builtin returns {'error': ...} to the model, and the legacy path emits its visible "An error occurred while searching the web" status event. Asserts the exact pinned literals so a digest bump that shifts the engine wrapper breaks the build loudly. ### INFO acknowledged: bypass flags widen the injection surface Acknowledged. BYPASS_WEB_SEARCH_WEB_LOADER=true means result snippets are injected as context without a full-page fetch and without the RAG embedding/retrieval pipeline, so third-party snippet text reaches the model as untrusted context. That surface exists either way (the snippets would be injected after retrieval instead), and the demo posture accepts it: five users, controlled box, snippets truncated to the configured result count (3 on the box). The enterprise profile stays off by default, which is the posture that matters for the data-sovereign buyers. No code change taken from this one. ## Rev 2 live evidence After the manual box step, verified against the real deployment through chat-hive.scubed.co: 1. Authenticated /api/config reports enable_web_search: True. 2. The retrieval web-search API returned live DuckDuckGo results from the box (Bangladesh technology news, August 2026: thedailystar.net, dailybanglapost.com, digibanglatech.news). 3. In the real Hive-branded chat UI (deepseek-v4-flash): the Web Search globe pill renders active, the turn ran, and the message shows "Retrieved 3 sources" from live DuckDuckGo results. Screenshot posted below. Honest limitation: the completion TEXT on the box errored with the fork's "chat session is not carrying a signed-in user token" message because the verification used a throwaway locally-created chat account, which has no stored OAuth session for hive_jwt_forward to forward to the gateway. Every real user signs in through Hive SSO and carries that token. The search half (the feature this change enables) is fully exercised on the box; the completion-text half is blocked by the throwaway account's missing OAuth session, a pre-existing deployment auth property unrelated to web search. The full cited-reply loop is proven end to end on the throwaway stack running this branch's image. ### Reconcile proven against a seeded stale DB A throwaway boot of the patched image against a hand-seeded webui.db carrying the box's exact stale rows flipped all four at boot (log line "hive: reconciled Open WebUI config from env: ... web.search.enable=True, web.search.engine=duckduckgo ..." plus a read-back showing true / "duckduckgo" / true / true). The rate-limit re-raise is present in the built image.
Summary
engine == 'duckduckgo'branch) requiring zero API key or account signup, confirmed against the current upstreammainbranch (package.json version 0.10.2), matching the pinned imageghcr.io/open-webui/open-webui:main@sha256:74093dadc9c6....Verification (live, against the running local stack)
GET /api/v1/retrieval/config(authenticated) confirms the running instance reportsENABLE_WEB_SEARCH=true,WEB_SEARCH_ENGINE=duckduckgo, and both bypass flags true — this is the same config the chat UI reads to decide whether to render the web-search toggle.POST /api/v1/retrieval/process/web/search(authenticated) with a live query returned real, current DuckDuckGo results (page snippets referencing "Jul 22, 2026", i.e. genuinely current, not cached/stale) end to end, with zero API key configured.GET /api/modelson this instance returns an empty list (no working provider/model wired yet). That is being fixed by another agent and is not part of this change. The web-search wiring itself is independently confirmed working via the retrieval API above.Test plan
Greptile Summary
This PR adds opt-in DuckDuckGo search to Open WebUI chat. The main changes are:
.env.examplewith disabled defaults.Confidence Score: 5/5
This looks safe to merge.
Important Files Changed
Reviews (2): Last reviewed commit: "fix: make Open WebUI web search opt-in, ..." | Re-trigger Greptile
Context used (3)