Skip to content

chore: runnable baseline (monorepo, seedable RAG, streaming fixes, tests, CI) - #5

Draft
Sal-Fakoya wants to merge 52 commits into
mainfrom
chore/runnable-baseline
Draft

Sal-Fakoya wants to merge 52 commits into
mainfrom
chore/runnable-baseline

Conversation

@Sal-Fakoya

@Sal-Fakoya Sal-Fakoya commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Makes AskTemoc runnable from one repo. A teammate clones it, picks a model provider (OpenAI or a local Ollama), seeds the vector store with one command, starts the API and the web app, and gets a streamed answer with sources at http://localhost:3000. No sign-in is needed. This is not the 10-week plan; anything left for that plan is listed in docs/KNOWN_ISSUES.md.

Merge with a merge commit, not squash. This branch brings in davis118/AskTemoc_Backend with git subtree, and squashing would flatten that history. It also contains 0415dde from fix/chat-streaming-markdown-and-docs, so that branch doesn't need its own PR.

What was tested

  • Fresh clone on Windows, following the README quickstart. dev.ps1 setup, then seed (193 docs, 2047 chunks), then api and web.
  • On the final branch state:
    • /chat loads with no sign-in.
    • POST /api/proxy/chat (the path the browser uses) streams 4 sources, the answer and done from the local API on Ollama.
    • GET /api/health works with and without the trailing slash.
  • Tests: pytest passes 13 offline tests; npm run lint and npm run build pass. CI runs the same checks.
  • Docker: docker compose up api comes up healthy, and seeding and chat work in the container.
  • Not yet tested:
    • OpenAI. The test key's account had no credits, so the live checks ran on Ollama.
    • Answer quality. With llama3.1:8b, the BS CS question retrieves the right program page but its "Goals" and "Objectives" chunks rather than the course list, so the answer is weak. That's retrieval work for the 10-week plan.

What changed

  1. One repo.
    • backend/ is AskTemoc_Backend main, added with git subtree and its full history.
    • The scraper moved to backend/scrapers/catalog/ and the scraped programs to data/catalog/. The backend had its own byte-identical copy of the catalog, which is removed; its housing pages moved to data/housing/ and aren't seeded.
    • backend/app/.env is untracked.
    • requirements.txt is now UTF-8 and holds only the pinned runtime set. Scraping packages are in requirements-scrape.txt and test tools in requirements-dev.txt.
  2. Backend runs on a clean clone.
    • All settings go through app/core/config.py (pydantic-settings), and validation errors never echo setting values such as API keys.
    • The retriever and chain are built on first use, so importing the app never touches Chroma, OpenAI or Ollama.
    • /api/health answers with or without the trailing slash.
    • /api/chat streams answer pieces with astream instead of cumulative prefixes, then a final done event. The frontend appends the pieces, also accepts the older delta event shape, and ignores unknown types.
    • CORS origins come from CORS_ORIGINS.
    • backend/scripts/seed_catalog.py can be re-run safely and cites real catalog URLs from data/catalog/sources.json, which was generated once with the scraper's own link discovery.
    • The route collisions described in the original brief don't exist (every router has a prefix). A test now guards against duplicate routes.
  3. Frontend talks to the local API. The /api/proxy/* rewrites and NEXT_PUBLIC_API_BASE_URL are unchanged from before, except that next dev now proxies to http://localhost:8000 by default. Production builds still use Cloud Run from .env.production and vercel.json.
  4. Dev experience, tests, CI. docker-compose.yml (with optional Ollama), scripts/dev.sh / scripts/dev.ps1 plus a Makefile, the backend pytest suite, and .github/workflows/ci.yml.
  5. Docs. A root README.md and docs/KNOWN_ISSUES.md.

Auth work

Sign-in isn't part of the baseline; it's a teammate's assignment in Weeks 2-4 of the 10-week plan. An implementation was built here and then removed with git revert:

  • Auth.js with Microsoft Entra ID and Google, limited to @utdallas.edu.
  • 5-minute HS256 tokens minted by the Next server and verified by the API.
  • A users table and an AUTH_DISABLED dev escape hatch.

It's preserved on reference/auth-oauth (at 4a162a7), which also has its tests and docs/AUTH_SETUP.md.

Original commit Reverted by
4f9c7fd feat(web): UTD sign-in with Auth.js and authenticated API proxy 7216e18
23a0f07 feat(api): require signed bearer token on /api/chat, persist users 9a22e04

To restore it, revert the reverts (newest first):

git revert 9a22e04 7216e18

The API half reapplies cleanly. The web half stops with conflicts in frontend/next.config.ts, frontend/src/lib/api.ts and frontend/.env.example (checked with a dry run), because they changed after the reverts; resolve them, then run git revert --continue. The auth tests and docs/AUTH_SETUP.md were removed in separate commits; bring them back with:

git checkout reference/auth-oauth -- backend/tests docs/AUTH_SETUP.md

Merging reference/auth-oauth will NOT bring auth back. Its commits are already in this branch's history, so git considers them merged and the reverts win.

Before merging

  • CI passes on this PR.

Before the next Cloud Run deploy

  • Confirm which AskTemoc_Backend branch production runs. The deployed API's streaming format matches the speedrun branch (12 commits ahead of main: a [n, "verbatim"] citation prompt, lower temperature, and delta plus cumulative message events), not main, which is what this PR imports.
  • If it's speedrun, port its citation prompt and streaming changes in a follow-up PR first. Otherwise the next deploy silently drops the citation links in answers. The frontend already accepts both streaming formats.

What still has to be done by hand

  • Give the OpenAI account credits (and rotate the key), then re-seed with OPENAI_API_KEY set.
  • Archive davis118/AskTemoc_Backend, or point its README here, after this merges.

Deployment notes

Vercel (frontend): no new variables. ASKTEMOC_API_ORIGIN is committed in vercel.json and .env.production. Until app-level sign-in exists, keep Vercel Deployment Protection on.

Cloud Run (API)

Variable Value
ENVIRONMENT production
OPENAI_API_KEY key with billing enabled
CORS_ORIGINS https://<vercel domain>
DATABASE_URL database connection URL
CHROMA_PERSIST_DIRECTORY on persistent storage, then run python scripts/seed_catalog.py

The API has no authentication, so anyone with its URL can call it. See docs/KNOWN_ISSUES.md.

🤖 Generated with Claude Code

Conwenu and others added 30 commits September 23, 2025 21:13
Web Scraper - 
-Scrapes from https://catalog.utdallas.edu/2025/undergraduate/home
-Uses DOM pattern matching to get requirements & example degree plan for all majors
	new file:   CHANGES.md
	new file:   DATABASE_API_DOCS.md
	new file:   IMPLEMENTATION_STATUS.md
	new file:   IMPLEMENTATION_SUMMARY.md
	new file:   QUICK_START.md
	new file:   app/api/endpoints/dashboard.py
	new file:   app/api/endpoints/documents.py
	new file:   app/api/endpoints/pinecone.py
	new file:   app/db/__init__.py
	new file:   app/db/database.py
	new file:   app/db/models.py
	new file:   app/db/services.py
	modified:   app/main.py
	new file:   app/schemas/db_schemas.py
	new file:   app/services/document_management.py
	new file:   app/services/pinecone_service.py
	modified:   requirements.txt
- Added `app/services/data_ingestion_service.py` for ChromaDB ingestion of university program data
  - Processes 116 program files into 1483 chunks with proper metadata and embeddings
  - Implements ingestion logging and retrieval test verification
- Connected ChromaDB RAG pipeline to `asktemoc.db` with Ollama embeddings
- Enhanced streaming API endpoint (`rag_endpoint.py`) with real-time streaming and async flow
- Added `.env` file with Ollama, ChromaDB, and FastAPI configuration
- Updated `.gitignore` to exclude `chroma_db/` persistence directory
- Documented implementation details and changelog updates in `CHANGES.md`

Includes:
- Fixes for import paths (relative → absolute)
- Improved retrieval and streaming reliability
This script verifies ChromaDB contents and data ingestion status by counting
source files, checking document count, and testing retrieval functionality.
It helps ensure the RAG system has properly ingested data and is operational.
* Add `/api/health` endpoint for basic backend liveness checks
* Return simple JSON `{ "status": "healthy" }` with HTTP 200
* Register health router in main FastAPI app
* Enable CORS middleware for local frontend (`http://localhost:3000`)
* Temporarily allow credentials, all headers, and all HTTP methods (need to restrict for prod)
* Add ChromaDB persist directory and collection name to `.env.example`
* Update app `.env` to use consistent ChromaDB storage path
* Read ChromaDB config from environment variables in ingestion service
* Remove hardcoded ChromaDB paths from retriever service
* Standardize ChromaDB setup across ingestion and retrieval layers
* Remove all Pinecone configuration from `.env` and `.env.example`
* Delete Pinecone API endpoints, services, and schemas
* Rename Pinecone-related fields to ChromaDB equivalents across models and services
* Update embedding sync logic to reference `chroma_id`
* Adjust dashboard and document APIs to reflect ChromaDB usage
* Swap Pinecone router for Chroma router in main app
* Clean up obsolete ChromaDB verification script `check_chromadb.py` now that pipeline is stable
* Add detailed migration entry to `CHANGES.md` (Pinecone → ChromaDB)
* Revise Quick Start, workflow examples, and troubleshooting steps for ChromaDB
* Replace all Pinecone references with ChromaDB in implementation docs
    * Update service names (PineconeExportService → ChromaService)
    * Update API routes (`/api/pinecone` → `/api/chroma`)
    * Rename config variables to `CHROMA_PERSIST_DIRECTORY` and `CHROMA_COLLECTION_NAME`
    * Update database schema examples (`pinecone_id` → `chroma_id`)
…nt flow

* Add explicit system requirements (Python 3.13+, pip, Git, Ollama)
* Include repository cloning step
* Add virtual environment setup instructions
* Clarify dependency installation process
* Document Ollama installation and local model check
* Improve FastAPI server startup instructions
* Add link to Swagger UI (`/docs`)
* Reorganize sections for clearer step-by-step onboarding
added openai support and put into docker container
The chat UI had several defects found while running the app end to end.

- SSE parsing dropped any event whose `data:` line straddled a network chunk
  boundary, and decoded each chunk independently so multi-byte UTF-8 split at a
  boundary corrupted. A dropped `source` event permanently loses a citation link.
  Buffer the trailing partial line and use a streaming TextDecoder.
- `npm run dev` could not reach any backend: the dev default pointed at
  localhost:8000, which no process in this repo serves. Default to the same-origin
  /api/proxy rewrite instead, which `next dev` also serves.
- Assistant markdown rendered literally, so answers showed `**Communication**`.
  Add inline **bold**, *italic* and `code` rendering, composed with the existing
  citation pills. Underscore emphasis is unsupported on purpose so course codes
  and URLs stay literal.
- A 5s health check timeout reported a healthy backend as down, since Cloud Run
  cold starts were measured over 20s. Raise it to 35s, drop the redundant
  pre-flight check before each send, and stop disabling the composer on a failed
  probe so a cold start can't lock the UI.
- Use onKeyDown rather than the deprecated onKeyPress.
- Bump Next.js to 15.5.26, clearing the critical App Router proxy-bypass advisory.

The scraper turned footnote anchors into programs: `…/programs#footnote-1` has
link text "1", producing an output directory named "1" holding the whole index
page (visible as test_output/1, 2, 3). Skip footnote anchors, numeric link text
and the index page itself, while keeping URL fragments so concentrations of the
same degree stay distinct. Live discovery goes from 118 links to 115, keeping all
18 concentrations.

Both READMEs were inaccurate. frontend/README.md was untouched create-next-app
boilerplate; it now documents the proxy architecture, env vars, SSE format and
deployment. backend/README.md described test_scraper.py as a single-page test when
it scrapes every program, and omitted that `playwright install` must match the
venv's Playwright version. Both now note that the chat API lives in the separate
AskTemoc_Backend repository.

Co-authored-by: Cursor <cursoragent@cursor.com>
…btree

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sal-Fakoya and others added 22 commits October 5, 2026 15:46
git-subtree-dir: backend
git-subtree-mainline: 6edd28b
git-subtree-split: 806fef6
- scraper -> backend/scrapers/catalog (README updated for the new layout)
- drop backend/app/services/data (byte-identical copy of data/catalog);
  housing scrape moves to data/housing
- untrack backend/app/.env; root .gitignore replaces backend/.gitignore,
  whose t*.py and /test_* patterns hid test files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
requirements.txt is now the pinned runtime set the Dockerfile installs.
Playwright/Crawl4AI/docling/torch move to requirements-scrape.txt; pytest
and httpx to requirements-dev.txt. Add .dockerignore so .env, chroma_db
and SQLite files never enter the image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build_sources.py reuses the scraper's link discovery and folder naming, so
seeded chunks can cite the real catalog page. 113/116 folders mapped; the
three numeric folders are footnote artefacts with no page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ices

- app/core/config.py: typed Settings read from env and backend/.env; all
  os.getenv calls go through it; relative paths resolve against backend/
- retriever and chain are built on first use and cached, so importing the
  app never touches Chroma, OpenAI or Ollama
- one embeddings factory for retrieval and ingestion; Ollama clients now
  honour OLLAMA_BASE_URL (langchain-ollama instead of deprecated community
  classes); warn when the collection was seeded with another model
- CORS origins from CORS_ORIGINS

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api/chat uses chain.astream: sources as soon as retrieval finishes, then
  token deltas instead of O(n^2) cumulative prefixes; final {type: done}
- GET /api/health answers without the trailing-slash redirect
- frontend appends text deltas and ignores unknown event types

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seeds data/catalog into the collection the retriever reads, with the same
splitter and embeddings, stable chunk ids, and catalog URLs as sources.
Rebuilds the collection when the embedding provider/model changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…failures

A local Ollama runner crashed on 256-chunk batches; 64 completes the full
catalog. Embedding errors (e.g. exhausted OpenAI quota) now exit with a
hint instead of a traceback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- get_current_user verifies HS256 tokens minted by the web app (required
  aud/iss/exp/iat/sub, small leeway); 401 on missing/invalid/expired
  tokens, 403 for emails outside ALLOWED_EMAIL_DOMAIN
- users table (provider_sub, email unique) upserted on each request;
  portable SQLAlchemy types, so Postgres works by changing DATABASE_URL
- AUTH_DISABLED dev escape hatch; config refuses to load it outside
  ENVIRONMENT=development, and requires BACKEND_JWT_SECRET (>=32 chars)
  outside development
- health stays public; admin routers unchanged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A startup refusal (e.g. AUTH_DISABLED outside development) printed the
settings input, including the start of OPENAI_API_KEY, to the log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Auth.js v5 (JWT sessions) with Microsoft Entra ID and/or Google, each
  enabled by its env vars; signIn allows only verified @ALLOWED_EMAIL_DOMAIN
  emails (ALLOWED_EMAILS extra addresses under next dev only); Entra must
  use a single-tenant issuer and the token's tid must match it
- middleware sends signed-out visitors of /chat to /signin; sign-in on the
  landing page, name + sign-out in the navbar
- /api/chat and /api/health route handlers check the session, mint a
  5-minute HS256 token (jose) and stream the backend response through
- api.ts always calls same-origin routes and redirects to sign-in on 401;
  NEXT_PUBLIC_API_BASE_URL and the /api/proxy rewrites are removed since
  they bypassed auth

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 offline tests: health with/without slash, duplicate-route guard, lazy
services, 401 for missing/garbage/forged/expired/alg-none/wrong aud-iss
tokens, 403 for other domains, well-formed SSE deltas + done with a fake
chain, user upsert, AUTH_DISABLED guard, secret hiding, seed metadata and
stable chunk ids. Settings are pinned so a local backend/.env never leaks in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Ollama

Chroma and SQLite live in a named volume, data/ is mounted read-only for
seeding, and host Ollama is reachable via host.docker.internal; an ollama
service is available under the 'ollama' profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/dev.sh and scripts/dev.ps1 share one task list; the Makefile
delegates to dev.sh. Shell scripts are pinned to LF endings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deployed API (AskTemoc_Backend 'speedrun' branch) sends text events
with a delta plus a cumulative message. Prefer delta when present so the
web app renders correctly whichever API it is paired with during rollout.
Also pin the Makefile to LF endings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: what Ask Temoc is, architecture, quickstart matching the
  definition of done, Docker and AUTH_DISABLED usage, SSE contract,
  deployment env vars for Vercel and Cloud Run
- docs/AUTH_SETUP.md: Entra app registration (UTD tenant issuer, OIT
  consent), Google OAuth client, secret generation, troubleshooting
- docs/KNOWN_ISSUES.md: everything deliberately left for the 10-week plan
- backend/frontend READMEs rewritten for the monorepo layout

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 4f9c7fd. Sign-in moves out of the baseline: auth is a
teammate's assignment in the 10-week plan (Weeks 2-4). The work is kept on
reference/auth-oauth.

Restores the /api/proxy rewrites, NEXT_PUBLIC_API_BASE_URL and the
placeholder 401/403 branch in api.ts. The delta-event support added later
(34e2019) is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…users"

This reverts commit 23a0f07. Removes get_current_user, PyJWT, the users
table, AUTH_DISABLED and the auth settings; /api/chat is unauthenticated
again, as before the baseline. The work is kept on reference/auth-oauth.

The validation-error secret hiding from f6c4036 is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With sign-in removed, the /api/proxy rewrites are back, but their default
origin was Cloud Run, so 'npm run dev' sent local chat to production.
Default to http://localhost:8000; production builds still take the Cloud
Run origin from .env.production and vercel.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat tests call /api/chat without a token; user/upsert and AUTH_DISABLED
tests go with the feature (kept on reference/auth-oauth). The secret-hiding
test now triggers a validation error on OPENAI_API_KEY itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README quickstart no longer needs secrets or an OAuth app: pick a model
  provider, seed, run; chat API and deployment notes without auth
- KNOWN_ISSUES: no sign-in (planned for Weeks 2-4; earlier implementation
  on reference/auth-oauth), UTD email is Microsoft 365 so utdallas.edu-
  restricted Google OAuth won't work for most students, open admin routes,
  placeholder 401/403 handling
- remove docs/AUTH_SETUP.md (kept on reference/auth-oauth)
- frontend/backend READMEs match the proxy setup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Sal-Fakoya Sal-Fakoya changed the title chore: runnable baseline (monorepo, UTD sign-in, seedable RAG, tests, CI) chore: runnable baseline (monorepo, seedable RAG, streaming fixes, tests, CI) Oct 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants