diff --git a/backend/app/main.py b/backend/app/main.py index 1c7e480..15302d7 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -524,6 +524,26 @@ async def upload_creative_asset(file: UploadFile = File(...)) -> AssetRecord: return await asset_store.save_bytes(data, filename=filename, media_type=content_type) +class AssetUploadBase64Request(BaseModel): + filename: str + content_base64: str + media_type: str = "image" + + +@app.post("/api/v1/creative/upload-base64", response_model=AssetRecord) +async def upload_creative_asset_base64(req: AssetUploadBase64Request) -> AssetRecord: + """Upload asset encoded in base64 (used by CLI and programmatic API clients).""" + import base64 + import binascii + try: + data = base64.b64decode(req.content_base64, validate=True) + except (binascii.Error, ValueError) as exc: + raise HTTPException(status_code=400, detail=f"Invalid base64 payload: {exc}") + return await asset_store.save_bytes(data, filename=req.filename, media_type=req.media_type) + + + + # --------------------------------------------------------------------------- # Cloud Providers & BYOK Credentials Endpoints (M4) # --------------------------------------------------------------------------- diff --git a/backend/tests/test_m8_cli.py b/backend/tests/test_m8_cli.py new file mode 100644 index 0000000..164625b --- /dev/null +++ b/backend/tests/test_m8_cli.py @@ -0,0 +1,76 @@ +"""Tests for Milestone M8: Unified Berry CLI backend support.""" + +import asyncio +import pytest +from fastapi.testclient import TestClient + +from app.main import app, active_cancellations + +client = TestClient(app) + + +def test_upload_base64_image_success(): + """Test successful base64 asset upload via the CLI/backend endpoint.""" + # 1x1 transparent PNG in base64 + tiny_png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + + resp = client.post( + "/api/v1/creative/upload-base64", + json={ + "filename": "cli_test_image.png", + "content_base64": tiny_png_b64, + "media_type": "image", + }, + ) + + assert resp.status_code == 200 + data = resp.json() + assert "id" in data + assert data["filename"] == "cli_test_image.png" + assert data["media_type"] == "image" + assert data["byte_size"] > 0 + assert "content_hash" in data + + +def test_upload_base64_invalid_data(): + """Test handling of invalid base64 payload.""" + resp = client.post( + "/api/v1/creative/upload-base64", + json={ + "filename": "bad.png", + "content_base64": "not-valid-base-64!!!", + "media_type": "image", + }, + ) + assert resp.status_code == 400 + assert "Invalid base64 payload" in resp.json()["detail"] + + +def test_active_tasks_and_cancel_endpoints(): + """Test task inspection and cancellation endpoints.""" + # Register an active task cancellation token + test_task_id = "test-task-cli-123" + cancel_event = asyncio.Event() + active_cancellations[test_task_id] = cancel_event + + try: + # Active tasks query + resp = client.get("/api/v1/tasks/active") + assert resp.status_code == 200 + tasks = resp.json() + assert "tasks" in tasks + assert any(t["id"] == test_task_id for t in tasks["tasks"]) + + # Cancel the active task + cancel_resp = client.post(f"/api/v1/tasks/{test_task_id}/cancel") + assert cancel_resp.status_code == 200 + data = cancel_resp.json() + assert data["task_id"] == test_task_id + assert data["status"] == "cancelled" + assert cancel_event.is_set() + + # Cancel a non-existent task returns 404 + nonexistent_resp = client.post("/api/v1/tasks/nonexistent-task-id/cancel") + assert nonexistent_resp.status_code == 404 + finally: + active_cancellations.pop(test_task_id, None) diff --git a/docs/CLI_REFERENCE.md b/docs/CLI_REFERENCE.md new file mode 100644 index 0000000..ad128d9 --- /dev/null +++ b/docs/CLI_REFERENCE.md @@ -0,0 +1,171 @@ +# Berry AI Studio - Unified Command-Line Interface (CLI) Reference + +The Berry AI Studio CLI provides headless, scriptable, and programmatic access to creative workflows, process supervision, engine lifecycle, task management, and system diagnostics. The CLI is embedded in the Rust launcher executable (`berry.exe` on Windows, `berry` on Linux/macOS) and communicates directly with the Berry Core backend over localhost loopback HTTP REST and WebSocket APIs. + +--- + +## 1. Quick Start + +Ensure the Berry backend is running or launch it via the supervisor: +```bash +# Launch GUI workspace and environment supervisor +berry + +# Check status of core, engines, and model catalog +berry status + +# Check hardware and system diagnostics +berry system +``` + +--- + +## 2. Global Options and Environment Variables + +| Option / Variable | Description | Default | +| :--- | :--- | :--- | +| `BERRY_PORT` | Custom port for Berry AI Studio backend HTTP server | `8000` | +| `--json` | Format output as structured JSON for automation and scripts | `false` | +| `--help`, `-h` | Display command syntax and available subcommands | - | + +Exit Codes: +- `0`: Success +- `1`: Error (validation failure, connectivity issue, or engine execution error) + +--- + +## 3. Creative Workflow Execution (`berry run`) + +Executes high-level creative actions directly against local engines (ComfyUI / WebUI) or configured cloud BYOK providers (Fal.ai, SiliconFlow). Supports automatic image/mask uploading, deterministic caching, and output downloading. + +### Syntax +```bash +berry run [options] +``` + +### Supported Actions +- `txt2img`: Text-to-image generation +- `img2img`: Image-to-image styling and transformation +- `inpaint`: Mask-based image editing and replacement +- `upscale`: Image upscaling and resolution enhancement +- `txt2video`: Text-to-video generation (e.g. CogVideoX / AnimateDiff) +- `img2video`: Image-to-video animation (e.g. SVD XT / Fast SVD) + +### Command Options +| Option | Type | Default | Description | +| :--- | :--- | :--- | :--- | +| `--prompt ` | String | `""` | Positive prompt describing the desired generation | +| `--negative-prompt ` | String | Default negative | Negative prompt describing unwanted attributes | +| `--engine ` | String | `managed_comfyui` | Target engine: `managed_comfyui`, `managed_webui`, `fal_ai`, `siliconflow` | +| `--model ` | String | `v1-5-pruned-emaonly.safetensors` | Checkpoint or cloud model identifier | +| `--aspect-ratio ` | String | `1:1` | Aspect ratio: `1:1`, `16:9`, `9:16`, `4:3`, `3:4` | +| `--seed ` | Integer | `-1` | Random seed (`-1` for randomized generation) | +| `--steps ` | Integer | `20` | Denoising inference steps | +| `--cfg ` | Float | `7.0` | Classifier-Free Guidance (CFG) scale | +| `--denoise ` | Float | `0.75` | Denoise strength for `img2img` and `inpaint` | +| `--image ` | Path | None | Path to local input image for `img2img`, `inpaint`, `upscale`, or `img2video` | +| `--mask ` | Path | None | Path to local mask image for `inpaint` | +| `--fps ` | Integer | `16` | Video framerate (frames per second) | +| `--frames ` | Integer | `25` | Total number of video frames to generate | +| `--motion ` | Integer | `127` | Motion bucket ID / intensity for video animation | +| `--upscale-factor ` | Float | `2.0` | Scale multiplier for `upscale` action | +| `--output ` | Path | None | Destination path to automatically download and save the resulting media file | +| `--json` | Flag | False | Output result as structured JSON to stdout | + +### Examples + +#### Text-to-Image Generation +```bash +berry run txt2img \ + --prompt "A retro sci-fi rover exploring a neon crater on Mars" \ + --aspect-ratio 16:9 \ + --steps 25 \ + --output ./mars_rover.png +``` + +#### Image-to-Video Animation +```bash +berry run img2video \ + --image ./mars_rover.png \ + --fps 24 \ + --frames 49 \ + --motion 150 \ + --output ./mars_rover.mp4 +``` + +#### JSON Pipeline Integration +```bash +berry run txt2img --prompt "cyberpunk street market" --json | jq .image_url +``` + +--- + +## 4. Task Management (`berry tasks`) + +Inspect and cancel background generation and workflow tasks. + +### Syntax +```bash +# List all in-flight tasks +berry tasks list [--json] + +# Cancel an active task by task ID +berry tasks cancel [--json] +``` + +### Examples +```bash +berry tasks list +# Active Creative Tasks: +# - Task [c997ff97-15ef-4573-8efd-bb74b09e44eb] : action=txt2video, engine=managed_comfyui + +berry tasks cancel c997ff97-15ef-4573-8efd-bb74b09e44eb +# [OK] Task cancellation signal sent for c997ff97-15ef-4573-8efd-bb74b09e44eb +``` + +--- + +## 5. System Diagnostics (`berry system`) + +Queries hardware readiness, discrete GPU detection, CUDA / ROCm / DirectML availability, memory capacity, and running engine endpoints. + +### Syntax +```bash +berry system [--json] +``` + +--- + +## 6. Engine and Environment Lifecycle + +| Command | Description | +| :--- | :--- | +| `berry status` | Unified status of Berry Core, local managed runtimes, external engines, and model index | +| `berry stop [--force]` | Graceful or forced shutdown of core and child engine processes | +| `berry manager` | Launch or focus the web-based Environment Manager | +| `berry engine start ` | Start managed engine subprocess | +| `berry engine stop ` | Stop managed engine subprocess | +| `berry engine install ` | Initiate isolated engine installation | +| `berry engine update ` | Update engine Git repository with dirty-check & rollback protection | + +--- + +## 7. Model Inventory Management (`berry models`) + +| Command | Description | +| :--- | :--- | +| `berry models list` | List indexed models, architecture, file size, and paths | +| `berry models rescan` | Trigger filesystem rescan across all registered model roots | +| `berry models roots` | Display configured model storage roots | +| `berry models add-root