Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
eec7c97
phase-1: domain models + SQLite schema — gate PASSED (11/11 tests)
Jun 15, 2026
3535bfc
phase-2: stubbed agent loop + UI + README — gate PASSED (16/16 tests)
Jun 15, 2026
467c678
switch LLM provider from Gemini SDK to OpenRouter (openai-compatible)
Jun 16, 2026
fc6319c
track token usage, API request count, and estimated cost per query
Jun 16, 2026
0dc32aa
redesign UI: Inter font, indigo palette, cleaner layout throughout
Jun 16, 2026
277f2cf
phase-3: replace single-shot analysis with iterative SQL ReAct loop
Jun 16, 2026
a598eef
spec: encode ReAct loop pattern as non-negotiable rule in base harness
Jun 17, 2026
8eb7c6b
spec: redesign around modular tool registry + data sources as primary…
Jun 17, 2026
85184b2
phase-3: tool registry + data sources as primary entity (21/21 tests …
Jun 17, 2026
bc91c61
phase-3: sessions ↔ data sources many-to-many + unified home page (22…
Jun 17, 2026
6e88bcf
fix agent loop, add async query execution and structured logging
Jun 18, 2026
efda8fb
deploy: Docker + GitHub Actions CI/CD + Render config; fix ReAct loop…
Jun 20, 2026
ecabb38
ci: read DATAANALYSIS_OPENROUTER_API_KEY from GH Actions secret
Jun 20, 2026
721184b
spec: add Tool Registry and Capability Dispatch to engineering harnes…
Jun 21, 2026
e4454c9
feat: add Database class with SQLite/PostgreSQL backend switching
Jun 22, 2026
809b1e0
chore: add psycopg2-binary as core dependency for PostgreSQL support
Jun 22, 2026
8232939
feat: convert uploads to Parquet, store schema metadata in master DB
Jun 22, 2026
365b062
refactor: stream uploads directly to Parquet, no raw file written to …
Jun 22, 2026
9cecb4f
feat: LLM-generated tool and capability descriptions on upload
Jun 22, 2026
8d30bb9
feat: add Sync button to re-generate tool descriptions via LLM
Jun 22, 2026
be37336
refactor: modularise codebase — one responsibility per file, methods …
Jun 22, 2026
032ee00
phase-0: formalize tool layer as MCP in the spec; add mcp + duckdb deps
Jun 23, 2026
61ae403
phase-1: drop Tool/ToolCapability tables; move descriptions onto data…
Jun 23, 2026
9392d4e
phase-2: add per-source DuckDB MCP server + per-run client pool (isol…
Jun 23, 2026
5507c67
phase-3: cut the agent graph over to async MCP + DuckDB; delete SQLit…
Jun 23, 2026
ac7b0bf
spec: reframe session as long-lived agent context (per-session MCP po…
Jun 25, 2026
fe310dd
phase-B: relocate MCP code under tools/ (no behavior change)
Jun 25, 2026
9a917e7
phase-C: session-scoped MCP pool + 3-step ReAct loop (no load_data)
Jun 25, 2026
535c041
phase-D: durable per-session agent memory via LangGraph SqliteSaver
Jun 25, 2026
3fe4bc1
phase-0: spec rewrite — tool = named multi-table dataset (URI), two-l…
Jun 25, 2026
5ca9c97
phase-1: dataset data model — DatasetTableRow child + uri/sync/error …
Jun 25, 2026
2456a49
phase-2: dataset connector seam (uri, base, parquet) + build_dataset_…
Jun 25, 2026
ee91420
phase-3: pool + addressing cutover to datasets (two-level tool/capabi…
Jun 25, 2026
27ba151
phase-4: datasets API — named upload, add-csv, sync-all, delete-dir, …
Jun 25, 2026
834a452
phase-5: UI for datasets — named upload + type chooser, dataset cards…
Jun 25, 2026
a2deb10
phase-6: PostgresConnector (external datasets, BETA, flag-gated)
Jun 25, 2026
bcd91c0
docs/ops: README + env for the dataset model; datasets_dir on the per…
Jun 25, 2026
e478a55
phase-0: rewrite spec for the MCP-server model (tools/resources/promp…
Jun 25, 2026
52c057e
phase-A: MCP-server entity + sync pipeline + JSON-RPC dispatch + sing…
Jun 25, 2026
e8afafc
docs: README for the MCP-server model; fix stale GEMINI->OPENROUTER e…
Jun 25, 2026
1c91d4f
phase-B-spec: mutation methods (additive cascade) + hybrid agent addr…
Jun 25, 2026
fc5aa06
phase-B: granular MCP mutation methods (additive cascade) + hybrid ag…
Jun 25, 2026
f8d3d0a
phase-B-review: block DuckDB file-read functions; patch-update + prot…
Jun 25, 2026
5105efe
phase-B-ui: SPA shell + multi-connector + Load-more pagination; fix 4…
Jun 27, 2026
16a354a
phase-B-ui: cursor pagination + AJAX-loaded lists with Previous/Next;…
Jun 27, 2026
161c62c
phase-B-ui: chat thread = infinite scroll (cursor-based), not Previou…
Jun 27, 2026
2c28019
fix: add pytz dependency so DuckDB can materialize timestamptz values
Jun 28, 2026
9ae1044
refactor: consolidate mcp_tools/prompts/resources JSON columns
Jun 28, 2026
3ff9e91
phase-B-ui: remove top Analyse/Database tabs — Inspect opens the DB view
Jun 28, 2026
675d7f0
scaffold: data-analysis-agent spec + .env.example + gitignore
madhyamakist Jun 28, 2026
6c7ac23
phase-1: CSV upload → LangGraph analysis → text answer + Plotly chart
madhyamakist Jun 28, 2026
28fbde0
phase-B-ui: sessions sidebar + MCP capabilities become overlay sliders
Jun 28, 2026
a378182
fix: dialect-aware SQL for McpResourceRow.kind hybrid expression
Jun 28, 2026
7b6b9eb
merge: harness v0.2.x updates from origin — zero-shot-build/fix/sync,…
Jun 28, 2026
bf6823c
phase-B-ui: show loading overlay in modals on Save
Jun 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Version control
.git/
.gitignore

# Python bytecode and caches
__pycache__/
*.pyc
*.pyo
.pytest_cache/
.mypy_cache/
.ruff_cache/

# Virtual environments
.venv/
venv/

# Local secrets and config
.env
.env.*
!.env.example

# Runtime data (never bake into image — mount a volume)
*.db
uploads/
logs/
htmlcov/
.coverage

# Tests (not needed in production image)
tests/

# Dev tooling and docs
*.md
reports/
spec/
.claude/
.github/

# macOS
.DS_Store
31 changes: 21 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
AGENT_DATABASE_URL=sqlite:///./data/agent.db
# Data Analysis Agent — Environment Variables
# Copy to .env and fill in your values

# LLM provider — auto-detected from whichever key is set below.
# Override explicitly: anthropic | gemini
# AGENT_LLM_PROVIDER=anthropic
# ── Database ────────────────────────────────────────────────────────────────
# SQLite database file path
DATAANALYSIS_DATABASE_URL=sqlite:///data_analysis.db

# Override model (uses provider default when blank):
# AGENT_LLM_MODEL=claude-sonnet-4-6 (Anthropic default)
# AGENT_LLM_MODEL=gemini-2.5-pro (Gemini default)
# Durable per-session agent memory (LangGraph SqliteSaver) — a SEPARATE SQLite file
DATAANALYSIS_CHECKPOINT_DB=checkpoints.db

# Set exactly ONE provider key:
AGENT_ANTHROPIC_API_KEY=
AGENT_GEMINI_API_KEY=
# Internal parquet datasets directory (one subdirectory per dataset)
DATAANALYSIS_DATASETS_DIR=uploads/datasets
# Enable external database datasets (PostgreSQL, BETA). Default off.
DATAANALYSIS_ENABLE_EXTERNAL_DATASETS=false

# ── LLM ─────────────────────────────────────────────────────────────────────
# OpenRouter API key — leave blank or unset for stub mode
# Get yours at https://openrouter.ai/keys
DATAANALYSIS_OPENROUTER_API_KEY=

# LLM model name (any model available on OpenRouter)
DATAANALYSIS_LLM_MODEL=google/gemini-2.5-flash

# ── App ──────────────────────────────────────────────────────────────────────
PORT=8001
DATAANALYSIS_LOG_LEVEL=INFO
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

on:
push:
branches: ["**"]
pull_request:

jobs:
test:
name: Test (Python 3.12)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "0.5.x"
enable-cache: true

- name: Install dependencies (including dev)
run: uv sync --dev

- name: Run tests
env:
DATAANALYSIS_OPENROUTER_API_KEY: ${{ secrets.DATAANALYSIS_OPENROUTER_API_KEY }}
DATAANALYSIS_DATABASE_URL: "sqlite:///ci_test.db"
run: uv run pytest tests/ -v --tb=short

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
.pytest_cache/
retention-days: 7
100 changes: 100 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Deploy

# Trigger on push to the app branch (or main after you merge the PR).
# Change the branch name below when you promote to main.
on:
push:
branches:
- feature/data-analysis-agent-v0.1
- main
workflow_dispatch: # allows manual trigger from GitHub UI

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
# ── 1. Run full test suite ────────────────────────────────────────────────
test:
name: Test gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "0.5.x"
enable-cache: true

- name: Install dependencies
run: uv sync --dev

- name: Run tests
env:
DATAANALYSIS_OPENROUTER_API_KEY: ${{ secrets.DATAANALYSIS_OPENROUTER_API_KEY }}
DATAANALYSIS_DATABASE_URL: "sqlite:///ci_test.db"
run: uv run pytest tests/ -v --tb=short

# ── 2. Build Docker image and push to GHCR ───────────────────────────────
build-and-push:
name: Build & push image
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # required to push to ghcr.io

outputs:
image-tag: ${{ steps.meta.outputs.tags }}
image-digest: ${{ steps.push.outputs.digest }}

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata (tags + labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# tag with full commit SHA for traceability
type=sha,format=long,prefix=
# always update :latest on this branch
type=raw,value=latest,enable=true

- name: Build and push image
id: push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# Cache layers across runs using GitHub Actions cache
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64

# ── 3. Trigger Render to pull latest image and redeploy ──────────────────
deploy-render:
name: Trigger Render deploy
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Deploy to Render
# RENDER_DEPLOY_HOOK_URL is stored as a GitHub Actions secret.
# Get it from: Render dashboard → your service → Settings → Deploy Hook
run: |
curl -sSf --max-time 30 -X POST "${{ secrets.RENDER_DEPLOY_HOOK_URL }}" \
-o /dev/null -w "HTTP %{http_code}\n"
15 changes: 12 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ dist/
build/
*.egg-info/
.DS_Store

# Logs
*.log
logs/

# Runtime data
*.db
*.db-shm
*.db-wal
data/
uploads/

# Test / tool caches
.pytest_cache/
.mypy_cache/
.ruff_cache/
htmlcov/
.coverage
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM python:3.12-slim

# Install uv for reproducible dependency installs
COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /usr/local/bin/uv

WORKDIR /app

# Disable uv's venv relinking — we copy files, not symlinks
ENV UV_LINK_MODE=copy
ENV UV_COMPILE_BYTECODE=1

# Layer 1: install third-party deps only (cache busts only on lock file change,
# not on application source changes). --no-install-project skips the local package.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project

# Layer 2: application source (changes most frequently — keeps layer 1 cached)
COPY alembic.ini alembic.ini
COPY alembic/ alembic/
COPY src/ src/

# Layer 3: install the local package itself (fast — deps are already installed)
RUN uv sync --frozen --no-dev

# Runtime defaults — override these with Render env vars or docker run -e
ENV DATAANALYSIS_DATABASE_URL=sqlite:////data/app.db
ENV DATAANALYSIS_CHECKPOINT_DB=/data/checkpoints.db
ENV DATAANALYSIS_DATASETS_DIR=/data/datasets
ENV DATAANALYSIS_UPLOAD_DIR=/data/uploads
ENV DATAANALYSIS_LOG_FILE=/data/logs/app.log
ENV DATAANALYSIS_LOG_LEVEL=INFO

EXPOSE 8000

COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]
Loading
Loading