Skip to content

feat(search): add 'auto' adapter — DDG primary, Brave fallback#40

Open
askalf wants to merge 2 commits into
masterfrom
feat/search-auto-fallback-v2
Open

feat(search): add 'auto' adapter — DDG primary, Brave fallback#40
askalf wants to merge 2 commits into
masterfrom
feat/search-auto-fallback-v2

Conversation

@askalf
Copy link
Copy Markdown
Owner

@askalf askalf commented May 25, 2026

What

Adds a new --search=auto adapter that runs DuckDuckGo primary and transparently falls back to Brave Search on DDG failure (HTTP 429 / 5xx / network error) or empty result set.

export DEEPDIVE_BRAVE_KEY=<your-brave-key>
deepdive "test query" --search=auto

# or via env
DEEPDIVE_SEARCH=auto deepdive "test query"

If DEEPDIVE_BRAVE_KEY is unset, auto degrades to DDG-only rather than erroring — same effective behavior as the prior default. With the key set, transient DDG failures are masked by the Brave retry.

Brave itself has been a first-class --search=brave adapter since before this PR (src/search/brave.ts); this change adds the orchestration layer the ticket asked for.

Why

Per ticket OPS-8171409F0B49493D95C2EF: the planner needs a way to recover when DDG hits a rate limit or returns poor results, without forcing users to manually retry with a different --search. auto is the route the ticket spec'd; the spec said DEEPDIVE_SEARCH_ENGINE=brave|ddg|auto but the codebase already uses DEEPDIVE_SEARCH (singular surface), so I folded auto into that existing flag rather than introducing a parallel env name.

Changes

  • src/search/auto.ts (new, 33 lines): AutoSearch wraps primary + optional secondary SearchAdapter. On primary error OR empty result it retries the secondary, unless the abort signal is already tripped (in which case it rethrows the primary error to preserve cancellation semantics).
  • src/search.ts: new auto case in resolveSearchAdapter. Brave secondary is constructed only when DEEPDIVE_BRAVE_KEY is present.
  • src/cli.ts: --search help text lists auto and documents the DDG→Brave fallback contract.
  • README.md: new Auto row in the Search adapters table.
  • CHANGELOG.md: Unreleased entry.
  • test/search-auto.test.mjs (new, 9 cases): primary success, primary error→fallback, primary empty→fallback, primary error with no secondary rethrows, primary empty with no secondary throws zero-results, signal/limit pass-through, aborted signal skips fallback, and the two resolveSearchAdapter("auto", env) paths (with and without DEEPDIVE_BRAVE_KEY).

Verification

> @askalf/deepdive@0.12.0 typecheck
> tsc --noEmit
(clean)

> @askalf/deepdive@0.12.0 build
> tsc
(clean)

> npm test
ℹ tests 432
ℹ pass 432
ℹ fail 0

Including the 9 new cases in test/search-auto.test.mjs. No regressions in the existing 423-case suite.

Smoke-checked adapter resolution end-to-end (no network calls, all permutations):

auto without brave: auto AutoSearch
auto with brave:    auto AutoSearch
brave:              brave BraveSearch
brave-no-key error: brave adapter requires DEEPDIVE_BRAVE_KEY
unknown error:      unknown search adapter: nope

Ref: OPS-8171409F0B49493D95C2EF

askalf and others added 2 commits May 25, 2026 02:09
New `--search=auto` / `DEEPDIVE_SEARCH=auto` runs DuckDuckGo first and
falls back to Brave Search when DDG throws (rate-limit / 5xx / network
error) or returns zero results. Brave secondary is constructed only when
DEEPDIVE_BRAVE_KEY is present, so `auto` degrades to DDG-only in
unconfigured environments rather than erroring.

Brave Search itself has been a first-class `--search=brave` adapter
since before this change; this PR adds the orchestration layer the
ticket asked for.

Ref: OPS-8171409F0B49493D95C2EF
Tightens types around AutoSearch's optional secondary adapter so the
'auto' DDG-only degradation path compiles cleanly on a fresh checkout.

Ref: OPS-8171409F0B49493D95C2EF
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