Contributing to the LangGraph Chain¶
This guide covers working on backend/chain/ — the LangGraph 8-node AI pipeline.
Repo: aharbii/movie-finder-chain
For cross-cutting conventions (branching, commits, PRs, releases) see the Contributing Overview.
Pipeline overview¶
classify → search_rag → enrich_imdb → reason → route
│
┌─────────────────────────────┤
│ │
refine (≤3×) confirm → qa_agent
│ │
dead_end ◄────────────────── (exhausted)
All state is carried in MovieFinderState (TypedDict, src/chain/state.py).
Nodes are pure functions: they receive the full state and return a partial update.
Development setup¶
The chain runs inside the backend Docker stack. From backend/chain/:
make dev # build + start chain container with volume mount
make shell # attach a shell to the running container
make test # run pytest inside Docker
make lint # ruff check + format check
make typecheck # mypy --strict
make pre-commit # all hooks
You can also run from the backend root:
Adding a new node¶
- Create
src/chain/nodes/<name>.py— implement as an async function returning a state partial: - Register the node in
src/chain/graph.py— add to the builder and connect edges. - Update
state.pyif new fields are added toMovieFinderState. - Write tests in
tests/nodes/test_<name>.py. - Update
docs/architecture/plantuml/04-langgraph-pipeline.pumland05-langgraph-statemachine.puml.
State rule: MovieFinderState has total=False (issue #15). Always use .get() with a safe
default when reading fields — never index directly.
Code standards¶
mypy --strictmust pass on every node- Nodes are pure functions — no shared mutable state between calls
- No
os.getenv()in node files — read settings fromconfig.py(Pydantic BaseSettings) - No
print()— uselogging.getLogger(__name__) - Async all the way — no blocking I/O inside async functions
- Line length: 100
Testing¶
- Mock external services (Qdrant, OpenAI, Anthropic, imdbapi) — no real API calls in unit tests
pytest --asyncio-mode=autois configured —async def test_*works without@pytest.mark.asyncio- Coverage must not regress
Environment variables¶
Copy backend/chain/.env.example to .env and fill in: