FastAPI, Vue, Clerk, Postgres/pgvector, MinIO, and Pydantic AI working together as a small internal RAG workspace. Authenticated users upload shared documents, ask grounded questions, keep their own chat sessions, and review source citations returned with each answer.
- Clerk-backed sign in, sign up, and sign out.
- Protected FastAPI API with local
app_usersprojection for app-owned records. - Shared document pool for
.txt,.md, and.pdfuploads. - Any authenticated user can deliberately delete any shared document.
- Postgres-backed ingestion queue with a worker process that extracts text, chunks documents, creates embeddings, and writes retrieval-ready vectors.
- Postgres with pgvector stores documents, chunks, embeddings, chat sessions, messages, and citations.
- Pydantic AI rewrites conversational questions and generates source-grounded answers.
- Streaming chat returns
delta,final, anderrorSSE events. - Deterministic backend evals validate retrieval, citations, no-source behavior, deleted-document exclusion, and query rewrite wiring.
Vue + Clerk
|
| Bearer Clerk session token
v
FastAPI API
|-- /api/me syncs local app user
|-- /api/documents uploads, lists, tombstones shared documents
|-- /api/chat/sessions stores user-owned sessions and streams answers
|
| metadata, chat, vectors
v
Postgres + pgvector
MinIO stores original uploaded files.
The worker process claims Postgres-backed ingestion jobs with `SKIP LOCKED`, extracts text, chunks, embeds, and marks documents ready.
OpenAI-backed Pydantic AI services handle query rewrite and answer generation.
Create local configuration:
cp .env.example .envFill these values in .env:
VITE_CLERK_PUBLISHABLE_KEYCLERK_JWT_PUBLIC_KEYOPENAI_API_KEY
Start the full stack:
docker compose up --buildOpen:
- Frontend:
http://localhost:5173 - Backend health:
http://localhost:8000/health - MinIO console:
http://localhost:9001
Compose also starts Postgres, runs Alembic migrations, creates the MinIO bucket, and starts the ingestion worker.
Internal queue health and failed jobs can be inspected from the backend environment without exposing an HTTP endpoint:
cd backend
uv run python -m app.ingestion.worker --queue-health
uv run python -m app.ingestion.worker --failed-jobsBackend:
docker compose up -d postgres minio minio-init
cd backend
uv sync
uv run alembic upgrade head
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Frontend:
cd frontend
npm install
npm run devRun the main validation path:
make testUseful focused checks:
make backend-test
make evals
make frontend-check
make e2eThe backend tests and evals use TEST_DATABASE_URL and refuse to run destructively against the main DATABASE_URL.
Advanced contract checks:
make e2e-clerk
make e2e-schema-capturee2e-clerk validates the real Clerk/browser/backend auth contract when Clerk credentials are configured. e2e-schema-capture is opt-in for refreshing real document/chat API schema fixtures from a configured local stack.
Core auth and runtime:
VITE_API_BASE_URLBACKEND_CORS_ORIGINSDATABASE_URLTEST_DATABASE_URLPOSTGRES_DB,POSTGRES_USER,POSTGRES_PASSWORD
RAG/OpenAI:
OPENAI_API_KEYOPENAI_BASE_URLCHAT_MODELEMBEDDING_MODELRAG_CHUNK_TARGET_TOKENS,RAG_CHUNK_OVERLAP_TOKENSRAG_QUERY_REWRITE_HISTORY_MESSAGES,RAG_ANSWER_HISTORY_MESSAGESRAG_RETRIEVAL_TOP_K,RAG_RETRIEVAL_MIN_SIMILARITYRAG_RETRIEVAL_MODE:oneshotby default.toolis experimental and enables bounded tool-based retrieval.RAG_TOOL_RETRIEVAL_MAX_CALLS: maximum successful retrieval tool calls per answer, from1to10, default3.RAG_TOOL_RETRIEVAL_TOP_K: maximum chunks returned by each retrieval tool call, from1to20, default5.RAG_TOOL_RETRIEVAL_REQUEST_LIMIT: maximum model requests for tool-mode answer generation, from1to20, default5.RAG_TOOL_RETRIEVAL_TIMEOUT_SECONDS: retrieval tool timeout in seconds, greater than0and up to60, default10.0.
Storage and ingestion:
OBJECT_STORAGE_ENDPOINT,OBJECT_STORAGE_BUCKETOBJECT_STORAGE_ACCESS_KEY,OBJECT_STORAGE_SECRET_KEYOBJECT_STORAGE_REGION,OBJECT_STORAGE_SECURE,OBJECT_STORAGE_FORCE_PATH_STYLEMAX_UPLOAD_BYTES,MAX_EXTRACTED_CHARSINGESTION_WORKER_ID,INGESTION_MAX_ATTEMPTS,INGESTION_BASE_RETRY_SECONDSINGESTION_STALE_AFTER_SECONDS,INGESTION_STALE_RECOVERY_BATCH_SIZEINGESTION_WORKER_POLL_INITIAL_SECONDS,INGESTION_WORKER_POLL_MAX_SECONDSINGESTION_FAILED_JOBS_LIMIT
