# Directiva: Generacion de Contenido IA - PMC

**Version:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Activa
**Modulo:** PMC-004 Generation

---

## Proposito

Define los patrones, convenciones y flujos para implementar el motor de generacion de contenido IA en PMC, incluyendo integracion con ComfyUI, gestion de workflows, modelos personalizados y colas de procesamiento.

---

## Stack de Generacion

```yaml
Imagenes:
  Motor: ComfyUI
  Modelo base: SDXL 1.0
  Extensiones:
    - ControlNet (poses, depth)
    - IP-Adapter (consistencia)
    - LoRA (personalizacion)
  Formato salida: PNG, WebP

Texto:
  API: OpenAI GPT-4 / Claude API
  Uso: Copys, hashtags, adaptacion de tono

Colas:
  Sistema: BullMQ + Redis
  Colas definidas:
    - generation:image (concurrency: 2)
    - generation:text (concurrency: 10)
    - generation:training (concurrency: 1)

WebSocket:
  Framework: Socket.io
  Eventos: progress, completed, failed
```

---

## Arquitectura

```
┌─────────────────────────────────────────────────────────────┐
│                      Client (React)                          │
│                                                              │
│  1. Usuario configura generacion                             │
│  2. Submit POST /api/v1/generation/jobs                      │
│  3. Suscribe a WebSocket job:{jobId}                         │
│  4. Recibe eventos de progreso                               │
│  5. Muestra resultado al completar                           │
└─────────────────────┬───────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                    Backend (NestJS)                          │
│                                                              │
│  GenerationController:                                       │
│    - Valida permisos (tenant, usuario)                       │
│    - Verifica quota del tenant                               │
│    - Valida inputs contra schema del workflow                │
│    - Crea registro GenerationJob                             │
│    - Encola en BullMQ                                        │
│    - Retorna jobId                                           │
└─────────────────────┬───────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                Bull Processor (Worker)                       │
│                                                              │
│  ImageGenerationProcessor:                                   │
│    1. Actualiza status a "processing"                        │
│    2. Carga workflow template                                │
│    3. Inyecta parametros del usuario                         │
│    4. Carga LoRA de marca si aplica                          │
│    5. Envia a ComfyUI API                                    │
│    6. Escucha progreso via WebSocket                         │
│    7. Emite eventos al cliente                               │
│    8. Descarga imagenes resultado                            │
│    9. Crea Assets en storage                                 │
│   10. Actualiza job a "completed"                            │
└─────────────────────┬───────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                    ComfyUI Server                            │
│                                                              │
│  Hardware: GPU NVIDIA 12-24GB VRAM                           │
│  Modelos cargados:                                           │
│    - sd_xl_base_1.0.safetensors                              │
│    - sd_xl_refiner_1.0.safetensors                           │
│    - ControlNets (canny, depth, openpose)                    │
│    - IP-Adapter                                              │
│    - LoRAs del tenant (cargados dinamicamente)               │
└─────────────────────────────────────────────────────────────┘
```

---

## Workflows Predefinidos

### WF-001: Product Photo Synthetic

```yaml
ID: product_photo_synthetic
Proposito: Fotos de producto en contexto comercial
Tiempo estimado: 30-60 segundos

Inputs:
  - product_description: string (REQUERIDO)
  - reference_image: file (opcional)
  - background: enum [white, lifestyle, custom]
  - style: enum [minimalist, premium, casual]
  - lora_id: UUID (opcional, o autodetectar de marca)
  - seed: number (opcional)

Outputs:
  - 5 variaciones
  - 1024x1024 PNG
  - Fondo transparente disponible

Nodos ComfyUI:
  - CheckpointLoaderSimple (SDXL)
  - LoraLoader (si lora_id)
  - CLIPTextEncode (positive + negative)
  - KSampler
  - VAEDecode
  - RemoveBackground (si fondo transparente)
  - SaveImage
```

### WF-002: Social Media Post

```yaml
ID: social_media_post
Proposito: Imagen + copy para redes sociales
Tiempo estimado: 45-90 segundos

Inputs:
  - brief: object {objetivo, audiencia, tono}
  - product_id: UUID (opcional)
  - channel: enum [instagram, facebook, linkedin, tiktok]
  - format: enum [post, story, carousel]
  - brand_id: UUID

Outputs:
  - 3-5 variaciones de imagen
  - Copy sugerido por variacion
  - Hashtags recomendados

Proceso especial:
  1. Generar imagen con ComfyUI
  2. Llamar LLM para copy basado en imagen + brief
  3. Llamar LLM para hashtags
```

