# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Pixel is an AI-powered platform for creating comics and videos from scripts. It uses multi-agent systems (AgentScope) and multiple AI providers for image/video/audio generation.

## Repository Structure

- `backend/` — FastAPI (Python 3.12+), SQLModel/SQLAlchemy, Alembic migrations, AgentScope agents
- `frontend/` — Next.js 15 (App Router), React 19, Tailwind CSS, Zustand, @xyflow/react (canvas), TanStack Query

## Common Commands

### Backend

```bash
cd backend
uv sync                                    # Install dependencies
uv run uvicorn src.main:app --reload --port 8000  # Start dev server
./start.sh                                 # Alternative start script
alembic upgrade head                       # Apply DB migrations
alembic revision --autogenerate -m "desc"  # Create migration

# Tests (pytest with asyncio_mode=auto)
pytest                                     # All tests
pytest tests/test_models.py                # Single test file
pytest -k "test_name"                      # Single test by name
pytest --cov=src                           # With coverage

# Code quality
black src/ && isort src/                   # Format
flake8 src/ && mypy src/                   # Lint
```

### Frontend

```bash
cd frontend
pnpm install              # Install dependencies
pnpm dev                  # Start dev server (localhost:3000)
pnpm build                # Production build
pnpm lint                 # ESLint
pnpm type-check           # tsc --noEmit
pnpm format               # ESLint --fix

# Tests
pnpm test                 # Vitest (watch mode)
pnpm test:run             # Vitest (single run)
pnpm test:coverage        # With coverage
pnpm e2e                  # Playwright E2E tests

# Regenerate API types after backend changes
pnpm gen:api
```

### Docker

```bash
docker-compose up -d      # Start all services (backend, frontend, redis, postgres)
docker-compose down        # Stop services
```

## Architecture

### Backend Three-Layer Pattern

All backend code follows: **API Layer** → **Service Layer** → **Repository Layer**

- **API** (`src/api/`): FastAPI routers, request validation, response formatting. All endpoints prefixed with `/api/v1`.
- **Services** (`src/services/`): Business logic. Key services: `project_service`, `task_service`, `storyboard_service`, `storage_service`.
- **Repositories** (`src/repositories/`): Data access via SQLModel. Use `AsyncBaseRepository` (from `base_async.py`) for new code; the sync `BaseRepository` is deprecated.
- **Models** (`src/models/`): `entities.py` has SQLModel table definitions; `schemas/` has Pydantic request/response schemas.
- **Mappers** (`src/mappers/`): Convert between DB entities and API schemas.

### AI Provider System

Providers are registered via JSON config files in `src/config/services/`. The `ModelRegistry` (in `src/services/provider/registry.py`) is a thread-safe factory registry. Model IDs use composite format: `provider/model_key` (e.g., `dashscope/qwen-image`).

Provider implementations live in `src/services/provider/<provider_name>/`. Each provider module has an `image.py`, `video.py`, etc. Provider types: dashscope, volcengine, kling, minimax, modelscope, midjourney, openai, google.

The `ModelRegistry.get()` method creates fresh instances each call (factory pattern) to avoid shared state. Supports variant resolution and fallback models.

### Task Management

The `UnifiedTaskManager` (`src/services/task_manager/`) handles async generation tasks with priority queues, concurrency control, exponential backoff retries, and Prometheus metrics. Tasks are persisted in `TaskDB` with statuses: pending, processing, success, failed, timeout, retrying.

### Agent Engine

`src/services/agent_engine/` contains an AgentScope-based multi-agent system for script analysis and creative workflows. Skills are organized under `skills/` with film production (storyboarding, cinematography, screenwriting, sound design) and general (canvas workflow, project management, creative generation) categories.

### Frontend Architecture

- **App Router** (`src/app/`): Pages organized by feature — admin, canvas, login, register, etc.
- **State**: Zustand stores in `src/lib/store/` — `canvasStore.ts` (with slices: nodes, edges, groups, history, selection, persistence), `authStore.ts`, `modelStore.ts`, `uiStore.ts`.
- **API Client**: Auto-generated from OpenAPI spec into `src/lib/api/`. Services in `src/lib/api/services/` wrap the generated client. Auth token resolved from localStorage via `src/lib/client.ts`.
- **Canvas**: React Flow based infinite canvas (`src/components/canvas/`). Canvas state persisted via `persistenceSlice`.
- **UI Components**: Radix UI primitives in `src/components/ui/`, styled with Tailwind + class-variance-authority.

### API Proxy

In development, Next.js rewrites (`next.config.mjs`) proxy `/api/*`, `/files/*`, `/uploads/*`, `/chat/*`, `/health` to the backend at `API_URL` (default `http://localhost:8000`). In production, set `NEXT_PUBLIC_API_URL` for direct browser-to-backend calls.

### Database

- **Dev**: SQLite (default, no config needed)
- **Prod**: PostgreSQL 15+ via `DATABASE_URL`
- **ORM**: SQLModel (Pydantic v2 + SQLAlchemy)
- **Migrations**: Alembic in `backend/alembic/`
- **Cache**: Optional Redis for caching and rate limiting

### Middleware Stack (backend)

Applied in order: error handler → request tracking → response formatter → security headers → security → rate limiter → performance → metrics → tracing → GZip → CORS.

## Key Conventions

- Backend uses `uv` as package manager, not pip/poetry
- Frontend uses `pnpm`, not npm/yarn
- All generation endpoints return a `task_id`; poll `GET /api/v1/tasks/{task_id}` for results
- Model IDs must use composite format: `provider/model_key` — never pass a separate `provider` parameter
- JSON fields in SQLite use `sa_column=Column(JSON)` on SQLModel fields
- Timestamps stored as Unix floats (not datetime objects) with `TimestampMixin` for ISO conversion
- Soft delete pattern: `deleted_at` field, not physical deletion
