diff --git a/00_VISION/filosofia.md b/00_VISION/filosofia.md index c7f9a4a..2dc34b4 100644 --- a/00_VISION/filosofia.md +++ b/00_VISION/filosofia.md @@ -1,118 +1,185 @@ -# Filosofía del Sistema TZZR +# Filosofía Fundacional -**Versión:** 5.0 -**Fecha:** 2024-12-24 +**Versión:** 1.0 +**Estado:** Definición --- -## 10 Principios Fundacionales - -### 1. Constructores, no gestores - -ARCHITECT (con Claude) es un **constructor de arquitecturas**, no un gestor permanente. - -- ARCHITECT diseña y construye la arquitectura de cada servidor -- Cuando la arquitectura esté madura, el servidor será **clonable e independiente** -- DECK y CORP funcionan sin conexión a ARCHITECT ni a Claude -- Son sistemas diseñados para **usar** servicios de IA (APIs externas, RunPod), no para contenerla - -### 2. Descentralización operativa +## 1. Principio Fundamental ``` -Architect App (centralizado) → Diseña moldes - ↓ - Instancias reales (descentralizadas) - ↓ - Cada una con su CORP, su DECK, sus agentes +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ "La realidad es un flujo energético continuo. │ +│ Las categorías son modelos mentales útiles, │ +│ no verdades absolutas. │ +│ Ante la duda, analizar el flujo de energía │ +│ clarifica la situación." │ +│ │ +└─────────────────────────────────────────────────────────────────┘ ``` -### 3. Referencias ligeras mediante hashes +Este principio guía todas las decisiones arquitectónicas del sistema. Cuando existe ambigüedad sobre cómo clasificar algo, la pregunta correcta es: **¿dónde se consume la energía?** + +--- + +## 2. Paradigma de Resultados ``` -DEFINICIÓN ORIGINAL → SHA-256 → HASH UNÍVOCO (64 chars) +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ "El sistema no registra incidencias. Registra resultados." │ +│ │ +└─────────────────────────────────────────────────────────────────┘ ``` -El hash es una referencia ligera que arrastra toda la información del original sin duplicarla. +| Paradigma Burocrático | Paradigma de Resultados | +|----------------------|------------------------| +| ¿Por qué no cumpliste? | ¿Cumpliste? | +| Documenta la desviación | Documenta el resultado | +| Justifica el proceso | Evidencia el producto | +| El error es información | El error es ruido | -### 4. Separación de planos +### Implicación Técnica -| Plano | Nombre | Función | -|-------|--------|---------| -| **T-N → T0** | ITM (Ítems) | Lo ideal, la partitura | -| **Burocrático** | MST (Milestones) | Documentos, hitos, estados | -| **Físico** | BCK (Bloques) | Acciones, evidencias, trabajo real | +Cuando un Bloque de trabajo se completa, el sistema registra: +- Qué se hizo +- Evidencia de que se hizo +- Quién lo hizo +- Cuándo quedó completo -### 5. Período flotante antes de inmutabilidad +El sistema **NO registra**: +- Cuánto tardó internamente +- Qué interrupciones hubo +- Por qué se desvió del proceso ideal +- Cuántos intentos fueron necesarios -Los datos pasan por un período flotante que permite mejora antes del sellado definitivo. +--- + +## 3. Paradigma Temporal + +### Línea Temporal Fundamental ``` -Dato nuevo → Período flotante (24h) → Verificación SENTINEL → Sellado FELDMAN → Inmutable +T-N ────→ T-1 ────→ T0 ────→ T+1 ────→ T+N + │ │ │ │ │ +Origen Pre- Inicio Cierre Futuro/ +Difuso consoli- Real Eviden- Histórico + dación cia ``` -### 6. Renombrabilidad de agentes +### Definición de Puntos Temporales -Los componentes marcados como renombrables pueden personalizarse: +| Punto | Significado | Características | +|-------|-------------|-----------------| +| **T−N** | Origen difuso | Exploración, ideas no consolidadas | +| **T−1** | Referencia previa | Punto desde el cual planificar hacia T0 | +| **T0** | Inicio ejecución | Arranque formal, recursos comprometidos | +| **T+1** | Hito burocrático | Registro formal de cierre operativo | +| **T+N** | Resultado consolidado | Materialización real en el mundo | + +### Naturaleza Temporal de Entidades + +| Entidad | Intervalo | Naturaleza | +|---------|-----------|------------| +| **Ítem** | T-N → T-1 → T0 | Definición ideal, proceso, producto conceptual | +| **Milestone** | T0 → T+1 | Estado contable/legal en vigor | +| **Bloque** | T+1 | Evidencia física de la ejecución | + +> **"Un Bloque es la fotografía energética del intervalo T0 → T+1"** + +--- + +## 4. El Acuerdo Bilateral + +El sistema implementa un **pacto implícito de descargo de responsabilidad**: + +### La Empresa Ofrece + +- Herramientas para que el trabajador demuestre su buen trabajo +- Ausencia de vigilancia de proceso +- Contexto automático (arrastre de datos conocidos) +- Granularidad que permite progreso incluso en días difíciles + +### El Trabajador Ofrece + +- Resultados evidenciados +- Bloques completados +- Narrativa verificable de su aportación + +### El Límite del Acuerdo + +> **Si los resultados no llegan, la trazabilidad revela el problema.** + +Pero lo revela de forma **constructiva**, no punitiva. + +--- + +## 5. Trazabilidad como Movilidad + +La trazabilidad tradicional es un arma de control. En este sistema, es una **herramienta de movilidad**: + +### Para el Trabajador + +- Portfolio verificable de resultados +- Evidencia objetiva de capacidades +- Independencia de la opinión subjetiva del supervisor +- Portabilidad entre puestos y empresas + +### Para la Empresa + +- Visibilidad real de dónde se genera valor +- Identificación de cuellos de botella sin buscar culpables +- Datos para optimizar asignación de recursos +- Base objetiva para decisiones de personal + +### Mecánica Técnica + +Cada Bloque completado es un registro inmutable que demuestra: ``` -CLARA → "Lucía" -ALFRED → "Pepe" -PENNY → "Asistente" -``` - -Esto permite que cada usuario sienta el sistema como propio. - -### 7. GRACE nunca modifica - -GRACE extrae y comprende, pero **nunca modifica** el contenido original. - -> "Solo extrae y comprende, nunca modifica el contenido original." - -### 8. Auditoría dual: confía pero verifica - -SENTINEL opera en dos modos: - -| Modo | Frecuencia | Alcance | Tecnología | -|------|------------|---------|------------| -| **LIGHT** | Cada 5 min | Todos los registros | Reglas automáticas | -| **DEEP** | Cada 1 hora | Muestreo | LLM análisis | - -### 9. Zero-retention en interfaces móviles - -PACKET no almacena datos localmente. Todo va directo al servidor. - -### 10. Curación humana en Vision Builder - -``` -VALORES → OBJETIVOS → IMÁGENES IA → CURACIÓN HUMANA → LO QUE SOBREVIVE DEFINE QUIÉN ERES +[Trabajador X] completó [Tarea Y] con [Evidencia Z] en [Contexto W] ``` --- -## Modelo de Instancias +## 6. Implicaciones para el Diseño -**DECK** y **CORP** son plantillas. En producción habrá múltiples instancias: +### Granularidad de Bloques -| Tipo | Ejemplos | -|------|----------| -| **DECK** | "Deck de Juan", "Deck de Victoria", "Deck de Pablo" | -| **CORP** | "Lacitos de Colores SL", "TZR Tech", "Acme Corp" | +Los Bloques deben ser suficientemente pequeños para que: +- Un día malo no impida completar ninguno +- El progreso sea visible incluso con interrupciones +- La evidencia sea simple (una foto, un tap, una confirmación) -Cada instancia: -- Tiene su propio bucket de almacenamiento -- Puede renombrar sus agentes -- **Opera de forma autónoma** (no depende de ARCHITECT en runtime) -- Tiene su propio gestor de secretos (Vaultwarden) -- Hace sus propios backups a R2 +### Arrastre de Contexto -### Servicios Compartidos (Opcionales) +Cada nuevo Bloque debe heredar automáticamente: +- Identidad del trabajador (sesión) +- Datos del Milestone padre +- Información ya capturada en pasos anteriores +- Reglas aplicables (Bandera) -Las instancias **pueden** conectarse a servicios GPU compartidos: +El trabajador **solo aporta lo nuevo**. Nunca re-introduce datos existentes. -| Servicio | Función | Requerido | -|----------|---------|-----------| -| GRACE | Extracción IA | Opcional | -| THE FACTORY | Generación | Opcional | -| CIRCLE | Colaboración | Opcional | +### Evidencia Mínima Viable -> **Nota:** Si los servicios compartidos no están disponibles, la instancia sigue operando. Solo las funciones de IA estarán limitadas. +La evidencia requerida debe ser: +- La mínima necesaria para demostrar el resultado +- Capturable en segundos +- No interpretable (foto > descripción textual) +- Protección para el trabajador, no vigilancia + +--- + +## Resumen + +| Principio | Implementación | +|-----------|----------------| +| Resultados sobre justificaciones | Solo se registran Bloques completados | +| Ocultación legítima | Las incidencias se absorben si hay resultado | +| Acuerdo bilateral | Herramientas a cambio de resultados | +| Trazabilidad como movilidad | Portfolio verificable para ambas partes | +| Eliminación del burócrata | Arrastre automático, sin transcripción | +| Granularidad liberadora | Bloques pequeños = progreso posible | +| Evidencia mínima | Demostrar resultado, no documentar proceso | diff --git a/00_VISION/glosario.md b/00_VISION/glosario.md index 7442b7d..2b67388 100644 --- a/00_VISION/glosario.md +++ b/00_VISION/glosario.md @@ -1,7 +1,7 @@ -# Glosario TZZR +# Glosario -**Versión:** 5.0 -**Fecha:** 2024-12-24 +**Versión:** 1.0 +**Estado:** Definición --- @@ -10,7 +10,7 @@ | Término | Hash | Descripción | Estado | |---------|------|-------------|--------| | **HST** | h_maestro | Hash Semantic Tagging - Etiquetas semánticas visuales | Implementado | -| **ITM** | h_item | Ítems ideales - Definiciones del plano T0 | Planificado | +| **ITM** | h_item | Items - Definiciones del plano ideal (T0) | Planificado | | **PLY** | h_player | Players - Identidad de actores | Planificado | | **LOC** | h_loc | Locations - Ubicaciones geográficas | Planificado | | **FLG** | h_flag | Flags - Marcos jurídicos y banderas | Planificado | @@ -27,84 +27,70 @@ --- -## Componentes (Microservicios) +## Componentes del Sistema -> **Nota:** Los componentes tienen nombres de persona pero son **microservicios backend**, no IA. -> Están diseñados para consumir servicios de IA externos (APIs, RunPod), no para contenerla. +### Registro Inmutable -### secretaria (ingesta) -| Componente | Servidor | Puerto | Descripción | -|------------|----------|--------|-------------| -| **secretaria (CLARA)** | DECK | 5051 | Recoge información personal, log inmutable | -| **secretaria (MARGARET)** | CORP | 5051 | Recoge información corporativa, log inmutable | +| Componente | Nombre Personal | Nombre Corporativo | Descripción | +|------------|-----------------|-------------------|-------------| +| Secretaría | Clara | Margaret | Ingesta, log inmutable | +| Administración | Mason | Mason | Enriquecimiento, ventana flotante 24h | +| Contable | Feldman | Feldman | Consolidación final, inmutable | +| Producción | Alfred | Jared | Flujos predefinidos, orquestación | +| Auditoría | Sentinel | Sentinel | LIGHT (5min) + DEEP (1h) | -### administrativo (enriquecimiento) -| Componente | Servidor | Puerto | Descripción | -|------------|----------|--------|-------------| -| **administrativo (MASON)** | DECK | 5053 | Enriquecimiento, ventana flotante 24h | -| **administrativo (MASON)** | CORP | 5053 | Enriquecimiento, ventana flotante 24h | +### Servicios IA -### contable (consolidación) -| Componente | Servidor | Puerto | Descripción | -|------------|----------|--------|-------------| -| **contable (FELDMAN)** | DECK | 5054 | Consolidación final, blockchain, inmutable | -| **contable (FELDMAN)** | CORP | 5054 | Consolidación final, blockchain, inmutable | +| Componente | Descripción | +|------------|-------------| +| Grace | Capa cognitiva, 18 módulos IA | +| Penny | Asistente de voz conversacional | +| The Factory | Generación iterativa | -### producción (orquestación) -| Componente | Servidor | Puerto | Descripción | -|------------|----------|--------|-------------| -| **producción (ALFRED)** | DECK | 5052 | Flujos predefinidos, orden de ejecución | -| **producción (JARED)** | CORP | 5052 | Flujos predefinidos, orden de ejecución | +### Orquestación -### auditoría (planificado) -| Componente | Servidor | Puerto | Descripción | -|------------|----------|--------|-------------| -| **SENTINEL** | - | - | Auditoría LIGHT (5min) + DEEP (1h) | - ---- - -## Servicios GPU (RunPod) - -| Servicio | Endpoint | Descripción | Estado | -|----------|----------|-------------|--------| -| **GRACE** | r00x4g3rrwkbyh | IA cognitiva, extracción (18 módulos) | Bloqueado | -| **PENNY** | 0mxhaokgsmgee3 | Asistente personal, generación texto | Planificado | -| **FACTORY** | ddnuk6y35zi56a | Generación iterativa | Planificado | +| Componente | Descripción | +|------------|-------------| +| Orchestrator | Orquestación multi-agente LLM | +| Circle | Consejo de perspectivas | +| Cloudville | Laboratorio de zumbados | --- ## Servidores -| Servidor | IP | Rol | -|----------|-----|-----| -| **ARCHITECT** | 69.62.126.110 | Coordinador central, Gitea, PostgreSQL | -| **DECK** | 72.62.1.113 | Servidor personal | -| **CORP** | 92.112.181.188 | Servidor empresarial | -| **HST** | 72.62.2.84 | API tags semánticos | -| **LOCKER** | Cloudflare R2 | Almacenamiento (5 buckets) | +| Servidor | Descripción | +|----------|-------------| +| **ARCHITECT** | Coordinador central, Gitea, PostgreSQL | +| **DECK** | Servidor personal | +| **CORP** | Servidor empresarial | +| **HST** | API tags semánticos | --- -## Hashes y Identificadores +## Hashes e Identificadores -| Prefijo | Tipo | Ejemplo | Descripción | -|---------|------|---------|-------------| -| **h_maestro** | SHA-256 | `a1b2c3...` | Hash de tag HST | -| **h_instancia** | SHA-256 | `d4e5f6...` | Identidad servidor/contexto | -| **h_entrada** | SHA-256 | `g7h8i9...` | Hash contenedor ingesta | -| **h_bloque** | SHA-256 | `j0k1l2...` | Hash de bloque BCK | -| **h_milestone** | SHA-256 | `m3n4o5...` | Hash de milestone MST | -| **H_proyecto** | Prefijo | `H_tzzr_genesis` | Identificador proyecto legible | +| Prefijo | Tipo | Descripción | +|---------|------|-------------| +| **h_maestro** | SHA-256 | Hash de tag HST | +| **h_instancia** | SHA-256 | Identidad servidor/contexto | +| **h_entrada** | SHA-256 | Hash contenedor ingesta | +| **h_bloque** | SHA-256 | Hash de bloque BCK | +| **h_milestone** | SHA-256 | Hash de milestone MST | +| **h_item** | SHA-256 | Hash de ítem T0 | +| **h_player** | SHA-256 | Hash de identidad PLY | +| **h_loc** | SHA-256 | Hash de ubicación LOC | +| **h_flag** | SHA-256 | Hash de bandera FLG | --- -## Interfaces +## Aplicaciones -| Interfaz | Tipo | Descripción | -|----------|------|-------------| -| **PACKET** | App móvil | Captura multimedia, zero-retention | +| Aplicación | Tipo | Descripción | +|------------|------|-------------| +| **Packet** | App móvil | Captura multimedia, zero-retention | | **Vision Builder** | Web | Diseño de vida mediante curación visual | -| **Mind Link** | Web | Compartición de información | +| **Mind Link** | Web | Interfaz unificada de acceso | --- @@ -112,7 +98,7 @@ | Protocolo | Versión | Descripción | |-----------|---------|-------------| -| **S-CONTRACT** | v2.1 | Comunicación con IAs (context, deployment, envelope, payload) | +| **S-CONTRACT** | v2.1 | Comunicación con servicios IA | | **locker://** | - | Referencias almacenamiento R2 | --- @@ -134,9 +120,9 @@ | Estado | Etapa | Descripción | |--------|-------|-------------| -| **recibido** | CLARA/MARGARET | Contenedor ingresado, inmutable | -| **en_edicion** | MASON | Usuario enriqueciendo datos | -| **listo** | MASON | Listo para consolidar | -| **pendiente** | FELDMAN | En cola de consolidación | -| **consolidado** | FELDMAN | Registrado en milestone/bloque | -| **sellado** | NOTARIO | Blockchain confirmado | +| **recibido** | Secretaría | Contenedor ingresado, inmutable | +| **en_edicion** | Administración | Usuario enriqueciendo datos | +| **listo** | Administración | Listo para consolidar | +| **pendiente** | Contable | En cola de consolidación | +| **consolidado** | Contable | Registrado en milestone/bloque | +| **sellado** | Notario | Blockchain confirmado | diff --git a/01_ARQUITECTURA/almacenamiento.md b/01_ARQUITECTURA/almacenamiento.md new file mode 100644 index 0000000..25125b3 --- /dev/null +++ b/01_ARQUITECTURA/almacenamiento.md @@ -0,0 +1,128 @@ +# Almacenamiento + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Visión General + +El sistema utiliza Cloudflare R2 como almacenamiento de objetos. Cada instancia tiene su propio bucket independiente. + +--- + +## Buckets R2 + +| Bucket | Uso | Acceso | +|--------|-----|--------| +| **architect** | Coordinador central | Privado | +| **deck** | Servidor personal | Privado | +| **corp** | Servidor empresarial | Privado | +| **hst** | Tags semánticos (imágenes) | Público | +| **locker** | Almacenamiento general | Privado | + +--- + +## Protocolo locker:// + +Referencia interna para archivos almacenados en R2. + +### Formato + +``` +locker://{bucket}/{h_entrada}/{filename} +``` + +### Ejemplos + +``` +locker://deck/a1b2c3d4.../documento.pdf +locker://corp/f5e6d7c8.../factura.png +locker://hst/abc123.../imagen.png +``` + +--- + +## Estructura de Almacenamiento + +### Por contenedor (ingesta) + +``` +{bucket}/ +└── {h_entrada}/ + ├── archivo1.pdf + ├── archivo2.jpg + └── metadata.json +``` + +### HST (tags) + +``` +hst/ +└── {mrf}.png # mrf = SHA-256 del archivo de imagen +``` + +--- + +## Configuración R2 + +### Variables de Entorno + +```bash +R2_ENDPOINT=https://{account_id}.r2.cloudflarestorage.com +R2_ACCESS_KEY=**** +R2_SECRET_KEY=**** +R2_BUCKET={bucket_name} +``` + +### Cliente Python + +```python +import boto3 + +s3 = boto3.client( + 's3', + endpoint_url=R2_ENDPOINT, + aws_access_key_id=R2_ACCESS_KEY, + aws_secret_access_key=R2_SECRET_KEY +) + +# Subir archivo +s3.put_object( + Bucket=R2_BUCKET, + Key=f"{h_entrada}/{filename}", + Body=file_bytes +) + +# Obtener archivo +response = s3.get_object( + Bucket=R2_BUCKET, + Key=f"{h_entrada}/{filename}" +) +content = response['Body'].read() +``` + +--- + +## Política de Retención + +| Tipo de datos | Retención | Backup | +|---------------|-----------|--------| +| Archivos de ingesta | Permanente | Sí | +| Imágenes HST | Permanente | Sí | +| Temporales | 7 días | No | + +--- + +## Integridad + +Cada archivo se identifica por su hash SHA-256: + +```python +import hashlib + +def calcular_hash(contenido_bytes): + return hashlib.sha256(contenido_bytes).hexdigest() +``` + +La referencia `locker://` incluye el hash del contenedor, permitiendo verificación de integridad. diff --git a/01_ARQUITECTURA/aplicaciones/mind-link.md b/01_ARQUITECTURA/aplicaciones/mind-link.md new file mode 100644 index 0000000..1f23ab3 --- /dev/null +++ b/01_ARQUITECTURA/aplicaciones/mind-link.md @@ -0,0 +1,142 @@ +# Mind Link + +**Tipo:** Aplicación Web +**Versión:** v11 +**Estado:** En desarrollo + +--- + +## Descripción + +Interfaz de comunicación visual tipo mindmap/linktree. Presenta nodos principales (categorías) con subnodos desplegables (documentos) conectados por líneas animadas. + +--- + +## Arquitectura Visual + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ MIND LINK │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────┐ ┌─────────────────────┐ │ +│ │ NODO │──────────────│ SUBNODO 1 │ │ +│ │ 120x120 │ │ 180x101 │ │ +│ │ │──────────────│ SUBNODO 2 │ │ +│ └─────────┘ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Especificaciones Técnicas + +### Dimensiones + +| Elemento | Valor | Notas | +|----------|-------|-------| +| Nodo principal | 120×120 px | Cuadrado | +| Subnodo | 180×101 px | Ratio 16:9 | +| Radio esquinas nodo | 20px | | +| Radio esquinas subnodo | 15px | | +| Grosor líneas/bordes | 3px | | +| Gap entre subnodos | 20px | Vertical | +| Gap entre grupos | 50px | Vertical | +| Gap nodo a subnodo | 70px | Horizontal | + +### Tipografía + +| Elemento | Tamaño | Peso | Color | +|----------|--------|------|-------| +| Título página | 32px | 600 | #1a1a1a | +| Título nodo | 18px | 500 | Color del nodo | +| Título subnodo | 16px | 500 | Color del nodo | + +### Colores por Defecto + +``` +#FF6B35 - Naranja (nodo 1) +#E5A000 - Amarillo (nodo 2) +#06D6A0 - Verde (nodo 3) +#0891B2 - Cyan (nodo 4) +#3B82F6 - Azul (nodo 5) +#8B5CF6 - Violeta (nodo 6) +#EC4899 - Rosa (nodo 7) +``` + +--- + +## Iconos de Acción + +Posicionados en las 4 esquinas del subnodo: + +``` +┌─────────────────────┐ +│ 🕐 👁 │ 🕐 = historial +│ │ 👁 = preview +│ │ +│ ⬇ 🔗 │ ⬇ = descarga +└─────────────────────┘ 🔗 = enlace +``` + +| Icono | Campo | Función | +|-------|-------|---------| +| 👁 | tiene_preview | Abrir modal con documento | +| ⬇ | tiene_descarga | Descargar archivo | +| 🔗 | tiene_enlace | Copiar URL | +| 🕐 | tiene_historial | Mostrar versiones | + +--- + +## Animaciones + +| Elemento | Duración | Función | Delay | +|----------|----------|---------|-------| +| Movimiento vertical nodo | 400ms | ease-out | - | +| Revelado de líneas | 450ms | linear | 80ms | +| Aparición subnodos | instantánea | - | 30ms | + +--- + +## Estructura de Datos + +### Configuración + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| titulo | String | Título principal | +| mostrar_titulo | Boolean | Mostrar/ocultar | +| colores | String | Hex separados por comas | +| fuente | String | Nombre de fuente | + +### Nodos + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | String | Identificador único | +| titulo | String | Nombre categoría | +| imagen | URL | Imagen 120×120 | +| subnodos | Array | Lista documentos | + +### Subnodos + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | String | Identificador único | +| titulo | String | Nombre documento | +| imagen | URL | Miniatura 16:9 | +| tiene_preview | Boolean | Icono ojo | +| tiene_descarga | Boolean | Icono descarga | +| tiene_enlace | Boolean | Icono enlace | +| tiene_historial | Boolean | Icono reloj | + +--- + +## Pendiente + +- [ ] Conexión real con API +- [ ] Sistema de autenticación con PIN +- [ ] Funcionalidad real de iconos +- [ ] Responsive móvil +- [ ] Integración en aplicación empresarial diff --git a/01_ARQUITECTURA/aplicaciones/packet.md b/01_ARQUITECTURA/aplicaciones/packet.md new file mode 100644 index 0000000..bcd00d6 --- /dev/null +++ b/01_ARQUITECTURA/aplicaciones/packet.md @@ -0,0 +1,74 @@ +# Packet + +**Tipo:** App Móvil +**Estado:** En desarrollo + +--- + +## Descripción + +App móvil de captura multimedia con política **zero-retention**. No almacena datos localmente; todo va directo al servidor. + +--- + +## Principio + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ PACKET no almacena datos localmente. │ +│ Todo va directo al servidor. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Flujo + +``` +Captura (foto/audio/texto) + │ + ▼ + Upload inmediato + │ + ▼ +Clara/Margaret (secretaría) + │ + ▼ + Confirmación al usuario + │ + ▼ + Borrado local +``` + +--- + +## Tipos de Captura + +| Tipo | Formato | Uso | +|------|---------|-----| +| **Foto** | JPEG/PNG | Evidencia visual | +| **Audio** | MP3/WAV | Notas de voz | +| **Texto** | String | Notas rápidas | +| **Ubicación** | Coords | Geolocalización | + +--- + +## Características + +| Característica | Valor | +|----------------|-------| +| Almacenamiento local | NO | +| Funciona offline | NO (requiere conexión) | +| Autenticación | Token de sesión | +| Destino | Secretaría (Clara/Margaret) | + +--- + +## Integración + +``` +PACKET ──► Clara (secretaría) ──► Mason (si no encaja) ──► Feldman + └──► Feldman (si encaja) +``` diff --git a/01_ARQUITECTURA/aplicaciones/vision-builder.md b/01_ARQUITECTURA/aplicaciones/vision-builder.md new file mode 100644 index 0000000..39503b3 --- /dev/null +++ b/01_ARQUITECTURA/aplicaciones/vision-builder.md @@ -0,0 +1,67 @@ +# Vision Builder + +**Tipo:** Aplicación Web +**Estado:** En desarrollo + +--- + +## Descripción + +Sistema de construcción de visiones que se convierten en acciones medibles. + +--- + +## Flujo Principal + +``` +VISIÓN → MST (Milestones) → ACCIONES + HÁBITOS → BCK (Evidencias) +``` + +--- + +## Tipos de Visión + +| Tipo | Descripción | Ejemplo | +|------|-------------|---------| +| **fitness** | Transformación física | Perder 10kg en 12 semanas | +| **career** | Desarrollo profesional | Conseguir ascenso | +| **project** | Proyecto específico | Lanzar MVP | +| **habit** | Crear hábito | Meditar diario | +| **learning** | Aprendizaje | Dominar Python | +| **finance** | Finanzas | Ahorrar €10k | + +--- + +## Acciones vs Hábitos + +| Concepto | Descripción | Ejemplo | +|----------|-------------|---------| +| **Acción** | Tarea única | Comprar báscula | +| **Hábito** | Comportamiento recurrente | Entrenar 5x/semana | + +--- + +## Tablas + +```sql +deck_visiones -- Visiones del usuario +deck_milestones -- MST: objetivos con fecha +deck_acciones -- Tareas únicas +deck_habitos -- Comportamientos recurrentes +deck_habito_registros -- Tracking diario de hábitos +deck_bck -- Bloques de evidencia +``` + +--- + +## Integración con HST + +Cada visión puede etiquetarse con tags HST: + +```json +{ + "vision": "Transformación física", + "tags": ["fitness", "health", "body"], + "h_maestro": ["abc...", "def...", "ghi..."] +} +``` diff --git a/01_ARQUITECTURA/entidades/corp.md b/01_ARQUITECTURA/entidades/corp.md new file mode 100644 index 0000000..e736dd5 --- /dev/null +++ b/01_ARQUITECTURA/entidades/corp.md @@ -0,0 +1,89 @@ +# CORP + +**Tipo:** Servidor Empresarial +**IP:** 92.112.181.188 +**Dominio:** tzzrcorp.me +**Estado:** Operativo + +--- + +## Descripción + +CORP es el **servidor empresarial** del ecosistema. Gestiona múltiples usuarios y mayor complejidad que DECK. + +--- + +## Arquitectura + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ CORP │ +│ (Servidor Empresarial) │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ DIRECTUS │ │ NEXTCLOUD │ │ ODOO │ │ +│ │ (admin) │ │ (archivos) │ │ (ERP) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────────────────┐ │ +│ │ Componentes Internos │ │ +│ │ Margaret (secretaría) │ Jared (producción) │ │ +│ │ Mason (administración) │ Feldman (contable) │ │ +│ │ Sentinel (auditoría) │ │ +│ └───────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ PostgreSQL + Redis │ │ +│ └───────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Componentes Internos + +| Componente | Puerto | Función | +|------------|--------|---------| +| Margaret (secretaría) | 5051 | Ingesta inmutable, múltiples usuarios | +| Jared (producción) | 5052 | Flujos predefinidos, mayor complejidad | +| Mason (administración) | 5053 | Enriquecimiento | +| Feldman (contable) | 5054 | Consolidación | +| Sentinel (auditoría) | - | Verificación LIGHT/DEEP | + +--- + +## Servicios Docker + +| Servicio | Puerto | Estado | +|----------|--------|--------| +| Directus | 8055 | OK | +| Nextcloud | 8080 | OK | +| Vaultwarden | 8081 | OK | +| Shlink | 8082 | OK | +| Odoo | 8069 | OK | +| ntfy | 8880 | OK | +| HSU API | 5002 | OK | +| Redis | 6379 | OK | +| PostgreSQL | 5432 | OK | +| Caddy | 80/443 | OK | + +--- + +## Almacenamiento + +- **Bucket R2:** corp +- **Protocolo:** locker://corp/... + +--- + +## Diferencias con DECK + +| Aspecto | DECK | CORP | +|---------|------|------| +| Dominio | tzzrdeck.me | tzzrcorp.me | +| Usuarios | Uno | Múltiples | +| Secretaría | Clara | Margaret | +| Producción | Alfred | Jared | +| Complejidad | Menor | Mayor | diff --git a/01_ARQUITECTURA/entidades/deck.md b/01_ARQUITECTURA/entidades/deck.md new file mode 100644 index 0000000..d1526c6 --- /dev/null +++ b/01_ARQUITECTURA/entidades/deck.md @@ -0,0 +1,254 @@ +# DECK + +**Tipo:** Servidor Personal +**IP:** 72.62.1.113 +**Dominio:** tzzrdeck.me + +--- + +## Rol en el Ecosistema + +DECK es el **servidor central** y **punto de iniciación** de todas las conexiones hacia los servicios de IA. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRINCIPIO DECK │ +│ │ +│ "DECK inicia TODAS las conexiones. Los servicios son │ +│ stateless. Cada request lleva su contexto completo." │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Funciones + +| Función | Descripción | +|---------|-------------| +| **Iniciador** | Todas las llamadas a GRACE, PENNY, FACTORY se originan aquí | +| **Gestor contexto** | Envía información de contexto con cada request | +| **Router despliegue** | Decide si usar self-hosted o externos | +| **Almacén credenciales** | Gestiona API keys vía Key Vault | + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ DECK │ +│ (Servidor Central) │ +├─────────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ PostgreSQL │ │ Directus │ │ FileBrowser │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Vaultwarden │ │ Shlink │ │ Addy.io │ │ +│ │ (Key Vault) │ │ │ │ │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ DEPLOYMENT ROUTER │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ GRACE │ │ PENNY │ │ FACTORY │ │ │ +│ │ │ Connector │ │ Connector │ │ Connector │ │ │ +│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ +│ └─────────┼────────────────┼────────────────┼───────────────┘ │ +└────────────┼────────────────┼────────────────┼──────────────────┘ + │ │ │ + ▼ ▼ ▼ + ┌────────────────────────────────────────────────────┐ + │ SERVICIOS DE IA │ + ├────────────────────────────────────────────────────┤ + │ ┌─────────────────┐ ┌─────────────────┐ │ + │ │ SELF-HOSTED │ │ EXTERNAL │ │ + │ │ (RunPod/GPU) │ │ (APIs) │ │ + │ ├─────────────────┤ ├─────────────────┤ │ + │ │ Faster Whisper │ │ Groq API │ │ + │ │ Local LLM │ │ OpenRouter │ │ + │ │ Tesseract │ │ OpenAI │ │ + │ │ Embeddings │ │ Anthropic │ │ + │ └─────────────────┘ └─────────────────┘ │ + └────────────────────────────────────────────────────┘ +``` + +--- + +## Componentes Internos + +| Componente | Puerto | Función | +|------------|--------|---------| +| Clara (secretaría) | 5051 | Ingesta inmutable | +| Alfred (producción) | 5052 | Flujos predefinidos | +| Mason (administración) | 5053 | Enriquecimiento | +| Feldman (contable) | 5054 | Consolidación | + +--- + +## Modos de Despliegue + +### EXTERNAL + +```yaml +grace: + mode: EXTERNAL + external: + providers_allowed: [groq, openrouter, openai, anthropic] +``` + +- Todas las llamadas a proveedores externos +- Sin infraestructura propia +- Ideal para: inicio rápido, bajo volumen + +### SELF_HOSTED + +```yaml +grace: + mode: SELF_HOSTED + self_hosted: + endpoint: "https://your-runpod-endpoint.runpod.net" + timeout_ms: 30000 +``` + +- Todas las llamadas a infraestructura propia +- Requiere RunPod/GPU configurado +- Ideal para: privacidad total, alto volumen + +### SEMI (Recomendado) + +```yaml +grace: + mode: SEMI + tier_preference: + - SELF_HOSTED + - EXTERNAL + self_hosted: + endpoint: "..." + retry_on_failure: true + external: + providers_allowed: [groq, openrouter] +``` + +- Intenta self-hosted primero +- Fallback automático a external +- Balance privacidad/disponibilidad + +--- + +## Flujo de Request + +``` +┌──────────┐ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ +│ Cliente │────▶│ DECK │────▶│ Deployment │────▶│ Servicio │ +│ │ │ │ │ Router │ │ (IA) │ +└──────────┘ └────┬─────┘ └───────┬───────┘ └──────────────┘ + │ │ + │ 1. Recibe request │ + │ 2. Lee config/deployment.yaml + │ 3. Obtiene credenciales de Vaultwarden + │ 4. Construye S-CONTRACT request + │ │ + │ ┌────────▼────────┐ + │ │ mode: SEMI │ + │ ├─────────────────┤ + │ │ 1. Try SELF_HOSTED + │ │ 2. If fail → EXTERNAL + │ └─────────────────┘ +``` + +--- + +## Servicios Docker + +| Servicio | Puerto | Descripción | +|----------|--------|-------------| +| PostgreSQL | 5432 | Base de datos principal | +| Directus | 8055 | Interface datos | +| FileBrowser | 8081 | Gestión archivos | +| Vaultwarden | 8082 | Key Vault | +| Shlink | 8083 | Acortador URLs | +| Addy.io | 8084 | Alias correo | +| Redis | 6379 | Cache y colas | + +--- + +## Conectores de IA + +### GRACE Connector + +- Procesa requests de módulos GRACE +- Soporta todos los M-CONTRACTs +- Maneja fallback chain según M-CONTRACT + +### PENNY Connector + +- Conexión WebSocket para real-time voice +- Sesiones de voz bidireccionales +- Integración con ASR/TTS + +### FACTORY Connector + +- Jobs de generación iterativa +- Gestión de iteraciones y convergencia +- Tracking de costos + +--- + +## Gestión de Credenciales + +Las credenciales se referencian con URIs `kv://`: + +| URI | Contenido | +|-----|-----------| +| `kv://deck/credentials/groq` | API key Groq | +| `kv://deck/credentials/openrouter` | API key OpenRouter | +| `kv://deck/credentials/runpod` | API key RunPod | +| `kv://deck/credentials/grace` | Credenciales consolidadas GRACE | +| `kv://deck/credentials/penny` | Credenciales PENNY | +| `kv://deck/credentials/factory` | Credenciales FACTORY | + +--- + +## Integración S-CONTRACT + +DECK construye requests siguiendo S-CONTRACT v2.1: + +```json +{ + "contract_version": "2.1", + "profile": "FULL", + "envelope": { + "trace_id": "uuid-generado-por-deck", + "idempotency_key": "sha256-del-contenido" + }, + "routing": { + "module": "ASR_ENGINE" + }, + "deployment": { + "mode": "SEMI", + "tier_preference": ["SELF_HOSTED", "EXTERNAL"], + "credentials_ref": "kv://deck/credentials/grace" + }, + "payload": { + "type": "audio", + "encoding": "url", + "content": "https://storage.tzzrdeck.me/audio/file.mp3" + } +} +``` + +--- + +## Estructura de Directorios + +``` +deck/ +├── config/ +│ └── deployment.yaml # Modos de despliegue +├── docker-compose.yml # Servicios +├── docs/ +│ ├── ARQUITECTURA.md +│ └── ESPECIFICACION_SERVIDOR.md +├── .env # Variables entorno +└── README.md +``` diff --git a/01_ARQUITECTURA/entidades/flg.md b/01_ARQUITECTURA/entidades/flg.md new file mode 100644 index 0000000..63fbe4b --- /dev/null +++ b/01_ARQUITECTURA/entidades/flg.md @@ -0,0 +1,58 @@ +# FLG - Flags + +**Tipo:** Marcos Jurídicos +**Estado:** Planificado + +--- + +## Descripción + +Los FLG representan **marcos jurídicos**, normativas y países. Determinan las reglas aplicables a cada operación. + +--- + +## Uso + +| Contexto | Ejemplo | +|----------|---------| +| País | España, México, Argentina | +| Normativa | RGPD, SOX, ISO 27001 | +| Jurisdicción | UE, LATAM, USA | + +--- + +## Grupos HST Relacionados + +Actualmente existe un grupo `flg` en HST con 65 tags de países, pero no como entidad independiente con schema propio. + +--- + +## Schema Propuesto + +```sql +CREATE TABLE flags ( + id BIGSERIAL PRIMARY KEY, + h_flag VARCHAR(64) UNIQUE NOT NULL, + tipo VARCHAR(50) NOT NULL, + codigo VARCHAR(10), + nombre VARCHAR(255) NOT NULL, + normativas JSONB, + metadata JSONB, + activo BOOLEAN DEFAULT true, + created_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +--- + +## Aplicación en Bloques + +Cada bloque puede tener un FLG asociado: + +```json +{ + "h_bloque": "abc123...", + "h_flag": "esp...", + "implicacion": "RGPD aplica, retención 5 años" +} +``` diff --git a/01_ARQUITECTURA/entidades/hst.md b/01_ARQUITECTURA/entidades/hst.md new file mode 100644 index 0000000..563d126 --- /dev/null +++ b/01_ARQUITECTURA/entidades/hst.md @@ -0,0 +1,95 @@ +# HST - Hash Semantic Tagging + +**Tipo:** Sistema de Etiquetado +**Estado:** Implementado + +--- + +## Descripción + +Sistema de etiquetas semánticas visuales de 3 caracteres. Cada tag tiene un hash SHA-256 único (h_maestro) y una imagen asociada. + +--- + +## Fórmula + +``` +h_maestro = SHA-256(grupo:ref) +``` + +--- + +## Grupos + +| Grupo | Cantidad | Descripción | +|-------|----------|-------------| +| **hst** | 639 | Tags del sistema base | +| **spe** | 145 | Tags específicos | +| **vsn** | 84 | Visiones | +| **flg** | 65 | Banderas/países | +| **vue** | 21 | Vistas | + +**Total:** 954 tags maestros + +--- + +## Schema + +```sql +CREATE TABLE hst_tags ( + id SERIAL PRIMARY KEY, + ref VARCHAR(100) UNIQUE NOT NULL, + h_maestro VARCHAR(64) UNIQUE NOT NULL, + grupo VARCHAR(50) NOT NULL, + nombre_es VARCHAR(255), + nombre_en VARCHAR(255), + descripcion TEXT, + imagen_url TEXT, + activo BOOLEAN DEFAULT true, + version INTEGER DEFAULT 1, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +--- + +## Ejemplo + +```json +{ + "ref": "person", + "h_maestro": "a1b2c3d4e5f6...", + "grupo": "hst", + "nombre_es": "Persona", + "nombre_en": "Person", + "imagen_url": "https://tzrtech.org/a1b2c3d4e5f6...png" +} +``` + +--- + +## Extensiones de Usuario + +Los usuarios pueden crear sus propios tags: + +| Tabla | Descripción | +|-------|-------------| +| **hsu** | HST Usuario | +| **spu** | SPE Usuario | +| **vsu** | VSN Usuario | +| **vuu** | VUE Usuario | +| **pju** | Proyectos Usuario | +| **flu** | FLG Usuario | + +--- + +## API + +**Base URL:** https://tzrtech.org/api + +| Endpoint | Método | Descripción | +|----------|--------|-------------| +| /tags | GET | Lista todos los tags | +| /tags/{ref} | GET | Obtiene tag por ref | +| /tags/search | GET | Busca tags | +| /biblioteca | GET | Tags de usuario | diff --git a/01_ARQUITECTURA/entidades/itm.md b/01_ARQUITECTURA/entidades/itm.md new file mode 100644 index 0000000..a323953 --- /dev/null +++ b/01_ARQUITECTURA/entidades/itm.md @@ -0,0 +1,84 @@ +# ITM - Items + +**Tipo:** Entidad de Plano Ideal +**Estado:** Planificado + +--- + +## Descripción + +Los ITM representan el **plano ideal** (T0). Son la "partitura" que guía las acciones. Definiciones perfectas de lo que debería existir o lograrse. + +--- + +## Concepto + +| Aspecto | Descripción | +|---------|-------------| +| **Naturaleza** | Ideal, abstracta | +| **Temporalidad** | T-N → T-1 → T0 | +| **Energía** | No consume (es definición) | +| **Mutabilidad** | Versionable | + +--- + +## Tipos de Items + +| Tipo | Descripción | +|------|-------------| +| **accion_ideal** | Acción que debería ejecutarse | +| **objetivo** | Meta a alcanzar | +| **requerimiento** | Requisito a cumplir | + +--- + +## Schema Propuesto + +```sql +CREATE TABLE items ( + id BIGSERIAL PRIMARY KEY, + h_item VARCHAR(64) UNIQUE NOT NULL, + h_instancia VARCHAR(64) NOT NULL, + secuencia BIGINT NOT NULL, + hash_previo VARCHAR(64), + hash_contenido VARCHAR(64), + tipo_item VARCHAR(50) NOT NULL, + titulo VARCHAR(255) NOT NULL, + descripcion TEXT, + criterios_aceptacion JSONB, + metadata JSONB, + proyecto_tag VARCHAR(64), + etiquetas JSONB, + estado VARCHAR(20) DEFAULT 'draft', + created_at TIMESTAMPTZ DEFAULT NOW(), + created_by VARCHAR(64), + UNIQUE (h_instancia, secuencia) +); +``` + +--- + +## Fórmula Hash + +``` +h_item = SHA-256(h_instancia:secuencia:tipo:contenido) +``` + +--- + +## Relación con Otros Planos + +``` +ITM (ideal) + │ + ├──► h_maestro (HST tags) + ├──► h_player (PLY responsable) + ├──► h_loc (LOC ubicación) + └──► h_flag (FLG normativa) + │ + ▼ materializa + MST (milestone) + │ + ▼ genera + BCK (bloque) +``` diff --git a/01_ARQUITECTURA/entidades/loc.md b/01_ARQUITECTURA/entidades/loc.md new file mode 100644 index 0000000..6474c7f --- /dev/null +++ b/01_ARQUITECTURA/entidades/loc.md @@ -0,0 +1,67 @@ +# LOC - Locations + +**Tipo:** Ubicaciones Geográficas +**Estado:** Planificado + +--- + +## Descripción + +Los LOC representan **ubicaciones geográficas** con precisión variable. + +--- + +## Tipos + +| Tipo | Descripción | +|------|-------------| +| **punto** | Coordenadas exactas | +| **area** | Zona delimitada | +| **ruta** | Trayecto entre puntos | + +--- + +## Schema Propuesto + +```sql +CREATE TABLE locations ( + id BIGSERIAL PRIMARY KEY, + h_loc VARCHAR(64) UNIQUE NOT NULL, + nombre VARCHAR(255), + lat DECIMAL(10, 8), + lon DECIMAL(11, 8), + precision_metros INTEGER, + tipo VARCHAR(50), + metadata JSONB, + created_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +--- + +## Fórmula Hash + +``` +h_loc = SHA-256(lat:lon:precision) +``` + +--- + +## Uso en el Sistema + +Cada bloque puede tener una ubicación asociada: + +```json +{ + "h_bloque": "abc123...", + "h_loc": "geo456...", + "lat": 41.6488, + "lon": -0.8891, + "precision_metros": 10 +} +``` + +Esto permite: +- Geolocalización de evidencias +- Verificación de ubicación +- Análisis espacial de operaciones diff --git a/01_ARQUITECTURA/entidades/ply.md b/01_ARQUITECTURA/entidades/ply.md new file mode 100644 index 0000000..2f72533 --- /dev/null +++ b/01_ARQUITECTURA/entidades/ply.md @@ -0,0 +1,64 @@ +# PLY - Players + +**Tipo:** Entidad de Identidad +**Estado:** Planificado + +--- + +## Descripción + +Los PLY representan la **identidad de actores** en el sistema: personas, empresas, agentes. + +--- + +## Tipos + +| Tipo | Descripción | +|------|-------------| +| **persona** | Usuario individual | +| **empresa** | Organización | +| **agente** | Sistema automatizado | + +--- + +## Schema Propuesto + +```sql +CREATE TABLE players ( + id BIGSERIAL PRIMARY KEY, + h_player VARCHAR(64) UNIQUE NOT NULL, + tipo VARCHAR(50) NOT NULL, + nombre VARCHAR(255) NOT NULL, + email VARCHAR(255), + metadata JSONB, + activo BOOLEAN DEFAULT true, + created_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +--- + +## Fórmula Hash + +``` +h_player = SHA-256(tipo:identificador_unico) +``` + +--- + +## Uso en el Sistema + +Cada acción registrada incluye referencia al player: + +```json +{ + "h_bloque": "abc123...", + "h_player": "def456...", + "accion": "completó tarea X" +} +``` + +Esto permite: +- Trazabilidad de quién hizo qué +- Portfolio verificable por persona +- Auditoría sin ambigüedad diff --git a/01_ARQUITECTURA/overview.md b/01_ARQUITECTURA/overview.md index 06a0609..255fca5 100644 --- a/01_ARQUITECTURA/overview.md +++ b/01_ARQUITECTURA/overview.md @@ -1,167 +1,221 @@ -# Arquitectura Overview +# Overview -**Versión:** 5.0 -**Fecha:** 2024-12-24 +**Versión:** 1.1 +**Estado:** Definición --- -## Principio Fundamental +## Visión General -> **ARCHITECT es el constructor. DECK y CORP son instancias autónomas.** - -- **ARCHITECT**: Construye, despliega, coordina. NO es dependencia runtime. -- **DECK/CORP**: Operan independientemente. Funcionan si ARCHITECT está caído. +Sistema de arquitecturas personales y empresariales. ARCHITECT (con Claude) construye y despliega servidores autónomos (DECK, CORP) que operan independientemente. Los componentes internos son microservicios backend, no IA. Las instancias están diseñadas para **consumir** servicios de IA externos (APIs, RunPod), no para contenerla. --- -## Diagrama General +## Arquitectura General ``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ CAPA DE CONSTRUCCIÓN (solo deploy/dev) │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────┐ │ -│ │ ARCHITECT (69.62.126.110) │ │ -│ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌───────────────┐ │ │ -│ │ │PostgreSQL│ │ Gitea │ │Orchestrator│ │ Infisical │ │ │ -│ │ │ contexto │ │ 25 repos │ │ v5 │ │ (master) │ │ │ -│ │ └──────────┘ └──────────┘ └────────────┘ └───────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────┘ │ -│ │ -│ Rol: Construcción, deployment, gestión central de secretos │ -│ NO es dependencia runtime de las instancias │ -└─────────────────────────────────────────────────────────────────────────┘ - │ │ │ - deploy │ │ │ deploy - ▼ ▼ ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ CAPA DE INSTANCIAS (autónomas) │ -│ │ -│ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ -│ │ DECK │ │ CORP │ │ HST │ │ -│ │ 72.62.1.113 │ │ 92.112.181.188 │ │ 72.62.2.84 │ │ -│ │ │ │ │ │ │ │ -│ │ secretaria :5051 │ │ secretaria :5051 │ │ 973 tags │ │ -│ │ producción :5052 │ │ producción :5052 │ │ API pública │ │ -│ │ admin.vo :5053 │ │ admin.vo :5053 │ │ Directus :8055 │ │ -│ │ contable :5054 │ │ contable :5054 │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ Vaultwarden │ │ Odoo │ │ │ │ -│ └───────────────────┘ │ Nextcloud │ └───────────────────┘ │ -│ │ └───────────────────┘ │ │ -│ │ │ │ │ -│ └─────────────────────────┼──────────────────────┘ │ -│ │ │ -└───────────────────────────────────┼─────────────────────────────────────┘ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ CAPA DE ALMACENAMIENTO │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────┐ │ -│ │ Cloudflare R2 │ │ -│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │ -│ │ │architect │ │ deck │ │ corp │ │ hst │ │ locker │ │ │ -│ │ │ backups │ │ archivos │ │ archivos │ │ imágenes │ │ general│ │ │ -│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ CAPA GPU (RunPod) │ -│ Estado: BLOQUEADO - Workers no inician │ -│ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ GRACE │ │ PENNY │ │ FACTORY │ │ -│ │ 18 módulos│ │ texto │ │generación│ │ -│ └──────────┘ └──────────┘ └──────────┘ │ -└─────────────────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────┐ +│ ARCHITECT │ +│ PostgreSQL central │ Gitea │ Orchestrator │ Infisical │ +└─────────────────────────────────────────────────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ DECK │ │ CORP │ │ HST │ +│ │ │ │ │ │ +│ secretaría │ │ secretaría │ │ 973 tags │ +│ administración │ │ administración │ │ API pública │ +│ contable │ │ contable │ │ │ +│ producción │ │ producción │ │ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ + └────────┬───────────┘ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Cloudflare R2 (buckets) │ +│ architect │ deck │ corp │ hst │ locker │ +└─────────────────────────────────────────────────────────────┘ ``` --- -## Flujo de Datos +## Flujo de Datos Principal + +El sistema tiene dos flujos según el origen y completitud de la información: + +### Diagrama General ``` -PACKET (App móvil) - │ - │ POST /ingest - │ Content-Type: multipart/form-data - │ X-Auth-Key: {h_instancia} - │ - ▼ -┌─────────────────────────────────────────┐ -│ secretaria (CLARA / MARGARET) │ -│ │ -│ 1. Validar h_instancia │ -│ 2. Calcular h_entrada (SHA-256) │ -│ 3. Subir archivos a R2 │ -│ 4. Insertar en log │ -│ 5. Estado: 'recibido' (inmutable) │ -└─────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ producción (ALFRED / JARED) │ -│ │ -│ 1. Definir orden de ejecución │ -│ 2. Aplicar flujos predefinidos │ -│ 3. Enviar a administrativo │ -└─────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ administrativo (MASON) │ -│ │ -│ 1. Recibir de producción │ -│ 2. Ventana flotante 24h │ -│ 3. Usuario enriquece datos │ -│ 4. [Futuro] GRACE extrae información │ -│ 5. Enviar a contable cuando listo │ -└─────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ contable (FELDMAN) │ -│ │ -│ 1. Recibir en cola │ -│ 2. Validar reglas (M-001, M-002, M-003)│ -│ 3. Crear milestone o bloque │ -│ 4. Calcular hash_contenido │ -│ 5. Encadenar (hash_previo) │ -│ 6. [Futuro] Merkle tree, blockchain │ -└─────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ auditoría (SENTINEL) [planificado] │ -│ │ -│ LIGHT: Cada 5 min, reglas automáticas │ -│ DEEP: Cada 1 hora, muestreo con LLM │ -└─────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────┐ +│ ORÍGENES │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────────────┐ │ +│ │ PRODUCCIÓN │ │ ENTRADA MANUAL │ │ +│ │ Alfred/Jared │ │ (Packet, API, manual) │ │ +│ │ │ │ │ │ +│ │ Flujos │ │ Datos incompletos │ │ +│ │ predefinidos │ │ o ad-hoc │ │ +│ └────────┬────────┘ └────────────┬────────────┘ │ +│ │ │ │ +│ │ (datos completos) │ │ +│ │ │ │ +└───────────┼─────────────────────────────────┼──────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────┐ +│ SECRETARÍA │ +│ Clara / Margaret │ +├─────────────────────────────────────────────────────────────┤ +│ • Punto de entrada ÚNICO │ +│ • Log inmutable │ +│ • Todo se registra exactamente como llega │ +└─────────────────────────────────────────────────────────────┘ + │ + │ + ┌────────┴────────┐ + │ │ +encaja no encaja + │ │ + │ ▼ + │ ┌─────────────────────────────────────────────────────┐ + │ │ ADMINISTRACIÓN │ + │ │ Mason │ + │ ├─────────────────────────────────────────────────────┤ + │ │ • Ventana de enriquecimiento (24h configurable) │ + │ │ • Usuario completa/corrige datos │ + │ │ • Auto-envío si expira el tiempo │ + │ └──────────────────────────┬──────────────────────────┘ + │ │ + └───────────────┬───────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ CONTABLE │ +│ Feldman │ +├─────────────────────────────────────────────────────────────┤ +│ • Cola de validación (24h configurable) │ +│ • Consolidación en bloques inmutables │ +│ • Encadenamiento hash (blockchain-style) │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Regla de Decisión + +| Condición | Ruta | Descripción | +|-----------|------|-------------| +| **Encaja** | Secretaría → Feldman | Datos completos de flujo predefinido | +| **No encaja** | Secretaría → Mason → Feldman | Entrada manual o incompleta | + +### Flujo de Producción (encaja) + +Cuando la información viene de un proceso predefinido y está completa: + +``` +Producción (Alfred/Jared) + │ + │ flujo predefinido completo + ▼ +Secretaría (Clara/Margaret) ← Registro inmutable + │ + │ directo (salta Mason) + ▼ +Contable (Feldman) ← Consolidación +``` + +### Flujo Estándar (no encaja) + +Cuando la información es manual, ad-hoc o incompleta: + +``` +Entrada manual (Packet, API, etc.) + │ + ▼ +Secretaría (Clara/Margaret) ← Registro inmutable + │ + ▼ +Administración (Mason) ← Enriquecimiento (24h) + │ + ▼ +Contable (Feldman) ← Consolidación ``` --- -## Comunicación Entre Servidores +## Auditoría (Sentinel) -| Origen | Destino | Protocolo | Puerto | Propósito | -|--------|---------|-----------|--------|-----------| -| DECK | ARCHITECT | SSH/Git | 2222 | Push repos | -| DECK | R2 | HTTPS | 443 | Almacenamiento | -| CORP | ARCHITECT | SSH/Git | 2222 | Push repos | -| CORP | R2 | HTTPS | 443 | Almacenamiento | -| HST | ARCHITECT | SSH | 22 | Administración | -| ARCHITECT | DECK | SSH | 22 | Deploy | -| ARCHITECT | CORP | SSH | 22 | Deploy | +Sentinel es un **componente independiente** que opera de forma transversal sobre el sistema: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SENTINEL │ +│ (Auditoría) │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Monitoriza y verifica integridad en múltiples puntos: │ +│ │ +│ • Secretaría ←── verifica inmutabilidad del log │ +│ • Mason ←── verifica ventanas temporales │ +│ • Feldman ←── verifica encadenamiento de bloques │ +│ │ +│ Modos de operación: │ +│ • LIGHT: verificación rápida (cada 5 min) │ +│ • DEEP: auditoría completa (cada 1h) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` --- -## Bases de Datos +## Principio de Diseño -| Servidor | Database | Tablas Principales | -|----------|----------|--------------------| -| ARCHITECT | architect | context_blocks, agents, creds_* | -| DECK | tzzr | clara_log, alfred_executions | -| CORP | corp | margaret_log, mason_workspace, feldman_* | -| HST | hst_images | hst_tags, hst_trees | +### Constructores, no gestores + +ARCHITECT (con Claude) es un **constructor de arquitecturas**, no un gestor permanente. + +- ARCHITECT diseña y construye la arquitectura de cada servidor +- Cuando la arquitectura esté madura, el servidor será **clonable e independiente** +- DECK y CORP funcionan sin conexión a ARCHITECT ni a Claude +- Son sistemas diseñados para **usar** servicios de IA (APIs externas, RunPod), no para contenerla + +### Descentralización operativa + +``` +Architect App (centralizado) → Diseña moldes + ↓ + Instancias reales (descentralizadas) + ↓ + Cada una con su CORP, su DECK, sus agentes +``` + +--- + +## Modelo de Instancias + +**DECK** y **CORP** son plantillas. En producción habrá múltiples instancias: + +| Tipo | Ejemplos | +|------|----------| +| **DECK** | "Deck de Juan", "Deck de Victoria" | +| **CORP** | "Empresa A SL", "Empresa B Corp" | + +Cada instancia: +- Tiene su propio bucket de almacenamiento +- Puede renombrar sus agentes +- **Opera de forma autónoma** (no depende de ARCHITECT en runtime) +- Tiene su propio gestor de secretos +- Hace sus propios backups a R2 + +--- + +## Servicios Compartidos (Opcionales) + +Las instancias **pueden** conectarse a servicios GPU compartidos: + +| Servicio | Función | Requerido | +|----------|---------|-----------| +| Grace | Extracción IA | Opcional | +| The Factory | Generación | Opcional | +| Circle | Colaboración | Opcional | + +> **Nota:** Si los servicios compartidos no están disponibles, la instancia sigue operando. Solo las funciones de IA estarán limitadas. diff --git a/01_ARQUITECTURA/servidores.md b/01_ARQUITECTURA/servidores.md deleted file mode 100644 index 00b4f8f..0000000 --- a/01_ARQUITECTURA/servidores.md +++ /dev/null @@ -1,230 +0,0 @@ -# Servidores TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## ARCHITECT (69.62.126.110) - -**Rol:** Coordinador central del sistema - -### Servicios - -| Servicio | Puerto | Estado | -|----------|--------|--------| -| PostgreSQL | 5432 | Operativo | -| Gitea | 3000 (HTTP), 2222 (SSH) | Operativo | -| Orchestrator App | 5050 | Operativo | -| Infisical | 8082 | Operativo | - -### PostgreSQL (database: architect) - -| Tabla | Descripción | -|-------|-------------| -| context_blocks | 30 bloques de contexto atómicos | -| agent_context_index | Asignaciones agente-bloque | -| agents | 6 agentes definidos | -| creds_* | 6 tablas de credenciales | -| s_contract_contexts | Contextos IA | -| s_contract_datasets | Datasets IA | - -### Acceso - -```bash -# SSH -ssh orchestrator@69.62.126.110 - -# PostgreSQL -sudo -u postgres psql -d architect - -# Gitea -http://localhost:3000 -``` - ---- - -## DECK (72.62.1.113) - -**Rol:** Servidor personal - -### Servicios - -| Servicio | Puerto | Estado | -|----------|--------|--------| -| CLARA | 5051 | Operativo | -| ALFRED | 5052 | Operativo | -| Mailcow (15 containers) | SMTP, IMAP | Operativo | -| Directus | 8055 | Operativo | -| FileBrowser | 8082 | Operativo | -| Shlink | 8083 | Operativo | -| Vaultwarden | 8085 | Operativo | -| ntfy | 8080 | Operativo | - -### PostgreSQL (database: tzzr) - -| Tabla | Descripción | -|-------|-------------| -| clara_log | Log inmutable de ingesta | -| deck_visiones | Visiones personales | -| deck_milestones | Milestones personales | -| deck_acciones | Acciones | -| deck_habitos | Hábitos | -| deck_bck | Bloques | - -### Docker Containers - -``` -mailcowdockerized-acme-mailcow-1 -mailcowdockerized-clamd-mailcow-1 -mailcowdockerized-dovecot-mailcow-1 -mailcowdockerized-mysql-mailcow-1 -mailcowdockerized-netfilter-mailcow-1 -mailcowdockerized-nginx-mailcow-1 -mailcowdockerized-olefy-mailcow-1 -mailcowdockerized-php-fpm-mailcow-1 -mailcowdockerized-postfix-mailcow-1 -mailcowdockerized-redis-mailcow-1 -mailcowdockerized-rspamd-mailcow-1 -mailcowdockerized-sogo-mailcow-1 -mailcowdockerized-solr-mailcow-1 -mailcowdockerized-unbound-mailcow-1 -mailcowdockerized-watchdog-mailcow-1 -clara-clara -alfred-alfred -directus -filebrowser -shlink -vaultwarden -ntfy -``` - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@72.62.1.113 -``` - ---- - -## CORP (92.112.181.188) - -**Rol:** Servidor empresarial - -### Servicios - -| Servicio | Puerto | Estado | -|----------|--------|--------| -| MARGARET | 5051 | Operativo | -| JARED | 5052 | Operativo | -| MASON | 5053 | Operativo | -| FELDMAN | 5054 | Operativo | -| PostgreSQL | 5432 | Operativo | -| Directus | 8055 | Operativo | -| Nextcloud | 8080 | Operativo | -| Vaultwarden | 8081 | Operativo | -| Odoo | 8069 | Operativo | -| Caddy | 80/443 | Operativo | - -### PostgreSQL (database: corp) - -| Tabla | Descripción | -|-------|-------------| -| margaret_log | Log inmutable de ingesta | -| mason_workspace | Espacio de enriquecimiento | -| feldman_cola | Cola de consolidación | -| feldman_bloques | Bloques inmutables | -| feldman_validaciones | Auditoría validaciones | -| milestones | Plano MST | -| bloques | Plano BCK | -| hst_mirror | Mirror de tags HST | - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@92.112.181.188 -``` - ---- - -## HST (72.62.2.84) - -**Rol:** API de tags semánticos - -### Servicios - -| Servicio | Puerto | Estado | -|----------|--------|--------| -| Nginx | 80/443 | Operativo | -| Directus | 8055 | Operativo | -| PostgreSQL | 5432 | Operativo | - -### Estadísticas HST - -| Grupo | Cantidad | -|-------|----------| -| hst | 639 | -| spe | 145 | -| vsn | 84 | -| flg | 65 | -| vue | 21 | -| **Total** | **973** | - -### API - -``` -https://tzrtech.org/{h_maestro}.png # Imagen de tag -``` - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@72.62.2.84 -``` - ---- - -## LOCKER (Cloudflare R2) - -**Rol:** Almacenamiento distribuido - -### Endpoint - -``` -https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -``` - -### Buckets - -| Bucket | Uso | -|--------|-----| -| architect | Backups Gitea, configs, GPU services | -| deck | Archivos personales (CLARA) | -| corp | Archivos empresariales (MARGARET) | -| hst | Imágenes de tags | -| locker | Almacenamiento general/temporal | - -### Estructura GPU Services - -``` -s3://architect/gpu-services/ -├── base/ -│ └── bootstrap.sh -├── grace/ -│ └── code/handler.py -├── penny/ -│ └── code/handler.py -└── factory/ - └── code/handler.py -``` - ---- - -## Resumen de IPs - -| Servidor | IP Pública | IP Interna | -|----------|------------|------------| -| ARCHITECT | 69.62.126.110 | localhost | -| DECK | 72.62.1.113 | - | -| CORP | 92.112.181.188 | - | -| HST | 72.62.2.84 | - | diff --git a/02_COMPONENTES/internos/administracion.md b/02_COMPONENTES/internos/administracion.md new file mode 100644 index 0000000..6034d29 --- /dev/null +++ b/02_COMPONENTES/internos/administracion.md @@ -0,0 +1,106 @@ +# Administración + +**Nombre:** Mason +**Estado:** Implementado + +--- + +## Descripción + +Tabla de trabajo temporal donde se enriquece, procesa y consolida la información antes de su registro definitivo. + +--- + +## Características + +| Característica | Valor | +|----------------|-------| +| Mutabilidad | Editable | +| Persistencia | **Temporal** | +| Eliminación | Siempre se elimina tras consolidar | + +--- + +## Función + +``` +Secretaría (entrada) + │ + ▼ +┌─────────────────┐ +│ Administración │ +│ Mason │ +├─────────────────┤ +│ • Enriquecer │ +│ • Validar │ +│ • Completar │ +│ • Corregir │ +└────────┬────────┘ + │ + ▼ +Contable (Feldman) +``` + +--- + +## Ventana Flotante + +| Parámetro | Valor | +|-----------|-------| +| Duración | 24 horas | +| Durante | Modificable | +| Después | Pasa a Feldman automáticamente | + +--- + +## Operaciones Permitidas + +| Operación | Permitido | +|-----------|-----------| +| Editar campos | ✓ | +| Añadir datos | ✓ | +| Corregir errores | ✓ | +| Eliminar registro | ✗ (solo consolidar) | +| Modificar h_entrada | ✗ | + +--- + +## Referencia a Origen + +Mason siempre mantiene referencia al registro original en Secretaría: + +```json +{ + "id": 123, + "h_entrada_origen": "abc123...", + "datos_enriquecidos": { ... }, + "estado": "en_edicion" +} +``` + +--- + +## Estados + +| Estado | Descripción | +|--------|-------------| +| **en_edicion** | Usuario trabajando | +| **listo** | Preparado para consolidar | +| **consolidado** | Ya pasó a Feldman | + +--- + +## Schema + +```sql +CREATE TABLE mason_temporal ( + id BIGSERIAL PRIMARY KEY, + h_entrada_origen VARCHAR(64) NOT NULL, + datos JSONB NOT NULL, + estado VARCHAR(20) DEFAULT 'en_edicion', + created_at TIMESTAMPTZ DEFAULT NOW(), + expires_at TIMESTAMPTZ, + modified_at TIMESTAMPTZ, + modified_by VARCHAR(64) +); +``` diff --git a/02_COMPONENTES/internos/auditoria.md b/02_COMPONENTES/internos/auditoria.md new file mode 100644 index 0000000..109ed94 --- /dev/null +++ b/02_COMPONENTES/internos/auditoria.md @@ -0,0 +1,204 @@ +# Auditoría + +**Nombre:** Sentinel +**Estado:** Planificado + +--- + +## Descripción + +Sistema de auditoría automatizada con estrategia dual: LIGHT (rápido, exhaustivo) y DEEP (selectivo, profundo). + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRINCIPIO SENTINEL │ +│ │ +│ "Confía, pero verifica. Verifica todo, siempre, sin excepción" │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Filosofía Dual + +| Aspecto | SENTINEL-LIGHT | SENTINEL-DEEP | +|---------|----------------|---------------| +| Analogía | Guardia de seguridad | Detective investigador | +| Enfoque | Exhaustivo, superficial | Selectivo, profundo | +| Motor | Reglas + ML ligero | LLM pesado | +| Frecuencia | Cada 5 min | Cada hora | +| Costo | Mínimo | Variable | + +--- + +## SENTINEL-LIGHT + +### Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SENTINEL-LIGHT │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ RULES │ │ STATS │ │ ALERTS │ │ +│ │ ENGINE │───▶│ COLLECTOR │───▶│ DISPATCHER │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┤ +│ │ SYS_LOG (Source) │ +│ └─────────────────────────────────────────────────────────────┤ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┤ +│ │ AUDIT_RESULTS (Sink) │ +│ └─────────────────────────────────────────────────────────────┤ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Ciclo de Ejecución (cada 5 min) + +``` +1. QUERY + SELECT * FROM SYS_LOG + WHERE audit_status = 'PENDING' + AND timestamp_created > NOW() - INTERVAL '10 minutes' + LIMIT 1000 + +2. APPLY RULES + Para cada registro: + - Ejecutar reglas de integridad + - Ejecutar reglas de conformidad + - Ejecutar reglas de rendimiento + +3. CLASSIFY + - PASS: Todas las reglas OK + - WARN: Reglas MEDIUM fallaron + - FAIL: Reglas HIGH/CRITICAL fallaron + +4. UPDATE & ESCALATE + - UPDATE SYS_LOG SET audit_status + - INSERT AUDIT_RESULTS + - Si FAIL → marcar para DEEP + - Si CRITICAL → alerta inmediata +``` + +--- + +## Reglas de Integridad (I-*) + +| Regla | Nombre | Severidad | Condición | +|-------|--------|-----------|-----------| +| I-001 | Hash entrada coincide | CRITICAL | input_hash == SHA256(archivo) | +| I-002 | Hash salida coincide | CRITICAL | output_hash == SHA256(archivo) | +| I-003 | Cadena trazas válida | HIGH | parent_trace_id existe o es null | +| I-004 | Referencias no huérfanas | HIGH | input_ref y output_ref existen | +| I-005 | Idempotency key única | MEDIUM | No duplicados con distinto resultado | + +--- + +## Reglas de Conformidad (C-*) + +| Regla | Nombre | Severidad | Condición | +|-------|--------|-----------|-----------| +| C-001 | Campos obligatorios | HIGH | Todos los campos requeridos presentes | +| C-002 | Status code válido | HIGH | status_code en lista permitida | +| C-003 | Step type válido | HIGH | step_type en lista permitida | +| C-004 | Confidence en rango | MEDIUM | 0 <= confidence <= 1 | +| C-005 | Timestamps coherentes | MEDIUM | completed >= started >= created | + +--- + +## Reglas de Rendimiento (P-*) + +| Regla | Nombre | Severidad | Condición | +|-------|--------|-----------|-----------| +| P-001 | Latencia aceptable | MEDIUM | duration_ms <= expected * 2 | +| P-002 | Costo razonable | MEDIUM | cost_units <= max_expected | +| P-003 | Sin reintentos excesivos | LOW | retries <= 3 | + +--- + +## SENTINEL-DEEP + +### Cuándo se Activa + +- Regla CRITICAL falló en LIGHT +- Muestreo aleatorio (5% de registros PASS) +- Petición manual de auditoría + +### Qué Hace + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SENTINEL-DEEP │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. Obtener contexto completo del registro │ +│ 2. Recuperar archivos de input_ref y output_ref │ +│ 3. Analizar con LLM: │ +│ - ¿El output es coherente con el input? │ +│ - ¿Hay anomalías semánticas? │ +│ - ¿La confianza reportada es realista? │ +│ 4. Generar informe detallado │ +│ 5. Si anomalía → alerta + bloqueo │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Niveles de Severidad + +| Nivel | Acción | Alerta | +|-------|--------|--------| +| **CRITICAL** | Escalado inmediato + DEEP | Inmediata | +| **HIGH** | Log error + revisar | Batch (cada hora) | +| **MEDIUM** | Log warning | Batch (diario) | +| **LOW** | Log info | Ninguna | + +--- + +## Schema SQL + +```sql +CREATE TABLE audit_results ( + id BIGSERIAL PRIMARY KEY, + batch_id UUID NOT NULL, + audit_type VARCHAR(10) NOT NULL, -- 'LIGHT' o 'DEEP' + records_checked INTEGER NOT NULL, + records_pass INTEGER NOT NULL, + records_warn INTEGER NOT NULL, + records_fail INTEGER NOT NULL, + rules_triggered JSONB, + started_at TIMESTAMPTZ NOT NULL, + completed_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +-- Índice para búsqueda por fecha +CREATE INDEX idx_audit_results_created ON audit_results(created_at); +``` + +--- + +## Métricas + +| Métrica | Descripción | Umbral | +|---------|-------------|--------| +| pass_rate | % registros que pasan | > 99% | +| avg_latency_ms | Latencia promedio | < 100ms | +| escalation_rate | % escalados a DEEP | < 1% | +| alert_rate | Alertas por hora | < 5 | + +--- + +## Pendiente + +- [ ] Implementación scheduler LIGHT (cada 5 min) +- [ ] Implementación scheduler DEEP (cada hora) +- [ ] Dashboard de estado +- [ ] Sistema de alertas (ntfy) +- [ ] Integración con Feldman para verificar cadena de bloques diff --git a/02_COMPONENTES/internos/contable.md b/02_COMPONENTES/internos/contable.md new file mode 100644 index 0000000..c259021 --- /dev/null +++ b/02_COMPONENTES/internos/contable.md @@ -0,0 +1,122 @@ +# Contable + +**Nombre:** Feldman +**Estado:** Implementado + +--- + +## Descripción + +Registro final definitivo e inmutable. Representa el estado válido y oficial de cada operación. Aplica principios contables de inmutabilidad. + +--- + +## Características + +| Característica | Valor | +|----------------|-------| +| Mutabilidad | **Inmutable** | +| Persistencia | Permanente | +| Eliminación | Prohibida bajo ningún concepto | + +--- + +## Estructura Interna + +Feldman es una **unidad conceptual** que contiene dos tablas: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ FELDMAN │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ Cola de Validación │ → │ Registro Final │ │ +│ │ (espera bloques) │ │ (inmutable) │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Aclaración:** La división es **técnica** (tiempo entre validaciones), no conceptual. + +--- + +## Validación por Bloques + +| Aspecto | Valor | +|---------|-------| +| Frecuencia | Periódica | +| Tipo | Por bloques, no transacción a transacción | +| Reglas | M-001, M-002, M-003 | + +### Reglas de Validación + +| Regla | Nombre | Descripción | +|-------|--------|-------------| +| M-001 | Hash único | h_bloque/h_milestone no existe previamente | +| M-002 | Encadenamiento | hash_previo apunta a bloque existente | +| M-003 | Integridad | hash_contenido = SHA-256(contenido) | + +--- + +## Encadenamiento + +``` +Bloque N-1 Bloque N +┌──────────────────┐ ┌──────────────────┐ +│ h_bloque: abc123 │ ──────► │ hash_previo: │ +│ hash_contenido: │ │ abc123 │ +│ def456 │ │ h_bloque: ghi789 │ +│ secuencia: 1 │ │ secuencia: 2 │ +└──────────────────┘ └──────────────────┘ +``` + +--- + +## Schema Cola + +```sql +CREATE TABLE feldman_cola ( + id BIGSERIAL PRIMARY KEY, + tipo VARCHAR(50) NOT NULL, + h_entrada VARCHAR(64) NOT NULL, + contenido JSONB NOT NULL, + prioridad INTEGER DEFAULT 0, + estado VARCHAR(20) DEFAULT 'pendiente', + created_at TIMESTAMPTZ DEFAULT NOW(), + processed_at TIMESTAMPTZ +); +``` + +--- + +## Schema Registro Final + +```sql +CREATE TABLE bloques ( + id BIGSERIAL PRIMARY KEY, + h_bloque VARCHAR(64) UNIQUE NOT NULL, + h_instancia VARCHAR(64) NOT NULL, + secuencia BIGINT NOT NULL, + hash_previo VARCHAR(64), + hash_contenido VARCHAR(64) NOT NULL, + tipo_bloque VARCHAR(50) NOT NULL, + contenido JSONB NOT NULL, + metadata JSONB, + estado VARCHAR(20) DEFAULT 'consolidado', + created_at TIMESTAMPTZ DEFAULT NOW(), + validated_at TIMESTAMPTZ, + UNIQUE (h_instancia, secuencia) +); +``` + +--- + +## Trazabilidad + +Cualquier registro en Feldman puede rastrearse hasta Secretaría: + +``` +Bloque (Feldman) → h_entrada → Secretaría (Clara/Margaret) +``` diff --git a/02_COMPONENTES/internos/context-system.md b/02_COMPONENTES/internos/context-system.md new file mode 100644 index 0000000..b2552a1 --- /dev/null +++ b/02_COMPONENTES/internos/context-system.md @@ -0,0 +1,150 @@ +# Context System + +**Versión:** 0.1 +**Estado:** Especificación inicial + +--- + +## Descripción + +Herramienta local para gestionar el contexto de trabajo con modelos de IA. Funciona de forma idéntica en cualquier servidor. No depende de servicios externos. + +--- + +## Principios + +1. **Local first** - Todo funciona sin conexión a servicios externos +2. **Inmutabilidad del log** - El historial nunca se modifica +3. **Control del usuario** - El usuario decide qué contexto envía +4. **Independencia** - Cada componente funciona por separado +5. **Portabilidad** - La misma herramienta en cualquier servidor + +--- + +## Componentes + +### 1. Log Inmutable + +Registro de TODOS los mensajes de conversaciones con IA. + +| Característica | Valor | +|----------------|-------| +| Editable | NO | +| Borrable | NO | +| Ubicación | Local (PostgreSQL) | + +**Contenido:** +- Cada mensaje enviado a la IA +- Cada respuesta recibida +- Timestamp +- Identificador de sesión + +### 2. Gestor de Contexto + +| Característica | Valor | +|----------------|-------| +| Editable | Sí | +| Borrable | Sí | +| Ubicación | Local | + +**Contenido:** +- Bloques de contexto reutilizables +- Configuración de qué se incluye en cada sesión +- Prioridades y orden + +### 3. Dataset del Usuario + +| Característica | Valor | +|----------------|-------| +| Editable | Sí | +| Borrable | Sí | +| Ubicación | Local | + +**Contenido:** +- Referencias a archivos, documentos, datos +- Metadatos para búsqueda y acceso +- Estructura del espacio de trabajo + +### 4. Buzón de Entrada + +| Característica | Valor | +|----------------|-------| +| Editable | Según implementación | +| Ubicación | Local | + +**Contenido:** +- Resultados que el usuario quiere persistir +- Datos que entran al sistema +- Conexión con almacenamiento externo + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SERVIDOR (local) │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 1. LOG │ │ 2. GESTOR │ │ +│ │ INMUTABLE │ │ DE CONTEXTO │ │ +│ │ [NO editable] │ │ [Editable] │ │ +│ └─────────────────┘ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 3. DATASET │ │ 4. BUZÓN │ │ +│ │ DEL USUARIO │ │ DE ENTRADA │ │ +│ │ [Editable] │ │ [E/S datos] │ │ +│ └─────────────────┘ └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ IA EXTERNA │ + │ (no en local) │ + └─────────────────┘ +``` + +--- + +## Módulo Grafo (Kuzu) + +Capa de selección dinámica de contexto. El grafo no almacena datos primarios — es una proyección reconstruible del log. + +``` +LOG (inmutable) → GRAFO (derivado) → Contexto seleccionado + fuente de verdad caché relaciones input para LLM +``` + +### Tecnología: Kuzu + +| Criterio | Kuzu | +|----------|------| +| Instalación | `pip install kuzu` | +| Dependencias | Ninguna | +| Servidor | No requiere | +| Persistencia | Carpeta local | +| Licencia | MIT | + +### Uso Básico + +```python +import kuzu + +db = kuzu.Database("./grafo_contexto") +conn = kuzu.Connection(db) + +# Crear esquema +conn.execute("CREATE NODE TABLE Mensaje(id INT64, hash STRING, PRIMARY KEY(id))") +conn.execute("CREATE NODE TABLE Concepto(nombre STRING, PRIMARY KEY(nombre))") +conn.execute("CREATE REL TABLE Menciona(FROM Mensaje TO Concepto)") +``` + +### Mantenimiento + +| Tarea | Comando | +|-------|---------| +| Backup | `cp -r ./grafo_contexto ./backup` | +| Borrar | `rm -rf ./grafo_contexto` | +| Regenerar | Script desde log | diff --git a/02_COMPONENTES/internos/produccion.md b/02_COMPONENTES/internos/produccion.md new file mode 100644 index 0000000..1cf951b --- /dev/null +++ b/02_COMPONENTES/internos/produccion.md @@ -0,0 +1,106 @@ +# Producción + +**Nombres:** Alfred (personal), Jared (corporativo) +**Estado:** Implementado + +--- + +## Descripción + +Almacena flujos y secuencias de procesos predefinidos. Pueden ser complejos (árboles de procesos de producción) o simples (rutinas de entrenamiento). + +--- + +## Características + +| Característica | Valor | +|----------------|-------| +| Mutabilidad | Editable | +| Persistencia | Permanente | +| Eliminación | Permitida | + +--- + +## Función + +- Tiene su propia tabla para definiciones de flujos +- La información ya está disponible y ordenada +- Permite ejecución directa sin intervención manual +- Implementación: Windmill (orquestador de procesos) + +--- + +## Alfred vs Jared + +| Aspecto | Alfred | Jared | +|---------|--------|-------| +| Contexto | Personal (DECK) | Corporativo (CORP) | +| Complejidad | Menor | Mayor (múltiples usuarios) | +| Flujos | Simples | Complejos | + +--- + +## Flujo de Producción + +Cuando la información **encaja** (viene de un proceso predefinido): + +``` +Alfred/Jared (producción) + │ + ▼ +Clara/Margaret (secretaría) ← Registro inmutable + │ + ▼ +Feldman (contable) ← Consolidación directa +``` + +**Nota:** Este flujo salta Administración porque no hay nada que enriquecer. + +--- + +## Decisión de Flujo + +``` +┌─────────────────┐ +│ ¿Encaja? │ +└────────┬────────┘ + │ + ┌────┴────┐ + │ │ + SÍ NO + │ │ + ▼ ▼ +Feldman Mason +(directo) (enriquecer) +``` + +--- + +## Relación con Grace + +``` +Alfred/Jared (decide) ──► Grace (transforma) +``` + +- **Producción** decide el flujo +- **Grace** transforma los datos + +--- + +## Mapeo de Intenciones + +```javascript +// Ejemplo de decisiones de Alfred +"resumir" → GRACE.SUMMARIZER +"transcribir" → GRACE.ASR_ENGINE +"generar plan" → PENNY +"crear imagen" → FACTORY +``` + +--- + +## Pendiente + +- [ ] Estructura de la tabla de flujos +- [ ] Formato de definición de flujos +- [ ] Mecanismo de hashes para "encaja/no encaja" diff --git a/02_COMPONENTES/internos/secretaria.md b/02_COMPONENTES/internos/secretaria.md new file mode 100644 index 0000000..75771fb --- /dev/null +++ b/02_COMPONENTES/internos/secretaria.md @@ -0,0 +1,105 @@ +# Secretaría + +**Nombres:** Clara (personal), Margaret (corporativo) +**Estado:** Implementado + +--- + +## Descripción + +Punto de entrada inmutable del sistema. Todo dato que ingresa queda registrado exactamente como llegó. + +--- + +## Principio + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ Todo lo que entra se registra. │ +│ Nada se modifica. Nada se elimina. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Características + +| Característica | Valor | +|----------------|-------| +| Mutabilidad | **Inmutable** | +| Persistencia | Permanente | +| Eliminación | Prohibida | + +--- + +## Registra + +- Fecha y hora +- Origen +- Usuario +- Contenido +- Hash SHA-256 de archivos + +--- + +## Clara vs Margaret + +| Aspecto | Clara | Margaret | +|---------|-------|----------| +| Contexto | Personal (DECK) | Corporativo (CORP) | +| Usuarios | Uno | Múltiples | +| Tabla recepción | Menor | Mayor | +| Funcionalidad | Equivalente | Equivalente | + +--- + +## Flujo de Entrada + +``` +PACKET / API / Manual + │ + ▼ +┌─────────────────┐ +│ Secretaría │ +│ Clara/Margaret │ +├─────────────────┤ +│ • Registrar │ +│ • Hashear │ +│ • Almacenar │ +│ • Confirmar │ +└────────┬────────┘ + │ + ├──► Producción (si encaja) + │ + └──► Administración (si no encaja) +``` + +--- + +## Schema + +```sql +CREATE TABLE secretaria_log ( + id BIGSERIAL PRIMARY KEY, + h_entrada VARCHAR(64) UNIQUE NOT NULL, + ts TIMESTAMPTZ DEFAULT NOW(), + origen VARCHAR(100), + usuario_id INTEGER, + tipo_contenido VARCHAR(50), + contenido JSONB, + archivos_hashes JSONB, + metadata JSONB +); +``` + +--- + +## API + +| Endpoint | Método | Descripción | +|----------|--------|-------------| +| /ingesta | POST | Nueva entrada | +| /ingesta/{h_entrada} | GET | Consultar entrada | +| /ingesta/verificar | POST | Verificar hash | diff --git a/02_COMPONENTES/internos/utilidades.md b/02_COMPONENTES/internos/utilidades.md new file mode 100644 index 0000000..4c3bf45 --- /dev/null +++ b/02_COMPONENTES/internos/utilidades.md @@ -0,0 +1,90 @@ +# Utilidades + +**Estado:** Operativo + +--- + +## Descripción + +Pack de servicios auxiliares disponibles en cada instancia del sistema. + +--- + +## Servicios + +| Servicio | Función | Base | +|----------|---------|------| +| **Mail** | Correo electrónico | Cowmail | +| **Alias** | Gestión de alias de correo | Addy.io | +| **Credenciales** | Gestión de credenciales | Vaultwarden | +| **Acortador** | Redirección de URLs | Shlink | + +--- + +## Puertos por Servidor + +| Servicio | DECK | CORP | +|----------|------|------| +| Mail | 8084 | 8084 | +| Alias | 8085 | 8085 | +| Credenciales | 8082 | 8081 | +| Acortador | 8083 | 8082 | + +--- + +## Mail + +Gestión de correo electrónico. + +| Aspecto | Valor | +|---------|-------| +| Base | Cowmail | +| Protocolo | SMTP/IMAP | + +--- + +## Alias + +Gestión de alias de correo para privacidad y organización. + +| Aspecto | Valor | +|---------|-------| +| Base | Addy.io | +| Función | Crear alias temporales o permanentes | + +--- + +## Credenciales + +Almacén seguro de credenciales y secretos. + +| Aspecto | Valor | +|---------|-------| +| Base | Vaultwarden | +| Protocolo | API compatible Bitwarden | +| Referencia | `kv://` URIs | + +--- + +## Acortador + +Acortador y redirector de URLs con tracking. + +| Aspecto | Valor | +|---------|-------| +| Base | Shlink | +| Función | URLs cortas, estadísticas de clics | + +--- + +## Bases de Datos de Usuario + +Extensiones de bibliotecas para contenido personalizado: + +| Tabla | Descripción | Extiende | +|-------|-------------|----------| +| **hsu** | Tags de usuario | HST | +| **spu** | Especialidades de usuario | SPE | +| **pju** | Proyectos de usuario | PJT | + +Estas tablas permiten que cada usuario cree sus propias etiquetas, especialidades y proyectos sin modificar las bibliotecas base del sistema. diff --git a/02_COMPONENTES/servicios externos/asistente.md b/02_COMPONENTES/servicios externos/asistente.md new file mode 100644 index 0000000..242a881 --- /dev/null +++ b/02_COMPONENTES/servicios externos/asistente.md @@ -0,0 +1,224 @@ +# Asistente de Voz + +**Nombre:** Penny +**Versión:** 1.0 +**Estado:** Especificación + +--- + +## Descripción + +PENNY es el asistente personal de voz del sistema DECK. Proporciona interfaz conversacional hablada 100% natural. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PENNY │ +│ │ +│ • ES la voz del DECK │ +│ • ES la interfaz hablada con el usuario │ +│ • ES quien llama a GRACE cuando necesita datos │ +│ • HABLA con el usuario (GRACE no puede) │ +│ • REGISTRA todo en el log (planos de información) │ +│ • MANTIENE contexto durante la sesión │ +│ │ +│ "PENNY habla, GRACE procesa, el Log recuerda." │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Principios + +| Principio | Descripción | +|-----------|-------------| +| **Privacidad radical** | Audio procesado 100% local | +| **Conversación natural** | Turnos fluidos, latencia <2s | +| **Log como verdad** | Todo está en el log | +| **Planos separados** | Contextos cargables independientemente | +| **GRACE como backend** | PENNY pregunta, GRACE extrae | + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PENNY VOICE ENGINE │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐│ +│ │ Silero │ │ Faster │ │ PENNY │ │ XTTS-v2 ││ +│ │ VAD │─▶│ Whisper │─▶│ CORE │─▶│ /Kokoro ││ +│ │ │ │ │ │ │ │ ││ +│ │ Detecta │ │ Transcribe │ │ Orquesta │ │ Sintetiza ││ +│ │ voz │ │ audio │ │ todo │ │ respuesta ││ +│ └────────────┘ └────────────┘ └─────┬──────┘ └────────────┘│ +│ │ │ +│ ┌───────────────────┼───────────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ PLANOS │ │ GRACE │ │ +│ │ (Log) │ │ (S-CONTRACT)│ │ +│ └─────────────┘ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ + │ + │ SOLO texto (nunca audio) + ▼ + ┌─────────────────────────────┐ + │ Claude API │ + └─────────────────────────────┘ +``` + +--- + +## Flujo Voz → Voz + +``` +1. VAD DETECTA (Silero) + └── Usuario empieza a hablar + └── Usuario termina (silencio 700ms) + +2. ASR TRANSCRIBE (Faster-Whisper) + └── Audio → Texto + └── ~200-400ms con GPU + +3. PENNY CORE PROCESA + ├── Cargar planos relevantes + ├── ¿Necesita GRACE? → Llamar con S-CONTRACT + ├── Construir prompt con contexto + └── Enviar a Claude API (solo texto) + +4. LLM RESPONDE (Claude) + └── Streaming de tokens + └── ~1-2s primera palabra + +5. TTS SINTETIZA (XTTS-v2/Kokoro) + └── Texto → Audio + └── Streaming mientras llegan tokens + +6. LOG REGISTRA + └── Todo el intercambio → plano CONVERSACION + +LATENCIA TOTAL OBJETIVO: <2 segundos voice-to-voice +``` + +--- + +## Planos de Información + +El Log está organizado en planos que se cargan independientemente: + +| Plano | Nombre | Descripción | Comportamiento | +|-------|--------|-------------|----------------| +| 0 | SISTEMA | Instrucciones base | INMUTABLE durante sesión | +| 1 | PERSONALIDAD | Tono, estilo, nombre, voz | CONFIGURABLE por usuario | +| 2 | CONTEXTO PERSONAL | Info usuario, preferencias | PERSISTENTE entre sesiones | +| 3 | CONTEXTO AMBIENTAL | Fecha, hora, estado sistema | DINÁMICO cada sesión | +| 4 | DATASET | Datos específicos de tarea | OPCIONAL bajo demanda | +| 5 | CONVERSACIÓN | Historial de sesión | TEMPORAL durante sesión | + +--- + +## Modelos Autoalojados + +| Componente | Modelo | Descripción | +|------------|--------|-------------| +| VAD | Silero VAD v5 | Detección de actividad vocal | +| ASR | Faster-Whisper Large-v3 | Transcripción | +| TTS | XTTS-v2 / Kokoro | Síntesis de voz | +| LLM | Claude API | Solo texto, streaming | + +--- + +## Configuración + +```yaml +penny: + version: "1.0" + + server: + host: "0.0.0.0" + port: 8765 + websocket_path: "/ws/voice" + + models: + vad: + type: "silero" + path: "models/silero/vad_v5.onnx" + + asr: + type: "faster-whisper" + model: "large-v3" + device: "cuda" + compute_type: "float16" + + tts: + type: "xtts-v2" + speaker_wav: "voces/penny_base.wav" + language: "es" + + llm: + provider: "anthropic" + model: "claude-sonnet-4-20250514" + max_tokens: 150 + temperature: 0.7 + streaming: true + + grace: + endpoint: "http://localhost:8080/api/v1" + timeout_ms: 10000 + + log: + database_url: "postgresql://..." + retain_audio_days: 7 + retain_text_days: 365 +``` + +--- + +## Rol en el Sistema + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ DECK (Servidor Personal) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ Usuario ◄────────────────────────────────────► PENNY │ +│ (voz natural) │ (habla) │ +│ │ │ +│ ┌──────────────┼──────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ GRACE │ │ LOG │ │ DECK │ │ +│ │ (datos) │ │(planos) │ │(acciones│ │ +│ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Características Conversacionales + +| Característica | Descripción | +|----------------|-------------| +| **Barge-in** | Usuario puede interrumpir | +| **Turn detection** | Detecta fin de turno por silencio | +| **Contexto** | Mantiene contexto durante sesión | +| **Streaming** | Respuesta progresiva mientras genera | + +--- + +## Pendiente + +- [ ] Configurar servidor con GPU +- [ ] Instalar modelos (Whisper, XTTS-v2, Silero) +- [ ] Crear tablas de log +- [ ] Definir planos específicos +- [ ] Grabar voz base para clonación TTS +- [ ] Implementar pipeline con Pipecat +- [ ] Ajustar turn detection diff --git a/02_COMPONENTES/servicios externos/circle.md b/02_COMPONENTES/servicios externos/circle.md new file mode 100644 index 0000000..1190b78 --- /dev/null +++ b/02_COMPONENTES/servicios externos/circle.md @@ -0,0 +1,84 @@ +# Circle + +**Nombre:** The Circle - Consejo de Perspectivas +**Estado:** Implementado + +--- + +## Descripción + +Sistema que convoca múltiples perspectivas (agentes con roles distintos) para analizar un problema desde diferentes ángulos. + +--- + +## Concepto + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ THE CIRCLE │ +│ (Consejo de Perspectivas) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ Crítico │ │Optimista│ │ +│ └────┬────┘ └────┬────┘ │ +│ │ │ │ +│ │ ┌─────────────┐ │ │ +│ └─────►│ PROBLEMA │◄─────────┘ │ +│ └──────┬──────┘ │ +│ ┌─────────────┼─────────────┐ │ +│ │ │ │ │ +│ ┌────┴────┐ ┌─────┴─────┐ ┌────┴────┐ │ +│ │Pragmático│ │ Creativo │ │Analítico│ │ +│ └─────────┘ └───────────┘ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Presets + +| Preset | Perspectivas | Uso | +|--------|--------------|-----| +| **decision** | Crítico, Optimista, Pragmático | Toma de decisiones | +| **creative** | Creativo, Crítico, Explorador | Brainstorming | +| **analysis** | Analítico, Escéptico, Sintético | Análisis profundo | + +--- + +## Uso + +```javascript +const circle = new Circle('decision'); +const result = await circle.deliberate({ + problem: "¿Deberíamos lanzar el producto ahora?", + context: "..." +}); +``` + +--- + +## Output + +```json +{ + "perspectives": [ + { "role": "critico", "opinion": "..." }, + { "role": "optimista", "opinion": "..." }, + { "role": "pragmatico", "opinion": "..." } + ], + "synthesis": "...", + "recommendation": "..." +} +``` + +--- + +## Diferencia con Orchestrator + +| Aspecto | Orchestrator | Circle | +|---------|--------------|--------| +| Enfoque | Ejecución de tareas | Deliberación | +| Agentes | Colaborativos | Contrapuestos | +| Output | Resultado | Perspectivas + síntesis | diff --git a/02_COMPONENTES/servicios externos/cloudville.md b/02_COMPONENTES/servicios externos/cloudville.md new file mode 100644 index 0000000..720cb07 --- /dev/null +++ b/02_COMPONENTES/servicios externos/cloudville.md @@ -0,0 +1,87 @@ +# Cloudville + +**Nombre:** Cloudville - Laboratorio de Zumbados +**Estado:** Implementado + +--- + +## Descripción + +Laboratorio experimental donde agentes con personalidades extremas o poco convencionales exploran ideas sin restricciones. + +--- + +## Concepto + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ CLOUDVILLE │ +│ (Laboratorio de Zumbados) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ "Donde las ideas locas tienen permiso para existir" │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │Visionario│ │ Caótico │ │Contrarian│ │ Loco │ │ +│ │ │ │ │ │ │ │ Genio │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Propósito + +| Aspecto | Valor | +|---------|-------| +| Restricciones | Mínimas | +| Filtro | Ninguno | +| Output | Ideas sin censura | +| Uso | Exploración, brainstorming extremo | + +--- + +## Diferencia con Circle + +| Aspecto | Circle | Cloudville | +|---------|--------|------------| +| Tono | Profesional | Experimental | +| Filtro | Moderado | Ninguno | +| Propósito | Decisión | Exploración | +| Personalidades | Equilibradas | Extremas | + +--- + +## Uso + +```javascript +const cloudville = new Cloudville(); +const ideas = await cloudville.explore({ + challenge: "¿Cómo podríamos...?", + constraints: "ninguna" +}); +``` + +--- + +## Output + +```json +{ + "ideas": [ + { "agent": "visionario", "idea": "..." }, + { "agent": "caotico", "idea": "..." }, + { "agent": "contrarian", "idea": "..." }, + { "agent": "loco_genio", "idea": "..." } + ], + "wildest": "...", + "hidden_gems": ["...", "..."] +} +``` + +--- + +## Advertencia + +> **Nota:** Las ideas de Cloudville no están filtradas. Requieren evaluación posterior antes de implementación. diff --git a/02_COMPONENTES/servicios externos/generacion-iterativa.md b/02_COMPONENTES/servicios externos/generacion-iterativa.md new file mode 100644 index 0000000..50e9178 --- /dev/null +++ b/02_COMPONENTES/servicios externos/generacion-iterativa.md @@ -0,0 +1,96 @@ +# Generación Iterativa + +**Nombre:** The Factory +**Estado:** Implementado (RunPod) + +--- + +## Descripción + +Sistema de generación iterativa. Produce contenido mediante ciclos de refinamiento sucesivos. + +--- + +## Concepto + +``` +Input inicial + │ + ▼ +┌─────────────────┐ +│ THE FACTORY │ +├─────────────────┤ +│ │ +│ Iteración 1 ───┼──► Resultado parcial +│ │ │ +│ ▼ │ +│ Iteración 2 ───┼──► Resultado mejorado +│ │ │ +│ ▼ │ +│ Iteración N ───┼──► Resultado final +│ │ +└─────────────────┘ +``` + +--- + +## Endpoint + +| Parámetro | Valor | +|-----------|-------| +| Ubicación | RunPod | +| GPU | NVIDIA L4 | +| Modo | Serverless | + +--- + +## Casos de Uso + +| Caso | Descripción | +|------|-------------| +| Generación de imágenes | Refinamiento progresivo | +| Generación de texto | Mejora iterativa | +| Producción de contenido | Ciclos de calidad | + +--- + +## Request + +```json +{ + "input": { + "prompt": "...", + "iterations": 3, + "quality_threshold": 0.8, + "output_format": "..." + } +} +``` + +--- + +## Response + +```json +{ + "status": "completed", + "iterations_used": 3, + "outputs": [ + { "iteration": 1, "result": "...", "score": 0.6 }, + { "iteration": 2, "result": "...", "score": 0.75 }, + { "iteration": 3, "result": "...", "score": 0.85 } + ], + "final_result": "...", + "trace_id": "..." +} +``` + +--- + +## Diferencia con Grace + +| Aspecto | Grace | The Factory | +|---------|-------|-------------| +| Modo | Single-shot | Iterativo | +| Enfoque | Transformación | Generación | +| Ciclos | 1 | N | diff --git a/02_COMPONENTES/servicios externos/gestoria.md b/02_COMPONENTES/servicios externos/gestoria.md new file mode 100644 index 0000000..7ed475a --- /dev/null +++ b/02_COMPONENTES/servicios externos/gestoria.md @@ -0,0 +1,203 @@ +# Gestoría + +**Nombre:** Grace +**Versión:** 2.0 +**Estado:** Enterprise Standard + +--- + +## Descripción + +GRACE no es un chatbot. Es un **conjunto de 18 microservicios cognitivos desacoplados**. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRINCIPIO GRACE │ +│ │ +│ "GRACE transforma, Producción (Alfred/Jared) decide" │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Características + +| Propiedad | Valor | +|-----------|-------| +| Stateless | No guarda estado | +| Determinista | Misma entrada → misma salida | +| Intercambiable | Modelos sustituibles | +| Trazable | Todo via trace_id | + +**GRACE no:** +- Toma decisiones +- Altera estados +- Escribe en base de datos + +--- + +## Infraestructura + +| Aspecto | Valor | +|---------|-------| +| **Plataforma** | RunPod Serverless | +| **GPU** | NVIDIA L4 (24GB) | +| **Endpoint** | https://api.runpod.ai/v2/{id} | +| **Modos** | runsync, run, status | + +--- + +## Catálogo de 18 Módulos + +### FAMILIA A - VISIÓN (Procesado Documental) + +| # | Módulo | Función | +|---|--------|---------| +| 1 | **IMG_PREPROCESS** | Normalización: crop, denoise, upscale, grayscale | +| 2 | **PDF_SCANNER** | Limpieza escaneados: deskew, binarización, bordes | +| 3 | **OCR_CORE** | Extracción texto: layout analysis, HOCR | + +### FAMILIA B - VOZ (Reuniones) + +| # | Módulo | Función | +|---|--------|---------| +| 4 | **ASR_ENGINE** | Reconocimiento habla: Whisper Large v3, diarización | +| 5 | **TTS_ENGINE** | Síntesis voz neutral para notificaciones | + +### FAMILIA C - IDENTIDAD (Biometría) + +| # | Módulo | Función | +|---|--------|---------| +| 6 | **FACE_VECTOR** | Vector biométrico facial (Float32 L2) | +| 7 | **ID_CONSOLIDATION** | Fusión identidades: mediana geométrica | +| 8 | **AVATAR_GEN** | Avatar neutral 512x512 desde vector | + +### FAMILIA D - SEMÁNTICA (NLP) + +| # | Módulo | Función | +|---|--------|---------| +| 9 | **EMBEDDINGS** | Vectorización semántica para búsquedas | +| 10 | **SUMMARIZER** | Resumen estructurado: objetivos, acuerdos, riesgos | +| 11 | **TASK_EXTRACTOR** | Minería de acciones, responsables, fechas | +| 12 | **CLASSIFIER** | Asigna tags HST basado en contenido | +| 13 | **SIMILARITY** | Comparador vectorial (Cosine Similarity) | + +### FAMILIA E - UTILIDADES + +| # | Módulo | Función | +|---|--------|---------| +| 14 | **FIELD_EXTRACTOR** | Lectura estructurada: CIF, fechas, importes | +| 15 | **HASHER** | Generador SHA256 y UUID v4 | +| 16 | **INPUT_NORMALIZER** | Traducción formatos y enumeraciones | +| 17 | **OUTPUT_ADAPTER** | Adaptación a formatos legacy | +| 18 | **LANG_DETECT** | Identificación idioma ISO 639-1 | + +--- + +## Cadenas de Fallback + +Cada módulo define degradación vía `fallback_chain` en S-CONTRACT: + +### Ejemplo OCR + +```json +"routing": { + "module": "OCR_CORE", + "fallback_chain": ["OCR_LOCAL", "OCR_GROQ", "OCR_OPENAI"] +} +``` + +### Ejemplo ASR + +```json +"routing": { + "module": "ASR_ENGINE", + "fallback_chain": ["ASR_WHISPER_LOCAL", "ASR_FASTER_WHISPER", "ASR_GROQ"] +} +``` + +--- + +## Integración con S-CONTRACT + +Todos los módulos GRACE cumplen S-CONTRACT sin excepciones. No hay contratos específicos por módulo. + +### Request (LITE) + +```json +{ + "contract_version": "2.1", + "profile": "LITE", + "envelope": { + "trace_id": "uuid-v4", + "idempotency_key": "sha256-input" + }, + "routing": { + "module": "CLASSIFIER" + }, + "context": { + "lang": "es", + "mode": "strict" + }, + "payload": { + "type": "text", + "encoding": "utf-8", + "content": "Texto a procesar" + } +} +``` + +### Response + +```json +{ + "contract_version": "2.1", + "profile": "LITE", + "status": { + "code": "SUCCESS", + "provider_used": "groq" + }, + "result": { + "data": { + "category": "FINANZAS", + "confidence": 0.98 + } + }, + "quality": { + "confidence": 0.98, + "tokens_input": 150, + "tokens_output": 45 + }, + "metadata": { + "model_id": "llama-3.1-70b-versatile", + "processing_ms": 340 + } +} +``` + +--- + +## Relación con Otros Componentes + +GRACE es un servicio de transformación. Cuando termina de procesar, envía el resultado a Secretaría (Clara/Margaret) como cualquier otro origen: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PUNTO DE ENTRADA ÚNICO │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ GRACE ──────┐ │ +│ Penny ──────┼──► Secretaría (Clara/Margaret) │ +│ Packet ─────┤ │ +│ Manual ─────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +GRACE no es un paso del flujo de datos. Es un servicio que cualquier componente puede invocar vía S-CONTRACT para obtener transformaciones cognitivas (OCR, transcripción, clasificación, etc.). + +--- + +## Compatibilidad Universal + +El sistema está diseñado para que cuando aparezcan módulos mejores, encajen sin trabajo añadido. S-CONTRACT es el marco de compatibilidad universal — cualquier módulo que lo cumpla es interoperable. diff --git a/02_COMPONENTES/servicios externos/orchestratorv6.md b/02_COMPONENTES/servicios externos/orchestratorv6.md new file mode 100644 index 0000000..ff7ea59 --- /dev/null +++ b/02_COMPONENTES/servicios externos/orchestratorv6.md @@ -0,0 +1,4092 @@ +# TZZR Orchestrator v6 - Código Completo + +Sistema de orquestación multi-agente robusto y flexible. + +## Índice + +1. [Estructura del Proyecto](#estructura-del-proyecto) +2. [main.py](#mainpy) +3. [config.yaml](#configyaml) +4. [orchestrator/\_\_init\_\_.py](#orchestrator__init__py) +5. [orchestrator/config.py](#orchestratorconfigpy) +6. [orchestrator/core/\_\_init\_\_.py](#orchestratorcore__init__py) +7. [orchestrator/core/retry.py](#orchestratorcoreretrypy) +8. [orchestrator/core/circuit_breaker.py](#orchestratorcorecircuit_breakerpy) +9. [orchestrator/core/rate_limiter.py](#orchestratorcorerate_limiterpy) +10. [orchestrator/providers/\_\_init\_\_.py](#orchestratorproviders__init__py) +11. [orchestrator/providers/base.py](#orchestratorprovidersbasepy) +12. [orchestrator/providers/claude_provider.py](#orchestratorprovidersclaude_providerpy) +13. [orchestrator/providers/litellm_provider.py](#orchestratorproviderslitellm_providerpy) +14. [orchestrator/tools/\_\_init\_\_.py](#orchestratortools__init__py) +15. [orchestrator/tools/definitions.py](#orchestratortoolsdefinitionspy) +16. [orchestrator/tools/executor.py](#orchestratortoolsexecutorpy) +17. [orchestrator/agents/\_\_init\_\_.py](#orchestratoragents__init__py) +18. [orchestrator/agents/base.py](#orchestratoragentsbasepy) +19. [orchestrator/tracking/\_\_init\_\_.py](#orchestratortracking__init__py) +20. [Ejemplos de Configuración](#ejemplos-de-configuración) + +--- + +## Estructura del Proyecto + +``` +tzzr_v6/ +├── main.py # Punto de entrada +├── config.yaml # Configuración principal +├── .env # Variables de entorno (crear desde .env.example) +├── .env.example # Ejemplo de variables +├── .gitignore +├── README.md +├── orchestrator/ +│ ├── __init__.py +│ ├── config.py # Gestión de configuración +│ ├── core/ # Componentes de resiliencia +│ │ ├── __init__.py +│ │ ├── circuit_breaker.py +│ │ ├── rate_limiter.py +│ │ └── retry.py +│ ├── providers/ # Proveedores de LLM +│ │ ├── __init__.py +│ │ ├── base.py +│ │ ├── claude_provider.py +│ │ └── litellm_provider.py +│ ├── tools/ # Herramientas +│ │ ├── __init__.py +│ │ ├── definitions.py +│ │ └── executor.py +│ ├── agents/ # Agentes +│ │ ├── __init__.py +│ │ └── base.py +│ └── tracking/ # Cost tracking +│ └── __init__.py +├── logs/ # Logs por agente +├── outputs/ # Archivos generados +└── examples/ # Ejemplos de configuración + ├── simple.yaml + ├── dev_team.yaml + ├── devops.yaml + └── local_ollama.yaml +``` + +--- + +## main.py + +```python +#!/usr/bin/env python3 +""" +LLM Orchestrator - Multi-agent orchestration system. + +Usage: + python main.py # Interactive mode + python main.py --status # Show status + python main.py --agents # List agents + python main.py --agent X --prompt "Y" # Execute prompt + python main.py --health # Health check +""" + +import asyncio +import argparse +import json +import sys +from datetime import datetime +from pathlib import Path + +# Add orchestrator to path +sys.path.insert(0, str(Path(__file__).parent)) + +from orchestrator.config import get_config, reload_config +from orchestrator.agents import Agent, AgentResult +from orchestrator.tracking import CostTracker + + +class Orchestrator: + """Main orchestrator class.""" + + def __init__(self, config_path: str = None): + print("🚀 Starting Orchestrator...") + + self.config = get_config(config_path) + self.agents: dict[str, Agent] = {} + self.cost_tracker = CostTracker() + + # Load agents + for name, agent_config in self.config.agents.items(): + try: + self.agents[name] = Agent(config_obj=agent_config) + except Exception as e: + print(f"⚠️ Error creating agent {name}: {e}") + + if self.agents: + print(f"✅ Loaded {len(self.agents)} agent(s): {', '.join(self.agents.keys())}") + else: + print("⚠️ No agents loaded. Edit config.yaml") + + async def run_agent(self, agent_name: str, prompt: str) -> AgentResult: + """Run a single agent.""" + if agent_name not in self.agents: + return AgentResult( + success=False, + output="", + agent_name=agent_name, + error=f"Agent '{agent_name}' not found", + ) + + result = await self.agents[agent_name].run(prompt) + + # Track costs + if result.usage: + self.cost_tracker.record( + model=self.agents[agent_name].model, + input_tokens=result.usage.get("input_tokens", 0), + output_tokens=result.usage.get("output_tokens", 0), + ) + + return result + + async def run_all(self, prompt: str) -> dict[str, AgentResult]: + """Run all agents in parallel.""" + tasks = { + name: agent.run(prompt) + for name, agent in self.agents.items() + } + + results_list = await asyncio.gather(*tasks.values(), return_exceptions=True) + + results = {} + for name, result in zip(tasks.keys(), results_list): + if isinstance(result, Exception): + results[name] = AgentResult( + success=False, + output="", + agent_name=name, + error=str(result), + ) + else: + results[name] = result + + return results + + async def health_check(self) -> dict[str, bool]: + """Check health of all agents.""" + results = {} + for name, agent in self.agents.items(): + try: + results[name] = await agent.health_check() + except Exception: + results[name] = False + return results + + def get_status(self) -> dict: + """Get orchestrator status.""" + return { + "config_path": str(self.config.config_path) if self.config.config_path else None, + "settings": { + "timeout": self.config.settings.timeout, + "rate_limit": self.config.settings.rate_limit_per_minute, + "max_retries": self.config.settings.max_retries, + "sandbox_paths": self.config.settings.sandbox_paths, + "circuit_breaker": self.config.settings.enable_circuit_breaker, + }, + "agents": { + name: { + "provider": agent.provider_name, + "model": agent.model, + "tools": agent.tools, + } + for name, agent in self.agents.items() + }, + "servers": list(self.config.servers.keys()), + "costs": self.cost_tracker.summary.to_dict(), + } + + async def interactive(self): + """Run interactive mode.""" + print("\n" + "=" * 60) + print("LLM Orchestrator - Interactive Mode") + print("=" * 60) + print("\nCommands:") + print(" /status - Show status") + print(" /agents - List agents") + print(" /agent - Switch active agent") + print(" /logs - Show agent logs") + print(" /costs - Show cost tracking") + print(" /health - Health check") + print(" /reload - Reload configuration") + print(" /all - Run on all agents (parallel)") + print(" /quit - Exit") + print("-" * 60) + + if not self.agents: + print("\n⚠️ No agents available. Edit config.yaml") + return + + current_agent = list(self.agents.keys())[0] + + while True: + try: + prompt = input(f"\n[{current_agent}] > ").strip() + + if not prompt: + continue + + if prompt == "/quit": + print("Goodbye!") + break + + elif prompt == "/status": + print(json.dumps(self.get_status(), indent=2)) + + elif prompt == "/agents": + for name, agent in self.agents.items(): + marker = "→" if name == current_agent else " " + print(f" {marker} {name}: {agent.provider_name}/{agent.model}") + if agent.tools: + print(f" tools: {', '.join(agent.tools)}") + + elif prompt.startswith("/agent "): + name = prompt[7:].strip() + if name in self.agents: + current_agent = name + print(f"Active agent: {current_agent}") + else: + print(f"Agent '{name}' not found. Available: {', '.join(self.agents.keys())}") + + elif prompt.startswith("/logs "): + name = prompt[6:].strip() + if name in self.agents: + log = self.agents[name].read_log(last_n=5) + print(log if log else "(empty)") + else: + print(f"Agent '{name}' not found") + + elif prompt == "/costs": + summary = self.cost_tracker.summary + print(f"\n📊 Cost Summary:") + print(f" Requests: {summary.total_requests}") + print(f" Tokens: {summary.total_input_tokens:,} in / {summary.total_output_tokens:,} out") + print(f" Cost: ${summary.total_cost_usd:.4f}") + if summary.by_model: + print("\n By model:") + for model, data in summary.by_model.items(): + print(f" {model}: {data['requests']} req, ${data['cost_usd']:.4f}") + + elif prompt == "/health": + print("Checking health...") + results = await self.health_check() + for name, healthy in results.items(): + status = "✅" if healthy else "❌" + print(f" {status} {name}") + + elif prompt == "/reload": + self.config = reload_config() + self.agents = {} + for name, agent_config in self.config.agents.items(): + try: + self.agents[name] = Agent(config_obj=agent_config) + except Exception as e: + print(f"⚠️ Error: {name}: {e}") + print(f"✅ Reloaded: {len(self.agents)} agent(s)") + if self.agents: + current_agent = list(self.agents.keys())[0] + + elif prompt == "/all": + user_prompt = input("Prompt: ").strip() + if user_prompt: + print("Running on all agents...") + results = await self.run_all(user_prompt) + for name, result in results.items(): + status = "✅" if result.success else "❌" + output = result.output[:300] + "..." if len(result.output) > 300 else result.output + print(f"\n{status} {name}:\n{output or result.error}") + + elif prompt.startswith("/"): + print(f"Unknown command: {prompt}") + + else: + print("Running...") + result = await self.run_agent(current_agent, prompt) + + if result.success: + print(f"\n{result.output}") + if result.tool_results: + print(f"\n[Tools used: {len(result.tool_results)}]") + else: + print(f"\n❌ {result.error}") + + except KeyboardInterrupt: + print("\n\nUse /quit to exit.") + except Exception as e: + print(f"\n❌ Error: {e}") + + +async def main(): + parser = argparse.ArgumentParser(description="LLM Orchestrator") + parser.add_argument("--config", type=str, help="Path to config.yaml") + parser.add_argument("--status", action="store_true", help="Show status") + parser.add_argument("--agents", action="store_true", help="List agents") + parser.add_argument("--agent", type=str, help="Agent to use") + parser.add_argument("--prompt", type=str, help="Prompt to execute") + parser.add_argument("--health", action="store_true", help="Health check") + + args = parser.parse_args() + + orchestrator = Orchestrator(args.config) + + if args.status: + print(json.dumps(orchestrator.get_status(), indent=2)) + + elif args.agents: + for name, agent in orchestrator.agents.items(): + print(f" {name}: {agent.provider_name}/{agent.model}") + + elif args.health: + results = await orchestrator.health_check() + for name, healthy in results.items(): + status = "✅" if healthy else "❌" + print(f" {status} {name}") + + elif args.agent and args.prompt: + result = await orchestrator.run_agent(args.agent, args.prompt) + if result.success: + print(result.output) + else: + print(f"Error: {result.error}", file=sys.stderr) + sys.exit(1) + + else: + await orchestrator.interactive() + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +--- + +## config.yaml + +```yaml +# config.yaml - LLM Orchestrator Configuration +# +# Edit this file to define your agents and servers. + +# ============================================================================ +# SETTINGS +# ============================================================================ + +settings: + # Default provider/model if not specified per agent + default_provider: claude + default_model: sonnet + + # Timeout in seconds + timeout: 300 + + # Working directory (relative to this file) + working_dir: . + + # Max tool iterations per turn + max_tool_iterations: 10 + + # Security + sandbox_paths: true # Restrict file access to working_dir + ssh_strict_host_checking: true # Verify SSH hosts + # allowed_commands: [] # Whitelist commands (empty = all allowed) + + # Rate limiting + rate_limit_per_minute: 60 + + # Retry + max_retries: 3 + retry_delay: 1.0 + + # Circuit breaker (protects against cascading failures) + enable_circuit_breaker: true + circuit_breaker_threshold: 5 # Failures before opening + circuit_breaker_timeout: 30.0 # Seconds before retry + +# ============================================================================ +# SERVERS (optional) +# ============================================================================ +# Define servers for SSH access + +servers: + # Example: + # production: + # host: 192.168.1.100 + # user: deploy + # key: ~/.ssh/id_rsa + # port: 22 + # description: "Production server" + +# ============================================================================ +# AGENTS +# ============================================================================ +# Define your AI agents + +agents: + # Default assistant + assistant: + role: | + You are a helpful assistant that can execute commands, + read and write files, and help with various tasks. + provider: claude + model: sonnet + tools: + - bash + - read + - write + - list_dir + - glob + + # Coder agent (uncomment to enable) + # coder: + # role: | + # You are an expert programmer. + # You write clean, well-documented, and testable code. + # You follow best practices and include error handling. + # provider: claude + # model: sonnet + # tools: + # - read + # - write + # - bash + # - grep + # - glob + +# ============================================================================ +# TASKS (optional) +# ============================================================================ +# Define automated task sequences + +tasks: + # Example: + # deploy: + # description: "Deploy application to production" + # steps: + # - agent: coder + # prompt: "Run tests" + # - agent: deployer + # prompt: "Deploy to production" +``` + +--- + +## orchestrator/\_\_init\_\_.py + +```python +""" +Orchestrator module. +""" + +from .config import Config, get_config, reload_config, AgentConfig, ServerConfig, Settings +from .agents import Agent, AgentResult + +__all__ = [ + "Config", + "get_config", + "reload_config", + "AgentConfig", + "ServerConfig", + "Settings", + "Agent", + "AgentResult", +] +``` + +--- + +## orchestrator/config.py + +```python +""" +Configuration management. + +Loads configuration from: +1. Environment variables +2. .env file +3. config.yaml + +NEVER hardcode credentials. +""" + +import os +from pathlib import Path +from dataclasses import dataclass, field +from typing import Optional, Dict, Any, List + + +def load_env(): + """Load variables from .env file if it exists.""" + env_paths = [ + Path.cwd() / ".env", + Path(__file__).parent.parent / ".env", + ] + + for env_file in env_paths: + if env_file.exists(): + with open(env_file) as f: + for line in f: + line = line.strip() + if line and not line.startswith("#") and "=" in line: + key, _, value = line.partition("=") + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and value: + os.environ.setdefault(key, value) + break + + +# Load .env on import +load_env() + + +def get_env(key: str, default: str = "") -> str: + """Get environment variable.""" + return os.environ.get(key, default) + + +def get_env_bool(key: str, default: bool = False) -> bool: + """Get environment variable as boolean.""" + val = os.environ.get(key, "").lower() + if val in ("true", "yes", "1"): + return True + if val in ("false", "no", "0"): + return False + return default + + +def get_env_list(key: str, default: Optional[List] = None) -> List: + """Get environment variable as list.""" + val = os.environ.get(key, "") + if val: + return [x.strip() for x in val.split(",") if x.strip()] + return default or [] + + +@dataclass +class ServerConfig: + """Server configuration for SSH.""" + name: str + host: str + user: str = "root" + key: str = "" + port: int = 22 + description: str = "" + + def __post_init__(self): + if not self.key: + self.key = get_env("SSH_KEY_PATH", "~/.ssh/id_rsa") + + +@dataclass +class AgentConfig: + """Agent configuration.""" + name: str + role: str + provider: str = "claude" + model: str = "sonnet" + tools: List[str] = field(default_factory=list) + servers: List[str] = field(default_factory=list) + max_turns: int = 10 + + +@dataclass +class Settings: + """General settings.""" + default_provider: str = "claude" + default_model: str = "sonnet" + timeout: float = 300.0 + working_dir: str = "." + max_tool_iterations: int = 10 + + # Security + ssh_strict_host_checking: bool = True + sandbox_paths: bool = True + allowed_commands: List[str] = field(default_factory=list) + + # Rate limiting + rate_limit_per_minute: int = 60 + + # Retry + max_retries: int = 3 + retry_delay: float = 1.0 + + # Circuit breaker + enable_circuit_breaker: bool = True + circuit_breaker_threshold: int = 5 + circuit_breaker_timeout: float = 30.0 + + +class Config: + """Main configuration manager.""" + + def __init__(self, config_path: Optional[str] = None): + self.config_path = self._find_config(config_path) + self.base_dir = self.config_path.parent if self.config_path else Path.cwd() + self._raw = self._load_yaml() if self.config_path else {} + + # Parse configuration + self.settings = self._parse_settings() + self.servers = self._parse_servers() + self.agents = self._parse_agents() + self.tasks = self._raw.get("tasks", {}) + + # Directories + self.logs_dir = self.base_dir / "logs" + self.outputs_dir = self.base_dir / "outputs" + + # Create directories + self.logs_dir.mkdir(exist_ok=True) + self.outputs_dir.mkdir(exist_ok=True) + + def _find_config(self, config_path: Optional[str]) -> Optional[Path]: + """Find configuration file.""" + if config_path: + path = Path(config_path) + if path.exists(): + return path + raise FileNotFoundError(f"Config not found: {config_path}") + + search_paths = [ + Path.cwd() / "config.yaml", + Path.cwd() / "config.yml", + Path(__file__).parent.parent / "config.yaml", + ] + + for path in search_paths: + if path.exists(): + return path + + return None + + def _load_yaml(self) -> dict: + """Load YAML file.""" + if not self.config_path: + return {} + + try: + import yaml + with open(self.config_path) as f: + return yaml.safe_load(f) or {} + except ImportError: + print("WARNING: PyYAML not installed. Run: pip install pyyaml") + return {} + + def _parse_settings(self) -> Settings: + """Parse settings section.""" + raw = self._raw.get("settings", {}) + return Settings( + default_provider=raw.get("default_provider", "claude"), + default_model=raw.get("default_model", "sonnet"), + timeout=float(raw.get("timeout", 300)), + working_dir=raw.get("working_dir", "."), + max_tool_iterations=int(raw.get("max_tool_iterations", 10)), + ssh_strict_host_checking=raw.get( + "ssh_strict_host_checking", + get_env_bool("SSH_KNOWN_HOSTS_CHECK", True) + ), + sandbox_paths=raw.get("sandbox_paths", True), + allowed_commands=raw.get("allowed_commands", []), + rate_limit_per_minute=int(raw.get("rate_limit_per_minute", 60)), + max_retries=int(raw.get("max_retries", 3)), + retry_delay=float(raw.get("retry_delay", 1.0)), + enable_circuit_breaker=raw.get("enable_circuit_breaker", True), + circuit_breaker_threshold=int(raw.get("circuit_breaker_threshold", 5)), + circuit_breaker_timeout=float(raw.get("circuit_breaker_timeout", 30.0)), + ) + + def _parse_servers(self) -> Dict[str, ServerConfig]: + """Parse servers section.""" + servers = {} + for name, data in self._raw.get("servers", {}).items(): + if data: + servers[name] = ServerConfig( + name=name, + host=data.get("host", ""), + user=data.get("user", "root"), + key=data.get("key", get_env("SSH_KEY_PATH", "~/.ssh/id_rsa")), + port=int(data.get("port", 22)), + description=data.get("description", ""), + ) + return servers + + def _parse_agents(self) -> Dict[str, AgentConfig]: + """Parse agents section.""" + agents = {} + for name, data in self._raw.get("agents", {}).items(): + if data: + agents[name] = AgentConfig( + name=name, + role=data.get("role", ""), + provider=data.get("provider", self.settings.default_provider), + model=data.get("model", self.settings.default_model), + tools=data.get("tools", []), + servers=data.get("servers", []), + max_turns=int(data.get("max_turns", self.settings.max_tool_iterations)), + ) + return agents + + def get_agent(self, name: str) -> Optional[AgentConfig]: + return self.agents.get(name) + + def get_server(self, name: str) -> Optional[ServerConfig]: + return self.servers.get(name) + + def list_agents(self) -> List[str]: + return list(self.agents.keys()) + + def list_servers(self) -> List[str]: + return list(self.servers.keys()) + + +# Global instance +_config: Optional[Config] = None + + +def get_config(config_path: Optional[str] = None) -> Config: + """Get global configuration.""" + global _config + if _config is None: + _config = Config(config_path) + return _config + + +def reload_config(config_path: Optional[str] = None) -> Config: + """Reload configuration.""" + global _config + _config = Config(config_path) + return _config +``` + +--- + +## orchestrator/core/\_\_init\_\_.py + +```python +""" +Core components for resilience and reliability. +""" + +from .retry import retry_with_backoff, RetryConfig +from .circuit_breaker import CircuitBreaker, CircuitState +from .rate_limiter import RateLimiter + +__all__ = [ + "retry_with_backoff", + "RetryConfig", + "CircuitBreaker", + "CircuitState", + "RateLimiter", +] +``` + +--- + +## orchestrator/core/retry.py + +```python +""" +Retry logic with exponential backoff. + +Features: +- Configurable retry attempts +- Exponential backoff with jitter +- Selective exception handling +- Async support +""" + +import asyncio +import random +from dataclasses import dataclass, field +from typing import Callable, TypeVar, Optional, Type, Tuple, Any +from functools import wraps + +T = TypeVar("T") + + +@dataclass +class RetryConfig: + """Configuration for retry behavior.""" + + max_retries: int = 3 + base_delay: float = 1.0 + max_delay: float = 60.0 + exponential_base: float = 2.0 + jitter: bool = True + + # Exceptions that should trigger retry + retryable_exceptions: Tuple[Type[Exception], ...] = field( + default_factory=lambda: ( + ConnectionError, + TimeoutError, + asyncio.TimeoutError, + ) + ) + + # Exceptions that should NOT retry (fail immediately) + fatal_exceptions: Tuple[Type[Exception], ...] = field( + default_factory=lambda: ( + KeyboardInterrupt, + SystemExit, + PermissionError, + ) + ) + + def calculate_delay(self, attempt: int) -> float: + """Calculate delay for given attempt number.""" + delay = min( + self.base_delay * (self.exponential_base ** attempt), + self.max_delay + ) + + if self.jitter: + # Add random jitter (±25%) + delay = delay * (0.75 + random.random() * 0.5) + + return delay + + def should_retry(self, exception: Exception) -> bool: + """Determine if exception should trigger retry.""" + if isinstance(exception, self.fatal_exceptions): + return False + + if self.retryable_exceptions: + return isinstance(exception, self.retryable_exceptions) + + # By default, retry on any non-fatal exception + return True + + +async def retry_with_backoff( + func: Callable[..., T], + *args, + config: Optional[RetryConfig] = None, + on_retry: Optional[Callable[[int, Exception], None]] = None, + **kwargs +) -> Tuple[T, int]: + """ + Execute async function with retry and exponential backoff. + + Args: + func: Async function to execute + *args: Arguments for func + config: Retry configuration + on_retry: Optional callback on each retry (attempt, exception) + **kwargs: Keyword arguments for func + + Returns: + Tuple of (result, number_of_retries) + + Raises: + Last exception if all retries exhausted + """ + config = config or RetryConfig() + last_exception: Optional[Exception] = None + + for attempt in range(config.max_retries + 1): + try: + if asyncio.iscoroutinefunction(func): + result = await func(*args, **kwargs) + else: + result = func(*args, **kwargs) + return result, attempt + + except Exception as e: + last_exception = e + + # Check if we should retry + if not config.should_retry(e): + raise + + # Check if we have retries left + if attempt >= config.max_retries: + raise + + # Calculate delay and wait + delay = config.calculate_delay(attempt) + + # Call retry callback if provided + if on_retry: + on_retry(attempt + 1, e) + + await asyncio.sleep(delay) + + # Should never reach here, but just in case + if last_exception: + raise last_exception + raise RuntimeError("Retry logic error") + + +def with_retry(config: Optional[RetryConfig] = None): + """ + Decorator for retry with backoff. + + Usage: + @with_retry(RetryConfig(max_retries=5)) + async def my_function(): + ... + """ + def decorator(func: Callable[..., T]) -> Callable[..., T]: + @wraps(func) + async def wrapper(*args, **kwargs) -> T: + result, _ = await retry_with_backoff(func, *args, config=config, **kwargs) + return result + return wrapper + return decorator +``` + +--- + +## orchestrator/core/circuit_breaker.py + +```python +""" +Circuit Breaker pattern implementation. + +Protects against cascading failures by: +- Tracking failure rates +- Opening circuit after threshold +- Auto-recovery with half-open state +""" + +import asyncio +import time +from dataclasses import dataclass +from enum import Enum +from typing import Callable, TypeVar, Optional, Any + +T = TypeVar("T") + + +class CircuitState(Enum): + """Circuit breaker states.""" + CLOSED = "closed" # Normal operation + OPEN = "open" # Failing, reject calls + HALF_OPEN = "half_open" # Testing recovery + + +@dataclass +class CircuitStats: + """Statistics for circuit breaker.""" + total_calls: int = 0 + successful_calls: int = 0 + failed_calls: int = 0 + rejected_calls: int = 0 + last_failure_time: Optional[float] = None + last_success_time: Optional[float] = None + + @property + def failure_rate(self) -> float: + if self.total_calls == 0: + return 0.0 + return self.failed_calls / self.total_calls + + +class CircuitBreakerError(Exception): + """Raised when circuit is open.""" + pass + + +class CircuitBreaker: + """ + Circuit breaker for fault tolerance. + + States: + - CLOSED: Normal operation, tracking failures + - OPEN: Circuit tripped, rejecting calls immediately + - HALF_OPEN: Testing if service recovered + + Usage: + breaker = CircuitBreaker(failure_threshold=5) + + async with breaker: + result = await risky_operation() + """ + + def __init__( + self, + failure_threshold: int = 5, + success_threshold: int = 2, + timeout: float = 30.0, + half_open_max_calls: int = 3, + ): + """ + Args: + failure_threshold: Failures before opening circuit + success_threshold: Successes in half-open to close + timeout: Seconds before trying half-open + half_open_max_calls: Max concurrent calls in half-open + """ + self.failure_threshold = failure_threshold + self.success_threshold = success_threshold + self.timeout = timeout + self.half_open_max_calls = half_open_max_calls + + self._state = CircuitState.CLOSED + self._failure_count = 0 + self._success_count = 0 + self._last_failure_time: Optional[float] = None + self._half_open_calls = 0 + self._lock = asyncio.Lock() + + self.stats = CircuitStats() + + @property + def state(self) -> CircuitState: + """Current circuit state.""" + return self._state + + @property + def is_closed(self) -> bool: + return self._state == CircuitState.CLOSED + + @property + def is_open(self) -> bool: + return self._state == CircuitState.OPEN + + def _should_attempt_reset(self) -> bool: + """Check if enough time passed to try half-open.""" + if self._last_failure_time is None: + return True + return time.time() - self._last_failure_time >= self.timeout + + async def _check_state(self) -> bool: + """ + Check and potentially transition state. + Returns True if call should proceed. + """ + async with self._lock: + if self._state == CircuitState.CLOSED: + return True + + if self._state == CircuitState.OPEN: + if self._should_attempt_reset(): + self._state = CircuitState.HALF_OPEN + self._success_count = 0 + self._half_open_calls = 0 + else: + self.stats.rejected_calls += 1 + return False + + if self._state == CircuitState.HALF_OPEN: + if self._half_open_calls >= self.half_open_max_calls: + self.stats.rejected_calls += 1 + return False + self._half_open_calls += 1 + + return True + + async def _record_success(self): + """Record successful call.""" + async with self._lock: + self.stats.total_calls += 1 + self.stats.successful_calls += 1 + self.stats.last_success_time = time.time() + + if self._state == CircuitState.HALF_OPEN: + self._success_count += 1 + if self._success_count >= self.success_threshold: + self._state = CircuitState.CLOSED + self._failure_count = 0 + + elif self._state == CircuitState.CLOSED: + # Reset failure count on success + self._failure_count = 0 + + async def _record_failure(self, exception: Exception): + """Record failed call.""" + async with self._lock: + self.stats.total_calls += 1 + self.stats.failed_calls += 1 + self.stats.last_failure_time = time.time() + self._last_failure_time = time.time() + + if self._state == CircuitState.HALF_OPEN: + # Any failure in half-open reopens circuit + self._state = CircuitState.OPEN + + elif self._state == CircuitState.CLOSED: + self._failure_count += 1 + if self._failure_count >= self.failure_threshold: + self._state = CircuitState.OPEN + + async def call( + self, + func: Callable[..., T], + *args, + **kwargs + ) -> T: + """ + Execute function through circuit breaker. + + Raises: + CircuitBreakerError: If circuit is open + """ + if not await self._check_state(): + raise CircuitBreakerError( + f"Circuit breaker is {self._state.value}. " + f"Retry after {self.timeout}s" + ) + + try: + if asyncio.iscoroutinefunction(func): + result = await func(*args, **kwargs) + else: + result = func(*args, **kwargs) + + await self._record_success() + return result + + except Exception as e: + await self._record_failure(e) + raise + + async def __aenter__(self): + """Async context manager entry.""" + if not await self._check_state(): + raise CircuitBreakerError( + f"Circuit breaker is {self._state.value}" + ) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Async context manager exit.""" + if exc_type is None: + await self._record_success() + else: + await self._record_failure(exc_val) + return False # Don't suppress exceptions + + def reset(self): + """Manually reset circuit breaker.""" + self._state = CircuitState.CLOSED + self._failure_count = 0 + self._success_count = 0 + self._last_failure_time = None + self.stats = CircuitStats() + + def __repr__(self) -> str: + return ( + f"CircuitBreaker(state={self._state.value}, " + f"failures={self._failure_count}/{self.failure_threshold})" + ) +``` + +--- + +## orchestrator/core/rate_limiter.py + +```python +""" +Rate limiter with sliding window. + +Features: +- Sliding window algorithm +- Async-safe with lock +- Configurable burst allowance +""" + +import asyncio +import time +from collections import deque +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class RateLimitStats: + """Statistics for rate limiter.""" + total_requests: int = 0 + allowed_requests: int = 0 + throttled_requests: int = 0 + total_wait_time: float = 0.0 + + +class RateLimiter: + """ + Sliding window rate limiter. + + Limits calls to max_calls per period seconds. + + Usage: + limiter = RateLimiter(max_calls=60, period=60.0) + + await limiter.acquire() # Blocks if rate exceeded + do_something() + """ + + def __init__( + self, + max_calls: int = 60, + period: float = 60.0, + burst_allowance: int = 0, + ): + """ + Args: + max_calls: Maximum calls allowed in period + period: Time window in seconds + burst_allowance: Extra calls allowed for bursts + """ + self.max_calls = max_calls + self.period = period + self.burst_allowance = burst_allowance + + self._calls: deque = deque() + self._lock = asyncio.Lock() + + self.stats = RateLimitStats() + + @property + def effective_limit(self) -> int: + """Effective rate limit including burst.""" + return self.max_calls + self.burst_allowance + + def _cleanup_old_calls(self, now: float): + """Remove calls outside the window.""" + cutoff = now - self.period + while self._calls and self._calls[0] < cutoff: + self._calls.popleft() + + @property + def current_usage(self) -> int: + """Current number of calls in window.""" + self._cleanup_old_calls(time.time()) + return len(self._calls) + + @property + def available_calls(self) -> int: + """Number of calls available right now.""" + return max(0, self.effective_limit - self.current_usage) + + async def acquire(self, timeout: Optional[float] = None) -> bool: + """ + Acquire permission to make a call. + + Blocks until rate limit allows the call. + + Args: + timeout: Maximum time to wait (None = unlimited) + + Returns: + True if acquired, False if timeout + """ + start_time = time.time() + + async with self._lock: + self.stats.total_requests += 1 + + while True: + now = time.time() + self._cleanup_old_calls(now) + + # Check if we can proceed + if len(self._calls) < self.effective_limit: + self._calls.append(now) + self.stats.allowed_requests += 1 + return True + + # Calculate wait time + oldest_call = self._calls[0] + wait_time = oldest_call + self.period - now + + if wait_time <= 0: + continue + + # Check timeout + if timeout is not None: + elapsed = now - start_time + if elapsed + wait_time > timeout: + self.stats.throttled_requests += 1 + return False + + # Wait and retry + self.stats.total_wait_time += wait_time + + # Release lock while waiting + self._lock.release() + try: + await asyncio.sleep(wait_time) + finally: + await self._lock.acquire() + + async def try_acquire(self) -> bool: + """ + Try to acquire without waiting. + + Returns: + True if acquired, False if would need to wait + """ + async with self._lock: + self.stats.total_requests += 1 + now = time.time() + self._cleanup_old_calls(now) + + if len(self._calls) < self.effective_limit: + self._calls.append(now) + self.stats.allowed_requests += 1 + return True + + self.stats.throttled_requests += 1 + return False + + def reset(self): + """Reset rate limiter state.""" + self._calls.clear() + self.stats = RateLimitStats() + + def __repr__(self) -> str: + return ( + f"RateLimiter({self.current_usage}/{self.effective_limit} " + f"per {self.period}s)" + ) + + +class MultiRateLimiter: + """ + Multiple rate limiters combined. + + Useful for APIs with multiple rate limits: + - 60 requests per minute + - 1000 requests per hour + + Usage: + limiter = MultiRateLimiter([ + RateLimiter(60, 60), # per minute + RateLimiter(1000, 3600), # per hour + ]) + + await limiter.acquire() + """ + + def __init__(self, limiters: list[RateLimiter]): + self.limiters = limiters + + async def acquire(self, timeout: Optional[float] = None) -> bool: + """Acquire from all limiters.""" + for limiter in self.limiters: + if not await limiter.acquire(timeout): + return False + return True + + async def try_acquire(self) -> bool: + """Try to acquire from all limiters without waiting.""" + for limiter in self.limiters: + if not await limiter.try_acquire(): + return False + return True +``` + +--- + +## orchestrator/providers/\_\_init\_\_.py + +```python +""" +LLM Providers. +""" + +from .base import BaseProvider, ProviderResponse +from .claude_provider import ClaudeProvider +from .litellm_provider import LiteLLMProvider + +__all__ = [ + "BaseProvider", + "ProviderResponse", + "ClaudeProvider", + "LiteLLMProvider", +] +``` + +--- + +## orchestrator/providers/base.py + +```python +""" +Base provider with resilience features. + +All providers inherit from this and get: +- Rate limiting +- Circuit breaker +- Retry with backoff +- Consistent response format +""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from datetime import datetime +from typing import Optional, Any, List + +from ..core import ( + CircuitBreaker, + RateLimiter, + RetryConfig, + retry_with_backoff, +) + + +@dataclass +class ToolCall: + """A tool call from the LLM.""" + tool: str + params: dict + id: Optional[str] = None + + +@dataclass +class ToolResult: + """Result of executing a tool.""" + tool: str + success: bool + output: str + error: Optional[str] = None + execution_time: float = 0.0 + + +@dataclass +class UsageInfo: + """Token usage information.""" + input_tokens: int = 0 + output_tokens: int = 0 + cache_read_tokens: int = 0 + cache_creation_tokens: int = 0 + + @property + def total_tokens(self) -> int: + return self.input_tokens + self.output_tokens + + +@dataclass +class ProviderResponse: + """Standardized response from any provider.""" + + success: bool + text: str + provider: str + model: str + timestamp: str = field(default_factory=lambda: datetime.now().isoformat()) + error: Optional[str] = None + usage: Optional[UsageInfo] = None + tool_calls: List[ToolCall] = field(default_factory=list) + tool_results: List[ToolResult] = field(default_factory=list) + retries: int = 0 + raw_response: Optional[Any] = None + session_id: Optional[str] = None + + @property + def ok(self) -> bool: + return self.success + + def to_dict(self) -> dict: + """Convert to dictionary.""" + return { + "success": self.success, + "text": self.text, + "provider": self.provider, + "model": self.model, + "timestamp": self.timestamp, + "error": self.error, + "usage": { + "input_tokens": self.usage.input_tokens, + "output_tokens": self.usage.output_tokens, + "total_tokens": self.usage.total_tokens, + } if self.usage else None, + "retries": self.retries, + "session_id": self.session_id, + } + + +class BaseProvider(ABC): + """ + Abstract base class for all LLM providers. + + Provides: + - Rate limiting + - Circuit breaker for fault tolerance + - Retry with exponential backoff + - Consistent response format + + Subclasses must implement: + - name (property) + - available_models (property) + - _execute (method) + """ + + # Model aliases - override in subclasses + MODEL_ALIASES: dict[str, str] = {} + + def __init__( + self, + model: str, + timeout: float = 300.0, + max_retries: int = 3, + retry_delay: float = 1.0, + rate_limit_per_minute: int = 60, + enable_circuit_breaker: bool = True, + circuit_breaker_threshold: int = 5, + circuit_breaker_timeout: float = 30.0, + **kwargs + ): + self.model = model + self.timeout = timeout + self.config = kwargs + + # Retry config + self.retry_config = RetryConfig( + max_retries=max_retries, + base_delay=retry_delay, + max_delay=60.0, + ) + + # Rate limiter + self.rate_limiter = RateLimiter( + max_calls=rate_limit_per_minute, + period=60.0, + ) + + # Circuit breaker + self.enable_circuit_breaker = enable_circuit_breaker + self.circuit_breaker = CircuitBreaker( + failure_threshold=circuit_breaker_threshold, + timeout=circuit_breaker_timeout, + ) if enable_circuit_breaker else None + + @property + @abstractmethod + def name(self) -> str: + """Provider name (e.g., 'claude', 'litellm').""" + pass + + @property + @abstractmethod + def available_models(self) -> list[str]: + """List of available model names/aliases.""" + pass + + @property + def supports_native_tools(self) -> bool: + """Whether provider supports native function calling.""" + return False + + @property + def supports_streaming(self) -> bool: + """Whether provider supports streaming responses.""" + return False + + def resolve_model(self, model: str) -> str: + """Resolve model alias to full model name.""" + return self.MODEL_ALIASES.get(model, model) + + @abstractmethod + async def _execute( + self, + prompt: str, + system_prompt: Optional[str] = None, + tools: Optional[list[str]] = None, + **kwargs + ) -> ProviderResponse: + """ + Internal execution method - implement in subclasses. + + This is the raw execution without retry/circuit breaker. + """ + pass + + async def run( + self, + prompt: str, + system_prompt: Optional[str] = None, + **kwargs + ) -> ProviderResponse: + """ + Execute a prompt without tools. + + Includes rate limiting, retry, and circuit breaker. + """ + return await self.run_with_tools( + prompt=prompt, + tools=[], + system_prompt=system_prompt, + **kwargs + ) + + async def run_with_tools( + self, + prompt: str, + tools: list[str], + system_prompt: Optional[str] = None, + **kwargs + ) -> ProviderResponse: + """ + Execute a prompt with tools. + + Includes rate limiting, retry, and circuit breaker. + """ + # Rate limiting + await self.rate_limiter.acquire() + + # Define the execution function + async def do_execute() -> ProviderResponse: + return await self._execute( + prompt=prompt, + system_prompt=system_prompt, + tools=tools, + **kwargs + ) + + try: + # With circuit breaker + if self.circuit_breaker: + async with self.circuit_breaker: + response, retries = await retry_with_backoff( + do_execute, + config=self.retry_config, + ) + else: + response, retries = await retry_with_backoff( + do_execute, + config=self.retry_config, + ) + + response.retries = retries + return response + + except Exception as e: + return ProviderResponse( + success=False, + text="", + provider=self.name, + model=self.model, + error=str(e), + ) + + async def health_check(self) -> bool: + """Check if provider is healthy.""" + try: + response = await self.run("Respond with only: OK") + return response.success and "OK" in response.text.upper() + except Exception: + return False + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(model={self.model})" +``` + +--- + +## orchestrator/providers/claude_provider.py + +```python +""" +Claude provider using Claude Code CLI. + +This provider executes Claude via the CLI, handling: +- Proper JSON parsing +- Session management +- Tool execution +- Error handling +""" + +import asyncio +import json +import shutil +import os +from typing import Optional, List, Any +from pathlib import Path + +from .base import BaseProvider, ProviderResponse, UsageInfo, ToolCall + + +class ClaudeProviderError(Exception): + """Base exception for Claude provider.""" + pass + + +class CLINotFoundError(ClaudeProviderError): + """Claude CLI not found.""" + pass + + +class ExecutionError(ClaudeProviderError): + """Error during execution.""" + pass + + +class ClaudeProvider(BaseProvider): + """ + Provider that uses Claude Code CLI. + + Requires: + - Claude Code CLI installed (`claude` command available) + - Valid authentication (Pro/Max subscription or API key) + + Features: + - Native tool support (Bash, Read, Write, etc.) + - Session management + - JSON output parsing + - Autocompact for long conversations + """ + + MODEL_ALIASES = { + # Friendly names + "haiku": "claude-3-5-haiku-latest", + "sonnet": "claude-sonnet-4-20250514", + "opus": "claude-opus-4-20250514", + # Semantic names + "fast": "claude-3-5-haiku-latest", + "default": "claude-sonnet-4-20250514", + "smart": "claude-sonnet-4-20250514", + "powerful": "claude-opus-4-20250514", + } + + # Tools supported by Claude CLI + NATIVE_TOOLS = [ + "Bash", "Read", "Write", "Edit", + "Glob", "Grep", "WebFetch", + "TodoRead", "TodoWrite", + ] + + # Mapping from our tool names to Claude CLI tool names + TOOL_NAME_MAP = { + "bash": "Bash", + "read": "Read", + "write": "Write", + "edit": "Edit", + "glob": "Glob", + "grep": "Grep", + "web_fetch": "WebFetch", + "webfetch": "WebFetch", + } + + def __init__( + self, + model: str = "sonnet", + timeout: float = 300.0, + cli_path: str = "claude", + working_directory: Optional[str] = None, + max_turns: int = 10, + autocompact: bool = True, + **kwargs + ): + """ + Args: + model: Model to use (sonnet, opus, haiku, or full name) + timeout: Execution timeout in seconds + cli_path: Path to claude CLI + working_directory: Working directory for execution + max_turns: Maximum tool use turns + autocompact: Enable automatic context compaction + """ + super().__init__(model=model, timeout=timeout, **kwargs) + + self.cli_path = cli_path + self.working_directory = working_directory or os.getcwd() + self.max_turns = max_turns + self.autocompact = autocompact + + # Verify CLI exists + if not shutil.which(self.cli_path): + raise CLINotFoundError( + f"Claude CLI not found at '{self.cli_path}'. " + "Install with: npm install -g @anthropic-ai/claude-code" + ) + + @property + def name(self) -> str: + return "claude" + + @property + def available_models(self) -> list[str]: + return list(self.MODEL_ALIASES.keys()) + + @property + def supports_native_tools(self) -> bool: + return True + + def _map_tools(self, tools: List[str]) -> List[str]: + """Map our tool names to Claude CLI tool names.""" + mapped = [] + for tool in tools: + tool_lower = tool.lower() + if tool_lower in self.TOOL_NAME_MAP: + mapped.append(self.TOOL_NAME_MAP[tool_lower]) + elif tool in self.NATIVE_TOOLS: + mapped.append(tool) + # Skip non-native tools (http_request, ssh, etc.) + return mapped + + def _build_command( + self, + prompt: str, + system_prompt: Optional[str] = None, + tools: Optional[List[str]] = None, + session_id: Optional[str] = None, + continue_session: bool = False, + ) -> List[str]: + """Build the CLI command.""" + cmd = [self.cli_path] + + # Prompt + cmd.extend(["-p", prompt]) + + # Output format + cmd.extend(["--output-format", "json"]) + + # Model + resolved_model = self.resolve_model(self.model) + cmd.extend(["--model", resolved_model]) + + # System prompt + if system_prompt: + cmd.extend(["--system-prompt", system_prompt]) + + # Tools + if tools: + native_tools = self._map_tools(tools) + if native_tools: + cmd.extend(["--allowedTools", ",".join(native_tools)]) + + # Max turns + cmd.extend(["--max-turns", str(self.max_turns)]) + + # Session management + if session_id: + if continue_session: + cmd.extend(["--continue", session_id]) + else: + cmd.extend(["--session-id", session_id]) + + return cmd + + def _parse_response(self, stdout: str, stderr: str, return_code: int) -> dict: + """Parse the CLI response.""" + # Try to parse JSON + try: + # Handle potential multiple JSON objects (streaming artifacts) + lines = stdout.strip().split('\n') + + # Find the last valid JSON object + for line in reversed(lines): + line = line.strip() + if line.startswith('{'): + try: + return json.loads(line) + except json.JSONDecodeError: + continue + + # Try parsing the whole output + return json.loads(stdout) + + except json.JSONDecodeError: + # If JSON parsing fails, return raw output + return { + "result": stdout, + "is_error": return_code != 0, + "error": stderr if stderr else None, + } + + def _extract_usage(self, response: dict) -> Optional[UsageInfo]: + """Extract usage information from response.""" + usage_data = response.get("usage") + if not usage_data: + return None + + return UsageInfo( + input_tokens=usage_data.get("input_tokens", 0), + output_tokens=usage_data.get("output_tokens", 0), + cache_read_tokens=usage_data.get("cache_read_input_tokens", 0), + cache_creation_tokens=usage_data.get("cache_creation_input_tokens", 0), + ) + + async def _execute( + self, + prompt: str, + system_prompt: Optional[str] = None, + tools: Optional[List[str]] = None, + session_id: Optional[str] = None, + continue_session: bool = False, + **kwargs + ) -> ProviderResponse: + """Execute the Claude CLI.""" + cmd = self._build_command( + prompt=prompt, + system_prompt=system_prompt, + tools=tools or [], + session_id=session_id, + continue_session=continue_session, + ) + + try: + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=self.working_directory, + ) + + stdout, stderr = await asyncio.wait_for( + process.communicate(), + timeout=self.timeout + ) + + stdout_str = stdout.decode("utf-8", errors="replace") + stderr_str = stderr.decode("utf-8", errors="replace") + + # Parse response + response_data = self._parse_response( + stdout_str, stderr_str, process.returncode + ) + + # Check for errors + is_error = response_data.get("is_error", False) or process.returncode != 0 + + # Extract text + text = response_data.get("result", "") + if not text and "content" in response_data: + # Handle different response formats + content = response_data["content"] + if isinstance(content, list): + text = "\n".join( + c.get("text", "") for c in content + if c.get("type") == "text" + ) + elif isinstance(content, str): + text = content + + # Extract error + error = None + if is_error: + error = response_data.get("error") or stderr_str or "Unknown error" + + return ProviderResponse( + success=not is_error, + text=text, + provider=self.name, + model=self.model, + error=error, + usage=self._extract_usage(response_data), + session_id=response_data.get("session_id"), + raw_response=response_data, + ) + + except asyncio.TimeoutError: + raise ExecutionError(f"Timeout after {self.timeout}s") + except FileNotFoundError: + raise CLINotFoundError(f"Claude CLI not found: {self.cli_path}") + except Exception as e: + raise ExecutionError(f"Execution failed: {e}") + + async def run_with_session( + self, + prompt: str, + session_id: str, + continue_session: bool = True, + **kwargs + ) -> ProviderResponse: + """Run with explicit session management.""" + return await self.run_with_tools( + prompt=prompt, + tools=kwargs.pop("tools", []), + system_prompt=kwargs.pop("system_prompt", None), + session_id=session_id, + continue_session=continue_session, + **kwargs + ) + + async def health_check(self) -> bool: + """Verify Claude CLI is working.""" + try: + # Simple test command + cmd = [self.cli_path, "-p", "Say OK", "--output-format", "json", "--max-turns", "1"] + + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + stdout, _ = await asyncio.wait_for( + process.communicate(), + timeout=30.0 + ) + + if process.returncode == 0: + return True + + return False + + except Exception: + return False +``` + +--- + +## orchestrator/providers/litellm_provider.py + +```python +""" +Universal LLM provider using LiteLLM. + +Supports 100+ models through a unified interface: +- OpenAI (GPT-4, o1) +- Google (Gemini) +- Anthropic (via API) +- Mistral +- Ollama (local) +- Groq +- Together +- And many more... +""" + +import asyncio +import json +import re +from typing import Optional, List, Any + +from .base import BaseProvider, ProviderResponse, UsageInfo, ToolCall, ToolResult + + +class LiteLLMProvider(BaseProvider): + """ + Universal provider using LiteLLM. + + Requires: + - pip install litellm + - Appropriate API keys in environment + + Features: + - 100+ model support + - Unified API + - Tool execution loop + - Cost tracking + """ + + MODEL_ALIASES = { + # OpenAI + "gpt4": "gpt-4", + "gpt4o": "gpt-4o", + "gpt4-turbo": "gpt-4-turbo", + "gpt4o-mini": "gpt-4o-mini", + "o1": "o1-preview", + "o1-mini": "o1-mini", + "o3-mini": "o3-mini", + + # Google Gemini + "gemini": "gemini/gemini-1.5-pro", + "gemini-pro": "gemini/gemini-1.5-pro", + "gemini-flash": "gemini/gemini-1.5-flash", + "gemini-2": "gemini/gemini-2.0-flash", + + # Anthropic (via API, not CLI) + "claude-api": "claude-3-5-sonnet-20241022", + "claude-opus-api": "claude-3-opus-20240229", + + # Mistral + "mistral": "mistral/mistral-large-latest", + "mistral-small": "mistral/mistral-small-latest", + "mixtral": "mistral/open-mixtral-8x22b", + "codestral": "mistral/codestral-latest", + + # Ollama (local) + "llama3": "ollama/llama3", + "llama3.1": "ollama/llama3.1", + "llama3.2": "ollama/llama3.2", + "codellama": "ollama/codellama", + "mixtral-local": "ollama/mixtral", + "qwen": "ollama/qwen2.5", + "deepseek": "ollama/deepseek-r1", + + # Groq (fast inference) + "groq-llama": "groq/llama-3.3-70b-versatile", + "groq-mixtral": "groq/mixtral-8x7b-32768", + + # Together + "together-llama": "together_ai/meta-llama/Llama-3-70b-chat-hf", + + # DeepSeek + "deepseek-chat": "deepseek/deepseek-chat", + "deepseek-coder": "deepseek/deepseek-coder", + } + + # Models that support native function calling + FUNCTION_CALLING_MODELS = { + "gpt-4", "gpt-4o", "gpt-4-turbo", "gpt-4o-mini", + "claude-3", "gemini", + } + + def __init__( + self, + model: str = "gpt-4o", + timeout: float = 300.0, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + max_tool_iterations: int = 10, + temperature: float = 0.7, + **kwargs + ): + """ + Args: + model: Model name or alias + timeout: Request timeout + api_key: Override API key + api_base: Override API base URL + max_tool_iterations: Max tool use loops + temperature: Sampling temperature + """ + super().__init__(model=model, timeout=timeout, **kwargs) + + self.api_key = api_key + self.api_base = api_base + self.max_tool_iterations = max_tool_iterations + self.temperature = temperature + + self._litellm = None + + def _get_litellm(self): + """Lazy load litellm.""" + if self._litellm is None: + try: + import litellm + litellm.set_verbose = False + self._litellm = litellm + except ImportError: + raise ImportError( + "LiteLLM not installed. Run: pip install litellm" + ) + return self._litellm + + @property + def name(self) -> str: + return "litellm" + + @property + def available_models(self) -> list[str]: + return list(self.MODEL_ALIASES.keys()) + + @property + def supports_native_tools(self) -> bool: + """Check if current model supports function calling.""" + resolved = self.resolve_model(self.model) + return any(fc in resolved for fc in self.FUNCTION_CALLING_MODELS) + + def _build_tools_prompt(self, tools: List[str]) -> str: + """Build a prompt section describing available tools.""" + if not tools: + return "" + + prompt = """ +## Available Tools + +You can use tools by responding with a JSON block like this: + +```tool +{ + "tool": "tool_name", + "params": { + "param1": "value1" + } +} +``` + +Available tools: + +""" + + tool_descriptions = { + "bash": "Execute a bash command. Params: command (required), working_dir (optional)", + "read": "Read a file. Params: path (required), encoding (optional)", + "write": "Write to a file. Params: path (required), content (required), append (optional bool)", + "glob": "Find files by pattern. Params: pattern (required), base_dir (optional)", + "grep": "Search text in files. Params: pattern (required), path (required), recursive (optional bool)", + "http_request": "Make HTTP request. Params: url (required), method (optional), headers (optional), body (optional)", + "ssh": "Execute remote command. Params: host (required), command (required), user (optional), key_path (optional)", + "list_dir": "List directory contents. Params: path (required), recursive (optional bool)", + } + + for tool in tools: + if tool in tool_descriptions: + prompt += f"- **{tool}**: {tool_descriptions[tool]}\n" + + prompt += "\nAfter using a tool, you'll receive the result and can continue.\n" + + return prompt + + def _parse_tool_calls(self, text: str) -> List[ToolCall]: + """Parse tool calls from response text.""" + calls = [] + + # Pattern: ```tool { ... } ``` + pattern1 = r'```tool\s*\n?(.*?)\n?```' + for match in re.findall(pattern1, text, re.DOTALL): + try: + data = json.loads(match.strip()) + if "tool" in data: + calls.append(ToolCall( + tool=data["tool"], + params=data.get("params", {}), + )) + except json.JSONDecodeError: + continue + + # Pattern: ```json { "tool": ... } ``` + pattern2 = r'```json\s*\n?(.*?)\n?```' + for match in re.findall(pattern2, text, re.DOTALL): + try: + data = json.loads(match.strip()) + if "tool" in data: + calls.append(ToolCall( + tool=data["tool"], + params=data.get("params", {}), + )) + except json.JSONDecodeError: + continue + + return calls + + async def _call_llm( + self, + messages: List[dict], + max_tokens: Optional[int] = None, + ) -> dict: + """Make a single LLM call.""" + litellm = self._get_litellm() + + resolved_model = self.resolve_model(self.model) + + kwargs = { + "model": resolved_model, + "messages": messages, + "temperature": self.temperature, + "timeout": self.timeout, + } + + if max_tokens: + kwargs["max_tokens"] = max_tokens + + if self.api_key: + kwargs["api_key"] = self.api_key + + if self.api_base: + kwargs["api_base"] = self.api_base + + return await litellm.acompletion(**kwargs) + + async def _execute( + self, + prompt: str, + system_prompt: Optional[str] = None, + tools: Optional[List[str]] = None, + tool_executor: Optional[Any] = None, + max_tokens: Optional[int] = None, + **kwargs + ) -> ProviderResponse: + """Execute with optional tool loop.""" + tools = tools or [] + resolved_model = self.resolve_model(self.model) + + # Build system prompt with tools + full_system = system_prompt or "" + if tools: + tools_prompt = self._build_tools_prompt(tools) + full_system = f"{full_system}\n\n{tools_prompt}" if full_system else tools_prompt + + # Build messages + messages = [] + if full_system: + messages.append({"role": "system", "content": full_system}) + messages.append({"role": "user", "content": prompt}) + + all_tool_results: List[ToolResult] = [] + total_usage = UsageInfo() + text = "" + + # Tool execution loop + for iteration in range(self.max_tool_iterations): + try: + response = await self._call_llm(messages, max_tokens) + except Exception as e: + return ProviderResponse( + success=False, + text="", + provider=self.name, + model=resolved_model, + error=f"LiteLLM error: {e}", + ) + + # Extract text + text = response.choices[0].message.content or "" + + # Update usage + if hasattr(response, 'usage') and response.usage: + total_usage.input_tokens += getattr(response.usage, 'prompt_tokens', 0) + total_usage.output_tokens += getattr(response.usage, 'completion_tokens', 0) + + # Check for tool calls + if tools and tool_executor: + tool_calls = self._parse_tool_calls(text) + + if tool_calls: + # Execute tools + results_text_parts = [] + + for call in tool_calls: + result = await tool_executor.execute(call.tool, call.params) + all_tool_results.append(ToolResult( + tool=call.tool, + success=result.success, + output=result.output, + error=result.error, + execution_time=result.execution_time, + )) + + status = "✅" if result.success else "❌" + output = result.output if result.success else result.error + results_text_parts.append( + f"**{call.tool}** {status}\n{output}" + ) + + # Add to conversation and continue + results_text = "\n\n".join(results_text_parts) + messages.append({"role": "assistant", "content": text}) + messages.append({"role": "user", "content": f"Tool results:\n\n{results_text}\n\nContinue."}) + continue + + # No tool calls, we're done + break + + return ProviderResponse( + success=True, + text=text, + provider=self.name, + model=resolved_model, + usage=total_usage if total_usage.total_tokens > 0 else None, + tool_results=[ + ToolResult( + tool=r.tool, + success=r.success, + output=r.output, + error=r.error, + ) + for r in all_tool_results + ], + ) +``` + +--- + +## orchestrator/tools/\_\_init\_\_.py + +```python +""" +Tool definitions and executor. +""" + +from .definitions import TOOL_DEFINITIONS, get_tool_schema, get_tools_prompt +from .executor import ToolExecutor, ToolResult, SecurityValidator + +__all__ = [ + "TOOL_DEFINITIONS", + "get_tool_schema", + "get_tools_prompt", + "ToolExecutor", + "ToolResult", + "SecurityValidator", +] +``` + +--- + +## orchestrator/tools/definitions.py + +```python +""" +Tool definitions in standard format. + +Compatible with OpenAI function calling and other providers. +""" + +from typing import Any, List, Dict + + +TOOL_DEFINITIONS: Dict[str, dict] = { + "bash": { + "name": "bash", + "description": "Execute a bash command on the system. Use for running scripts, system commands, git, etc.", + "parameters": { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "The bash command to execute" + }, + "working_dir": { + "type": "string", + "description": "Optional working directory" + } + }, + "required": ["command"] + } + }, + + "read": { + "name": "read", + "description": "Read the contents of a file. Returns the text content.", + "parameters": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Path to the file to read" + }, + "encoding": { + "type": "string", + "description": "File encoding (default: utf-8)" + } + }, + "required": ["path"] + } + }, + + "write": { + "name": "write", + "description": "Write content to a file. Creates the file if it doesn't exist, overwrites if it does.", + "parameters": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Path to the file to write" + }, + "content": { + "type": "string", + "description": "Content to write to the file" + }, + "append": { + "type": "boolean", + "description": "If true, append instead of overwrite" + } + }, + "required": ["path", "content"] + } + }, + + "glob": { + "name": "glob", + "description": "Find files using glob patterns. E.g., '**/*.py' finds all Python files.", + "parameters": { + "type": "object", + "properties": { + "pattern": { + "type": "string", + "description": "Glob pattern to search for files" + }, + "base_dir": { + "type": "string", + "description": "Base directory for the search" + } + }, + "required": ["pattern"] + } + }, + + "grep": { + "name": "grep", + "description": "Search for text in files using regular expressions.", + "parameters": { + "type": "object", + "properties": { + "pattern": { + "type": "string", + "description": "Regex pattern to search for" + }, + "path": { + "type": "string", + "description": "File or directory to search in" + }, + "recursive": { + "type": "boolean", + "description": "Search recursively in subdirectories" + } + }, + "required": ["pattern", "path"] + } + }, + + "http_request": { + "name": "http_request", + "description": "Make an HTTP request. Useful for REST APIs.", + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "URL to request" + }, + "method": { + "type": "string", + "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"], + "description": "HTTP method" + }, + "headers": { + "type": "object", + "description": "Request headers" + }, + "body": { + "type": "string", + "description": "Request body (for POST, PUT, PATCH)" + }, + "timeout": { + "type": "number", + "description": "Request timeout in seconds" + } + }, + "required": ["url"] + } + }, + + "ssh": { + "name": "ssh", + "description": "Execute a command on a remote server via SSH.", + "parameters": { + "type": "object", + "properties": { + "host": { + "type": "string", + "description": "Server host or IP" + }, + "command": { + "type": "string", + "description": "Command to execute on the server" + }, + "user": { + "type": "string", + "description": "SSH user (default: root)" + }, + "key_path": { + "type": "string", + "description": "Path to SSH key" + }, + "port": { + "type": "integer", + "description": "SSH port (default: 22)" + } + }, + "required": ["host", "command"] + } + }, + + "list_dir": { + "name": "list_dir", + "description": "List the contents of a directory.", + "parameters": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Path to the directory" + }, + "recursive": { + "type": "boolean", + "description": "List recursively" + }, + "include_hidden": { + "type": "boolean", + "description": "Include hidden files" + } + }, + "required": ["path"] + } + }, +} + + +def get_tool_schema(tool_names: List[str], format: str = "openai") -> List[dict]: + """ + Get tool schemas in the specified format. + + Args: + tool_names: List of tool names + format: Output format (openai, anthropic, gemini) + + Returns: + List of tool definitions + """ + tools = [] + + for name in tool_names: + if name not in TOOL_DEFINITIONS: + continue + + tool_def = TOOL_DEFINITIONS[name] + + if format == "openai": + tools.append({ + "type": "function", + "function": { + "name": tool_def["name"], + "description": tool_def["description"], + "parameters": tool_def["parameters"] + } + }) + elif format == "anthropic": + tools.append({ + "name": tool_def["name"], + "description": tool_def["description"], + "input_schema": tool_def["parameters"] + }) + elif format == "gemini": + tools.append({ + "function_declarations": [{ + "name": tool_def["name"], + "description": tool_def["description"], + "parameters": tool_def["parameters"] + }] + }) + else: + tools.append(tool_def) + + return tools + + +def get_tools_prompt(tool_names: List[str]) -> str: + """ + Generate a prompt describing available tools. + + For models that don't support native function calling. + """ + if not tool_names: + return "" + + prompt = """## Available Tools + +You can use the following tools. To use them, respond with a JSON block like this: + +```tool +{ + "tool": "tool_name", + "params": { + "param1": "value1" + } +} +``` + +Tools: + +""" + + for name in tool_names: + if name not in TOOL_DEFINITIONS: + continue + + tool = TOOL_DEFINITIONS[name] + props = tool["parameters"]["properties"] + required = tool["parameters"].get("required", []) + + prompt += f"### {tool['name']}\n" + prompt += f"{tool['description']}\n" + prompt += "Parameters:\n" + + for prop_name, prop_def in props.items(): + req_marker = "(required)" if prop_name in required else "(optional)" + prompt += f" - {prop_name} {req_marker}: {prop_def.get('description', '')}\n" + + prompt += "\n" + + prompt += """ +After using a tool, you'll receive the result and can continue. +You can use multiple tools in sequence for complex tasks. +""" + + return prompt +``` + +--- + +## orchestrator/tools/executor.py + +```python +""" +Secure tool executor. + +Features: +- Path sandboxing +- Command validation +- Rate limiting +- Retry with backoff +- Security logging +""" + +import asyncio +import json +import re +import shlex +import time +import urllib.request +import urllib.error +from pathlib import Path +from dataclasses import dataclass, field +from typing import Optional, Callable, Dict, Any, List +from datetime import datetime + +from ..core import RateLimiter, retry_with_backoff, RetryConfig + + +@dataclass +class ToolResult: + """Result of tool execution.""" + tool: str + success: bool + output: str + error: Optional[str] = None + execution_time: float = 0.0 + timestamp: str = field(default_factory=lambda: datetime.now().isoformat()) + retries: int = 0 + + +class SecurityValidator: + """Security validator for tool operations.""" + + # Commands that should never be executed + DANGEROUS_COMMANDS = { + "rm -rf /", "rm -rf /*", ":(){ :|:& };:", + "dd if=", "mkfs", "fdisk", "> /dev/sd", + "chmod -R 777 /", "chown -R", "curl | sh", + "wget | sh", "curl | bash", "wget | bash", + } + + # Dangerous patterns + DANGEROUS_PATTERNS = [ + r"rm\s+-rf\s+/(?!\w)", # rm -rf / (but not /home) + r">\s*/dev/sd[a-z]", # Write to devices + r"mkfs\.", # Format disks + r"dd\s+if=.+of=/dev", # dd to devices + r"\|\s*(?:ba)?sh", # Piping to shell + r";\s*rm\s+-rf", # Command injection with rm + ] + + def __init__( + self, + working_dir: Path, + sandbox: bool = True, + allowed_commands: Optional[List[str]] = None + ): + self.working_dir = working_dir.resolve() + self.sandbox = sandbox + self.allowed_commands = allowed_commands + + def validate_path(self, path: str) -> tuple[bool, str]: + """ + Validate that a path is within the sandbox. + + Returns: + (is_valid, resolved_path_or_error) + """ + if not self.sandbox: + return True, str(Path(path).expanduser()) + + try: + resolved = Path(path).expanduser() + if not resolved.is_absolute(): + resolved = self.working_dir / resolved + resolved = resolved.resolve() + + # Check if within working_dir + try: + resolved.relative_to(self.working_dir) + return True, str(resolved) + except ValueError: + return False, f"Path outside sandbox: {path}" + except Exception as e: + return False, f"Invalid path: {e}" + + def validate_command(self, command: str) -> tuple[bool, str]: + """ + Validate that a command is safe. + + Returns: + (is_safe, error_if_unsafe) + """ + # Check exact dangerous commands + for dangerous in self.DANGEROUS_COMMANDS: + if dangerous in command: + return False, f"Dangerous command: {dangerous}" + + # Check dangerous patterns + for pattern in self.DANGEROUS_PATTERNS: + if re.search(pattern, command, re.IGNORECASE): + return False, f"Dangerous pattern: {pattern}" + + # Check whitelist if provided + if self.allowed_commands: + cmd_name = command.split()[0] if command.split() else "" + if cmd_name not in self.allowed_commands: + return False, f"Command not allowed: {cmd_name}" + + return True, "" + + def sanitize_for_shell(self, value: str) -> str: + """Escape a value for safe shell use.""" + return shlex.quote(value) + + +class ToolExecutor: + """ + Executes tools securely. + + Features: + - Path sandboxing + - Command validation + - Rate limiting + - Automatic retry + """ + + def __init__( + self, + working_dir: Optional[str] = None, + timeout: float = 60.0, + sandbox_paths: bool = True, + allowed_commands: Optional[List[str]] = None, + ssh_key_path: str = "~/.ssh/id_rsa", + ssh_strict_host_checking: bool = True, + rate_limit_per_minute: int = 60, + max_retries: int = 3, + retry_delay: float = 1.0, + ): + self.working_dir = Path(working_dir).resolve() if working_dir else Path.cwd() + self.timeout = timeout + self.ssh_key_path = Path(ssh_key_path).expanduser() + self.ssh_strict_host_checking = ssh_strict_host_checking + self.max_retries = max_retries + + # Security + self.validator = SecurityValidator( + self.working_dir, + sandbox=sandbox_paths, + allowed_commands=allowed_commands, + ) + + # Rate limiting + self.rate_limiter = RateLimiter(rate_limit_per_minute) + + # Retry config + self.retry_config = RetryConfig( + max_retries=max_retries, + base_delay=retry_delay, + ) + + # Tool registry + self._tools: Dict[str, Callable] = { + "bash": self._exec_bash, + "read": self._exec_read, + "write": self._exec_write, + "glob": self._exec_glob, + "grep": self._exec_grep, + "http_request": self._exec_http, + "ssh": self._exec_ssh, + "list_dir": self._exec_list_dir, + } + + @property + def available_tools(self) -> List[str]: + """List of available tool names.""" + return list(self._tools.keys()) + + async def execute(self, tool: str, params: dict) -> ToolResult: + """ + Execute a tool with rate limiting and retry. + """ + start_time = time.time() + + if tool not in self._tools: + return ToolResult( + tool=tool, + success=False, + output="", + error=f"Unknown tool: {tool}", + ) + + # Rate limiting + await self.rate_limiter.acquire() + + # Execute with retry + async def do_execute(): + return await self._tools[tool](params) + + try: + result, retries = await retry_with_backoff( + do_execute, + config=self.retry_config, + ) + result.retries = retries + result.execution_time = time.time() - start_time + return result + except Exception as e: + return ToolResult( + tool=tool, + success=False, + output="", + error=str(e), + execution_time=time.time() - start_time, + ) + + def parse_tool_calls(self, text: str) -> List[dict]: + """ + Parse tool calls from text. + + Supports formats: + - ```tool { ... } ``` + - ```json { "tool": ... } ``` + """ + calls = [] + + # Format: ```tool ... ``` + pattern1 = r'```tool\s*\n?(.*?)\n?```' + for match in re.findall(pattern1, text, re.DOTALL): + try: + data = json.loads(match.strip()) + if "tool" in data: + calls.append(data) + except json.JSONDecodeError: + continue + + # Format: ```json ... ``` with tool + pattern2 = r'```json\s*\n?(.*?)\n?```' + for match in re.findall(pattern2, text, re.DOTALL): + try: + data = json.loads(match.strip()) + if "tool" in data: + calls.append(data) + except json.JSONDecodeError: + continue + + return calls + + async def execute_from_text(self, text: str) -> List[ToolResult]: + """Execute all tools found in text.""" + calls = self.parse_tool_calls(text) + results = [] + + for call in calls: + tool = call.get("tool") + params = call.get("params", {}) + result = await self.execute(tool, params) + results.append(result) + + return results + + # ==================== TOOL IMPLEMENTATIONS ==================== + + async def _exec_bash(self, params: dict) -> ToolResult: + """Execute a bash command securely.""" + command = params.get("command", "") + working_dir = params.get("working_dir") + + if not command: + return ToolResult(tool="bash", success=False, output="", error="Empty command") + + # Validate command + is_safe, error = self.validator.validate_command(command) + if not is_safe: + return ToolResult(tool="bash", success=False, output="", error=error) + + # Determine working directory + cwd = self.working_dir + if working_dir: + is_valid, result = self.validator.validate_path(working_dir) + if is_valid: + cwd = Path(result) + + try: + process = await asyncio.create_subprocess_exec( + "/bin/bash", "-c", command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=str(cwd), + ) + + stdout, stderr = await asyncio.wait_for( + process.communicate(), + timeout=self.timeout, + ) + + return ToolResult( + tool="bash", + success=process.returncode == 0, + output=stdout.decode("utf-8", errors="replace"), + error=stderr.decode("utf-8", errors="replace") if process.returncode != 0 else None, + ) + except asyncio.TimeoutError: + return ToolResult( + tool="bash", + success=False, + output="", + error=f"Timeout after {self.timeout}s", + ) + + async def _exec_read(self, params: dict) -> ToolResult: + """Read a file within the sandbox.""" + path = params.get("path", "") + encoding = params.get("encoding", "utf-8") + + if not path: + return ToolResult(tool="read", success=False, output="", error="Empty path") + + is_valid, result = self.validator.validate_path(path) + if not is_valid: + return ToolResult(tool="read", success=False, output="", error=result) + + try: + content = Path(result).read_text(encoding=encoding) + return ToolResult(tool="read", success=True, output=content) + except FileNotFoundError: + return ToolResult(tool="read", success=False, output="", error=f"File not found: {path}") + except Exception as e: + return ToolResult(tool="read", success=False, output="", error=str(e)) + + async def _exec_write(self, params: dict) -> ToolResult: + """Write a file within the sandbox.""" + path = params.get("path", "") + content = params.get("content", "") + append = params.get("append", False) + + if not path: + return ToolResult(tool="write", success=False, output="", error="Empty path") + + is_valid, result = self.validator.validate_path(path) + if not is_valid: + return ToolResult(tool="write", success=False, output="", error=result) + + try: + file_path = Path(result) + file_path.parent.mkdir(parents=True, exist_ok=True) + + mode = "a" if append else "w" + with open(file_path, mode) as f: + f.write(content) + + return ToolResult( + tool="write", + success=True, + output=f"Wrote {len(content)} bytes to {file_path.name}", + ) + except Exception as e: + return ToolResult(tool="write", success=False, output="", error=str(e)) + + async def _exec_glob(self, params: dict) -> ToolResult: + """Find files by glob pattern.""" + pattern = params.get("pattern", "") + base_dir = params.get("base_dir") + + if not pattern: + return ToolResult(tool="glob", success=False, output="", error="Empty pattern") + + search_dir = self.working_dir + if base_dir: + is_valid, result = self.validator.validate_path(base_dir) + if is_valid: + search_dir = Path(result) + + try: + files = list(search_dir.glob(pattern)) + safe_files = [] + + for f in files[:100]: # Limit results + try: + f.relative_to(self.working_dir) + safe_files.append(str(f.relative_to(self.working_dir))) + except ValueError: + continue + + output = "\n".join(sorted(safe_files)) + return ToolResult( + tool="glob", + success=True, + output=f"Found {len(safe_files)} files:\n{output}", + ) + except Exception as e: + return ToolResult(tool="glob", success=False, output="", error=str(e)) + + async def _exec_grep(self, params: dict) -> ToolResult: + """Search text in files.""" + pattern = params.get("pattern", "") + path = params.get("path", "") + recursive = params.get("recursive", False) + + if not pattern or not path: + return ToolResult(tool="grep", success=False, output="", error="Pattern or path empty") + + is_valid, validated_path = self.validator.validate_path(path) + if not is_valid: + return ToolResult(tool="grep", success=False, output="", error=validated_path) + + try: + cmd = ["grep", "-n", "--color=never"] + if recursive: + cmd.append("-r") + cmd.extend([pattern, validated_path]) + + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=str(self.working_dir), + ) + + stdout, _ = await asyncio.wait_for( + process.communicate(), + timeout=self.timeout, + ) + + return ToolResult(tool="grep", success=True, output=stdout.decode()) + except Exception as e: + return ToolResult(tool="grep", success=False, output="", error=str(e)) + + async def _exec_http(self, params: dict) -> ToolResult: + """Make an HTTP request.""" + url = params.get("url", "") + method = params.get("method", "GET").upper() + headers = params.get("headers", {}) + body = params.get("body", "") + timeout = params.get("timeout", self.timeout) + + if not url: + return ToolResult(tool="http_request", success=False, output="", error="Empty URL") + + try: + data = body.encode() if body else None + req = urllib.request.Request(url, data=data, method=method) + + for key, value in headers.items(): + req.add_header(key, value) + + with urllib.request.urlopen(req, timeout=timeout) as response: + content = response.read().decode() + return ToolResult(tool="http_request", success=True, output=content) + + except urllib.error.HTTPError as e: + return ToolResult( + tool="http_request", + success=False, + output=e.read().decode() if e.fp else "", + error=f"HTTP {e.code}: {e.reason}", + ) + except Exception as e: + return ToolResult(tool="http_request", success=False, output="", error=str(e)) + + async def _exec_ssh(self, params: dict) -> ToolResult: + """Execute command via SSH.""" + host = params.get("host", "") + command = params.get("command", "") + user = params.get("user", "root") + key_path = params.get("key_path", str(self.ssh_key_path)) + port = params.get("port", 22) + + if not host or not command: + return ToolResult(tool="ssh", success=False, output="", error="Host or command empty") + + # Validate remote command too + is_safe, error = self.validator.validate_command(command) + if not is_safe: + return ToolResult(tool="ssh", success=False, output="", error=f"Unsafe remote command: {error}") + + try: + ssh_cmd = [ + "ssh", + "-o", f"StrictHostKeyChecking={'yes' if self.ssh_strict_host_checking else 'no'}", + "-o", "BatchMode=yes", + "-o", "ConnectTimeout=10", + "-i", key_path, + "-p", str(port), + f"{user}@{host}", + command, + ] + + process = await asyncio.create_subprocess_exec( + *ssh_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + stdout, stderr = await asyncio.wait_for( + process.communicate(), + timeout=self.timeout, + ) + + return ToolResult( + tool="ssh", + success=process.returncode == 0, + output=stdout.decode(), + error=stderr.decode() if process.returncode != 0 else None, + ) + except asyncio.TimeoutError: + return ToolResult( + tool="ssh", + success=False, + output="", + error=f"SSH timeout after {self.timeout}s", + ) + except Exception as e: + return ToolResult(tool="ssh", success=False, output="", error=str(e)) + + async def _exec_list_dir(self, params: dict) -> ToolResult: + """List directory contents.""" + path = params.get("path", ".") + recursive = params.get("recursive", False) + include_hidden = params.get("include_hidden", False) + + is_valid, validated_path = self.validator.validate_path(path) + if not is_valid: + return ToolResult(tool="list_dir", success=False, output="", error=validated_path) + + try: + dir_path = Path(validated_path) + + if not dir_path.exists(): + return ToolResult( + tool="list_dir", + success=False, + output="", + error=f"Directory not found: {path}", + ) + + entries = [] + pattern = "**/*" if recursive else "*" + + for entry in dir_path.glob(pattern): + if not include_hidden and entry.name.startswith("."): + continue + + try: + entry.relative_to(self.working_dir) + except ValueError: + continue + + entry_type = "d" if entry.is_dir() else "f" + rel_path = entry.relative_to(dir_path) + entries.append(f"[{entry_type}] {rel_path}") + + return ToolResult( + tool="list_dir", + success=True, + output="\n".join(sorted(entries)[:200]), + ) + except Exception as e: + return ToolResult(tool="list_dir", success=False, output="", error=str(e)) +``` + +--- + +## orchestrator/agents/\_\_init\_\_.py + +```python +""" +Agent module. +""" + +from .base import Agent, AgentResult + +__all__ = ["Agent", "AgentResult"] +``` + +--- + +## orchestrator/agents/base.py + +```python +""" +Agent implementation. + +An Agent combines: +- A provider (Claude, LiteLLM, etc.) +- Tools +- A role/system prompt +- Logging +""" + +import asyncio +from datetime import datetime +from pathlib import Path +from typing import Optional, List, Dict, Any +from dataclasses import dataclass, field + +from ..config import get_config, AgentConfig +from ..providers import ClaudeProvider, LiteLLMProvider, ProviderResponse +from ..tools import ToolExecutor + + +@dataclass +class AgentResult: + """Result of an agent execution.""" + success: bool + output: str + agent_name: str + timestamp: str = field(default_factory=lambda: datetime.now().isoformat()) + error: Optional[str] = None + tool_results: List[Dict] = field(default_factory=list) + usage: Optional[Dict] = None + retries: int = 0 + + +class Agent: + """ + Configurable AI agent. + + Combines a provider, tools, and a role to create + a specialized AI assistant. + """ + + def __init__( + self, + name: str = "", + role: str = "", + provider: str = "claude", + model: str = "sonnet", + tools: Optional[List[str]] = None, + servers: Optional[List[str]] = None, + max_turns: int = 10, + config_obj: Optional[AgentConfig] = None, + ): + """ + Initialize agent from parameters or config object. + """ + if config_obj: + self.name = config_obj.name + self.role = config_obj.role + self.provider_name = config_obj.provider + self.model = config_obj.model + self.tools = config_obj.tools or [] + self.servers = config_obj.servers or [] + self.max_turns = config_obj.max_turns + else: + self.name = name + self.role = role + self.provider_name = provider + self.model = model + self.tools = tools or [] + self.servers = servers or [] + self.max_turns = max_turns + + self.config = get_config() + + # Paths + self.log_file = self.config.logs_dir / f"{self.name}.md" + self.output_dir = self.config.outputs_dir / self.name + + # Create provider + self.provider = self._create_provider() + + # Create tool executor for non-native tools + self.tool_executor = self._create_tool_executor() + + # Build system prompt + self.system_prompt = self._build_system_prompt() + + # Initialize + self.output_dir.mkdir(parents=True, exist_ok=True) + if not self.log_file.exists(): + self._init_log() + + def _create_provider(self): + """Create the appropriate provider.""" + settings = self.config.settings + + common_kwargs = { + "model": self.model, + "timeout": settings.timeout, + "max_retries": settings.max_retries, + "rate_limit_per_minute": settings.rate_limit_per_minute, + "enable_circuit_breaker": settings.enable_circuit_breaker, + "circuit_breaker_threshold": settings.circuit_breaker_threshold, + "circuit_breaker_timeout": settings.circuit_breaker_timeout, + } + + if self.provider_name == "claude": + return ClaudeProvider( + max_turns=self.max_turns, + working_directory=str(self.config.base_dir), + **common_kwargs, + ) + else: + return LiteLLMProvider( + max_tool_iterations=self.max_turns, + **common_kwargs, + ) + + def _create_tool_executor(self) -> ToolExecutor: + """Create tool executor for non-native tools.""" + settings = self.config.settings + + return ToolExecutor( + working_dir=str(self.config.base_dir), + timeout=settings.timeout, + sandbox_paths=settings.sandbox_paths, + allowed_commands=settings.allowed_commands or None, + ssh_strict_host_checking=settings.ssh_strict_host_checking, + rate_limit_per_minute=settings.rate_limit_per_minute, + max_retries=settings.max_retries, + ) + + def _build_system_prompt(self) -> str: + """Build the system prompt including role and server info.""" + prompt = f"# {self.name}\n\n{self.role}" + + if self.servers: + prompt += "\n\n## Available Servers\n" + for server_name in self.servers: + server = self.config.get_server(server_name) + if server: + prompt += f"- **{server_name}**: {server.user}@{server.host}" + if server.description: + prompt += f" ({server.description})" + prompt += "\n" + + return prompt + + def _init_log(self): + """Initialize the log file.""" + header = f"""# {self.name} + +- **Created:** {datetime.now().isoformat()} +- **Provider:** {self.provider_name} +- **Model:** {self.model} +- **Tools:** {', '.join(self.tools) if self.tools else 'none'} + +--- + +""" + self.log_file.write_text(header) + + def log(self, entry_type: str, content: str): + """Add entry to log file.""" + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + entry = f"\n## {timestamp} | {entry_type}\n\n{content}\n" + + with open(self.log_file, "a") as f: + f.write(entry) + + def _get_non_native_tools(self) -> List[str]: + """Get tools that need manual execution (not native to provider).""" + if self.provider_name == "claude": + # Claude CLI handles: bash, read, write, edit, glob, grep, webfetch + native = {"bash", "read", "write", "edit", "glob", "grep", "webfetch", "web_fetch"} + return [t for t in self.tools if t.lower() not in native] + else: + # LiteLLM doesn't have native tools + return self.tools + + async def run(self, prompt: str, log_action: bool = True) -> AgentResult: + """ + Execute a prompt through this agent. + """ + if log_action: + self.log("PROMPT", prompt[:500] + "..." if len(prompt) > 500 else prompt) + + try: + # Determine which tools need manual handling + non_native_tools = self._get_non_native_tools() + + # For Claude provider, pass all tools (it handles native ones) + # For LiteLLM, we need to handle tools ourselves + if self.provider_name == "claude": + response = await self.provider.run_with_tools( + prompt=prompt, + tools=self.tools, + system_prompt=self.system_prompt, + ) + else: + # LiteLLM with tool execution + response = await self.provider.run_with_tools( + prompt=prompt, + tools=self.tools, + system_prompt=self.system_prompt, + tool_executor=self.tool_executor, + ) + + # Build result + result = AgentResult( + success=response.success, + output=response.text, + agent_name=self.name, + error=response.error, + tool_results=[ + { + "tool": tr.tool, + "success": tr.success, + "output": tr.output[:200] if tr.output else "", + "error": tr.error, + } + for tr in response.tool_results + ], + usage=response.usage.to_dict() if response.usage else None, + retries=response.retries, + ) + + if log_action: + status = "RESULT" if response.success else "ERROR" + content = response.text[:500] if response.success else (response.error or "Unknown error") + self.log(status, content) + + return result + + except Exception as e: + if log_action: + self.log("ERROR", str(e)) + + return AgentResult( + success=False, + output="", + agent_name=self.name, + error=str(e), + ) + + async def run_with_context( + self, + prompt: str, + context: str, + log_action: bool = True, + ) -> AgentResult: + """ + Execute a prompt with additional context. + """ + full_prompt = f"{context}\n\n---\n\n{prompt}" + return await self.run(full_prompt, log_action) + + def save_output(self, filename: str, content: str) -> Path: + """Save content to the agent's output directory.""" + filepath = self.output_dir / filename + filepath.parent.mkdir(parents=True, exist_ok=True) + filepath.write_text(content) + self.log("FILE", f"Created: {filepath}") + return filepath + + def read_log(self, last_n: Optional[int] = None) -> str: + """Read the agent's log file.""" + if not self.log_file.exists(): + return "" + + content = self.log_file.read_text() + + if last_n: + entries = content.split("\n## ") + return "\n## ".join(entries[-last_n:]) + + return content + + async def health_check(self) -> bool: + """Check if the agent's provider is healthy.""" + return await self.provider.health_check() + + def __repr__(self) -> str: + return f"Agent({self.name}, {self.provider_name}/{self.model})" +``` + +--- + +## orchestrator/tracking/\_\_init\_\_.py + +```python +""" +Cost tracking module. + +Track token usage and costs across providers. +""" + +from dataclasses import dataclass, field +from datetime import datetime +from typing import Dict, List, Optional +import json + + +# Cost per million tokens (approximate, may vary) +COST_PER_MILLION = { + # Claude models + "claude-opus-4-20250514": {"input": 15.0, "output": 75.0}, + "claude-sonnet-4-20250514": {"input": 3.0, "output": 15.0}, + "claude-3-5-haiku-latest": {"input": 0.25, "output": 1.25}, + + # OpenAI models + "gpt-4o": {"input": 2.5, "output": 10.0}, + "gpt-4o-mini": {"input": 0.15, "output": 0.6}, + "gpt-4-turbo": {"input": 10.0, "output": 30.0}, + "o1-preview": {"input": 15.0, "output": 60.0}, + "o1-mini": {"input": 3.0, "output": 12.0}, + + # Google models + "gemini/gemini-1.5-pro": {"input": 1.25, "output": 5.0}, + "gemini/gemini-1.5-flash": {"input": 0.075, "output": 0.3}, + + # Default for unknown models + "default": {"input": 1.0, "output": 3.0}, +} + + +@dataclass +class RequestCost: + """Cost information for a single request.""" + model: str + input_tokens: int + output_tokens: int + cache_read_tokens: int = 0 + cache_creation_tokens: int = 0 + timestamp: str = field(default_factory=lambda: datetime.now().isoformat()) + + @property + def total_tokens(self) -> int: + return self.input_tokens + self.output_tokens + + @property + def input_cost(self) -> float: + """Calculate input cost in USD.""" + rates = COST_PER_MILLION.get(self.model, COST_PER_MILLION["default"]) + # Cache reads are typically 90% cheaper + regular_input = self.input_tokens - self.cache_read_tokens + cache_cost = (self.cache_read_tokens / 1_000_000) * rates["input"] * 0.1 + regular_cost = (regular_input / 1_000_000) * rates["input"] + return regular_cost + cache_cost + + @property + def output_cost(self) -> float: + """Calculate output cost in USD.""" + rates = COST_PER_MILLION.get(self.model, COST_PER_MILLION["default"]) + return (self.output_tokens / 1_000_000) * rates["output"] + + @property + def total_cost(self) -> float: + """Total cost in USD.""" + return self.input_cost + self.output_cost + + +@dataclass +class CostSummary: + """Summary of costs over multiple requests.""" + total_requests: int = 0 + total_input_tokens: int = 0 + total_output_tokens: int = 0 + total_cache_read_tokens: int = 0 + total_cost_usd: float = 0.0 + by_model: Dict[str, Dict] = field(default_factory=dict) + + def to_dict(self) -> dict: + return { + "total_requests": self.total_requests, + "total_input_tokens": self.total_input_tokens, + "total_output_tokens": self.total_output_tokens, + "total_tokens": self.total_input_tokens + self.total_output_tokens, + "total_cache_read_tokens": self.total_cache_read_tokens, + "total_cost_usd": round(self.total_cost_usd, 6), + "by_model": self.by_model, + } + + +class CostTracker: + """ + Track costs across multiple requests. + + Usage: + tracker = CostTracker(budget_limit=10.0) + + # Record usage + tracker.record(model="claude-sonnet-4-20250514", input_tokens=1000, output_tokens=500) + + # Check status + print(tracker.summary) + print(tracker.remaining_budget) + """ + + def __init__( + self, + budget_limit: Optional[float] = None, + on_budget_warning: Optional[callable] = None, + warning_threshold: float = 0.8, + ): + """ + Args: + budget_limit: Optional budget limit in USD + on_budget_warning: Callback when approaching budget + warning_threshold: Fraction of budget to trigger warning (0.8 = 80%) + """ + self.budget_limit = budget_limit + self.on_budget_warning = on_budget_warning + self.warning_threshold = warning_threshold + + self._requests: List[RequestCost] = [] + self._warning_sent = False + + def record( + self, + model: str, + input_tokens: int, + output_tokens: int, + cache_read_tokens: int = 0, + cache_creation_tokens: int = 0, + ) -> RequestCost: + """Record a request's token usage.""" + cost = RequestCost( + model=model, + input_tokens=input_tokens, + output_tokens=output_tokens, + cache_read_tokens=cache_read_tokens, + cache_creation_tokens=cache_creation_tokens, + ) + + self._requests.append(cost) + + # Check budget + if self.budget_limit and not self._warning_sent: + if self.total_cost >= self.budget_limit * self.warning_threshold: + self._warning_sent = True + if self.on_budget_warning: + self.on_budget_warning(self.total_cost, self.budget_limit) + + return cost + + def record_from_response(self, response) -> Optional[RequestCost]: + """Record from a ProviderResponse object.""" + if not response.usage: + return None + + return self.record( + model=response.model, + input_tokens=response.usage.input_tokens, + output_tokens=response.usage.output_tokens, + cache_read_tokens=getattr(response.usage, 'cache_read_tokens', 0), + cache_creation_tokens=getattr(response.usage, 'cache_creation_tokens', 0), + ) + + @property + def total_cost(self) -> float: + """Total cost across all requests.""" + return sum(r.total_cost for r in self._requests) + + @property + def total_tokens(self) -> int: + """Total tokens across all requests.""" + return sum(r.total_tokens for r in self._requests) + + @property + def remaining_budget(self) -> Optional[float]: + """Remaining budget in USD, or None if no limit.""" + if self.budget_limit is None: + return None + return max(0, self.budget_limit - self.total_cost) + + @property + def is_over_budget(self) -> bool: + """Check if over budget.""" + if self.budget_limit is None: + return False + return self.total_cost >= self.budget_limit + + @property + def summary(self) -> CostSummary: + """Get cost summary.""" + summary = CostSummary() + + for request in self._requests: + summary.total_requests += 1 + summary.total_input_tokens += request.input_tokens + summary.total_output_tokens += request.output_tokens + summary.total_cache_read_tokens += request.cache_read_tokens + summary.total_cost_usd += request.total_cost + + # By model + if request.model not in summary.by_model: + summary.by_model[request.model] = { + "requests": 0, + "input_tokens": 0, + "output_tokens": 0, + "cost_usd": 0.0, + } + + summary.by_model[request.model]["requests"] += 1 + summary.by_model[request.model]["input_tokens"] += request.input_tokens + summary.by_model[request.model]["output_tokens"] += request.output_tokens + summary.by_model[request.model]["cost_usd"] += request.total_cost + + return summary + + def reset(self): + """Reset all tracked costs.""" + self._requests = [] + self._warning_sent = False + + def export(self) -> str: + """Export cost data as JSON.""" + return json.dumps({ + "requests": [ + { + "model": r.model, + "input_tokens": r.input_tokens, + "output_tokens": r.output_tokens, + "cache_read_tokens": r.cache_read_tokens, + "total_cost": r.total_cost, + "timestamp": r.timestamp, + } + for r in self._requests + ], + "summary": self.summary.to_dict(), + "budget_limit": self.budget_limit, + }, indent=2) +``` + +--- + +## Ejemplos de Configuración + +### examples/simple.yaml + +```yaml +# Simple single agent configuration + +settings: + default_provider: claude + default_model: sonnet + +agents: + assistant: + role: "You are a helpful assistant." + provider: claude + model: sonnet + tools: + - bash + - read + - write +``` + +### examples/dev_team.yaml + +```yaml +# Software development team + +settings: + default_provider: claude + default_model: sonnet + timeout: 300 + sandbox_paths: true + +agents: + architect: + role: | + You are a senior software architect. + You design scalable and maintainable systems. + You make important technical decisions. + You document decisions in ADRs (Architecture Decision Records). + provider: claude + model: opus + tools: + - read + - write + - list_dir + - glob + + developer: + role: | + You are an experienced full-stack developer. + You write clean, well-documented, and testable code. + You follow language best practices. + You always include appropriate error handling. + provider: claude + model: sonnet + tools: + - read + - write + - bash + - grep + - glob + + reviewer: + role: | + You are a demanding but constructive code reviewer. + You look for bugs, security issues, and improvements. + You suggest refactoring when needed. + You validate that code follows standards. + provider: litellm + model: gpt4o + tools: + - read + - grep + - glob + + tester: + role: | + You are a QA engineer specialized in testing. + You write unit, integration, and e2e tests. + You identify edge cases and error scenarios. + You ensure good test coverage. + provider: litellm + model: gemini-pro + tools: + - read + - write + - bash +``` + +### examples/devops.yaml + +```yaml +# DevOps team with server access + +settings: + default_provider: claude + default_model: sonnet + timeout: 300 + ssh_strict_host_checking: true + +servers: + production: + host: prod.example.com + user: deploy + key: ~/.ssh/deploy_key + description: "Production server" + + staging: + host: staging.example.com + user: deploy + key: ~/.ssh/deploy_key + description: "Staging server" + +agents: + deployer: + role: | + You are a deployment specialist. + You deploy applications safely to production. + You always verify deployments succeed. + You rollback immediately if issues are detected. + provider: claude + model: sonnet + tools: + - ssh + - bash + - read + servers: + - production + - staging + + monitor: + role: | + You are a systems monitoring expert. + You check server health and metrics. + You identify issues before they become problems. + provider: claude + model: haiku + tools: + - ssh + - http_request + - read + servers: + - production + - staging +``` + +### examples/local_ollama.yaml + +```yaml +# Using local Ollama models (no API costs!) +# +# Requirements: +# 1. Install Ollama: https://ollama.ai +# 2. Pull models: ollama pull llama3 +# 3. pip install litellm + +settings: + default_provider: litellm + default_model: llama3 + timeout: 120 + max_tool_iterations: 15 + +agents: + assistant: + role: | + You are a helpful assistant running locally. + You can execute commands and work with files. + provider: litellm + model: llama3 + tools: + - bash + - read + - write + - list_dir + + coder: + role: | + You are a coding assistant specialized in programming. + You write clean, efficient code. + provider: litellm + model: codellama + tools: + - read + - write + - bash + - grep +``` + +--- + +## .env.example + +```bash +# .env.example - Copy to .env and fill in your values + +# API Keys +OPENAI_API_KEY=sk-... +GOOGLE_API_KEY=... +ANTHROPIC_API_KEY=... +MISTRAL_API_KEY=... +GROQ_API_KEY=... + +# SSH Configuration +SSH_KEY_PATH=~/.ssh/id_rsa +SSH_KNOWN_HOSTS_CHECK=true +``` + +--- + +## .gitignore + +``` +# Environment +.env +.venv/ +venv/ + +# Python +__pycache__/ +*.py[cod] +*.egg-info/ + +# IDE +.idea/ +.vscode/ + +# Logs and outputs +logs/*.md +!logs/.gitkeep +outputs/** +!outputs/.gitkeep + +# Secrets +*.pem +*.key +``` + +--- + +## Uso Rápido + +```bash +# 1. Crear estructura de directorios +mkdir -p tzzr_v6/orchestrator/{core,providers,tools,agents,tracking} +mkdir -p tzzr_v6/{logs,outputs,examples} + +# 2. Copiar cada archivo del código de arriba a su ubicación correspondiente + +# 3. Instalar dependencias +pip install pyyaml +pip install litellm # opcional, para GPT-4/Gemini/etc + +# 4. Ejecutar +cd tzzr_v6 +python main.py +``` diff --git a/02_MODELO_DATOS/entidades.md b/02_MODELO_DATOS/entidades.md deleted file mode 100644 index eeee58c..0000000 --- a/02_MODELO_DATOS/entidades.md +++ /dev/null @@ -1,237 +0,0 @@ -# Entidades Base TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Visión General - -Las entidades base son los bloques fundamentales del sistema. Cada una tiene un hash único SHA-256 que la identifica de forma unívoca. - -``` -DEFINICIÓN ORIGINAL → SHA-256 → h_{tipo} (64 chars) -``` - ---- - -## HST (Hash Semantic Tagging) - -**Estado:** Implementado -**Servidor:** HST (72.62.2.84) -**Hash:** h_maestro - -### Fórmula - -``` -h_maestro = SHA-256(grupo:ref) -``` - -### Grupos - -| Grupo | Cantidad | Descripción | -|-------|----------|-------------| -| hst | 639 | Tags del sistema base | -| spe | 145 | Tags específicos | -| vsn | 84 | Visiones | -| flg | 65 | Banderas/países | -| vue | 21 | Vistas | - -### Schema - -```sql -CREATE TABLE hst_tags ( - id SERIAL PRIMARY KEY, - ref VARCHAR(100) UNIQUE NOT NULL, - h_maestro VARCHAR(64) UNIQUE NOT NULL, - grupo VARCHAR(50) NOT NULL, - nombre_es VARCHAR(255), - nombre_en VARCHAR(255), - descripcion TEXT, - imagen_url TEXT, - activo BOOLEAN DEFAULT true, - version INTEGER DEFAULT 1, - created_at TIMESTAMP DEFAULT NOW() -); -``` - -### Ejemplo - -```json -{ - "ref": "person", - "h_maestro": "a1b2c3d4e5f6...", - "grupo": "hst", - "nombre_es": "Persona", - "nombre_en": "Person", - "imagen_url": "https://tzrtech.org/a1b2c3d4e5f6...png" -} -``` - ---- - -## ITM (Items - Plano Ideal) - -**Estado:** Planificado -**Servidor:** Por definir -**Hash:** h_item - -### Concepto - -Los ITM representan el estado ideal futuro. Son la "partitura" que guía las acciones. - -### Schema Propuesto - -```sql -CREATE TABLE items ( - id BIGSERIAL PRIMARY KEY, - h_item VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - secuencia BIGINT NOT NULL, - hash_previo VARCHAR(64), - hash_contenido VARCHAR(64), - tipo_item VARCHAR(50) NOT NULL, -- accion_ideal, objetivo, requerimiento - titulo VARCHAR(255) NOT NULL, - descripcion TEXT, - criterios_aceptacion JSONB, - metadata JSONB, - proyecto_tag VARCHAR(64), - etiquetas JSONB, - estado VARCHAR(20) DEFAULT 'draft', -- draft, vigente, obsoleto - created_at TIMESTAMPTZ DEFAULT NOW(), - created_by VARCHAR(64), - UNIQUE (h_instancia, secuencia) -); -``` - ---- - -## PLY (Players - Identidad) - -**Estado:** Planificado -**Servidor:** Por definir -**Hash:** h_player - -### Concepto - -Los PLY representan la identidad de actores en el sistema (personas, empresas, agentes). - -### Schema Propuesto - -```sql -CREATE TABLE players ( - id BIGSERIAL PRIMARY KEY, - h_player VARCHAR(64) UNIQUE NOT NULL, - tipo VARCHAR(50) NOT NULL, -- persona, empresa, agente - nombre VARCHAR(255) NOT NULL, - email VARCHAR(255), - metadata JSONB, - activo BOOLEAN DEFAULT true, - created_at TIMESTAMPTZ DEFAULT NOW() -); -``` - ---- - -## LOC (Locations - Ubicaciones) - -**Estado:** Planificado -**Servidor:** Por definir -**Hash:** h_loc - -### Concepto - -Los LOC representan ubicaciones geográficas con precisión variable. - -### Schema Propuesto - -```sql -CREATE TABLE locations ( - id BIGSERIAL PRIMARY KEY, - h_loc VARCHAR(64) UNIQUE NOT NULL, - nombre VARCHAR(255), - lat DECIMAL(10, 8), - lon DECIMAL(11, 8), - precision_metros INTEGER, - tipo VARCHAR(50), -- punto, area, ruta - metadata JSONB, - created_at TIMESTAMPTZ DEFAULT NOW() -); -``` - -### Fórmula Hash - -``` -h_loc = SHA-256(lat:lon:precision) -``` - ---- - -## FLG (Flags - Marcos Jurídicos) - -**Estado:** Planificado -**Servidor:** Por definir -**Hash:** h_flag - -### Concepto - -Los FLG representan marcos jurídicos, normativas, países. - -### Notas - -Actualmente existe un grupo `flg` en HST con 65 tags de países, pero no como entidad independiente. - ---- - -## Tags de Usuario (Extensiones) - -Los usuarios pueden crear sus propios tags como extensiones de HST. - -| Tabla | Descripción | -|-------|-------------| -| hsu | HST Usuario | -| spu | SPE Usuario | -| vsu | VSN Usuario | -| vuu | VUE Usuario | -| pju | Proyectos Usuario | -| flu | FLG Usuario | - -### Schema - -```sql -CREATE TABLE hsu ( - id SERIAL PRIMARY KEY, - ref VARCHAR(10) NOT NULL, - h_usuario VARCHAR(64) NOT NULL, - nombre VARCHAR(255) NOT NULL, - user_id INTEGER REFERENCES users(id), - metadata JSONB, - activo BOOLEAN DEFAULT true, - created_at TIMESTAMP DEFAULT NOW(), - UNIQUE (user_id, ref) -); -``` - ---- - -## Relaciones Entre Entidades - -``` -ITM (ideal) - │ - ├──► h_maestro (HST tags) - ├──► h_player (PLY responsable) - ├──► h_loc (LOC ubicación) - └──► h_flag (FLG normativa) - -MST (milestone) - │ - ├──► item_asociado (ITM) - └──► h_maestro (HST tags) - -BCK (bloque) - │ - ├──► item_asociado (ITM) - ├──► milestone_asociado (MST) - └──► h_maestro (HST tags) -``` diff --git a/02_MODELO_DATOS/planos.md b/02_MODELO_DATOS/planos.md deleted file mode 100644 index 0f90073..0000000 --- a/02_MODELO_DATOS/planos.md +++ /dev/null @@ -1,309 +0,0 @@ -# Planos de Datos TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Visión General - -El sistema TZZR utiliza tres planos conceptuales para organizar la información: - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ T0 - PLANO IDEAL │ -│ ITM (Items): Objetivos, acciones ideales, requerimientos │ -│ Estado: PLANIFICADO - Sin implementación actual │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ materializa -┌─────────────────────────────────────────────────────────────────┐ -│ MST - MILESTONES │ -│ Compromisos futuros con fecha de vencimiento │ -│ Estado: IMPLEMENTADO en CORP (tabla milestones) │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ cumple/genera -┌─────────────────────────────────────────────────────────────────┐ -│ BCK - BLOQUES │ -│ Hechos inmutables del pasado (blockchain-ready) │ -│ Estado: IMPLEMENTADO en CORP (tabla bloques) │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## T0 - Plano Ideal (ITM) - -### Concepto - -El plano T0 contiene los Items (ITM) que representan el estado ideal futuro. Son la "partitura" que guía las acciones. - -### Tipos de Items - -| Tipo | Descripción | -|------|-------------| -| accion_ideal | Acción que debería ejecutarse | -| objetivo | Meta a alcanzar | -| requerimiento | Requisito a cumplir | - -### Estado de Implementación - -**PLANIFICADO** - Schema definido pero sin tablas creadas. - -### Schema Propuesto - -```sql -CREATE TABLE items ( - id BIGSERIAL PRIMARY KEY, - h_item VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - secuencia BIGINT NOT NULL, - hash_previo VARCHAR(64), - hash_contenido VARCHAR(64), - tipo_item VARCHAR(50) NOT NULL, - titulo VARCHAR(255) NOT NULL, - descripcion TEXT, - criterios_aceptacion JSONB, - metadata JSONB, - proyecto_tag VARCHAR(64), - etiquetas JSONB, - estado VARCHAR(20) DEFAULT 'draft', - created_at TIMESTAMPTZ DEFAULT NOW(), - created_by VARCHAR(64), - UNIQUE (h_instancia, secuencia) -); -``` - ---- - -## MST - Milestones - -### Concepto - -Los milestones representan compromisos con fecha futura. Tienen un período flotante de 24 horas antes de consolidarse. - -### Estado de Implementación - -**IMPLEMENTADO** en CORP (database: corp). - -### Schema Actual - -```sql --- Tabla: milestones (CORP) -CREATE TABLE milestones ( - id BIGSERIAL PRIMARY KEY, - h_milestone VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - secuencia BIGINT NOT NULL, - hash_previo VARCHAR(64), - hash_contenido VARCHAR(64), - tipo_milestone VARCHAR(50) NOT NULL, - fecha_vencimiento DATE NOT NULL, - descripcion TEXT, - metadata JSONB, - proyecto_tag VARCHAR(64), - etiquetas JSONB, - estado VARCHAR(20) DEFAULT 'draft', - created_at TIMESTAMPTZ DEFAULT NOW(), - modified_at TIMESTAMPTZ, - created_by VARCHAR(64), - UNIQUE (h_instancia, secuencia) -); -``` - -### Tipos de Milestones - -| Tipo | Descripción | -|------|-------------| -| compromiso | Compromiso de entrega | -| deadline | Fecha límite | -| evento | Evento programado | -| recordatorio | Recordatorio futuro | - -### Fórmula Hash - -``` -h_milestone = SHA-256(h_instancia:secuencia:tipo:contenido) -``` - -### Período Flotante - -- **Duración:** 24 horas desde creación -- **Durante período:** Modificable vía MASON -- **Después:** Inmutable, solo lectura - -**Nota:** Actualmente no hay scheduler que procese la expiración automáticamente. - ---- - -## BCK - Bloques - -### Concepto - -Los bloques son registros inmutables de hechos pasados. Cada bloque está encadenado al anterior mediante hash, preparando la infraestructura para blockchain. - -### Estado de Implementación - -**IMPLEMENTADO** en CORP (database: corp). - -### Schema Actual - -```sql --- Tabla: bloques (CORP) -CREATE TABLE bloques ( - id BIGSERIAL PRIMARY KEY, - h_bloque VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - secuencia BIGINT NOT NULL, - hash_previo VARCHAR(64), - hash_contenido VARCHAR(64) NOT NULL, - tipo_bloque VARCHAR(50) NOT NULL, - contenido JSONB NOT NULL, - metadata JSONB, - item_asociado VARCHAR(64), - milestone_asociado VARCHAR(64), - h_maestro JSONB, - h_player VARCHAR(64), - h_loc VARCHAR(64), - h_flag VARCHAR(64), - estado VARCHAR(20) DEFAULT 'consolidado', - created_at TIMESTAMPTZ DEFAULT NOW(), - validated_at TIMESTAMPTZ, - validated_by VARCHAR(64), - UNIQUE (h_instancia, secuencia) -); -``` - -### Tipos de Bloques - -| Tipo | Descripción | -|------|-------------| -| transaccion | Transacción económica | -| documento | Documento consolidado | -| evento | Evento registrado | -| milestone_cumplido | Milestone que se cumplió | - -### Fórmula Hash - -``` -h_bloque = SHA-256(h_instancia:secuencia:hash_previo:hash_contenido) -hash_contenido = SHA-256(contenido_serializado) -``` - -### Encadenamiento - -``` -Bloque N-1 Bloque N -┌──────────────────┐ ┌──────────────────┐ -│ h_bloque: abc123 │ ──────► │ hash_previo: │ -│ hash_contenido: │ │ abc123 │ -│ def456 │ │ h_bloque: ghi789 │ -│ secuencia: 1 │ │ secuencia: 2 │ -└──────────────────┘ └──────────────────┘ -``` - ---- - -## Flujo Entre Planos - -``` -Usuario crea ITM - │ - ▼ -ITM (T0) "Objetivo: Completar proyecto X" - │ - │ materializa - ▼ -MST "Milestone: Entrega módulo 1 - 2024-01-15" - │ - │ [período flotante 24h] - │ - ▼ -MASON (enriquecimiento) - │ - ▼ -FELDMAN (validación) - │ - │ cumple/genera - ▼ -BCK "Bloque: Módulo 1 entregado - 2024-01-14" - │ - ▼ -Inmutable ✓ -``` - ---- - -## Tablas de Procesamiento - -### FELDMAN Cola - -```sql --- feldman_cola: Elementos pendientes de validación -CREATE TABLE feldman_cola ( - id BIGSERIAL PRIMARY KEY, - tipo VARCHAR(50) NOT NULL, -- 'milestone' o 'bloque' - h_entrada VARCHAR(64) NOT NULL, - contenido JSONB NOT NULL, - prioridad INTEGER DEFAULT 0, - estado VARCHAR(20) DEFAULT 'pendiente', - created_at TIMESTAMPTZ DEFAULT NOW(), - processed_at TIMESTAMPTZ -); -``` - -### FELDMAN Validaciones - -```sql --- feldman_validaciones: Auditoría de validaciones -CREATE TABLE feldman_validaciones ( - id BIGSERIAL PRIMARY KEY, - h_entrada VARCHAR(64) NOT NULL, - regla VARCHAR(50) NOT NULL, -- M-001, M-002, M-003 - resultado BOOLEAN NOT NULL, - mensaje TEXT, - validated_at TIMESTAMPTZ DEFAULT NOW() -); -``` - ---- - -## Reglas de Validación FELDMAN - -| Regla | Nombre | Descripción | -|-------|--------|-------------| -| M-001 | Hash único | h_bloque/h_milestone no existe previamente | -| M-002 | Encadenamiento | hash_previo apunta a bloque existente | -| M-003 | Integridad | hash_contenido = SHA-256(contenido) | - ---- - -## Decisiones de Diseño - -### D-004: Eliminar columnas duplicadas - -**Problema:** Milestones tienen columnas + JSONB duplicados. - -**Decisión:** Mantener metadatos solo en JSONB `metadata`. - -### D-007: Timestamps UTC - -**Problema:** Inconsistencia en zonas horarias. - -**Decisión:** Usar `TIMESTAMPTZ` y almacenar en UTC. - ---- - -## Roadmap - -### Implementado -- [x] Tabla milestones en CORP -- [x] Tabla bloques en CORP -- [x] Fórmulas hash definidas - -### Pendiente -- [ ] Tabla items (T0) -- [ ] Scheduler para período flotante -- [ ] Merkle tree para verificación -- [ ] Smart contract blockchain (futuro) diff --git a/03_COMPONENTES/agentes/clara-margaret.md b/03_COMPONENTES/agentes/clara-margaret.md deleted file mode 100644 index b32eb48..0000000 --- a/03_COMPONENTES/agentes/clara-margaret.md +++ /dev/null @@ -1,314 +0,0 @@ -# secretaria (CLARA / MARGARET) - -**Componente:** Ingesta -**Versión:** 5.0 -**Fecha:** 2024-12-24 - -> **Nota:** Este es un microservicio backend, no IA. - ---- - -## Resumen - -| Aspecto | secretaria (CLARA) | secretaria (MARGARET) | -|---------|-------|----------| -| Servidor | DECK (72.62.1.113) | CORP (92.112.181.188) | -| Puerto | 5051 | 5051 | -| Dominio | Personal | Empresarial | -| Base de datos | tzzr | corp | -| Tabla log | secretaria_log | secretaria_log | -| Bucket R2 | deck | corp | -| Estado | Operativo | Operativo | - ---- - -## Arquitectura - -``` -PACKET (App móvil) - │ - │ POST /ingest - │ Content-Type: multipart/form-data - │ X-Auth-Key: {h_instancia} - │ - ▼ -┌─────────────────────────────────────────┐ -│ secretaria (CLARA / MARGARET) │ -│ │ -│ 1. Validar h_instancia │ -│ 2. Calcular h_entrada (SHA-256) │ -│ 3. Subir archivos a R2 │ -│ 4. Insertar en log (inmutable) │ -│ 5. Estado: 'recibido' │ -│ 6. Responder con h_entrada │ -└─────────────────────────────────────────┘ - │ - ▼ - producción (ALFRED / JARED) -``` - ---- - -## Endpoint de Ingesta - -### Request - -```http -POST /ingest HTTP/1.1 -Host: deck.example.com:5051 -Content-Type: multipart/form-data -X-Auth-Key: {h_instancia} - ---boundary -Content-Disposition: form-data; name="tipo" - -documento ---boundary -Content-Disposition: form-data; name="archivo"; filename="doc.pdf" -Content-Type: application/pdf - -{binary data} ---boundary-- -``` - -### Response - -```json -{ - "success": true, - "h_entrada": "a1b2c3d4e5f6...", - "estado": "recibido", - "timestamp": "2024-12-24T12:00:00Z" -} -``` - ---- - -## Cálculo de h_entrada - -```python -import hashlib -import json - -def calcular_h_entrada(h_instancia, tipo, contenido_bytes, timestamp): - """ - Calcula el hash único de entrada. - - h_entrada = SHA-256(h_instancia:tipo:sha256(contenido):timestamp) - """ - hash_contenido = hashlib.sha256(contenido_bytes).hexdigest() - - data = f"{h_instancia}:{tipo}:{hash_contenido}:{timestamp}" - - return hashlib.sha256(data.encode()).hexdigest() -``` - ---- - -## Schema: clara_log / margaret_log - -```sql -CREATE TABLE clara_log ( - id BIGSERIAL PRIMARY KEY, - h_entrada VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - tipo VARCHAR(50) NOT NULL, - contenido_url TEXT, -- locker://deck/{h_entrada}/archivo - contenido_hash VARCHAR(64), -- SHA-256 del contenido - metadata JSONB, - estado VARCHAR(20) DEFAULT 'recibido', - created_at TIMESTAMPTZ DEFAULT NOW(), - processed_at TIMESTAMPTZ, - processed_by VARCHAR(50) -- 'alfred' o 'manual' -); - --- margaret_log tiene schema idéntico -CREATE TABLE margaret_log ( - id BIGSERIAL PRIMARY KEY, - h_entrada VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - tipo VARCHAR(50) NOT NULL, - contenido_url TEXT, - contenido_hash VARCHAR(64), - metadata JSONB, - estado VARCHAR(20) DEFAULT 'recibido', - created_at TIMESTAMPTZ DEFAULT NOW(), - processed_at TIMESTAMPTZ, - processed_by VARCHAR(50) -); -``` - ---- - -## Estados del Log - -| Estado | Descripción | -|--------|-------------| -| recibido | Ingesta completada, pendiente procesamiento | -| procesando | ALFRED/JARED está procesando | -| enviado_mason | Enviado a MASON para enriquecimiento | -| consolidado | Procesado por FELDMAN | -| error | Error en procesamiento | - ---- - -## Almacenamiento en R2 - -### Estructura de URLs - -``` -locker://deck/{h_entrada}/archivo.ext -locker://corp/{h_entrada}/archivo.ext -``` - -### Implementación - -```python -import boto3 - -def subir_a_r2(bucket, h_entrada, archivo_bytes, filename): - """Sube archivo a Cloudflare R2.""" - - s3 = boto3.client( - 's3', - endpoint_url='https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com', - aws_access_key_id=R2_ACCESS_KEY, - aws_secret_access_key=R2_SECRET_KEY - ) - - key = f"{h_entrada}/{filename}" - - s3.put_object( - Bucket=bucket, - Key=key, - Body=archivo_bytes - ) - - return f"locker://{bucket}/{key}" -``` - ---- - -## Validación de h_instancia - -```python -def validar_instancia(h_instancia): - """ - Valida que h_instancia exista y esté activa. - - TODO: Implementar tabla de instancias activas. - Actualmente: Validación básica de formato. - """ - if not h_instancia: - return False - - if len(h_instancia) != 64: - return False - - # TODO: Consultar tabla de instancias - return True -``` - ---- - -## Tipos de Ingesta Soportados - -| Tipo | Descripción | Extensiones | -|------|-------------|-------------| -| documento | Documentos | pdf, doc, docx | -| imagen | Imágenes | jpg, png, gif | -| audio | Audio | mp3, wav, m4a | -| video | Video | mp4, mov | -| texto | Texto plano | txt, md | -| json | Datos estructurados | json | - ---- - -## Configuración - -### Variables de Entorno - -```bash -# CLARA (.env en /opt/clara/) -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=tzzr -DB_USER=clara -DB_PASSWORD=**** - -R2_ENDPOINT=https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -R2_ACCESS_KEY=**** -R2_SECRET_KEY=**** -R2_BUCKET=deck - -# MARGARET (.env en /opt/margaret/) -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=corp -DB_USER=margaret -DB_PASSWORD=**** - -R2_BUCKET=corp -``` - -### Docker Compose - -```yaml -# CLARA -version: '3.8' -services: - clara: - container_name: clara-clara - image: tzzr/clara:latest - ports: - - "5051:5051" - env_file: - - .env - restart: unless-stopped -``` - ---- - -## Diferencias CLARA vs MARGARET - -| Aspecto | CLARA | MARGARET | -|---------|-------|----------| -| Uso | Personal | Empresarial | -| Volumen | Bajo | Alto | -| Privacidad | Máxima | Compartida (empresa) | -| Integración | ALFRED | JARED → MASON → FELDMAN | -| Backup | deck bucket | corp bucket | - ---- - -## Seguridad - -### Actual -- Autenticación por h_instancia -- Comunicación HTTP (sin TLS interno) -- .env con permisos 644 (**CRÍTICO: cambiar a 600**) - -### Recomendado -- TLS interno con certificados -- Rate limiting -- Validación de tipos MIME -- Escaneo antivirus (ClamAV) - ---- - -## Monitoreo - -### Logs -```bash -# CLARA -docker logs clara-clara -f - -# MARGARET -docker logs margaret-margaret -f -``` - -### Métricas Sugeridas -- Ingestas por minuto -- Tamaño promedio de archivos -- Tiempo de procesamiento -- Errores por tipo diff --git a/03_COMPONENTES/agentes/feldman.md b/03_COMPONENTES/agentes/feldman.md deleted file mode 100644 index 5980f41..0000000 --- a/03_COMPONENTES/agentes/feldman.md +++ /dev/null @@ -1,393 +0,0 @@ -# contable (FELDMAN) - -**Componente:** Consolidación -**Versión:** 5.0 -**Fecha:** 2024-12-24 - -> **Nota:** Este es un microservicio backend, no IA. - ---- - -## Resumen - -| Aspecto | Valor | -|---------|-------| -| Servidor | DECK y CORP | -| Puerto | 5054 | -| Base de datos | tzzr / corp | -| Tablas | contable_cola, contable_bloques, contable_validaciones | -| Estado | Operativo | - ---- - -## Rol en el Sistema - -contable (FELDMAN) es el "notario" del sistema: valida y consolida datos en bloques inmutables. - -``` -administrativo (MASON) - │ - ▼ -┌─────────────────────────────────────────┐ -│ contable (FELDMAN) │ -│ │ -│ 1. Recibir en cola │ -│ 2. Validar reglas (M-001, M-002, M-003)│ -│ 3. Calcular hash_contenido │ -│ 4. Encadenar (hash_previo) │ -│ 5. Crear milestone o bloque │ -│ 6. Marcar como consolidado │ -└─────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ milestones / bloques (inmutables) │ -└─────────────────────────────────────────┘ - │ - ▼ - auditoría (SENTINEL) [planificado] -``` - ---- - -## Reglas de Validación - -| Regla | Nombre | Descripción | Acción si falla | -|-------|--------|-------------|-----------------| -| M-001 | Hash único | h_bloque/h_milestone no existe previamente | Rechazar | -| M-002 | Encadenamiento | hash_previo apunta a bloque existente | Rechazar | -| M-003 | Integridad | hash_contenido = SHA-256(contenido) | Rechazar | - -### Implementación - -```python -def validar_m001(h_bloque): - """M-001: Hash debe ser único.""" - existe = db.execute( - "SELECT 1 FROM bloques WHERE h_bloque = %s", - [h_bloque] - ).fetchone() - return existe is None - -def validar_m002(hash_previo, h_instancia): - """M-002: hash_previo debe existir (excepto génesis).""" - if hash_previo is None: - # Primer bloque de la instancia (génesis) - existe = db.execute( - "SELECT 1 FROM bloques WHERE h_instancia = %s", - [h_instancia] - ).fetchone() - return existe is None # OK si no hay bloques previos - - existe = db.execute( - "SELECT 1 FROM bloques WHERE h_bloque = %s", - [hash_previo] - ).fetchone() - return existe is not None - -def validar_m003(hash_contenido, contenido): - """M-003: Hash debe coincidir con contenido.""" - import hashlib - import json - - contenido_serializado = json.dumps(contenido, sort_keys=True) - hash_calculado = hashlib.sha256(contenido_serializado.encode()).hexdigest() - - return hash_contenido == hash_calculado -``` - ---- - -## Schema de Tablas - -### feldman_cola - -```sql -CREATE TABLE feldman_cola ( - id BIGSERIAL PRIMARY KEY, - tipo VARCHAR(50) NOT NULL, -- 'milestone' o 'bloque' - h_entrada VARCHAR(64) NOT NULL, -- Referencia a origen - h_instancia VARCHAR(64) NOT NULL, - contenido JSONB NOT NULL, - prioridad INTEGER DEFAULT 0, - estado VARCHAR(20) DEFAULT 'pendiente', - intentos INTEGER DEFAULT 0, - ultimo_error TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - processed_at TIMESTAMPTZ -); - -CREATE INDEX idx_feldman_cola_estado ON feldman_cola(estado); -CREATE INDEX idx_feldman_cola_prioridad ON feldman_cola(prioridad DESC); -``` - -### feldman_bloques (alias de bloques) - -```sql --- Ver 02_MODELO_DATOS/planos.md para schema completo -CREATE TABLE bloques ( - id BIGSERIAL PRIMARY KEY, - h_bloque VARCHAR(64) UNIQUE NOT NULL, - h_instancia VARCHAR(64) NOT NULL, - secuencia BIGINT NOT NULL, - hash_previo VARCHAR(64), - hash_contenido VARCHAR(64) NOT NULL, - tipo_bloque VARCHAR(50) NOT NULL, - contenido JSONB NOT NULL, - -- ... campos adicionales - UNIQUE (h_instancia, secuencia) -); -``` - -### feldman_validaciones - -```sql -CREATE TABLE feldman_validaciones ( - id BIGSERIAL PRIMARY KEY, - h_entrada VARCHAR(64) NOT NULL, - tipo_destino VARCHAR(50) NOT NULL, -- 'milestone' o 'bloque' - h_destino VARCHAR(64), -- h_milestone o h_bloque - regla VARCHAR(50) NOT NULL, -- M-001, M-002, M-003 - resultado BOOLEAN NOT NULL, - mensaje TEXT, - validated_at TIMESTAMPTZ DEFAULT NOW() -); - -CREATE INDEX idx_feldman_val_entrada ON feldman_validaciones(h_entrada); -``` - ---- - -## Flujo de Consolidación - -``` -1. Entrada en feldman_cola - │ - ▼ -2. FELDMAN procesa (poll cada N segundos) - │ - ├─► Validar M-001 (hash único) - │ ├─ PASS → continuar - │ └─ FAIL → registrar error, marcar 'rechazado' - │ - ├─► Validar M-002 (encadenamiento) - │ ├─ PASS → continuar - │ └─ FAIL → registrar error, marcar 'rechazado' - │ - ├─► Validar M-003 (integridad) - │ ├─ PASS → continuar - │ └─ FAIL → registrar error, marcar 'rechazado' - │ - ▼ -3. Todas las validaciones PASS - │ - ▼ -4. Obtener última secuencia de h_instancia - │ - ▼ -5. Calcular h_bloque/h_milestone - │ - ▼ -6. INSERT en bloques/milestones - │ - ▼ -7. Actualizar feldman_cola → 'consolidado' - │ - ▼ -8. Registrar en feldman_validaciones -``` - ---- - -## Cálculo de Hashes - -### h_bloque - -```python -def calcular_h_bloque(h_instancia, secuencia, hash_previo, hash_contenido): - """ - Calcula el hash del bloque. - - h_bloque = SHA-256(h_instancia:secuencia:hash_previo:hash_contenido) - """ - import hashlib - - # hash_previo puede ser None para bloque génesis - previo = hash_previo or "genesis" - - data = f"{h_instancia}:{secuencia}:{previo}:{hash_contenido}" - - return hashlib.sha256(data.encode()).hexdigest() -``` - -### hash_contenido - -```python -def calcular_hash_contenido(contenido): - """ - Calcula el hash del contenido. - - hash_contenido = SHA-256(contenido_serializado) - """ - import hashlib - import json - - # Serialización determinista - contenido_str = json.dumps(contenido, sort_keys=True, ensure_ascii=False) - - return hashlib.sha256(contenido_str.encode()).hexdigest() -``` - ---- - -## API Endpoints - -### POST /consolidar - -```http -POST /consolidar HTTP/1.1 -Host: corp.example.com:5054 -Content-Type: application/json - -{ - "tipo": "bloque", - "h_entrada": "abc123...", - "h_instancia": "def456...", - "contenido": { - "tipo_bloque": "transaccion", - "monto": 1000, - "descripcion": "Pago factura #123" - } -} -``` - -### Response (éxito) - -```json -{ - "success": true, - "h_bloque": "ghi789...", - "secuencia": 42, - "hash_contenido": "jkl012...", - "validaciones": { - "M-001": true, - "M-002": true, - "M-003": true - } -} -``` - -### Response (error) - -```json -{ - "success": false, - "error": "Validación fallida", - "validaciones": { - "M-001": true, - "M-002": false, - "M-003": true - }, - "mensaje": "M-002: hash_previo no existe en cadena" -} -``` - ---- - -## Configuración - -### Variables de Entorno - -```bash -# /opt/feldman/.env -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=corp -DB_USER=feldman -DB_PASSWORD=**** - -POLL_INTERVAL=5 # Segundos entre polls -MAX_BATCH=10 # Elementos por lote -MAX_RETRIES=3 # Reintentos antes de rechazar -``` - ---- - -## Estados de la Cola - -| Estado | Descripción | -|--------|-------------| -| pendiente | En espera de procesamiento | -| procesando | FELDMAN está validando | -| consolidado | Validado y creado bloque/milestone | -| rechazado | Falló validación, no se reintentará | -| error | Error técnico, puede reintentarse | - ---- - -## Auditoría - -Cada validación se registra en `feldman_validaciones`: - -```sql --- Consultar historial de validaciones -SELECT - h_entrada, - regla, - resultado, - mensaje, - validated_at -FROM feldman_validaciones -WHERE h_entrada = 'abc123...' -ORDER BY validated_at; -``` - ---- - -## Futuro: Blockchain - -FELDMAN está diseñado para evolucionar hacia blockchain: - -1. **Actual:** Encadenamiento por hash_previo en PostgreSQL -2. **Fase 2:** Merkle tree para verificación eficiente -3. **Fase 3:** Smart contract en blockchain (Ethereum/Polygon) - -``` - Merkle Root - │ - ┌───────────┴───────────┐ - │ │ - Hash(AB) Hash(CD) - │ │ - ┌─────┴─────┐ ┌─────┴─────┐ - │ │ │ │ - Hash(A) Hash(B) Hash(C) Hash(D) - │ │ │ │ - Bloque A Bloque B Bloque C Bloque D -``` - ---- - -## Monitoreo - -### Logs -```bash -docker logs feldman-feldman -f -``` - -### Consultas de Estado - -```sql --- Elementos pendientes -SELECT COUNT(*) FROM feldman_cola WHERE estado = 'pendiente'; - --- Últimas validaciones fallidas -SELECT * FROM feldman_validaciones -WHERE resultado = false -ORDER BY validated_at DESC -LIMIT 10; - --- Bloques creados hoy -SELECT COUNT(*) FROM bloques -WHERE created_at >= CURRENT_DATE; -``` diff --git a/03_COMPONENTES/servicios/grace.md b/03_COMPONENTES/servicios/grace.md deleted file mode 100644 index 821cd2f..0000000 --- a/03_COMPONENTES/servicios/grace.md +++ /dev/null @@ -1,403 +0,0 @@ -# GRACE - Servicio de IA Cognitiva - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Estado Actual - -**BLOQUEADO:** RunPod no inicia workers. - -| Aspecto | Valor | -|---------|-------| -| Plataforma | RunPod Serverless | -| Endpoint ID | r00x4g3rrwkbyh | -| Balance | ~$72 USD | -| Workers activos | 0 | -| Estado | Inoperativo | - ---- - -## Descripción - -GRACE es el servicio de extracción de información mediante IA. Procesa contenido multimedia para extraer datos estructurados. - -``` -Contenido Raw -(audio, imagen, video, documento) - │ - ▼ -┌─────────────────────────────────────────┐ -│ GRACE (GPU) │ -│ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ ASR │ │ OCR │ │ TTS │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ Face │ │Embeddings│ │ Avatar │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ + 12 módulos pendientes │ -└─────────────────────────────────────────┘ - │ - ▼ -Datos Estructurados -(JSON, vectores, metadatos) -``` - ---- - -## Módulos Implementados (6 de 18) - -### ASR - Automatic Speech Recognition - -| Campo | Valor | -|-------|-------| -| Modelo | Whisper Large v3 | -| Input | Audio (mp3, wav, m4a) | -| Output | Texto + timestamps | -| GPU | A10G recomendado | - -```python -@grace.module("asr") -def process_asr(audio_bytes: bytes) -> dict: - """Transcribe audio a texto.""" - model = whisper.load_model("large-v3") - result = model.transcribe(audio_bytes) - return { - "text": result["text"], - "segments": result["segments"], - "language": result["language"] - } -``` - -### OCR - Optical Character Recognition - -| Campo | Valor | -|-------|-------| -| Modelo | EasyOCR + Tesseract | -| Input | Imagen (jpg, png) | -| Output | Texto + bounding boxes | -| GPU | A10G recomendado | - -```python -@grace.module("ocr") -def process_ocr(image_bytes: bytes) -> dict: - """Extrae texto de imagen.""" - reader = easyocr.Reader(['es', 'en']) - result = reader.readtext(image_bytes) - return { - "text": " ".join([r[1] for r in result]), - "boxes": [{"text": r[1], "bbox": r[0], "confidence": r[2]} for r in result] - } -``` - -### TTS - Text to Speech - -| Campo | Valor | -|-------|-------| -| Modelo | Coqui TTS | -| Input | Texto | -| Output | Audio (wav) | -| GPU | A10G recomendado | - -### Face - Reconocimiento Facial - -| Campo | Valor | -|-------|-------| -| Modelo | InsightFace | -| Input | Imagen | -| Output | Embeddings faciales | -| GPU | A10G recomendado | - -### Embeddings - Vectores Semánticos - -| Campo | Valor | -|-------|-------| -| Modelo | Sentence Transformers | -| Input | Texto | -| Output | Vector 384/768 dims | -| GPU | A10G recomendado | - -### Avatar - Generación de Avatares - -| Campo | Valor | -|-------|-------| -| Modelo | StyleGAN | -| Input | Imagen facial | -| Output | Avatar estilizado | -| GPU | A10G recomendado | - ---- - -## Módulos Pendientes (12) - -| Módulo | Descripción | Prioridad | -|--------|-------------|-----------| -| Document parsing | Extracción de PDFs | Alta | -| Image classification | Clasificación de imágenes | Media | -| Object detection | Detección de objetos | Media | -| Sentiment analysis | Análisis de sentimiento | Media | -| Named entity recognition | Extracción de entidades | Alta | -| Translation | Traducción de texto | Media | -| Summarization | Resumen de texto | Media | -| Question answering | Respuestas a preguntas | Baja | -| Code generation | Generación de código | Baja | -| Audio classification | Clasificación de audio | Baja | -| Video analysis | Análisis de video | Baja | -| Multimodal fusion | Fusión multimodal | Baja | - ---- - -## Código en R2 - -El código está listo y disponible: - -``` -s3://architect/gpu-services/ -├── base/ -│ └── bootstrap.sh # Script de inicialización -├── grace/ -│ └── code/ -│ └── handler.py # Handler principal -├── penny/ -│ └── code/ -│ └── handler.py -└── factory/ - └── code/ - └── handler.py -``` - -### Descargar Código - -```bash -source /home/orchestrator/orchestrator/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" - -aws s3 sync s3://architect/gpu-services/grace/ ./grace/ \ - --endpoint-url https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -``` - ---- - -## Handler RunPod - -```python -import runpod - -def handler(event): - """ - Handler principal de GRACE para RunPod Serverless. - - Input: - { - "input": { - "module": "asr|ocr|tts|face|embeddings|avatar", - "data": "base64 encoded content", - "options": {} - } - } - - Output: - { - "output": { ... resultado del módulo ... }, - "error": null - } - """ - try: - module = event["input"]["module"] - data = base64.b64decode(event["input"]["data"]) - options = event["input"].get("options", {}) - - if module == "asr": - result = process_asr(data, **options) - elif module == "ocr": - result = process_ocr(data, **options) - elif module == "tts": - result = process_tts(data, **options) - elif module == "face": - result = process_face(data, **options) - elif module == "embeddings": - result = process_embeddings(data, **options) - elif module == "avatar": - result = process_avatar(data, **options) - else: - return {"error": f"Módulo desconocido: {module}"} - - return {"output": result, "error": None} - - except Exception as e: - return {"error": str(e)} - -runpod.serverless.start({"handler": handler}) -``` - ---- - -## Uso desde MASON - -```python -import runpod -import base64 - -runpod.api_key = "..." # Desde Infisical - -def call_grace(module: str, content: bytes, options: dict = None) -> dict: - """Llama a GRACE para procesar contenido.""" - - job = runpod.run( - endpoint_id="r00x4g3rrwkbyh", - input={ - "module": module, - "data": base64.b64encode(content).decode(), - "options": options or {} - } - ) - - # Polling hasta completar - while True: - status = runpod.status(job["id"]) - if status["status"] == "COMPLETED": - return status["output"] - elif status["status"] == "FAILED": - raise Exception(status.get("error", "Job failed")) - time.sleep(1) -``` - ---- - -## Problema Actual: Workers No Inician - -### Incidente 2024-12-24 - -- **Síntoma:** 0 workers activos a pesar de jobs en cola -- **Balance:** $72+ (suficiente) -- **Configuración:** Correcta -- **Causa probable:** Problema de capacidad RunPod - -### Intentos de Diagnóstico - -1. Verificado endpoint activo en dashboard -2. Verificado balance suficiente -3. Verificado configuración de scaling -4. Jobs quedan en estado "IN_QUEUE" indefinidamente - -### Log de Errores - -``` -No hay logs disponibles - workers nunca inician -``` - ---- - -## Alternativas Evaluadas - -### 1. Modal (Recomendado) - -```python -import modal - -stub = modal.Stub("grace") -image = modal.Image.debian_slim().pip_install("whisper", "easyocr") - -@stub.function(gpu="A10G", image=image) -def process_asr(audio_bytes: bytes) -> dict: - import whisper - model = whisper.load_model("large-v3") - result = model.transcribe(audio_bytes) - return {"text": result["text"]} -``` - -**Pros:** -- Serverless real -- Buen DX (developer experience) -- Python-native -- Cold start rápido - -**Contras:** -- Menos GPUs disponibles que RunPod -- Pricing puede ser mayor - -### 2. Replicate - -```python -import replicate - -output = replicate.run( - "openai/whisper:large-v3", - input={"audio": audio_url} -) -``` - -**Pros:** -- Modelos pre-entrenados -- API simple -- Sin gestión de infraestructura - -**Contras:** -- Menos control -- Más caro a escala -- Dependencia de modelos de terceros - -### 3. Lambda Labs - -**Pros:** -- Hardware dedicado -- GPUs disponibles - -**Contras:** -- Menos flexible -- Reserva manual -- No serverless - -### 4. Self-Hosted - -**Pros:** -- Control total -- Sin dependencias externas -- Costo fijo a largo plazo - -**Contras:** -- CapEx alto -- Mantenimiento -- Requiere expertise - ---- - -## Plan de Migración - -### Fase 1: Evaluación - -- [ ] Crear cuenta Modal -- [ ] Portar módulo ASR a Modal -- [ ] Comparar latencia con RunPod (cuando funcione) -- [ ] Comparar costos - -### Fase 2: Migración - -- [ ] Portar 6 handlers a Modal -- [ ] Actualizar endpoints en MASON -- [ ] Actualizar documentación -- [ ] Testing end-to-end - -### Fase 3: Producción - -- [ ] Desplegar en Modal -- [ ] Monitorear costos y performance -- [ ] Deprecar RunPod endpoints -- [ ] Cancelar cuenta RunPod - ---- - -## Principio Fundamental - -**GRACE nunca modifica datos.** - -GRACE es read-only: extrae información pero no puede modificar el contenido original ni los datos del sistema. Las extracciones se almacenan como metadatos asociados al contenido original. - -``` -Contenido Original ────► GRACE ────► Metadatos Extraídos - (inmutable) (nuevos datos) -``` diff --git a/03_MODELO_DATOS/contenedor-schema.md b/03_MODELO_DATOS/contenedor-schema.md new file mode 100644 index 0000000..2d6ac41 --- /dev/null +++ b/03_MODELO_DATOS/contenedor-schema.md @@ -0,0 +1,347 @@ +# Estructura CONTENEDOR + +Esquema de datos completo para un registro en el sistema TZZR. + +## Esquema JSON + +```json +{ + "id": "uuid-contenedor", + "h_instancia": "sha256-instancia", + "archivo_hash": "sha256-archivo", + + "origen": { + "dispositivo": "uuid-dispositivo", + "app_version": "1.0.0", + "timestamp_captura": "2025-01-15T10:30:00Z", + "geolocalizacion": { + "lat": 41.3851, + "lon": 2.1734, + "precision_m": 10 + } + }, + + "archivo": { + "tipo": "image/jpeg", + "categoria": "imagen", + "size_bytes": 2048576, + "nombre_original": "IMG_2024.jpg", + "dimensiones": { + "ancho": 1920, + "alto": 1080 + }, + "duracion_seg": null + }, + + "tags": [ + { + "h_maestro": "sha256-tag", + "grupo": "hst", + "nombre": "Factura", + "confianza": 1.0 + } + ], + + "extraccion": { + "servicio": "openai-vision", + "version": "gpt-4o", + "timestamp": "2025-01-15T10:30:05Z", + "coste_cents": 2, + "texto": "FACTURA N 2024-001...", + "resumen": "Factura de servicios por 1.500EUR", + "entidades": { + "fechas": ["2025-01-15"], + "importes": [{"valor": 1500.00, "moneda": "EUR"}], + "personas": [], + "organizaciones": ["Acme Corp"], + "ubicaciones": [], + "documentos": [{"tipo": "factura", "numero": "2024-001"}] + }, + "clasificacion": { + "categoria": "finanzas", + "subcategoria": "factura_recibida", + "confianza": 0.95 + }, + "tags_sugeridos": [ + {"h_maestro": "sha256", "nombre": "Factura", "confianza": 0.95} + ], + "especifico": {} + }, + + "enriquecimiento": { + "notas": "Pendiente de pago", + "campos_personalizados": { + "proyecto": "Proyecto Alpha", + "responsable": "Juan Garcia" + }, + "tags_confirmados": ["sha256-tag1", "sha256-tag2"], + "tags_rechazados": ["sha256-tag3"], + "correcciones": { + "texto": null, + "entidades": null + }, + "editado_por": "usuario-id", + "editado_at": "2025-01-15T11:00:00Z" + }, + + "estado": { + "actual": "en_feldman_cola", + "historial": [ + {"paso": "clara", "entrada": "...", "salida": "...", "procesado_por": "clara-service"}, + {"paso": "mason", "entrada": "...", "salida": "...", "procesado_por": "usuario-id"}, + {"paso": "feldman_cola", "entrada": "...", "procesado_por": "feldman-service"} + ] + }, + + "bloque": { + "id": "sha256-bloque", + "numero": 42, + "registro_hash": "sha256-este-registro", + "merkle_proof": ["sha256-1", "sha256-2", "sha256-3"] + } +} +``` + +--- + +## Secciones + +### IDENTIFICACION + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `id` | UUID | Identificador unico del contenedor, generado por PACKET | +| `h_instancia` | SHA-256 | Identifica servidor DECK/CORP de origen | +| `archivo_hash` | SHA-256 | Hash del archivo, sirve como ruta en R2 | + +**Ruta R2**: `{endpoint}/{archivo_hash}` + +--- + +### ORIGEN + +Informacion del momento de captura. Generada automaticamente por PACKET. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `dispositivo` | UUID | ID del dispositivo que capturo | +| `app_version` | String | Version de PACKET | +| `timestamp_captura` | ISO 8601 | Momento exacto de captura | +| `geolocalizacion` | Object | Ubicacion GPS (opcional) | +| `geolocalizacion.lat` | Float | Latitud | +| `geolocalizacion.lon` | Float | Longitud | +| `geolocalizacion.precision_m` | Int | Precision en metros | + +**Inmutable**: Esta seccion nunca se modifica despues de la captura. + +--- + +### ARCHIVO + +Metadata tecnica del archivo capturado. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `tipo` | String | MIME type (image/jpeg, audio/mp3, etc.) | +| `categoria` | Enum | imagen, audio, video, documento | +| `size_bytes` | Int | Tamano en bytes | +| `nombre_original` | String | Nombre original del archivo (opcional) | +| `dimensiones.ancho` | Int | Ancho en pixels (imagen/video) | +| `dimensiones.alto` | Int | Alto en pixels (imagen/video) | +| `duracion_seg` | Float | Duracion en segundos (audio/video) | + +**Inmutable**: Esta seccion nunca se modifica. + +--- + +### TAGS + +Etiquetas aplicadas por el usuario en PACKET al momento de captura. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `h_maestro` | SHA-256 | Referencia a la biblioteca de tags | +| `grupo` | Enum | hst, emp, hsu, pjt, sys | +| `nombre` | String | Nombre visible del tag | +| `confianza` | Float | 1.0 = usuario, <1.0 = sugerido por IA | + +**Grupos de tags**: +- `hst`: Biblioteca publica HST +- `emp`: Biblioteca de empresa +- `hsu`: Biblioteca personal de usuario +- `pjt`: Biblioteca de proyecto +- `sys`: Tags de sistema (automaticos) + +**Mutable**: Se pueden anadir tags en MASON. + +--- + +### EXTRACCION + +Resultado del servicio de extraccion (OCR, transcripcion, analisis IA). + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `servicio` | String | Identificador del servicio usado | +| `version` | String | Version del modelo/servicio | +| `timestamp` | ISO 8601 | Momento de la extraccion | +| `coste_cents` | Int | Coste en centavos (obligatorio para auditoria) | +| `texto` | String | Contenido textual extraido | +| `resumen` | String | Resumen generado por IA | +| `entidades` | Object | Datos estructurados detectados | +| `clasificacion` | Object | Categoria sugerida automaticamente | +| `tags_sugeridos` | Array | Tags de biblioteca que podrian aplicar | +| `especifico` | Object | Campos segun tipo de archivo | + +#### Entidades detectadas + +```json +{ + "fechas": ["2025-01-15"], + "importes": [{"valor": 1500.00, "moneda": "EUR"}], + "personas": ["Juan Garcia"], + "organizaciones": ["Acme Corp"], + "ubicaciones": ["Barcelona"], + "documentos": [{"tipo": "factura", "numero": "2024-001"}] +} +``` + +#### Campos especificos por tipo + +**Imagen**: +```json +{ + "descripcion_visual": "Documento escaneado...", + "objetos_detectados": ["documento", "texto", "logo"], + "texto_ocr_bruto": "..." +} +``` + +**Audio**: +```json +{ + "transcripcion": "...", + "idioma_detectado": "es", + "hablantes": 2 +} +``` + +**Video**: +```json +{ + "transcripcion": "...", + "escenas": [...] +} +``` + +**Documento (PDF)**: +```json +{ + "paginas": 3, + "texto_por_pagina": ["...", "...", "..."] +} +``` + +**Inmutable**: Esta seccion no se modifica una vez generada. + +--- + +### ENRIQUECIMIENTO + +Lo que el usuario anade o modifica en MASON. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `notas` | String | Texto libre del usuario | +| `campos_personalizados` | Object | Campos key-value definidos por usuario | +| `tags_confirmados` | Array | h_maestro de tags sugeridos aceptados | +| `tags_rechazados` | Array | h_maestro de tags sugeridos descartados | +| `correcciones.texto` | String | Texto corregido si OCR fallo | +| `correcciones.entidades` | Object | Entidades corregidas | +| `editado_por` | String | ID del usuario que edito | +| `editado_at` | ISO 8601 | Ultima edicion | + +**Mutable**: Hasta que se consolida en FELDMAN. + +--- + +### ESTADO + +Trazabilidad del registro a traves del sistema. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `actual` | String | Ubicacion actual del registro | +| `historial` | Array | Pasos por los que ha pasado | +| `historial[].paso` | String | Nombre del paso (clara, mason, feldman_cola, feldman_bloque) | +| `historial[].entrada` | ISO 8601 | Cuando entro | +| `historial[].salida` | ISO 8601 | Cuando salio | +| `historial[].procesado_por` | String | Servicio o usuario que proceso | + +**Estados posibles**: +- `en_clara` / `en_margaret`: Recien capturado +- `en_mason`: En enriquecimiento +- `en_feldman_cola`: Esperando consolidacion +- `en_feldman_bloque`: Consolidado (inmutable) + +--- + +### BLOQUE + +Solo se rellena cuando FELDMAN consolida el registro. + +| Campo | Tipo | Descripcion | +|-------|------|-------------| +| `id` | SHA-256 | Hash del bloque | +| `numero` | Int | Numero secuencial del bloque | +| `registro_hash` | SHA-256 | Hash de este registro | +| `merkle_proof` | Array | Prueba de inclusion en el arbol Merkle | + +**Inmutable**: Una vez consolidado, nunca cambia. + +--- + +## Mutabilidad por Seccion + +| Seccion | Quien escribe | Inmutable | +|---------|---------------|-----------| +| identificacion | PACKET | Si | +| origen | PACKET | Si | +| archivo | PACKET | Si | +| tags | PACKET (usuario) | No (se puede anadir) | +| extraccion | Servicio externo | Si | +| enriquecimiento | Usuario en MASON | No (hasta consolidar) | +| estado | Sistema | Se acumula | +| bloque | FELDMAN | Si (al consolidar) | + +--- + +## Flujo de datos + +``` +PACKET genera: + - identificacion + - origen + - archivo + - tags (iniciales) + +CLARA/MARGARET registra: + - estado.historial += {paso: "clara/margaret"} + +Servicio extraccion anade: + - extraccion (completa) + +MASON permite editar: + - enriquecimiento + - tags (anadir) + - estado.historial += {paso: "mason"} + +FELDMAN consolida: + - bloque (completo) + - estado.actual = "en_feldman_bloque" + - estado.historial += {paso: "feldman_bloque"} +``` + +--- + +*Actualizado: 2025-12-22* diff --git a/03_MODELO_DATOS/context-tables.md b/03_MODELO_DATOS/context-tables.md new file mode 100644 index 0000000..7649ec4 --- /dev/null +++ b/03_MODELO_DATOS/context-tables.md @@ -0,0 +1,262 @@ +# Tablas de Contexto + +**Estado:** Implementado +**Base de datos:** PostgreSQL (ARCHITECT) + +--- + +## Descripción + +Tablas que proporcionan contexto a las instancias Claude. + +--- + +## Diagrama de Relaciones + +``` +instancias ──┬── memoria + │ + └── conversaciones ──── mensajes_v2 + │ + └── memoria (conversacion_origen) + +conocimiento (independiente, compartido) + +contexto_ambiental (independiente, periódico) +``` + +--- + +## instancias + +Identidad de cada instancia Claude. + +```sql +CREATE TABLE instancias ( + id VARCHAR(50) PRIMARY KEY, + nombre VARCHAR(100), + system_prompt TEXT NOT NULL, + personalidad JSONB DEFAULT '{ + "tono": "profesional", + "idioma": "es", + "verbosidad": "conciso" + }', + permisos JSONB DEFAULT '{ + "puede_ejecutar_bash": false, + "puede_acceder_red": false, + "puede_modificar_archivos": false, + "servidores_permitidos": [] + }', + modelo VARCHAR(50) DEFAULT 'sonnet', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +``` + +**Uso:** Define quién es cada Claude, su personalidad, y qué puede hacer. + +### Instancias Activas + +| ID | Nombre | Modelo | Servidor | +|----|--------|--------|----------| +| architect | Architect | sonnet | ARCHITECT | +| hst | HST | sonnet | HST | +| deck | Deck | sonnet | DECK | +| corp | Corp | sonnet | CORP | +| runpod | Runpod | sonnet | RunPod | +| locker | Locker | haiku | R2/Storage | + +--- + +## memoria + +Memoria a largo plazo por instancia. + +```sql +CREATE TABLE memoria ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + instancia_id VARCHAR(50) REFERENCES instancias(id), + tipo VARCHAR(50) NOT NULL, + contenido TEXT NOT NULL, + importancia INT DEFAULT 5, + usos INT DEFAULT 0, + ultimo_uso TIMESTAMP, + conversacion_origen UUID REFERENCES conversaciones(id), + expira_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_memoria_instancia ON memoria(instancia_id, importancia DESC); +CREATE INDEX idx_memoria_tipo ON memoria(instancia_id, tipo); +``` + +### Tipos de Memoria + +| Tipo | Descripción | +|------|-------------| +| preferencia | Preferencias del usuario | +| hecho | Hechos aprendidos | +| decision | Decisiones tomadas | +| aprendizaje | Lecciones aprendidas | +| procedimiento | Procedimientos aprendidos | + +--- + +## conocimiento + +Base de conocimiento compartida (RAG). + +```sql +CREATE TABLE conocimiento ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + categoria VARCHAR(50) NOT NULL, + titulo VARCHAR(200), + contenido TEXT NOT NULL, + tags TEXT[], + instancias_permitidas VARCHAR(50)[], + prioridad INT DEFAULT 0, + fuente VARCHAR(200), + hash_contenido VARCHAR(64), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_conocimiento_categoria ON conocimiento(categoria); +CREATE INDEX idx_conocimiento_prioridad ON conocimiento(prioridad DESC); +CREATE INDEX idx_conocimiento_tags ON conocimiento USING GIN(tags); +``` + +### Categorías + +| Categoría | Descripción | +|-----------|-------------| +| infraestructura | Documentación de infra | +| proyecto | Información de proyectos | +| personal | Datos personales | +| procedimiento | Procedimientos operativos | + +--- + +## conversaciones + +Sesiones de chat. + +```sql +CREATE TABLE conversaciones ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + instancia_id VARCHAR(50) REFERENCES instancias(id), + usuario VARCHAR(50) NOT NULL, + titulo VARCHAR(200), + activa BOOLEAN DEFAULT TRUE, + total_tokens INT DEFAULT 0, + total_mensajes INT DEFAULT 0, + resumen TEXT, + resumen_updated_at TIMESTAMP, + contexto_ambiental JSONB DEFAULT '{ + "proyecto_activo": null, + "archivos_abiertos": [], + "ultimo_comando": null, + "hora_local": null + }', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_conversaciones_instancia ON conversaciones(instancia_id, activa); +CREATE INDEX idx_conversaciones_usuario ON conversaciones(usuario, activa); +``` + +--- + +## mensajes_v2 + +Mensajes con soporte para compactación. + +```sql +CREATE TABLE mensajes_v2 ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + conversacion_id UUID REFERENCES conversaciones(id), + role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant', 'system')), + contenido TEXT NOT NULL, + archivos JSONB DEFAULT '[]', + tokens_estimados INT, + compactado BOOLEAN DEFAULT FALSE, + resumen_compactado TEXT, + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_mensajes_v2_conversacion ON mensajes_v2(conversacion_id, created_at); +CREATE INDEX idx_mensajes_v2_no_compactados ON mensajes_v2(conversacion_id, compactado) + WHERE compactado = FALSE; +``` + +**Nota:** Los mensajes antiguos se compactan (resumen) para ahorrar tokens. + +--- + +## contexto_ambiental + +Estado actual del sistema (captura periódica). + +```sql +CREATE TABLE contexto_ambiental ( + id SERIAL PRIMARY KEY, + capturado_at TIMESTAMP DEFAULT NOW(), + servidores JSONB, + servicios JSONB, + tareas_pendientes JSONB, + alertas JSONB, + git_estado JSONB, + expira_at TIMESTAMP DEFAULT NOW() + INTERVAL '1 hour' +); + +CREATE INDEX idx_contexto_ambiental_tiempo ON contexto_ambiental(capturado_at DESC); +``` + +### Estructura JSONB + +```json +{ + "servidores": { + "architect": {"status": "online", "uptime": "5d"}, + "deck": {"status": "online", "uptime": "3d"} + }, + "servicios": { + "gitea": "running", + "windmill": "running", + "directus": "running" + }, + "tareas_pendientes": [ + {"id": 1, "descripcion": "...", "prioridad": "alta"} + ], + "alertas": [ + {"tipo": "warning", "mensaje": "...", "severidad": "medium"} + ], + "git_estado": { + "branch": "main", + "commits_ahead": 0, + "uncommitted_changes": false + } +} +``` + +--- + +## Flujo de Contexto + +Cuando Claude recibe un mensaje: + +``` +1. System Prompt ← instancias.system_prompt +2. Personalidad ← instancias.personalidad +3. Memorias ← memoria WHERE instancia_id = X + ORDER BY importancia DESC LIMIT 20 +4. Conocimiento ← conocimiento WHERE instancias_permitidas + IS NULL OR X = ANY(instancias_permitidas) +5. Contexto Ambiental ← contexto_ambiental + ORDER BY capturado_at DESC LIMIT 1 +6. Historial ← mensajes_v2 WHERE compactado = FALSE + ORDER BY created_at +7. Resumen ← conversaciones.resumen (mensajes compactados) +``` diff --git a/03_MODELO_DATOS/entidades.md b/03_MODELO_DATOS/entidades.md new file mode 100644 index 0000000..2fdfe10 --- /dev/null +++ b/03_MODELO_DATOS/entidades.md @@ -0,0 +1,139 @@ +# Entidades + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Visión General + +Las entidades base son los bloques fundamentales del sistema. Cada una tiene un hash único SHA-256 que la identifica de forma unívoca. + +``` +DEFINICIÓN ORIGINAL → SHA-256 → h_{tipo} (64 chars) +``` + +--- + +## Entidades del Sistema + +| Entidad | Hash | Descripción | Estado | +|---------|------|-------------|--------| +| **HST** | h_maestro | Tags semánticos | Implementado | +| **ITM** | h_item | Plano ideal | Planificado | +| **PLY** | h_player | Identidad | Planificado | +| **LOC** | h_loc | Ubicaciones | Planificado | +| **FLG** | h_flag | Marcos jurídicos | Planificado | + +--- + +## HST (Hash Semantic Tagging) + +Sistema de etiquetas semánticas visuales. + +### Fórmula + +``` +h_maestro = SHA-256(grupo:ref) +``` + +### Grupos + +| Grupo | Cantidad | +|-------|----------| +| hst | 639 | +| spe | 145 | +| vsn | 84 | +| flg | 65 | +| vue | 21 | + +--- + +## ITM (Items) + +Plano ideal - definiciones abstractas. + +### Tipos + +| Tipo | Descripción | +|------|-------------| +| accion_ideal | Acción que debería ejecutarse | +| objetivo | Meta a alcanzar | +| requerimiento | Requisito a cumplir | + +--- + +## PLY (Players) + +Identidad de actores en el sistema. + +### Tipos + +| Tipo | Descripción | +|------|-------------| +| persona | Usuario individual | +| empresa | Organización | +| agente | Sistema automatizado | + +--- + +## LOC (Locations) + +Ubicaciones geográficas. + +### Tipos + +| Tipo | Descripción | +|------|-------------| +| punto | Coordenadas exactas | +| area | Zona delimitada | +| ruta | Trayecto | + +--- + +## FLG (Flags) + +Marcos jurídicos y normativas. + +### Uso + +- Países +- Normativas (RGPD, SOX, ISO) +- Jurisdicciones + +--- + +## Relaciones Entre Entidades + +``` +ITM (ideal) + │ + ├──► h_maestro (HST tags) + ├──► h_player (PLY responsable) + ├──► h_loc (LOC ubicación) + └──► h_flag (FLG normativa) + +MST (milestone) + │ + ├──► item_asociado (ITM) + └──► h_maestro (HST tags) + +BCK (bloque) + │ + ├──► item_asociado (ITM) + ├──► milestone_asociado (MST) + └──► h_maestro (HST tags) +``` + +--- + +## Extensiones de Usuario + +| Tabla | Descripción | +|-------|-------------| +| hsu | HST Usuario | +| spu | SPE Usuario | +| vsu | VSN Usuario | +| vuu | VUE Usuario | +| pju | Proyectos Usuario | +| flu | FLG Usuario | diff --git a/03_MODELO_DATOS/flujos.md b/03_MODELO_DATOS/flujos.md new file mode 100644 index 0000000..a1c7f26 --- /dev/null +++ b/03_MODELO_DATOS/flujos.md @@ -0,0 +1,188 @@ +# Flujos + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Visión General + +El sistema tiene tres flujos principales según la naturaleza de la información entrante. + +--- + +## Flujo Estándar (entrada manual) + +**Condición:** La información no encaja (entrada manual, incidencia, improvisación). Requiere enriquecimiento o validación manual. + +``` +Secretaría (Clara/Margaret) + │ + │ registro inmutable + ▼ +Administración (Mason) + │ + │ enriquecimiento (24h) + ▼ +Contable (Feldman) + │ + │ consolidación + ▼ + Inmutable +``` + +--- + +## Flujo de Producción (procesos predefinidos) + +**Condición:** La información encaja (viene de Producción, proceso predefinido completo). La información ya está completa y estructurada. + +``` +Producción (Alfred/Jared) + │ + │ flujo predefinido + ▼ +Secretaría (Clara/Margaret) + │ + │ registro inmutable + ▼ +Contable (Feldman) + │ + │ consolidación directa + ▼ + Inmutable +``` + +**Nota:** Este flujo salta Administración porque no hay nada que enriquecer. + +--- + +## Flujo con Incidencia + +**Condición:** Durante un flujo de producción, el usuario improvisa o se produce un cambio. + +**Comportamiento:** La improvisación marca el punto de quiebre. Todo lo anterior registrado se mantiene, pero lo que viene después requiere el paso por Administración. + +``` +Producción (Alfred/Jared) + │ + │ flujo predefinido + ▼ +Secretaría (Clara/Margaret) + │ + │ ⚠️ incidencia detectada + ▼ +Administración (Mason) + │ + │ enriquecimiento + ▼ +Contable (Feldman) +``` + +--- + +## Regla de Decisión + +| Condición | Flujo | +|-----------|-------| +| **Encaja** | Secretaría → Feldman | +| **No encaja** | Secretaría → Mason → Feldman | + +--- + +## Diagrama de Decisión + +``` + ┌─────────────────┐ + │ Entrada │ + └────────┬────────┘ + │ + ┌────────▼────────┐ + │ ¿Viene de │ + │ Producción? │ + └────────┬────────┘ + │ + ┌────────┴────────┐ + │ │ + SÍ NO + │ │ + ┌────────▼────────┐ │ + │ ¿Encaja │ │ + │ completo? │ │ + └────────┬────────┘ │ + │ │ + ┌────────┴────────┐ │ + │ │ │ + SÍ NO │ + │ │ │ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────────────┐ + │ Directo │ │ Mason │ + │ Feldman │ │ (enriquecer) │ + └──────────┘ └──────────────────┘ +``` + +--- + +## Mecanismo de "Encaja" + +**Pendiente de definir:** Sistema de hashes que determina automáticamente si la información encaja con un flujo predefinido. + +Concepto propuesto: + +```python +def encaja(entrada, flujo_esperado): + # Comparar estructura de datos + # Verificar campos requeridos + # Validar tipos + return estructura_coincide and campos_completos +``` + +--- + +## Estados del Flujo + +| Estado | Ubicación | Descripción | +|--------|-----------|-------------| +| **recibido** | Secretaría | Entrada registrada | +| **en_edicion** | Mason | Usuario enriqueciendo | +| **listo** | Mason | Preparado para Feldman | +| **pendiente** | Feldman cola | En espera de consolidación | +| **consolidado** | Feldman bloques | Registro final inmutable | + +--- + +## Estructura de Feldman + +Feldman tiene **dos tablas internas**: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ FELDMAN │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ COLA (24h configurable) │ │ +│ │ • Registros esperando consolidación │ │ +│ │ • Usuario puede: DEVOLVER a Mason │ │ +│ │ • Si expira → pasa a BLOQUE │ │ +│ └─────────────────────────┬────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ BLOQUES INMUTABLES │ │ +│ │ GÉNESIS ─▶ BLOQUE 1 ─▶ BLOQUE 2 ─▶ ... │ │ +│ │ (hash anterior, merkle root, timestamp) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Ventanas Temporales + +| Etapa | Default | Configurable | Descripción | +|-------|---------|--------------|-------------| +| Mason | 24h | ✓ | Tiempo para enriquecer antes de auto-envío | +| Feldman cola | 24h | ✓ | Tiempo en cola antes de cerrar en bloque | diff --git a/03_MODELO_DATOS/hashes.md b/03_MODELO_DATOS/hashes.md new file mode 100644 index 0000000..4d12c6e --- /dev/null +++ b/03_MODELO_DATOS/hashes.md @@ -0,0 +1,134 @@ +# Hashes e Identificadores + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Visión General + +El sistema usa SHA-256 para identificación única e inmutable de todos los elementos. + +``` +CONTENIDO → SHA-256 → 64 caracteres hexadecimales +``` + +--- + +## Tipos de Hash + +| Prefijo | Tipo | Descripción | +|---------|------|-------------| +| **h_maestro** | Tag HST | Hash de etiqueta semántica | +| **h_instancia** | Contexto | Identidad del servidor/instancia | +| **h_entrada** | Contenedor | Hash de ingesta en secretaría | +| **h_bloque** | BCK | Hash de bloque consolidado | +| **h_milestone** | MST | Hash de milestone | +| **h_item** | ITM | Hash de ítem (plano ideal) | +| **h_player** | PLY | Hash de identidad | +| **h_loc** | LOC | Hash de ubicación | +| **h_flag** | FLG | Hash de marco jurídico | + +--- + +## Fórmulas + +### h_maestro (HST) + +``` +h_maestro = SHA-256(grupo:ref) + +Ejemplo: +h_maestro = SHA-256("hst:person") +``` + +### h_entrada (Secretaría) + +``` +h_entrada = SHA-256(timestamp:origen:contenido) +``` + +### h_bloque (BCK) + +``` +h_bloque = SHA-256(h_instancia:secuencia:hash_previo:hash_contenido) +hash_contenido = SHA-256(contenido_serializado) +``` + +### h_milestone (MST) + +``` +h_milestone = SHA-256(h_instancia:secuencia:tipo:contenido) +``` + +### h_item (ITM) + +``` +h_item = SHA-256(h_instancia:secuencia:tipo:contenido) +``` + +### h_loc (LOC) + +``` +h_loc = SHA-256(lat:lon:precision) +``` + +--- + +## Encadenamiento + +``` +Bloque 1 Bloque 2 Bloque 3 +┌────────────┐ ┌────────────┐ ┌────────────┐ +│h_bloque: A │──────►│hash_prev: A│──────►│hash_prev: B│ +│ │ │h_bloque: B │ │h_bloque: C │ +└────────────┘ └────────────┘ └────────────┘ +``` + +--- + +## Verificación + +### Integridad de Contenido + +```python +import hashlib +import json + +def verificar_integridad(bloque): + contenido_serializado = json.dumps(bloque['contenido'], sort_keys=True) + hash_calculado = hashlib.sha256(contenido_serializado.encode()).hexdigest() + return hash_calculado == bloque['hash_contenido'] +``` + +### Encadenamiento + +```python +def verificar_cadena(bloque, bloque_previo): + return bloque['hash_previo'] == bloque_previo['h_bloque'] +``` + +--- + +## Propiedades + +| Propiedad | Descripción | +|-----------|-------------| +| **Determinista** | Mismo input → mismo hash | +| **Único** | Colisión prácticamente imposible | +| **Irreversible** | No se puede obtener contenido desde hash | +| **Fijo** | Siempre 64 caracteres | + +--- + +## Uso en Trazabilidad + +``` +Bloque (Feldman) + │ + └── h_entrada ──► Contenedor (Secretaría) + │ + └── archivos_hashes ──► R2 Storage +``` + +Cualquier registro final puede rastrearse hasta su origen. diff --git a/03_MODELO_DATOS/planos.md b/03_MODELO_DATOS/planos.md new file mode 100644 index 0000000..a2a352f --- /dev/null +++ b/03_MODELO_DATOS/planos.md @@ -0,0 +1,141 @@ +# Planos de Datos + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Visión General + +El sistema utiliza tres planos conceptuales para organizar la información: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ T0 - PLANO IDEAL │ +│ ITM (Items): Objetivos, acciones ideales, requerimientos │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ materializa +┌─────────────────────────────────────────────────────────────────┐ +│ MST - MILESTONES │ +│ Compromisos futuros con fecha de vencimiento │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ cumple/genera +┌─────────────────────────────────────────────────────────────────┐ +│ BCK - BLOQUES │ +│ Hechos inmutables del pasado (blockchain-ready) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## T0 - Plano Ideal (ITM) + +### Concepto + +El plano T0 contiene los Items que representan el estado ideal futuro. Son la "partitura" que guía las acciones. + +### Naturaleza + +| Aspecto | Valor | +|---------|-------| +| Temporalidad | T-N → T-1 → T0 | +| Energía | No consume (es definición) | +| Mutabilidad | Versionable | + +### Estado + +**PLANIFICADO** - Schema definido pero sin tablas creadas. + +--- + +## MST - Milestones + +### Concepto + +Compromisos con fecha futura. Tienen un período flotante de 24 horas antes de consolidarse. + +### Tipos + +| Tipo | Descripción | +|------|-------------| +| compromiso | Compromiso de entrega | +| deadline | Fecha límite | +| evento | Evento programado | +| recordatorio | Recordatorio futuro | + +### Período Flotante + +| Parámetro | Valor | +|-----------|-------| +| Duración | 24 horas | +| Durante | Modificable vía Mason | +| Después | Inmutable | + +### Estado + +**IMPLEMENTADO** en CORP. + +--- + +## BCK - Bloques + +### Concepto + +Registros inmutables de hechos pasados. Cada bloque está encadenado al anterior mediante hash. + +### Tipos + +| Tipo | Descripción | +|------|-------------| +| transaccion | Transacción económica | +| documento | Documento consolidado | +| evento | Evento registrado | +| milestone_cumplido | Milestone que se cumplió | + +### Encadenamiento + +``` +Bloque N-1 Bloque N +┌──────────────────┐ ┌──────────────────┐ +│ h_bloque: abc123 │ ──────► │ hash_previo: │ +│ hash_contenido: │ │ abc123 │ +│ def456 │ │ h_bloque: ghi789 │ +│ secuencia: 1 │ │ secuencia: 2 │ +└──────────────────┘ └──────────────────┘ +``` + +### Estado + +**IMPLEMENTADO** en CORP. + +--- + +## Flujo Entre Planos + +``` +Usuario crea ITM + │ + ▼ +ITM (T0) "Objetivo: Completar proyecto X" + │ + │ materializa + ▼ +MST "Milestone: Entrega módulo 1 - 2024-01-15" + │ + │ [período flotante 24h] + │ + ▼ +MASON (enriquecimiento) + │ + ▼ +FELDMAN (validación) + │ + │ cumple/genera + ▼ +BCK "Bloque: Módulo 1 entregado - 2024-01-14" + │ + ▼ +Inmutable ✓ +``` diff --git a/03_MODELO_DATOS/schemas/00_types.sql b/03_MODELO_DATOS/schemas/00_types.sql new file mode 100644 index 0000000..32377a9 --- /dev/null +++ b/03_MODELO_DATOS/schemas/00_types.sql @@ -0,0 +1,101 @@ +-- ============================================ +-- TIPOS ENUMERADOS COMUNES +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Aplicar primero antes de cualquier otro schema + +-- Estados de tarea +DO $$ BEGIN + CREATE TYPE task_status AS ENUM ( + 'draft', -- Borrador, no iniciada + 'pending', -- Pendiente de inicio + 'in_progress', -- En progreso + 'blocked', -- Bloqueada por dependencia + 'review', -- En revisión + 'completed', -- Completada + 'cancelled' -- Cancelada + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Prioridad de tarea +DO $$ BEGIN + CREATE TYPE task_priority AS ENUM ( + 'critical', -- Crítica, atención inmediata + 'high', -- Alta prioridad + 'medium', -- Media (default) + 'low', -- Baja prioridad + 'someday' -- Algún día / sin fecha + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Dirección de archivo en work log +DO $$ BEGIN + CREATE TYPE file_direction AS ENUM ( + 'inbound', -- Archivo recibido + 'outbound', -- Archivo enviado + 'internal', -- Archivo interno/generado + 'reference' -- Archivo de referencia + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Servicios de IA del ecosistema +DO $$ BEGIN + CREATE TYPE ai_service AS ENUM ( + 'grace', -- GRACE - Capa de procesamiento + 'penny', -- PENNY - Asistente de voz + 'factory' -- THE FACTORY - Procesamiento documental + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Grupos de etiquetas HST +DO $$ BEGIN + CREATE TYPE hst_grupo AS ENUM ( + 'hst', -- Sistema (sync tzrtech.org) + 'emp', -- Empresa + 'hsu', -- Usuario + 'pjt' -- Proyecto + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Modo de despliegue S-CONTRACT +DO $$ BEGIN + CREATE TYPE deployment_mode AS ENUM ( + 'EXTERNAL', -- Solo APIs externas + 'SELF_HOSTED', -- Solo infraestructura propia + 'SEMI' -- Híbrido con fallback + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- Tier de proveedor +DO $$ BEGIN + CREATE TYPE provider_tier AS ENUM ( + 'SELF_HOSTED', -- Infraestructura propia + 'EXTERNAL' -- API externa + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- ============================================ +-- FUNCIÓN: Actualizar updated_at automáticamente +-- ============================================ + +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ language 'plpgsql'; diff --git a/03_MODELO_DATOS/schemas/01_hst_tags.sql b/03_MODELO_DATOS/schemas/01_hst_tags.sql new file mode 100644 index 0000000..6c7c9af --- /dev/null +++ b/03_MODELO_DATOS/schemas/01_hst_tags.sql @@ -0,0 +1,227 @@ +-- ============================================ +-- TABLAS HST (Sistema de Etiquetas) +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Requiere: 00_types.sql +-- +-- SISTEMA DUAL DE HASHES: +-- ┌─────────────────────────────────────────────────────────────┐ +-- │ h_maestro = SHA-256(grupo || ':' || ref) │ +-- │ → Identidad SEMÁNTICA (determinista, para S-CONTRACT) │ +-- │ → Ejemplo: SHA-256("hst:finanzas") = "a7b3c9..." │ +-- │ │ +-- │ mrf = SHA-256(bytes_imagen) │ +-- │ → Identidad de ARCHIVO (para servir imágenes) │ +-- │ → URL: https://tzrtech.org/{mrf}.png │ +-- └─────────────────────────────────────────────────────────────┘ + +-- ============================================ +-- TABLA BASE: hst_tags +-- Tabla unificada de etiquetas HST +-- ============================================ + +CREATE TABLE IF NOT EXISTS hst_tags ( + id SERIAL PRIMARY KEY, + + -- Identificadores duales + h_maestro VARCHAR(64) UNIQUE NOT NULL, -- SHA-256(grupo:ref), identidad semántica + ref VARCHAR(50) NOT NULL, -- Referencia corta (ej: "finanzas") + mrf VARCHAR(64), -- SHA-256(imagen_bytes), hash de imagen + + -- Nombres + nombre VARCHAR(100) NOT NULL, -- Nombre legible en español + nombre_en VARCHAR(100), -- Nombre en inglés + descripcion TEXT, + + -- Visual + imagen_url TEXT, -- URL completa (https://tzrtech.org/{mrf}.png) + color VARCHAR(7), -- Color hex (#RRGGBB) + icono VARCHAR(50), -- Nombre de icono (ej: lucide:tag) + + -- Clasificación + grupo hst_grupo NOT NULL, -- hst, emp, hsu, pjt + categoria VARCHAR(100), -- Categoría dentro del grupo + + -- Jerarquía + padre_h_maestro VARCHAR(64) REFERENCES hst_tags(h_maestro), + rootref VARCHAR(50), -- Ref del ancestro raíz + nivel INTEGER DEFAULT 0, -- Nivel de profundidad (0 = raíz) + path_h_maestros TEXT[], -- Array de ancestros para queries rápidas + + -- Estado + activo BOOLEAN DEFAULT true, + visible BOOLEAN DEFAULT true, -- Visible en UI + + -- Sync con tzrtech.org + source VARCHAR(50) DEFAULT 'local', -- local, tzrtech, empresa + source_id VARCHAR(100), -- ID en el sistema origen + synced_at TIMESTAMP, -- Última sincronización + + -- Metadata + metadata JSONB DEFAULT '{}', + + -- Timestamps + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + -- Constraints + CONSTRAINT unique_ref_grupo UNIQUE(ref, grupo) +); + +-- Índices principales +CREATE INDEX IF NOT EXISTS idx_hst_tags_h_maestro ON hst_tags(h_maestro); +CREATE INDEX IF NOT EXISTS idx_hst_tags_ref ON hst_tags(ref); +CREATE INDEX IF NOT EXISTS idx_hst_tags_mrf ON hst_tags(mrf); +CREATE INDEX IF NOT EXISTS idx_hst_tags_grupo ON hst_tags(grupo); +CREATE INDEX IF NOT EXISTS idx_hst_tags_categoria ON hst_tags(categoria); +CREATE INDEX IF NOT EXISTS idx_hst_tags_padre ON hst_tags(padre_h_maestro); +CREATE INDEX IF NOT EXISTS idx_hst_tags_rootref ON hst_tags(rootref); +CREATE INDEX IF NOT EXISTS idx_hst_tags_activo ON hst_tags(activo); +CREATE INDEX IF NOT EXISTS idx_hst_tags_path ON hst_tags USING GIN(path_h_maestros); + +-- Trigger para updated_at +DROP TRIGGER IF EXISTS update_hst_tags_updated_at ON hst_tags; +CREATE TRIGGER update_hst_tags_updated_at BEFORE UPDATE ON hst_tags + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- FUNCIÓN: Calcular path de ancestros +-- ============================================ + +CREATE OR REPLACE FUNCTION calculate_hst_path() +RETURNS TRIGGER AS $$ +DECLARE + parent_path TEXT[]; +BEGIN + IF NEW.padre_h_maestro IS NULL THEN + NEW.path_h_maestros := ARRAY[]::TEXT[]; + NEW.nivel := 0; + ELSE + SELECT path_h_maestros, nivel + 1 + INTO parent_path, NEW.nivel + FROM hst_tags + WHERE h_maestro = NEW.padre_h_maestro; + + NEW.path_h_maestros := array_append(parent_path, NEW.padre_h_maestro); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trigger_calculate_hst_path ON hst_tags; +CREATE TRIGGER trigger_calculate_hst_path +BEFORE INSERT OR UPDATE OF padre_h_maestro ON hst_tags +FOR EACH ROW EXECUTE FUNCTION calculate_hst_path(); + +-- ============================================ +-- VISTAS POR GRUPO (Compatibilidad con v2) +-- ============================================ + +-- Vista: Etiquetas del sistema (HST) +CREATE OR REPLACE VIEW hst_tags_sistema AS +SELECT * FROM hst_tags WHERE grupo = 'hst'; + +-- Vista: Etiquetas de empresa (EMP) +CREATE OR REPLACE VIEW hst_tags_empresa AS +SELECT * FROM hst_tags WHERE grupo = 'emp'; + +-- Vista: Etiquetas de usuario (HSU) +CREATE OR REPLACE VIEW hst_tags_usuario AS +SELECT * FROM hst_tags WHERE grupo = 'hsu'; + +-- Vista: Etiquetas de proyecto (PJT) +CREATE OR REPLACE VIEW hst_tags_proyecto AS +SELECT * FROM hst_tags WHERE grupo = 'pjt'; + +-- ============================================ +-- TABLA: hst_tag_relations +-- Relaciones entre etiquetas (sinónimos, relacionados) +-- ============================================ + +CREATE TABLE IF NOT EXISTS hst_tag_relations ( + id SERIAL PRIMARY KEY, + tag_a_h_maestro VARCHAR(64) NOT NULL REFERENCES hst_tags(h_maestro), + tag_b_h_maestro VARCHAR(64) NOT NULL REFERENCES hst_tags(h_maestro), + + -- Tipo de relación + relation_type VARCHAR(50) NOT NULL, -- synonym, related, opposite, child_of + strength DECIMAL(3,2) DEFAULT 1.0, -- Fuerza de la relación (0-1) + bidirectional BOOLEAN DEFAULT true, + + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_tag_relation UNIQUE(tag_a_h_maestro, tag_b_h_maestro, relation_type), + CONSTRAINT chk_different_tags CHECK (tag_a_h_maestro != tag_b_h_maestro) +); + +CREATE INDEX IF NOT EXISTS idx_tag_relations_a ON hst_tag_relations(tag_a_h_maestro); +CREATE INDEX IF NOT EXISTS idx_tag_relations_b ON hst_tag_relations(tag_b_h_maestro); +CREATE INDEX IF NOT EXISTS idx_tag_relations_type ON hst_tag_relations(relation_type); + +-- ============================================ +-- FUNCIÓN: Generar h_maestro (DETERMINISTA) +-- SHA-256(grupo || ':' || ref) +-- ============================================ + +CREATE OR REPLACE FUNCTION generate_h_maestro(p_grupo TEXT, p_ref TEXT) +RETURNS VARCHAR(64) AS $$ +BEGIN + -- DETERMINISTA: mismo input = mismo output SIEMPRE + RETURN encode(sha256((p_grupo || ':' || p_ref)::bytea), 'hex'); +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- ============================================ +-- FUNCIÓN: Generar imagen_url desde mrf +-- ============================================ + +CREATE OR REPLACE FUNCTION generate_imagen_url(p_mrf VARCHAR(64)) +RETURNS TEXT AS $$ +BEGIN + IF p_mrf IS NULL THEN + RETURN NULL; + END IF; + RETURN 'https://tzrtech.org/' || p_mrf || '.png'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- ============================================ +-- DATOS INICIALES: Categorías base +-- Nota: Los datos reales vienen de tzrtech.org/api/index.json +-- ============================================ + +INSERT INTO hst_tags (h_maestro, ref, nombre, nombre_en, grupo, categoria, color) VALUES + (generate_h_maestro('hst', 'trabajo'), 'trabajo', 'Trabajo', 'Work', 'hst', 'actividad', '#3498db'), + (generate_h_maestro('hst', 'personal'), 'personal', 'Personal', 'Personal', 'hst', 'actividad', '#2ecc71'), + (generate_h_maestro('hst', 'urgente'), 'urgente', 'Urgente', 'Urgent', 'hst', 'prioridad', '#e74c3c'), + (generate_h_maestro('hst', 'proyecto'), 'proyecto', 'Proyecto', 'Project', 'hst', 'organizacion', '#9b59b6'), + (generate_h_maestro('hst', 'factura'), 'factura', 'Factura', 'Invoice', 'hst', 'documento', '#f39c12'), + (generate_h_maestro('hst', 'contrato'), 'contrato', 'Contrato', 'Contract', 'hst', 'documento', '#1abc9c'), + (generate_h_maestro('hst', 'email'), 'email', 'Email', 'Email', 'hst', 'comunicacion', '#34495e'), + (generate_h_maestro('hst', 'reunion'), 'reunion', 'Reunión', 'Meeting', 'hst', 'evento', '#e67e22') +ON CONFLICT (h_maestro) DO NOTHING; + +-- ============================================ +-- VISTA: API JSON compatible con tzrtech.org +-- ============================================ + +CREATE OR REPLACE VIEW hst_api_index AS +SELECT + ref, + h_maestro, + mrf, + nombre AS nombre_es, + nombre_en, + grupo, + categoria, + color, + imagen_url, + padre_h_maestro, + rootref, + nivel, + activo +FROM hst_tags +WHERE activo = true +ORDER BY grupo, nivel, nombre; diff --git a/03_MODELO_DATOS/schemas/02_task_manager.sql b/03_MODELO_DATOS/schemas/02_task_manager.sql new file mode 100644 index 0000000..91f275a --- /dev/null +++ b/03_MODELO_DATOS/schemas/02_task_manager.sql @@ -0,0 +1,290 @@ +-- ============================================ +-- GESTOR DE TAREAS +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Requiere: 00_types.sql, 01_hst_tags.sql + +-- ============================================ +-- TABLA: task_projects +-- Proyectos que agrupan tareas +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_projects ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + codigo VARCHAR(50) UNIQUE NOT NULL, -- Código corto (ej: DECK-2024) + nombre VARCHAR(255) NOT NULL, + descripcion TEXT, + + -- Estado + activo BOOLEAN DEFAULT true, + archivado BOOLEAN DEFAULT false, + fecha_inicio DATE, + fecha_fin_estimada DATE, + fecha_fin_real DATE, + + -- Etiquetas (array de h_maestro) + tags JSONB DEFAULT '[]', + + -- Configuración de contexto por defecto + default_context_id UUID, -- FK a task_contexts (añadido después) + + -- Metadata + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_task_projects_codigo ON task_projects(codigo); +CREATE INDEX IF NOT EXISTS idx_task_projects_activo ON task_projects(activo); +CREATE INDEX IF NOT EXISTS idx_task_projects_tags ON task_projects USING GIN(tags); + +DROP TRIGGER IF EXISTS update_task_projects_updated_at ON task_projects; +CREATE TRIGGER update_task_projects_updated_at BEFORE UPDATE ON task_projects + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- TABLA: task_milestones +-- Hitos dentro de un proyecto +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_milestones ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + project_id UUID REFERENCES task_projects(id) ON DELETE CASCADE, + + codigo VARCHAR(50) NOT NULL, -- Código dentro del proyecto + nombre VARCHAR(255) NOT NULL, + descripcion TEXT, + + -- Orden y jerarquía + orden INTEGER DEFAULT 0, + parent_milestone_id UUID REFERENCES task_milestones(id), + + -- Fechas + fecha_objetivo DATE, + fecha_completado DATE, + + -- Progreso (calculado automáticamente) + progreso_percent DECIMAL(5,2) DEFAULT 0, + + -- Etiquetas + tags JSONB DEFAULT '[]', + + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_milestone_code UNIQUE(project_id, codigo) +); + +CREATE INDEX IF NOT EXISTS idx_task_milestones_project ON task_milestones(project_id); +CREATE INDEX IF NOT EXISTS idx_task_milestones_parent ON task_milestones(parent_milestone_id); +CREATE INDEX IF NOT EXISTS idx_task_milestones_orden ON task_milestones(orden); + +DROP TRIGGER IF EXISTS update_task_milestones_updated_at ON task_milestones; +CREATE TRIGGER update_task_milestones_updated_at BEFORE UPDATE ON task_milestones + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- TABLA: task_blocks +-- Bloques de trabajo dentro de milestones +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_blocks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + milestone_id UUID REFERENCES task_milestones(id) ON DELETE CASCADE, + + codigo VARCHAR(50) NOT NULL, + nombre VARCHAR(255) NOT NULL, + descripcion TEXT, + + -- Orden + orden INTEGER DEFAULT 0, + + -- Estimación + horas_estimadas DECIMAL(6,2), + horas_reales DECIMAL(6,2), + + -- Estado + status task_status DEFAULT 'pending', + + -- Etiquetas + tags JSONB DEFAULT '[]', + + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_block_code UNIQUE(milestone_id, codigo) +); + +CREATE INDEX IF NOT EXISTS idx_task_blocks_milestone ON task_blocks(milestone_id); +CREATE INDEX IF NOT EXISTS idx_task_blocks_status ON task_blocks(status); + +DROP TRIGGER IF EXISTS update_task_blocks_updated_at ON task_blocks; +CREATE TRIGGER update_task_blocks_updated_at BEFORE UPDATE ON task_blocks + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- TABLA: task_manager +-- Tareas individuales +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_manager ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Jerarquía + project_id UUID REFERENCES task_projects(id) ON DELETE SET NULL, + milestone_id UUID REFERENCES task_milestones(id) ON DELETE SET NULL, + block_id UUID REFERENCES task_blocks(id) ON DELETE SET NULL, + parent_task_id UUID REFERENCES task_manager(id) ON DELETE SET NULL, + + -- Identificación + codigo VARCHAR(100), -- Código único generado + titulo VARCHAR(500) NOT NULL, + descripcion TEXT, + + -- Estado y prioridad + status task_status DEFAULT 'pending', + priority task_priority DEFAULT 'medium', + + -- Fechas + fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + fecha_inicio DATE, + fecha_vencimiento DATE, + fecha_completado TIMESTAMP, + + -- Estimación + horas_estimadas DECIMAL(6,2), + horas_reales DECIMAL(6,2), + + -- Asignación + asignado_a VARCHAR(100), -- Usuario o servicio + creado_por VARCHAR(100) DEFAULT 'system', + + -- Etiquetas HST (separadas por grupo para queries eficientes) + tags_hst JSONB DEFAULT '[]', -- h_maestros de grupo hst + tags_hsu JSONB DEFAULT '[]', -- h_maestros de grupo hsu + tags_emp JSONB DEFAULT '[]', -- h_maestros de grupo emp + tags_pjt JSONB DEFAULT '[]', -- h_maestros de grupo pjt + + -- Contexto IA + context_id UUID, -- FK a task_contexts (añadido después) + + -- Archivos relacionados (array de UUIDs de work_log) + archivos_ref JSONB DEFAULT '[]', + + -- Trazabilidad S-CONTRACT + trace_id VARCHAR(64), + + -- Metadata + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_task_manager_project ON task_manager(project_id); +CREATE INDEX IF NOT EXISTS idx_task_manager_milestone ON task_manager(milestone_id); +CREATE INDEX IF NOT EXISTS idx_task_manager_block ON task_manager(block_id); +CREATE INDEX IF NOT EXISTS idx_task_manager_parent ON task_manager(parent_task_id); +CREATE INDEX IF NOT EXISTS idx_task_manager_status ON task_manager(status); +CREATE INDEX IF NOT EXISTS idx_task_manager_priority ON task_manager(priority); +CREATE INDEX IF NOT EXISTS idx_task_manager_fecha_venc ON task_manager(fecha_vencimiento); +CREATE INDEX IF NOT EXISTS idx_task_manager_asignado ON task_manager(asignado_a); +CREATE INDEX IF NOT EXISTS idx_task_manager_tags_hst ON task_manager USING GIN(tags_hst); +CREATE INDEX IF NOT EXISTS idx_task_manager_tags_hsu ON task_manager USING GIN(tags_hsu); +CREATE INDEX IF NOT EXISTS idx_task_manager_trace ON task_manager(trace_id); + +DROP TRIGGER IF EXISTS update_task_manager_updated_at ON task_manager; +CREATE TRIGGER update_task_manager_updated_at BEFORE UPDATE ON task_manager + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- TABLA: task_dependencies +-- Dependencias entre tareas +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_dependencies ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + task_id UUID NOT NULL REFERENCES task_manager(id) ON DELETE CASCADE, + depends_on_task_id UUID NOT NULL REFERENCES task_manager(id) ON DELETE CASCADE, + + -- Tipo de dependencia + tipo VARCHAR(50) DEFAULT 'finish_to_start', + -- finish_to_start: B empieza cuando A termina + -- start_to_start: B empieza cuando A empieza + -- finish_to_finish: B termina cuando A termina + -- start_to_finish: B termina cuando A empieza + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_dependency UNIQUE(task_id, depends_on_task_id), + CONSTRAINT chk_no_self_dep CHECK (task_id != depends_on_task_id) +); + +CREATE INDEX IF NOT EXISTS idx_task_deps_task ON task_dependencies(task_id); +CREATE INDEX IF NOT EXISTS idx_task_deps_depends ON task_dependencies(depends_on_task_id); + +-- ============================================ +-- FUNCIÓN: Actualizar progreso de milestone +-- ============================================ + +CREATE OR REPLACE FUNCTION update_milestone_progress() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE task_milestones + SET progreso_percent = ( + SELECT COALESCE( + (COUNT(*) FILTER (WHERE status = 'completed')::DECIMAL / + NULLIF(COUNT(*), 0) * 100), + 0 + ) + FROM task_manager + WHERE milestone_id = COALESCE(NEW.milestone_id, OLD.milestone_id) + ), + updated_at = CURRENT_TIMESTAMP + WHERE id = COALESCE(NEW.milestone_id, OLD.milestone_id); + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trigger_update_milestone_progress ON task_manager; +CREATE TRIGGER trigger_update_milestone_progress +AFTER INSERT OR UPDATE OF status OR DELETE ON task_manager +FOR EACH ROW EXECUTE FUNCTION update_milestone_progress(); + +-- ============================================ +-- FUNCIÓN: Generar código de tarea +-- ============================================ + +CREATE OR REPLACE FUNCTION generate_task_codigo() +RETURNS TRIGGER AS $$ +DECLARE + prefix VARCHAR(20); + seq INTEGER; +BEGIN + IF NEW.codigo IS NULL OR NEW.codigo = '' THEN + IF NEW.project_id IS NOT NULL THEN + SELECT codigo INTO prefix FROM task_projects WHERE id = NEW.project_id; + ELSE + prefix := 'TASK'; + END IF; + + SELECT COALESCE(MAX( + CAST(SUBSTRING(codigo FROM '[0-9]+$') AS INTEGER) + ), 0) + 1 + INTO seq + FROM task_manager + WHERE codigo LIKE prefix || '-%'; + + NEW.codigo := prefix || '-' || LPAD(seq::TEXT, 4, '0'); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trigger_generate_task_codigo ON task_manager; +CREATE TRIGGER trigger_generate_task_codigo +BEFORE INSERT ON task_manager +FOR EACH ROW EXECUTE FUNCTION generate_task_codigo(); diff --git a/03_MODELO_DATOS/schemas/03_work_log.sql b/03_MODELO_DATOS/schemas/03_work_log.sql new file mode 100644 index 0000000..bc00139 --- /dev/null +++ b/03_MODELO_DATOS/schemas/03_work_log.sql @@ -0,0 +1,168 @@ +-- ============================================ +-- LOG DE TRABAJO (Work Log) +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Requiere: 00_types.sql, 02_task_manager.sql + +-- ============================================ +-- TABLA: task_work_log +-- Registro de archivos entrantes/salientes +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_work_log ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Relación con tarea (opcional) + task_id UUID REFERENCES task_manager(id) ON DELETE SET NULL, + project_id UUID REFERENCES task_projects(id) ON DELETE SET NULL, + + -- Dirección + direction file_direction NOT NULL, + + -- Información del archivo + filename VARCHAR(500) NOT NULL, + filepath TEXT, -- Ruta en FileBrowser o URL + file_hash VARCHAR(64), -- SHA-256 del contenido + file_size_bytes BIGINT, + mime_type VARCHAR(100), + file_extension VARCHAR(20), + + -- Origen/Destino + source VARCHAR(255), -- email, upload, api, scrape, etc. + source_ref VARCHAR(500), -- Referencia específica (message_id, url, etc.) + destination VARCHAR(255), -- grace, penny, factory, export, storage + destination_ref VARCHAR(500), + + -- Procesamiento IA + processed_by_ia BOOLEAN DEFAULT false, + ai_service ai_service, + ai_module VARCHAR(50), -- Módulo específico (ASR_ENGINE, OCR_CORE, etc.) + ai_trace_id VARCHAR(64), -- trace_id del S-CONTRACT + ai_status VARCHAR(20), -- SUCCESS, ERROR, PARTIAL + ai_result_summary JSONB, -- Resumen del resultado + + -- Descripción + titulo VARCHAR(255), + descripcion TEXT, + + -- Etiquetas (h_maestros) + tags JSONB DEFAULT '[]', + + -- Metadata + metadata JSONB DEFAULT '{}', + + -- Timestamps + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + processed_at TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_work_log_task ON task_work_log(task_id); +CREATE INDEX IF NOT EXISTS idx_work_log_project ON task_work_log(project_id); +CREATE INDEX IF NOT EXISTS idx_work_log_direction ON task_work_log(direction); +CREATE INDEX IF NOT EXISTS idx_work_log_hash ON task_work_log(file_hash); +CREATE INDEX IF NOT EXISTS idx_work_log_source ON task_work_log(source); +CREATE INDEX IF NOT EXISTS idx_work_log_destination ON task_work_log(destination); +CREATE INDEX IF NOT EXISTS idx_work_log_ai_service ON task_work_log(ai_service); +CREATE INDEX IF NOT EXISTS idx_work_log_ai_trace ON task_work_log(ai_trace_id); +CREATE INDEX IF NOT EXISTS idx_work_log_fecha ON task_work_log(created_at); +CREATE INDEX IF NOT EXISTS idx_work_log_tags ON task_work_log USING GIN(tags); +CREATE INDEX IF NOT EXISTS idx_work_log_mime ON task_work_log(mime_type); + +-- ============================================ +-- TABLA: task_work_log_versions +-- Versiones de archivos (tracking de cambios) +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_work_log_versions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + work_log_id UUID NOT NULL REFERENCES task_work_log(id) ON DELETE CASCADE, + + version INTEGER NOT NULL, + filepath TEXT NOT NULL, + file_hash VARCHAR(64) NOT NULL, + file_size_bytes BIGINT, + + -- Cambio + change_type VARCHAR(50), -- created, modified, processed, exported + change_description TEXT, + changed_by VARCHAR(100), + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_version UNIQUE(work_log_id, version) +); + +CREATE INDEX IF NOT EXISTS idx_work_log_versions_log ON task_work_log_versions(work_log_id); +CREATE INDEX IF NOT EXISTS idx_work_log_versions_hash ON task_work_log_versions(file_hash); + +-- ============================================ +-- FUNCIÓN: Auto-incrementar versión +-- ============================================ + +CREATE OR REPLACE FUNCTION auto_version_work_log() +RETURNS TRIGGER AS $$ +DECLARE + next_version INTEGER; +BEGIN + SELECT COALESCE(MAX(version), 0) + 1 + INTO next_version + FROM task_work_log_versions + WHERE work_log_id = NEW.work_log_id; + + NEW.version := next_version; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trigger_auto_version_work_log ON task_work_log_versions; +CREATE TRIGGER trigger_auto_version_work_log +BEFORE INSERT ON task_work_log_versions +FOR EACH ROW EXECUTE FUNCTION auto_version_work_log(); + +-- ============================================ +-- VISTA: Work log reciente +-- ============================================ + +CREATE OR REPLACE VIEW v_work_log_recent AS +SELECT + w.id, + w.filename, + w.direction, + w.source, + w.destination, + w.ai_service, + w.ai_module, + w.ai_status, + w.processed_by_ia, + t.titulo AS tarea, + t.codigo AS tarea_codigo, + p.nombre AS proyecto, + p.codigo AS proyecto_codigo, + w.file_size_bytes, + w.mime_type, + w.created_at, + w.processed_at +FROM task_work_log w +LEFT JOIN task_manager t ON w.task_id = t.id +LEFT JOIN task_projects p ON w.project_id = p.id +ORDER BY w.created_at DESC; + +-- ============================================ +-- VISTA: Archivos pendientes de procesar +-- ============================================ + +CREATE OR REPLACE VIEW v_work_log_pending AS +SELECT + w.id, + w.filename, + w.mime_type, + w.file_size_bytes, + w.source, + w.direction, + p.nombre AS proyecto, + w.created_at +FROM task_work_log w +LEFT JOIN task_projects p ON w.project_id = p.id +WHERE w.processed_by_ia = false + AND w.direction IN ('inbound', 'internal') +ORDER BY w.created_at ASC; diff --git a/03_MODELO_DATOS/schemas/04_ai_context.sql b/03_MODELO_DATOS/schemas/04_ai_context.sql new file mode 100644 index 0000000..5cc4870 --- /dev/null +++ b/03_MODELO_DATOS/schemas/04_ai_context.sql @@ -0,0 +1,354 @@ +-- ============================================ +-- CONTEXTO PARA SERVICIOS IA +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Requiere: 00_types.sql, 01_hst_tags.sql, 02_task_manager.sql +-- +-- IMPORTANTE: Las estructuras aquí almacenadas corresponden +-- exactamente al bloque "context" del S-CONTRACT v2.1 +-- (schemas/s-contract-request.json) + +-- ============================================ +-- TABLA: s_contract_contexts +-- Contextos reutilizables en formato S-CONTRACT +-- ============================================ + +CREATE TABLE IF NOT EXISTS s_contract_contexts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Identificación + codigo VARCHAR(100) UNIQUE NOT NULL, + nombre VARCHAR(255) NOT NULL, + descripcion TEXT, + + -- ============================================ + -- BLOQUE context (S-CONTRACT v2.1) + -- Almacenado como JSONB para garantizar formato exacto + -- ============================================ + context JSONB NOT NULL DEFAULT '{ + "lang": "es", + "mode": "strict", + "pii_filter": false, + "bandera_id": null, + "player_id": null, + "method_hash": null, + "human_readable": null, + "system_instruction": null, + "datasets": [], + "tags": {"hst": [], "hsu": [], "emp": [], "pjt": []}, + "ambiente": {"timezone": "Europe/Madrid", "locale": "es-ES"} + }'::jsonb, + + -- ============================================ + -- BLOQUE deployment (S-CONTRACT v2.1) + -- Configuración de despliegue asociada + -- ============================================ + deployment JSONB DEFAULT '{ + "mode": "SEMI", + "tier_preference": ["SELF_HOSTED", "EXTERNAL"] + }'::jsonb, + + -- Alcance + scope VARCHAR(50) DEFAULT 'global', -- global, project, task + project_id UUID REFERENCES task_projects(id) ON DELETE SET NULL, + + -- Estado + activo BOOLEAN DEFAULT true, + + -- Metadata (campos no S-CONTRACT, solo para gestión interna) + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_codigo ON s_contract_contexts(codigo); +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_scope ON s_contract_contexts(scope); +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_project ON s_contract_contexts(project_id); +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_activo ON s_contract_contexts(activo); +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_context ON s_contract_contexts USING GIN(context); +CREATE INDEX IF NOT EXISTS idx_scontract_ctx_deployment ON s_contract_contexts USING GIN(deployment); + +DROP TRIGGER IF EXISTS update_scontract_ctx_updated_at ON s_contract_contexts; +CREATE TRIGGER update_scontract_ctx_updated_at BEFORE UPDATE ON s_contract_contexts + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- TABLA: s_contract_datasets +-- Datasets reutilizables (formato S-CONTRACT context.datasets[]) +-- ============================================ + +CREATE TABLE IF NOT EXISTS s_contract_datasets ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Campos que mapean a context.datasets[] en S-CONTRACT + codigo VARCHAR(100) UNIQUE NOT NULL, + tipo VARCHAR(50) NOT NULL, -- knowledge, examples, rules, vocabulary, context, persona + contenido TEXT, -- Contenido inline + contenido_ref TEXT, -- URI a contenido externo + + -- Campos adicionales de gestión (no van en S-CONTRACT) + nombre VARCHAR(255) NOT NULL, + descripcion TEXT, + version VARCHAR(20) DEFAULT '1.0', + scope VARCHAR(50) DEFAULT 'global', + project_id UUID REFERENCES task_projects(id) ON DELETE SET NULL, + activo BOOLEAN DEFAULT true, + + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT chk_dataset_tipo CHECK (tipo IN ('knowledge', 'examples', 'rules', 'vocabulary', 'context', 'persona')) +); + +CREATE INDEX IF NOT EXISTS idx_scontract_ds_codigo ON s_contract_datasets(codigo); +CREATE INDEX IF NOT EXISTS idx_scontract_ds_tipo ON s_contract_datasets(tipo); +CREATE INDEX IF NOT EXISTS idx_scontract_ds_activo ON s_contract_datasets(activo); + +DROP TRIGGER IF EXISTS update_scontract_ds_updated_at ON s_contract_datasets; +CREATE TRIGGER update_scontract_ds_updated_at BEFORE UPDATE ON s_contract_datasets + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- FUNCIÓN: Convertir dataset a formato S-CONTRACT +-- ============================================ + +CREATE OR REPLACE FUNCTION dataset_to_scontract(p_dataset_id UUID) +RETURNS JSONB AS $$ + SELECT jsonb_build_object( + 'codigo', codigo, + 'tipo', tipo, + 'contenido', contenido, + 'contenido_ref', contenido_ref + ) + FROM s_contract_datasets + WHERE id = p_dataset_id AND activo = true; +$$ LANGUAGE SQL STABLE; + +-- ============================================ +-- FUNCIÓN: Obtener context block para S-CONTRACT +-- Devuelve EXACTAMENTE el formato de context en S-CONTRACT +-- ============================================ + +CREATE OR REPLACE FUNCTION get_scontract_context(p_context_codigo VARCHAR) +RETURNS JSONB AS $$ + SELECT context + FROM s_contract_contexts + WHERE codigo = p_context_codigo AND activo = true; +$$ LANGUAGE SQL STABLE; + +-- ============================================ +-- FUNCIÓN: Obtener deployment block para S-CONTRACT +-- ============================================ + +CREATE OR REPLACE FUNCTION get_scontract_deployment(p_context_codigo VARCHAR) +RETURNS JSONB AS $$ + SELECT deployment + FROM s_contract_contexts + WHERE codigo = p_context_codigo AND activo = true; +$$ LANGUAGE SQL STABLE; + +-- ============================================ +-- FUNCIÓN: Construir context con datasets expandidos +-- Útil cuando se quiere incluir datasets por código +-- ============================================ + +CREATE OR REPLACE FUNCTION build_scontract_context( + p_context_codigo VARCHAR, + p_dataset_codigos VARCHAR[] DEFAULT NULL +) +RETURNS JSONB AS $$ +DECLARE + base_context JSONB; + datasets_array JSONB; +BEGIN + -- Obtener contexto base + SELECT context INTO base_context + FROM s_contract_contexts + WHERE codigo = p_context_codigo AND activo = true; + + IF base_context IS NULL THEN + RETURN NULL; + END IF; + + -- Si se especifican datasets, construir el array + IF p_dataset_codigos IS NOT NULL AND array_length(p_dataset_codigos, 1) > 0 THEN + SELECT jsonb_agg( + jsonb_build_object( + 'codigo', d.codigo, + 'tipo', d.tipo, + 'contenido', d.contenido, + 'contenido_ref', d.contenido_ref + ) + ) + INTO datasets_array + FROM s_contract_datasets d + WHERE d.codigo = ANY(p_dataset_codigos) + AND d.activo = true; + + -- Reemplazar datasets en el contexto + base_context := jsonb_set(base_context, '{datasets}', COALESCE(datasets_array, '[]'::jsonb)); + END IF; + + RETURN base_context; +END; +$$ LANGUAGE plpgsql STABLE; + +-- ============================================ +-- FUNCIÓN: Actualizar campo específico del context +-- ============================================ + +CREATE OR REPLACE FUNCTION update_scontract_context_field( + p_context_codigo VARCHAR, + p_field_path TEXT[], + p_value JSONB +) +RETURNS BOOLEAN AS $$ +BEGIN + UPDATE s_contract_contexts + SET context = jsonb_set(context, p_field_path, p_value), + updated_at = CURRENT_TIMESTAMP + WHERE codigo = p_context_codigo; + + RETURN FOUND; +END; +$$ LANGUAGE plpgsql; + +-- ============================================ +-- Añadir FK en task_manager +-- ============================================ + +-- Eliminar columna antigua si existe +ALTER TABLE task_manager DROP COLUMN IF EXISTS context_id; + +-- Añadir referencia al contexto S-CONTRACT +ALTER TABLE task_manager + ADD COLUMN IF NOT EXISTS scontract_context_codigo VARCHAR(100); + +-- Añadir FK en task_projects +ALTER TABLE task_projects DROP COLUMN IF EXISTS default_context_id; +ALTER TABLE task_projects + ADD COLUMN IF NOT EXISTS default_scontract_context VARCHAR(100); + +-- ============================================ +-- DATOS INICIALES +-- ============================================ + +-- Contexto por defecto +INSERT INTO s_contract_contexts (codigo, nombre, descripcion, context, deployment) VALUES +( + 'CTX_DEFAULT', + 'Contexto por defecto', + 'Contexto base para operaciones sin configuración específica', + '{ + "lang": "es", + "mode": "strict", + "pii_filter": false, + "bandera_id": null, + "player_id": null, + "method_hash": null, + "human_readable": null, + "system_instruction": "Procesa la información recibida de forma precisa. Responde en español.", + "datasets": [], + "tags": {"hst": [], "hsu": [], "emp": [], "pjt": []}, + "ambiente": { + "timezone": "Europe/Madrid", + "locale": "es-ES", + "currency": "EUR" + } + }'::jsonb, + '{ + "mode": "SEMI", + "tier_preference": ["SELF_HOSTED", "EXTERNAL"] + }'::jsonb +), +( + 'CTX_GRACE', + 'Contexto para GRACE', + 'Contexto optimizado para procesamiento GRACE', + '{ + "lang": "es", + "mode": "strict", + "pii_filter": false, + "system_instruction": "Eres un asistente de procesamiento del sistema GRACE. Procesa los datos de forma estructurada y precisa. Si hay incertidumbre, indica el nivel de confianza.", + "datasets": [], + "tags": {"hst": [], "hsu": [], "emp": [], "pjt": []}, + "ambiente": { + "timezone": "Europe/Madrid", + "locale": "es-ES" + } + }'::jsonb, + '{"mode": "SEMI", "tier_preference": ["SELF_HOSTED", "EXTERNAL"]}'::jsonb +), +( + 'CTX_PENNY', + 'Contexto para PENNY', + 'Contexto optimizado para asistente de voz PENNY', + '{ + "lang": "es", + "mode": "lenient", + "pii_filter": false, + "system_instruction": "Eres PENNY, un asistente de voz amable y eficiente. Responde de forma concisa (máximo 2-3 oraciones). Usa tono conversacional pero profesional.", + "datasets": [], + "tags": {"hst": [], "hsu": [], "emp": [], "pjt": []}, + "ambiente": { + "timezone": "Europe/Madrid", + "locale": "es-ES", + "session_type": "interactive" + } + }'::jsonb, + '{"mode": "SEMI", "tier_preference": ["SELF_HOSTED", "EXTERNAL"]}'::jsonb +), +( + 'CTX_FACTORY', + 'Contexto para THE FACTORY', + 'Contexto optimizado para procesamiento documental', + '{ + "lang": "es", + "mode": "strict", + "pii_filter": true, + "system_instruction": "Eres un procesador de documentos de THE FACTORY. Extrae información de forma estructurada. Mantén fidelidad al documento original. Señala incertidumbres con [INCIERTO: razón].", + "datasets": [], + "tags": {"hst": [], "hsu": [], "emp": [], "pjt": []}, + "ambiente": { + "timezone": "Europe/Madrid", + "locale": "es-ES", + "session_type": "batch" + } + }'::jsonb, + '{"mode": "SEMI", "tier_preference": ["EXTERNAL", "SELF_HOSTED"]}'::jsonb +) +ON CONFLICT (codigo) DO UPDATE SET + context = EXCLUDED.context, + deployment = EXCLUDED.deployment, + updated_at = CURRENT_TIMESTAMP; + +-- Datasets base +INSERT INTO s_contract_datasets (codigo, nombre, tipo, contenido) VALUES +( + 'DS_TZZR_ECOSYSTEM', + 'Ecosistema TZZR', + 'knowledge', + 'TZZR es un ecosistema personal de productividad: +- DECK: Servidor central, iniciador de conexiones +- GRACE: Capa de procesamiento IA +- PENNY: Asistente de voz real-time +- THE FACTORY: Procesamiento documental + +Sistema de etiquetas HST con h_maestro (SHA-256) como ID único.' +), +( + 'DS_HST_INTRO', + 'Introducción HST', + 'vocabulary', + 'Grupos de etiquetas HST: +- hst: Sistema (sync tzrtech.org) +- emp: Empresa +- hsu: Usuario +- pjt: Proyecto + +Cada etiqueta tiene: codigo, nombre, descripcion, color, jerarquía.' +) +ON CONFLICT (codigo) DO UPDATE SET + contenido = EXCLUDED.contenido, + updated_at = CURRENT_TIMESTAMP; diff --git a/03_MODELO_DATOS/schemas/05_ai_requests.sql b/03_MODELO_DATOS/schemas/05_ai_requests.sql new file mode 100644 index 0000000..cc19d92 --- /dev/null +++ b/03_MODELO_DATOS/schemas/05_ai_requests.sql @@ -0,0 +1,277 @@ +-- ============================================ +-- LOG DE REQUESTS A SERVICIOS IA +-- Sistema TZZR - contratos-comunes +-- ============================================ +-- Requiere: 00_types.sql, 02_task_manager.sql, 03_work_log.sql, 04_ai_context.sql + +-- ============================================ +-- TABLA: task_ai_requests +-- Registro de todas las requests a servicios IA +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_ai_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Trazabilidad S-CONTRACT + trace_id VARCHAR(64) NOT NULL, -- trace_id del S-CONTRACT + idempotency_key VARCHAR(64), -- Para deduplicación + step_id VARCHAR(64), -- ID del paso en cadena + step_index INTEGER, -- Posición en la cadena + + -- Origen + task_id UUID REFERENCES task_manager(id) ON DELETE SET NULL, + work_log_id UUID REFERENCES task_work_log(id) ON DELETE SET NULL, + project_id UUID REFERENCES task_projects(id) ON DELETE SET NULL, + + -- Destino + target_service ai_service NOT NULL, + target_module VARCHAR(50) NOT NULL, -- ASR_ENGINE, CLASSIFIER, OCR_CORE, etc. + + -- Contexto usado + context_id UUID REFERENCES task_contexts(id) ON DELETE SET NULL, + context_snapshot JSONB, -- Snapshot del contexto en el momento + + -- Deployment + deployment_mode deployment_mode, + tier_requested provider_tier, + tier_used provider_tier, + provider_used VARCHAR(50), + endpoint_used TEXT, + + -- Request (sin contenido sensible) + request_summary JSONB, -- Resumen de la request + payload_type VARCHAR(50), -- text, audio, image, document + payload_size_bytes BIGINT, + payload_hash VARCHAR(64), + + -- Response + status VARCHAR(20) NOT NULL, -- SUCCESS, PARTIAL, ERROR, TIMEOUT, FALLBACK + fallback_level INTEGER DEFAULT 0, + response_summary JSONB, -- Resumen de respuesta + output_schema VARCHAR(100), + output_hash VARCHAR(64), + + -- Calidad + confidence DECIMAL(4,3), + coverage DECIMAL(4,3), + validation_passed BOOLEAN, + + -- Métricas + latency_ms INTEGER, + queue_wait_ms INTEGER, + tokens_input INTEGER, + tokens_output INTEGER, + cost_usd DECIMAL(10,6), + + -- Errores + error_code VARCHAR(50), + error_message TEXT, + retry_count INTEGER DEFAULT 0, + + -- Timestamps + timestamp_init TIMESTAMP NOT NULL, + timestamp_end TIMESTAMP, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Índices principales +CREATE INDEX IF NOT EXISTS idx_ai_requests_trace ON task_ai_requests(trace_id); +CREATE INDEX IF NOT EXISTS idx_ai_requests_idempotency ON task_ai_requests(idempotency_key); +CREATE INDEX IF NOT EXISTS idx_ai_requests_task ON task_ai_requests(task_id); +CREATE INDEX IF NOT EXISTS idx_ai_requests_work_log ON task_ai_requests(work_log_id); +CREATE INDEX IF NOT EXISTS idx_ai_requests_project ON task_ai_requests(project_id); +CREATE INDEX IF NOT EXISTS idx_ai_requests_service ON task_ai_requests(target_service); +CREATE INDEX IF NOT EXISTS idx_ai_requests_module ON task_ai_requests(target_module); +CREATE INDEX IF NOT EXISTS idx_ai_requests_status ON task_ai_requests(status); +CREATE INDEX IF NOT EXISTS idx_ai_requests_fecha ON task_ai_requests(timestamp_init); +CREATE INDEX IF NOT EXISTS idx_ai_requests_tier ON task_ai_requests(tier_used); +CREATE INDEX IF NOT EXISTS idx_ai_requests_provider ON task_ai_requests(provider_used); + +-- Índice para métricas temporales +CREATE INDEX IF NOT EXISTS idx_ai_requests_fecha_service ON task_ai_requests(timestamp_init, target_service); + +-- ============================================ +-- TABLA: task_ai_request_chain +-- Cadenas de requests relacionadas +-- ============================================ + +CREATE TABLE IF NOT EXISTS task_ai_request_chain ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Trace principal de la cadena + chain_trace_id VARCHAR(64) NOT NULL, + + -- Request en la cadena + request_id UUID NOT NULL REFERENCES task_ai_requests(id) ON DELETE CASCADE, + + -- Posición + chain_index INTEGER NOT NULL, + + -- Dependencias (requests previas necesarias) + depends_on JSONB DEFAULT '[]', -- Array de request_ids + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_chain_position UNIQUE(chain_trace_id, chain_index) +); + +CREATE INDEX IF NOT EXISTS idx_request_chain_trace ON task_ai_request_chain(chain_trace_id); +CREATE INDEX IF NOT EXISTS idx_request_chain_request ON task_ai_request_chain(request_id); + +-- ============================================ +-- VISTAS DE MÉTRICAS +-- ============================================ + +-- Vista: Métricas por servicio (últimos 30 días) +CREATE OR REPLACE VIEW v_ai_metrics_by_service AS +SELECT + target_service, + target_module, + COUNT(*) AS total_requests, + COUNT(*) FILTER (WHERE status = 'SUCCESS') AS successful, + COUNT(*) FILTER (WHERE status = 'ERROR') AS errors, + COUNT(*) FILTER (WHERE status = 'FALLBACK') AS fallbacks, + ROUND(AVG(latency_ms)::numeric, 2) AS avg_latency_ms, + ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY latency_ms)::numeric, 2) AS p95_latency_ms, + SUM(cost_usd) AS total_cost_usd, + SUM(tokens_input) AS total_tokens_in, + SUM(tokens_output) AS total_tokens_out, + ROUND(AVG(confidence)::numeric, 3) AS avg_confidence +FROM task_ai_requests +WHERE timestamp_init > CURRENT_DATE - INTERVAL '30 days' +GROUP BY target_service, target_module +ORDER BY total_requests DESC; + +-- Vista: Métricas por tier +CREATE OR REPLACE VIEW v_ai_metrics_by_tier AS +SELECT + tier_used, + provider_used, + COUNT(*) AS total_requests, + COUNT(*) FILTER (WHERE status = 'SUCCESS') AS successful, + ROUND(AVG(latency_ms)::numeric, 2) AS avg_latency_ms, + SUM(cost_usd) AS total_cost_usd +FROM task_ai_requests +WHERE timestamp_init > CURRENT_DATE - INTERVAL '30 days' + AND tier_used IS NOT NULL +GROUP BY tier_used, provider_used +ORDER BY total_requests DESC; + +-- Vista: Costos diarios +CREATE OR REPLACE VIEW v_ai_daily_costs AS +SELECT + DATE(timestamp_init) AS fecha, + target_service, + COUNT(*) AS requests, + SUM(cost_usd) AS cost_usd, + SUM(tokens_input) AS tokens_in, + SUM(tokens_output) AS tokens_out +FROM task_ai_requests +WHERE timestamp_init > CURRENT_DATE - INTERVAL '30 days' +GROUP BY DATE(timestamp_init), target_service +ORDER BY fecha DESC, cost_usd DESC; + +-- Vista: Errores recientes +CREATE OR REPLACE VIEW v_ai_recent_errors AS +SELECT + id, + trace_id, + target_service, + target_module, + status, + error_code, + error_message, + provider_used, + tier_used, + timestamp_init +FROM task_ai_requests +WHERE status IN ('ERROR', 'TIMEOUT') + AND timestamp_init > CURRENT_DATE - INTERVAL '7 days' +ORDER BY timestamp_init DESC +LIMIT 100; + +-- ============================================ +-- FUNCIÓN: Registrar request de S-CONTRACT +-- ============================================ + +CREATE OR REPLACE FUNCTION log_ai_request( + p_trace_id VARCHAR(64), + p_service ai_service, + p_module VARCHAR(50), + p_context_id UUID DEFAULT NULL, + p_task_id UUID DEFAULT NULL, + p_work_log_id UUID DEFAULT NULL +) +RETURNS UUID AS $$ +DECLARE + new_id UUID; + ctx_snapshot JSONB; +BEGIN + -- Obtener snapshot del contexto si existe + IF p_context_id IS NOT NULL THEN + ctx_snapshot := build_ai_context(p_context_id); + END IF; + + INSERT INTO task_ai_requests ( + trace_id, + target_service, + target_module, + context_id, + context_snapshot, + task_id, + work_log_id, + status, + timestamp_init + ) VALUES ( + p_trace_id, + p_service, + p_module, + p_context_id, + ctx_snapshot, + p_task_id, + p_work_log_id, + 'pending', + CURRENT_TIMESTAMP + ) + RETURNING id INTO new_id; + + RETURN new_id; +END; +$$ LANGUAGE plpgsql; + +-- ============================================ +-- FUNCIÓN: Actualizar request completada +-- ============================================ + +CREATE OR REPLACE FUNCTION complete_ai_request( + p_request_id UUID, + p_status VARCHAR(20), + p_tier_used provider_tier, + p_provider VARCHAR(50), + p_latency_ms INTEGER, + p_tokens_in INTEGER DEFAULT NULL, + p_tokens_out INTEGER DEFAULT NULL, + p_cost_usd DECIMAL(10,6) DEFAULT NULL, + p_confidence DECIMAL(4,3) DEFAULT NULL, + p_error_code VARCHAR(50) DEFAULT NULL, + p_error_message TEXT DEFAULT NULL +) +RETURNS VOID AS $$ +BEGIN + UPDATE task_ai_requests + SET + status = p_status, + tier_used = p_tier_used, + provider_used = p_provider, + latency_ms = p_latency_ms, + tokens_input = p_tokens_in, + tokens_output = p_tokens_out, + cost_usd = p_cost_usd, + confidence = p_confidence, + error_code = p_error_code, + error_message = p_error_message, + timestamp_end = CURRENT_TIMESTAMP + WHERE id = p_request_id; +END; +$$ LANGUAGE plpgsql; diff --git a/03_MODELO_DATOS/schemas/06_clara.sql b/03_MODELO_DATOS/schemas/06_clara.sql new file mode 100644 index 0000000..98be0e8 --- /dev/null +++ b/03_MODELO_DATOS/schemas/06_clara.sql @@ -0,0 +1,50 @@ +-- Inicialización de la base de datos para CLARA +-- Servicio inmutable de log de entrada + +-- Tabla principal: clara_log +CREATE TABLE IF NOT EXISTS clara_log ( + id BIGSERIAL PRIMARY KEY, + h_instancia VARCHAR(64) NOT NULL, + h_entrada VARCHAR(64) NOT NULL, + contenedor JSONB NOT NULL, + r2_paths JSONB, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Índices para búsqueda eficiente +CREATE INDEX IF NOT EXISTS idx_clara_instancia ON clara_log(h_instancia); +CREATE INDEX IF NOT EXISTS idx_clara_entrada ON clara_log(h_entrada); +CREATE INDEX IF NOT EXISTS idx_clara_created ON clara_log(created_at DESC); + +-- Índice para buscar por estado del contenedor +CREATE INDEX IF NOT EXISTS idx_clara_estado ON clara_log((contenedor->'estado'->>'actual')); + +-- Índice para buscar por timestamp de captura +CREATE INDEX IF NOT EXISTS idx_clara_timestamp ON clara_log((contenedor->'origen'->>'timestamp_captura')); + +-- Índice compuesto para búsquedas frecuentes +CREATE INDEX IF NOT EXISTS idx_clara_inst_entrada ON clara_log(h_instancia, h_entrada); + +-- Vista para consultas comunes +CREATE OR REPLACE VIEW clara_summary AS +SELECT + id, + h_instancia, + h_entrada, + contenedor->>'id' as contenedor_id, + contenedor->'origen'->>'timestamp_captura' as timestamp_captura, + contenedor->'archivo'->>'tipo' as tipo_archivo, + contenedor->'archivo'->>'categoria' as categoria, + contenedor->'estado'->>'actual' as estado_actual, + jsonb_array_length(COALESCE(contenedor->'tags', '[]'::jsonb)) as num_tags, + created_at +FROM clara_log +ORDER BY id DESC; + +-- Comentarios para documentación +COMMENT ON TABLE clara_log IS 'Log inmutable de contenedores recibidos de PACKET'; +COMMENT ON COLUMN clara_log.h_instancia IS 'Hash de identificación de la instancia DECK'; +COMMENT ON COLUMN clara_log.h_entrada IS 'Hash del archivo (sha256)'; +COMMENT ON COLUMN clara_log.contenedor IS 'Contenedor completo según esquema de contratos-comunes'; +COMMENT ON COLUMN clara_log.r2_paths IS 'Rutas de los archivos en Cloudflare R2'; +COMMENT ON COLUMN clara_log.created_at IS 'Timestamp de recepción (inmutable)'; diff --git a/03_MODELO_DATOS/schemas/07_feldman.sql b/03_MODELO_DATOS/schemas/07_feldman.sql new file mode 100644 index 0000000..2a63c54 --- /dev/null +++ b/03_MODELO_DATOS/schemas/07_feldman.sql @@ -0,0 +1,123 @@ +-- Los Libros Contables - FELDMAN v2.0 +-- Tablas para milestones, bloques y validaciones + +-- MILESTONES +CREATE TABLE IF NOT EXISTS milestones ( + id BIGSERIAL PRIMARY KEY, + h_milestone VARCHAR(64) NOT NULL UNIQUE, + h_instancia VARCHAR(64) NOT NULL, + secuencia BIGINT NOT NULL, + hash_previo VARCHAR(64), + hash_contenido VARCHAR(64) NOT NULL, + alias VARCHAR(200) NOT NULL, + tipo_item VARCHAR(50) NOT NULL, + descripcion TEXT, + datos JSONB DEFAULT '{}', + etiqueta_principal VARCHAR(64), + proyecto_tag VARCHAR(64), + id_padre_milestone BIGINT REFERENCES milestones(id), + id_bloque_asociado BIGINT, + blockchain_pending BOOLEAN DEFAULT TRUE, + blockchain_tx_ref VARCHAR(128), + notario_batch_id VARCHAR(64), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by VARCHAR(64), + CONSTRAINT milestone_secuencia_unica UNIQUE (h_instancia, secuencia) +); + +-- BLOQUES +CREATE TABLE IF NOT EXISTS bloques ( + id BIGSERIAL PRIMARY KEY, + h_bloque VARCHAR(64) NOT NULL UNIQUE, + h_instancia VARCHAR(64) NOT NULL, + secuencia BIGINT NOT NULL, + hash_previo VARCHAR(64), + hash_contenido VARCHAR(64) NOT NULL, + alias VARCHAR(200) NOT NULL, + tipo_accion VARCHAR(50) NOT NULL, + descripcion TEXT, + datos JSONB DEFAULT '{}', + evidencia_hash VARCHAR(64) NOT NULL, + evidencia_url VARCHAR(500) NOT NULL, + evidencia_tipo VARCHAR(50) NOT NULL, + etiqueta_principal VARCHAR(64), + proyecto_tag VARCHAR(64), + id_padre_bloque BIGINT REFERENCES bloques(id), + id_milestone_asociado BIGINT REFERENCES milestones(id), + blockchain_pending BOOLEAN DEFAULT TRUE, + blockchain_tx_ref VARCHAR(128), + notario_batch_id VARCHAR(64), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by VARCHAR(64), + CONSTRAINT bloque_secuencia_unica UNIQUE (h_instancia, secuencia) +); + +-- COLA DE VALIDACION +CREATE TABLE IF NOT EXISTS feldman_cola ( + id BIGSERIAL PRIMARY KEY, + h_entrada VARCHAR(64) NOT NULL UNIQUE, + h_instancia VARCHAR(64) NOT NULL, + origen VARCHAR(50) NOT NULL, + h_origen VARCHAR(64), + tipo_destino VARCHAR(20) NOT NULL, + datos JSONB NOT NULL, + estado VARCHAR(20) DEFAULT 'pendiente', + error_mensaje TEXT, + intentos INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + processed_at TIMESTAMP +); + +-- VALIDACIONES +CREATE TABLE IF NOT EXISTS feldman_validaciones ( + id BIGSERIAL PRIMARY KEY, + h_entrada VARCHAR(64) NOT NULL, + validacion_ok BOOLEAN NOT NULL, + reglas_aplicadas JSONB NOT NULL, + tipo_registro VARCHAR(20), + h_registro VARCHAR(64), + validated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- INDICES +CREATE INDEX IF NOT EXISTS idx_milestones_pending ON milestones(blockchain_pending) WHERE blockchain_pending = TRUE; +CREATE INDEX IF NOT EXISTS idx_bloques_pending ON bloques(blockchain_pending) WHERE blockchain_pending = TRUE; +CREATE INDEX IF NOT EXISTS idx_milestones_proyecto ON milestones(proyecto_tag); +CREATE INDEX IF NOT EXISTS idx_bloques_proyecto ON bloques(proyecto_tag); +CREATE INDEX IF NOT EXISTS idx_feldman_estado ON feldman_cola(estado); +CREATE INDEX IF NOT EXISTS idx_feldman_instancia ON feldman_cola(h_instancia); + +-- FUNCIONES +CREATE OR REPLACE FUNCTION get_ultimo_hash_milestone(p_h_instancia VARCHAR) +RETURNS VARCHAR AS $$ +DECLARE v_hash VARCHAR; +BEGIN + SELECT hash_contenido INTO v_hash FROM milestones + WHERE h_instancia = p_h_instancia ORDER BY secuencia DESC LIMIT 1; + RETURN COALESCE(v_hash, 'GENESIS'); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION get_ultimo_hash_bloque(p_h_instancia VARCHAR) +RETURNS VARCHAR AS $$ +DECLARE v_hash VARCHAR; +BEGIN + SELECT hash_contenido INTO v_hash FROM bloques + WHERE h_instancia = p_h_instancia ORDER BY secuencia DESC LIMIT 1; + RETURN COALESCE(v_hash, 'GENESIS'); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION get_siguiente_secuencia_milestone(p_h_instancia VARCHAR) +RETURNS BIGINT AS $$ +BEGIN + RETURN (SELECT COALESCE(MAX(secuencia), 0) + 1 FROM milestones WHERE h_instancia = p_h_instancia); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION get_siguiente_secuencia_bloque(p_h_instancia VARCHAR) +RETURNS BIGINT AS $$ +BEGIN + RETURN (SELECT COALESCE(MAX(secuencia), 0) + 1 FROM bloques WHERE h_instancia = p_h_instancia); +END; +$$ LANGUAGE plpgsql; diff --git a/03_MODELO_DATOS/schemas/08_alfred.sql b/03_MODELO_DATOS/schemas/08_alfred.sql new file mode 100644 index 0000000..41ccce6 --- /dev/null +++ b/03_MODELO_DATOS/schemas/08_alfred.sql @@ -0,0 +1,36 @@ +-- ALFRED - Flujos Predefinidos +-- Deploy en DECK PostgreSQL + +-- Flujos predefinidos +CREATE TABLE IF NOT EXISTS flujos_predefinidos ( + id VARCHAR(64) PRIMARY KEY, + h_instancia VARCHAR(64) NOT NULL, + nombre VARCHAR(100) NOT NULL, + descripcion TEXT, + pasos JSONB NOT NULL, + campos_fijos JSONB DEFAULT '{}', + campos_variables JSONB DEFAULT '[]', + activo BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Ejecuciones de flujos +CREATE TABLE IF NOT EXISTS flujo_ejecuciones ( + id BIGSERIAL PRIMARY KEY, + h_flujo VARCHAR(64) REFERENCES flujos_predefinidos(id), + h_instancia VARCHAR(64) NOT NULL, + h_ejecucion VARCHAR(64) NOT NULL UNIQUE, + datos JSONB NOT NULL, + estado VARCHAR(20) DEFAULT 'ok', + destino VARCHAR(20) DEFAULT 'feldman', + notas TEXT, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Indices +CREATE INDEX IF NOT EXISTS idx_flujos_h_instancia ON flujos_predefinidos(h_instancia); +CREATE INDEX IF NOT EXISTS idx_flujos_activo ON flujos_predefinidos(activo); +CREATE INDEX IF NOT EXISTS idx_ejecuciones_h_flujo ON flujo_ejecuciones(h_flujo); +CREATE INDEX IF NOT EXISTS idx_ejecuciones_estado ON flujo_ejecuciones(estado); +CREATE INDEX IF NOT EXISTS idx_ejecuciones_created ON flujo_ejecuciones(created_at DESC); diff --git a/03_MODELO_DATOS/schemas/README.md b/03_MODELO_DATOS/schemas/README.md new file mode 100644 index 0000000..90ae737 --- /dev/null +++ b/03_MODELO_DATOS/schemas/README.md @@ -0,0 +1,82 @@ +# Schemas SQL + +**Versión:** 1.0 +**Estado:** Implementado + +--- + +## Archivos + +| Archivo | Descripción | Tablas | +|---------|-------------|--------| +| 00_types.sql | Tipos y enums | task_status, task_priority, file_direction, ai_service, hst_grupo, deployment_mode | +| 01_hst_tags.sql | Tags semánticos | hst_tags, hst_biblioteca | +| 02_task_manager.sql | Gestión de tareas | tasks, task_dependencies, task_comments | +| 03_work_log.sql | Log de trabajo | work_log, work_log_files | +| 04_ai_context.sql | Contexto IA | ai_context_blocks, ai_context_datasets | +| 05_ai_requests.sql | Peticiones IA | ai_requests, ai_responses | +| 06_clara.sql | Clara (secretaría) | clara_log, clara_summary (vista) | +| 07_feldman.sql | Feldman (contable) | milestones, bloques, feldman_cola, feldman_validaciones | +| 08_alfred.sql | Alfred (producción) | flujos_predefinidos, flujo_ejecuciones | + +--- + +## Orden de Aplicación + +```bash +# Aplicar en este orden +psql -U tzzr -d tzzr -f 00_types.sql +psql -U tzzr -d tzzr -f 01_hst_tags.sql +psql -U tzzr -d tzzr -f 02_task_manager.sql +psql -U tzzr -d tzzr -f 03_work_log.sql +psql -U tzzr -d tzzr -f 04_ai_context.sql +psql -U tzzr -d tzzr -f 05_ai_requests.sql +psql -U tzzr -d tzzr -f 06_clara.sql +psql -U tzzr -d tzzr -f 07_feldman.sql +psql -U tzzr -d tzzr -f 08_alfred.sql +``` + +--- + +## Tipos Enumerados (00_types.sql) + +```sql +-- Estados de tarea +CREATE TYPE task_status AS ENUM ( + 'draft', 'pending', 'in_progress', + 'blocked', 'review', 'completed', 'cancelled' +); + +-- Prioridad de tarea +CREATE TYPE task_priority AS ENUM ( + 'critical', 'high', 'medium', 'low', 'someday' +); + +-- Dirección de archivo +CREATE TYPE file_direction AS ENUM ( + 'inbound', 'outbound', 'internal', 'reference' +); + +-- Servicios IA +CREATE TYPE ai_service AS ENUM ( + 'grace', 'penny', 'factory' +); + +-- Grupos HST +CREATE TYPE hst_grupo AS ENUM ( + 'hst', 'emp', 'hsu', 'pjt' +); + +-- Modo despliegue +CREATE TYPE deployment_mode AS ENUM ( + 'EXTERNAL', 'SELF_HOSTED', 'SEMI' +); +``` + +--- + +## Notas + +- Todos los schemas usan `DO $$ BEGIN ... EXCEPTION ... END $$` para ser idempotentes +- Función `update_updated_at_column()` actualiza `updated_at` automáticamente +- Los SQL están en `/schemas/` junto a este README diff --git a/04_INFRAESTRUCTURA/architect.md b/04_INFRAESTRUCTURA/architect.md new file mode 100644 index 0000000..2bc7597 --- /dev/null +++ b/04_INFRAESTRUCTURA/architect.md @@ -0,0 +1,83 @@ +# ARCHITECT + +**Tipo:** Servidor Central +**Estado:** Operativo + +--- + +## Descripción + +Coordinador central del ecosistema. Contiene servicios compartidos y actúa como hub de desarrollo y despliegue. + +--- + +## Servicios + +| Servicio | Puerto | Función | +|----------|--------|---------| +| PostgreSQL | 5432 | Base de datos central | +| Gitea | 3000 | Repositorios Git | +| Infisical | 8082 | Gestión de secretos | +| Orchestrator | 5000 | Coordinación multi-agente | + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ ARCHITECT │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ PostgreSQL │ │ Gitea │ │ Infisical │ │ +│ │ (datos) │ │ (repos) │ │ (secretos) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ Orchestrator│ │ Backups │ │ +│ │ (agentes) │ │ (R2) │ │ +│ └─────────────┘ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Repositorios Gitea + +| Categoría | Repos | +|-----------|-------| +| **tzzr/** | system-docs, architect, deck, corp, hst, etc. | +| **admin/** | architect-app, data-structures, credentials | + +--- + +## Infisical + +Gestor centralizado de secretos. + +| Parámetro | Valor | +|-----------|-------| +| Proyectos | anthropic, servers, databases, r2 | +| Acceso | Machine Identities por cada instancia | + +--- + +## Función + +ARCHITECT es un **constructor de arquitecturas**, no un gestor permanente: + +1. Diseña y construye la arquitectura de cada servidor +2. Despliega configuraciones +3. Mantiene repositorios +4. Las instancias (DECK, CORP) funcionan independientemente + +--- + +## Backups + +| Destino | Frecuencia | Contenido | +|---------|------------|-----------| +| R2 (architect) | Diario | PostgreSQL, Gitea | +| R2 (gitea) | Por commit | Repos bare | diff --git a/04_INFRAESTRUCTURA/backup-recovery.md b/04_INFRAESTRUCTURA/backup-recovery.md new file mode 100644 index 0000000..a1adf14 --- /dev/null +++ b/04_INFRAESTRUCTURA/backup-recovery.md @@ -0,0 +1,131 @@ +# Backup y Recovery + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Estrategia + +| Tipo | Frecuencia | Retención | Destino | +|------|------------|-----------|---------| +| **PostgreSQL** | Diario | 30 días | R2 | +| **Gitea repos** | Por commit | Indefinido | R2 | +| **Archivos** | Diario | 30 días | R2 | +| **Configuración** | Por cambio | Indefinido | Gitea | + +--- + +## Backup PostgreSQL + +### Script + +```bash +#!/bin/bash +# backup-postgres.sh + +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_FILE="postgres_backup_${DATE}.sql.gz" + +# Dump +pg_dump -U $DB_USER -h $DB_HOST $DB_NAME | gzip > /tmp/$BACKUP_FILE + +# Upload a R2 +aws s3 cp /tmp/$BACKUP_FILE s3://$R2_BUCKET/backups/postgres/$BACKUP_FILE \ + --endpoint-url $R2_ENDPOINT + +# Limpiar local +rm /tmp/$BACKUP_FILE + +# Limpiar antiguos (>30 días) +aws s3 ls s3://$R2_BUCKET/backups/postgres/ --endpoint-url $R2_ENDPOINT | \ + while read -r line; do + createDate=$(echo $line | awk '{print $1}') + if [[ $(date -d "$createDate" +%s) -lt $(date -d "30 days ago" +%s) ]]; then + fileName=$(echo $line | awk '{print $4}') + aws s3 rm s3://$R2_BUCKET/backups/postgres/$fileName --endpoint-url $R2_ENDPOINT + fi + done +``` + +### Cron + +```bash +0 3 * * * /opt/scripts/backup-postgres.sh +``` + +--- + +## Recovery PostgreSQL + +```bash +# Descargar backup +aws s3 cp s3://$R2_BUCKET/backups/postgres/$BACKUP_FILE /tmp/ \ + --endpoint-url $R2_ENDPOINT + +# Restaurar +gunzip -c /tmp/$BACKUP_FILE | psql -U $DB_USER -h $DB_HOST $DB_NAME +``` + +--- + +## Backup Gitea + +Los repositorios bare se sincronizan automáticamente a R2: + +```bash +#!/bin/bash +# backup-gitea.sh + +rsync -av /var/lib/gitea/git/repositories/ /tmp/gitea_repos/ + +tar -czf /tmp/gitea_backup_$(date +%Y%m%d).tar.gz -C /tmp gitea_repos/ + +aws s3 cp /tmp/gitea_backup_*.tar.gz s3://$R2_BUCKET/backups/gitea/ \ + --endpoint-url $R2_ENDPOINT +``` + +--- + +## Disaster Recovery + +### Escenario: Pérdida total del servidor + +1. Aprovisionar nuevo servidor +2. Instalar Docker +3. Clonar repos de configuración desde R2/Gitea backup +4. Restaurar PostgreSQL desde R2 +5. `docker-compose up -d` +6. Verificar servicios +7. Actualizar DNS + +### RTO/RPO + +| Métrica | Objetivo | +|---------|----------| +| **RPO** (Recovery Point Objective) | 24 horas | +| **RTO** (Recovery Time Objective) | 4 horas | + +--- + +## Verificación de Backups + +```bash +# Verificar integridad mensualmente +#!/bin/bash + +# Descargar último backup +LATEST=$(aws s3 ls s3://$R2_BUCKET/backups/postgres/ --endpoint-url $R2_ENDPOINT | tail -1 | awk '{print $4}') + +aws s3 cp s3://$R2_BUCKET/backups/postgres/$LATEST /tmp/ --endpoint-url $R2_ENDPOINT + +# Restaurar en BD temporal +createdb -U $DB_USER test_restore +gunzip -c /tmp/$LATEST | psql -U $DB_USER test_restore + +# Verificar tablas +psql -U $DB_USER test_restore -c "\dt" + +# Limpiar +dropdb -U $DB_USER test_restore +``` diff --git a/04_INFRAESTRUCTURA/despliegue.md b/04_INFRAESTRUCTURA/despliegue.md new file mode 100644 index 0000000..caa0a91 --- /dev/null +++ b/04_INFRAESTRUCTURA/despliegue.md @@ -0,0 +1,126 @@ +# Despliegue + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Stack Tecnológico + +| Componente | Tecnología | +|------------|------------| +| Contenedores | Docker + Docker Compose | +| Base de datos | PostgreSQL | +| Cache | Redis | +| Proxy | Caddy | +| Orquestación | Windmill | + +--- + +## Docker Compose Base + +```yaml +version: '3.8' + +services: + postgres: + image: postgres:15 + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_NAME} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + volumes: + - redis_data:/data + + caddy: + image: caddy:2-alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile + - caddy_data:/data + +volumes: + postgres_data: + redis_data: + caddy_data: +``` + +--- + +## Checklist de Despliegue + +### Pre-despliegue + +- [ ] Servidor aprovisionado +- [ ] DNS configurado +- [ ] Secretos en Infisical +- [ ] Backup de datos existentes + +### Despliegue + +- [ ] Clonar repositorio +- [ ] Copiar .env desde Infisical +- [ ] `docker-compose up -d` +- [ ] Verificar healthchecks +- [ ] Aplicar schemas SQL + +### Post-despliegue + +- [ ] Verificar servicios +- [ ] Configurar backups +- [ ] Test de endpoints +- [ ] Documentar IPs y puertos + +--- + +## Variables de Entorno + +```bash +# Base de datos +DB_HOST=localhost +DB_PORT=5432 +DB_USER=tzzr +DB_PASSWORD=**** +DB_NAME=tzzr + +# Redis +REDIS_URL=redis://localhost:6379 + +# R2 Storage +R2_ENDPOINT=https://{account}.r2.cloudflarestorage.com +R2_ACCESS_KEY=**** +R2_SECRET_KEY=**** +R2_BUCKET=**** + +# Servicios externos +ANTHROPIC_API_KEY=**** +RUNPOD_API_KEY=**** +``` + +--- + +## Rollback + +```bash +# Parar servicios +docker-compose down + +# Restaurar backup +pg_restore -U tzzr -d tzzr backup.dump + +# Volver a versión anterior +git checkout {commit_anterior} +docker-compose up -d +``` diff --git a/04_INFRAESTRUCTURA/status.md b/04_INFRAESTRUCTURA/status.md new file mode 100644 index 0000000..bf7fbdc --- /dev/null +++ b/04_INFRAESTRUCTURA/status.md @@ -0,0 +1,114 @@ +# Estado del Sistema + +**Última actualización:** 2024-12-24 10:45 UTC + +--- + +## Estado Rápido + +``` +Pipeline de Ingesta: [########--] 80% +Procesamiento IA: [#---------] 10% (GRACE pendiente) +Apps Usuario: [----------] 0% +Observabilidad: [----------] 0% +``` + +--- + +## Servicios Activos + +### DECK (72.62.1.113) - tzzrdeck.me + +| Servicio | Puerto | Función | Estado | +|----------|--------|---------|--------| +| Clara (secretaría) | 5051 | Ingesta de datos | ✓ OK | +| Alfred (producción) | 5052 | Flujos predefinidos | ✓ OK | +| Mason (administración) | 5053 | Enriquecimiento | ⚠ Pendiente | +| Feldman (contable) | 5054 | Consolidación | ⚠ Pendiente | + +### CORP (92.112.181.188) - tzzrcorp.me + +| Servicio | Puerto | Función | Estado | +|----------|--------|---------|--------| +| Margaret (secretaría) | 5051 | Ingesta de datos | ✓ OK | +| Jared (producción) | 5052 | Flujos predefinidos | ✓ OK | +| Mason (administración) | 5053 | Enriquecimiento | ✓ OK | +| Feldman (contable) | 5054 | Consolidación | ✓ OK | + +### RunPod (Serverless) + +| Servicio | Endpoint ID | Función | Estado | +|----------|-------------|---------|--------| +| GRACE | r00x4g3rrwkbyh | GPU Processing | ⚠ Pendiente Docker image | +| PENNY | 0mxhaokgsmgee3 | Asistente voz | ✓ OK | +| FACTORY | - | Generación | ⚠ Pendiente | + +### Infraestructura + +| Servicio | Servidor | Puerto | Estado | +|----------|----------|--------|--------| +| Gitea | ARCHITECT | 3000 | ✓ OK | +| Directus | Todos | 8055 | ✓ OK | +| HST | HST | 80 | ✓ OK | +| Infisical | ARCHITECT | 8082 | ✓ OK | + +--- + +## Fases Completadas + +| Fase | Descripción | Fecha | +|------|-------------|-------| +| 0 | Limpieza y consolidación | 2024-12-24 | +| 1 | Pipeline mínimo - Clara (secretaría) | 2024-12-24 | +| 3 | Flujo empresarial - Margaret (secretaría) | 2024-12-24 | +| 4 | Alfred/Jared (producción), Mason (administración), Feldman (contable) | 2024-12-24 | + +--- + +## h_instancia (Auth Keys) + +| Servidor | Hash | +|----------|------| +| DECK | `f25e44ac3c075f57b9a198c880cb1fc05cf3af56f6466828b405d8c062374179` | +| CORP | `ea9e99d5f95bcc23749d5f25b71a5b520ae7917438912fc6e29564534484a514` | + +--- + +## Endpoints de Verificación + +```bash +# DECK (tzzrdeck.me) +curl http://72.62.1.113:5051/health # Clara (secretaría) +curl http://72.62.1.113:5052/health # Alfred (producción) +curl http://72.62.1.113:5053/health # Mason (administración) +curl http://72.62.1.113:5054/health # Feldman (contable) + +# CORP (tzzrcorp.me) +curl http://92.112.181.188:5051/health # Margaret (secretaría) +curl http://92.112.181.188:5052/health # Jared (producción) +curl http://92.112.181.188:5053/health # Mason (administración) +curl http://92.112.181.188:5054/health # Feldman (contable) +``` + +--- + +## Bloqueadores Actuales + +| Componente | Bloqueador | Impacto | +|------------|------------|---------| +| GRACE | Docker image en RunPod | Procesamiento IA | + +--- + +## Próximos Pasos + +### Inmediato + +1. **GRACE** - Construir Docker image y desplegar en RunPod +2. **PACKET** - Publicar APK + +### Posterior + +1. SENTINEL - Observabilidad +2. THE-FACTORY - Generación contenido +3. PENNY - Procesamiento voz diff --git a/04_SEGURIDAD/modelo-amenazas.md b/04_SEGURIDAD/modelo-amenazas.md deleted file mode 100644 index 1de8902..0000000 --- a/04_SEGURIDAD/modelo-amenazas.md +++ /dev/null @@ -1,177 +0,0 @@ -# Modelo de Amenazas TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Resumen Ejecutivo - -El sistema TZZR tiene una base de seguridad sólida (SSH keys, PostgreSQL SSL, tokens de autenticación) pero presenta exposiciones críticas que requieren atención inmediata. - ---- - -## Hallazgos Críticos - -### CRÍTICO-001: CORP/HST sin Firewall - -**Descripción:** UFW inactivo en servidores CORP y HST. -**Impacto:** Todos los puertos expuestos a Internet. -**Evidencia:** `ufw status` devuelve "inactive". - -**Mitigación:** -```bash -# CORP -ufw default deny incoming -ufw allow 22/tcp -ufw allow 80/tcp -ufw allow 443/tcp -ufw enable - -# HST -ufw default deny incoming -ufw allow 22/tcp -ufw allow 80/tcp -ufw allow 443/tcp -ufw enable -``` - -### CRÍTICO-002: PostgreSQL 5432 Expuesto - -**Descripción:** PostgreSQL en ARCHITECT escucha en 0.0.0.0:5432. -**Impacto:** Accesible desde cualquier IP (aunque requiere autenticación). - -**Mitigación:** -``` -# /etc/postgresql/16/main/postgresql.conf -listen_addresses = '127.0.0.1,172.17.0.1' -``` - -### CRÍTICO-003: Archivos .env Legibles - -**Descripción:** Archivos .env con permisos 644 (legibles por todos). -**Impacto:** Credenciales accesibles a cualquier usuario del sistema. - -**Mitigación:** -```bash -chmod 600 /opt/clara/.env -chmod 600 /opt/alfred/.env -chmod 600 /opt/margaret/.env -chmod 600 /opt/mason/.env -``` - ---- - -## Hallazgos Altos - -### ALTO-001: fail2ban No Configurado - -**Descripción:** fail2ban failed en DECK, inactive en CORP. -**Impacto:** SSH bruteforce sin mitigación automática. -**Evidencia:** Ataques activos desde 165.232.81.204, 188.166.115.48. - -**Mitigación:** -```bash -apt install fail2ban -systemctl enable fail2ban -# jail: sshd, maxretry=3, bantime=1h -``` - -### ALTO-002: Triple Gestión de Secretos - -**Descripción:** Credenciales en Infisical, tablas creds_*, y archivos .env. -**Impacto:** No hay fuente única de verdad, riesgo de desincronización. - -**Mitigación:** -- Migrar todas las credenciales a Infisical -- Eliminar .env hardcodeados -- creds_* solo para referencia - -### ALTO-003: Servicios sin TLS Interno - -**Descripción:** CLARA, MARGARET, MASON, FELDMAN en HTTP plano. -**Impacto:** Tráfico interno sin cifrar. - -**Mitigación:** -- Configurar mTLS entre servicios -- Usar Caddy como reverse proxy con TLS - ---- - -## Hallazgos Medios - -### MEDIO-001: No hay Backup PostgreSQL - -**Descripción:** Solo Gitea tiene backup en R2, PostgreSQL sin backup. -**Impacto:** Pérdida de datos en caso de fallo. - -**Mitigación:** -```bash -# Cron diario -pg_dump architect | gzip | aws s3 cp - s3://architect/backups/architect_$(date +%F).sql.gz -``` - -### MEDIO-002: SENTINEL No Desplegado - -**Descripción:** Sistema de auditoría planificado pero no implementado. -**Impacto:** Sin visibilidad de anomalías. - -**Mitigación:** Implementar SENTINEL LIGHT mode básico. - -### MEDIO-003: No hay Rotación de Secretos - -**Descripción:** Sin política de rotación de credenciales. -**Impacto:** Credenciales comprometidas persisten indefinidamente. - -**Mitigación:** -- API keys: 90 días -- DB passwords: 180 días -- SSH keys: 365 días - ---- - -## Controles Existentes - -| Control | Estado | Notas | -|---------|--------|-------| -| SSH Keys | Activo | Permisos 600 correctos | -| PostgreSQL SSL | Activo | SCRAM-SHA-256 | -| H_INSTANCIA Auth | Activo | CLARA/MARGARET | -| Gitea Tokens | Activo | Lectura/Escritura separados | -| DECK UFW | Activo | Política deny incoming | - ---- - -## Matriz de Riesgos - -| ID | Riesgo | Probabilidad | Impacto | Prioridad | -|----|--------|--------------|---------|-----------| -| CRÍTICO-001 | Firewall desactivado | Alta | Alto | Semana 1 | -| CRÍTICO-002 | PostgreSQL expuesto | Media | Alto | Semana 1 | -| CRÍTICO-003 | .env legibles | Alta | Alto | Semana 1 | -| ALTO-001 | Sin fail2ban | Alta | Medio | Semana 1 | -| ALTO-002 | Secretos duplicados | Media | Medio | Mes 1 | -| ALTO-003 | Sin TLS interno | Baja | Medio | Mes 1 | -| MEDIO-001 | Sin backup PG | Baja | Alto | Mes 1 | -| MEDIO-002 | Sin SENTINEL | Baja | Medio | Mes 2 | -| MEDIO-003 | Sin rotación | Baja | Medio | Mes 2 | - ---- - -## Plan de Remediación - -### Semana 1 (Urgente) -- [ ] Activar UFW en CORP y HST -- [ ] chmod 600 en todos los .env -- [ ] Instalar fail2ban -- [ ] PostgreSQL bind 127.0.0.1 - -### Mes 1 (Alta) -- [ ] Migrar secretos a Infisical -- [ ] TLS interno -- [ ] Backup PostgreSQL automatizado - -### Mes 2 (Media) -- [ ] SENTINEL LIGHT mode -- [ ] Política de rotación -- [ ] Monitoreo de certificados diff --git a/04_SEGURIDAD/secretos.md b/04_SEGURIDAD/secretos.md deleted file mode 100644 index bf5f943..0000000 --- a/04_SEGURIDAD/secretos.md +++ /dev/null @@ -1,344 +0,0 @@ -# Gestión de Secretos TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Arquitectura de Secretos - -### Principio Fundamental - -> **DECK y CORP son instancias autónomas. Deben operar independientemente de ARCHITECT.** - -ARCHITECT es el constructor/coordinador. DECK y CORP son instancias diseñadas para trabajar solas. - -### Modelo de Gestión - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ ARCHITECT │ -│ (Constructor) │ -│ │ -│ ┌──────────────┐ │ -│ │ Infisical │ ← Gestión centralizada │ -│ │ :8082 │ Single source of truth │ -│ └──────┬───────┘ │ -│ │ │ -└───────────────────────────┼─────────────────────────────────────┘ - │ - sync/push │ (en deploy o periódico) - │ - ┌────────────────┴────────────────┐ - ▼ ▼ -┌──────────────────────┐ ┌──────────────────────┐ -│ DECK │ │ CORP │ -│ (Instancia) │ │ (Instancia) │ -│ │ │ │ -│ ┌──────────────┐ │ │ ┌──────────────┐ │ -│ │ Vaultwarden │ │ │ │ Vaultwarden │ │ -│ │ :8085 │ │ │ │ :8081 │ │ -│ └──────┬───────┘ │ │ └──────┬───────┘ │ -│ │ │ │ │ │ -│ ▼ │ │ ▼ │ -│ CLARA, ALFRED │ │ MARGARET, JARED │ -│ leen de aquí │ │ MASON, FELDMAN │ -│ │ │ leen de aquí │ -└──────────────────────┘ └──────────────────────┘ -``` - -### Gestores por Servidor - -| Servidor | Gestor | Puerto | Rol | -|----------|--------|--------|-----| -| ARCHITECT | Infisical | 8082 | Gestión central, source of truth | -| DECK | Vaultwarden | 8085 | Operación autónoma DECK | -| CORP | Vaultwarden | 8081 | Operación autónoma CORP | - ---- - -## Flujo de Secretos - -### Administración (en ARCHITECT) - -``` -Administrador - │ - ▼ -┌─────────────┐ -│ Infisical │ ← Crear/modificar/rotar secretos aquí -│ (central) │ -└─────────────┘ -``` - -### Sincronización (ARCHITECT → Instancias) - -``` -Infisical (ARCHITECT) - │ - │ Script de sync / deploy - │ - ├────────────────┬────────────────┐ - ▼ ▼ ▼ -Vaultwarden Vaultwarden (otras instancias - DECK CORP futuras) -``` - -### Operación (en cada instancia) - -``` -┌──────────────────────────────────────┐ -│ DECK │ -│ │ -│ CLARA ──────► Vaultwarden ◄─────── ALFRED -│ :8085 │ -│ │ -│ ✅ No depende de ARCHITECT │ -│ ✅ Opera si ARCHITECT está caído │ -└──────────────────────────────────────┘ -``` - ---- - -## Estado Actual: Problema - -**Triple gestión sin sincronización:** - -| Fuente | Ubicación | Estado | -|--------|-----------|--------| -| Infisical | ARCHITECT :8082 | Parcialmente usado | -| creds_* | PostgreSQL ARCHITECT | Activo | -| .env | /opt/*/.env en cada servidor | Activo, **permisos 644** | - -**Problemas:** -- Secretos duplicados en múltiples lugares -- Sin sincronización entre fuentes -- .env legibles por todos (644) -- Vaultwarden de DECK/CORP no integrados - ---- - -## Decisión D-001: Arquitectura de Secretos - -### Objetivo - -1. **Infisical** (ARCHITECT): Única fuente de verdad para administración -2. **Vaultwarden** (DECK/CORP): Gestores locales para operación autónoma -3. **Sync**: Mecanismo de propagación Infisical → Vaultwarden - -### División de Responsabilidades - -| Tipo de Secreto | Gestión en | Operación en | -|-----------------|------------|--------------| -| API keys externas (OpenAI, etc.) | Infisical | Vaultwarden local | -| R2 credentials | Infisical | Vaultwarden local | -| Gitea tokens | Infisical | Vaultwarden local | -| DB password DECK | Infisical | Vaultwarden DECK | -| DB password CORP | Infisical | Vaultwarden CORP | -| Tokens internos DECK | Infisical | Vaultwarden DECK | -| Tokens internos CORP | Infisical | Vaultwarden CORP | - -### Flujo de Trabajo - -``` -1. Admin modifica secreto en Infisical (ARCHITECT) - │ - ▼ -2. Script de sync detecta cambio - │ - ▼ -3. Propaga a Vaultwarden de DECK y/o CORP - │ - ▼ -4. Servicios locales usan Vaultwarden local - │ - ▼ -5. ARCHITECT puede caer → DECK/CORP siguen operando -``` - ---- - -## Plan de Migración - -### Fase 1: Inventario - -``` -├── Catalogar todos los secretos en creds_* -├── Catalogar todos los secretos en .env -├── Identificar duplicados/inconsistencias -└── Mapear qué secretos necesita cada instancia -``` - -### Fase 2: Centralización en Infisical - -``` -├── Migrar todos los secretos a Infisical -├── Organizar por instancia (DECK, CORP) -├── Establecer políticas de acceso -└── Documentar cada secreto -``` - -### Fase 3: Configurar Vaultwarden en Instancias - -``` -├── DECK: Configurar Vaultwarden :8085 -│ ├── Crear organización "DECK" -│ ├── Importar secretos de DECK desde Infisical -│ └── Configurar acceso para servicios -│ -└── CORP: Configurar Vaultwarden :8081 - ├── Crear organización "CORP" - ├── Importar secretos de CORP desde Infisical - └── Configurar acceso para servicios -``` - -### Fase 4: Implementar Sync - -``` -├── Crear script de sincronización -│ └── infisical export → vaultwarden import -├── Configurar trigger (manual, cron, o webhook) -└── Probar sync en ambas direcciones -``` - -### Fase 5: Actualizar Servicios - -``` -├── CLARA: Leer de Vaultwarden DECK -├── ALFRED: Leer de Vaultwarden DECK -├── MARGARET: Leer de Vaultwarden CORP -├── JARED: Leer de Vaultwarden CORP -├── MASON: Leer de Vaultwarden CORP -└── FELDMAN: Leer de Vaultwarden CORP -``` - -### Fase 6: Deprecación - -``` -├── Eliminar archivos .env -├── Marcar creds_* como "solo referencia histórica" -└── Documentar proceso completo -``` - ---- - -## Implementación - -### Lectura desde Vaultwarden (Python) - -```python -import requests - -VAULTWARDEN_URL = "http://localhost:8085" # DECK -# VAULTWARDEN_URL = "http://localhost:8081" # CORP - -def get_secret(name: str) -> str: - """Obtiene secreto de Vaultwarden local.""" - # Implementar según API de Vaultwarden/Bitwarden - pass -``` - -### Script de Sync (Infisical → Vaultwarden) - -```bash -#!/bin/bash -# /opt/scripts/sync_secrets.sh - -# Exportar desde Infisical -infisical export --env=deck --format=json > /tmp/deck_secrets.json -infisical export --env=corp --format=json > /tmp/corp_secrets.json - -# Importar a Vaultwarden DECK -ssh deck 'bw import --format json /tmp/deck_secrets.json' - -# Importar a Vaultwarden CORP -ssh corp 'bw import --format json /tmp/corp_secrets.json' - -# Cleanup -rm /tmp/*_secrets.json -``` - ---- - -## Mitigación Inmediata - -### Paso 1: Permisos .env (URGENTE) - -```bash -# DECK -ssh -i ~/.ssh/tzzr root@72.62.1.113 'chmod 600 /opt/clara/.env /opt/alfred/.env' - -# CORP -ssh -i ~/.ssh/tzzr root@92.112.181.188 'chmod 600 /opt/margaret/.env /opt/mason/.env /opt/feldman/.env' -``` - -### Paso 2: Verificar Vaultwarden - -```bash -# DECK -curl -s http://72.62.1.113:8085/api/health - -# CORP -curl -s http://92.112.181.188:8081/api/health -``` - ---- - -## Rotación de Secretos - -### Política - -| Tipo | Frecuencia | Responsable | -|------|------------|-------------| -| API Keys externas | 90 días | Admin vía Infisical | -| Contraseñas DB | 180 días | Admin vía Infisical | -| SSH Keys | 365 días | Admin vía Infisical | -| Tokens Gitea | 180 días | Admin vía Infisical | - -### Proceso - -``` -1. Rotar en Infisical (ARCHITECT) -2. Ejecutar sync a Vaultwarden (DECK/CORP) -3. Verificar servicios operando -4. Documentar rotación -``` - ---- - -## Verificación - -### Checklist - -- [ ] Infisical operativo en ARCHITECT -- [ ] Vaultwarden operativo en DECK (:8085) -- [ ] Vaultwarden operativo en CORP (:8081) -- [ ] Sync configurado y probado -- [ ] Servicios leen de Vaultwarden local -- [ ] .env eliminados o con permisos 600 -- [ ] DECK opera si ARCHITECT cae -- [ ] CORP opera si ARCHITECT cae - -### Test de Autonomía - -```bash -# 1. Detener ARCHITECT (simulado) -# 2. Verificar DECK sigue operando -curl http://72.62.1.113:5051/health # CLARA - -# 3. Verificar CORP sigue operando -curl http://92.112.181.188:5051/health # MARGARET -``` - ---- - -## Resumen - -| Componente | Rol | -|------------|-----| -| **Infisical (ARCHITECT)** | Gestión centralizada, source of truth | -| **Vaultwarden (DECK)** | Operación autónoma instancia personal | -| **Vaultwarden (CORP)** | Operación autónoma instancia empresarial | -| **Sync** | Propagación de cambios | - -**Principio clave:** Las instancias (DECK, CORP) nunca dependen de ARCHITECT en runtime. diff --git a/05_INTEGRACIONES/factory-protocol.md b/05_INTEGRACIONES/factory-protocol.md new file mode 100644 index 0000000..f922b06 --- /dev/null +++ b/05_INTEGRACIONES/factory-protocol.md @@ -0,0 +1,205 @@ +# Factory Protocol + +**Versión:** 1.0 +**Estado:** Especificación + +--- + +## Descripción + +THE FACTORY es el sistema de generación iterativa. Implementa un ciclo de mejora continua con Director, Executor, Evaluator y Auditor. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ THE FACTORY │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ DIRECTOR │───▶│ EXECUTOR │───▶│ EVALUATOR│ │ +│ │ (decide) │◀───│ (genera) │◀───│ (evalúa) │ │ +│ └────┬─────┘ └──────────┘ └──────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────┐ │ +│ │ AUDITOR │ ──▶ SENTINEL │ +│ │(registra)│ │ +│ └──────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Componentes + +### DIRECTOR + +Cerebro de THE FACTORY: + +- Recibe jobs de Alfred (DECK) o Clara (CORP) +- Selecciona modelos apropiados +- Decide convergencia +- Gestiona presupuesto e iteraciones + +```javascript +// Convergencia si: +// - confidence >= 0.85 +// - mejora < 0.02 con confidence > 0.7 (rendimientos decrecientes) +const decision = director.decideConvergence(job, evaluation); +``` + +### EXECUTOR + +Genera artefactos según tipo: + +| Función | Modelos | +|---------|---------| +| TEXT_GENERATION | claude-sonnet, claude-opus, gpt-4, llama-3 | +| IMAGE_GENERATION | flux-pro, flux-dev, sdxl, dalle-3 | +| CODE_GENERATION | claude-sonnet, claude-opus, gpt-4 | +| DOCUMENT_GENERATION | claude-opus, gpt-4 | +| AUDIO_GENERATION | elevenlabs, bark, xtts | +| VIDEO_GENERATION | runway, pika, stable-video | + +### EVALUATOR + +Analiza cada artefacto: + +```json +{ + "confidence": 0.82, + "strengths": ["Cumple estructura básica", "Lenguaje apropiado"], + "weaknesses": ["Falta detalle en sección X"], + "feedback": "Mejorar especificidad y añadir ejemplos." +} +``` + +### AUDITOR + +Registra eventos para trazabilidad: + +- `JOB_CREATED` - Inicio del job +- `JOB_STARTED` - Ejecución iniciada +- `ITERATION_COMPLETED` - Cada iteración +- `JOB_COMPLETED` - Finalización + +--- + +## Ciclo de Iteración + +``` +1. DIRECTOR recibe job +2. DIRECTOR selecciona modelo para EXECUTOR +3. EXECUTOR genera artefacto +4. EVALUATOR evalúa resultado +5. DIRECTOR decide: + - Converge → Finalizar + - No converge → Volver a paso 3 con feedback +6. AUDITOR registra cada paso +``` + +--- + +## Request + +```json +{ + "contract_version": "2.1", + "profile": "FULL", + + "routing": { + "module": "FACTORY", + "version": "1.0" + }, + + "factory": { + "function": "TEXT_GENERATION", + "objective": "Generar artículo sobre IA", + "constraints": { + "max_iterations": 5, + "min_confidence": 0.85, + "max_cost_units": 1.0 + }, + "context": { + "style": "profesional", + "length": "1500 palabras", + "audience": "técnico" + } + }, + + "payload": { + "type": "text", + "content": "Brief del artículo..." + } +} +``` + +--- + +## Response + +```json +{ + "result": { + "status": "SUCCESS", + "content": "Artículo generado...", + "content_hash": "sha256" + }, + + "factory": { + "iterations": 3, + "final_confidence": 0.89, + "model_used": "claude-opus", + "convergence_reason": "confidence_threshold" + }, + + "audit": { + "job_id": "uuid", + "events": [ + {"type": "JOB_CREATED", "ts": "..."}, + {"type": "ITERATION_COMPLETED", "iteration": 1, "confidence": 0.72}, + {"type": "ITERATION_COMPLETED", "iteration": 2, "confidence": 0.81}, + {"type": "ITERATION_COMPLETED", "iteration": 3, "confidence": 0.89}, + {"type": "JOB_COMPLETED", "ts": "..."} + ] + }, + + "performance": { + "total_ms": 45000, + "cost_units": 0.45 + } +} +``` + +--- + +## Convergencia + +| Condición | Acción | +|-----------|--------| +| confidence >= threshold | Converge | +| mejora < 0.02 && confidence > 0.7 | Converge (rendimientos decrecientes) | +| iteraciones >= max | Converge forzado | +| costo >= max | Converge forzado | + +--- + +## Configuración + +```yaml +factory: + default_max_iterations: 5 + default_min_confidence: 0.85 + default_max_cost: 1.0 + + convergence: + confidence_threshold: 0.85 + diminishing_returns_threshold: 0.02 + diminishing_returns_min_confidence: 0.7 + + models: + text: + primary: claude-opus + fallback: [claude-sonnet, gpt-4] + image: + primary: flux-pro + fallback: [flux-dev, sdxl] +``` diff --git a/05_INTEGRACIONES/gpu-services.md b/05_INTEGRACIONES/gpu-services.md new file mode 100644 index 0000000..a9cc768 --- /dev/null +++ b/05_INTEGRACIONES/gpu-services.md @@ -0,0 +1,125 @@ +# GPU Services + +**Plataforma:** RunPod +**Estado:** Operativo + +--- + +## Servicios + +| Servicio | Endpoint ID | GPU | Workers | Función | +|----------|-------------|-----|---------|---------| +| **Grace** | {grace_id} | NVIDIA L4 | 2 | Procesamiento IA | +| **Penny** | 0mxhaokgsmgee3 | NVIDIA L4 | 2 | Asistente voz | +| **The Factory** | {factory_id} | NVIDIA L4 | 2 | Generación iterativa | + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ RunPod │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ GRACE │ │ PENNY │ │ FACTORY │ │ +│ │ │ │ │ │ │ │ +│ │ 18 módulos │ │ ASR + TTS │ │ Iterativo │ │ +│ │ NVIDIA L4 │ │ + Claude │ │ NVIDIA L4 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ + ▲ ▲ ▲ + │ │ │ + └──────────────────┼──────────────────┘ + │ + ┌───────┴───────┐ + │ S-CONTRACT │ + │ v2.1 │ + └───────────────┘ + ▲ + │ + ┌──────────────┴──────────────┐ + │ │ + ┌────┴────┐ ┌────┴────┐ + │ DECK │ │ CORP │ + │ Alfred │ │ Jared │ + └─────────┘ └─────────┘ +``` + +--- + +## Llamada a Servicio + +### URL Base + +``` +https://api.runpod.ai/v2/{endpoint_id}/runsync +``` + +### Headers + +``` +Authorization: Bearer {RUNPOD_API_KEY} +Content-Type: application/json +``` + +### Request + +```json +{ + "input": { + "contract": { + "contract_version": "2.1", + "source": { "system": "DECK" }, + "target": { "service": "GRACE", "module": "SUMMARIZER" }, + "input": { "type": "text", "data": "..." } + } + } +} +``` + +### Response + +```json +{ + "id": "job_id", + "status": "COMPLETED", + "output": { + "status": "completed", + "output": { "data": "..." }, + "usage": { "cost_usd": 0.002 } + } +} +``` + +--- + +## Modos de Ejecución + +| Modo | Endpoint | Uso | +|------|----------|-----| +| **runsync** | /runsync | Espera respuesta | +| **run** | /run | Asíncrono, devuelve job_id | +| **status** | /status/{job_id} | Consultar estado | + +--- + +## Costes + +| Recurso | Coste | +|---------|-------| +| NVIDIA L4 | ~$0.20/hora | +| Worker idle | $0 (serverless) | +| Por request | Variable según tokens | + +--- + +## Monitoreo + +```bash +# Estado de endpoints +curl -H "Authorization: Bearer $RUNPOD_API_KEY" \ + https://api.runpod.ai/v2/{endpoint_id}/health +``` diff --git a/05_INTEGRACIONES/hst-api.md b/05_INTEGRACIONES/hst-api.md new file mode 100644 index 0000000..6c82774 --- /dev/null +++ b/05_INTEGRACIONES/hst-api.md @@ -0,0 +1,162 @@ +# HST API + +**Base URL:** https://tzrtech.org/api +**Estado:** Implementado + +--- + +## Endpoints + +### Listar Tags + +``` +GET /tags +``` + +**Query params:** + +| Param | Tipo | Descripción | +|-------|------|-------------| +| grupo | string | Filtrar por grupo (hst, spe, vsn, flg, vue) | +| activo | boolean | Solo activos | +| limit | integer | Límite de resultados | + +**Response:** + +```json +{ + "tags": [ + { + "ref": "person", + "h_maestro": "a1b2c3...", + "grupo": "hst", + "nombre_es": "Persona", + "nombre_en": "Person", + "imagen_url": "https://tzrtech.org/a1b2c3...png" + } + ], + "total": 639 +} +``` + +--- + +### Obtener Tag + +``` +GET /tags/{ref} +``` + +**Response:** + +```json +{ + "ref": "person", + "h_maestro": "a1b2c3...", + "grupo": "hst", + "nombre_es": "Persona", + "nombre_en": "Person", + "descripcion": "...", + "imagen_url": "https://tzrtech.org/a1b2c3...png", + "activo": true, + "version": 1 +} +``` + +--- + +### Buscar Tags + +``` +GET /tags/search?q={query} +``` + +**Response:** + +```json +{ + "query": "person", + "results": [ + { "ref": "person", "score": 1.0 }, + { "ref": "people", "score": 0.8 } + ] +} +``` + +--- + +### Biblioteca de Usuario + +``` +GET /biblioteca +``` + +**Headers:** + +``` +Authorization: Bearer {token} +``` + +**Response:** + +```json +{ + "user_id": 123, + "tags": [ + { + "ref": "mi_tag", + "h_usuario": "def456...", + "nombre": "Mi Tag Personal" + } + ] +} +``` + +--- + +### Crear Tag de Usuario + +``` +POST /biblioteca +``` + +**Body:** + +```json +{ + "ref": "mi_tag", + "nombre": "Mi Tag Personal", + "metadata": {} +} +``` + +**Response:** + +```json +{ + "ref": "mi_tag", + "h_usuario": "def456...", + "created": true +} +``` + +--- + +## Autenticación + +| Endpoint | Auth | +|----------|------| +| GET /tags | Pública | +| GET /tags/{ref} | Pública | +| GET /tags/search | Pública | +| GET /biblioteca | Token requerido | +| POST /biblioteca | Token requerido | + +--- + +## Rate Limits + +| Tipo | Límite | +|------|--------| +| Público | 100 req/min | +| Autenticado | 1000 req/min | diff --git a/05_INTEGRACIONES/notario.md b/05_INTEGRACIONES/notario.md new file mode 100644 index 0000000..ec4fd72 --- /dev/null +++ b/05_INTEGRACIONES/notario.md @@ -0,0 +1,250 @@ +# Notario + +**Versión:** 2.0 +**Estado:** Especificación + +--- + +## Descripción + +Sistema de sellado inmutable en blockchain. Última capa de confianza del ecosistema. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRINCIPIO NOTARIO │ +│ │ +│ "Lo que NOTARIO sella, nadie lo altera" │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ NOTARIO │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ Feldman (bloques consolidados) │ +│ │ │ +│ │ (auditados por SENTINEL) │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ BATCH COLLECTOR │ Agrupa por ventana temporal │ +│ └─────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ MERKLE BUILDER │ Construye árbol Merkle │ +│ └─────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ BLOCKCHAIN TX │ Publica hash raíz │ +│ └─────────────────┘ │ +│ │ │ +│ ▼ │ +│ Registro actualizado con blockchain_tx_ref │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Dos Franjas Temporales + +### Franja Interna + +Tiempo del sistema para: +- Procesar operaciones +- Ejecutar auditoría SENTINEL +- Consolidar en Feldman + +**Duración:** Minutos a horas + +### Franja Blockchain + +Tiempo de NOTARIO para: +- Agrupar registros en batches +- Construir pruebas criptográficas +- Publicar en blockchain +- Confirmar transacciones + +**Duración:** Horas a días + +--- + +## Ciclo de Sellado + +``` +1. RECOLECTAR + SELECT FROM feldman_bloques + WHERE audit_status = 'DEEP_PASS' + AND blockchain_tx_ref IS NULL + +2. AGRUPAR + Crear batch con ventana temporal (ej: 24h) + Asignar notario_batch_id + +3. CONSTRUIR MERKLE + - Ordenar registros por h_bloque + - Calcular hash de cada registro + - Construir árbol binario + - Obtener merkle_root + +4. FIRMAR + - Firmar merkle_root con llave NOTARIO + - Generar timestamp certificado + +5. PUBLICAR + - Enviar transacción a blockchain + - Esperar confirmaciones + +6. ACTUALIZAR + UPDATE feldman_bloques SET blockchain_tx_ref = tx_hash +``` + +--- + +## Schema SQL + +```sql +CREATE TABLE notario_batches ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + batch_id UUID UNIQUE NOT NULL, + + -- Ventana temporal + window_start TIMESTAMPTZ NOT NULL, + window_end TIMESTAMPTZ NOT NULL, + + -- Contenido + records_count INTEGER NOT NULL, + bloque_hashes TEXT[] NOT NULL, + + -- Merkle + merkle_root CHAR(64) NOT NULL, + merkle_tree JSONB, + + -- Firma + signature TEXT NOT NULL, + signing_key_id VARCHAR(100), + signed_at TIMESTAMPTZ NOT NULL, + + -- Blockchain + blockchain_network VARCHAR(50) DEFAULT 'ethereum', + blockchain_tx_ref VARCHAR(100), + blockchain_block BIGINT, + blockchain_timestamp TIMESTAMPTZ, + confirmations INTEGER DEFAULT 0, + + -- Estado + status VARCHAR(20) DEFAULT 'PENDING', + created_at TIMESTAMPTZ DEFAULT NOW(), + + CONSTRAINT valid_status CHECK ( + status IN ('PENDING', 'SUBMITTED', 'CONFIRMED', 'FAILED') + ) +); + +CREATE INDEX idx_notario_status ON notario_batches(status); +CREATE INDEX idx_notario_window ON notario_batches(window_start, window_end); +``` + +--- + +## Verificación de Integridad + +```python +def verify_record_integrity(h_bloque, notario_batch_id): + # 1. Obtener batch + batch = get_batch(notario_batch_id) + + # 2. Verificar que h_bloque está en el batch + assert h_bloque in batch.bloque_hashes + + # 3. Obtener registro de Feldman + record = get_feldman_record(h_bloque) + + # 4. Calcular hash del registro + record_hash = sha256(serialize(record)) + + # 5. Verificar inclusión en merkle tree + proof = get_merkle_proof(batch.merkle_tree, h_bloque) + assert verify_merkle_proof(record_hash, proof, batch.merkle_root) + + # 6. Verificar firma del batch + assert verify_signature( + batch.merkle_root, + batch.signature, + batch.signing_key_id + ) + + # 7. Verificar que merkle_root está en blockchain + tx = get_blockchain_tx(batch.blockchain_tx_ref) + assert tx.data == batch.merkle_root + + return True +``` + +--- + +## Integración con S-CONTRACT + +### Campos en Response (pre-sellado) + +```json +"audit": { + "blockchain_pending": true, + "blockchain_tx_ref": null, + "notario_batch_id": "uuid-batch" +} +``` + +### Campos post-sellado + +```json +"audit": { + "blockchain_pending": false, + "blockchain_tx_ref": "0x1234...abcd", + "notario_batch_id": "uuid-batch" +} +``` + +--- + +## Configuración + +```yaml +notario: + enabled: true + + # Ventana de agregación + batch_window_hours: 24 + min_records_per_batch: 10 + max_records_per_batch: 10000 + + # Blockchain + network: ethereum + contract_address: "0x..." + gas_limit: 100000 + + # Firma + signing_key: kv://production/signing/notario + + # Reintentos + max_retries: 3 + retry_delay_minutes: 60 +``` + +--- + +## Pendiente + +- [ ] Crear tabla notario_batches +- [ ] Implementar recolector de registros +- [ ] Implementar constructor Merkle tree +- [ ] Configurar llave de firma +- [ ] Integrar con proveedor blockchain +- [ ] Implementar verificador de integridad +- [ ] Crear workflow de sellado periódico diff --git a/05_OPERACIONES/backup-recovery.md b/05_OPERACIONES/backup-recovery.md deleted file mode 100644 index 57f96e8..0000000 --- a/05_OPERACIONES/backup-recovery.md +++ /dev/null @@ -1,422 +0,0 @@ -# Backup y Recovery TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Principio Fundamental - -> **Cada instancia es responsable de su propio backup.** - -DECK y CORP son instancias autónomas. No dependen de ARCHITECT para hacer backups. -Cada servidor ejecuta su script de backup localmente y sube directamente a R2. - ---- - -## Estado Actual - -### Backups Existentes - -| Sistema | Backup | Destino | Frecuencia | Estado | -|---------|--------|---------|------------|--------| -| Gitea | Sí | R2 | Manual | Operativo | -| PostgreSQL ARCHITECT | No | - | - | **CRÍTICO** | -| PostgreSQL DECK | No | - | - | **CRÍTICO** | -| PostgreSQL CORP | No | - | - | **CRÍTICO** | -| PostgreSQL HST | No | - | - | **CRÍTICO** | -| R2 buckets | Built-in | R2 | Automático | Operativo | - ---- - -## Arquitectura de Backups - -``` -┌──────────────┐ ┌──────────────┐ ┌──────────────┐ -│ ARCHITECT │ │ DECK │ │ CORP │ -│ │ │ │ │ │ -│ backup.sh ───┼────►│ backup.sh ───┼────►│ backup.sh ───┼────► R2 -│ (local) │ │ (local) │ │ (local) │ -└──────────────┘ └──────────────┘ └──────────────┘ - │ │ - ▼ ▼ - Sin dependencia Sin dependencia - de ARCHITECT de ARCHITECT -``` - ---- - -## Backup por Servidor (LOCAL) - -### ARCHITECT (69.62.126.110) - -**Ubicación script:** `/opt/scripts/backup_postgres.sh` - -```bash -#!/bin/bash -# Ejecutar EN ARCHITECT - backup local - -set -e -DATE=$(date +%F) - -# Credenciales R2 (desde Vaultwarden local o .env) -source /opt/architect/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" -R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com" - -# Backup local -sudo -u postgres pg_dump architect | gzip > /tmp/architect_$DATE.sql.gz - -# Subir a R2 -aws s3 cp /tmp/architect_$DATE.sql.gz s3://architect/backups/postgres/ \ - --endpoint-url $R2_ENDPOINT - -rm /tmp/architect_$DATE.sql.gz -echo "ARCHITECT backup completado: $DATE" -``` - -### DECK (72.62.1.113) - -**Ubicación script:** `/opt/scripts/backup_postgres.sh` - -```bash -#!/bin/bash -# Ejecutar EN DECK - backup local (NO depende de ARCHITECT) - -set -e -DATE=$(date +%F) - -# Credenciales R2 (desde Vaultwarden DECK) -source /opt/deck/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" -R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com" - -# Backup local -sudo -u postgres pg_dump tzzr | gzip > /tmp/deck_tzzr_$DATE.sql.gz - -# Subir a R2 (bucket propio de DECK) -aws s3 cp /tmp/deck_tzzr_$DATE.sql.gz s3://deck/backups/postgres/ \ - --endpoint-url $R2_ENDPOINT - -rm /tmp/deck_tzzr_$DATE.sql.gz -echo "DECK backup completado: $DATE" -``` - -### CORP (92.112.181.188) - -**Ubicación script:** `/opt/scripts/backup_postgres.sh` - -```bash -#!/bin/bash -# Ejecutar EN CORP - backup local (NO depende de ARCHITECT) - -set -e -DATE=$(date +%F) - -# Credenciales R2 (desde Vaultwarden CORP) -source /opt/corp/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" -R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com" - -# Backup local -sudo -u postgres pg_dump corp | gzip > /tmp/corp_$DATE.sql.gz - -# Subir a R2 (bucket propio de CORP) -aws s3 cp /tmp/corp_$DATE.sql.gz s3://corp/backups/postgres/ \ - --endpoint-url $R2_ENDPOINT - -rm /tmp/corp_$DATE.sql.gz -echo "CORP backup completado: $DATE" -``` - -### HST (72.62.2.84) - -**Ubicación script:** `/opt/scripts/backup_postgres.sh` - -```bash -#!/bin/bash -# Ejecutar EN HST - backup local - -set -e -DATE=$(date +%F) - -source /opt/hst/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" -R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com" - -sudo -u postgres pg_dump hst_images | gzip > /tmp/hst_$DATE.sql.gz - -aws s3 cp /tmp/hst_$DATE.sql.gz s3://hst/backups/postgres/ \ - --endpoint-url $R2_ENDPOINT - -rm /tmp/hst_$DATE.sql.gz -echo "HST backup completado: $DATE" -``` - ---- - -## Cron en Cada Servidor - -Cada instancia configura su propio cron: - -```bash -# /etc/cron.d/tzzr-backup (en cada servidor) -# Backup diario a las 3:00 AM -0 3 * * * root /opt/scripts/backup_postgres.sh >> /var/log/backup.log 2>&1 -``` - ---- - -## Gitea Backup - -### Backup Manual - -```bash -# En ARCHITECT -docker exec -t gitea bash -c 'gitea dump -c /data/gitea/conf/app.ini' -docker cp gitea:/app/gitea/gitea-dump-*.zip ./ - -# Subir a R2 -aws s3 cp gitea-dump-*.zip s3://architect/backups/gitea/ \ - --endpoint-url $R2_ENDPOINT -``` - -### Backup Automático - -```bash -#!/bin/bash -# /opt/scripts/backup_gitea.sh - -DATE=$(date +%F_%H%M) - -# Crear dump -docker exec -t gitea bash -c "gitea dump -c /data/gitea/conf/app.ini -f /tmp/gitea-dump-$DATE.zip" - -# Copiar fuera del container -docker cp gitea:/tmp/gitea-dump-$DATE.zip /tmp/ - -# Subir a R2 -source /home/orchestrator/orchestrator/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" - -aws s3 cp /tmp/gitea-dump-$DATE.zip s3://architect/backups/gitea/ \ - --endpoint-url https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com - -# Cleanup -rm /tmp/gitea-dump-$DATE.zip -docker exec gitea rm /tmp/gitea-dump-$DATE.zip -``` - ---- - -## Estructura de Backups en R2 - -Cada instancia usa su propio bucket: - -``` -s3://architect/backups/ -├── postgres/ -│ └── architect_2024-12-24.sql.gz -└── gitea/ - └── gitea-dump-2024-12-24_0300.zip - -s3://deck/backups/ -└── postgres/ - └── deck_tzzr_2024-12-24.sql.gz - -s3://corp/backups/ -└── postgres/ - └── corp_2024-12-24.sql.gz - -s3://hst/backups/ -└── postgres/ - └── hst_2024-12-24.sql.gz -``` - -> **Nota:** Cada instancia es dueña de sus backups. No hay dependencia cruzada. - ---- - -## Retención de Backups - -### Política Propuesta - -| Tipo | Retención | Notas | -|------|-----------|-------| -| Diario | 7 días | Últimos 7 backups | -| Semanal | 4 semanas | Domingos | -| Mensual | 12 meses | Primer día del mes | - -### Script de Limpieza - -```bash -#!/bin/bash -# /opt/scripts/cleanup_backups.sh - -source /home/orchestrator/orchestrator/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" - -R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com" - -# Eliminar backups más antiguos de 30 días -# (Implementar con lifecycle rules de R2 preferentemente) - -aws s3 ls s3://architect/backups/postgres/ --endpoint-url $R2_ENDPOINT | \ - while read -r line; do - createDate=$(echo $line | awk '{print $1}') - fileName=$(echo $line | awk '{print $4}') - - # Comparar fechas y eliminar si > 30 días - # ... - done -``` - ---- - -## Recovery - -### PostgreSQL - Restaurar Base de Datos - -```bash -# Descargar backup -aws s3 cp s3://architect/backups/postgres/architect_2024-12-24.sql.gz . \ - --endpoint-url $R2_ENDPOINT - -# Descomprimir -gunzip architect_2024-12-24.sql.gz - -# Restaurar (¡CUIDADO! Sobrescribe datos existentes) -sudo -u postgres psql -d architect < architect_2024-12-24.sql -``` - -### PostgreSQL - Restaurar a Nueva Base - -```bash -# Crear nueva base de datos -sudo -u postgres createdb architect_restored - -# Restaurar -gunzip -c architect_2024-12-24.sql.gz | sudo -u postgres psql -d architect_restored -``` - -### Gitea - Restaurar - -```bash -# Descargar backup -aws s3 cp s3://architect/backups/gitea/gitea-dump-2024-12-24_0300.zip . \ - --endpoint-url $R2_ENDPOINT - -# Detener Gitea -docker stop gitea - -# Copiar al container -docker cp gitea-dump-2024-12-24_0300.zip gitea:/tmp/ - -# Restaurar -docker exec gitea bash -c "cd /tmp && unzip gitea-dump-2024-12-24_0300.zip" -# Seguir instrucciones de Gitea para restore - -# Iniciar Gitea -docker start gitea -``` - ---- - -## Disaster Recovery Plan - -### Escenario 1: Pérdida de ARCHITECT - -1. Provisionar nuevo VPS con misma IP (si posible) -2. Instalar Ubuntu 22.04 -3. Configurar usuario orchestrator -4. Restaurar PostgreSQL desde R2 -5. Restaurar Gitea desde R2 -6. Reinstalar Docker y servicios -7. Verificar conectividad con DECK/CORP/HST - -### Escenario 2: Pérdida de DECK - -1. Provisionar nuevo VPS -2. Restaurar PostgreSQL (tzzr) desde backup -3. Reinstalar CLARA, ALFRED -4. Reinstalar Mailcow (requiere backup separado) -5. Actualizar DNS si IP cambió - -### Escenario 3: Pérdida de CORP - -1. Provisionar nuevo VPS -2. Restaurar PostgreSQL (corp) desde backup -3. Reinstalar MARGARET, JARED, MASON, FELDMAN -4. Reinstalar Odoo, Nextcloud -5. Activar UFW (nuevo servidor) - -### Escenario 4: Pérdida de R2 - -**IMPROBABLE** - Cloudflare tiene redundancia multi-región. - -Mitigación: Backup mensual a segundo proveedor (AWS S3 Glacier). - ---- - -## Verificación de Backups - -### Test Mensual - -```bash -#!/bin/bash -# /opt/scripts/verify_backup.sh - -# Descargar último backup -LATEST=$(aws s3 ls s3://architect/backups/postgres/ --endpoint-url $R2_ENDPOINT | \ - sort | tail -1 | awk '{print $4}') - -aws s3 cp s3://architect/backups/postgres/$LATEST /tmp/ \ - --endpoint-url $R2_ENDPOINT - -# Verificar integridad -gunzip -t /tmp/$LATEST -if [ $? -eq 0 ]; then - echo "Backup válido: $LATEST" -else - echo "ERROR: Backup corrupto: $LATEST" - # Enviar alerta -fi - -rm /tmp/$LATEST -``` - -### Checklist de Verificación - -- [ ] Backup PostgreSQL ARCHITECT existe (< 24h) -- [ ] Backup PostgreSQL DECK existe (< 24h) -- [ ] Backup PostgreSQL CORP existe (< 24h) -- [ ] Backup Gitea existe (< 7d) -- [ ] Integridad verificada (gunzip -t) -- [ ] Restore test exitoso (mensual) - ---- - -## Alertas - -### Configuración ntfy - -```bash -# Notificar si backup falla -if [ $? -ne 0 ]; then - curl -d "Backup FALLIDO: $DATE" ntfy.sh/tzzr-alerts -fi -``` - -### Monitoreo - -```bash -# Verificar último backup -aws s3 ls s3://architect/backups/postgres/ --endpoint-url $R2_ENDPOINT | \ - sort | tail -5 -``` diff --git a/05_OPERACIONES/infraestructura.md b/05_OPERACIONES/infraestructura.md deleted file mode 100644 index 34f47c0..0000000 --- a/05_OPERACIONES/infraestructura.md +++ /dev/null @@ -1,383 +0,0 @@ -# Infraestructura TZZR - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Resumen de Servidores - -| Servidor | IP Pública | Rol | Proveedor | -|----------|------------|-----|-----------| -| ARCHITECT | 69.62.126.110 | Coordinador central | VPS | -| DECK | 72.62.1.113 | Personal | VPS | -| CORP | 92.112.181.188 | Empresarial | VPS | -| HST | 72.62.2.84 | API Tags | VPS | -| LOCKER | R2 | Almacenamiento | Cloudflare | - ---- - -## ARCHITECT (69.62.126.110) - -### Especificaciones -- **OS:** Ubuntu 22.04 LTS -- **Usuario:** orchestrator -- **Servicios:** PostgreSQL, Gitea, Orchestrator, Infisical - -### Puertos - -| Puerto | Servicio | Estado | -|--------|----------|--------| -| 22 | SSH | Abierto | -| 2222 | Gitea SSH | Abierto | -| 3000 | Gitea HTTP | Abierto | -| 5050 | Orchestrator | Abierto | -| 5432 | PostgreSQL | **CRÍTICO: 0.0.0.0** | -| 8082 | Infisical | Abierto | - -### Acceso - -```bash -ssh orchestrator@69.62.126.110 -``` - -### PostgreSQL - -```bash -sudo -u postgres psql -d architect -``` - -### Gitea - -``` -URL: http://localhost:3000 -Token lectura: 5ca10e5b71d41f9b22f12d0f96bfc2e6de5c2c7f -Token escritura: ac5a604b9aac5cee81192a656fc918f9efa3834b -``` - ---- - -## DECK (72.62.1.113) - -### Especificaciones -- **OS:** Ubuntu 22.04 LTS -- **Usuario:** root -- **Servicios:** CLARA, ALFRED, Mailcow, Directus, etc. - -### Puertos - -| Puerto | Servicio | Estado | -|--------|----------|--------| -| 22 | SSH | Abierto | -| 25 | SMTP | Abierto | -| 143 | IMAP | Abierto | -| 465 | SMTPS | Abierto | -| 587 | Submission | Abierto | -| 993 | IMAPS | Abierto | -| 5051 | CLARA | Abierto | -| 5052 | ALFRED | Abierto | -| 8055 | Directus | Abierto | -| 8080 | ntfy | Abierto | -| 8082 | FileBrowser | Abierto | -| 8083 | Shlink | Abierto | -| 8085 | Vaultwarden | Abierto | - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@72.62.1.113 -``` - -### Docker Containers - -```bash -docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" -``` - ---- - -## CORP (92.112.181.188) - -### Especificaciones -- **OS:** Ubuntu 22.04 LTS -- **Usuario:** root -- **Servicios:** MARGARET, JARED, MASON, FELDMAN, Odoo, Nextcloud - -### Puertos - -| Puerto | Servicio | Estado | -|--------|----------|--------| -| 22 | SSH | Abierto | -| 80 | Caddy HTTP | Abierto | -| 443 | Caddy HTTPS | Abierto | -| 5051 | MARGARET | Abierto | -| 5052 | JARED | Abierto | -| 5053 | MASON | Abierto | -| 5054 | FELDMAN | Abierto | -| 5432 | PostgreSQL | Local | -| 8055 | Directus | Abierto | -| 8069 | Odoo | Abierto | -| 8080 | Nextcloud | Abierto | -| 8081 | Vaultwarden | Abierto | - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@92.112.181.188 -``` - -### Firewall - -**ALERTA CRÍTICA: UFW INACTIVO** - -```bash -# Verificar estado -ufw status - -# Activar (cuando se autorice) -ufw default deny incoming -ufw allow 22/tcp -ufw allow 80/tcp -ufw allow 443/tcp -ufw enable -``` - ---- - -## HST (72.62.2.84) - -### Especificaciones -- **OS:** Ubuntu 22.04 LTS -- **Usuario:** root -- **Servicios:** Nginx, Directus, PostgreSQL - -### Puertos - -| Puerto | Servicio | Estado | -|--------|----------|--------| -| 22 | SSH | Abierto | -| 80 | Nginx HTTP | Abierto | -| 443 | Nginx HTTPS | Abierto | -| 5432 | PostgreSQL | Local | -| 8055 | Directus | Abierto | - -### Acceso - -```bash -ssh -i ~/.ssh/tzzr root@72.62.2.84 -``` - -### API Pública - -``` -https://tzrtech.org/{h_maestro}.png -``` - -### Estadísticas HST - -| Grupo | Cantidad | -|-------|----------| -| hst | 639 | -| spe | 145 | -| vsn | 84 | -| flg | 65 | -| vue | 21 | -| **Total** | **973** | - ---- - -## LOCKER (Cloudflare R2) - -### Endpoint - -``` -https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -``` - -### Buckets - -| Bucket | Uso | Tamaño Aprox | -|--------|-----|--------------| -| architect | Backups, configs, GPU services | ~500 MB | -| deck | Archivos personales (CLARA) | Variable | -| corp | Archivos empresariales (MARGARET) | Variable | -| hst | Imágenes de tags | ~100 MB | -| locker | Almacenamiento general | Variable | - -### Acceso - -```bash -source /home/orchestrator/orchestrator/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" - -aws s3 ls s3://architect/ \ - --endpoint-url https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -``` - ---- - -## SSH Keys - -### Ubicación - -``` -/home/orchestrator/.ssh/tzzr # Private key -/home/orchestrator/.ssh/tzzr.pub # Public key -``` - -### Permisos - -```bash -chmod 600 ~/.ssh/tzzr -chmod 644 ~/.ssh/tzzr.pub -``` - -### Configuración SSH - -```bash -# ~/.ssh/config -Host deck - HostName 72.62.1.113 - User root - IdentityFile ~/.ssh/tzzr - -Host corp - HostName 92.112.181.188 - User root - IdentityFile ~/.ssh/tzzr - -Host hst - HostName 72.62.2.84 - User root - IdentityFile ~/.ssh/tzzr -``` - ---- - -## Bases de Datos - -### architect (ARCHITECT) - -``` -Host: localhost -Port: 5432 -Database: architect -User: postgres -``` - -Tablas principales: -- context_blocks -- agents -- creds_* - -### tzzr (DECK) - -``` -Host: localhost -Port: 5432 -Database: tzzr -``` - -Tablas principales: -- clara_log -- deck_visiones -- deck_milestones - -### corp (CORP) - -``` -Host: localhost -Port: 5432 -Database: corp -``` - -Tablas principales: -- margaret_log -- mason_workspace -- feldman_cola -- milestones -- bloques - -### hst_images (HST) - -``` -Host: localhost -Port: 5432 -Database: hst_images -``` - -Tablas principales: -- hst_tags -- hst_trees - ---- - -## Monitoreo - -### Comandos de Estado - -```bash -# Verificar servicios ARCHITECT -systemctl status postgresql -docker ps - -# Verificar servicios DECK -ssh deck 'docker ps' -ssh deck 'systemctl status fail2ban' - -# Verificar servicios CORP -ssh corp 'docker ps' -ssh corp 'ufw status' - -# Verificar servicios HST -ssh hst 'systemctl status nginx' -ssh hst 'systemctl status postgresql' -``` - -### Logs Importantes - -```bash -# PostgreSQL -journalctl -u postgresql -f - -# Docker containers -docker logs -f - -# Nginx (HST) -tail -f /var/log/nginx/access.log -tail -f /var/log/nginx/error.log - -# SSH auth -tail -f /var/log/auth.log -``` - ---- - -## Diagrama de Red - -``` - Internet - │ - ┌──────────────────────────┼──────────────────────────┐ - │ │ │ - ▼ ▼ ▼ - ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ - │ ARCHITECT │ │ DECK │ │ CORP │ - │ 69.62.126.110 │◄────────│ 72.62.1.113 │────────►│92.112.181.188 │ - └───────────────┘ SSH └───────────────┘ SSH └───────────────┘ - │ │ │ - │ │ │ - │ ▼ │ - │ ┌───────────────┐ │ - │ │ HST │ │ - └────────────────►│ 72.62.2.84 │◄──────────────────┘ - SSH └───────────────┘ - │ - │ HTTPS - ▼ - ┌───────────────┐ - │ Cloudflare │ - │ R2 │ - └───────────────┘ -``` diff --git a/05_OPERACIONES/runpod-serverless.md b/05_OPERACIONES/runpod-serverless.md deleted file mode 100644 index 8d81334..0000000 --- a/05_OPERACIONES/runpod-serverless.md +++ /dev/null @@ -1,169 +0,0 @@ -# RunPod Serverless - GPU Services - -## Resumen - -RunPod Serverless proporciona GPUs on-demand para los servicios GRACE, PENNY y FACTORY. - -**Cuenta**: rpd@tzr.systems -**Balance**: ~$69 USD -**Cuota máxima**: 5 workers simultáneos - ---- - -## Endpoints Activos - -| Servicio | Endpoint ID | Workers Max | Módulos | -|----------|-------------|-------------|---------| -| **GRACE** | `rfltzijgn1jno4` | 2 | ASR, OCR, TTS, Face, Embeddings, Avatar | -| **PENNY** | `zsu7eah0fo7xt6` | 2 | TTS (voz) | -| **FACTORY** | `hffu4q5pywjzng` | 1 | Embeddings, procesamiento | - -**GPUs soportadas**: RTX 3090, RTX 4090, RTX A4000, NVIDIA L4 - ---- - -## Configuración Crítica - -### Problema Resuelto (2025-12-27) - -Los workers serverless no iniciaban usando la API GraphQL. La solución fue usar la **REST API** con parámetros específicos. - -### API Correcta: REST (no GraphQL) - -```bash -# CORRECTO - REST API -curl -X POST "https://rest.runpod.io/v1/endpoints" \ - -H "Authorization: Bearer $RUNPOD_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "mi-endpoint", - "templateId": "TEMPLATE_ID", - "gpuTypeIds": ["NVIDIA GeForce RTX 3090", "NVIDIA GeForce RTX 4090"], - "scalerType": "QUEUE_DELAY", - "scalerValue": 4, - "workersMin": 0, - "workersMax": 2, - "idleTimeout": 60, - "executionTimeoutMs": 600000, - "flashboot": true - }' -``` - -**Campos obligatorios**: -- `gpuTypeIds`: Array de strings (NO string separado por comas) -- `scalerType`: "QUEUE_DELAY" -- `scalerValue`: 4 (segundos de delay antes de escalar) -- `flashboot`: true (arranque rápido) - ---- - -## Uso de la API - -### Enviar Job - -```bash -curl -X POST "https://api.runpod.ai/v2/{ENDPOINT_ID}/run" \ - -H "Authorization: $RUNPOD_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"input": {"module": "ASR_ENGINE", "audio_base64": "..."}}' -``` - -### Verificar Estado - -```bash -curl "https://api.runpod.ai/v2/{ENDPOINT_ID}/status/{JOB_ID}" \ - -H "Authorization: $RUNPOD_API_KEY" -``` - -### Health Check - -```bash -curl "https://api.runpod.ai/v2/{ENDPOINT_ID}/health" \ - -H "Authorization: $RUNPOD_API_KEY" -``` - -Respuesta esperada: -```json -{ - "jobs": {"completed": 5, "failed": 0, "inQueue": 0}, - "workers": {"idle": 1, "ready": 1, "running": 0} -} -``` - ---- - -## Módulos GRACE - -| Módulo | Descripción | Modelo | -|--------|-------------|--------| -| `ASR_ENGINE` | Speech-to-Text | Faster Whisper Large V3 | -| `OCR_CORE` | Reconocimiento de texto | GOT-OCR 2.0 | -| `TTS` | Text-to-Speech | XTTS-v2 | -| `FACE_VECTOR` | Vectores faciales | InsightFace | -| `EMBEDDINGS` | Embeddings de texto | BGE-Large | -| `AVATAR_GEN` | Generación de avatares | SDXL | - ---- - -## Docker Image - -**Registry temporal**: `ttl.sh/tzzr-grace:24h` (expira en 24h) - -### Rebuild - -```bash -# En servidor deck (72.62.1.113) -cd /tmp/docker-grace -docker build -t ttl.sh/tzzr-grace:24h . -docker push ttl.sh/tzzr-grace:24h -``` - -### Dockerfile clave - -```dockerfile -FROM runpod/pytorch:2.1.0-py3.10-cuda11.8.0-devel-ubuntu22.04 -# Fix blinker antes de requirements -RUN pip install --no-cache-dir --ignore-installed blinker -RUN pip install --no-cache-dir -r requirements.txt -``` - ---- - -## Troubleshooting - -### Workers no inician (0 workers) - -1. Usar REST API, no GraphQL -2. Verificar `gpuTypeIds` es array -3. Incluir `flashboot: true` -4. Verificar cuota de workers no excedida - -### Job en cola indefinidamente - -```bash -# Purgar cola -curl -X POST "https://api.runpod.ai/v2/{ENDPOINT_ID}/purge-queue" \ - -H "Authorization: $RUNPOD_API_KEY" -``` - -### Error de modelo (TOS) - -Agregar variable de entorno: `COQUI_TOS_AGREED=1` - ---- - -## Credenciales - -Ubicación: `/home/orchestrator/.secrets/runpod_api_key` - -```bash -export RUNPOD_API_KEY=$(cat ~/.secrets/runpod_api_key) -``` - ---- - -## Referencias - -- [RunPod REST API](https://docs.runpod.io/api-reference) -- [Serverless Endpoints](https://docs.runpod.io/serverless/endpoints/manage-endpoints) -- [Status Page](https://uptime.runpod.io) diff --git a/06_INTEGRACIONES/gpu-services.md b/06_INTEGRACIONES/gpu-services.md deleted file mode 100644 index 1561f52..0000000 --- a/06_INTEGRACIONES/gpu-services.md +++ /dev/null @@ -1,179 +0,0 @@ -# GPU Services - RunPod - -**Versión:** 5.0 -**Fecha:** 2024-12-24 - ---- - -## Estado Crítico - -**ALERTA: RunPod NO es confiable para producción.** - -### Incidente 2024-12-24 - -RunPod falló en provisionar workers a pesar de: -- Configuración correcta -- Balance suficiente ($72+) -- Jobs en cola - -**Resultado:** 0 workers activos. Servicio GPU inoperativo. - ---- - -## Endpoints Configurados - -| Servicio | Endpoint ID | Estado | -|----------|-------------|--------| -| GRACE | r00x4g3rrwkbyh | Inactivo | -| PENNY | 0mxhaokgsmgee3 | Inactivo | -| FACTORY | ddnuk6y35zi56a | Inactivo | - ---- - -## GRACE (Extracción IA) - -### Módulos Implementados (6 de 18) - -| Módulo | Descripción | -|--------|-------------| -| ASR | Speech-to-text | -| OCR | Reconocimiento texto | -| TTS | Text-to-speech | -| Face | Reconocimiento facial | -| Embeddings | Vectores semánticos | -| Avatar | Generación avatares | - -### Módulos Pendientes (12) - -- Document parsing -- Image classification -- Object detection -- Sentiment analysis -- Named entity recognition -- Translation -- Summarization -- Question answering -- Code generation -- Audio classification -- Video analysis -- Multimodal fusion - ---- - -## Código Disponible en R2 - -Los handlers están listos y son portables: - -``` -s3://architect/gpu-services/ -├── base/ -│ └── bootstrap.sh -├── grace/ -│ └── code/handler.py -├── penny/ -│ └── code/handler.py -└── factory/ - └── code/handler.py -``` - -### Descargar Código - -```bash -source /home/orchestrator/orchestrator/.env -export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" -export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" - -aws s3 sync s3://architect/gpu-services/ ./gpu-services/ \ - --endpoint-url https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com -``` - ---- - -## Alternativas a Evaluar - -### 1. Modal (Recomendado) - -- Pricing: Pay-per-use -- Pros: Serverless real, buen DX, Python-native -- Contras: Menos GPUs disponibles que RunPod - -```python -import modal - -stub = modal.Stub("grace") - -@stub.function(gpu="A10G") -def process_asr(audio_bytes): - # Whisper inference - pass -``` - -### 2. Replicate - -- Pricing: Per-inference -- Pros: Modelos pre-entrenados, API simple -- Contras: Menos control, más caro a escala - -### 3. Lambda Labs - -- Pricing: Hourly -- Pros: Hardware dedicado -- Contras: Menos flexible, reserva manual - -### 4. Self-Hosted - -- Pricing: Upfront + hosting -- Pros: Control total, sin dependencias -- Contras: CapEx alto, mantenimiento - ---- - -## Migración Propuesta - -### Fase 1: Evaluación (1 semana) -- [ ] Probar Modal con ASR handler -- [ ] Comparar latencia y costo - -### Fase 2: Migración (2 semanas) -- [ ] Portar 6 handlers a Modal -- [ ] Actualizar endpoints en MASON - -### Fase 3: Producción -- [ ] Desplegar en Modal -- [ ] Deprecar RunPod endpoints - ---- - -## Configuración Actual RunPod - -### API Key - -``` -Almacenada en: creds_runpod (PostgreSQL ARCHITECT) -Campo: api_key_runpod -``` - -### SDK - -```python -import runpod - -runpod.api_key = "..." - -# Crear job -job = runpod.run( - endpoint_id="r00x4g3rrwkbyh", - input={"audio": "base64..."} -) - -# Check status -status = runpod.status(job["id"]) -``` - ---- - -## Recomendación - -**NO confiar en RunPod para producción.** - -Migrar a Modal como prioridad alta una vez resueltos los issues de seguridad críticos. diff --git a/06_SEGURIDAD/cifrado.md b/06_SEGURIDAD/cifrado.md new file mode 100644 index 0000000..7078797 --- /dev/null +++ b/06_SEGURIDAD/cifrado.md @@ -0,0 +1,170 @@ +# Cifrado + +**Estado:** Especificación + +--- + +## Principio KEY VAULT + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ "Las llaves nunca viajan con los datos que protegen" │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Envelope Encryption + +### Jerarquía de Llaves + +``` + MASTER KEY (MK) + (HSM / KMS externo) + │ + ┌───────────┼───────────┐ + │ │ │ + ▼ ▼ ▼ + KEK-DATA KEK-SECRETS KEK-SIGNING + (Cifra (Cifra API (Firma + datos) keys) tokens) + │ │ │ + ▼ ▼ ▼ + DEK-1..n API Keys JWT Keys + (Efímeras) (Rotables) (Rotables) +``` + +--- + +## Niveles + +### 1. MASTER KEY (MK) + +| Aspecto | Valor | +|---------|-------| +| Almacenamiento | HSM/KMS externo | +| Exposición | Nunca se expone | +| Rotación | Anual | +| Uso | Deriva KEKs | + +### 2. Key Encryption Keys (KEK) + +| KEK | Función | Rotación | +|-----|---------|----------| +| KEK-DATA | Cifra datos | 365 días | +| KEK-SECRETS | Cifra API keys | 365 días | +| KEK-SIGNING | Firma tokens | 180 días | + +### 3. Data Encryption Keys (DEK) + +| Aspecto | Valor | +|---------|-------| +| Generación | Por operación | +| Cifrado | Con KEK correspondiente | +| Rotación | Por uso (efímeras) | + +--- + +## Ventajas de Envelope Encryption + +| Ventaja | Descripción | +|---------|-------------| +| **Rotación eficiente** | Rotar KEKs sin re-cifrar todos los datos | +| **Separación** | Diferentes KEKs para diferentes propósitos | +| **Trazabilidad** | Cada nivel tiene su propio ciclo de vida | +| **Seguridad en capas** | Compromiso de DEK no expone otras DEKs | + +--- + +## Cifrado en Reposo + +### Base de Datos + +| Componente | Cifrado | +|------------|---------| +| PostgreSQL | Transparent Data Encryption (TDE) | +| Backups | AES-256-GCM con KEK-DATA | +| Logs sensibles | AES-256-GCM con KEK-DATA | + +### Almacenamiento + +| Componente | Cifrado | +|------------|---------| +| Cloudflare R2 | SSE con llaves gestionadas | +| Backups locales | AES-256-GCM | +| Archivos temporales | Cifrado en memoria | + +--- + +## Cifrado en Tránsito + +| Conexión | Protocolo | +|----------|-----------| +| HTTPS | TLS 1.3 | +| PostgreSQL | SSL required | +| Redis | TLS | +| SSH | Ed25519 | + +--- + +## Gestión de Llaves + +### Ubicación + +| Llave | Ubicación | +|-------|-----------| +| MASTER KEY | HSM / KMS externo | +| KEKs | Infisical (cifradas) | +| DEKs | Generadas en runtime | +| API Keys | Infisical | + +### Acceso + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Servicio │────▶│ Infisical │────▶│ KEK/DEK │ +│ │ │ (API) │ │ │ +└─────────────┘ └─────────────┘ └─────────────┘ + │ │ + │ (nunca en mismo lugar) │ + ▼ ▼ +┌─────────────┐ ┌─────────────┐ +│ Datos │ │ Llaves │ +│ Cifrados │ │ │ +└─────────────┘ └─────────────┘ +``` + +--- + +## Algoritmos + +| Uso | Algoritmo | +|-----|-----------| +| Cifrado simétrico | AES-256-GCM | +| Hashing | SHA-256, SHA-512 | +| Firmas | Ed25519, ECDSA | +| KDF | Argon2id | +| TLS | TLS 1.3 | + +--- + +## Rotación de Llaves + +| Llave | Proceso | +|-------|---------| +| **MASTER KEY** | Re-derivar todas las KEKs, re-cifrar DEKs | +| **KEK** | Re-cifrar DEKs asociadas | +| **DEK** | Generar nueva, re-cifrar datos específicos | + +--- + +## Auditoría + +| Evento | Log | +|--------|-----| +| Acceso a KEK | SENTINEL-LIGHT | +| Uso de DEK | Trace en S-CONTRACT | +| Rotación | Alerta + registro | +| Compromiso | SENTINEL-DEEP + alerta crítica | diff --git a/06_SEGURIDAD/inmutabilidad.md b/06_SEGURIDAD/inmutabilidad.md new file mode 100644 index 0000000..982ecf2 --- /dev/null +++ b/06_SEGURIDAD/inmutabilidad.md @@ -0,0 +1,119 @@ +# Inmutabilidad + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Principio + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ Los datos de entrada y salida nunca se modifican. │ +│ La trazabilidad siempre es posible. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Componentes Inmutables + +| Componente | Mutabilidad | Eliminación | +|------------|-------------|-------------| +| Secretaría (Clara/Margaret) | **Inmutable** | Prohibida | +| Contable (Feldman) | **Inmutable** | Prohibida | + +--- + +## Componentes Editables + +| Componente | Mutabilidad | Eliminación | +|------------|-------------|-------------| +| Producción (Alfred/Jared) | Editable | Permitida | +| Administración (Mason) | Editable | Siempre (tras consolidar) | + +--- + +## Cadena de Hashes + +### Encadenamiento + +``` +Bloque 1 Bloque 2 Bloque 3 +┌────────────┐ ┌────────────┐ ┌────────────┐ +│h_bloque: A │──────►│hash_prev: A│──────►│hash_prev: B│ +│content: X │ │h_bloque: B │ │h_bloque: C │ +│ │ │content: Y │ │content: Z │ +└────────────┘ └────────────┘ └────────────┘ +``` + +### Verificación + +```python +def verificar_cadena_completa(): + bloques = obtener_todos_bloques_ordenados() + + for i, bloque in enumerate(bloques): + # Verificar hash de contenido + if not verificar_integridad(bloque): + return False, f"Integridad fallida en bloque {i}" + + # Verificar encadenamiento + if i > 0: + if bloque.hash_previo != bloques[i-1].h_bloque: + return False, f"Encadenamiento roto en bloque {i}" + + return True, "Cadena válida" +``` + +--- + +## Reglas de Validación + +| Regla | Nombre | Descripción | +|-------|--------|-------------| +| M-001 | Hash único | h_bloque no existe previamente | +| M-002 | Encadenamiento | hash_previo apunta a bloque existente | +| M-003 | Integridad | hash_contenido = SHA-256(contenido) | + +--- + +## Trazabilidad + +Cualquier registro en Feldman puede rastrearse hasta Secretaría: + +``` +Bloque (Feldman) + │ + └── h_entrada ──► Contenedor (Secretaría) + │ + ├── timestamp + ├── origen + ├── usuario + └── archivos_hashes ──► R2 +``` + +--- + +## Auditoría + +Sentinel verifica periódicamente: + +| Modo | Frecuencia | Verificación | +|------|------------|--------------| +| LIGHT | 5 min | Últimos bloques | +| DEEP | 1 hora | Cadena completa | + +--- + +## Blockchain Ready + +La estructura de bloques encadenados está preparada para: + +- Publicación en blockchain (Polygon) +- Merkle tree para verificación +- Smart contracts para sellado + +**Estado:** Preparado pero no implementado. diff --git a/06_SEGURIDAD/modelo-amenazas.md b/06_SEGURIDAD/modelo-amenazas.md new file mode 100644 index 0000000..f2a104e --- /dev/null +++ b/06_SEGURIDAD/modelo-amenazas.md @@ -0,0 +1,135 @@ +# Modelo de Amenazas + +**Versión:** 1.0 +**Estado:** Definición + +--- + +## Activos Críticos + +| Activo | Criticidad | Descripción | +|--------|------------|-------------| +| Bloques consolidados | Alta | Datos inmutables de Feldman | +| Cadena de hashes | Alta | Integridad del sistema | +| Credenciales | Alta | Acceso a servicios | +| Datos de usuario | Media | Información personal/empresarial | +| Configuración | Media | Parámetros del sistema | + +--- + +## Amenazas Identificadas + +### T1: Manipulación de Datos + +| Aspecto | Valor | +|---------|-------| +| Descripción | Alteración de bloques consolidados | +| Impacto | Crítico | +| Probabilidad | Baja | +| Mitigación | Inmutabilidad, cadena de hashes, auditoría | + +### T2: Acceso No Autorizado + +| Aspecto | Valor | +|---------|-------| +| Descripción | Acceso a datos sin permiso | +| Impacto | Alto | +| Probabilidad | Media | +| Mitigación | Autenticación, autorización, cifrado | + +### T3: Pérdida de Datos + +| Aspecto | Valor | +|---------|-------| +| Descripción | Pérdida de información por fallo | +| Impacto | Alto | +| Probabilidad | Media | +| Mitigación | Backups, replicación, DR | + +### T4: Compromiso de Credenciales + +| Aspecto | Valor | +|---------|-------| +| Descripción | Robo de API keys o contraseñas | +| Impacto | Alto | +| Probabilidad | Media | +| Mitigación | Infisical, rotación, least privilege | + +### T5: Denegación de Servicio + +| Aspecto | Valor | +|---------|-------| +| Descripción | Indisponibilidad del sistema | +| Impacto | Medio | +| Probabilidad | Baja | +| Mitigación | Rate limiting, CDN, redundancia | + +--- + +## Matriz de Riesgos + +``` + IMPACTO + Bajo Medio Alto Crítico + ┌────────┬────────┬────────┬────────┐ + Alta │ │ │ T2 │ │ + ├────────┼────────┼────────┼────────┤ +P Media │ │ T5 │ T3,T4 │ │ +R ├────────┼────────┼────────┼────────┤ +O Baja │ │ │ │ T1 │ +B ├────────┼────────┼────────┼────────┤ + Muy │ │ │ │ │ + Baja │ │ │ │ │ + └────────┴────────┴────────┴────────┘ +``` + +--- + +## Controles + +### Preventivos + +| Control | Amenazas | +|---------|----------| +| Cifrado en reposo | T1, T2 | +| Cifrado en tránsito | T2 | +| Autenticación MFA | T2, T4 | +| Rotación de secretos | T4 | +| Principio least privilege | T2, T4 | + +### Detectivos + +| Control | Amenazas | +|---------|----------| +| Sentinel LIGHT | T1 | +| Sentinel DEEP | T1 | +| Logs de acceso | T2 | +| Alertas de anomalías | T2, T5 | + +### Correctivos + +| Control | Amenazas | +|---------|----------| +| Backups | T3 | +| DR plan | T3, T5 | +| Revocación de credenciales | T4 | + +--- + +## Respuesta a Incidentes + +### Proceso + +1. **Detectar** - Sentinel, logs, alertas +2. **Contener** - Aislar sistema afectado +3. **Erradicar** - Eliminar amenaza +4. **Recuperar** - Restaurar servicio +5. **Documentar** - Lecciones aprendidas + +### Contactos + +| Rol | Responsabilidad | +|-----|-----------------| +| Administrador | Primera respuesta | +| Propietario | Decisiones de negocio | +| Backup | Restauración de datos | diff --git a/06_SEGURIDAD/respuesta-incidentes.md b/06_SEGURIDAD/respuesta-incidentes.md new file mode 100644 index 0000000..aef3d1d --- /dev/null +++ b/06_SEGURIDAD/respuesta-incidentes.md @@ -0,0 +1,101 @@ +# Respuesta a Incidentes + +**Estado:** Activo + +--- + +## Respuesta a Compromiso de Credencial + +| Tiempo | Acción | +|--------|--------| +| **INMEDIATO** | Revocar credencial comprometida en el servicio origen | +| **T+5min** | Generar y desplegar nueva credencial | +| **T+15min** | Auditar logs de acceso para evaluar impacto | +| **T+1h** | Documentar incidente y actualizar procedimientos | + +--- + +## Niveles de Severidad + +| Severidad | Descripción | Tiempo Respuesta | +|-----------|-------------|------------------| +| **CRITICAL** | Master Key, KEK comprometida | Inmediato | +| **HIGH** | API Key producción, DB credential | < 5 min | +| **MEDIUM** | Service token, webhook | < 30 min | +| **LOW** | Token de desarrollo | < 24h | + +--- + +## Acciones por Tipo de Incidente + +### Compromiso de API Key + +``` +1. Revocar en el servicio (Anthropic, OpenRouter, etc.) +2. Generar nueva key +3. Actualizar en Infisical +4. Verificar servicios +5. Revisar logs +``` + +### Compromiso de DB Credential + +``` +1. Cambiar contraseña en PostgreSQL +2. Actualizar en Infisical +3. Reiniciar servicios dependientes +4. Revisar accesos anómalos +``` + +### Compromiso de SSH Key + +``` +1. Revocar en authorized_keys +2. Generar nuevo par de llaves +3. Actualizar en todos los servidores +4. Revisar logs de acceso SSH +``` + +### Compromiso de KEK/Master Key + +``` +1. ALERTA CRÍTICA inmediata +2. Rotar TODAS las credenciales derivadas +3. Re-cifrar datos con nueva KEK +4. Auditoría completa de accesos +5. Análisis forense +``` + +--- + +## Post-Incidente + +| Paso | Descripción | +|------|-------------| +| 1 | Documentar timeline completo | +| 2 | Identificar causa raíz | +| 3 | Actualizar procedimientos | +| 4 | Comunicar a stakeholders si aplica | +| 5 | Implementar medidas preventivas | + +--- + +## Contactos de Emergencia + +| Rol | Canal | +|-----|-------| +| Admin principal | Slack #alerts | +| Backup admin | ntfy + Email | +| Escalación | PagerDuty | + +--- + +## Checklist de Incidente + +- [ ] Credencial revocada +- [ ] Nueva credencial generada +- [ ] Servicios verificados +- [ ] Logs revisados +- [ ] Impacto evaluado +- [ ] Incidente documentado +- [ ] Stakeholders notificados (si aplica) diff --git a/06_SEGURIDAD/rotacion.md b/06_SEGURIDAD/rotacion.md new file mode 100644 index 0000000..f159ea1 --- /dev/null +++ b/06_SEGURIDAD/rotacion.md @@ -0,0 +1,62 @@ +# Política de Rotación + +**Estado:** Activo + +--- + +## Matriz por Tipo de Credencial + +| Tipo | Rotación | Ejemplos | Responsable | +|------|----------|----------|-------------| +| `API_KEY` | 90 días | OpenRouter, Groq, Cloudflare | Automatizado + Revisión | +| `DB_CREDENTIAL` | 30 días | PostgreSQL, Redis | Automatizado | +| `SERVICE_TOKEN` | 7 días | n8n, Nextcloud, Slack webhooks | Automatizado | +| `CERTIFICATE` | 90 días | SSL/TLS (Let's Encrypt) | Traefik auto-renovación | +| `SIGNING_KEY` | 180 días | JWT, KEK-SIGNING | Manual + Notificación | +| `ENCRYPTION_KEY` | 365 días | AES-256, KEK-DATA | Manual + Auditoría | +| `SSH_CREDENTIAL` | 180 días | Llaves SSH servidores | Manual + Backup | + +--- + +## Procedimiento de Rotación + +``` +1. SENTINEL genera alerta 7 días antes de expiración +2. Generar nueva credencial en el servicio +3. Actualizar en Infisical (versión anterior se mantiene 24h para rollback) +4. Verificar funcionamiento de servicios dependientes +5. Revocar credencial anterior en el servicio origen +``` + +--- + +## Alertas de Rotación + +| Tiempo restante | Severidad | Acción | +|-----------------|-----------|--------| +| 7 días | MEDIUM | Email a admin | +| 24 horas | HIGH | Email + Slack | +| Expirada | CRITICAL | Email + Slack + PagerDuty | + +--- + +## Calendario de Rotación + +| Frecuencia | Credenciales | +|------------|--------------| +| **Semanal** | SERVICE_TOKEN (webhooks, tokens temporales) | +| **Mensual** | DB_CREDENTIAL (PostgreSQL, Redis) | +| **Trimestral** | API_KEY, CERTIFICATE | +| **Semestral** | SIGNING_KEY, SSH_CREDENTIAL | +| **Anual** | ENCRYPTION_KEY, MASTER_KEY | + +--- + +## Automatización + +| Tarea | Herramienta | +|-------|-------------| +| Alertas expiración | SENTINEL | +| Renovación certificados | Traefik + Let's Encrypt | +| Rotación DB credentials | Script + Infisical API | +| Notificaciones | ntfy + Slack | diff --git a/06_SEGURIDAD/secretos.md b/06_SEGURIDAD/secretos.md new file mode 100644 index 0000000..c8f5c3f --- /dev/null +++ b/06_SEGURIDAD/secretos.md @@ -0,0 +1,115 @@ +# Gestión de Secretos + +**Gestor:** Infisical +**Estado:** Operativo + +--- + +## Principio + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ Las llaves nunca viajan con los datos que protegen. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Infisical + +| Parámetro | Valor | +|-----------|-------| +| URL | http://{ip}:8082 | +| Proyectos | anthropic, servers, databases, r2 | +| Acceso | Machine Identities | + +--- + +## Proyectos + +| Proyecto | Contenido | +|----------|-----------| +| **anthropic** | API keys de IA | +| **servers** | SSH, accesos servidores | +| **databases** | Credenciales PostgreSQL, Redis | +| **r2** | Keys de Cloudflare R2 | + +--- + +## Categorías de Secretos + +| Categoría | Ejemplos | +|-----------|----------| +| APIs IA | OpenRouter, Groq, Anthropic, OpenAI | +| Bases de datos | PostgreSQL, Redis | +| Almacenamiento | Storj, R2, Arweave | +| Infraestructura | n8n, Nextcloud, Windmill | +| Blockchain | Polygon, NOTARIO | +| Comunicaciones | Resend, Slack, ntfy | +| DNS/CDN | Cloudflare, Let's Encrypt | +| GPU | RunPod | +| Servidores | SSH credentials | +| Cifrado | Master Key, KEKs | + +--- + +## Matriz de Rotación + +| Tipo | Rotación | Ejemplos | +|------|----------|----------| +| API_KEY | 90 días | OpenRouter, Groq, Cloudflare | +| DB_CREDENTIAL | 30 días | PostgreSQL, Redis | +| SERVICE_TOKEN | 7 días | n8n, Nextcloud, Slack | +| CERTIFICATE | 90 días | SSL/TLS (auto) | +| SIGNING_KEY | 180 días | JWT, KEK-SIGNING | +| ENCRYPTION_KEY | 365 días | AES-256, KEK-DATA | +| SSH_CREDENTIAL | 180 días | Llaves SSH | + +--- + +## Acceso por Instancia + +Cada instancia tiene su propia Machine Identity: + +``` +ARCHITECT → Machine Identity: architect_prod +DECK → Machine Identity: deck_prod +CORP → Machine Identity: corp_prod +HST → Machine Identity: hst_prod +``` + +--- + +## Uso en Código + +```python +from infisical import InfisicalClient + +client = InfisicalClient( + client_id=os.environ["INFISICAL_CLIENT_ID"], + client_secret=os.environ["INFISICAL_CLIENT_SECRET"] +) + +# Obtener secreto +api_key = client.get_secret( + project_id="anthropic", + environment="production", + secret_name="ANTHROPIC_API_KEY" +) +``` + +--- + +## Nunca en Código + +Los siguientes NUNCA deben estar en repositorios: + +- API keys +- Contraseñas de BD +- Tokens de acceso +- Llaves privadas +- Certificados + +Siempre usar Infisical o variables de entorno. diff --git a/99_ANEXOS/changelog.md b/99_ANEXOS/changelog.md new file mode 100644 index 0000000..023ff30 --- /dev/null +++ b/99_ANEXOS/changelog.md @@ -0,0 +1,184 @@ +# Changelog + +--- + +## 2024-12-28 (v2) + +### FASE 1 - Contenido Técnico Puro + +- Añadidos 6 schemas SQL reales a `03_MODELO_DATOS/schemas/` +- Añadidos 3 M-CONTRACTs JSON a `05_INTEGRACIONES/m-contracts/` +- Creado `04_INFRAESTRUCTURA/status.md` con IPs, puertos, h_instancia +- Creado `03_MODELO_DATOS/contenedor-schema.md` con estructura JSON +- Expandido `01_ARQUITECTURA/aplicaciones/mind-link.md` con spec UI v11 + +### FASE 2 - Flujos y Protocolos (auditado) + +- Corregido `03_MODELO_DATOS/flujos.md`: + - Producción OK salta Mason + - Feldman = Cola + Bloques (dos tablas) + - Ventanas 24h configurables +- Expandido `05_INTEGRACIONES/s-contract.md`: + - Perfiles FULL/LITE + - Modos despliegue (EXTERNAL, SELF_HOSTED, SEMI) + - Request/Response completos + - Jerarquía S/M-CONTRACT +- Expandido `02_COMPONENTES/auditoria.md`: + - Arquitectura SENTINEL-LIGHT/DEEP + - Reglas de integridad (I-*) + - Reglas de conformidad (C-*) + - Reglas de rendimiento (P-*) + - Schema SQL + +### FASE 3 - Protocolos Adicionales + +- Creado `05_INTEGRACIONES/factory-protocol.md`: + - Ciclo Director → Executor → Evaluator → Auditor + - Convergencia y configuración +- Creado `05_INTEGRACIONES/notario.md`: + - Sellado blockchain con Merkle trees + - Schema SQL notario_batches + - Verificación de integridad +- Expandido `02_COMPONENTES/cognitiva.md`: + - Catálogo completo de 18 módulos Grace + - 5 familias: Visión, Voz, Identidad, Semántica, Utilidades + - Cadenas de fallback + +### FASE 4 - Contenido v4 (credenciales y políticas) + +- Expandido `99_ANEXOS/inventario-credenciales.md`: + - 10 categorías: APIs IA, BD, almacenamiento, infra, blockchain, comunicaciones, DNS, GPU, servidores, cifrado + - Modelos Anthropic y configuración instancias + - Tablas PostgreSQL principales + - IPs y dominios de todos los servidores +- Creado `06_SEGURIDAD/rotacion.md`: + - Matriz por tipo de credencial + - Procedimiento de rotación + - Alertas y calendario +- Creado `06_SEGURIDAD/respuesta-incidentes.md`: + - Tiempos de respuesta por severidad + - Acciones por tipo de incidente + - Checklist post-incidente +- Expandido `06_SEGURIDAD/cifrado.md`: + - Envelope encryption completo + - Jerarquía MK → KEK → DEK + - Algoritmos y rotación +- Expandido `01_ARQUITECTURA/entidades/deck.md`: + - Arquitectura deployment router + - Modos EXTERNAL, SELF_HOSTED, SEMI + - Conectores GRACE, PENNY, FACTORY + - Servicios Docker y credenciales + +### FASE 5 - Contenido v3 (contexto y voz) + +- Creado `03_MODELO_DATOS/context-tables.md`: + - Tablas instancias, memoria, conocimiento, conversaciones, mensajes_v2 + - Tabla contexto_ambiental + - Flujo de contexto para Claude + - Diagrama de relaciones +- Expandido `02_COMPONENTES/asistente.md` (PENNY): + - Arquitectura completa VAD → ASR → Core → TTS + - Planos de información (0-5) + - Flujo voz-a-voz con latencias + - Configuración YAML + - Modelos autoalojados + +### Corrección - Schemas faltantes de v5 + +- Añadido `06_clara.sql`: Log inmutable de secretaría +- Añadido `07_feldman.sql`: Libros contables (milestones, bloques, cola, validaciones) +- Añadido `08_alfred.sql`: Flujos de producción + +### Estadísticas Finales + +| Métrica | v1 | v2 | +|---------|----|----| +| Documentos .md | 45 | 53 | +| Archivos SQL | 0 | 9 | +| Archivos JSON | 0 | 3 | +| Total | 45 | 65 | + +--- + +## 2024-12-28 (v1) + +### Documentación + +- Creación de estructura documental completa +- 40+ documentos organizados en 7 secciones +- Esquema basado en análisis de backups v3, v4, v5 + +### Estructura + +``` +00_VISION (2 docs) +01_ARQUITECTURA (9 docs) +02_COMPONENTES (12 docs) +03_MODELO_DATOS (5 docs) +04_INFRAESTRUCTURA (3 docs) +05_INTEGRACIONES (3 docs) +06_SEGURIDAD (4 docs) +99_ANEXOS (4 docs) +``` + +--- + +## 2024-12-24 + +### Sistema + +- Migración NocoDB → Directus en todos los servidores +- Nuevos dominios: tzzrarchitect.me, tzzrdeck.me, tzzrcorp.me + +### Descubrimientos + +- RunPod: pods corriendo 24/7 innecesariamente ($75 gasto) +- Pods detenidos: grace, penny, factory +- Serverless endpoints ya existían + +--- + +## 2024-12-22/23 + +### Sesión Multi-instancia + +- ARCHITECT como hub central +- Coordinación con DECK, CORP, HST, LOCKER +- Documentación DNS en zone files +- Actualización de repositorios Gitea + +### Servicios Documentados + +- CORP: 11 servicios +- DECK: 9 servicios +- HST: API v3.2 + +--- + +## Versiones de Documentos + +| Documento | Versión | Fecha | +|-----------|---------|-------| +| Sistema Registro Inmutable | 1.2 | 2024-12-27 | +| Context System | 0.1 | 2024-12-27 | +| S-CONTRACT | 2.1 | 2024-12-24 | +| Manual Arquitectura SHE | 5.0 | 2024-12-22 | + +--- + +## Convenciones + +### Versionado + +- **Major (X.0)**: Cambios incompatibles +- **Minor (X.Y)**: Nueva funcionalidad compatible +- **Patch (X.Y.Z)**: Correcciones + +### Estados + +| Estado | Significado | +|--------|-------------| +| Planificado | Definido pero no iniciado | +| En desarrollo | Trabajo en curso | +| Implementado | Funcional en producción | +| Obsoleto | Reemplazado o descontinuado | diff --git a/99_ANEXOS/inventario-credenciales.md b/99_ANEXOS/inventario-credenciales.md new file mode 100644 index 0000000..f702bcb --- /dev/null +++ b/99_ANEXOS/inventario-credenciales.md @@ -0,0 +1,240 @@ +# Inventario de Credenciales + +**Actualizado:** 2024-12-22 + +--- + +## 1. APIs de Inteligencia Artificial + +| Servicio | Variable | Tipo | Rotación | Proyecto Infisical | +|----------|----------|------|----------|-------------------| +| OpenRouter | `OPENROUTER_API_KEY` | API_KEY | 90 días | anthropic | +| Groq | `GROQ_API_KEY` | API_KEY | 90 días | anthropic | +| OpenAI | `OPENAI_API_KEY` | API_KEY | 90 días | anthropic | +| Anthropic | `ANTHROPIC_API_KEY` | API_KEY | 90 días | anthropic | + +### Uso por Servicio + +| Servicio | Función | +|----------|---------| +| OpenRouter | LLM Principal | +| Groq | Backup LLM | +| OpenAI | Embeddings | +| Anthropic | Agentes Claude | + +### Modelos Anthropic + +| Modelo | ID | Uso | +|--------|-----|-----| +| Opus 4.5 | claude-opus-4-5-20251101 | Tareas complejas | +| Sonnet 4.5 | claude-sonnet-4-5-20251101 | Balance calidad/coste | +| Haiku 3.5 | claude-3-5-haiku-20241022 | Tareas rápidas | + +### Configuración Instancias Claude + +| Instancia | Modelo | +|-----------|--------| +| architect | sonnet | +| hst | sonnet | +| deck | sonnet | +| corp | sonnet | +| runpod | sonnet | +| locker | haiku | + +--- + +## 2. Bases de Datos + +| Servicio | Variable | Tipo | Rotación | Servidor | +|----------|----------|------|----------|----------| +| PostgreSQL | `POSTGRES_PASS` | DB_CREDENTIAL | 30 días | ARCHITECT | +| Directus | `DIRECTUS_ADMIN_PASS` | SERVICE_TOKEN | 30 días | ARCHITECT | +| Redis | `REDIS_PASSWORD` | DB_CREDENTIAL | 30 días | ARCHITECT | +| Gitea | `GITEA_DB_PASSWD` | DB_CREDENTIAL | 30 días | ARCHITECT | + +### PostgreSQL (ARCHITECT) + +``` +Host: localhost / 172.17.0.1 (Docker) +Port: 5432 +User: architect +Database: architect +``` + +### Tablas Principales + +| Tabla | Uso | +|-------|-----| +| instancias | Configuración instancias Claude | +| conversaciones | Chats activos | +| mensajes_v2 | Mensajes de conversaciones | +| memoria | Memoria persistente | +| conocimiento | Base de conocimiento | +| contexto_ambiental | Contexto del sistema | + +### Directus (ARCHITECT) + +``` +URL: http://69.62.126.110:8055 +Email: admin@tzzr.me +``` + +### Redis (ARCHITECT) + +``` +Host: localhost +Port: 6379 +``` + +--- + +## 3. Almacenamiento + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| Cloudflare R2 | `R2_ACCESS_KEY_ID` | API_KEY | 90 días | +| Cloudflare R2 | `R2_SECRET_ACCESS_KEY` | API_KEY | 90 días | +| Hostinger S3 | `HOSTINGER_ACCESS_KEY` | API_KEY | 90 días | + +### Buckets R2 + +| Bucket | Uso | +|--------|-----| +| tzzr-backups | Backups automáticos | +| tzzr-assets | Assets estáticos | +| tzzr-uploads | Uploads usuarios | + +--- + +## 4. Infraestructura + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| Hostinger | `HOSTINGER_API_TOKEN` | API_KEY | 90 días | +| Cloudflare | `CF_API_TOKEN` | API_KEY | 90 días | +| Traefik | `TRAEFIK_DASHBOARD_PASS` | SERVICE_TOKEN | 30 días | + +--- + +## 5. Blockchain + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| Ethereum | `ETH_PRIVATE_KEY` | SIGNING_KEY | 180 días | +| Infura | `INFURA_API_KEY` | API_KEY | 90 días | + +--- + +## 6. Comunicaciones + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| Slack | `SLACK_WEBHOOK_URL` | SERVICE_TOKEN | 7 días | +| ntfy | `NTFY_TOKEN` | SERVICE_TOKEN | 7 días | +| Email SMTP | `SMTP_PASSWORD` | SERVICE_TOKEN | 30 días | + +--- + +## 7. DNS y CDN + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| Cloudflare DNS | `CF_DNS_TOKEN` | API_KEY | 90 días | +| Let's Encrypt | `LETSENCRYPT_EMAIL` | CERTIFICATE | 90 días | + +--- + +## 8. GPU / RunPod + +| Servicio | Variable | Tipo | Rotación | +|----------|----------|------|----------| +| RunPod | `RUNPOD_API_KEY` | API_KEY | 90 días | +| RunPod Endpoint | `RUNPOD_ENDPOINT_ID` | CONFIG | N/A | + +### Endpoints Activos + +| Módulo | Endpoint | +|--------|----------| +| ASR_ENGINE | https://api.runpod.ai/v2/{asr_id} | +| OCR_CORE | https://api.runpod.ai/v2/{ocr_id} | +| EMBEDDINGS | https://api.runpod.ai/v2/{emb_id} | + +--- + +## 9. Acceso a Servidores + +| Servidor | IP | Dominio | Tipo | Función | +|----------|-----|---------|------|---------| +| ARCHITECT | 69.62.126.110 | tzzr.me | SSH_CREDENTIAL | Central | +| HST | 72.62.2.84 | tzrtech.org | SSH_CREDENTIAL | Tags HST | +| DECK | 72.62.1.113 | tzzrdeck.me | SSH_CREDENTIAL | Personal | +| CORP | 92.112.181.188 | tzzrcorp.me | SSH_CREDENTIAL | Empresas | + +### ARCHITECT (69.62.126.110) + +``` +Dominio: tzzr.me +Proveedor: Hostinger KVM2 +SSH Port: 22 +User: root +``` + +#### Servicios Docker + +| Servicio | Puerto | +|----------|--------| +| Directus | 8055 | +| Gitea | 3000 | +| Windmill | 8100 | +| Infisical | 8082 | + +### HST (72.62.2.84) + +``` +Dominio: tzrtech.org +Función: Tags HST +``` + +### DECK (72.62.1.113) + +``` +Dominio: tzzrdeck.me +Función: Personal +``` + +### CORP (92.112.181.188) + +``` +Dominio: tzzrcorp.me +Función: Empresas +``` + +--- + +## 10. Cifrado + +| Tipo | Variable | Rotación | +|------|----------|----------| +| Master Key | `MASTER_KEY` | 365 días | +| KEK-DATA | `KEK_DATA` | 365 días | +| KEK-SECRETS | `KEK_SECRETS` | 365 días | +| KEK-SIGNING | `KEK_SIGNING` | 180 días | +| JWT Secret | `JWT_SECRET` | 180 días | + +--- + +## Regla de Ejecución + +> Cada instancia Claude solo ejecuta en su propio servidor. +> Para otros servidores, delega vía PostgreSQL. + +--- + +## Ubicación de Credenciales + +| Tipo | Ubicación | +|------|-----------| +| API Keys | Infisical | +| Contraseñas | Proton Pass | +| SSH Keys | /root/.ssh/ | +| Certificados | Traefik (auto-renovación) | diff --git a/99_ANEXOS/inventario-repos.md b/99_ANEXOS/inventario-repos.md index 947adc4..53f377e 100644 --- a/99_ANEXOS/inventario-repos.md +++ b/99_ANEXOS/inventario-repos.md @@ -1,387 +1,83 @@ -# Inventario de Repositorios TZZR +# Inventario de Repositorios -**Versión:** 5.0 -**Fecha:** 2024-12-24 -**Fuente:** Gitea (http://localhost:3000/tzzr) +**Actualizado:** 2024-12-28 --- -## Resumen +## Repositorios tzzr/ -| Categoría | Cantidad | -|-----------|----------| -| Infraestructura | 6 | -| Data Services | 6 | -| Security/Ops | 6 | -| Interfaces | 6 | -| **Total** | **24** | +| Repo | Descripción | Estado | +|------|-------------|--------| +| **system-docs** | Documentación central | Activo | +| **architect** | Servidor coordinador | Activo | +| **deck** | Servidor personal | Activo | +| **corp** | Servidor empresarial | Activo | +| **hst** | Tags semánticos | Activo | +| **packet** | App móvil | En desarrollo | +| **grace** | Capa cognitiva IA | Activo | +| **penny** | Asistente de voz | Activo | +| **the-factory** | Generación iterativa | Activo | +| **orchestrator** | Multi-agente | Activo | +| **clara** | Secretaría personal | Activo | +| **margaret** | Secretaría corporativa | Activo | +| **mason** | Administración | Activo | +| **feldman** | Contable | Activo | +| **alfred** | Producción personal | Activo | +| **jared** | Producción corporativa | Planificado | +| **sentinel** | Auditoría | Planificado | +| **locker** | Almacenamiento R2 | Activo | +| **vision-builder** | App de visiones | En desarrollo | +| **mind-link** | Interfaz unificada | Planificado | +| **contratos-comunes** | S-CONTRACT, schemas | Activo | --- -## Infraestructura +## Repositorios admin/ -### orchestrator - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/orchestrator | -| Estado | Activo | -| Descripción | Sistema de orquestación central | -| Prioridad | Alta | - -**Archivos clave:** -- `main.py` - Entrada principal -- `.env` - Configuración -- `docker-compose.yml` - Despliegue +| Repo | Descripción | Estado | +|------|-------------|--------| +| **architect-app** | Código API Flask | Activo | +| **data-structures** | Schema PostgreSQL | Activo | +| **credentials** | Estructura de secretos | Privado | --- -### hst-api +## Por Categoría -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/hst-api | -| Estado | Activo | -| Descripción | API de tags HST (973 tags) | -| Prioridad | Alta | +### Core -**Archivos clave:** -- `api/routes.py` - Endpoints -- `db/schema.sql` - Schema +- system-docs +- architect +- contratos-comunes ---- +### Servidores -### clara +- deck +- corp +- hst -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/clara | -| Estado | Activo | -| Descripción | Agente de ingesta personal | -| Prioridad | Alta | +### Componentes -**Archivos clave:** -- `app.py` - API FastAPI -- `ingest.py` - Lógica de ingesta -- `Dockerfile` - Container +- clara, margaret (secretaría) +- mason (administración) +- feldman (contable) +- alfred, jared (producción) +- sentinel (auditoría) ---- +### Servicios IA -### margaret +- grace +- penny +- the-factory +- orchestrator -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/margaret | -| Estado | Activo | -| Descripción | Agente de ingesta corporativo | -| Prioridad | Alta | +### Aplicaciones -**Archivos clave:** -- `app.py` - API FastAPI -- `ingest.py` - Lógica de ingesta -- `Dockerfile` - Container +- packet +- vision-builder +- mind-link ---- +### Infraestructura -### alfred - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/alfred | -| Estado | Activo | -| Descripción | Flujos predefinidos DECK | -| Prioridad | Media | - -**Archivos clave:** -- `flows/` - Definiciones de flujos -- `executor.py` - Motor de ejecución - ---- - -### jared - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/jared | -| Estado | Activo | -| Descripción | Flujos predefinidos CORP | -| Prioridad | Media | - -**Archivos clave:** -- `flows/` - Definiciones de flujos -- `executor.py` - Motor de ejecución - ---- - -## Data Services - -### mason - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/mason | -| Estado | Activo | -| Descripción | Enriquecimiento de datos | -| Prioridad | Alta | - -**Archivos clave:** -- `workspace.py` - Gestión workspace -- `enrichment.py` - Lógica de enriquecimiento - ---- - -### feldman - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/feldman | -| Estado | Activo | -| Descripción | Consolidación blockchain | -| Prioridad | Alta | - -**Archivos clave:** -- `validator.py` - Reglas M-001, M-002, M-003 -- `consolidator.py` - Creación de bloques - ---- - -### grace-handler - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/grace-handler | -| Estado | Bloqueado (RunPod) | -| Descripción | Handler GPU para GRACE | -| Prioridad | Alta | - -**Archivos clave:** -- `handler.py` - RunPod handler -- `modules/` - 6 módulos IA - ---- - -### penny-handler - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/penny-handler | -| Estado | Planificado | -| Descripción | Handler GPU para PENNY | -| Prioridad | Baja | - ---- - -### factory-handler - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/factory-handler | -| Estado | Planificado | -| Descripción | Handler GPU para FACTORY | -| Prioridad | Baja | - ---- - -### s-contract - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/s-contract | -| Estado | En desarrollo | -| Descripción | Contextos y datasets IA | -| Prioridad | Media | - ---- - -## Security/Ops - -### sentinel - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/sentinel | -| Estado | Planificado | -| Descripción | Auditoría del sistema | -| Prioridad | Media | - -**Modos:** -- LIGHT: Cada 5 min, reglas automáticas -- DEEP: Cada 1 hora, muestreo con LLM - ---- - -### infisical-config - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/infisical-config | -| Estado | Activo | -| Descripción | Configuración Infisical | -| Prioridad | Alta | - ---- - -### backup-scripts - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/backup-scripts | -| Estado | En desarrollo | -| Descripción | Scripts de backup R2 | -| Prioridad | Alta | - ---- - -### deploy-configs - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/deploy-configs | -| Estado | Activo | -| Descripción | Configuraciones de despliegue | -| Prioridad | Media | - ---- - -### monitoring - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/monitoring | -| Estado | Planificado | -| Descripción | Dashboards y alertas | -| Prioridad | Media | - ---- - -### security-audit - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/security-audit | -| Estado | En desarrollo | -| Descripción | Scripts de auditoría | -| Prioridad | Alta | - ---- - -## Interfaces - -### packet-app - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/packet-app | -| Estado | En desarrollo | -| Descripción | App móvil Flutter | -| Prioridad | Alta | - -**Tecnología:** Flutter/Dart - ---- - -### vision-builder - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/vision-builder | -| Estado | En desarrollo | -| Descripción | Diseñador de visiones | -| Prioridad | Media | - ---- - -### admin-dashboard - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/admin-dashboard | -| Estado | Planificado | -| Descripción | Dashboard administrativo | -| Prioridad | Baja | - ---- - -### api-gateway - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/api-gateway | -| Estado | Planificado | -| Descripción | Gateway API unificado | -| Prioridad | Media | - ---- - -### docs-site - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/docs-site | -| Estado | En desarrollo | -| Descripción | Sitio de documentación | -| Prioridad | Media | - ---- - -### system-docs - -| Campo | Valor | -|-------|-------| -| URL | http://localhost:3000/tzzr/system-docs | -| Estado | Activo (este repo) | -| Descripción | Documentación System v5 | -| Prioridad | Alta | - ---- - -## Estadísticas - -### Por Estado - -| Estado | Cantidad | -|--------|----------| -| Activo | 12 | -| En desarrollo | 6 | -| Planificado | 5 | -| Bloqueado | 1 | - -### Por Prioridad - -| Prioridad | Cantidad | -|-----------|----------| -| Alta | 12 | -| Media | 8 | -| Baja | 4 | - ---- - -## Dependencias Entre Repos - -``` -packet-app - │ - ▼ -clara / margaret - │ - ▼ -alfred / jared - │ - ▼ -mason ◄──── grace-handler (bloqueado) - │ - ▼ -feldman - │ - ▼ -sentinel (planificado) -``` - ---- - -## Notas - -1. **grace-handler**: Código listo en R2, RunPod no inicia workers -2. **sentinel**: Solo documentación, sin implementación -3. **system-docs**: Este repositorio, documentación v5 -4. **orchestrator**: Coordinador central en ARCHITECT +- locker +- credentials diff --git a/99_ANEXOS/pendientes.md b/99_ANEXOS/pendientes.md new file mode 100644 index 0000000..d683f8a --- /dev/null +++ b/99_ANEXOS/pendientes.md @@ -0,0 +1,68 @@ +# Elementos Pendientes + +**Actualizado:** 2024-12-28 + +--- + +## Por Definir + +| Elemento | Prioridad | Dependencias | +|----------|-----------|--------------| +| Estructura tabla ITM (Items) | Alta | - | +| Estructura tabla PLY (Players) | Alta | - | +| Estructura tabla LOC (Locations) | Media | - | +| Estructura tabla FLG (Flags) | Media | HST | +| Mecanismo "encaja/no encaja" | Alta | Producción | +| Formato flujos en Producción | Alta | - | +| Lista módulos Grace completa | Media | - | +| Contratos E/S de Grace | Media | S-CONTRACT | +| Scheduler período flotante Mason | Alta | - | +| Merkle tree para verificación | Media | Feldman | +| Smart contract blockchain | Baja | Merkle tree | + +--- + +## Por Implementar + +| Componente | Estado | Bloqueador | +|------------|--------|------------| +| Sentinel LIGHT | Planificado | - | +| Sentinel DEEP | Planificado | LIGHT | +| ITM (plano ideal) | Schema definido | - | +| PLY (identidad) | Schema definido | - | +| LOC (ubicaciones) | Schema definido | - | +| Mind Link | Planificado | API REST | +| Packet offline | En desarrollo | - | + +--- + +## Por Documentar + +| Documento | Ubicación | Estado | +|-----------|-----------|--------| +| API Feldman | 05_INTEGRACIONES | Pendiente | +| API Mason | 05_INTEGRACIONES | Pendiente | +| API Clara/Margaret | 05_INTEGRACIONES | Pendiente | +| Guía despliegue DECK | 04_INFRAESTRUCTURA | Parcial | +| Guía despliegue CORP | 04_INFRAESTRUCTURA | Parcial | + +--- + +## Decisiones Pendientes + +| ID | Decisión | Opciones | Estado | +|----|----------|----------|--------| +| D-008 | Motor de búsqueda contexto | PostgreSQL FTS vs Elasticsearch | Abierta | +| D-009 | Formato flujos Producción | JSON vs YAML vs DSL | Abierta | +| D-010 | Grafo de contexto | Kuzu vs Neo4j embedded | Kuzu elegido | + +--- + +## Deuda Técnica + +| Item | Impacto | Esfuerzo | +|------|---------|----------| +| Migrar NocoDB → Directus | Completado | - | +| Unificar schemas entre servidores | Medio | Alto | +| Tests automatizados | Alto | Alto | +| CI/CD pipeline | Medio | Medio | diff --git a/README.md b/README.md deleted file mode 100644 index 5b896ef..0000000 --- a/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# TZZR System Documentation v5 - -**Versión:** 5.0 -**Fecha:** 2024-12-24 -**Estado:** Consolidación completa - ---- - -## Qué es TZZR - -TZZR es un sistema de arquitecturas personales y empresariales. ARCHITECT (con Claude) construye y despliega servidores autónomos (DECK, CORP) que operan independientemente. Los componentes internos (CLARA, ALFRED, FELDMAN, etc.) son microservicios backend, no IA. Las instancias están diseñadas para **consumir** servicios de IA externos (APIs, RunPod), no para contenerla. - ---- - -## Arquitectura General - -``` -┌─────────────────────────────────────────────────────────────┐ -│ ARCHITECT (69.62.126.110) │ -│ PostgreSQL central │ Gitea │ Orchestrator │ Infisical │ -└─────────────────────────────────────────────────────────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ DECK │ │ CORP │ │ HST │ -│ 72.62.1.113 │ │ 92.112.181.188 │ │ 72.62.2.84 │ -│ │ │ │ │ │ -│ secretaria │ │ secretaria │ │ 973 tags │ -│ administrativo │ │ administrativo │ │ API pública │ -│ contable │ │ contable │ │ │ -│ producción │ │ producción │ │ │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ - └────────┬───────────┘ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Cloudflare R2 (5 buckets) │ -│ architect │ deck │ corp │ hst │ locker │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## Flujo de Datos Principal - -``` -PACKET (móvil) - ↓ -secretaria (CLARA/MARGARET) ← Ingesta inmutable - ↓ -producción (ALFRED/JARED) ← Orden de ejecución - ↓ -administrativo (MASON) ← Enriquecimiento (24h) - ↓ -contable (FELDMAN) ← Consolidación blockchain - ↓ -auditoría (SENTINEL) ← Planificado -``` - ---- - -## Índice de Documentación - -### 00_VISION -- [Filosofía](00_VISION/filosofia.md) - 10 principios fundacionales -- [Glosario](00_VISION/glosario.md) - Términos técnicos A-Z - -### 01_ARQUITECTURA -- [Overview](01_ARQUITECTURA/overview.md) - Vista general consolidada -- [Servidores](01_ARQUITECTURA/servidores.md) - ARCHITECT, DECK, CORP, HST - -### 02_MODELO_DATOS -- [Entidades](02_MODELO_DATOS/entidades.md) - HST, ITM, PLY, LOC, FLG -- [Planos](02_MODELO_DATOS/planos.md) - T0 (ITM), MST, BCK - -### 03_COMPONENTES -- [CLARA/MARGARET](03_COMPONENTES/agentes/clara-margaret.md) - Ingesta -- [FELDMAN](03_COMPONENTES/agentes/feldman.md) - Consolidación -- [GRACE](03_COMPONENTES/servicios/grace.md) - IA cognitiva - -### 04_SEGURIDAD -- [Modelo de Amenazas](04_SEGURIDAD/modelo-amenazas.md) - Riesgos -- [Secretos](04_SEGURIDAD/secretos.md) - Gestión credenciales - -### 05_OPERACIONES -- [Infraestructura](05_OPERACIONES/infraestructura.md) - Servidores -- [Backup/Recovery](05_OPERACIONES/backup-recovery.md) - DR plan - -### 06_INTEGRACIONES -- [GPU Services](06_INTEGRACIONES/gpu-services.md) - RunPod - -### 99_ANEXOS -- [Inventario Repos](99_ANEXOS/inventario-repos.md) - 24 repos - ---- - -## Estado Actual - -| Componente | Nombre | Servidor | Estado | -|------------|--------|----------|--------| -| secretaria | CLARA | DECK:5051 | Operativo | -| secretaria | MARGARET | CORP:5051 | Operativo | -| producción | ALFRED | DECK:5052 | Operativo | -| producción | JARED | CORP:5052 | Operativo | -| administrativo | MASON | DECK:5053 | Operativo | -| administrativo | MASON | CORP:5053 | Operativo | -| contable | FELDMAN | DECK:5054 | Operativo | -| contable | FELDMAN | CORP:5054 | Operativo | -| auditoría | SENTINEL | - | Planificado | -| GPU | GRACE | RunPod | Bloqueado | -| - | HST | 72.62.2.84 | Operativo (973 tags) | - ---- - -## Generación - -Documentación generada mediante proceso de auditoría colaborativa: -- 3 auditores especializados (Arquitectura, Datos, Seguridad) -- Cross-review y resolución de conflictos -- Síntesis consolidada - -**Fecha generación:** 2024-12-24 diff --git a/estructura skynet.md b/estructura skynet.md new file mode 100644 index 0000000..f450c6a --- /dev/null +++ b/estructura skynet.md @@ -0,0 +1,68 @@ +# Esquema Documental + +--- + +``` +00_VISION +├── filosofia.md # Principios, paradigmas, acuerdo bilateral +└── glosario.md # Términos, entidades, prefijos de hash + +01_ARQUITECTURA +├── overview.md # Visión general, diagrama del sistema +├── almacenamiento.md # R2, buckets, locker:// +├── entidades/ +│ ├── deck.md # Servidor personal +│ ├── corp.md # Servidor empresarial +│ ├── hst.md # Hash Semantic Tagging +│ ├── itm.md # Items (plano ideal) +│ ├── ply.md # Players (identidad) +│ ├── flg.md # Flags (marcos jurídicos) +│ └── loc.md # Locations (ubicaciones) +└── aplicaciones/ + ├── packet.md # App móvil, captura multimedia + ├── vision-builder.md # Visiones → milestones → acciones + └── mind-link.md # Interfaz unificada de acceso + +02_COMPONENTES +├── context-system.md # Log, gestor, dataset, grafo +├── secretaria.md # Clara (personal), Margaret (corporativo) +├── administracion.md # Mason (edición temporal) +├── contable.md # Feldman (consolidación, inmutabilidad) +├── produccion.md # Alfred (personal), Jared (corporativo) +├── auditoria.md # Sentinel (LIGHT/DEEP) +├── cognitiva.md # Grace (18 módulos IA) +├── asistente.md # Penny (voz conversacional) +├── generacion-iterativa.md # The Factory +├── orchestrator.md # Orquestación multi-agente LLM +├── circle.md # Consejo de perspectivas +└── cloudville.md # Laboratorio de zumbados + +03_MODELO_DATOS +├── entidades.md # HST, ITM, PLY, LOC, FLG +├── planos.md # T0 (ideal), MST (milestones), BCK (bloques) +├── hashes.md # h_maestro, h_entrada, h_instancia, h_bloque +├── flujos.md # Estándar, producción, incidencia +└── schemas/ # SQL: types, hst_tags, task_manager, work_log + +04_INFRAESTRUCTURA +├── architect.md # Servidor central (Gitea, PostgreSQL, Infisical) +├── despliegue.md # docker-compose, fases, checklists +└── backup-recovery.md # Estrategia DR, scripts + +05_INTEGRACIONES +├── s-contract.md # Protocolo de comunicación v2.1 +├── hst-api.md # API de tags semánticos +└── gpu-services.md # RunPod: Grace, Penny, Factory + +06_SEGURIDAD +├── inmutabilidad.md # Principios, cadena de hashes +├── secretos.md # Infisical, gestión de credenciales +├── cifrado.md # Envelope encryption, KEK, rotación +└── modelo-amenazas.md # Riesgos, mitigaciones + +99_ANEXOS +├── inventario-repos.md # Lista de repositorios +├── inventario-credenciales.md # Por categoría (APIs, DBs, storage...) +├── pendientes.md # Elementos por definir +└── changelog.md # Historial de cambios +``` diff --git a/glosario_she_enterprise_v2.md b/glosario_she_enterprise_v2.md new file mode 100644 index 0000000..6dd9307 --- /dev/null +++ b/glosario_she_enterprise_v2.md @@ -0,0 +1,400 @@ +# Glosario Consolidado — Sistema SHE Enterprise v5.0 + +**Versión:** 1.0 +**Fecha:** Diciembre 2025 +**Fuentes:** Documentación técnica SHE, Sistema de Registro Inmutable v1.2, Análisis de Paradigmas + +--- + +## 1. ARQUITECTURA CENTRAL + +### 1.1 Sistema Principal + +| Término | Definición | +|---------|------------| +| **SHE** | **S**emantic **H**ashtag **E**ngine. Motor narrativo-relacional que gestiona el flujo de estados y transiciones entre entidades. Es la caja fuerte de custodia (Milestones + Bloques). | +| **HST** | **H**ash **S**emantic **T**agging. Sistema de etiquetado semántico mediante etiquetas de 3 caracteres que proporciona clasificación visual e inmutable. | + +### 1.2 Componentes del Sistema de Registro Inmutable + +| Término | Versión Personal | Versión Corporativa | Definición | +|---------|------------------|---------------------|------------| +| **Producción** | Alfred | Jared | Almacena árboles de procesos y flujos predefinidos. Implementado con Windmill/n8n. | +| **Secretaría** | Clara | Margaret | Punto de entrada inmutable. Todo dato ingresa exactamente como llegó. Registro permanente. | +| **Administración** | Mason | Mason | Tabla de trabajo temporal para enriquecer y consolidar información antes de registro definitivo. | +| **Libros contables** | Feldman | Feldman | Registro final definitivo e inmutable. Contiene cola de validación + registro final. | +| **Gestoría** | Grace | Grace | Capa cognitiva con 18 módulos de IA para transformación de datos. Intercambiable. | + +--- + +## 2. LA TRÍADA DE PLANOS + +| Plano | Tabla Principal | Naturaleza | Descripción | +|-------|-----------------|------------|-------------| +| **Cannon** | Ítems | Definición perfecta | Especificaciones, planos, recetas. No consume energía. | +| **Burocrático** | Milestones | Estado legal/contable | Documentos, contratos, estados en vigor. Consume energía jurídica. | +| **Físico** | Bloques | Evidencia material | Fotos, registros, pruebas físicas. Consume energía real. | + +> **Regla de Oro**: Una burocracia sin evidencia física es ficción. Un ítem sin milestone es solo una idea. Un bloque sin contexto es ruido. + +--- + +## 3. ENTIDADES PRINCIPALES + +### 3.1 Milestones (Plano Burocrático) + +| Término | Definición | +|---------|------------| +| **Milestone** | Registro en el plano burocrático que representa estados legales, contables o hitos documentales. | +| **tipo_item** | Clasificación del milestone: `documento`, `estado`, `hito`, `evento`, `version`. | +| **roothash** | Cadena de IDs ancestrales que permite reconstruir la jerarquía completa. Formato: `/id_raiz/id_padre/.../id_actual` | + +#### Estados de Milestone + +| Estado | Descripción | +|--------|-------------| +| `draft` | Borrador inicial | +| `active` | En ejecución | +| `on_hold` | Pausado | +| `completed` | Finalizado | +| `cancelled` | Cancelado (terminal) | +| `archived` | Archivado (terminal) | + +### 3.2 Bloques (Plano Físico) + +| Término | Definición | +|---------|------------| +| **Bloque** | Unidad de evidencia física vinculada a un Milestone. Fotografía energética del intervalo T0 → T+1. | +| **tipo_accion** | Clasificación del bloque: `punto`, `secuencia`, `esfuerzo_maximo`. | +| **evidencia_url** | URL obligatoria de la evidencia física (foto, archivo, registro). | +| **evidencia_hash** | SHA-256 del archivo de evidencia para verificación de integridad. | + +#### Tipos de Acción en Bloques + +| Tipo | Descripción | Energía | +|------|-------------|---------| +| **Generadora** | Crea nuevo valor (diseño, producción) | Alta | +| **Preservadora** | Mantiene estado (almacenamiento, backup) | Media | +| **Transformadora** | Modifica existente (ensamblaje, edición) | Alta | +| **Verificadora** | Confirma calidad (inspección, testing) | Baja | + +### 3.3 Ítems (Plano Cannon) + +| Término | Definición | +|---------|------------| +| **Ítem** | Definición ideal de producto, proceso o servicio. Especificación perfecta que no consume energía. | +| **tipo** | Clasificación: `producto`, `servicio`, `proceso`, `componente`, `protocolo`. | +| **specs_estandar** | JSONB con especificaciones técnicas estructuradas. | + +--- + +## 4. ENTIDADES AUXILIARES + +| Término | Definición | +|---------|------------| +| **Player** | Actor del sistema: `persona`, `empresa`, `departamento`, `sistema_ia`, `sensor_iot`. Entidad que puede actuar. | +| **Bandera** | Marco jurídico/jurisdicción que gobierna operaciones. Define IVA, moneda, idioma, timezone. | +| **Localización** | Ubicación física o lógica: `pais`, `region`, `ciudad`, `direccion`, `edificio`, `planta`, `sala`, `almacen`, `virtual`. | + +--- + +## 5. SISTEMA HST — ETIQUETADO SEMÁNTICO + +### 5.1 Conceptos Clave + +| Término | Definición | +|---------|------------| +| **H_maestro** | SHA-256 de la imagen primigenia. 64 caracteres hexadecimales. Clave primaria inmutable. | +| **Imagen primigenia** | Conjunto mínimo de atributos que definen inequívocamente una entidad. Inmutable. | +| **ref** | Etiqueta semántica de 3 caracteres que clasifica registros (ej: `inv`, `pjt`, `ord`). | +| **Skin** | Capa de presentación que modifica visualización sin alterar datos subyacentes. | +| **hash_visible** | H_left(30) + estilo(4) + H_right(30). Identifica skin específico. | + +### 5.2 Grupos de Etiquetas + +| Grupo | Propiedad | Memorizable | Descripción | +|-------|-----------|-------------|-------------| +| **hst** | Sistema | ✅ Sí | Vocabulario semántico universal, inmutable | +| **spe** | Sistema | ❌ No | Especificaciones técnicas | +| **hsu** | Usuario | ✅ Sí | Etiquetas personales del usuario | +| **msu** | Usuario | ❌ No | Metodologías personales | + +### 5.3 Catálogo de Etiquetas (ref) + +| ref | Nombre | Grupo | Descripción | +|-----|--------|-------|-------------| +| **inc** | Incidencia | Origen | Punto de entrada inicial | +| **pjt** | Proyecto | Origen | Contenedor principal de trabajo | +| **prp** | Propuesta | Origen | Oferta comercial | +| **ord** | Pedido | Transacción | Orden de compra/venta | +| **inv** | Factura recibida | Transacción | Documento fiscal de proveedor | +| **siv** | Factura emitida | Transacción | Documento fiscal propio | +| **pay** | Pago | Transacción | Transacción monetaria | +| **exl** | Gasto | Transacción | Salida de dinero | +| **whs** | Almacén | Logístico | Ubicación de stock | +| **inn** | Entrada | Logístico | Recepción de material | +| **out** | Salida | Logístico | Expedición de material | +| **pct** | Producción | Productivo | Proceso productivo | +| **sod** | Albarán | Logístico | Documento de entrega | +| **doc** | Documento | Documental | Archivo genérico | +| **ctr** | Contrato | Documental | Acuerdo legal | +| **lic** | Licencia | Documental | Permiso/autorización | + +--- + +## 6. ORQUESTACIÓN Y FLUJOS + +### 6.1 Alfred (Producción) + +| Término | Definición | +|---------|------------| +| **Alfred** | Orquestador determinista (n8n/Windmill). NO es IA. Ejecuta Métodos de forma determinista. | +| **Jared** | Versión corporativa de Alfred para gestionar múltiples usuarios. | + +### 6.2 Métodos + +| Término | Definición | +|---------|------------| +| **Método** | Receta JSON pública que define procedimiento operativo. Secuencia de pasos atómicos. | +| **h_metodo** | SHA-256 del JSON del Método. Identificador único e inmutable. | +| **step_id** | Identificador del paso dentro del Método. | + +### 6.3 Tipos de Paso en Métodos + +| Tipo | Descripción | Ejemplo | +|------|-------------|---------| +| **module** | Invoca módulo Grace | `grace.parser` | +| **action** | Operación CRUD | `create_relation` | +| **condition** | Bifurcación lógica | `if amount > 1000` | +| **wait** | Espera evento externo | `approval_received` | +| **notify** | Envía notificación | `email`, `webhook` | + +--- + +## 7. GRACE (GESTORÍA) — MÓDULOS IA + +### 7.1 Conceptos + +| Término | Definición | +|---------|------------| +| **Grace (gestoría)** | Capa cognitiva. Colección de 18 microservicios de IA especializados. Transforma datos, no decide flujos. | +| **Contrato Común v1.2** | Interfaz universal para comunicación entre módulos Grace. Define estructura de Request/Response. | + +### 7.2 Catálogo de Módulos (18) + +| # | Módulo | Función | +|---|--------|---------| +| 1 | **PARSER** | Extracción de datos de documentos | +| 2 | **CLASSIFIER** | Categorización, asigna etiquetas ref | +| 3 | **VALIDATOR** | Verificación de datos contra reglas | +| 4 | **SUGGESTER** | Recomendaciones rankeadas | +| 5 | **NARRATOR** | Generación de texto natural | +| 6 | **AUDITOR** | Detección de anomalías | +| 7 | **MATCHER** | Conciliación de conjuntos | +| 8 | **SUMMARIZER** | Resumen de documentos | +| 9 | **TRANSLATOR** | Traducción de texto | +| 10 | **EXTRACTOR** | OCR avanzado | +| 11 | **PREDICTOR** | Pronóstico de series temporales | +| 12 | **CLUSTERER** | Agrupación de datos | +| 13 | **LINKER** | Detección de relaciones | +| 14 | **SCORER** | Puntuación según criterios | +| 15 | **ROUTER** | Enrutamiento de mensajes | +| 16 | **RESPONDER** | Respuesta automática | +| 17 | **MONITOR** | Vigilancia de métricas | +| 18 | **LEARNER** | Mejora continua de modelos | + +--- + +## 8. FLUJOS OPERATIVOS + +### 8.1 Flujos del Sistema de Registro Inmutable + +| Flujo | Condición | Ruta | +|-------|-----------|------| +| **Estándar** | Entrada manual, requiere enriquecimiento | Clara → Mason → Feldman | +| **Producción** | Proceso predefinido completo | Alfred → Clara → Feldman | +| **Incidencia** | Improvisación durante flujo | Clara → Mason → Feldman | + +### 8.2 Flujos HST Estándar + +| Flujo | Secuencia | Descripción | +|-------|-----------|-------------| +| **Compra completa** | `inc → pjt → prp → ord → exl → sod → inv → pay` | Ciclo completo de compra | +| **Compra directa** | `inc → exl → sod → inv` | Gastos menores sin proyecto | +| **Producción** | `inc → whs → inn → pct → out` | Transformación interna | + +--- + +## 9. PARADIGMA TEMPORAL + +### 9.1 Línea Temporal + +``` +T-N ────→ T-1 ────→ T0 ────→ T+1 ────→ T+N + │ │ │ │ │ +Origen Pre- Inicio Cierre Futuro/ +Difuso consoli- Real Eviden- Histórico + dación cia +``` + +| Punto | Significado | Características | +|-------|-------------|-----------------| +| **T−N** | Origen difuso | Exploración, ideas no consolidadas | +| **T−1** | Referencia previa | Punto desde el cual planificar | +| **T0** | Inicio ejecución | Arranque formal, recursos comprometidos | +| **T+1** | Hito burocrático | Registro formal de cierre operativo | +| **T+N** | Resultado consolidado | Materialización real en el mundo | + +### 9.2 Naturaleza Temporal de Entidades + +| Entidad | Intervalo | Naturaleza | +|---------|-----------|------------| +| **Ítem** | T-N → T-1 → T0 | Definición ideal, proceso, producto conceptual | +| **Milestone** | T0 → T+1 | Estado contable/legal en vigor | +| **Bloque** | T+1 | Evidencia física de la ejecución | + +--- + +## 10. SEGURIDAD Y TRAZABILIDAD + +### 10.1 Sistemas de Protección + +| Término | Definición | +|---------|------------| +| **SENTINEL** | Sistema dual de auditoría y protección (LIGHT + DEEP). Nivel perimetral + interno. | +| **NOTARIO** | Sistema de sellado blockchain con Merkle Tree para inmutabilidad temporal. | +| **Key Vault** | Gestor centralizado de secretos y claves de cifrado. | + +### 10.2 Cifrado + +| Término | Definición | +|---------|------------| +| **DEK** | Data Encryption Key. Llave efímera que cifra datos directamente. | +| **KEK** | Key Encryption Key. Llave maestra que cifra otras llaves. | +| **PII** | Personally Identifiable Information. Datos personales que requieren cifrado estricto. | + +### 10.3 Perfiles de Cifrado + +| Perfil | Uso | Rotación | +|--------|-----|----------| +| **ALTO** | Datos sensibles (PII, financieros) | 30 días | +| **MEDIO** | Datos operativos | 90 días | +| **BAJO** | Datos públicos | N/A | +| **TRANSIT** | Datos en tránsito (TLS 1.3) | Por sesión | + +--- + +## 11. ALMACENAMIENTO + +### 11.1 Modelo de Temperaturas + +| Temperatura | Ubicación | Latencia | Uso | +|-------------|-----------|----------|-----| +| **Hot** | Local + Redis | <100ms | Trabajo activo | +| **Warm** | Storj DCS | <5s | Histórico < 1 año | +| **Cold** | Arweave | <60s | Archivo permanente, inmutable | + +### 11.2 Términos de Almacenamiento + +| Término | Definición | +|---------|------------| +| **Storj DCS** | Almacenamiento distribuido con erasure coding 80/29. Cifrado client-side. | +| **Arweave** | Blockchain de almacenamiento permanente. Pago único, inmutable. | +| **WORM** | Write Once Read Many. Política de almacenamiento inmutable. | + +--- + +## 12. PARADIGMAS DE PROPIEDAD + +| Paradigma | Propietario | Alcance | Mutabilidad | Prefijo | +|-----------|-------------|---------|-------------|---------| +| **Sistema** | Plataforma SHE | Universal | Ninguna | `sys_` | +| **Organización** | Empresa | Multi-usuario interno | Controlada | `org_` | +| **Usuario** | Individual | Personal | Total | `usr_` | + +--- + +## 13. LOGGING Y AUDITORÍA + +| Término | Definición | +|---------|------------| +| **trace_id** | UUID v4 que identifica una ejecución completa de Método. | +| **idempotency_key** | Clave SHA-256 para detección de duplicados en 24h. | +| **audit_status** | Estado de auditoría: `PENDING`, `PASS`, `WARN`, `FAIL`. | + +--- + +## 14. MÓDULOS COMPLEMENTARIOS + +| Módulo | Función | +|--------|---------| +| **Vision Builder** | Herramienta de diseño visual de vida. Conecta objetivos con acciones. | +| **Cloudville** | Simulador de estructuras organizativas con agentes IA. | +| **Sistema de Voz** | Flujo autoalojado: VAD (Silero) → STT (Whisper) → NLU (LLM) → TTS (Coqui). | +| **Bloques de Información** | Sistema de 6 elementos para trabajo con IAs: Contexto, Instrucción, Ejemplo, Restricción, Datos, Formato. | + +--- + +## 15. PRINCIPIOS FUNDACIONALES + +| Principio | Descripción | +|-----------|-------------| +| **Paradigma de Resultados** | El sistema registra resultados, no incidencias ni justificaciones. | +| **Ocultación Legítima** | Si hay resultado válido, las incidencias del proceso no se documentan. | +| **Acuerdo Bilateral** | Empresa ofrece herramientas; trabajador ofrece resultados evidenciados. | +| **Trazabilidad como Movilidad** | La trazabilidad es portfolio verificable, no arma de control. | +| **Flujo Energético** | Ante duda, analizar dónde se consume la energía clarifica la clasificación. | +| **Tres Planos de Verdad** | Cannon, burocrático y físico deben estar interconectados. | +| **Project Cannon** | Los tres documentos inseparables: Listado de Costes (qué), Árbol de Procesos (cómo), Gantt (cuándo). | + +--- + +## 16. REGLAS DE VALIDACIÓN (Resumen) + +### Reglas SHE (Estructurales) + +| Código | Regla | +|--------|-------| +| SHE-E1 | Padre único (0..1 en propia tabla) | +| SHE-E2 | Cruce único (0..1 con tabla opuesta) | +| SHE-E3 | Anti-loop interno | +| SHE-E4 | Anti-loop cruzado | +| SHE-E5 | Proyecto único obligatorio | + +### Reglas HST + +| Código | Regla | +|--------|-------| +| HST-1 | Hash maestro inmutable | +| HST-2 | Imagen primigenia inmutable | +| HST-3 | Skins no alteran concepto | +| HST-4 | Jerarquía coherente | + +--- + +## 17. PROJECT CANNON + +Los tres documentos fundamentales e inseparables para gestionar cualquier producción o proyecto. + +| Documento | Pregunta | Contenido | +|-----------|----------|-----------| +| **Listado de Costes** | ¿Qué? | Materiales + Procesos (BOM expandido) | +| **Árbol de Procesos** | ¿Cómo? | Secuencia y dependencias | +| **Gráfica de Gantt** | ¿Cuándo? | Distribución temporal | + +--- + +## 18. NOMENCLATURA OBSOLETA (Pendiente de revisión) + +Términos heredados que deben sustituirse en la documentación. + +| Término Obsoleto | Sustituto | Origen | Notas | +|------------------|-----------|--------|-------| +| SFE (System Flow Engine) | *Eliminado* | Concepto obsoleto | Ya sustituido por SHE donde aplicaba | +| Pipeline | *Eliminar* | Jerga n8n | Concepto innecesario | +| Santísima Trinidad Documental | **Project Cannon** | PARTE_I | Ya renombrado | +| Ideal (plano) | **Cannon** | Tríada de Planos | Ya renombrado | + +--- + +*— Fin del Glosario —*