### WF-003: Ad Variations

```yaml
ID: ad_variations
Proposito: Variaciones para A/B testing
Tiempo estimado: Variable

Inputs:
  - base_image: asset_id o URL
  - variations_count: number (2-10)
  - variation_type: enum [color, background, composition]

Outputs:
  - N variaciones
  - Metadata de diferencias
```

### WF-004: Virtual Avatar

```yaml
ID: virtual_avatar
Proposito: Avatar/influencer virtual consistente
Tiempo estimado: 60-120 segundos

Inputs:
  - character_lora_id: UUID (REQUERIDO)
  - pose: enum [standing, sitting, walking, custom]
  - outfit: string
  - background: string
  - expression: enum [happy, serious, surprised, neutral]

Outputs:
  - Imagen del avatar
  - Consistencia facial garantizada

Nodos especiales:
  - IP-Adapter para consistencia
  - ControlNet OpenPose para pose
```

---

## Flujos de Trabajo

### Flujo: Crear Job de Generacion

```typescript
// 1. Validar permisos
const canGenerate = await this.checkPermissions(user, tenant);
if (!canGenerate) throw new ForbiddenException();

// 2. Verificar quota
const hasQuota = await this.quotaService.check(tenant.id, 'generations');
if (!hasQuota) throw new PaymentRequiredException('Quota exceeded');

// 3. Cargar y validar workflow
const workflow = await this.workflowService.findById(dto.workflow_id);
this.validateInputs(dto.inputs, workflow.input_schema);

// 4. Inyectar contexto de marca si aplica
if (dto.brand_id) {
  dto.inputs = await this.injectBrandContext(dto.brand_id, dto.inputs);
}

// 5. Crear job en BD
const job = await this.jobRepository.save({
  tenant_id: tenant.id,
  user_id: user.id,
  workflow_id: workflow.id,
  status: 'queued',
  priority: this.calculatePriority(tenant),
  input_params: dto.inputs,
});

// 6. Encolar en Bull
await this.generationQueue.add('generate', {
  jobId: job.id,
  tenantId: tenant.id,
  workflowId: workflow.id,
  params: dto.inputs,
}, {
  priority: job.priority,
  attempts: 3,
  backoff: { type: 'exponential', delay: 5000 },
});

// 7. Incrementar uso
await this.quotaService.increment(tenant.id, 'generations');

return job;
```

### Flujo: Procesar Job (Worker)

```typescript
@Process('generate')
async handleGeneration(bullJob: Job<JobData>): Promise<void> {
  const { jobId, tenantId, workflowId, params } = bullJob.data;

  try {
    // 1. Marcar como procesando
    await this.updateStatus(jobId, 'processing');
    this.gateway.emit(jobId, 'started');

    // 2. Construir workflow ComfyUI
    const workflow = await this.buildComfyWorkflow(workflowId, params);

    // 3. Enviar a ComfyUI
    const promptId = await this.comfyUI.queuePrompt(workflow);

    // 4. Monitorear progreso
    await this.monitorProgress(promptId, jobId);

    // 5. Descargar resultados
    const images = await this.downloadResults(promptId);

    // 6. Crear assets
    const assetIds = await this.createAssets(tenantId, jobId, images);

    // 7. Completar job
    await this.completeJob(jobId, assetIds);
    this.gateway.emit(jobId, 'completed', { assets: assetIds });

  } catch (error) {
    await this.failJob(jobId, error.message);
    this.gateway.emit(jobId, 'failed', { error: error.message });
    throw error;
  }
}
```

---

## Rate Limiting

### Limites por Plan

```yaml
Starter:
  generaciones_hora: 10
  generaciones_mes: 100
  entrenamiento_mes: 0
  modelos_custom: 0

Pro:
  generaciones_hora: 50
  generaciones_mes: 1000
  entrenamiento_mes: 5
  modelos_custom: 10

Business:
  generaciones_hora: 200
  generaciones_mes: 10000
  entrenamiento_mes: 20
  modelos_custom: 50

Enterprise:
  generaciones_hora: unlimited
  generaciones_mes: unlimited
  entrenamiento_mes: unlimited
  modelos_custom: unlimited
```

### Implementacion

```typescript
// Usar @CATALOG_RATELIMIT
@UseGuards(TenantThrottlerGuard)
@Throttle({ default: { ttl: 3600, limit: 50 } }) // Por defecto, ajustado por plan
@Post('jobs')
async createJob(...) { ... }
```

---

