An agentic, AI-powered data analysis tool: upload a CSV or Excel file, ask questions about it in plain English, and get back written insights plus auto-generated charts — no manual chart configuration, no query language.
Built with FastAPI (Python) on the backend and React + TypeScript + Material UI on the frontend, using Google Gemini (Vertex AI) for analysis and chart generation.
Note on history: this project was originally prototyped with a Go backend. That version was retired in favor of the Python/FastAPI backend documented below, which is the one actively developed and deployed.
- Upload a CSV or Excel file (up to 10 MB). The backend parses it, infers column types, and computes per-column statistics (min/max/mean/median/std-dev for numeric columns, unique/null counts for string columns).
- Get suggestions — Gemini reviews the dataset's schema and statistics and proposes several relevant analysis questions to start from.
- Ask a question in natural language ("compare average revenue across regions", "show me the distribution of order sizes"). The backend:
- if you've uploaded supporting documents, retrieves the most relevant excerpts (pgvector similarity search over Gemini embeddings) and folds them into the prompt as background context,
- asks Gemini for a written analysis of the data — informed by that retrieved context when there is any — in the context of your question,
- asks Gemini to propose 1–3 chart configurations that best visualize the answer (charts only ever look at the dataset itself, never the documents),
- validates every chart config against the actual dataset (rejecting hallucinated column names or type mismatches), retrying up to 3 times if needed,
- computes the real chart data server-side (sampling, binning, correlation matrices, box-plot quartiles, etc.) and returns it alongside the written insight and a list of the documents actually cited.
- Ground answers in your own documents — upload PDFs, text, or Markdown files as background context (a specific dataset's data dictionary, or general company knowledge). Each is chunked and embedded automatically; when you ask a question, Orion retrieves the most relevant excerpts and cites which document(s) informed its answer.
- Keep asking — follow-up questions get contextual suggestions based on the conversation so far, and everything is organized into chat threads (multiple files/conversations, switchable from the sidebar). Threads and messages are persisted server-side in Postgres, tied to your authenticated account — not to a single browser.
- 📊 Upload Excel/CSV files (max 10 MB)
- 🤖 AI-powered data analysis suggestions
- 💬 Natural language Q&A about your data
- 📈 Interactive charts (bar, line, scatter, pie, area, combo, histogram, boxplot, bubble, correlation)
- 📄 Retrieval-augmented answers — upload supporting PDF/TXT/Markdown documents; relevant excerpts are automatically retrieved (pgvector similarity search) and cited in the written analysis, with a "Sources" chip showing exactly which document(s) were used
- 📌 Pinnable dashboard canvas — pin any generated chart out of the chat onto a persistent, drag-and-resize grid (built on
react-grid-layout), so you can assemble your own dashboard instead of scrolling back through the conversation - 🌓 Light/dark theme
- ☁️ Server-side persistence — chat threads, messages, and documents are stored in Postgres/S3 against your account, so the same conversations and documents show up on any device/browser you log into; pinned dashboards currently remain per-browser (
localStorage)
┌─────────────────────────┐ HTTP/JSON ┌──────────────────────────┐
│ React + MUI Frontend │ ───────────────────────▶ │ FastAPI Backend │
│ (Vite, port 5173) │ ◀─────────────────────── │ (Uvicorn, port 3001) │
└─────────────────────────┘ └───┬──────────┬──────────┬┘
│ │ │
async httpx │ asyncpg │ aioboto3│
+ OAuth2 │ │ │
▼ ▼ ▼
┌──────────────────┐ ┌───────────┐ ┌───────────────┐
│ Google Vertex AI │ │ Postgres │ │ S3 / MinIO │
│ (Gemini 2.5 │ │ (+pgvector)│ │ (raw files) │
│ Flash) │ │ dataset │ │ │
│ │ │ metadata │ │ │
└──────────────────┘ └───────────┘ └───────────────┘
Backend — layered FastAPI app: handlers/ (HTTP routes) → services/ (Gemini integration, chart generation/orchestration, S3-backed file storage) → models/ (Pydantic schemas, dataset parsing, and SQLAlchemy DB models) → middleware/ + utils/ (logging, request IDs, structured errors). Uploaded files are stored in S3 (MinIO locally); dataset metadata lives in Postgres — nothing is held in server memory between requests, so any backend instance can serve any request. All Gemini calls and DB/S3 access are fully async so concurrent requests don't block one another. See backend-python/README.md for the full module-by-module breakdown and API reference.
Frontend — React 18 + TypeScript, Material UI v5 for the component/design system, Recharts for chart rendering, react-grid-layout for the pinnable dashboard canvas, react-router-dom for routing, axios for API calls. State lives in a single reducer-based context (AppContext/reducer.ts); the thread list and each thread's messages are fetched from the backend on demand rather than cached client-side. Pinned dashboards are the one piece of state still kept in localStorage, keyed per thread.
| Layer | Technology |
|---|---|
| Frontend framework | React 18 + TypeScript + Vite |
| UI / design system | Material UI (MUI) v5, @emotion |
| Charts | Recharts |
| Dashboard layout | react-grid-layout (drag/resize grid) |
| Backend framework | FastAPI (Python 3.11) + Uvicorn |
| Data processing | pandas, numpy |
| Database | Postgres (+ pgvector) via SQLAlchemy 2.0 (async, asyncpg) + Alembic |
| Object storage | S3-compatible (MinIO locally, AWS S3 in the cloud) via aioboto3 |
| AI / LLM | Google Vertex AI — Gemini (async httpx + google-auth) |
| Auth | AWS Cognito — JWT verification via PyJWT against cached JWKS |
| Validation | Pydantic v2 |
Agenetic Data Analyzer/
├── backend-python/ # FastAPI backend — see its own README for full details
│ ├── handlers/ # POST /upload, /suggestions, /contextual-suggestions, /analyze, threads.py, documents.py
│ ├── services/ # gemini.py, analysis_engine.py, storage_service.py (S3/MinIO),
│ │ # dataset_service.py, thread_service.py, auth_service.py,
│ │ # document_service.py, chunking_service.py (RAG ingestion + embedding)
│ ├── models/ # Pydantic schemas + dataset parsing (models/), SQLAlchemy models (models/db/)
│ ├── alembic/ # DB migrations
│ ├── middleware/ # request ID + request logging
│ ├── utils/ # error hierarchy, response helpers, input validation
│ ├── database.py # async engine/session
│ └── main.py # app entrypoint
│
└── frontend/ # React + MUI frontend
└── src/
├── components/ # AppSidebar, AppShell, ChatScreen, FileUpload, PromptInput,
│ # ChatHistory, ChartRenderer, DashboardCanvas, Suggestions,
│ # SupportingDocuments (RAG document upload + status), ...
├── pages/ # Landing, Home, Dashboard
├── context/ # AppContext + reducer (app state), ColorModeContext (theme)
├── api/ # backend.ts — typed axios client
├── utils/ # errorMessage.ts — shared error normalization
└── theme.ts # MUI theme (light/dark)
- Python 3.11 or 3.12
- Node.js 18+ and npm
- Docker (for local Postgres + MinIO)
- A Google Cloud project with the Vertex AI API enabled
Uploaded files and dataset metadata are persisted in S3 and Postgres respectively — bring these up before the backend, since /upload needs both.
docker compose up -dThis starts:
- Postgres (
pgvector/pgvector:pg16) onlocalhost:5433(not the Postgres default 5432 — deliberately avoids clashing with a native Postgres install you may already have running) — connect withpsql, TablePlus, DBeaver, or any Postgres GUI client using the credentials indocker-compose.yml/backend-python/.env.example(DATABASE_URL). - MinIO (S3-compatible object storage) — API on
localhost:9000, web console onlocalhost:9001(log in with theMINIO_ROOT_USER/MINIO_ROOT_PASSWORDvalues, defaults indocker-compose.yml).
Create the local bucket (orion-datasets-local) — either once by hand via the MinIO console, or automatically on every startup:
cp docker-compose.override.yml.example docker-compose.override.yml
docker compose up -dStop everything with docker compose down (add -v to also wipe the persisted volumes).
With Postgres running (previous step), apply the schema:
cd backend-python
python -m venv venv
venv\Scripts\activate # Windows
pip install -r requirements.txt
alembic upgrade head # applies all migrationsOther common commands, run from backend-python/:
alembic revision --autogenerate -m "describe your change" # generate a new migration after editing models/db/*.py
alembic upgrade head # apply all pending migrations
alembic downgrade -1 # revert the most recent migration
alembic current # show the currently-applied revisionThe schema lives in models/db/ (User, Dataset, ChatThread, Message) — Base.metadata from there is what alembic revision --autogenerate diffs against.
cd backend-python
venv\Scripts\activate # Windows — skip if already active from step 2
# source venv/bin/activate # macOS/Linux
copy .env.example .env # Windows
# cp .env.example .env # macOS/Linux
# then edit .env — set GOOGLE_CLOUD_PROJECT_ID (Postgres/S3/auth vars already default to local dev)
gcloud auth application-default login
python main.py # listens on http://localhost:3001Every endpoint except /health requires a Bearer token. .env.example defaults to AUTH_MODE=local, which accepts a single hardcoded dev token instead of real Cognito — see Authentication below before deploying this anywhere shared.
cd frontend
npm install
copy .env.example .env.local # Windows — points VITE_API_URL at the backend
# cp .env.example .env.local # macOS/Linux
npm run dev # listens on http://localhost:5173Open http://localhost:5173 in your browser.
Full backend details (Docker, data flow, module-by-module breakdown) are in backend-python/README.md.
Every endpoint except GET /health requires Authorization: Bearer <token>. Tokens are validated two different ways depending on AUTH_MODE:
AUTH_MODE=cognito (the real path) — the token must be a valid AWS Cognito access token (not an ID token) for the configured User Pool and App Client: signature verified against Cognito's public JWKS (fetched once, cached for an hour), plus token_use, client_id, and iss all checked explicitly. On first use, the token's sub claim is looked up in the users table and a row is created automatically if it doesn't exist yet — no separate signup step.
Setup:
- In the AWS Console, create a Cognito User Pool and an App Client (public client, no secret — this is a browser app).
- Set
COGNITO_USER_POOL_ID,COGNITO_APP_CLIENT_ID,COGNITO_REGIONinbackend-python/.env. - Set
AUTH_MODE=cognito.
AUTH_MODE=local (the default in .env.example) — for local development without a Cognito pool. Accepts one hardcoded token (local-dev-token) and maps every request to a single fixed test user, instead of verifying anything real.
Never set
AUTH_MODE=localoutside your own machine. There is no real authentication in this mode — the token is a public, checked-into-this-repo string. Do not set it in any deployed or shared environment.
The frontend sends this same hardcoded token on every request (VITE_DEV_AUTH_TOKEN in frontend/.env.local) as a stand-in until a real login flow exists — it only works against a backend running with AUTH_MODE=local.
backend-python/.env
| Variable | Required | Default | Description |
|---|---|---|---|
GOOGLE_CLOUD_PROJECT_ID |
Yes | — | Your GCP project ID |
GOOGLE_CLOUD_LOCATION |
No | us-central1 |
Vertex AI region |
GEMINI_MODEL |
No | gemini-2.5-flash |
Vertex AI model ID |
GOOGLE_APPLICATION_CREDENTIALS |
No | — | Path to a service-account key; not needed with gcloud auth application-default login |
CORS_ALLOWED_ORIGINS |
No | http://localhost:5173 |
Comma-separated allowed origins |
PORT |
No | 3001 |
Server port |
DATABASE_URL |
Yes | — | Postgres connection string (see Local infrastructure) |
S3_ENDPOINT_URL |
No | — | http://localhost:9000 locally (MinIO); unset for real AWS |
S3_BUCKET_NAME |
Yes | — | orion-datasets-local locally |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
No | — | MinIO root user/password locally; omit in the cloud to use an IAM role instead |
AUTH_MODE |
No | cognito (code default) |
cognito or local — see Authentication |
COGNITO_USER_POOL_ID / COGNITO_APP_CLIENT_ID / COGNITO_REGION |
Required when AUTH_MODE=cognito |
— | From your Cognito User Pool + App Client |
frontend/.env.local
| Variable | Required | Default | Description |
|---|---|---|---|
VITE_API_URL |
No | http://localhost:3001 |
Backend base URL |
VITE_DEV_AUTH_TOKEN |
No | — | Sent as the Bearer token on every request; only works against a backend running AUTH_MODE=local |
| Method | Path | Description |
|---|---|---|
POST |
/upload |
Upload a CSV/Excel file, get back fileId + column schema + statistics |
POST |
/suggestions |
Get initial AI-generated analysis suggestions for a dataset |
POST |
/contextual-suggestions |
Get follow-up question suggestions based on chat history |
POST |
/analyze |
Ask a question (optionally within an existing threadId); get back written insights + validated chart configs with data. If the dataset has supporting documents, relevant excerpts are retrieved (pgvector similarity search) and woven into the written analysis, with a sources field naming what was used |
GET |
/threads |
List the authenticated user's chat threads, most recently updated first |
GET |
/threads/{id}/messages |
Get a thread's dataset schema + full message history |
POST |
/threads |
Create a new (empty) thread for a dataset |
PATCH |
/threads/{id} |
Rename a thread |
DELETE |
/threads/{id} |
Delete a thread and its messages |
POST |
/documents |
Upload a PDF/TXT/Markdown file for RAG ingestion (optionally scoped to a datasetId); chunking + embedding (Vertex AI text-embedding-004) runs as a background task |
GET |
/documents |
List the user's uploaded documents with status and chunk count |
GET |
/documents/{id}/status |
Poll a document's ingestion status (pending → ready/failed) |
DELETE |
/documents/{id} |
Delete a document — removes its S3 object and cascades to delete its chunks |
GET |
/health |
Health check |
Interactive Swagger docs are available at http://localhost:3001/docs while the backend is running.
MIT