Skip to content

feat: enable duckduckgo web search in Open WebUI chat - #414

Merged
sakibsadmanshajib merged 2 commits into
mainfrom
worktree-agent-afc3352c89a6485f9
Jul 21, 2026
Merged

sakibsadmanshajib merged 2 commits into
mainfrom
worktree-agent-afc3352c89a6485f9

Conversation

@sakibsadmanshajib

@sakibsadmanshajib sakibsadmanshajib commented Jul 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Owner confirmed the open-webui service had zero web-search env vars wired (not disabled, just absent). Owner wants web search working in chat for a demo tonight.
  • Adds ENABLE_WEB_SEARCH=true, WEB_SEARCH_ENGINE=duckduckgo, BYPASS_WEB_SEARCH_WEB_LOADER=true, BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL=true to deploy/docker/docker-compose.yml (open-webui service). No docker-compose.staging.yml change needed; it does not override the open-webui service.
  • duckduckgo chosen because it is the only OWUI-supported engine (backend/open_webui/routers/retrieval.py, engine == 'duckduckgo' branch) requiring zero API key or account signup, confirmed against the current upstream main branch (package.json version 0.10.2), matching the pinned image ghcr.io/open-webui/open-webui:main@sha256:74093dadc9c6....
  • Both bypass flags return search-result snippets directly (skip full-page fetch and RAG-embedding retrieval), which decouples this feature from the RAG embedding path that runs through the same model-provider stack as chat completions.

Verification (live, against the running local stack)

  • Rebuilt and restarted the open-webui container; confirmed all four env vars present inside the container and the container reports healthy.
  • GET /api/v1/retrieval/config (authenticated) confirms the running instance reports ENABLE_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.
  • Full chat completion end-to-end (send message, toggle web search, get an assistant reply with cited sources) is currently blocked by a separate, parallel bug: GET /api/models on 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

  • Env vars present inside the running open-webui container
  • Retrieval config API reflects the new settings
  • Direct web-search retrieval API call returns live, current results with the duckduckgo engine
  • Full in-chat toggle + cited-source reply (blocked on the separate model-routing/provider bug; re-verify once that lands)

Greptile Summary

This PR adds opt-in DuckDuckGo search to Open WebUI chat. The main changes are:

  • Adds web-search settings to .env.example with disabled defaults.
  • Passes the settings into the Open WebUI container.
  • Enables snippet-only search when operators turn on both bypass options.

Confidence Score: 5/5

This looks safe to merge.

  • The previous default-on behavior is removed.
  • Enterprise deployments resolve web search to disabled unless an operator explicitly enables it.
  • No blocking issues remain in the changed code.

Important Files Changed

Filename Overview
.env.example Documents the Open WebUI search variables with safe disabled defaults.
deploy/docker/docker-compose.yml Passes operator-controlled web-search settings to Open WebUI and defaults outbound search to off.

Reviews (2): Last reviewed commit: "fix: make Open WebUI web search opt-in, ..." | Re-trigger Greptile

Context used (3)

  • Context used - .claude/rules/openwolf.md (source)
  • Context used - CLAUDE.md (source)
  • Context used - .claude/rules/orchestrator.md (source)

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.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@sakibsadmanshajib, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7585c424-fd05-4768-b953-bbfee24be06d

📥 Commits

Reviewing files that changed from the base of the PR and between 2818b3b and 208b075.

📒 Files selected for processing (2)
  • .env.example
  • deploy/docker/docker-compose.yml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-agent-afc3352c89a6485f9

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread deploy/docker/docker-compose.yml Outdated
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.
@sakibsadmanshajib
sakibsadmanshajib merged commit b1ac337 into main Jul 21, 2026
22 of 23 checks passed
@sakibsadmanshajib
sakibsadmanshajib deleted the worktree-agent-afc3352c89a6485f9 branch July 21, 2026 21:51
@sakibsadmanshajib

Copy link
Copy Markdown
Owner Author

Live verification, this session, against the running local demo stack (OWUI at localhost:3003):

  • Logged in as asdas@asdas.sda, sent a real chat message end to end via hive-auto: got a real assistant completion, confirming this PR's previously-flagged blocker ("GET /api/models returns empty list, no working provider/model wired yet, being fixed by another agent") is now resolved.
  • Toggled Web Search on and asked a genuinely current-events question; response came back with "Explored 2 search_web" and 4 real cited sources, confirming full end-to-end web search now works in the actual chat flow, not just at the retrieval API layer this PR originally verified.
  • Note: getting the model list populated required a live fix unrelated to this PR's code (Open WebUI's own persisted OPENAI_API_KEYS config had gone stale relative to the current OWUI_SHIM_KEY; fixed live via /openai/config/update, permanent code fix for the sync gap in flight separately).

sakibsadmanshajib added a commit that referenced this pull request Aug 24, 2026
# 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant