Fork of keboola/agnes-the-ai-analyst (via manana2520 GitHub fork). Develop here, push to GitHub fork to open upstream PRs.
Find a file
ZdenekSrotyr 2ad8828f8c fix: stdin register_bq parsing, separate BQ SQL validation
- cli/commands/query.py: --stdin mode now reads register_bq from the
  JSON payload and merges it into the register_bq option list, matching
  the documented {"register_bq": {...}, "sql": "..."} contract.
- src/remote_query.py: add _validate_bq_sql() with a narrower blocklist
  (writes only); register_bq() now calls _validate_bq_sql() so legitimate
  BQ operations like INFORMATION_SCHEMA, CALL, IMPORT are not blocked.
  The final DuckDB execute() path still uses the full _validate_sql().
- tests/test_remote_query.py: add TestValidateBqSql covering allowed
  INFORMATION_SCHEMA queries and blocked write operations.
2026-04-11 19:31:39 +02:00
.github/workflows fix: address Devin review round 5 — empty secret file, CI .env 2026-04-10 14:55:31 +02:00
app feat: add POST /api/query/hybrid endpoint for two-phase BQ+DuckDB queries 2026-04-11 11:09:42 +02:00
cli fix: stdin register_bq parsing, separate BQ SQL validation 2026-04-11 19:31:39 +02:00
config feat: add CLAUDE.md template for analyst bootstrap 2026-04-10 19:41:07 +02:00
connectors fix: strip HTML from table and column descriptions in OpenMetadata enricher 2026-04-09 18:42:37 +02:00
dev_docs docs: update stale v1 docs to v2 Docker/FastAPI/DuckDB architecture 2026-04-09 18:44:25 +02:00
docs docs: add remote query implementation plan (5 tasks) 2026-04-11 11:02:04 +02:00
infra feat(infra): add GCS remote backend and instance.yaml generation to startup script 2026-04-09 16:39:07 +02:00
scripts feat: add standalone metric YAML → DuckDB migration script 2026-04-10 19:35:36 +02:00
services fix: use DATA_DIR env var instead of hardcoded /data paths 2026-04-09 16:39:44 +02:00
src fix: stdin register_bq parsing, separate BQ SQL validation 2026-04-11 19:31:39 +02:00
tests fix: stdin register_bq parsing, separate BQ SQL validation 2026-04-11 19:31:39 +02:00
.dockerignore refactor: consolidate deps into pyproject.toml, remove requirements.txt 2026-04-09 13:17:59 +02:00
.gitignore fix: anchor auth/ gitignore rule to repo root only 2026-04-09 16:29:53 +02:00
ARCHITECTURE.md Update docs for modular architecture (auth/, services/, scripts/) 2026-03-09 13:11:40 +01:00
Caddyfile feat: add Caddy HTTPS reverse proxy and production compose override 2026-04-09 16:39:23 +02:00
CHANGELOG.md feat: multi-instance deployment — all 14 must-have items from spec 2026-04-10 11:57:42 +02:00
CLAUDE.md docs: add hybrid query usage instructions to CLAUDE.md 2026-04-11 11:11:10 +02:00
docker-compose.ci.yml feat: multi-instance deployment — all 14 must-have items from spec 2026-04-10 11:57:42 +02:00
docker-compose.override.yml chore: Docker prod config (Python 3.13, no reload), fix utcnow deprecation, update docs 2026-04-08 12:10:47 +02:00
docker-compose.prod.yml fix: address PR review findings — config write, CalVer, error handling 2026-04-10 13:16:40 +02:00
docker-compose.test.yml feat: add SEED_ADMIN_EMAIL for Docker test environments 2026-03-31 09:48:12 +02:00
docker-compose.yml feat: multi-instance deployment — all 14 must-have items from spec 2026-04-10 11:57:42 +02:00
Dockerfile feat: multi-instance deployment — all 14 must-have items from spec 2026-04-10 11:57:42 +02:00
LICENSE OSS cleanup: remove internal references, harden deployment, add config env interpolation 2026-03-09 07:59:57 +01:00
Makefile feat: multi-instance deployment — all 14 must-have items from spec 2026-04-10 11:57:42 +02:00
pyproject.toml fix: upgrade urllib3 1.26→2.6.3 — resolves all 4 Dependabot security alerts 2026-04-09 14:53:30 +02:00
pytest.ini feat: add pytest timeout and strict markers 2026-04-09 07:03:44 +02:00
README.md refactor: consolidate deps into pyproject.toml, remove requirements.txt 2026-04-09 13:17:59 +02:00
uv.lock fix: upgrade urllib3 1.26→2.6.3 — resolves all 4 Dependabot security alerts 2026-04-09 14:53:30 +02:00

Agnes — AI Data Analyst

