From 68b379ca13832e51ccdb781a167e12d027a57a4a Mon Sep 17 00:00:00 2001 From: Luis Esteban Acevedo Ladino Date: Wed, 22 Apr 2026 13:27:10 -0500 Subject: [PATCH] chore: initial NeuralCut setup - Add propuesta_tecnica.md with project architecture and plan - Fix .dockerignore to exclude nested node_modules (**/node_modules) - Update bun.lock (opencut-wasm from local to npm, @types/bun bump) --- .dockerignore | 1 + bun.lock | 10 +- propuesta_tecnica.md | 1018 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1024 insertions(+), 5 deletions(-) create mode 100644 propuesta_tecnica.md diff --git a/.dockerignore b/.dockerignore index 25735115..ace76847 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ node_modules +**/node_modules .next .git .gitignore diff --git a/bun.lock b/bun.lock index acb2f6e8..bce5233b 100644 --- a/bun.lock +++ b/bun.lock @@ -54,7 +54,7 @@ "nanoid": "^5.1.5", "next": "16.1.3", "next-themes": "^0.4.4", - "opencut-wasm": "file:../../rust/wasm/pkg", + "opencut-wasm": "^0.2.5", "pg": "^8.16.2", "postgres": "^3.4.5", "radix-ui": "^1.4.3", @@ -785,7 +785,7 @@ "@turbo/windows-arm64": ["@turbo/windows-arm64@2.8.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-voicVULvUV5yaGXo0Iue13BcHGYW3u0VgqSbfQwBaHbpj1zLjYV4KIe+7fYIo6DO8FVUJzxFps3ODCQG/Wy2Qw=="], - "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], "@types/culori": ["@types/culori@4.0.1", "", {}, "sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ=="], @@ -875,7 +875,7 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -1359,6 +1359,8 @@ "onnxruntime-web": ["onnxruntime-web@1.22.0-dev.20250409-89f8206ba4", "", { "dependencies": { "flatbuffers": "^25.1.24", "guid-typescript": "^1.0.9", "long": "^5.2.3", "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4", "platform": "^1.3.6", "protobufjs": "^7.2.4" } }, "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ=="], + "opencut-wasm": ["opencut-wasm@0.2.9", "", {}, "sha512-yZilhRRgNA02XY9Bq2yt6FidbnDQS1OYsvtNczxF6jL+nvl3vRboG0owwpQY0SPIbeJoJjJBuOVy0i1Pp6JS/w=="], + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], @@ -1721,8 +1723,6 @@ "@node-minify/core/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], - "@opencut/web/opencut-wasm": ["opencut-wasm@file:rust/wasm/pkg", {}], - "@opencut/web/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "@opennextjs/aws/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], diff --git a/propuesta_tecnica.md b/propuesta_tecnica.md new file mode 100644 index 00000000..715abfed --- /dev/null +++ b/propuesta_tecnica.md @@ -0,0 +1,1018 @@ +# Propuesta Técnica — Agente Conversacional para Edición de Video + +**Proyecto Expoandes | Introducción a la Ingeniería de Sistemas y Computación** + +--- + +## 0. Tabla de contenidos + +1. Resumen del proyecto +2. Problema y solución +3. Diferenciador competitivo +4. Arquitectura general +5. Stack tecnológico completo +6. Capa de abstracción de proveedores (clave) +7. Detalle de tools del agente (skills) +8. Flujos de usuario end-to-end +9. Plan de desarrollo (Scrum) +10. Consideraciones de performance y costos +11. Estrategia de demo para feria +12. Riesgos y mitigaciones +13. Apéndices técnicos + +--- + +## 1. Resumen del proyecto + +**Nombre tentativo:** [Por definir] + +**Una línea:** Un editor de video web con un agente conversacional que entiende lenguaje natural y edita por ti. *Lo que Cursor es a VSCode, esto es a CapCut.* + +**Entregable:** MVP funcional desplegado en web, con un agente capaz de ejecutar al menos 8 "skills" de edición a partir de instrucciones en lenguaje natural. + +--- + +## 2. Problema y solución + +### Problema identificado + +Los creadores de contenido pequeños y medianos (YouTubers, tiktokers, podcasters, educadores, periodistas estudiantes) invierten entre 3 y 5 horas editando cada pieza de contenido. La mayor parte de ese tiempo se consume en tareas mecánicas: + +- Cortar silencios y muletillas ("eh", "emm", "o sea") +- Encontrar los mejores momentos en grabaciones largas +- Generar subtítulos con estilo +- Reformatear entre aspect ratios (horizontal → vertical) +- Aplicar corrección de color básica +- Sincronizar cortes con música + +Las herramientas actuales resuelven piezas aisladas pero todas exigen aprender una interfaz compleja y ejecutar cada acción manualmente. + +### Solución + +Un editor de video con un **agente conversacional autónomo** que ejecuta estas tareas por instrucción en lenguaje natural, pero mantiene siempre la edición manual tradicional como respaldo. + +**Ejemplo concreto:** + +> *Usuario:* "Tengo un video de 5 minutos de mí explicando el proyecto. Hazme un reel de 30 segundos con los mejores momentos, subtítulos amarillos estilo TikTok, y formato vertical siguiendo mi cara." +> +> *Agente:* [transcribe → analiza escenas → identifica momentos de alta energía → corta → reencuadra dinámicamente → renderiza subtítulos → entrega] + +--- + +## 3. Diferenciador competitivo + +| Producto | Edición manual | Agente conversacional | Multimodal | Open source base | +|---|---|---|---|---| +| CapCut / Premiere | ✅ | ❌ | ❌ | ❌ | +| Descript | ⚠️ (via transcripción) | ❌ | ❌ | ❌ | +| Opus Clip | ❌ | ⚠️ (automático fijo) | ⚠️ | ❌ | +| Runway | ⚠️ | ❌ | ✅ | ❌ | +| DeeVid.ai / Sora | ❌ | ❌ | ✅ (genera) | ❌ | +| **Nuestra propuesta** | ✅ | ✅ | ✅ | ✅ | + +**El moat técnico:** esto no es una caja negra que genera contenido y ya — es un **editor de video completo** donde el usuario mantiene control total. Lo que Cursor es a VSCode, NeuralCut es a CapCut: un agente autónomo que opera sobre una herramienta real, no un generador sin ventana de edición. El usuario puede hablarle en lenguaje natural Y editar manualmente en la misma interfaz. + +--- + +## 4. Arquitectura general + +``` +╔══════════════════════════════════════════════════════════╗ +║ NAVEGADOR DEL USUARIO ║ +║ ║ +║ ┌────────────────────────┐ ┌────────────────────────┐ ║ +║ │ UI NeuralCut (web) │ │ Panel de chat nuevo │ ║ +║ │ │ │ │ ║ +║ │ • Timeline multi-pista│ │ • Historial de chat │ ║ +║ │ • Preview │ │ • Input de texto │ ║ +║ │ • Media bin │ │ • Streaming response │ ║ +║ │ • Efectos │ │ • Approvals inline │ ║ +║ └───────────┬────────────┘ └───────────┬────────────┘ ║ +║ │ │ ║ +║ └─────────────┬───────────────┘ ║ +║ ▼ ║ +║ ┌───────────────────────────────────────────────────┐ ║ +║ │ ZUSTAND STORES (estado único) │ ║ +║ │ editorStore · timelineStore · mediaStore │ ║ +║ │ playbackStore · chatStore · agentStore │ ║ +║ └───────────────────────────────────────────────────┘ ║ +║ │ ║ +║ ┌──────────────────┼──────────────────┐ ║ +║ ▼ ▼ ▼ ║ +║ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ║ +║ │ FFmpeg.wasm │ │ MediaBunny │ │ WebCodecs │ ║ +║ │ (operaciones │ │ (extracción │ │ (decode/ │ ║ +║ │ complejas) │ │ rápida) │ │ encode) │ ║ +║ └──────────────┘ └──────────────┘ └──────────────┘ ║ +║ ║ +║ ┌───────────────────────────────────────────────────┐ ║ +║ │ STORAGE: IndexedDB + OPFS (ya en NeuralCut) │ ║ +║ └───────────────────────────────────────────────────┘ ║ +╚══════════════════════════════════════════════════════════╝ + │ + ▼ (HTTPS) +╔══════════════════════════════════════════════════════════╗ +║ CAPA DEL AGENTE (TypeScript — apps/web/) ║ +║ ║ +║ Orquestación, tool calling, streaming, estado: ║ +║ ║ +║ agent/orchestrator.ts → coordina LLM + tools ║ +║ agent/tools/*.ts → definiciones de cada tool ║ +║ agent/prompts/ → system prompt y templates ║ +║ providers/ → abstracción de proveedores ║ +║ app/api/agent/ → Next.js API routes ║ +║ ║ +║ El agente es I/O-bound: llama APIs, parsea JSON, ║ +║ maneja streaming. No necesita Rust. ║ +╚══════════════════════════════════════════════════════════╝ + │ │ + │ cuando necesita │ siempre + │ procesamiento │ para LLMs + │ pesado │ / APIs + ▼ ▼ +╔════════════════════════╗ ╔══════════════════════════╗ +║ rust/ — PROCESAMIENTO ║ ║ PROVEEDORES EXTERNOS ║ +║ (WASM, CPU-bound) ║ ║ ║ +║ ║ ║ ┌─────────────────────┐ ║ +║ • compositor/ ║ ║ │ LOCAL (dev, gratis) │ ║ +║ • effects/ ║ ║ │ Qwen2.5-VL (Ollama) │ ║ +║ • gpu/ ║ ║ │ whisper.cpp │ ║ +║ • masks/ ║ ║ │ Qwen3-8B │ ║ +║ • time/ ║ ║ └─────────────────────┘ ║ +║ • bridge/ ║ ║ ┌─────────────────────┐ ║ +║ ║ ║ │ CLOUD (demo, rápido)│ ║ +║ Detección de silencios║ ║ │ Gemini 2.5 Flash │ ║ +║ Detección de escenas ║ ║ │ Claude Opus/Haiku │ ║ +║ Face tracking ║ ║ │ Whisper API │ ║ +║ Beat detection ║ ║ └─────────────────────┘ ║ +╚════════════════════════╝ ╚══════════════════════════╝ +``` + +--- + +## 5. Stack tecnológico completo + +### 5.1. Frontend y base del editor + +| Tecnología | Rol | Justificación | +|---|---|---| +| Fork de OpenCut (NeuralCut) | Base del editor | CapCut open source (MIT, 48k★). Timeline, preview, storage ya funcionan. Nos permite enfocarnos en el agente. | +| **Next.js 16** | Framework React | Ya viene en el fork. App Router, API routes, deploy trivial en Vercel. | +| **React 19** | UI | Estándar. | +| **Zustand** | State management | Ya viene en OpenCut. Simple y reactivo. | +| **Tailwind CSS** | Styling | Ya viene en OpenCut. | +| **shadcn/ui** | Componentes UI | Para el panel de chat nuevo. | +| **Bun** | Runtime / package manager | Ya viene en OpenCut. 3-5x más rápido que npm. | + +### 5.1.1. Decisión arquitectónica: TypeScript para el agente, Rust para procesamiento + +El agente conversacional (orquestación, tool calling, providers, streaming) se implementa en **TypeScript** dentro de `apps/web/src/agent/`. Las razones: + +- **I/O-bound**: el 90% del trabajo del agente es llamar APIs, esperar respuestas y parsear JSON. Rust no agrega valor acá. +- **Ecosistema maduro**: Vercel AI SDK, Anthropic SDK, OpenAI SDK, streaming con `ReadableStream` — todo first-class en TS. En Rust no hay equivalentes. +- **WASM no tiene networking**: para llamar a Gemini o Claude desde Rust+WASM habría que hacer un bridge a JS, lo cual es un roundtrip innecesario. +- **Iteración rápida**: prompts y tool definitions cambian constantemente. Hot reload en TS vs compile time en Rust. + +**Rust (`rust/`)** se reserva exclusivamente para procesamiento **CPU-bound** que corre en el browser via WASM: composición de video, efectos, detección de silencios/escenas, face tracking, beat detection. El agente en TS llama a estas funciones Rust cuando necesita poder de cálculo. + +### 5.2. Procesamiento de video (client-side) + +| Tecnología | Rol | Notas | +|---|---|---| +| **FFmpeg.wasm** | Operaciones complejas (cortes, filtros, export) | Ya integrado en OpenCut. Lento pero universal. | +| **MediaBunny** | Extracción de frames y conversiones rápidas | 5x más rápido que FFmpeg.wasm en muchas operaciones. Remotion lo recomienda oficialmente desde 2025. | +| **WebCodecs API** | Decode/encode con aceleración de hardware | Nativo del browser. Más rápido que cualquier WASM. | + +### 5.3. IA — Capas del agente + +#### Orquestador (LLM principal) + +| Proveedor | Modelo recomendado | Uso | +|---|---|---| +| Anthropic | **Claude Opus 4.7** | Razonamiento complejo, tool calling confiable | +| Anthropic | **Claude Haiku 4.5** | Decisiones simples y rápidas (más barato) | +| OpenAI | GPT-5 | Alternativa | +| Local | Qwen3-8B | Para desarrollo | + +**Estrategia:** usar Haiku para 80% de decisiones (rápidas/simples) y escalar a Opus solo para razonamiento complejo. + +#### Video Understanding (la killer feature) + +| Proveedor | Modelo | Cuándo usarlo | +|---|---|---| +| **Google** | **Gemini 2.5 Flash** | Producción/demo. Procesa hasta 6h de video con timestamps nativos. | +| **Google** | Gemini 2.5 Pro | Casos complejos. Más caro. | +| **Local** | **Qwen2.5-VL-7B** | Desarrollo local. Gratis. Entiende video con timestamps nativamente. | +| **Local** | Qwen2.5-VL-3B | Laptops con menos recursos. | + +#### Transcripción (Speech-to-Text) + +| Proveedor | Modelo | Notas | +|---|---|---| +| **OpenAI** | **Whisper API** | $0.006/min. Más barato. Mejor accuracy en benchmarks. | +| OpenAI | gpt-4o-transcribe | Más preciso, más caro | +| AssemblyAI | Universal-2 | Mejor formateo, diarización incluida | +| **Local** | **whisper.cpp** | Gratis, corre en CPU decentemente | +| Local | faster-whisper | Más rápido en GPU | + +#### Visión computacional (detección) + +| Tecnología | Rol | +|---|---| +| **MediaPipe Tasks for Web** | Detección de caras, pose, manos — corre en el browser | +| ONNX Runtime Web + YOLO | Detección de objetos general | +| TransNetV2 | Detección avanzada de transiciones de escena | + +#### Audio + +| Tecnología | Rol | +|---|---| +| **Web Audio API** | Análisis nativo (RMS, silencios) | +| **Essentia.js** | Detección de beats, tempo, tonalidad | +| FFmpeg silencedetect | Detección alternativa de silencios | + +### 5.4. Backend y deploy + +| Tecnología | Rol | +|---|---| +| **Next.js API Routes** | Proxy a LLMs, no lógica pesada | +| **Vercel Edge Functions** | Deploy gratis, global | +| **Vercel** | Hosting | + +### 5.5. Storage + +| Tecnología | Rol | +|---|---| +| **IndexedDB + OPFS** | Primary (ya en OpenCut) — storage local persistente | +| Cloudflare R2 / Supabase | Solo para compartir exports (opcional) | + +--- + +## 6. Capa de abstracción de proveedores (la joya del diseño) + +**Objetivo:** poder alternar entre modelos locales (dev, gratis) y APIs (producción, rápido) **sin cambiar código**. Solo cambiando una variable de entorno. + +### 6.1. Interfaces abstractas + +```typescript +// Interfaz común para todos los proveedores de visión multimodal +interface VisionProvider { + analyzeVideo( + videoPath: string, + prompt: string, + options?: { maxTokens?: number; temperature?: number } + ): Promise<{ + response: string; + timestamps?: Array<{ time: string; description: string }>; + usage: { inputTokens: number; outputTokens: number; cost: number }; + }>; + + analyzeImages( + images: string[], + prompt: string + ): Promise<{ response: string; usage: Usage }>; +} + +// Interfaz común para STT +interface TranscriptionProvider { + transcribe( + audioPath: string, + options?: { language?: string; wordTimestamps?: boolean } + ): Promise<{ + text: string; + segments: Array<{ start: number; end: number; text: string }>; + words?: Array<{ word: string; start: number; end: number }>; + }>; +} + +// Interfaz común para el LLM orquestador +interface LLMProvider { + chat( + messages: Message[], + tools?: Tool[], + options?: ChatOptions + ): AsyncGenerator; +} +``` + +### 6.2. Implementaciones intercambiables + +```typescript +// Cada proveedor implementa la misma interfaz +class GeminiVisionProvider implements VisionProvider { ... } +class QwenLocalVisionProvider implements VisionProvider { ... } +class ClaudeVisionProvider implements VisionProvider { ... } + +class WhisperAPIProvider implements TranscriptionProvider { ... } +class WhisperLocalProvider implements TranscriptionProvider { ... } +class AssemblyAIProvider implements TranscriptionProvider { ... } + +class ClaudeLLMProvider implements LLMProvider { ... } +class OpenAILLMProvider implements LLMProvider { ... } +class OllamaLLMProvider implements LLMProvider { ... } +``` + +### 6.3. Factory y configuración + +```typescript +// providers/factory.ts +export function getVisionProvider(): VisionProvider { + const provider = process.env.VISION_PROVIDER || 'local'; + + switch (provider) { + case 'gemini': + return new GeminiVisionProvider({ + apiKey: process.env.GEMINI_API_KEY!, + model: 'gemini-2.5-flash' + }); + case 'claude': + return new ClaudeVisionProvider({ + apiKey: process.env.ANTHROPIC_API_KEY!, + model: 'claude-opus-4-7' + }); + case 'local': + return new QwenLocalVisionProvider({ + endpoint: process.env.LOCAL_MODEL_ENDPOINT || 'http://localhost:11434', + model: 'qwen2.5vl:7b' + }); + default: + throw new Error(`Unknown vision provider: ${provider}`); + } +} +``` + +### 6.4. Uso desde el resto del código + +```typescript +// El resto del código NUNCA sabe qué proveedor está usando +import { getVisionProvider } from '@/providers'; + +async function findFunnyMoments(videoPath: string) { + const vision = getVisionProvider(); // abstracto + const result = await vision.analyzeVideo( + videoPath, + "Find all timestamps where someone is laughing" + ); + return result.timestamps; +} +``` + +### 6.5. Configuración por ambiente + +```bash +# .env.development (día a día de desarrollo) +VISION_PROVIDER=local +TRANSCRIPTION_PROVIDER=local +LLM_PROVIDER=local +LOCAL_MODEL_ENDPOINT=http://localhost:11434 + +# .env.demo (para feria y demos) +VISION_PROVIDER=gemini +TRANSCRIPTION_PROVIDER=whisper-api +LLM_PROVIDER=claude +GEMINI_API_KEY=... +OPENAI_API_KEY=... +ANTHROPIC_API_KEY=... +``` + +**Resultado:** cambias una variable, todo el código se adapta solo. 90% del desarrollo es gratis, demos finales usan APIs top. + +--- + +## 7. Detalle de tools del agente + +Cada tool es una función pura que el LLM puede invocar. Se definen con JSON Schema para que el LLM sepa cuándo llamarlas. + +### Tier 1 — Fundamentales (MVP must-have) + +#### 7.1. `transcribe_video` +```typescript +{ + name: "transcribe_video", + description: "Transcribe the audio of a video to text with timestamps", + input: { video_id: string, language?: string }, + output: { + segments: [{ start: number, end: number, text: string }], + words: [{ word: string, start: number, end: number }] + } +} +``` +**Implementación (TS → Whisper API o whisper.cpp local):** el agente en TS llama al `TranscriptionProvider` que abstrae si es API o local. + +#### 7.2. `detect_silences` +```typescript +{ + name: "detect_silences", + description: "Find silent parts in audio (for auto-cutting)", + input: { video_id: string, threshold_db?: number, min_duration?: number }, + output: { silences: [{ start: number, end: number, duration: number }] } +} +``` +**Implementación (TS → Web Audio API o Rust WASM):** análisis de amplitud en browser, o detección precisa via WASM para archivos grandes. + +#### 7.3. `detect_scenes` +```typescript +{ + name: "detect_scenes", + description: "Split video into semantic scenes based on visual changes", + input: { video_id: string, threshold?: number }, + output: { scenes: [{ start: number, end: number, id: string }] } +} +``` +**Implementación (TS → FFmpeg o Rust WASM):** frame diff analysis. FFmpeg scene filter para rápido, Rust WASM para precisión. + +#### 7.4. `cut_segment` +```typescript +{ + name: "cut_segment", + description: "Cut a segment from the video and add it to the timeline", + input: { video_id: string, start: number, end: number, track?: number }, + output: { clip_id: string, position: number } +} +``` +**Implementación (TS → Zustand store):** manipula el `timelineStore` de NeuralCut directamente desde el agente en TS. + +#### 7.5. `concat_segments` +```typescript +{ + name: "concat_segments", + description: "Concatenate multiple segments in the timeline", + input: { clip_ids: string[] }, + output: { composite_id: string, total_duration: number } +} +``` + +### Tier 2 — Visión y comprensión + +#### 7.6. `watch_video` +```typescript +{ + name: "watch_video", + description: "Ask a question about the visual content of a video. Returns answer with relevant timestamps.", + input: { video_id: string, question: string }, + output: { + answer: string, + relevant_timestamps: [{ time: string, reason: string }] + } +} +``` +**Implementación:** Gemini 2.5 Flash (o Qwen2.5-VL local). **Esta es la killer feature.** Gemini soporta context caching nativo: el video se sube una vez al importar al proyecto, y todas las queries posteriores contra ese video son instantáneas y más baratas. No hay re-upload. + +#### 7.7. `take_screenshot` +```typescript +{ + name: "take_screenshot", + description: "Extract a frame from the video at a specific timestamp", + input: { video_id: string, timestamp: number }, + output: { image_path: string, description?: string } +} +``` +**Implementación:** MediaBunny o WebCodecs. + +#### 7.8. `describe_scene` +```typescript +{ + name: "describe_scene", + description: "Get a text description of what's happening in a scene", + input: { video_id: string, start: number, end: number }, + output: { description: string, objects: string[], actions: string[] } +} +``` + +### Tier 3 — Operaciones creativas + +#### 7.9. `generate_subtitles` +```typescript +{ + name: "generate_subtitles", + description: "Generate and render styled subtitles", + input: { + video_id: string, + style: "tiktok" | "youtube" | "clean" | "cinematic", + language?: string, + color?: string + }, + output: { subtitle_track_id: string } +} +``` + +#### 7.10. `apply_lut` +```typescript +{ + name: "apply_lut", + description: "Apply a color grading LUT to make video look more cinematic", + input: { + clip_id: string, + mood: "warm" | "cool" | "cinematic" | "vintage" | "bright" | "moody" + }, + output: { effect_id: string } +} +``` + +#### 7.11. `detect_faces` +```typescript +{ + name: "detect_faces", + description: "Detect faces in video with bounding boxes per frame", + input: { video_id: string, start?: number, end?: number }, + output: { faces_per_frame: [{ frame: number, boxes: BoundingBox[] }] } +} +``` +**Implementación (TS → MediaPipe Tasks for Web):** corre en el browser, el agente en TS orquesta las llamadas. + +#### 7.12. `auto_reframe` +```typescript +{ + name: "auto_reframe", + description: "Convert video to a different aspect ratio tracking the main subject", + input: { + video_id: string, + target_ratio: "9:16" | "1:1" | "4:5" | "16:9", + tracking: "face" | "object" | "center" + }, + output: { reframed_clip_id: string } +} +``` + +#### 7.13. `detect_beats` +```typescript +{ + name: "detect_beats", + description: "Detect musical beats in an audio track", + input: { audio_id: string }, + output: { beats: number[], bpm: number } +} +``` +**Implementación (TS → Essentia.js):** corre en el browser via Web Worker para no bloquear el main thread. + +### Tier 4 — Inteligencia de alto nivel + +#### 7.14. `suggest_highlights` +```typescript +{ + name: "suggest_highlights", + description: "AI-powered analysis to find the most engaging moments in a video", + input: { + video_id: string, + target_duration: number, + criteria?: "energy" | "humor" | "information" | "visual" + }, + output: { + highlights: [{ + start: number, + end: number, + score: number, + reason: string + }] + } +} +``` +**Este es el pipeline más impresionante.** Combina: +- Transcripción (palabras emocionales) +- Análisis de audio (picos de volumen, risas) +- Análisis de escena (cambios, caras, acción) +- LLM que rankea todo + +#### 7.15. `remove_filler_words` +```typescript +{ + name: "remove_filler_words", + description: "Remove 'um', 'uh', 'like', and other filler words from video", + input: { video_id: string, language?: string }, + output: { cuts_made: number, total_time_saved: number } +} +``` + +--- + +## 8. Flujos de usuario end-to-end + +### Flujo 1: Reel automático + +``` +Usuario sube video de 5 min → "hazme un reel vertical de 30s con +los mejores momentos y subtítulos amarillos" + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Agente razona: │ +│ "Necesito: analizar video, cortar momentos, reframe, │ +│ subtítulos" │ +└──────────────────────────────────────────────────────────┘ + │ + ▼ +1. transcribe_video() ────────► transcripción con timestamps + │ + ▼ +2. detect_scenes() ───────────► 12 escenas identificadas + │ + ▼ +3. suggest_highlights(30s) ───► Top 5 momentos rankeados + │ + ▼ +4. Para cada highlight: + - cut_segment(start, end) + │ + ▼ +5. concat_segments(ids) ──────► Reel de ~30s + │ + ▼ +6. auto_reframe("9:16", face) ► Vertical con tracking + │ + ▼ +7. generate_subtitles(tiktok, yellow) ► Subs estilizados + │ + ▼ +Preview renderizado → usuario aprueba → export final +``` + +### Flujo 2: Pregunta abierta + +``` +Usuario: "¿En qué parte se ve mejor mi iluminación?" + │ + ▼ +Agente llama: watch_video(question="best lighting") + │ + ▼ +Gemini 2.5 / Qwen ve el video, responde: +"Entre 0:23 y 0:31 hay buena iluminación frontal natural. +Después de 2:45 la luz se vuelve amarillenta." + │ + ▼ +Agente presenta respuesta + timestamps + screenshots +``` + +### Flujo 3: Edición conversacional iterativa + +``` +Usuario: "hazme subtítulos" +Agente: generate_subtitles(default) → listo + +Usuario: "más grandes y amarillos" +Agente: actualiza estilo del subtitle_track_id existente + +Usuario: "quita la parte donde me equivoco" +Agente: watch_video("where does the person mess up?") + → identifica timestamp 1:23-1:31 + → pide confirmación + → cut_segment para remover +``` + +--- + +## 9. Plan de desarrollo (Scrum) + +### Equipo y roles + +- **Scrum Master:** coordina reuniones, remueve bloqueos +- **Product Owner:** prioriza backlog, dueño de la visión +- **Dev Frontend (2):** UI, NeuralCut, panel de chat +- **Dev Backend/IA (1-2):** agente (TS), tools, capa de proveedores + +### Sprints propuestos (asumiendo ~14 semanas) + +| Sprint | Duración | Objetivo | Entregable | +|---|---|---|---| +| **0** | 1 semana | Setup + aprendizaje | NeuralCut corriendo local. Ollama + Qwen funcionando. Cada dev hizo "hola mundo" con APIs. | +| **1** | 1 semana | Empatizar | 8-10 entrevistas a creadores de contenido. Mapa de empatía. Declaración del problema validada. | +| **2** | 1 semana | Ideación + diseño | Wireframes del panel de chat. Diseño de la capa de abstracción. Decisión de scope de tools. | +| **3** | 1 semana | Infraestructura | Capa de abstracción implementada. Panel de chat UI (sin lógica). 1 tool conectada end-to-end (transcribe). | +| **4** | 1 semana | Tier 1 tools | transcribe, detect_silences, detect_scenes, cut_segment, concat_segments. | +| **5** | 1 semana | Video understanding | watch_video + take_screenshot + describe_scene. Primera demo "wow". | +| **6** | 1 semana | Creative tools | generate_subtitles, apply_lut, detect_faces. | +| **7** | 1 semana | Reframe + beats | auto_reframe (MediaPipe), detect_beats (Essentia). | +| **8** | 1 semana | Pipeline inteligente | suggest_highlights + remove_filler_words. | +| **9** | 1 semana | Refinamiento UX | Streaming de respuestas, aprobaciones inline, undo/redo agéntico. | +| **10** | 1 semana | Testing con usuarios | 5-10 sesiones de usability testing. Ajustes. | +| **11** | 1 semana | Performance + deploy | Optimización, caching, deploy a Vercel. | +| **12** | 1 semana | Preparación feria | Videos de ejemplo pre-procesados, afiche, guión de demo. | +| **13** | 1 semana | Buffer + Post Mortem | Buffer para imprevistos. Reunión Post Mortem. | + +### Backlog priorizado (primeras historias) + +**Must-have (Sprint 3-5):** +- Como usuario, subo un video y el agente me lo transcribe +- Como usuario, le pido cortar un segmento específico por timestamp +- Como usuario, le pido que me identifique las escenas +- Como usuario, le pregunto sobre el contenido del video y me responde + +**Should-have (Sprint 6-8):** +- Como usuario, le pido subtítulos con un estilo específico +- Como usuario, le pido convertir a vertical siguiendo mi cara +- Como usuario, le pido un resumen automático de los mejores momentos + +**Nice-to-have (Sprint 9+):** +- Como usuario, puedo deshacer acciones del agente +- Como usuario, el agente me muestra un preview antes de aplicar cambios grandes +- Como usuario, comparto el proyecto por URL + +--- + +## 10. Consideraciones de performance y costos + +### 10.1. Performance esperado + +| Operación | Tiempo esperado | Optimización | +|---|---|---| +| Transcripción Whisper API (5 min video) | 15-30s | Cachear resultado | +| Gemini 2.5 Flash análisis (2 min video) | 10-20s | Context caching | +| Qwen2.5-VL local (2 min video) | 30-60s | Quantization 4-bit | +| Corte + export FFmpeg.wasm (30s clip) | 10-20s | WebCodecs cuando sea posible | +| Auto-reframe (1 min) | 20-40s | Process en Web Worker | + +### 10.2. Costos de APIs (estimado para todo el semestre) + +| Servicio | Uso estimado | Costo estimado | +|---|---|---| +| Whisper API | 50 horas video | $18 | +| Gemini 2.5 Flash | ~500 análisis | $15-30 | +| Claude Opus | ~2000 mensajes | $20-40 | +| **Total** | | **~$50-90 USD** | + +**Mitigaciones:** +- Usar modelos locales durante desarrollo (90% del tiempo) +- Context caching en Gemini (subir video una vez, preguntar muchas) +- Aprovechar créditos académicos (Anthropic, Google ofrecen) +- Free tier generoso de Google AI Studio + +### 10.3. Límites técnicos + +- **Tamaño máximo de video en browser:** ~500MB (limitación de memoria WebAssembly) +- **Duración máxima recomendada:** 10 minutos por video +- **Exports simultáneos:** 1 por pestaña +- **Contexto LLM:** ~200k tokens suficientes para proyectos típicos + +--- + +## 11. Estrategia de demo para feria + +### 11.1. Setup del stand + +- **Monitor grande** (idealmente 27"+) mostrando la app en Chrome +- **Laptop secundaria** corriendo Qwen local como fallback +- **2-3 celulares** listos para subir videos espontáneamente +- **Videos pre-procesados** listos para casos demo "seguros" +- **Afiche** con arquitectura visual + QR al URL live +- **Hoja de FAQ técnica** para jurados técnicos + +### 11.2. Casos demo (por nivel de engagement) + +#### Quick (30s — para visitantes casuales) +> "Mira, le hablas y edita". Sube video de 30s pre-cargado → "ponme subtítulos amarillos" → listo. + +#### Medium (2 min — para visitantes interesados) +> "Dame tu celular" → graban 30s → "hazme un reel vertical con los mejores momentos" → entrega. + +#### Deep (5 min — para jurados técnicos) +> Explicar: arquitectura, capa de abstracción (mostrar cambio local↔cloud en vivo), tool calling, video understanding. + +### 11.3. Respuestas a preguntas esperadas del jurado + +**"¿Por qué forkear OpenCut en vez de hacerlo desde cero?"** +> Porque el valor diferencial no está en rehacer el timeline, está en el agente. Forkear nos permitió enfocar el 80% del esfuerzo en lo innovador. Es exactamente la misma decisión que Cursor tomó con VSCode. El fork se llama NeuralCut y estamos migrando lógica a Rust para performance. + +**"¿Qué pasa si las APIs fallan?"** +> Diseñamos una capa de abstracción de proveedores. Puedo alternar entre Gemini en la nube y Qwen2.5-VL local sin cambiar código, solo una variable de entorno. [Demostrar en vivo si hay tiempo.] + +**"¿Esto no existe ya?"** +> Hay piezas aisladas: Descript edita por transcripción, Opus Clip hace clips automáticos, Runway hace efectos. Nadie tiene un agente conversacional completo sobre un editor tradicional. Es la diferencia entre "feature de IA" y "agente autónomo". + +**"¿Cómo se monetiza?"** +> No es foco del proyecto académico, pero el modelo natural sería freemium: local y funciones básicas gratis, APIs premium con suscripción. El usuario objetivo (creadores pequeños) ya paga $10-20/mes por herramientas. + +**"¿Cuánto cuesta procesarar un video?"** +> Entre $0.05 y $0.15 por video de 5 minutos usando APIs de producción. Local es gratis pero más lento. + +--- + +## 12. Riesgos y mitigaciones + +| Riesgo | Probabilidad | Impacto | Mitigación | +|---|---|---|---| +| Curva de aprendizaje del codebase de NeuralCut | Alta | Alto | Sprint 0 dedicado exclusivamente a exploración | +| APIs fallan durante demo | Media | Alto | Fallback a modelos locales (capa de abstracción) | +| Videos grandes crashean el browser | Alta | Medio | Limitar a <10 min, mostrar warnings, pre-comprimir | +| Modelo local muy lento en laptops del equipo | Media | Medio | Quantization 4-bit, usar versión 3B en lugar de 7B | +| Costos de APIs se salen de presupuesto | Baja | Medio | Dashboard de monitoring, límites por usuario, caching agresivo | +| Alguien del equipo se enferma/sale | Media | Alto | Documentación exhaustiva, pair programming | +| Demasiado scope | Alta | Alto | Scope definido por tiers — tier 1 es MVP, tier 4 es stretch | +| Tool calling poco confiable con modelos locales | Media | Medio | Testear con Gemini/Claude en demo, fallback a respuestas pre-armadas | + +--- + +## 13. Apéndices técnicos + +### A. Setup de desarrollo paso a paso + +```bash +# 1. Clonar NeuralCut (nuestro fork) +git clone NeuralCut +cd NeuralCut + +# 2. Instalar dependencias +bun install + +# 3. Configurar entorno local +cp apps/web/.env.example apps/web/.env.local + +# 4. Levantar Docker (Postgres + Redis) +docker compose up -d + +# 5. Correr la app (monorepo con Turbo) +bun dev:web +# → http://localhost:3000 + +# 6. (Opcional) Instalar Ollama para modelos locales +# Mac: brew install ollama +# Linux: curl -fsSL https://ollama.com/install.sh | sh +ollama pull qwen2.5vl:7b +ollama pull qwen3:8b # para orquestador liviano +``` + +### B. Estructura de carpetas propuesta + +``` +NeuralCut/ +├── rust/ # Procesamiento pesado (CPU-bound) +│ ├── crates/ +│ │ ├── bridge/ # Puente Rust ↔ JS (WASM bindings) +│ │ ├── compositor/ # Composición de video +│ │ ├── effects/ # Filtros y efectos +│ │ ├── gpu/ # Aceleración GPU +│ │ ├── masks/ # Mascaramiento +│ │ └── time/ # Manipulación de tiempo +│ └── wasm/ # WASM build target +│ +├── apps/ +│ ├── web/ # UI shell + agente (Next.js 16 + React 19) +│ │ ├── src/ +│ │ │ ├── app/ +│ │ │ │ ├── api/ +│ │ │ │ │ ├── agent/chat/route.ts # NUEVO: endpoint del agente (streaming) +│ │ │ │ │ └── tools/ # NUEVO: endpoints de tools +│ │ │ │ │ ├── transcribe/route.ts +│ │ │ │ │ ├── analyze/route.ts +│ │ │ │ │ └── highlights/route.ts +│ │ │ │ └── editor/ # UI del editor (existente) +│ │ │ │ +│ │ │ ├── components/ +│ │ │ │ ├── chat/ # NUEVO: panel de chat +│ │ │ │ │ ├── ChatPanel.tsx +│ │ │ │ │ ├── MessageList.tsx +│ │ │ │ │ ├── InputArea.tsx +│ │ │ │ │ └── ApprovalPrompt.tsx +│ │ │ │ ├── timeline/ # existente +│ │ │ │ └── preview/ # existente (NO TOCAR) +│ │ │ │ +│ │ │ ├── agent/ # NUEVO: lógica del agente (TS) +│ │ │ │ ├── orchestrator.ts # loop principal: LLM → tool → LLM +│ │ │ │ ├── tools/ # definiciones de tools + ejecutores +│ │ │ │ │ ├── index.ts # registro de todas las tools +│ │ │ │ │ ├── transcribe.ts # → llama TranscriptionProvider +│ │ │ │ │ ├── detectSilences.ts # → Web Audio API o Rust (WASM) +│ │ │ │ │ ├── detectScenes.ts # → FFmpeg o Rust (WASM) +│ │ │ │ │ ├── cutSegment.ts # → Zustand store manipulation +│ │ │ │ │ ├── watchVideo.ts # → llama VisionProvider +│ │ │ │ │ ├── generateSubtitles.ts # → TranscriptionProvider + render +│ │ │ │ │ └── ... +│ │ │ │ └── prompts/ +│ │ │ │ └── system.ts # system prompt template +│ │ │ │ +│ │ │ ├── providers/ # NUEVO: capa de abstracción +│ │ │ │ ├── vision/ +│ │ │ │ │ ├── types.ts +│ │ │ │ │ ├── gemini.ts +│ │ │ │ │ ├── qwen-local.ts +│ │ │ │ │ └── index.ts # factory +│ │ │ │ ├── transcription/ +│ │ │ │ │ ├── whisper-api.ts +│ │ │ │ │ ├── whisper-local.ts +│ │ │ │ │ └── index.ts +│ │ │ │ └── llm/ +│ │ │ │ ├── claude.ts +│ │ │ │ ├── openai.ts +│ │ │ │ ├── ollama.ts +│ │ │ │ └── index.ts +│ │ │ │ +│ │ │ ├── stores/ # extiende stores existentes +│ │ │ │ ├── ... (existentes) +│ │ │ │ ├── chatStore.ts # NUEVO +│ │ │ │ └── agentStore.ts # NUEVO +│ │ │ │ +│ │ │ └── lib/ +│ │ │ ├── ffmpeg.ts # wrapper sobre ffmpeg.wasm +│ │ │ ├── mediabunny.ts # wrapper sobre MediaBunny +│ │ │ └── video-utils.ts +│ │ │ +│ │ └── package.json +│ │ +│ └── desktop/ # UI shell (GPUI, futuro) +│ +├── package.json # workspace root +├── turbo.json +└── docker-compose.yml +``` + +**Regla de separación Rust ↔ TypeScript:** + +- **`rust/`** = CPU-bound: composición, efectos, GPU, detección de silencios/escenas, face tracking, beat detection. Compilado a WASM, llamado desde TS cuando se necesita poder de cálculo. +- **`apps/web/src/agent/`** = I/O-bound: orquestación del LLM, tool calling, streaming, manejo de estado del chat, definición de tools, provider abstraction. Todo lo que implica llamar APIs, parsear JSON, o manipular el Zustand store va acá. +- **`apps/web/src/providers/`** = integración con APIs externas (Gemini, Claude, Whisper, Ollama). Nunca en Rust porque WASM no tiene networking nativo. +- **`apps/web/src/components/`** = UI pura. Solo renderiza lo que los stores dictan. + +### C. System prompt del agente (borrador) + +``` +Eres un asistente experto en edición de video. Tu trabajo es entender +lo que el usuario quiere hacer con su video y usar las herramientas +disponibles para ejecutarlo. + +PRINCIPIOS: +1. Antes de hacer cambios grandes, muestra un preview o pide confirmación +2. Si no tienes suficiente contexto, usa watch_video o describe_scene +3. Encadena tools lógicamente: primero analiza, después modifica +4. Reporta siempre qué hiciste y por qué + +CONTEXTO ACTUAL DEL PROYECTO: +- Videos cargados: {media_list} +- Timeline actual: {timeline_summary} +- Duración total: {total_duration} + +HERRAMIENTAS DISPONIBLES: +{tools_schema} + +Cuando el usuario pida algo ambiguo, pregunta. Cuando algo sea destructivo, +confirma. Cuando puedas hacerlo con una sola tool, no lo compliques. +``` + +### D. Ejemplo de integración con Ollama (modo local) + +```typescript +// providers/llm/ollama.ts +import type { LLMProvider, Message, Tool, ChatChunk } from './types'; + +export class OllamaLLMProvider implements LLMProvider { + constructor(private config: { endpoint: string; model: string }) {} + + async *chat( + messages: Message[], + tools?: Tool[] + ): AsyncGenerator { + const response = await fetch(`${this.config.endpoint}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: this.config.model, + messages, + tools, + stream: true + }) + }); + + const reader = response.body!.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value); + const lines = chunk.split('\n').filter(l => l.trim()); + + for (const line of lines) { + const data = JSON.parse(line); + yield { + content: data.message?.content || '', + toolCalls: data.message?.tool_calls, + done: data.done + }; + } + } + } +} +``` + +### E. Recursos y referencias + +- **OpenCut:** https://github.com/OpenCut-app/OpenCut +- **Qwen2.5-VL:** https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct +- **Gemini Video Understanding:** https://ai.google.dev/gemini-api/docs/video-understanding +- **FFmpeg.wasm:** https://github.com/ffmpegwasm/ffmpeg.wasm +- **MediaBunny:** https://mediabunny.dev +- **MediaPipe Tasks for Web:** https://developers.google.com/mediapipe/solutions +- **Essentia.js:** https://mtg.github.io/essentia.js/ +- **Anthropic API:** https://docs.anthropic.com +- **Ollama:** https://ollama.com + +--- + +## Cierre + +Este documento es un **mapa de ruta**, no un contrato rígido. El scope se ajustará según el progreso real de cada sprint. La regla es: **tier 1 es innegociable, tier 2 es el objetivo, tier 3-4 son bonus.** + +El éxito del proyecto se mide en tres ejes: + +1. **¿Funciona?** El MVP ejecuta al menos 8 tools end-to-end. +2. **¿Impresiona?** El demo de feria genera el "wow" en visitantes. +3. **¿Aprendimos?** Aplicamos Design Thinking + Scrum de forma real, no de fachada.