{"id":49329182,"url":"https://github.com/santifer/cv-santiago","last_synced_at":"2026-04-26T21:04:49.166Z","repository":{"id":334359599,"uuid":"1140774520","full_name":"santifer/cv-santiago","owner":"santifer","description":"Interactive CV with AI chat integration. Built with React 19, TypeScript, Claude API. Chat with my AI avatar about my experience.","archived":false,"fork":false,"pushed_at":"2026-04-17T11:46:46.000Z","size":67859,"stargazers_count":355,"open_issues_count":2,"forks_count":142,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-17T12:15:37.536Z","etag":null,"topics":["ai","chatbot","claude","langfuse","llm","llmops","observability","portfolio","react","tailwindcss","typescript","vercel","vite"],"latest_commit_sha":null,"homepage":"https://santifer.io","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/santifer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-23T18:38:48.000Z","updated_at":"2026-04-17T11:46:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/santifer/cv-santiago","commit_stats":null,"previous_names":["santifer-dev/cv-santiago","santifer/cv-santiago"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/santifer/cv-santiago","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/santifer%2Fcv-santiago","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/santifer%2Fcv-santiago/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/santifer%2Fcv-santiago/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/santifer%2Fcv-santiago/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/santifer","download_url":"https://codeload.github.com/santifer/cv-santiago/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/santifer%2Fcv-santiago/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32312507,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T19:15:34.056Z","status":"ssl_error","status_checked_at":"2026-04-26T19:15:15.467Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","chatbot","claude","langfuse","llm","llmops","observability","portfolio","react","tailwindcss","typescript","vercel","vite"],"created_at":"2026-04-26T21:04:49.100Z","updated_at":"2026-04-26T21:04:49.157Z","avatar_url":"https://github.com/santifer.png","language":"HTML","funding_links":[],"categories":["*Ops for AI"],"sub_categories":["LLMOps"],"readme":"# santifer.io\n\n**[:gb: English](#the-problem)** | **[:es: Español](#es-versión-en-español)**\n\n\u003e Interactive portfolio with AI chatbot (text + voice), agentic RAG, 71 automated evals, LLMOps dashboard, and 6-layer prompt injection defense\n\n[![Live Demo](https://img.shields.io/badge/demo-santifer.io-blue?style=flat-square)](https://santifer.io)\n[![Built with Claude Code](https://img.shields.io/badge/built%20with-Claude%20Code-blueviolet?style=flat-square)](https://claude.ai/code)\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/hero.gif\" alt=\"santifer.io in motion\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n---\n\n## The Problem\n\nStatic CVs don't show what you can actually build. A PDF lists skills — it doesn't prove them.\n\n## The Solution\n\nA production-grade interactive portfolio that **demonstrates the skills it describes**: dual-mode AI chatbot (text + voice) with agentic RAG, full LLMOps observability with custom dashboard, 71 automated evals as CI gate, prompt versioning, and a closed-loop that generates tests from production failures.\n\n**Key Features:**\n- **AI Chatbot \"Santi\"** — Text (Claude Sonnet) + Voice (OpenAI Realtime API). Responds in first person as Santiago. Agentic RAG with hybrid search (pgvector + BM25) and Haiku reranking\n- **6-Layer Defense** — Keyword detection, canary tokens, fingerprinting, anti-extraction, online safety scoring, adversarial red team. Real-time jailbreak email alerts\n- **71 Automated Evals** — 10 categories: factual accuracy, persona, boundaries, quality, safety, language, RAG quality, multi-turn, source badges, voice quality. CI gate on every push\n- **LLMOps Dashboard** — Private `/ops` with 8 tabs: Overview, Conversations, Costs, RAG, Security, Evals, Voice, System. Real data from Langfuse + Supabase\n- **Closed Loop** — Trace → online scoring → quality \u003c 0.7 → auto-generate test → CI gate blocks deploy\n- **Voice Mode** — OpenAI Realtime API, audio-to-audio, shared RAG pipeline, ~$0.25/session\n- **6 Published Case Studies** — Bilingual (ES/EN) with JSON-LD, prerendered HTML, cross-linked RAG, and interactive architecture diagrams\n- **Interactive Architecture Diagram** — GSAP-animated SVG with narrated audio, pan/zoom, dark mode sync. [Explore it →](https://santifer.io/self-healing-chatbot#architecture)\n- **GEO-ready** — `llms.txt`, structured data (JSON-LD), AI crawler-friendly robots.txt\n\n---\n\n## Tech Stack\n\n![React](https://img.shields.io/badge/React_19-61DAFB?style=flat\u0026logo=react\u0026logoColor=black)\n![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat\u0026logo=typescript\u0026logoColor=white)\n![Vite](https://img.shields.io/badge/Vite_7-646CFF?style=flat\u0026logo=vite\u0026logoColor=white)\n![Tailwind](https://img.shields.io/badge/Tailwind_v4-06B6D4?style=flat\u0026logo=tailwindcss\u0026logoColor=white)\n![Claude](https://img.shields.io/badge/Claude_Sonnet-191919?style=flat\u0026logo=anthropic\u0026logoColor=white)\n![OpenAI](https://img.shields.io/badge/OpenAI_Realtime-412991?style=flat\u0026logo=openai\u0026logoColor=white)\n![Langfuse](https://img.shields.io/badge/Langfuse-000000?style=flat\u0026logoColor=white)\n![Supabase](https://img.shields.io/badge/Supabase-3FCF8E?style=flat\u0026logo=supabase\u0026logoColor=white)\n![Vercel](https://img.shields.io/badge/Vercel_Edge-000000?style=flat\u0026logo=vercel\u0026logoColor=white)\n![Recharts](https://img.shields.io/badge/Recharts-FF6384?style=flat\u0026logoColor=white)\n\n---\n\n## Chatbot Architecture\n\n[![Interactive Architecture Diagram](public/chatbot/diagram-thumbnail.webp)](https://santifer.io/self-healing-chatbot#architecture)\n\u003e **[Explore the interactive diagram →](https://santifer.io/self-healing-chatbot#architecture)** 10 phases · narrated audio · zoom + pan\n\n```\nUser message → FloatingChat.tsx → api/chat.js (Vercel Edge)\n                                    ├── System prompt (Langfuse registry + fallback)\n                                    ├── Claude Sonnet (tool_use decision)\n                                    ├── Agentic RAG (if needed):\n                                    │     ├── OpenAI embeddings (text-embedding-3-small)\n                                    │     ├── Supabase pgvector (semantic) + full-text (BM25)\n                                    │     └── Claude Haiku (reranking + diversification)\n                                    ├── Claude Sonnet (streaming generation)\n                                    ├── Langfuse tracing (every span with cost)\n                                    └── waitUntil → Haiku scoring (0ms added latency)\n\nVoice mode → useVoiceMode.ts → api/voice-token.js → OpenAI Realtime WebSocket\n                                  └── api/rag-search.js (function calling for RAG)\n```\n\n### Key Files\n\n| File | Path | Description |\n|------|------|-------------|\n| Chat edge function | `api/chat.js` | Main chatbot — RAG, tracing, scoring, streaming, defense |\n| RAG pipeline | `api/_shared/rag.js` | Hybrid search, reranking, cost tracking, intent classification |\n| Prompt management | `api/_shared/prompt.js` | Langfuse prompt registry with file fallback |\n| Voice token | `api/voice-token.js` | OpenAI Realtime ephemeral token + rate limiting |\n| Voice RAG | `api/rag-search.js` | RAG search for voice mode function calling |\n| Voice trace | `api/voice-trace.js` | Voice session tracing with cost estimation |\n| Chat widget | `src/FloatingChat.tsx` | React widget — streaming SSE, quick prompts, contact CTA |\n| Voice hook | `src/useVoiceMode.ts` | WebSocket management, audio capture, transcript persistence |\n| System prompt | `chatbot-prompt.txt` | Fallback prompt (production uses Langfuse v5) |\n\n---\n\n## LLMOps Dashboard (`/ops`)\n\nPrivate, password-protected dashboard with 8 tabs showing real production data:\n\n| Tab | What it shows | Data source |\n|-----|---------------|-------------|\n| Overview | KPIs, timelines, donuts, intent distribution | Langfuse traces |\n| Conversations | Filter + list + detail with spans, cost, latency, scores | Langfuse traces + observations |\n| Costs | Breakdown per component (toolDecision/embedding/reranking/generation/voice) | `trace.metadata.cost` |\n| RAG | Activation rate, chunks per article | Langfuse tags + Supabase |\n| Security | Defense funnel, safety distribution, jailbreak list | Langfuse tags + scores |\n| Evals | Pass rates by category (embedded from real eval reports) | `evals/results/` via build |\n| Voice | Sessions, text/voice split, latency P50/P95, cost per minute | Langfuse tags |\n| System | Prompt versions, RAG document stats, model pricing | Langfuse prompts API + Supabase |\n\n### Dashboard API Layer\n\n| Endpoint | Path | Description |\n|----------|------|-------------|\n| Auth | `api/ops/auth.js` | Login (validates `OPS_DASHBOARD_SECRET`) |\n| Stats | `api/ops/stats.js` | Aggregated stats, server-side compute from traces |\n| Traces | `api/ops/traces.js` | List traces with filters (lang, mode, RAG, jailbreak) |\n| Trace detail | `api/ops/trace/[id].js` | Full trace with observations, scores, Langfuse link |\n| Evals | `api/ops/evals.js` | Eval results embedded from build |\n| Prompts | `api/ops/prompts.js` | Prompt versions from Langfuse |\n| RAG stats | `api/ops/rag-stats.js` | Document stats from Supabase |\n\n---\n\n## Evals \u0026 Testing\n\n71 automated tests across 10 categories. ~70% deterministic (contains, regex, word count), ~30% LLM-as-Judge (Haiku).\n\n| Category | Tests | Type |\n|----------|-------|------|\n| factual_accuracy | 9 | Deterministic |\n| persona_adherence | 4 | Deterministic |\n| boundary_testing | 7 | Deterministic |\n| response_quality | 7 | Mixed |\n| safety_jailbreak | 7 | Deterministic |\n| language_handling | 5 | Deterministic |\n| rag_quality | 16 | Mixed |\n| multi_turn | 5 | Mixed |\n| source_badges | 5 | Deterministic |\n| voice_quality | 6 | Mixed |\n\n---\n\n## Scripts \u0026 CLI Tools\n\nAll scripts live in `scripts/` and run via `npm run`:\n\n### Chatbot Operations\n| Command | Script | Description |\n|---------|--------|-------------|\n| `npm run evals` | `evals/runner.ts` | Run 71 automated evals |\n| `npm run adversarial` | `scripts/adversarial-test.ts` | Red team: 20+ auto-generated attacks |\n| `npm run chats` | `scripts/chats.ts` | View last 50 conversations from Langfuse |\n| `npm run chats -- --full` | `scripts/chats.ts` | Full conversations with messages |\n| `npm run chats -- --jailbreak` | `scripts/chats.ts` | Only jailbreak attempts |\n| `npm run evaluate-traces` | `scripts/evaluate-traces.ts` | Batch eval with Haiku (quality, safety, intent) |\n| `npm run diagnose:rag` | `scripts/diagnose-rag.ts` | RAG quality diagnostic — detects retrieval misses |\n\n### Prompt \u0026 RAG Management\n| Command | Script | Description |\n|---------|--------|-------------|\n| `npm run prompt:sync` | `scripts/sync-prompt-to-langfuse.ts` | Sync prompt to Langfuse (hash-based, skip if unchanged) |\n| `npm run prompt:regression` | `scripts/prompt-regression.ts` | Compare two prompt versions side by side |\n| `npm run rag:sync` | `scripts/export-chunks.ts` + `scripts/ingest-rag.ts` | Re-export articles + ingest to Supabase |\n\n### Contract \u0026 Integration Tests\n| Command | Script | Description |\n|---------|--------|-------------|\n| `npm run test:contract` | `tests/ops-contract.test.ts` | Validate trace metadata matches dashboard contract (67 tests) |\n| `npm run test:ops` | `tests/ops-dashboard.test.ts` | Test all 7 dashboard API endpoints (102 tests) |\n\n### Build Pipeline\n| Command | Script | Description |\n|---------|--------|-------------|\n| `npm run build` | (chained) | rag:sync → prompt:sync → embed-evals → reddit-stats → tsc → vite → sitemap → validate → prerender |\n| — | `scripts/embed-evals.ts` | Parse eval reports → embed in dashboard |\n| — | `scripts/generate-sitemap.ts` | Generate sitemap.xml with lastmod |\n| — | `scripts/validate-articles.ts` | SEO validation (dates, keywords, OG images) |\n| — | `scripts/validate-llms-txt.ts` | Validate llms.txt consistency |\n| — | `scripts/prerender.tsx` | SSR prerender all pages with critical CSS |\n| — | `scripts/indexnow-ping.ts` | Ping Bing/Yandex on deploy |\n\n---\n\n## Quick Start\n\n```bash\ngit clone https://github.com/santifer/cv-santiago.git\ncd cv-santiago\nnpm install\nnpm run dev\n```\n\nOpen [localhost:5173](http://localhost:5173)\n\n### Environment Variables\n\n```bash\n# Core\nANTHROPIC_API_KEY=           # Claude API (chatbot)\nOPENAI_API_KEY=              # Embeddings + Voice\n\n# RAG\nSUPABASE_URL=                # Supabase project URL\nSUPABASE_SERVICE_ROLE_KEY=   # Supabase service key\n\n# Observability\nLANGFUSE_PUBLIC_KEY=         # Langfuse tracing\nLANGFUSE_SECRET_KEY=         # Langfuse tracing\n\n# Alerts \u0026 Dashboard\nRESEND_API_KEY=              # Jailbreak email alerts\nOPS_DASHBOARD_SECRET=        # Dashboard password (/ops)\n```\n\n---\n\n## Project Structure\n\n```\nsrc/\n├── App.tsx                  # Full CV — all sections\n├── FloatingChat.tsx         # Chat widget (text mode)\n├── useVoiceMode.ts          # Voice mode hook (OpenAI Realtime)\n├── VoiceOrb.tsx             # Voice UI (orb + transcript)\n├── GlobalNav.tsx            # Navigation with breadcrumbs\n├── main.tsx                 # React Router + lazy loading\n├── i18n.ts                  # Bilingual translations\n├── articles/\n│   ├── registry.ts          # Centralized article config\n│   ├── components.tsx       # Shared article components\n│   └── json-ld.ts           # JSON-LD builder\n├── ops/                     # LLMOps Dashboard\n│   ├── OpsDashboard.tsx     # Shell + Overview tab\n│   ├── OpsAuth.tsx          # Login screen\n│   ├── types.ts             # Shared TypeScript interfaces\n│   ├── hooks/               # useOpsApi, useTraces\n│   ├── components/          # KpiCard, MetricChart, FilterBar, etc.\n│   └── tabs/                # Conversations, Costs, Security, Evals, etc.\n├── [Article].tsx             # Case study components (5 articles)\n└── [article]-i18n.ts         # Bilingual content per article\n\napi/\n├── chat.js                  # Main chatbot edge function\n├── voice-token.js           # Voice ephemeral token + rate limit\n├── voice-trace.js           # Voice session tracing\n├── rag-search.js            # RAG for voice function calling\n├── _shared/\n│   ├── rag.js               # RAG pipeline (search, rerank, cost)\n│   ├── prompt.js            # Prompt versioning (Langfuse)\n│   └── ops-auth.js          # Dashboard auth helper\n└── ops/                     # Dashboard API proxy layer\n    ├── auth.js              # Login\n    ├── stats.js             # Aggregated stats\n    ├── traces.js            # Trace list with filters\n    ├── trace/[id].js        # Trace detail\n    ├── evals.js             # Eval results\n    ├── prompts.js           # Prompt versions\n    └── rag-stats.js         # RAG document stats\n\nevals/\n├── datasets/                # 10 JSON datasets (71 test cases)\n├── assertions.ts            # Deterministic assertions\n├── llm-judge.ts             # LLM-as-Judge (Haiku)\n└── runner.ts                # Eval runner\n\nscripts/                     # See \"Scripts \u0026 CLI Tools\" section above\ntests/\n├── ops-contract.test.ts     # Contract tests (67 assertions)\n└── ops-dashboard.test.ts    # Dashboard API tests (102 assertions)\n\nchatbot-prompt.txt           # System prompt (fallback, prod uses Langfuse)\n```\n\n---\n\n## Case Studies\n\n| Article | Slugs | Type |\n|---------|-------|------|\n| Self-Healing Chatbot | `/chatbot-que-se-cura-solo` `/self-healing-chatbot` | case-study |\n| Career-Ops | `/career-ops` `/career-ops-system` | case-study |\n| Jacobo AI Agent | `/agente-ia-jacobo` `/ai-agent-jacobo` | case-study |\n| Business OS | `/business-os-para-airtable` `/business-os-for-airtable` | case-study |\n| Programmatic SEO | `/seo-programatico` `/programmatic-seo` | case-study |\n| n8n for PMs | `/n8n-para-pms` `/n8n-for-pms` | collab |\n| Santifer iRepair | `/santifer-irepair` `/santifer-irepair-founder` | bridge |\n\n---\n\n## Cost\n\n- **\u003c$0.005 per text conversation** (5 models in the pipeline)\n- **~$0.25 per voice session** (OpenAI Realtime)\n- **$0 infrastructure** (free tiers: Vercel, Supabase, Langfuse)\n- **~$30/month** estimated at 200 conversations/day\n\n---\n\n## License\n\nMIT\n\n---\n\n---\n\n# :es: Versión en Español\n\n\u003e Portfolio interactivo con chatbot IA (texto + voz), RAG agéntico, 71 evals automatizados, dashboard LLMOps y defensa anti-inyección en 6 capas\n\n[![Demo en vivo](https://img.shields.io/badge/demo-santifer.io-blue?style=flat-square)](https://santifer.io)\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/hero.gif\" alt=\"santifer.io en movimiento\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n---\n\n## El Problema\n\nLos CVs estáticos no demuestran lo que realmente sabes construir. Un PDF lista habilidades — no las prueba.\n\n## La Solución\n\nUn portfolio interactivo de nivel producción que **demuestra las habilidades que describe**: chatbot IA dual (texto + voz) con RAG agéntico, observabilidad LLMOps completa con dashboard custom, 71 evals automatizados como CI gate, versionado de prompts, y un closed-loop que genera tests de fallos en producción.\n\n**Funcionalidades:**\n- **Chatbot IA \"Santi\"** — Texto (Claude Sonnet) + Voz (OpenAI Realtime API). Responde en primera persona como Santiago. RAG agéntico con búsqueda híbrida (pgvector + BM25) y reranking con Haiku\n- **Defensa en 6 capas** — Keyword detection, canary tokens, fingerprinting, anti-extraction, online safety scoring, adversarial red team. Alertas de jailbreak por email en tiempo real\n- **71 Evals automatizados** — 10 categorías: factual, persona, boundaries, quality, safety, language, RAG, multi-turn, source badges, voice. CI gate en cada push\n- **Dashboard LLMOps** — `/ops` privado con 8 pestañas: Overview, Conversations, Costs, RAG, Security, Evals, Voice, System. Datos reales de Langfuse + Supabase\n- **Closed Loop** — Traza → scoring online → quality \u003c 0.7 → auto-genera test → CI gate bloquea deploy\n- **Modo voz** — OpenAI Realtime API, audio-to-audio, mismo pipeline RAG, ~$0.25/sesión\n- **6 Case Studies publicados** — Bilingües (ES/EN) con JSON-LD, HTML prerenderizado, RAG cross-linked y diagramas de arquitectura interactivos\n- **Diagrama de Arquitectura Interactivo** — SVG animado con GSAP, audio narrado, pan/zoom, sync dark mode. [Explorar →](https://santifer.io/chatbot-que-se-cura-solo#architecture)\n- **GEO-ready** — `llms.txt`, datos estructurados (JSON-LD), robots.txt amigable con crawlers IA\n\n---\n\n## Stack Técnico\n\n![React](https://img.shields.io/badge/React_19-61DAFB?style=flat\u0026logo=react\u0026logoColor=black)\n![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat\u0026logo=typescript\u0026logoColor=white)\n![Vite](https://img.shields.io/badge/Vite_7-646CFF?style=flat\u0026logo=vite\u0026logoColor=white)\n![Tailwind](https://img.shields.io/badge/Tailwind_v4-06B6D4?style=flat\u0026logo=tailwindcss\u0026logoColor=white)\n![Claude](https://img.shields.io/badge/Claude_Sonnet-191919?style=flat\u0026logo=anthropic\u0026logoColor=white)\n![OpenAI](https://img.shields.io/badge/OpenAI_Realtime-412991?style=flat\u0026logo=openai\u0026logoColor=white)\n![Langfuse](https://img.shields.io/badge/Langfuse-000000?style=flat\u0026logoColor=white)\n![Supabase](https://img.shields.io/badge/Supabase-3FCF8E?style=flat\u0026logo=supabase\u0026logoColor=white)\n![Vercel](https://img.shields.io/badge/Vercel_Edge-000000?style=flat\u0026logo=vercel\u0026logoColor=white)\n![Recharts](https://img.shields.io/badge/Recharts-FF6384?style=flat\u0026logoColor=white)\n\n---\n\n## Arquitectura del Chatbot\n\n[![Diagrama Interactivo de Arquitectura](public/chatbot/diagram-thumbnail.webp)](https://santifer.io/chatbot-que-se-cura-solo#architecture)\n\u003e **[Explorar el diagrama interactivo →](https://santifer.io/chatbot-que-se-cura-solo#architecture)** 10 fases · audio narrado · zoom + pan\n\n```\nMensaje → FloatingChat.tsx → api/chat.js (Vercel Edge)\n                               ├── System prompt (Langfuse registry + fallback)\n                               ├── Claude Sonnet (decisión tool_use)\n                               ├── RAG Agéntico (si necesario):\n                               │     ├── OpenAI embeddings (text-embedding-3-small)\n                               │     ├── Supabase pgvector (semántico) + full-text (BM25)\n                               │     └── Claude Haiku (reranking + diversificación)\n                               ├── Claude Sonnet (generación streaming)\n                               ├── Langfuse tracing (cada span con coste)\n                               └── waitUntil → Haiku scoring (0ms de latencia añadida)\n\nModo voz → useVoiceMode.ts → api/voice-token.js → OpenAI Realtime WebSocket\n                                └── api/rag-search.js (function calling para RAG)\n```\n\n### Archivos Clave\n\n| Archivo | Ruta | Descripción |\n|---------|------|-------------|\n| Chat edge function | `api/chat.js` | Chatbot principal — RAG, tracing, scoring, streaming, defensa |\n| Pipeline RAG | `api/_shared/rag.js` | Búsqueda híbrida, reranking, coste, clasificación de intención |\n| Gestión de prompt | `api/_shared/prompt.js` | Langfuse prompt registry con fallback local |\n| Token de voz | `api/voice-token.js` | Token efímero OpenAI Realtime + rate limiting |\n| RAG voz | `api/rag-search.js` | Búsqueda RAG para function calling de voz |\n| Trace voz | `api/voice-trace.js` | Tracing de sesiones de voz con estimación de coste |\n| Widget chat | `src/FloatingChat.tsx` | Widget React — streaming SSE, quick prompts, CTA de contacto |\n| Hook de voz | `src/useVoiceMode.ts` | Gestión WebSocket, captura audio, persistencia de transcript |\n| System prompt | `chatbot-prompt.txt` | Prompt fallback (producción usa Langfuse v5) |\n\n---\n\n## Dashboard LLMOps (`/ops`)\n\nDashboard privado protegido por contraseña con 8 pestañas mostrando datos reales:\n\n| Pestaña | Qué muestra | Fuente de datos |\n|---------|-------------|-----------------|\n| Overview | KPIs, timelines, donuts, distribución de intents | Trazas Langfuse |\n| Conversations | Filtros + lista + detalle con spans, coste, latencia, scores | Trazas + observaciones |\n| Costs | Desglose por componente (toolDecision/embedding/reranking/generation/voice) | `trace.metadata.cost` |\n| RAG | Tasa de activación, chunks por artículo | Tags Langfuse + Supabase |\n| Security | Funnel de defensa, distribución de safety, lista de jailbreaks | Tags + scores |\n| Evals | Pass rates por categoría (embebidos de reports reales) | `evals/results/` via build |\n| Voice | Sesiones, split texto/voz, latencia P50/P95, coste por minuto | Tags Langfuse |\n| System | Versiones de prompt, stats de documentos RAG, precios de modelos | API prompts Langfuse + Supabase |\n\n---\n\n## Evals y Testing\n\n71 tests automatizados en 10 categorías. ~70% deterministas, ~30% LLM-as-Judge (Haiku).\n\n| Categoría | Tests | Tipo |\n|-----------|-------|------|\n| factual_accuracy | 9 | Determinista |\n| persona_adherence | 4 | Determinista |\n| boundary_testing | 7 | Determinista |\n| response_quality | 7 | Mixto |\n| safety_jailbreak | 7 | Determinista |\n| language_handling | 5 | Determinista |\n| rag_quality | 16 | Mixto |\n| multi_turn | 5 | Mixto |\n| source_badges | 5 | Determinista |\n| voice_quality | 6 | Mixto |\n\n---\n\n## Scripts y Herramientas CLI\n\nTodos los scripts están en `scripts/` y se ejecutan con `npm run`:\n\n### Operaciones del Chatbot\n| Comando | Script | Descripción |\n|---------|--------|-------------|\n| `npm run evals` | `evals/runner.ts` | Ejecutar 71 evals automatizados |\n| `npm run adversarial` | `scripts/adversarial-test.ts` | Red team: 20+ ataques auto-generados |\n| `npm run chats` | `scripts/chats.ts` | Ver últimas 50 conversaciones de Langfuse |\n| `npm run chats -- --full` | `scripts/chats.ts` | Conversaciones completas con mensajes |\n| `npm run chats -- --jailbreak` | `scripts/chats.ts` | Solo intentos de jailbreak |\n| `npm run evaluate-traces` | `scripts/evaluate-traces.ts` | Eval batch con Haiku (calidad, seguridad, intención) |\n| `npm run diagnose:rag` | `scripts/diagnose-rag.ts` | Diagnóstico de calidad RAG — detecta retrieval misses |\n\n### Gestión de Prompt y RAG\n| Comando | Script | Descripción |\n|---------|--------|-------------|\n| `npm run prompt:sync` | `scripts/sync-prompt-to-langfuse.ts` | Sync prompt a Langfuse (basado en hash, skip si no cambió) |\n| `npm run prompt:regression` | `scripts/prompt-regression.ts` | Comparar dos versiones del prompt |\n| `npm run rag:sync` | `scripts/export-chunks.ts` + `scripts/ingest-rag.ts` | Re-exportar artículos + ingestar en Supabase |\n\n### Tests de Contrato e Integración\n| Comando | Script | Descripción |\n|---------|--------|-------------|\n| `npm run test:contract` | `tests/ops-contract.test.ts` | Validar que metadata de trazas coincide con contrato del dashboard (67 tests) |\n| `npm run test:ops` | `tests/ops-dashboard.test.ts` | Testear los 7 endpoints API del dashboard (102 tests) |\n\n---\n\n## Inicio Rápido\n\n```bash\ngit clone https://github.com/santifer/cv-santiago.git\ncd cv-santiago\nnpm install\nnpm run dev\n```\n\nAbrir [localhost:5173](http://localhost:5173)\n\n### Variables de Entorno\n\n```bash\n# Core\nANTHROPIC_API_KEY=           # Claude API (chatbot)\nOPENAI_API_KEY=              # Embeddings + Voz\n\n# RAG\nSUPABASE_URL=                # URL del proyecto Supabase\nSUPABASE_SERVICE_ROLE_KEY=   # Clave de servicio Supabase\n\n# Observabilidad\nLANGFUSE_PUBLIC_KEY=         # Tracing Langfuse\nLANGFUSE_SECRET_KEY=         # Tracing Langfuse\n\n# Alertas y Dashboard\nRESEND_API_KEY=              # Alertas de jailbreak por email\nOPS_DASHBOARD_SECRET=        # Contraseña del dashboard (/ops)\n```\n\n---\n\n## Estructura del Proyecto\n\n```\nsrc/\n├── App.tsx                  # CV completo — todas las secciones\n├── FloatingChat.tsx         # Widget de chat (modo texto)\n├── useVoiceMode.ts          # Hook de modo voz (OpenAI Realtime)\n├── VoiceOrb.tsx             # UI de voz (orbe + transcript)\n├── GlobalNav.tsx            # Navegación con breadcrumbs\n├── main.tsx                 # React Router + lazy loading\n├── i18n.ts                  # Traducciones bilingües\n├── articles/\n│   ├── registry.ts          # Config centralizada de artículos\n│   ├── components.tsx        # Componentes compartidos\n│   └── json-ld.ts           # Builder de JSON-LD\n├── ops/                     # Dashboard LLMOps\n│   ├── OpsDashboard.tsx     # Shell + pestaña Overview\n│   ├── OpsAuth.tsx          # Pantalla de login\n│   ├── types.ts             # Interfaces TypeScript compartidas\n│   ├── hooks/               # useOpsApi, useTraces\n│   ├── components/          # KpiCard, MetricChart, FilterBar, etc.\n│   └── tabs/                # Conversations, Costs, Security, Evals, etc.\n├── [Articulo].tsx            # Componentes de case studies (5 artículos)\n└── [articulo]-i18n.ts        # Contenido bilingüe por artículo\n\napi/\n├── chat.js                  # Edge function principal del chatbot\n├── voice-token.js           # Token efímero de voz + rate limit\n├── voice-trace.js           # Tracing de sesiones de voz\n├── rag-search.js            # RAG para function calling de voz\n├── _shared/\n│   ├── rag.js               # Pipeline RAG (búsqueda, rerank, coste)\n│   ├── prompt.js            # Versionado de prompt (Langfuse)\n│   └── ops-auth.js          # Helper de auth del dashboard\n└── ops/                     # Capa API proxy del dashboard\n    ├── auth.js, stats.js, traces.js, trace/[id].js\n    ├── evals.js, prompts.js, rag-stats.js\n\nevals/\n├── datasets/                # 10 datasets JSON (71 test cases)\n├── assertions.ts            # Assertions deterministas\n├── llm-judge.ts             # LLM-as-Judge (Haiku)\n└── runner.ts                # Runner de evaluaciones\n\nscripts/                     # Ver sección \"Scripts y Herramientas CLI\"\ntests/\n├── ops-contract.test.ts     # Tests de contrato (67 assertions)\n└── ops-dashboard.test.ts    # Tests API del dashboard (102 assertions)\n\nchatbot-prompt.txt           # System prompt (fallback, producción usa Langfuse)\n```\n\n---\n\n## Case Studies\n\n| Artículo | Slugs | Tipo |\n|----------|-------|------|\n| Chatbot que se cura solo | `/chatbot-que-se-cura-solo` `/self-healing-chatbot` | case-study |\n| Career-Ops | `/career-ops` `/career-ops-system` | case-study |\n| Agente IA Jacobo | `/agente-ia-jacobo` `/ai-agent-jacobo` | case-study |\n| Business OS | `/business-os-para-airtable` `/business-os-for-airtable` | case-study |\n| SEO Programático | `/seo-programatico` `/programmatic-seo` | case-study |\n| n8n para PMs | `/n8n-para-pms` `/n8n-for-pms` | collab |\n| Santifer iRepair | `/santifer-irepair` `/santifer-irepair-founder` | bridge |\n\n---\n\n## Coste\n\n- **\u003c$0.005 por conversación de texto** (5 modelos en el pipeline)\n- **~$0.25 por sesión de voz** (OpenAI Realtime)\n- **$0 infraestructura** (free tiers: Vercel, Supabase, Langfuse)\n- **~$30/mes** estimado a 200 conversaciones/día\n\n---\n\n## Licencia\n\nMIT\n\n---\n\n## Let's Connect\n\n[![Website](https://img.shields.io/badge/santifer.io-000?style=for-the-badge\u0026logo=safari\u0026logoColor=white)](https://santifer.io)\n[![LinkedIn](https://img.shields.io/badge/LinkedIn-0A66C2?style=for-the-badge\u0026logo=linkedin\u0026logoColor=white)](https://linkedin.com/in/santifer)\n[![Email](https://img.shields.io/badge/Email-EA4335?style=for-the-badge\u0026logo=gmail\u0026logoColor=white)](mailto:hola@santifer.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsantifer%2Fcv-santiago","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsantifer%2Fcv-santiago","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsantifer%2Fcv-santiago/lists"}