Portfolio project with a strict $0 running cost (free tiers only). See plan.md for the full build plan.
cd backend
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
copy ..\.env.example ..\.env # then fill in DATABASE_URL, REDIS_URL, GEMINI_API_KEY
.venv\Scripts\alembic upgrade head
.venv\Scripts\python run.py # http://localhost:8000/health
.venv\Scripts\pytest
Notes:
- Use
redis://orrediss://inREDIS_URLdepending on whether your provider uses TLS. - Start the server with
python run.py, not plainuvicorn: psycopg async cannot use Windows' default event loop. /healthchecks Postgres (+ pgvector), Redis and that an LLM key is configured. It never calls Gemini.
cd backend
.venv\Scripts\python -m app.rag.cli "3 bed house in Austin under 500k" # one-shot
.venv\Scripts\python -m app.rag.cli # conversation, follow-ups keep filters
Each query uses 1 Gemini LLM call (parse) + 1 embedding call. The reranker runs locally (fastembed, downloads ~90 MB on first use).
Fair Housing guards live in backend/app/guardrails/ (rules first; the LLM classifier only runs for ambiguous input and fails closed). Run the eval from backend/:
.venv\Scripts\python -m evals.run_evals guardrails # 30 steering + 30 normal prompts, plus output set
.venv\Scripts\python -m evals.run_evals guardrails --set heldout # prompts the rules were NOT tuned on
.venv\Scripts\python -m evals.run_evals guardrails --no-llm # rules only, zero Gemini calls
- Every non-streaming error uses
{"error": {"code", "message", "request_id", "details?"}}; every response carriesX-Request-ID, and every log line is JSON with that id. Secrets are redacted from logs. - Rate limits (per IP, plus per session for chat):
RATE_LIMIT_PER_MINUTE,CHAT_RATE_LIMIT_PER_MINUTE->429+Retry-After. LLM_DAILY_CALL_LIMITcaps Gemini calls per Pacific-time day; once spent, chat answers503 high_demand+Retry-Afterinstead of failing on Gemini's own 429.- Redis caches listing queries, retrieval candidates and LLM parse results. Re-ingesting listings clears the cache.
cd frontend
copy .env.local.example .env.local
npm install
npm run dev # http://localhost:3000 (backend must be running on :8000)
See frontend/README.md for pages and design notes.