Produce a reusable template repo that adds a local Wikipedia search source to the LangGraph “Open Deep Research” stack and ships runnable Kubernetes resources. Generate files with concrete content, parameterized via {{variables}}. Follow the spec exactly.
Goal
Create a minimal, reproducible template a downstream user can clone, set a few vars, and deploy:
-
Runs
open_deep_researchAPI with a Wikipedia search backend. -
Hosts a local Kiwix mirror of Wikipedia ZIM files.
-
Performs semantic search with
txtaiand returns Markdown excerpts with citations to local Kiwix URLs. -
Selects the Wikipedia tool via
search_api: WIKIPEDIAwithout changing graph logic.
Output format
Do not add commentary outside files. Include all files listed below.
Repository layout
open-deep-research-wiki-template/
README.md
Makefile
k8s/
resources.yaml
kiwix.yaml
docker/
Dockerfile
entrypoint.sh
run.sh
run_fastapi.sh
pyproject.toml
uv.lock # generate a minimal lock; pin txtai + torch cpu
langgraph.json # pass-through if needed
src/open_deep_research/
__init__.py
deep_researcher.py # integrates unchanged graph, no business-logic changes
utils.py # adds wiki_search + get_search_tool wiring
wikipedia.py # local Kiwix + txtai retrieval
configuration.py # SearchAPI enum includes WIKIPEDIA
prompts.py # unchanged
state.py # unchanged
configuration_example.env
Parameterization
Use Jinja-style placeholders. Do not hardcode values.
-
{{NAMESPACE}}default:open-deep-research -
{{DOMAIN}}default:<domain> -
{{WIKI_HOST}}default:wiki.{{DOMAIN}} -
{{KIWIX_BASE}}default:https://{{WIKI_HOST}} -
{{ZIM_NAME}}default:wikipedia_en_all_maxi_2025-08 -
{{API_IMAGE}}default:localhost:32000/mm_open_deep_research:latest -
{{TLS_SECRET}}default:<domain>-wildcard-tls -
{{REPLICAS}}default:1 -
{{API_PORT}}default:8123 -
{{CPU_LIMIT}}default:4 -
{{MEM_LIMIT}}default:56Gi -
{{ZIM_HOSTPATH}}default:/mnt/data/zim
File requirements
1) k8s/resources.yaml
-
Deployment + Service for API.
-
Env for threading control:
FAISS_NUM_THREADS=1,OMP_NUM_THREADS=1,OPENBLAS_NUM_THREADS=1,MKL_NUM_THREADS=1,PYTHONFAULTHANDLER=1. -
SERVER_MODE=fastapi,PORT={{API_PORT}}. -
Resource requests: cpu
100m, mem2Gi. Limits from params. -
Readiness/Liveness GET
/. -
Service on port 80 ->
{{API_PORT}}.
2) k8s/kiwix.yaml
-
Deployment, Service, Ingress for
kiwix-serve. -
Container
ghcr.io/kiwix/kiwix-serve:latestwith argskiwix-serve -p 8080 /data/zim/*.zim. -
hostPathvolume at{{ZIM_HOSTPATH}}read-only. -
Ingress
{{WIKI_HOST}}with TLS{{TLS_SECRET}}.
3) docker/Dockerfile
-
Base:
python:3.11-slim. -
Install build deps:
ca-certificates curl git build-essential libgomp1. -
Install
uvvia official script. -
Set caches to
/opt/cache(HF, transformers, sentence-transformers, torch, pip). -
Copy
pyproject.toml,uv.lock,src,server(if present), scripts. -
uv syncthen export venv to PATH. -
Set
XDG_CACHE_HOMEand related envs. -
Prewarm by invoking
/app/src/open_deep_research/wikipedia.pyto trigger model pulls. -
ENTRYPOINTtoentrypoint.sh.
4) docker/entrypoint.sh
SERVER_MODEswitch:fastapi->run_fastapi.sh,cli->run.sh.
5) pyproject.toml and uv.lock
-
Dependencies include:
langgraph,langchain-*,openai,exa-py,tavily-python,pandas,fastapi,uvicorn,langserve,rich,httpx,beautifulsoup4==4.13.3,txtai==9.0.0,torch>=2.5,<3.
-
Configure CPU-only torch source:
[tool.uv.sources] torch = [{ index = "pytorch-cpu" }] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true -
Ensure
txtaipullsneuml/txtai-wikipediaat runtime.
6) src/open_deep_research/wikipedia.py
Implement:
-
KIWIX_BASE=os.getenv("KIWIX_BASE","{{KIWIX_BASE}}") -
ZIM_NAME=os.getenv("ZIM_NAME","{{ZIM_NAME}}") -
Force CPU:
os.environ["FAISS_DISABLE_GPU"]="1". -
Embeddings().load(provider="huggingface-hub", container="neuml/txtai-wikipedia")behind an@lru_cacheandthreading.RLock. -
wiki_query(query, percentile_threshold=0.99, max_articles=5)that:-
Executes
txtaiSQLsimilar('{query}')with percentile filter. -
For each hit: resolve
title, fetch HTML from Kiwix (/raw/...then XML search fallback), strip to Markdown, drop references, collapse whitespace. -
Return dicts:
{title,id,score,percentile,kiwix_url,markdown}.
-
7) src/open_deep_research/utils.py
Add:
-
wiki_searchtool with signaturequeries: List[str]that callswiki_search_async, flattens results, and formatsSearch results:\n\n--- SOURCE i: <title> ---\nURL: <kiwix_url>\n\nSUMMARY:\n<markdown>\n\n---------. -
get_search_toolto mapSearchAPI.WIKIPEDIAtowiki_searchand expose it asname="web_search"so the rest of the graph is unchanged.
8) src/open_deep_research/deep_researcher.py
-
Keep the supervisor/researcher/compression logic as-is.
-
Ensure
get_all_tools(config)path picks upSearchAPI.WIKIPEDIAviautils.get_search_tool. -
No other semantic changes.
9) src/open_deep_research/configuration.py
-
Ensure
SearchAPIenum includesWIKIPEDIA. -
Default remains selectable via
config.configurable.search_api.
10) Makefile
Targets:
-
build: build API image tag{{API_IMAGE}}. -
push: push image. -
deploy-kiwix: applyk8s/kiwix.yaml. -
deploy-api: applyk8s/resources.yaml. -
undeploy: delete both. -
test-wiki: curlhttps://{{WIKI_HOST}}/readiness. -
invoke: sample JSON call withsearch_api: wikipedia.
11) src/open_deep_research/configuration_example.env
Document:
KIWIX_BASE={{KIWIX_BASE}}
ZIM_NAME={{ZIM_NAME}}
FAISS_DISABLE_GPU=1
SERVER_MODE=fastapi
PORT={{API_PORT}}
Quality gates
Implement and satisfy these checks:
-
Static: repository
pip checkinside the built image must pass. -
Runtime: container starts and
GET /responds 200. -
Kiwix: readiness on
/returns 200 after ZIM mount. -
Search: calling the graph with
search_api=wikipediareturns at least oneSOURCEblock withURL: https://{{WIKI_HOST}}/content/{{ZIM_NAME}}/A/.... -
Isolation: no external HTTP calls are required during research if Kiwix and ZIM exist.
-
Idempotency:
kubectl applytwice yields no changes.
Constraints
-
Use only the libraries listed in
pyproject.toml. -
Keep
txtaiCPU-only; do not enable GPU. -
Keep tool name exposed to the graph as
web_searchto avoid changing planner logic. -
Keep code Python 3.11 compatible.
Done when
-
All files are emitted with full contents using the delimiter.
-
Placeholders are present where specified.
-
A user can fill variables, apply manifests, build image, and run a local Wikipedia-backed deep-research workflow end to end.