Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.9 KiB
Unified Lookup Tool & Wikipedia Integration
Goal
Replace the fragmented search_web tool with a single lookup tool that checks Wikipedia first and falls back to SearXNG web search. Add Wikipedia as an additional source in the research pipeline. Result: one lightweight tool for factual questions (always available, no config required), and richer research output.
Architecture
Two changes to the search/knowledge stack:
-
New
lookuptool replacessearch_web. Tries Wikipedia REST API summary endpoint first (~200ms, reliable, no config). Falls back to SearXNG + trafilatura article fetch when Wikipedia misses and SearXNG is configured. Always available (norequiresfield). -
Wikipedia sources in research pipeline. During sub-query execution,
wiki_searchruns alongside_search_searxng. Wikipedia articles merge into the source pool and get deduplicated by URL.
Shared Wikipedia logic lives in a new wikipedia.py service module.
Components
src/fabledassistant/services/wikipedia.py (new)
Two async functions:
wiki_summary(query: str) -> dict | None
- Direct title lookup via
https://en.wikipedia.org/api/rest_v1/page/summary/{title} - Returns
{"title": str, "extract": str, "url": str}on hit - Returns
Noneon 404, disambiguation pages ("type": "disambiguation"), network errors, or empty extracts - 5-second timeout
- User-Agent:
"FabledAssistant/1.0 (https://fabledsword.com)"
wiki_search(query: str, limit: int = 3) -> list[dict]
- Search via
https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch={query}&srlimit={limit}&format=json - For each search result, fetch its summary via the summary endpoint to get the extract
- Returns
[{"title": str, "extract": str, "url": str}, ...] - Returns
[]on any failure - Same timeout and User-Agent as above
src/fabledassistant/services/tools/web.py (modified)
Remove: search_web_tool
Add: lookup_tool
@tool(
name="lookup",
description="Look up a topic, concept, or factual question. Returns a concise
answer from Wikipedia or web sources. Use for definitions,
explanations, 'what is X', 'how does Y work'. For comprehensive
written reports saved as notes, use research_topic instead.",
parameters={
"query": {"type": "string", "description": "The topic or question to look up"},
},
required=["query"],
)
No requires field — always available.
Logic:
- Call
wiki_summary(query) - If Wikipedia returns a result: return
{"success": True, "type": "lookup", "source": "wikipedia", "data": {"title": ..., "extract": ..., "url": ...}} - If Wikipedia misses and
Config.searxng_enabled():- Call
_search_searxng(query)to get search results - Fetch top 1-2 result URLs via
_fetch_full_article(fromrss.py, trafilatura-based) - Return
{"success": True, "type": "lookup", "source": "web", "data": {"query": ..., "results": [...], "content": ...}}
- Call
- If Wikipedia misses and no SearXNG: return
{"success": True, "type": "lookup", "source": "none", "data": {"query": ..., "message": "No results found. You can answer from your own knowledge."}}
src/fabledassistant/services/research.py (modified)
In Step 2 (parallel search):
- For each sub-query, run
wiki_search(query, limit=1)concurrently with_search_searxng(query) - Merge Wikipedia results into the per-query result list
In Step 3 (deduplication):
- When deduplicating URLs, Wikipedia URLs (
wikipedia.org) are checked against SearXNG results - If a Wikipedia article URL already appears in SearXNG results, skip the duplicate
Wikipedia article content for synthesis:
- The
extractfromwiki_searchis used as the source content (no additional fetch needed, unlike SearXNG URLs which requirefetch_url_content) - This means Wikipedia sources are available immediately without an HTTP fetch step
Error Handling
- All Wikipedia API failures (network, timeout, malformed JSON) return
None/[]silently lookupnever raises — always returns a response the model can work with- In the research pipeline, Wikipedia is purely additive; its failure never degrades existing SearXNG-based research
- Disambiguation pages are detected via
"type": "disambiguation"in the summary response and treated as a miss
Testing
tests/test_wikipedia.py (new)
test_wiki_summary_returns_extract— mock successful summary response, verify return shapetest_wiki_summary_returns_none_on_404— mock 404, verifyNonetest_wiki_summary_returns_none_on_disambiguation— mock disambiguation response, verifyNonetest_wiki_search_returns_results— mock search API + summary fetches, verify listtest_wiki_search_returns_empty_on_failure— mock network error, verify[]
tests/test_lookup_tool.py (new)
test_lookup_wikipedia_hit— mockwiki_summaryreturning data, verify tool returns wikipedia sourcetest_lookup_wikipedia_miss_searxng_fallback— mockwiki_summaryreturning None, SearXNG returning results + article fetch, verify web sourcetest_lookup_wikipedia_miss_no_searxng— mock both missing, verify graceful "no results" responsetest_lookup_always_available— verify the tool appears inget_tools_for_userregardless of SearXNG config
tests/test_research_pipeline.py (add to existing)
test_research_includes_wikipedia_sources— mockwiki_searchalongside SearXNG, verify Wikipedia results appear in source pool
All tests mock HTTP calls — no live API hits.
What Doesn't Change
read_articletool — stays as-is (explicit URL fetch, different purpose)research_topictool definition — stays as-is (same name, description, parameters)generation_task.pyresearch interception — stays as-issearch_imagestool — stays as-is_search_searxngand_search_searxng_images— stay as-is_fetch_full_articleinrss.py— stays as-is, reused bylookupfor SearXNG fallback