QuickLab-MCP 0.1.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/PKG-INFO +62 -16
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/README.md +60 -14
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/pyproject.toml +2 -2
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/PKG-INFO +62 -16
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/__init__.py +1 -1
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/datasets/service.py +3 -1
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/datasets/validator.py +9 -3
- quicklab_mcp-0.2.1/src/ml_mcp/application/models/catalog_seed.py +294 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/models/service.py +17 -5
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/projects/service.py +1 -3
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/config/settings.py +23 -11
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/errors/base.py +3 -1
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/object_storage/s3.py +69 -34
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/projects.py +4 -4
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/session.py +1 -1
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/telemetry/otel.py +6 -4
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/app.py +12 -2
- quicklab_mcp-0.1.0/src/ml_mcp/application/models/catalog_seed.py +0 -131
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/setup.cfg +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/SOURCES.txt +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/dependency_links.txt +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/entry_points.txt +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/requires.txt +1 -1
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/QuickLab_MCP.egg-info/top_level.txt +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/analysis/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/analysis/diagnostics.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/analysis/service.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/artifacts/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/artifacts/service.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/datasets/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/experiments/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/experiments/service.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/models/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/application/projects/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/config/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/errors/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/errors/codes.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/policies/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/policies/rbac.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/policies/tenant_isolation.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/policies/tool_policy.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/value_objects/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/value_objects/dataset.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/value_objects/experiment.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/domain/value_objects/model.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/object_storage/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/base.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/models.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/artifacts.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/audit.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/datasets.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/experiments.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/metrics.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/models.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/postgres/repositories/outbox.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/queue/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/queue/outbox_processor.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/queue/task_queue.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/redis/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/redis/client.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/redis/rate_limiter.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/telemetry/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/infrastructure/telemetry/logging.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/prompts.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/resources.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/schemas/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/schemas/tools.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/analysis.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/datasets.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/discovery.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/experiments.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/projects.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/mcp/tools/results.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/auth.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/context.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/http.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/middleware.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/server/stdio.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/daemon.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/metrics.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/preprocessing.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/runner.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/sandbox.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/__init__.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/base.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/catboost_model.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/lightgbm_model.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/sklearn_models.py +0 -0
- {quicklab_mcp-0.1.0 → quicklab_mcp-0.2.1}/src/ml_mcp/workers/trainers/xgboost_model.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuickLab-MCP
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: QuickLab MCP Server - Production Tabular ML Experimentation Control Plane for AI Agents
|
|
5
5
|
Author: QuickLab Team
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -19,6 +19,7 @@ Requires-Dist: fastmcp==4.0.5
|
|
|
19
19
|
Requires-Dist: pydantic<3.0.0,>=2.7.0
|
|
20
20
|
Requires-Dist: pydantic-settings<3.0.0,>=2.2.0
|
|
21
21
|
Requires-Dist: sqlalchemy[asyncio]<3.0.0,>=2.0.30
|
|
22
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
22
23
|
Requires-Dist: asyncpg>=0.29.0
|
|
23
24
|
Requires-Dist: redis>=5.0.4
|
|
24
25
|
Requires-Dist: boto3>=1.34.0
|
|
@@ -44,7 +45,6 @@ Provides-Extra: dev
|
|
|
44
45
|
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
|
45
46
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
46
47
|
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: aiosqlite>=0.20.0; extra == "dev"
|
|
48
48
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
49
49
|
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
50
50
|
Requires-Dist: types-redis>=4.6.0; extra == "dev"
|
|
@@ -57,7 +57,7 @@ Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
|
|
|
57
57
|
[](https://pypi.org/project/QuickLab-MCP/)
|
|
58
58
|
[](https://modelcontextprotocol.io/)
|
|
59
59
|
[](https://github.com/jlowin/fastmcp)
|
|
60
|
-
[]()
|
|
61
61
|
[](https://github.com/astral-sh/ruff)
|
|
62
62
|
[](LICENSE)
|
|
63
63
|
[]()
|
|
@@ -77,7 +77,7 @@ Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
|
|
|
77
77
|
- **OAuth 2.1 & Multi-Tenant Security**: Tenant boundary enforcement, RBAC matrix (`Viewer`, `Researcher`, `Operator`, `Admin`), token scope attenuation, and anti-enumeration protections (HTTP 404 instead of 403 on cross-tenant probes).
|
|
78
78
|
- **Transactional Outbox Architecture**: Guarantees zero lost experiment submissions using PostgreSQL transactional Outbox events dispatched to Redis queues.
|
|
79
79
|
- **S3 / MinIO Object Storage**: Immutable tenant-isolated artifact storage with SHA-256 integrity verification, presigned download URLs, and offline memory fallback.
|
|
80
|
-
- **Full Test Coverage**: **
|
|
80
|
+
- **Full Test Coverage**: **108 tests passing (100% green)** across unit, integration, security matrix, MCP contract, zero-config fallbacks, and full end-to-end experiment lifecycle.
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
@@ -304,28 +304,27 @@ ModelLab supports both **STDIO** (local subprocess IPC) and **Streamable HTTP**
|
|
|
304
304
|
|
|
305
305
|
Select your preferred runtime configuration below:
|
|
306
306
|
|
|
307
|
-
### 1. Docker + STDIO
|
|
308
|
-
Runs
|
|
307
|
+
### 1. Docker + STDIO (Pre-Built Image)
|
|
308
|
+
Runs QuickLab inside an isolated Docker container with standard I/O (`sys.stdin` / `sys.stdout`) piped directly to the MCP client:
|
|
309
309
|
|
|
310
310
|
```json
|
|
311
311
|
{
|
|
312
312
|
"mcpServers": {
|
|
313
|
-
"
|
|
313
|
+
"quicklab": {
|
|
314
314
|
"command": "docker",
|
|
315
315
|
"args": [
|
|
316
316
|
"run",
|
|
317
317
|
"-i",
|
|
318
318
|
"--rm",
|
|
319
319
|
"--entrypoint", "python",
|
|
320
|
-
"--network", "host",
|
|
321
320
|
"-e", "APP_ENV=production",
|
|
322
|
-
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@
|
|
323
|
-
"-e", "REDIS_URL=redis://
|
|
324
|
-
"-e", "OBJECT_STORE_ENDPOINT_URL=http://
|
|
321
|
+
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@host.docker.internal:5433/modellab",
|
|
322
|
+
"-e", "REDIS_URL=redis://host.docker.internal:6379/0",
|
|
323
|
+
"-e", "OBJECT_STORE_ENDPOINT_URL=http://host.docker.internal:9000",
|
|
325
324
|
"-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
326
325
|
"-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
327
326
|
"-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
328
|
-
"
|
|
327
|
+
"ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
329
328
|
"-m", "ml_mcp.server.stdio"
|
|
330
329
|
]
|
|
331
330
|
}
|
|
@@ -333,11 +332,10 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
333
332
|
}
|
|
334
333
|
```
|
|
335
334
|
|
|
336
|
-
> **Note for Docker
|
|
335
|
+
> **Note for Docker Networking**: If running local services on your host, `host.docker.internal` allows the container to connect to your host ports seamlessly on macOS, Windows, and Linux. If using Docker Compose, connect via the shared Docker network:
|
|
337
336
|
> ```json
|
|
338
337
|
> "args": [
|
|
339
338
|
> "run", "-i", "--rm",
|
|
340
|
-
> "--entrypoint", "python",
|
|
341
339
|
> "--network", "modellab_default",
|
|
342
340
|
> "-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/modellab",
|
|
343
341
|
> "-e", "REDIS_URL=redis://redis:6379/0",
|
|
@@ -345,11 +343,42 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
345
343
|
> "-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
346
344
|
> "-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
347
345
|
> "-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
348
|
-
> "
|
|
346
|
+
> "ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
349
347
|
> "-m", "ml_mcp.server.stdio"
|
|
350
348
|
> ]
|
|
351
349
|
> ```
|
|
352
350
|
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### Database Schema Initialization & Migrations
|
|
354
|
+
|
|
355
|
+
When pointing QuickLab to a new PostgreSQL instance (local, AWS RDS, Supabase, Neon, or GCP Cloud SQL), the schema must be initialized with Alembic migrations:
|
|
356
|
+
|
|
357
|
+
#### Option A: Docker Compose (Fully Automated)
|
|
358
|
+
If deploying via [`docker-compose.stdio.yml`](docker-compose.stdio.yml) or [`docker-compose.http.yml`](docker-compose.http.yml), migrations execute automatically prior to application launch via the `db-migrate` service:
|
|
359
|
+
```bash
|
|
360
|
+
docker compose -f docker-compose.stdio.yml up -d
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Option B: Standalone Docker (External / Managed Database)
|
|
364
|
+
Run migrations on a remote or standalone PostgreSQL instance without installing Python or cloning the repo:
|
|
365
|
+
```bash
|
|
366
|
+
docker run --rm \
|
|
367
|
+
-e DATABASE_URL="postgresql+asyncpg://<USER>:<PASSWORD>@<HOST>:<PORT>/<DB_NAME>" \
|
|
368
|
+
ghcr.io/aldrin-joan/quicklab-api:latest \
|
|
369
|
+
-m alembic upgrade head
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### Option C: Local Python / UV (Source Repository)
|
|
373
|
+
If running from source:
|
|
374
|
+
```bash
|
|
375
|
+
uv run alembic upgrade head
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
> [!NOTE]
|
|
379
|
+
> **Automatic Seed Data**: Once Alembic creates the schema, QuickLab's startup lifespan automatically seeds the 7 approved tabular ML models and ensures a default project workspace exists for the tenant.
|
|
380
|
+
|
|
381
|
+
|
|
353
382
|
---
|
|
354
383
|
|
|
355
384
|
### 2. Docker + HTTP
|
|
@@ -371,7 +400,24 @@ Connects to the ModelLab API service running inside Docker Compose (`docker comp
|
|
|
371
400
|
---
|
|
372
401
|
|
|
373
402
|
### 3. UVX + STDIO (Recommended Turnkey Setup)
|
|
374
|
-
Runs QuickLab globally via `uvx` directly from PyPI
|
|
403
|
+
Runs QuickLab globally via `uvx` directly from PyPI.
|
|
404
|
+
|
|
405
|
+
#### Zero-Configuration Mode (Zero Dependencies Required)
|
|
406
|
+
If you don't have PostgreSQL or Docker installed, QuickLab automatically provisions an embedded SQLite database (`~/.quicklab/quicklab.db`), local filesystem artifact storage (`~/.quicklab/artifacts`), and an in-process queue:
|
|
407
|
+
|
|
408
|
+
```json
|
|
409
|
+
{
|
|
410
|
+
"mcpServers": {
|
|
411
|
+
"quicklab": {
|
|
412
|
+
"command": "uvx",
|
|
413
|
+
"args": ["quicklab-mcp"]
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Production Mode (Connecting to PostgreSQL & MinIO / S3)
|
|
420
|
+
To connect QuickLab to your own PostgreSQL and S3-compatible storage:
|
|
375
421
|
|
|
376
422
|
```json
|
|
377
423
|
{
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/QuickLab-MCP/)
|
|
5
5
|
[](https://modelcontextprotocol.io/)
|
|
6
6
|
[](https://github.com/jlowin/fastmcp)
|
|
7
|
-
[]()
|
|
8
8
|
[](https://github.com/astral-sh/ruff)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- **OAuth 2.1 & Multi-Tenant Security**: Tenant boundary enforcement, RBAC matrix (`Viewer`, `Researcher`, `Operator`, `Admin`), token scope attenuation, and anti-enumeration protections (HTTP 404 instead of 403 on cross-tenant probes).
|
|
25
25
|
- **Transactional Outbox Architecture**: Guarantees zero lost experiment submissions using PostgreSQL transactional Outbox events dispatched to Redis queues.
|
|
26
26
|
- **S3 / MinIO Object Storage**: Immutable tenant-isolated artifact storage with SHA-256 integrity verification, presigned download URLs, and offline memory fallback.
|
|
27
|
-
- **Full Test Coverage**: **
|
|
27
|
+
- **Full Test Coverage**: **108 tests passing (100% green)** across unit, integration, security matrix, MCP contract, zero-config fallbacks, and full end-to-end experiment lifecycle.
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -251,28 +251,27 @@ ModelLab supports both **STDIO** (local subprocess IPC) and **Streamable HTTP**
|
|
|
251
251
|
|
|
252
252
|
Select your preferred runtime configuration below:
|
|
253
253
|
|
|
254
|
-
### 1. Docker + STDIO
|
|
255
|
-
Runs
|
|
254
|
+
### 1. Docker + STDIO (Pre-Built Image)
|
|
255
|
+
Runs QuickLab inside an isolated Docker container with standard I/O (`sys.stdin` / `sys.stdout`) piped directly to the MCP client:
|
|
256
256
|
|
|
257
257
|
```json
|
|
258
258
|
{
|
|
259
259
|
"mcpServers": {
|
|
260
|
-
"
|
|
260
|
+
"quicklab": {
|
|
261
261
|
"command": "docker",
|
|
262
262
|
"args": [
|
|
263
263
|
"run",
|
|
264
264
|
"-i",
|
|
265
265
|
"--rm",
|
|
266
266
|
"--entrypoint", "python",
|
|
267
|
-
"--network", "host",
|
|
268
267
|
"-e", "APP_ENV=production",
|
|
269
|
-
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@
|
|
270
|
-
"-e", "REDIS_URL=redis://
|
|
271
|
-
"-e", "OBJECT_STORE_ENDPOINT_URL=http://
|
|
268
|
+
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@host.docker.internal:5433/modellab",
|
|
269
|
+
"-e", "REDIS_URL=redis://host.docker.internal:6379/0",
|
|
270
|
+
"-e", "OBJECT_STORE_ENDPOINT_URL=http://host.docker.internal:9000",
|
|
272
271
|
"-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
273
272
|
"-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
274
273
|
"-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
275
|
-
"
|
|
274
|
+
"ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
276
275
|
"-m", "ml_mcp.server.stdio"
|
|
277
276
|
]
|
|
278
277
|
}
|
|
@@ -280,11 +279,10 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
280
279
|
}
|
|
281
280
|
```
|
|
282
281
|
|
|
283
|
-
> **Note for Docker
|
|
282
|
+
> **Note for Docker Networking**: If running local services on your host, `host.docker.internal` allows the container to connect to your host ports seamlessly on macOS, Windows, and Linux. If using Docker Compose, connect via the shared Docker network:
|
|
284
283
|
> ```json
|
|
285
284
|
> "args": [
|
|
286
285
|
> "run", "-i", "--rm",
|
|
287
|
-
> "--entrypoint", "python",
|
|
288
286
|
> "--network", "modellab_default",
|
|
289
287
|
> "-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/modellab",
|
|
290
288
|
> "-e", "REDIS_URL=redis://redis:6379/0",
|
|
@@ -292,11 +290,42 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
292
290
|
> "-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
293
291
|
> "-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
294
292
|
> "-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
295
|
-
> "
|
|
293
|
+
> "ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
296
294
|
> "-m", "ml_mcp.server.stdio"
|
|
297
295
|
> ]
|
|
298
296
|
> ```
|
|
299
297
|
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### Database Schema Initialization & Migrations
|
|
301
|
+
|
|
302
|
+
When pointing QuickLab to a new PostgreSQL instance (local, AWS RDS, Supabase, Neon, or GCP Cloud SQL), the schema must be initialized with Alembic migrations:
|
|
303
|
+
|
|
304
|
+
#### Option A: Docker Compose (Fully Automated)
|
|
305
|
+
If deploying via [`docker-compose.stdio.yml`](docker-compose.stdio.yml) or [`docker-compose.http.yml`](docker-compose.http.yml), migrations execute automatically prior to application launch via the `db-migrate` service:
|
|
306
|
+
```bash
|
|
307
|
+
docker compose -f docker-compose.stdio.yml up -d
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### Option B: Standalone Docker (External / Managed Database)
|
|
311
|
+
Run migrations on a remote or standalone PostgreSQL instance without installing Python or cloning the repo:
|
|
312
|
+
```bash
|
|
313
|
+
docker run --rm \
|
|
314
|
+
-e DATABASE_URL="postgresql+asyncpg://<USER>:<PASSWORD>@<HOST>:<PORT>/<DB_NAME>" \
|
|
315
|
+
ghcr.io/aldrin-joan/quicklab-api:latest \
|
|
316
|
+
-m alembic upgrade head
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Option C: Local Python / UV (Source Repository)
|
|
320
|
+
If running from source:
|
|
321
|
+
```bash
|
|
322
|
+
uv run alembic upgrade head
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
> [!NOTE]
|
|
326
|
+
> **Automatic Seed Data**: Once Alembic creates the schema, QuickLab's startup lifespan automatically seeds the 7 approved tabular ML models and ensures a default project workspace exists for the tenant.
|
|
327
|
+
|
|
328
|
+
|
|
300
329
|
---
|
|
301
330
|
|
|
302
331
|
### 2. Docker + HTTP
|
|
@@ -318,7 +347,24 @@ Connects to the ModelLab API service running inside Docker Compose (`docker comp
|
|
|
318
347
|
---
|
|
319
348
|
|
|
320
349
|
### 3. UVX + STDIO (Recommended Turnkey Setup)
|
|
321
|
-
Runs QuickLab globally via `uvx` directly from PyPI
|
|
350
|
+
Runs QuickLab globally via `uvx` directly from PyPI.
|
|
351
|
+
|
|
352
|
+
#### Zero-Configuration Mode (Zero Dependencies Required)
|
|
353
|
+
If you don't have PostgreSQL or Docker installed, QuickLab automatically provisions an embedded SQLite database (`~/.quicklab/quicklab.db`), local filesystem artifact storage (`~/.quicklab/artifacts`), and an in-process queue:
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"mcpServers": {
|
|
358
|
+
"quicklab": {
|
|
359
|
+
"command": "uvx",
|
|
360
|
+
"args": ["quicklab-mcp"]
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### Production Mode (Connecting to PostgreSQL & MinIO / S3)
|
|
367
|
+
To connect QuickLab to your own PostgreSQL and S3-compatible storage:
|
|
322
368
|
|
|
323
369
|
```json
|
|
324
370
|
{
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "QuickLab-MCP"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "QuickLab MCP Server - Production Tabular ML Experimentation Control Plane for AI Agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -25,6 +25,7 @@ dependencies = [
|
|
|
25
25
|
"pydantic>=2.7.0,<3.0.0",
|
|
26
26
|
"pydantic-settings>=2.2.0,<3.0.0",
|
|
27
27
|
"sqlalchemy[asyncio]>=2.0.30,<3.0.0",
|
|
28
|
+
"aiosqlite>=0.20.0",
|
|
28
29
|
"asyncpg>=0.29.0",
|
|
29
30
|
"redis>=5.0.4",
|
|
30
31
|
"boto3>=1.34.0",
|
|
@@ -64,7 +65,6 @@ dev = [
|
|
|
64
65
|
"pytest>=8.2.0",
|
|
65
66
|
"pytest-asyncio>=0.23.0",
|
|
66
67
|
"pytest-cov>=5.0.0",
|
|
67
|
-
"aiosqlite>=0.20.0",
|
|
68
68
|
"ruff>=0.4.0",
|
|
69
69
|
"mypy>=1.10.0",
|
|
70
70
|
"types-redis>=4.6.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuickLab-MCP
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: QuickLab MCP Server - Production Tabular ML Experimentation Control Plane for AI Agents
|
|
5
5
|
Author: QuickLab Team
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -19,6 +19,7 @@ Requires-Dist: fastmcp==4.0.5
|
|
|
19
19
|
Requires-Dist: pydantic<3.0.0,>=2.7.0
|
|
20
20
|
Requires-Dist: pydantic-settings<3.0.0,>=2.2.0
|
|
21
21
|
Requires-Dist: sqlalchemy[asyncio]<3.0.0,>=2.0.30
|
|
22
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
22
23
|
Requires-Dist: asyncpg>=0.29.0
|
|
23
24
|
Requires-Dist: redis>=5.0.4
|
|
24
25
|
Requires-Dist: boto3>=1.34.0
|
|
@@ -44,7 +45,6 @@ Provides-Extra: dev
|
|
|
44
45
|
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
|
45
46
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
46
47
|
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: aiosqlite>=0.20.0; extra == "dev"
|
|
48
48
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
49
49
|
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
50
50
|
Requires-Dist: types-redis>=4.6.0; extra == "dev"
|
|
@@ -57,7 +57,7 @@ Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
|
|
|
57
57
|
[](https://pypi.org/project/QuickLab-MCP/)
|
|
58
58
|
[](https://modelcontextprotocol.io/)
|
|
59
59
|
[](https://github.com/jlowin/fastmcp)
|
|
60
|
-
[]()
|
|
61
61
|
[](https://github.com/astral-sh/ruff)
|
|
62
62
|
[](LICENSE)
|
|
63
63
|
[]()
|
|
@@ -77,7 +77,7 @@ Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
|
|
|
77
77
|
- **OAuth 2.1 & Multi-Tenant Security**: Tenant boundary enforcement, RBAC matrix (`Viewer`, `Researcher`, `Operator`, `Admin`), token scope attenuation, and anti-enumeration protections (HTTP 404 instead of 403 on cross-tenant probes).
|
|
78
78
|
- **Transactional Outbox Architecture**: Guarantees zero lost experiment submissions using PostgreSQL transactional Outbox events dispatched to Redis queues.
|
|
79
79
|
- **S3 / MinIO Object Storage**: Immutable tenant-isolated artifact storage with SHA-256 integrity verification, presigned download URLs, and offline memory fallback.
|
|
80
|
-
- **Full Test Coverage**: **
|
|
80
|
+
- **Full Test Coverage**: **108 tests passing (100% green)** across unit, integration, security matrix, MCP contract, zero-config fallbacks, and full end-to-end experiment lifecycle.
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
@@ -304,28 +304,27 @@ ModelLab supports both **STDIO** (local subprocess IPC) and **Streamable HTTP**
|
|
|
304
304
|
|
|
305
305
|
Select your preferred runtime configuration below:
|
|
306
306
|
|
|
307
|
-
### 1. Docker + STDIO
|
|
308
|
-
Runs
|
|
307
|
+
### 1. Docker + STDIO (Pre-Built Image)
|
|
308
|
+
Runs QuickLab inside an isolated Docker container with standard I/O (`sys.stdin` / `sys.stdout`) piped directly to the MCP client:
|
|
309
309
|
|
|
310
310
|
```json
|
|
311
311
|
{
|
|
312
312
|
"mcpServers": {
|
|
313
|
-
"
|
|
313
|
+
"quicklab": {
|
|
314
314
|
"command": "docker",
|
|
315
315
|
"args": [
|
|
316
316
|
"run",
|
|
317
317
|
"-i",
|
|
318
318
|
"--rm",
|
|
319
319
|
"--entrypoint", "python",
|
|
320
|
-
"--network", "host",
|
|
321
320
|
"-e", "APP_ENV=production",
|
|
322
|
-
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@
|
|
323
|
-
"-e", "REDIS_URL=redis://
|
|
324
|
-
"-e", "OBJECT_STORE_ENDPOINT_URL=http://
|
|
321
|
+
"-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@host.docker.internal:5433/modellab",
|
|
322
|
+
"-e", "REDIS_URL=redis://host.docker.internal:6379/0",
|
|
323
|
+
"-e", "OBJECT_STORE_ENDPOINT_URL=http://host.docker.internal:9000",
|
|
325
324
|
"-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
326
325
|
"-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
327
326
|
"-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
328
|
-
"
|
|
327
|
+
"ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
329
328
|
"-m", "ml_mcp.server.stdio"
|
|
330
329
|
]
|
|
331
330
|
}
|
|
@@ -333,11 +332,10 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
333
332
|
}
|
|
334
333
|
```
|
|
335
334
|
|
|
336
|
-
> **Note for Docker
|
|
335
|
+
> **Note for Docker Networking**: If running local services on your host, `host.docker.internal` allows the container to connect to your host ports seamlessly on macOS, Windows, and Linux. If using Docker Compose, connect via the shared Docker network:
|
|
337
336
|
> ```json
|
|
338
337
|
> "args": [
|
|
339
338
|
> "run", "-i", "--rm",
|
|
340
|
-
> "--entrypoint", "python",
|
|
341
339
|
> "--network", "modellab_default",
|
|
342
340
|
> "-e", "DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/modellab",
|
|
343
341
|
> "-e", "REDIS_URL=redis://redis:6379/0",
|
|
@@ -345,11 +343,42 @@ Runs ModelLab inside an isolated Docker container with standard I/O (`sys.stdin`
|
|
|
345
343
|
> "-e", "OBJECT_STORE_ACCESS_KEY_ID=minioadmin",
|
|
346
344
|
> "-e", "OBJECT_STORE_SECRET_ACCESS_KEY=minioadmin",
|
|
347
345
|
> "-e", "OBJECT_STORE_BUCKET_NAME=modellab-artifacts",
|
|
348
|
-
> "
|
|
346
|
+
> "ghcr.io/aldrin-joan/quicklab-api:latest",
|
|
349
347
|
> "-m", "ml_mcp.server.stdio"
|
|
350
348
|
> ]
|
|
351
349
|
> ```
|
|
352
350
|
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### Database Schema Initialization & Migrations
|
|
354
|
+
|
|
355
|
+
When pointing QuickLab to a new PostgreSQL instance (local, AWS RDS, Supabase, Neon, or GCP Cloud SQL), the schema must be initialized with Alembic migrations:
|
|
356
|
+
|
|
357
|
+
#### Option A: Docker Compose (Fully Automated)
|
|
358
|
+
If deploying via [`docker-compose.stdio.yml`](docker-compose.stdio.yml) or [`docker-compose.http.yml`](docker-compose.http.yml), migrations execute automatically prior to application launch via the `db-migrate` service:
|
|
359
|
+
```bash
|
|
360
|
+
docker compose -f docker-compose.stdio.yml up -d
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Option B: Standalone Docker (External / Managed Database)
|
|
364
|
+
Run migrations on a remote or standalone PostgreSQL instance without installing Python or cloning the repo:
|
|
365
|
+
```bash
|
|
366
|
+
docker run --rm \
|
|
367
|
+
-e DATABASE_URL="postgresql+asyncpg://<USER>:<PASSWORD>@<HOST>:<PORT>/<DB_NAME>" \
|
|
368
|
+
ghcr.io/aldrin-joan/quicklab-api:latest \
|
|
369
|
+
-m alembic upgrade head
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### Option C: Local Python / UV (Source Repository)
|
|
373
|
+
If running from source:
|
|
374
|
+
```bash
|
|
375
|
+
uv run alembic upgrade head
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
> [!NOTE]
|
|
379
|
+
> **Automatic Seed Data**: Once Alembic creates the schema, QuickLab's startup lifespan automatically seeds the 7 approved tabular ML models and ensures a default project workspace exists for the tenant.
|
|
380
|
+
|
|
381
|
+
|
|
353
382
|
---
|
|
354
383
|
|
|
355
384
|
### 2. Docker + HTTP
|
|
@@ -371,7 +400,24 @@ Connects to the ModelLab API service running inside Docker Compose (`docker comp
|
|
|
371
400
|
---
|
|
372
401
|
|
|
373
402
|
### 3. UVX + STDIO (Recommended Turnkey Setup)
|
|
374
|
-
Runs QuickLab globally via `uvx` directly from PyPI
|
|
403
|
+
Runs QuickLab globally via `uvx` directly from PyPI.
|
|
404
|
+
|
|
405
|
+
#### Zero-Configuration Mode (Zero Dependencies Required)
|
|
406
|
+
If you don't have PostgreSQL or Docker installed, QuickLab automatically provisions an embedded SQLite database (`~/.quicklab/quicklab.db`), local filesystem artifact storage (`~/.quicklab/artifacts`), and an in-process queue:
|
|
407
|
+
|
|
408
|
+
```json
|
|
409
|
+
{
|
|
410
|
+
"mcpServers": {
|
|
411
|
+
"quicklab": {
|
|
412
|
+
"command": "uvx",
|
|
413
|
+
"args": ["quicklab-mcp"]
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Production Mode (Connecting to PostgreSQL & MinIO / S3)
|
|
420
|
+
To connect QuickLab to your own PostgreSQL and S3-compatible storage:
|
|
375
421
|
|
|
376
422
|
```json
|
|
377
423
|
{
|
|
@@ -129,7 +129,9 @@ class DatasetService:
|
|
|
129
129
|
],
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
async def list_datasets(
|
|
132
|
+
async def list_datasets(
|
|
133
|
+
self, tenant_id: str, project_id: str | None = None
|
|
134
|
+
) -> list[dict[str, Any]]:
|
|
133
135
|
datasets = await self.repo.list_datasets(tenant_id, project_id)
|
|
134
136
|
return [
|
|
135
137
|
{
|
|
@@ -61,7 +61,9 @@ class DatasetValidator:
|
|
|
61
61
|
try:
|
|
62
62
|
text_content = raw_bytes.decode("utf-8")
|
|
63
63
|
except UnicodeDecodeError as exc:
|
|
64
|
-
raise DatasetValidationError(
|
|
64
|
+
raise DatasetValidationError(
|
|
65
|
+
f"Malformed encoding: dataset must be valid UTF-8: {exc}"
|
|
66
|
+
) from exc
|
|
65
67
|
|
|
66
68
|
reader = csv.reader(io.StringIO(text_content))
|
|
67
69
|
try:
|
|
@@ -89,7 +91,9 @@ class DatasetValidator:
|
|
|
89
91
|
elif fmt == DatasetFormat.JSONL.value:
|
|
90
92
|
df = pd.read_json(bio, lines=True)
|
|
91
93
|
else:
|
|
92
|
-
raise DatasetValidationError(
|
|
94
|
+
raise DatasetValidationError(
|
|
95
|
+
f"Unsupported dataset format '{data_format}'. Allowed: csv, parquet, jsonl"
|
|
96
|
+
)
|
|
93
97
|
except Exception as exc:
|
|
94
98
|
raise DatasetValidationError(f"Failed to parse dataset as {fmt}: {exc}") from exc
|
|
95
99
|
|
|
@@ -148,7 +152,9 @@ class DatasetValidator:
|
|
|
148
152
|
df.to_parquet(out_buf, index=False, engine="pyarrow", compression="snappy")
|
|
149
153
|
return out_buf.getvalue()
|
|
150
154
|
|
|
151
|
-
def process(
|
|
155
|
+
def process(
|
|
156
|
+
self, raw_bytes: bytes, data_format: DatasetFormat | str
|
|
157
|
+
) -> tuple[pd.DataFrame, bytes, DatasetSchema]:
|
|
152
158
|
"""Validate, convert to canonical parquet, and extract complete schema."""
|
|
153
159
|
df = self.parse_dataframe(raw_bytes, data_format)
|
|
154
160
|
parquet_bytes = self.canonicalize_to_parquet(df)
|