Agnes is an open-source data distribution platform for AI analytical systems. It extracts data from configured sources into DuckDB, serves it via a FastAPI backend, and distributes Parquet files to analysts who query them locally using Claude Code and DuckDB.

Each data source produces a self-describing extract.duckdb file. The SyncOrchestrator attaches all extract databases into a master analytics.duckdb, making every table available through a unified view layer without copying data unnecessarily.

Architecture: extract.duckdb Contract

Every connector produces the same output structure:

/data/extracts/{source_name}/
├── extract.duckdb          ← _meta table + views
└── data/                   ← parquet files (local sources only)

The orchestrator scans /data/extracts/*/extract.duckdb, attaches each into analytics.duckdb, and creates master views.

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│   Keboola    │  │   BigQuery   │  │   Jira       │
│  extractor   │  │  extractor   │  │  webhooks    │
│ (DuckDB ext) │  │ (remote BQ)  │  │ (incremental)│
└──────┬───────┘  └──────┬───────┘  └──────┬───────┘
       │                 │                 │
       ▼                 ▼                 ▼
   extract.duckdb    extract.duckdb    extract.duckdb
   + data/*.parquet  (views → BQ)      + data/*.parquet
       │                 │                 │
       └─────────────────┼─────────────────┘
                         ▼
              SyncOrchestrator.rebuild()
              ATTACH → master views in analytics.duckdb
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
          FastAPI      CLI
          (serve)    (da sync)

Supported Data Sources

Source Mode Description
Keboola Batch pull DuckDB Keboola extension downloads tables to Parquet on a schedule
BigQuery Remote attach DuckDB BQ extension; queries execute in BigQuery, no local download
Jira Real-time push Webhook receiver updates Parquet files incrementally

Adding a new source means creating connectors/<name>/extractor.py that produces extract.duckdb with a _meta table (table_name, description, rows, size_bytes, extracted_at, query_mode). The orchestrator attaches it automatically.

Quick Start with Docker

# Clone the repository
git clone https://github.com/keboola/agnes-the-ai-analyst.git
cd agnes-the-ai-analyst

# Copy and edit configuration
cp config/instance.yaml.example config/instance.yaml
cp config/.env.template .env
# Edit both files for your environment

# Start the app and scheduler
docker compose up

# Start with all optional services (Telegram bot, etc.)
docker compose --profile full up

Once running, the FastAPI app is available at http://localhost:8000. Trigger a manual sync:

curl -X POST http://localhost:8000/api/sync/trigger

Development Setup

# Create and activate virtual environment
python3 -m venv .venv && source .venv/bin/activate

# Install dependencies
uv pip install ".[dev]"

# Run FastAPI locally with hot reload
uvicorn app.main:app --reload

# Run the test suite
pytest tests/ -v

Project Structure

├── src/                    # Core engine
│   ├── db.py               # DuckDB schema (system.duckdb, analytics.duckdb)
│   ├── orchestrator.py     # SyncOrchestrator — ATTACHes extract.duckdb files
│   ├── repositories/       # DuckDB-backed CRUD (sync_state, table_registry, users, etc.)
│   ├── profiler.py         # Data profiling
│   └── catalog_export.py   # OpenMetadata catalog export
├── app/                    # FastAPI application
│   ├── main.py             # App setup, router registration
│   ├── api/                # REST API (sync, data, catalog, admin, auth)
│   ├── auth/               # Auth providers (Google OAuth, email magic link, desktop JWT)
│   └── web/                # HTML dashboard routes
├── connectors/             # Data source connectors (extract.duckdb contract)
│   ├── keboola/            # Keboola: extractor.py (DuckDB extension) + client.py (fallback)
│   ├── bigquery/           # BigQuery: extractor.py (remote-only via DuckDB BQ extension)
│   └── jira/               # Jira: webhook + incremental parquet → extract.duckdb
├── cli/                    # CLI tool (`da sync`, `da query`, `da admin`)
├── services/               # Standalone services (scheduler, telegram_bot, ws_gateway, etc.)
├── scripts/                # Utility + migration scripts
├── config/                 # Configuration templates (instance.yaml.example)
├── docs/                   # Documentation + metric YAML definitions
└── tests/                  # Test suite (633 tests)

Configuration

File Purpose
config/instance.yaml Instance-specific settings: branding, data source type, auth provider, Google domain
.env Secrets and environment variables — never committed
system.duckdb table_registry table Table definitions managed via POST /api/admin/tables/{id} or the web UI

Copy the example to get started:

cp config/instance.yaml.example config/instance.yaml

See config/instance.yaml.example for all available options.

Documentation

Contributing

  1. Fork the repository and create a feature branch.
  2. Run pytest tests/ -v to verify all tests pass before opening a pull request.
  3. Keep commits focused and messages concise.
  4. Open a pull request against main with a clear description of the change.

For bugs and feature requests, open a GitHub issue.

License

This project is licensed under the MIT License.