From 2ce057fd8694f0231f9db4c57657ff47491c4233 Mon Sep 17 00:00:00 2001 From: Ricardo Gonzalez Date: Tue, 26 May 2026 14:53:55 -0500 Subject: [PATCH] =?UTF-8?q?documentar=20integracion=20Alpha=20=E2=86=94=20?= =?UTF-8?q?Handy=20para=20transcripcion=20offline=20con=20Parakeet=20V3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/integracion_handy.md | 379 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 docs/integracion_handy.md diff --git a/docs/integracion_handy.md b/docs/integracion_handy.md new file mode 100644 index 0000000..3157959 --- /dev/null +++ b/docs/integracion_handy.md @@ -0,0 +1,379 @@ +# Alpha + Handy — Integración de Transcripción Offline + +> **Inspiración**: Handy (github.com/cjpais/Handy) — 22.4k ⭐, Tauri v2, MIT. +> Alpha toma de Handy su arquitectura de transcripción local con modelos Whisper/Parakeet. + +**Versión**: 1.0 | **Fecha**: 2026-05-26 | **Autor**: Ricardo Gonzalez (Teloprax) + +--- + +## 1. ¿Qué es Handy? + +Handy es una app Tauri v2 que captura audio del micrófono, lo transcribe con modelos locales (Whisper o Parakeet) y pega el texto donde esté el cursor. Todo offline. Es la referencia perfecta para integrar transcripción en RUMBO/Alpha. + +| Aspecto | Handy | Alpha (a integrar) | +|---------|-------|---------------------| +| Shell | Tauri v2 | Tauri v2 ✅ | +| Frontend | React + TS + Tailwind | Vue 3 + TS + Tailwind 🔄 | +| Transcripción | transcribe-rs (Whisper + Parakeet) | transcribe-rs | +| Captura audio | cpal (micrófono) | cpal (micrófono + archivos) | +| VAD (silencio) | vad-rs (Silero) | vad-rs | +| Atajos globales | rdev + tauri-plugin-global-shortcut | rdev + tauri-plugin-global-shortcut | +| Permisos macOS | tauri-plugin-macos-permissions | Ídem | +| Permisos Windows | Win32 APIs (Windows crate) | Ídem | + +--- + +## 2. Arquitectura de transcripción (basada en Handy) + +``` +┌──────────────────────────────────────────────────┐ +│ Alpha (Tauri) │ +│ │ +│ ┌─────────────┐ ┌─────────────────────────┐ │ +│ │ Vue 3 UI │ │ Rust Backend │ │ +│ │ (activar │◄──►│ │ │ +│ │ mic, │ │ ┌───────────────────┐ │ │ +│ │ modelos, │ │ │ Audio Capture │ │ │ +│ │ transc.) │ │ │ (cpal) │ │ │ +│ └─────────────┘ │ │ → micrófono │ │ │ +│ │ │ → archivo .wav │ │ │ +│ │ │ → archivo .mp3 │ │ │ +│ │ │ → video (ffmpeg │ │ │ +│ │ │ extrae audio) │ │ │ +│ │ └───────┬───────────┘ │ │ +│ │ │ audio raw │ │ +│ │ ┌───────▼───────────┐ │ │ +│ │ │ VAD (vad-rs) │ │ │ +│ │ │ → silencio OUT │ │ │ +│ │ │ → voz → buffer │ │ │ +│ │ └───────┬───────────┘ │ │ +│ │ │ segmentos │ │ +│ │ ┌───────▼───────────┐ │ │ +│ │ │ transcribe-rs │ │ │ +│ │ │ → Parakeet V3 │ │ │ +│ │ │ → Whisper Small │ │ │ +│ │ │ → Whisper Turbo │ │ │ +│ │ └───────┬───────────┘ │ │ +│ │ │ texto │ │ +│ │ ┌───────▼───────────┐ │ │ +│ │ │ Post-procesado │ │ │ +│ │ │ → pegar en app │ │ │ +│ │ │ → guardar en DB │ │ │ +│ │ │ → pipeline IA │ │ │ +│ │ └───────────────────┘ │ │ +│ └─────────────────────────┘ │ +└──────────────────────────────────────────────────┘ +``` + +--- + +## 3. Dependencias Rust necesarias + +Inspiradas en el `Cargo.toml` de Handy (v0.8.3): + +```toml +[dependencies] +# Transcripción (core) +transcribe-rs = { version = "0.3", features = ["whisper-cpp", "onnx"] } + +# Audio +cpal = "0.16" # Captura de micrófono +hound = "3.5" # Lectura/escritura de archivos .wav + +# VAD (Voice Activity Detection) +vad-rs = { git = "https://github.com/cjpais/vad-rs", default-features = false } + +# Atajos globales +rdev = { git = "https://github.com/rustdesk-org/rdev" } +tauri-plugin-global-shortcut = "2.3" + +# Permisos +tauri-plugin-macos-permissions = "2.3" + +# Resampling (si se necesita cambiar sample rate) +rubato = "0.16" + +# Audio playback (feedback sonoro) +rodio = "0.16" + +# Utilidades +anyhow = "1.0" +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# ─── Plataforma-específicas ─── + +[target.'cfg(target_os = "macos")'.dependencies] +transcribe-rs = { version = "0.3", features = ["whisper-metal"] } + +[target.'cfg(target_os = "windows")'.dependencies] +transcribe-rs = { version = "0.3", features = ["whisper-vulkan", "ort-directml"] } + +[target.'cfg(target_os = "linux")'.dependencies] +transcribe-rs = { version = "0.3", features = ["whisper-vulkan"] } +``` + +--- + +## 4. Modelos de transcripción + +### 4.1 Parakeet V3 (RECOMENDADO) + +| Característica | Detalle | +|---------------|---------| +| Tamaño | ~478 MB (comprimido) | +| Velocidad | ~5x tiempo real en CPU (i5) | +| GPU | No requiere — CPU-optimizado | +| Idioma | Detección automática (sin selección manual) | +| Precisión | Excelente en español e inglés | +| Descarga | `https://blob.handy.computer/parakeet-v3-int8.tar.gz` | +| Backend | ONNX Runtime (vía transcribe-rs feature `onnx`) | + +**Por qué Parakeet V3**: No requiere GPU, funciona en cualquier CPU moderna (Skylake+), velocidad adecuada para transcripción interactiva, y detección automática de idioma (esencial para equipos bilingües español/inglés). + +### 4.2 Whisper (alternativa) + +| Modelo | Tamaño | Velocidad | GPU | +|--------|--------|-----------|-----| +| Small | 487 MB | Rápido | Opcional | +| Medium | 492 MB | Medio | Recomendada | +| Turbo | 1.6 GB | Rápido | Recomendada | +| Large | 1.1 GB | Lento | Necesaria | + +Whisper es más preciso pero más pesado y requiere GPU para velocidad aceptable. Parakeet V3 es mejor opción para Alpha por su eficiencia CPU. + +--- + +## 5. ¿Parakeet V3 para video/audio pregrabado? + +**Sí**, pero con un paso intermedio: extraer el audio del video. + +### Flujo para video + +``` +video.mp4 → ffmpeg (extraer pista de audio) → audio.wav → transcribe-rs → texto +``` + +### Flujo para audio pregrabado + +``` +grabacion.mp3/.ogg/.flac/.m4a → decodificar a PCM/WAV → transcribe-rs → texto +``` + +### Implementación en Rust + +```rust +// Opción A: Usar ffmpeg CLI (sidecar) +use std::process::Command; +Command::new("ffmpeg") + .args(["-i", "video.mp4", "-vn", "-ar", "16000", "-ac", "1", "-f", "wav", "audio.wav"]) + .output()?; + +// Opción B: Usar crate ffmpeg-sidecar (más portátil) +// Opción C: Usar crate symphonia para decodificar audio sin ffmpeg +``` + +### Formatos soportados + +| Fuente | Método | Librería Rust | +|--------|--------|---------------| +| Micrófono (vivo) | cpal → buffer → transcribe-rs | cpal | +| .wav | hound → samples → transcribe-rs | hound | +| .mp3, .ogg, .flac, .m4a | symphonia → decode → transcribe-rs | symphonia | +| Video (.mp4, .mov, .mkv) | ffmpeg → extract audio → transcribe-rs | ffmpeg-sidecar | + +--- + +## 6. Permisos del sistema + +### 6.1 macOS + +| Permiso | Propósito | Cómo solicitarlo | +|---------|-----------|-----------------| +| **Microphone** | Capturar audio del micrófono | Info.plist: `NSMicrophoneUsageDescription` | +| **Accessibility** | Pegar texto en cualquier app | System Preferences → Privacy → Accessibility | +| **Screen Recording** | (futuro) Capturar audio del sistema | Info.plist: `NSScreenCaptureUsageDescription` | + +**Código Info.plist**: +```xml +NSMicrophoneUsageDescription +Alpha necesita acceso al micrófono para transcribir tus reuniones. +``` + +**Detección de permiso (Rust)**: +```rust +use tauri_plugin_macos_permissions; + +// Verificar si el permiso está concedido +let authorized = tauri_plugin_macos_permissions::check_accessibility(); + +// Solicitar permiso +tauri_plugin_macos_permissions::request_accessibility(); +``` + +### 6.2 Windows + +| Permiso | Propósito | Cómo solicitarlo | +|---------|-----------|-----------------| +| **Microphone** | Capturar audio | Windows Settings → Privacy → Microphone | +| **Global shortcuts** | Atajos de teclado | Automático con rdev | + +Windows solicita el permiso automáticamente la primera vez que se usa el micrófono. + +### 6.3 Linux + +| Requisito | Propósito | +|-----------|-----------| +| PulseAudio / ALSA | Captura de audio | +| Grupo `input` | Atajos globales (algunos WMs) | + +--- + +## 7. Flujo de transcripción paso a paso + +### 7.1 Micrófono (tiempo real) + +```rust +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use transcribe_rs::Transcriber; + +// 1. Obtener dispositivo de audio por defecto +let host = cpal::default_host(); +let device = host.default_input_device().unwrap(); +let config = device.default_input_config().unwrap(); + +// 2. Configurar stream de audio +let (tx, rx) = std::sync::mpsc::channel(); +let stream = device.build_input_stream(&config, move |data, _| { + tx.send(data.to_vec()).unwrap(); +}, |err| eprintln!("{}", err), None).unwrap(); +stream.play().unwrap(); + +// 3. Buffer de audio + VAD +let mut buffer: Vec = Vec::new(); +let mut vad = vad_rs::Vad::new(config.sample_rate().0); + +// 4. Acumular voz, ignorar silencio +while let Ok(samples) = rx.recv_timeout(Duration::from_millis(100)) { + for &sample in &samples { + if vad.is_voice(sample) { + buffer.push(sample); + } + } + if buffer.len() > config.sample_rate().0 as usize * 3 { + break; // 3 segundos de voz → transcribir + } +} + +// 5. Transcribir con Parakeet V3 +let transcriber = Transcriber::new("parakeet-tdt-0.6b-v3-int8")?; +let text = transcriber.transcribe(&buffer, config.sample_rate().0)?; +println!("{}", text); +``` + +### 7.2 Archivo de audio/video + +```rust +// 1. Si es video, extraer audio con ffmpeg +// 2. Leer archivo .wav +use hound::WavReader; +let mut reader = WavReader::open("audio.wav")?; +let samples: Vec = reader.samples::() + .map(|s| s.unwrap() as f32 / i16::MAX as f32) + .collect(); + +// 3. Transcribir +let transcriber = Transcriber::new("parakeet-tdt-0.6b-v3-int8")?; +let text = transcriber.transcribe(&samples, reader.spec().sample_rate)?; +``` + +--- + +## 8. Descarga y gestión de modelos + +Inspirado en Handy, los modelos se almacenan en: + +| SO | Ruta | +|----|------| +| macOS | `~/Library/Application Support/com.teloprax.alpha/models/` | +| Windows | `%APPDATA%\com.teloprax.alpha\models\` | +| Linux | `~/.config/com.teloprax.alpha/models/` | + +### Estructura esperada + +``` +models/ +├── parakeet-tdt-0.6b-v3-int8/ ← Parakeet V3 (recomendado) +│ ├── model.onnx +│ ├── config.json +│ └── tokenizer.json +├── ggml-small.bin ← Whisper Small (alternativo) +└── ggml-large-v3-turbo.bin ← Whisper Turbo (alternativo) +``` + +### Descarga programática + +```rust +use reqwest; +use flate2::read::GzDecoder; +use tar::Archive; + +async fn download_model(url: &str, dest: &Path) -> Result<()> { + let response = reqwest::get(url).await?; + let bytes = response.bytes().await?; + + // Extraer si es .tar.gz (Parakeet), copiar si es .bin (Whisper) + if url.ends_with(".tar.gz") { + let gz = GzDecoder::new(&bytes[..]); + let mut archive = Archive::new(gz); + archive.unpack(dest)?; + } else { + std::fs::write(dest.join(url.split('/').last().unwrap()), &bytes)?; + } + Ok(()) +} +``` + +--- + +## 9. Diferencia clave Alpha vs Handy + +Handy es una app de propósito general (transcribir y pegar). Alpha lo integra en el flujo de gestión de proyectos: + +| Capacidad | Handy | Alpha | +|-----------|-------|-------| +| Transcribir micrófono | ✅ | ✅ (para reuniones) | +| Pegar texto en cualquier app | ✅ | ⬜ (no necesario) | +| Transcribir archivos de audio | ⬜ | ✅ (.mp3, .wav, .ogg) | +| Extraer audio de video | ⬜ | ✅ (vía ffmpeg) | +| Pipeline IA post-transcripción | ⬜ | ✅ (→ análisis → HUs → KAPPA) | +| Guardar transcripción en proyecto | ⬜ | ✅ (Turso + contexto de proyecto) | +| Memoria de proyecto | ⬜ | ✅ (contexto_transcripciones.md) | +| Integración con KAPPA | ⬜ | ✅ (crear HUs automáticamente) | + +--- + +## 10. Roadmap de integración + +| Fase | Alcance | Prioridad | +|------|---------|-----------| +| **T1** | Agregar dependencias Rust (transcribe-rs, cpal, vad-rs) | 🔴 Alta | +| **T2** | Captura de micrófono + transcripción Parakeet V3 | 🔴 Alta | +| **T3** | Descarga de modelos desde UI | 🟡 Media | +| **T4** | Transcripción de archivos (.wav, .mp3) | 🟡 Media | +| **T5** | Transcripción de video (extracción audio) | 🟢 Baja | +| **T6** | Pipeline completo: audio → transcripción → IA → HUs | 🔴 Alta | +| **T7** | Permisos macOS/Windows UI | 🟡 Media | + +--- + +## 11. Referencias + +- [Handy GitHub](https://github.com/cjpais/Handy) +- [transcribe-rs crate](https://crates.io/crates/transcribe-rs) +- [cpal crate](https://crates.io/crates/cpal) +- [vad-rs (fork de Handy)](https://github.com/cjpais/vad-rs) +- [Parakeet models (HuggingFace)](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3) +- [Tauri Plugin macOS Permissions](https://github.com/ahkohd/tauri-plugin-macos-permissions)