## Modelos Personalizados (LoRAs)

### Estructura de Almacenamiento

```
storage/
└── {tenant_slug}/
    └── models/
        ├── loras/
        │   └── {lora_name}.safetensors
        ├── checkpoints/
        └── embeddings/
```

### Registro de Modelo

```typescript
interface RegisterModelDto {
  name: string;
  type: 'lora' | 'checkpoint' | 'embedding';
  file: Express.Multer.File;
  trigger_word: string; // Para LoRAs
  brand_id?: string;
  preview_images?: Express.Multer.File[];
}

// Validaciones
- Tamano maximo: 2GB
- Formatos: .safetensors, .ckpt (solo checkpoint)
- Nombre unico por tenant
```

### Carga Dinamica en ComfyUI

```typescript
// Al construir workflow, si hay lora_id
if (params.lora_id) {
  const model = await this.modelService.findById(params.lora_id);

  // Agregar nodo LoraLoader al workflow
  workflow['lora_loader'] = {
    class_type: 'LoraLoader',
    inputs: {
      model: ['checkpoint', 0],
      clip: ['checkpoint', 1],
      lora_name: model.file_path,
      strength_model: params.lora_strength || 0.8,
      strength_clip: params.lora_strength || 0.8,
    }
  };

  // Redirigir conexiones
  workflow['sampler'].inputs.model = ['lora_loader', 0];
}
```

---

## Eventos WebSocket

### Eventos Emitidos

```typescript
// Suscripcion
socket.emit('subscribe:job', jobId);

// Eventos recibidos
socket.on('generation:started', ({ jobId }) => {});
socket.on('generation:progress', ({ jobId, progress, step, total }) => {});
socket.on('generation:completed', ({ jobId, assets }) => {});
socket.on('generation:failed', ({ jobId, error }) => {});
```

### Formato de Eventos

```typescript
interface ProgressEvent {
  jobId: string;
  progress: number; // 0-100
  step: number;
  total: number;
  message?: string; // "Loading model...", "Sampling...", etc
}

interface CompletedEvent {
  jobId: string;
  assets: string[]; // Asset IDs
  duration: number; // milliseconds
}

interface FailedEvent {
  jobId: string;
  error: string;
  retryable: boolean;
}
```

---

## Manejo de Errores

### Errores Comunes

| Error | Causa | Accion |
|-------|-------|--------|
| QUOTA_EXCEEDED | Limite mensual alcanzado | Mostrar mensaje, sugerir upgrade |
| COMFYUI_UNAVAILABLE | Servidor no responde | Reintentar 3 veces, luego fallar |
| MODEL_NOT_FOUND | LoRA no existe | Verificar modelo antes de enviar |
| INVALID_WORKFLOW | Workflow corrupto | Notificar admin |
| OUT_OF_MEMORY | GPU sin memoria | Reducir batch o esperar |

### Estrategia de Reintentos

```typescript
// Configuracion Bull
{
  attempts: 3,
  backoff: {
    type: 'exponential',
    delay: 5000, // 5s, 10s, 20s
  },
  removeOnComplete: 100,
  removeOnFail: 50,
}
```

---

## Validaciones Obligatorias

### Antes de Crear Job

- [ ] Usuario autenticado
- [ ] Usuario pertenece al tenant
- [ ] Tenant tiene quota disponible
- [ ] Workflow existe y esta activo
- [ ] Inputs validos segun schema
- [ ] LoRA existe si se especifica
- [ ] Brand existe si se especifica

### Despues de Completar Job

- [ ] Imagenes descargadas correctamente
- [ ] Assets creados en storage
- [ ] Assets registrados en BD
- [ ] Job actualizado a "completed"
- [ ] Evento emitido via WebSocket
- [ ] Quota incrementada

---

## Referencias

- Definicion modulo: `docs/02-definicion-modulos/PMC-004-GENERATION.md`
- Requerimientos: `docs/03-requerimientos/RF-PMC-004-GENERATION.md`
- User Stories: `docs/05-user-stories/EPIC-004-GENERATION.md`
- ADR Motor: `docs/97-adr/ADR-003-motor-generacion.md`
- ADR Cola: `docs/97-adr/ADR-004-cola-tareas.md`
- Prompt Agente: `orchestration/prompts/PROMPT-GENERATION-PMC.md`
- Catalogo RateLimit: `shared/catalog/rate-limiting/`
- Catalogo WebSocket: `shared/catalog/websocket/`

---

**Generado por:** Requirements-Analyst
**Fecha:** 2025-12-08
