Scope: design/behavior ideas grounded in the current code at de48ddb, plus the fragile/undocumented parts of three subsystems the team flagged: the youtube_search tool, SearXNG engine auto-discovery, and the Reddit 3-tier pipeline. Fills the MASTER_CODE_REVIEW.md §4 "open issues" perspective from the product/behavior side.
- The tool is active if and only if
config.youtube.api_keyis set (envFORAGE_YOUTUBE_API_KEY/YOUTUBE_API_KEY).YouTubeConfig.enabledis a property returningbool(api_key). - Registration checks
config.youtube.enabledinmcp.py(tools list, declared-engine list, and the unknown-tool error) andmain.py(root()tools list). - There is a single execution path: the direct YouTube Data API v3 (
search_youtube_direct+resolve_handle_to_channel_id). There is no keyless / SearXNG fallback — without a key the tool is not registered and any call returns a clear "key required" error. - The response carries only
success,searched_at,results, andwarning. The SearXNG-flavoredrequested_engines/returned_engines/unresponsive_enginesfields (which described which search engines responded) are gone — they had no meaning for a native-only tool.
- A. (Done) Native-only activation — the SearXNG proxy fallback and the
enabled/searxng_engineconfig knobs are removed; key presence is the sole gating condition. - B. (Done) Response bookkeeping — the vestigial SearXNG engine fields are removed from the YouTube response; nothing downstream consumes them.
- C. (Nice-to-have) Show the backend in tool output, e.g.
"backend": "native", so the conversation can self-explain which path ran.
get_live_available_enginesfirst honorssearch.available_enginesif explicitly set (no discovery at all).- Otherwise it probes
GET /configwith a 2.0 s timeout, only accepting engines that (a) areenabled, (b) belong to thegeneralcategory (category-less engines count), and (c) are not translate/currencyor media-suffix sub-scrapers (NON_TEXT_SUFFIXES). - Result is cached 300 s (
_last_engine_fetch). - Fallback: if the probe throws or times out, the function returns the hard-coded
DEFAULT_AVAILABLE_ENGINEStuple (searxng.py:148–152) — a static list that can name engines your instance never had (e.g.youtube,reddit,qwant news) — or likewise when the probe "succeeds" but returns no engines. The static list and the live list can disagree about which engines are actually present.
So during (or after) a slow SearXNG cold start, Forage tells the model a static catalog that may name engines your instance never has (the tuple is ("google", "qwant", "qwant news", "brave", "bing", "startpage", "duckduckgo", "reddit", "wikipedia", "youtube", "github", "searxng", "yahoo", "wikidata"), i.e. it asserts reddit/wikidata/qwant news whether or not they're registered); every call then yields the "ignored unknown engine(s)" warning + default-engine fallback. The failed probe is logged only at logger.debug (searxng.py:186), so the process is quiet while it's wrong.
- A. Document it — a short "Engine auto-discovery & fallback" subsection: probe (2 s), 5-min cache, general-category filter, the static fallback list, and how to pin a manual catalog with
FORAGE_AVAILABLE_ENGINES/search.available_engines. - B. Liveness hygiene — warn-visible (not just debug) when the
/configprobe fails or returns empty; optionally negative-cache a failed probe for ~60 s so a query storm doesn't re-hammer the instance. - C. Cold-start awareness — the SearXNG compose has no healthcheck today; document "wait for SearXNG to be ready (first engine
GET /200) before first Forage search", or bump the probe timeout to ~5 s. - D. (Optional) Per-call refresh — let
POST /searchacceptfresh_engines: trueto bypass the 300 s cache when the model wants re-probe after a SearXNG restart.
What the code actually does today (app/extract.py:643–806 + Tier 2 :724–800 + fallback in :1075–1111)
- Tier 1 – official
.json(?raw_json=1&limit=100&depth=10for/comments/) with a Chrome UA +Sec-Fetch-*nav headers, 0.75 s throttle, and a 30 s cooldown on 403/429 (_set_reddit_json_cooldown(30.0)). - Tier 2 – Redlib/SafeReddit mirror with lean headers and a 4 s cap.
- Tier 3 – stealth browser + Readability full comment tree.
- The efficiency truth: without authenticated cookies (
reddit_session/token_v2), Reddit's JSON endpoint is rate-limit- or auth-gated in the vast majority of cases — 403/429 → cooldown → Tier 2 → if that's degraded too, Tier 3. With cookies, Tier 1 is sub-second and reliable. The code reflects this, but nothing in the primary docs says "tier 1 needs cookies for consistent throughput; mirror/browser is the graceful degradation path". Also, there is currently no rate-limit reporting surface for how many Tier-1 attempts actually go through under the current call load.
- A. Document the expectation — one paragraph in README "What's New" + a note under
extract.domain_overridesindocs/CONFIG.md: "Reddit JSON (tier 1) is most efficient with a logged-in session. SetFORAGE_REDDIT_SESSION/FORAGE_REDDIT_TOKEN_V2(see compose snippet), or rely on the mirror/browser fallback which trades ~sub-second for 5–30 s per page." - B. Expose the tier used — the result envelope already carries
method(web_egvariants likereddit+json,reddit+mirror,browser+solo). Surface which tier produced the response in the formatted text (e.g.[{position}] Reddit Thread (tier: 1 .json)), so a model or user can see why it was slower. Low risk;extract.pyalready tagsmethodper tier. - C. Search vs. link fetch — see §4.
web_extractalready handles Reddit URLs end-to-end (the three-tier pipeline, above), including/r/<sub>/search?q=...URLs,/comments/<id>, and user pages (seeextract.py:655–672path matchers).- There is no Reddit search tool; the only search-ish pattern is
web_searchusing SearXNG'sredditengine when/search?q=URLs are hand-shaped. That is exactly the undreadable spot theyoutube_searchtool wanted to fix: mid-size models produce search-syntax URLs for Reddit all the time but rarely call a dedicated search tool.
- Just document Reddit URL search in
web_extract(cheapest, no new endpoint): add a tool-description nudge inprompts.yamland one line in README: "To fetch a Reddit thread or search, pass the URL toweb_extract— e.g.https://www.reddit.com/r/...orhttps://www.reddit.com/search?q=...." - Dedicated
reddit_searchtool (mirrorsyoutube_search's routing): mapsearch: <query>+ optionalsubredditparam to the Reddit.jsonsearch endpoint (SearXNG'sredditengine or the.jsonsearch call at/r/<sub>/search/.json), races the same three-tier scrub pipeline asweb_extract. Pros: handles the "model wants search, not URL" case cleanly; no API keys. Cons: one more surface to test; support for which a mid-size model picks the wrong tool is already real ("search" vs "extract" vs "fetch"). - Middle ground — add a
reddit: {search_enabled: true}config flag that only exposesreddit_searchwhen an API/session cookie is configured (tier-1 can handle native JSON search in <1 s), keeping search for uncookie instances as a plainweb_extractURL.
For now: I'd stop at (1) if the goal is to reduce mis-calls without expanding surface — that reuses already-working pipeline; but honestly that might be overkill to ship with a proper search tool. The max surface gain is in (2)/(3) because youtube_search already shows the model how to parse search-strategy queries.
requirements.lock— untracked & mispoken (containscptr,claude-agent-sdk, …). Delete or clean-recreate.- Git identity still
cptr <cptr@localhost>. origin/main/stable-checkpointwere never advanced pastde48ddb.browser.pysingle-retry heal (one-shot_restart_scrapling_session) under burst load.- No direct tests for
browser.pyrender paths. - R-3 (
/extractREST duplication) still open. - SSE
/mcp/sse+/mcp/messagesqueue branch untested.
- Fix the
youtubename mismatch (IDEA 1B): ~3 lines inapp/youtube.py. Turns the keyless proxy behavior from silent-web to reliably-YouTube (when SearXNG has a youtube engine, whatever your name is). Do this before the availability gating so the tool is honest.- (Alternatively, wait: maybe
searxng.pyshould treatyoutube_apias a family alias likeduckduckgo_search→duckduckgo; that also fixes web_search when a model saysengines: ["youtube-api"].)
- (Alternatively, wait: maybe
- Docs/config sync (that pass): README tool list + env-var table; CONFIG.md env-var rows (
FORAGE_YOUTUBE_NAME/DEFAULT_LIMIT/MAX_LIMITany missingFORAGE_REDDIT_TOKEN_V2comment); SEARXNG.md note on the name mismatch + cold-start probe fragility; config.example.yaml addyoutube_nameundertools:+ note underyoutube.api_key(the no-key fallback behavior). - README + CONFIG.md Reddit-tier note (IDEA 3A) — one paragraph + one config.example line-comment.
- IDEA 2 docs + small
logger.warningwhen the probe falls back (cheap telemetry, no behavior change). - Decide on §4 (docs-only vs
reddit_searchtool) and schedule accordingly. - Then F-1..F-10 from MASTER_CODE_REVIEW §5 (code hygiene), knowing some (
logger, lock, git id) are one-liners.