LangGraph-based math tutor that guides a student through math problems step by step using structured solver output, graph interrupts, streaming responses, and persisted chat state.
math_tutor/
app/ FastAPI app, routers, login page, and Gradio HTTP interface
core/
base/ Base graph wrapper and stream event normalization
main_graph/ Top-level tutor graph
solver_subgraph/ Math problem classification and structured solution generation
next_hint_subgraph/ Guided hint, answer evaluation, and step reveal flow
models/ LLM factory and deterministic mock model
utils/ Shared graph/model utilities
database/ SQLAlchemy schema, database manager, and repositories
tests/
smoke/ Ruff, bytecode compile, and import checks
graph/ Fast MVP flow tests without API/Docker/Postgres
e2e/ End-to-end API persistence test with Docker Compose
integration/ Concurrent streaming checks with Docker Compose
ollama/ Local Ollama compose setup, model runner, and smoke test
tgi/ Example Text Generation Inference setup
postgres/ Standalone PostgreSQL helper compose
-
Create a local
.envfile from the example:cp .env.example .env
-
Fill the required application settings in
.env.Minimal local shape:
HOST=0.0.0.0 PORT=12000 INTERFACE_IP=127.0.0.1 LOG_DIR="math_tutor/math_tutor_logs" AUTH_ACCESS_CODE="change-me" DATABASE__HOST="db" DATABASE__PORT=5432 DATABASE__USER="math_tutor" DATABASE__PASSWORD="math_tutor" DATABASE__DB="math_tutor" PRIMARY_MODEL__LLM_BACKEND="mock" PRIMARY_MODEL__BASE_URL="http://mock" PRIMARY_MODEL__API_KEY="mock" PRIMARY_MODEL__MODEL_NAME="mock" PRIMARY_MODEL__STREAM_USAGE=false GRAPH__TOOL_SYSTEM_PROMPT="general_model_system_prompt_for_math"
For a real LLM, replace the
PRIMARY_MODEL__*values with your Ollama, vLLM, TGI, or OpenAI-compatible endpoint. When the LLM runs on the host and the app runs in Docker, usehost.docker.internalfrom inside the container. -
Start the app, Postgres, and Adminer:
docker compose -f compose.yaml up --build
-
Open the app:
http://127.0.0.1:12000Login requires:
user_id: any local user identifier for separating chat history;access_code: value fromAUTH_ACCESS_CODE.
-
Adminer is exposed at:
http://127.0.0.1:5556 -
Stop services:
docker compose -f compose.yaml down
To remove the local application database volume as well:
docker compose -f compose.yaml down -v
Install project dependencies first. In CI this is done through Poetry; locally use the same environment you use for development.
Fast checks for push/PR:
make test-ciThis runs:
test-smoke: Ruff linting, Python bytecode compilation, and import smoke checks;test-graph: six async graph-level MVP flow tests without FastAPI, Docker, or Postgres.
Run parts separately:
make test-smoke
make test-graph
python3 -m pytest tests/graphEnd-to-end Docker test:
bash tests/e2e/run.shThe e2e test starts its own app and Postgres services with mock LLM settings. App logs are written to:
tests/e2e/artifacts/
Streaming integration test:
bash tests/integration/run.shIntegration artifacts are written to:
tests/integration/artifacts/
For running only a standalone PostgreSQL/Adminer stack, see:
postgres/README.md
