FastAPI backend for the AI Research Navigator — a conversational assistant that helps researchers discover imaging technologies, facilities, and services across the Euro-BioImaging network.
- ReAct agent loop — multi-step reasoning with OpenAI tool calling
- BM25 full-text search over technologies, nodes, and website pages (index bundled locally)
- Streaming SSE — step-by-step reasoning events + token-by-token answer streaming
- JWT RS256 auth — validates tokens issued by the EAP Django backend (no shared DB required)
- CORS-ready — configurable origins for the EAP frontend
- Python 3.12+
- uv (recommended) or pip
- An OpenAI API key
- The EAP JWT RS256 public key (
keys/jwt_public_key.pem)
cd navigator
# Install dependencies
uv sync
# or: pip install -e ".[dev]"
# Configure
cp .env.example .env
# Edit .env — set OPENAI_API_KEY and JWT_PUBLIC_KEY_PATH at minimum
# Start the dev server (with auto-reload)
uv run uvicorn navigator.main:app --reload --port 8001
# or: python -m navigatorThe API will be available at http://localhost:8001.
Standalone chatbot demo page: http://localhost:8001/.
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
(required) | OpenAI API key |
JWT_PUBLIC_KEY_PATH |
keys/jwt_public_key.pem |
Path to RS256 public key PEM |
OPENAI_MODEL |
gpt-4o-mini |
OpenAI model name |
CORS_ORIGINS |
http://localhost:3000 |
Comma-separated allowed origins |
HOST |
0.0.0.0 |
Bind address |
PORT |
8001 |
Listen port |
LOG_LEVEL |
info |
Uvicorn log level |
| Method | Path | Description |
|---|---|---|
GET |
/ |
Standalone chatbot HTML page |
GET |
/health |
Health check ({"status":"ok"}) |
POST |
/api/chat |
Non-streaming chat |
POST |
/api/chat/stream |
SSE streaming chat |
data: {"type": "step", "text": "Searching for: \"confocal microscopy\""}\n\n
data: {"type": "step", "text": "Composing answer…"}\n\n
data: {"type": "token", "token": "Here"}\n\n
data: {"type": "token", "token": " are"}\n\n
data: [DONE]\n\n
stepevents — emitted before each tool-call batch and before the final answertokenevents — partial text tokens of the answer[DONE]— stream end marker
Build and run locally:
# Build image
docker build -t navigator:dev .
# Run (mount your public key and pass secrets via env)
docker run --rm -p 8001:8001 \
-e OPENAI_API_KEY=sk-... \
-e CORS_ORIGINS=https://eap.eurobioimaging.eu \
-v /path/to/jwt_public_key.pem:/app/keys/jwt_public_key.pem:ro \
navigator:devSee helm/README.md for Kubernetes deployment.
The BM25 search index is pre-built from the Euro-BioImaging Access Portal data and bundled in the data/ directory:
data/eurobioimaging_index.json— technologies, nodes, website pages (AI-enriched descriptions)data/eurobioimaging_bm25_index.pkl— pre-built BM25 pickle for fast retrieval
If the local files are absent at startup, the service downloads the latest index from aicell-lab.github.io/euro-bioimaging-finder.
# Lint
uv run ruff check navigator/
uv run ruff format navigator/
# Tests
uv run pytest tests/navigator/
├── Dockerfile
├── .dockerignore
├── pyproject.toml
├── README.md
├── helm/ # Helm chart for Kubernetes deployment
├── data/ # Bundled search index (auto-downloaded if missing)
│ ├── eurobioimaging_index.json
│ └── eurobioimaging_bm25_index.pkl
├── keys/ # RSA public key for JWT validation (not committed)
│ └── jwt_public_key.pem
└── navigator/ # Python package
├── __init__.py
├── __main__.py # Entry point: python -m navigator
├── main.py # FastAPI app, routes
├── agent.py # ReAct agent loop, tool dispatch
├── eurobioimaging.py # Data loader, BM25 search, tool functions
├── auth.py # JWT RS256 validation
├── config.py # Settings from env vars
└── templates/
└── index.html # Standalone chatbot page