eyewitness / ARCHITECTURE.md
Fcabla's picture
Upload folder using huggingface_hub
e1b885e verified
|
Raw
History Blame Contribute Delete
5.65 kB

A newer version of the Gradio SDK is available: 6.19.0

Upgrade

EYEWITNESS — cómo funciona (mapa del sistema)

El principio de diseño

Author the ground truth, don't detect it. El motor compone cada cara desde un vocabulario cerrado de 12 atributos (game/face.py: VOCAB), así que siempre sabe la verdad exacta. Los modelos solo traducen — nunca tienen que reconocer nada.

Flujo de una partida

intro ──ROLL──▶ glimpse (3s, CSS animation-delay oculta la cara)
      ──I SAW HIM──▶ testimony (texto libre EN/ES)
      ──SEND──▶ [PARSER] testimonio → JSON de atributos
                   ├── Tier B: MiniCPM5-1B fine-tuneado (ZeroGPU, frases sucias)
                   └── Tier A: matcher de sinónimos (backstop literal, siempre gana donde matchea)
              ──▶ sketch (dibuja SOLO lo que dijiste) + lineup (gr.Gallery PNG)
                   └── lineup.py: distractores desde TUS errores
                       · lo que dijiste MAL → plantado en distractores
                       · lo que NO mencionaste → ejes de confusión
                       · lo que acertaste → poda la rueda (p=0.72, afinado por simulación)
      ──click──▶ verdict: sello + cita con voz (VoxCPM2 pre-render, en memoria) +
                 tabla transparente dijiste/verdad + póster SE BUSCA (PIL) ──▶ next case

Rangos: ROOKIE (3s, rueda 4) → DETECTIVE (2s, 6) → INSPECTOR (1.5s, 6 + disfraz) → CHIEF (1s, 8 + disfraz). "Disfraz" = el culpable cambia UN atributo saliente entre el crimen y la rueda.

Ficheros

Fichero Qué hace
app.py UI Gradio: una sola etapa @gr.render dirigida por gr.State dict (screen=intro/glimpse/testimony/lineup/verdict)
game/face.py VOCAB + FaceSpec + renderer SVG paramétrico (estilo retrato robot)
game/render.py SVG→PIL compartido (Gallery y póster)
game/casegen.py Casos: crimen + culpable + parámetros por rango; carga banco Modal si existe
game/parser.py Tier A: sinónimos EN+ES, matching longest-first
game/model.py Tier B: MiniCPM5-1B slot-filling (gated: solo ZeroGPU/CUDA/env), merge con prioridad Tier A
game/lineup.py Distractores derivados de errores (la mecánica firma)
game/scoring.py Nota ponderada por saliencia + rating con humor
game/poster.py Póster SE BUSCA compartible (PIL)
modal_factory.py Build-time en Modal: banco de casos (llama.cpp+GGUF) + banco de voces (VoxCPM2, anchor a 48kHz)
train/gen_dataset.py Dataset sintético: spec→testimonio ruidoso bilingüe (verdad por construcción)
train/train_modal.py LoRA en Modal → publica Fcabla/MiniCPM5-1B-eyewitness
tests/test_engine.py QA: testimonios límite, equidad por simulación (400 partidas), bounds
deploy.sh Sube el Space a la org (idempotente)

Modelos en runtime (~5.7B total)

  • Fcabla/MiniCPM5-1B-eyewitness (1.08B, LoRA) — parser de testimonios (SOLO sabe slot-filling: olvido catastrófico verificado — balbucea dataset en prompts abiertos). OJO: en inferencia exige el formato EXACTO de entrenamiento (SYSTEM corto + Witness testimony: "..."); con el prompt few-shot del base parrotea los valores del ejemplo (fedora fantasma, 12 jun).
  • openbmb/MiniCPM5-1B base (1.08B) — escribe la COLETILLA personalizada del veredicto desde el diff dijiste/verdad (culprit_taunt); solo entra si pasa validación + actitud. El chiste principal del veredicto es AUTORADO por crimen (CRIME_TAUNTS en casegen.py — el lab demostró ~3% de acierto en comedia libre del 1B). enable_thinking=False obligatorio (modelo razonador).
  • CohereLabs/cohere-transcribe-03-2026 (2B) — ASR del testimonio hablado, EN EXCLUSIVA (sponsor). language es obligatorio: se transcribe con en Y es y gana el de mayor log-prob medio.
  • VoxCPM2 (2.29B) — voz del veredicto EN VIVO clonando un anchor (game/voice.py); banco pre-renderizado como fallback. Audio siempre en memoria (48000, int16) — gr.Audio con rutas fuera de allowed dirs revienta el render.

Veredicto: punchline autorado del crimen + coletilla de memoria (1B vivo si pasa el listón, autorada si no) → voz viva → banco enlatado → solo texto. Cada except loguea su causa ([taunt]/[voice]/[asr] en logs del Space).

Patrón ZeroGPU: en el arranque se precargan a CPU el parser+pulla (model.py), la voz (voice.py) y el ASR (asr.py) — así el primer testimonio hablado no paga la carga del 2B de Cohere. Las funciones @spaces.GPU (20s parser/pulla/ASR, 30s voz) solo transfieren y generan. La cuota ZeroGPU admite por duración SOLICITADA — pedir de más = llamadas rechazadas.

Infra

  • Space: build-small-hackathon/eyewitness, ZeroGPU (H200 slice), gating por env.
  • GitHub: fcabla/eyewitness (público).
  • Dataset: build-small-hackathon/eyewitness-testimonies.
  • Modal: apps eyewitness-factory (casos+voces) y eyewitness-train (LoRA, A10G).

Limitaciones conocidas (candidatas a iteración)

  1. La pulla del veredicto es chiste sobre el CRIMEN (banco autorado de 64 líneas + 1B base improvisando por encima cuando pasa validación). Riesgo: cuando entra el modelo, la calidad es variable (legible pero no siempre tan buena como el banco curado).
  2. El glimpse es vulnerable a inspeccionar el DOM (la cara sigue en el HTML borroso).
  3. El parser puede alucinar atributos no mencionados (mitigado: Tier A manda donde habló).
  4. Crash SIGSEGV espontáneo visto 1 vez en local (C-level, en el writer de stdout durante render de voz concurrente) — no reproducido en el Space; observar tras deploy.