feat: v0.6.0 — Fase 1 ContextMemory synthesis + CLAUDE.md (CLAUDE.md)
Browse files
CLAUDE.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md — Bruno / retro-cpu
|
| 2 |
+
|
| 3 |
+
**Componente:** `d1d4c/retro-cpu` HF Space ("Retro Lite Online")
|
| 4 |
+
**Propietario:** HFPilot (retro-hf)
|
| 5 |
+
**Versión actual:** 0.6.0 (2026-06-21 — Fase 1: síntesis ContextMemory activa)
|
| 6 |
+
**ADRs:** ADR-562 (Harness Independence), ADR-563 (Native Harness)
|
| 7 |
+
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
## Qué es Bruno
|
| 11 |
+
|
| 12 |
+
Bruno es la instancia online y ligera de Retro que corre en un HF Docker Space
|
| 13 |
+
con CPU gratuita (`cpu-basic`, free tier). Permite que Dídac opere desde el móvil
|
| 14 |
+
(Telegram) cuando `satelite` está offline. **No es el Retro completo** — es un
|
| 15 |
+
bridge conversacional con memoria propia.
|
| 16 |
+
|
| 17 |
+
Bot Telegram: `@Bruno_Retro_bot`. Solo responde al `chat_id` de Dídac (`6622755810`).
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## Arquitectura
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
[Móvil] ──Telegram──> [webhook POST /] ──> retro-cpu (CPU-only)
|
| 25 |
+
│
|
| 26 |
+
┌─────────────────────┴──────────────────────┐
|
| 27 |
+
│ │
|
| 28 |
+
texto (normal) voz (BLOQUEADA)
|
| 29 |
+
│ │
|
| 30 |
+
retro-space /v1/chat/completions getFile → api.telegram.org
|
| 31 |
+
(Qwen2.5-7B, Bearer HF_TOKEN) (ConnectTimeout en HF)
|
| 32 |
+
│
|
| 33 |
+
respuesta inline en webhook HTTP
|
| 34 |
+
{"method":"sendMessage","chat_id":...}
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
**Constraint crítico de red (2026-06-21):** HF Spaces no pueden hacer outbound
|
| 38 |
+
a `api.telegram.org`. La respuesta se devuelve inline en el body HTTP del webhook
|
| 39 |
+
(mecanismo oficial de Telegram). Las notas de voz están bloqueadas hasta que haya
|
| 40 |
+
un Cloudflare Worker relay (Fase 1, pendiente).
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Memoria — bruno/v1 (sovereign graph)
|
| 45 |
+
|
| 46 |
+
Bruno tiene su **propio grafo soberano**, independiente del HyperGraph central
|
| 47 |
+
de Retro (ADR-562: sin `sys.path` a casa, sin writes a la DB central).
|
| 48 |
+
|
| 49 |
+
### Stack
|
| 50 |
+
|
| 51 |
+
| Componente | Descripción |
|
| 52 |
+
|------------|-------------|
|
| 53 |
+
| `bruno_graph.py` | Adaptador stdlib (`sqlite3`+`json`), sin deps externas |
|
| 54 |
+
| `/data/bruno_graph.db` | SQLite en bucket `retro-cpu-storage`, **sobrevive reinicios** |
|
| 55 |
+
| `bruno_v1.json` | Ontología SSOT (horneada en imagen + copia en `retro-hf/ontology/`) |
|
| 56 |
+
| `test_bruno_graph.py` | 5 tests de rechazo de violaciones de schema |
|
| 57 |
+
|
| 58 |
+
### Tipos de nodo
|
| 59 |
+
|
| 60 |
+
| Tipo | Persistencia | Descripción |
|
| 61 |
+
|------|-------------|-------------|
|
| 62 |
+
| `Message` | append-only | Turno de conversación (role, content, turn_index, session_ref) |
|
| 63 |
+
| `ContextMemory` | singleton por chat_id, upsert monotónico | Síntesis LLM cross-session |
|
| 64 |
+
| `Pattern` | declarado | Reservado para patrones detectados |
|
| 65 |
+
|
| 66 |
+
### Cómo funciona la memoria (v0.6.0 — Fase 1 activa)
|
| 67 |
+
|
| 68 |
+
```
|
| 69 |
+
Turno de usuario
|
| 70 |
+
│
|
| 71 |
+
├── 1. build_system_prompt(chat_id, bruno)
|
| 72 |
+
│ └── lee ContextMemory → si synthesis_strategy="llm_qwen2.5-7b"
|
| 73 |
+
│ → inyecta en system prompt
|
| 74 |
+
│
|
| 75 |
+
├── 2. infer_chat(history, system_prompt)
|
| 76 |
+
│ └── Qwen2.5-7B genera respuesta
|
| 77 |
+
│
|
| 78 |
+
├── 3. Respuesta inline → Telegram
|
| 79 |
+
│
|
| 80 |
+
└── 4. asyncio.create_task(synthesize_context_memory(...)) ← background
|
| 81 |
+
└── llama a retro-space con meta-prompt de síntesis (≤200 tokens)
|
| 82 |
+
→ upsert ContextMemory (synthesis_strategy="llm_qwen2.5-7b")
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
La síntesis ocurre en background tras enviar la respuesta → latencia = 0 para el usuario.
|
| 86 |
+
|
| 87 |
+
### API de bruno_graph.py
|
| 88 |
+
|
| 89 |
+
```python
|
| 90 |
+
from bruno_graph import BrunoGraph
|
| 91 |
+
|
| 92 |
+
bruno = BrunoGraph(db_path="/data/bruno_graph.db", ontology_path="bruno_v1.json")
|
| 93 |
+
|
| 94 |
+
# Append mensaje
|
| 95 |
+
bruno.append_message(chat_id, "user", "hola", source="telegram")
|
| 96 |
+
bruno.append_message(chat_id, "assistant", "hola!", source="qwen2.5-7b")
|
| 97 |
+
|
| 98 |
+
# Carrusel activo (N últimos turnos)
|
| 99 |
+
history = bruno.carousel(chat_id, n=20) # → [{"role": ..., "content": ...}]
|
| 100 |
+
|
| 101 |
+
# ContextMemory (singleton cross-session)
|
| 102 |
+
cm = bruno.get_context_memory(chat_id) # → dict | None
|
| 103 |
+
bruno.upsert_context_memory(chat_id, content, # → crea o actualiza
|
| 104 |
+
synthesis_strategy="llm_qwen2.5-7b")
|
| 105 |
+
|
| 106 |
+
# Reset de sesión (epoch rota, turnos anteriores se conservan)
|
| 107 |
+
bruno.reset_session(chat_id)
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
---
|
| 111 |
+
|
| 112 |
+
## Deploy
|
| 113 |
+
|
| 114 |
+
**⚠️ retro-cpu usa Dev Mode → código horneado en imagen Docker (sin git restore).**
|
| 115 |
+
`api.upload_file()` NO refresca el contenedor. Para aplicar cambios: **`factory_reboot=True`**.
|
| 116 |
+
|
| 117 |
+
```python
|
| 118 |
+
from huggingface_hub import HfApi
|
| 119 |
+
import os
|
| 120 |
+
|
| 121 |
+
api = HfApi(token=os.environ["HF_TOKEN"])
|
| 122 |
+
|
| 123 |
+
# 1. Subir archivo(s) modificado(s)
|
| 124 |
+
for fname in ["app.py", "bruno_graph.py", "bruno_v1.json"]:
|
| 125 |
+
api.upload_file(
|
| 126 |
+
path_or_fileobj=open(f"retro-cpu/{fname}", "rb").read(),
|
| 127 |
+
path_in_repo=fname,
|
| 128 |
+
repo_id="d1d4c/retro-cpu",
|
| 129 |
+
repo_type="space",
|
| 130 |
+
commit_message=f"deploy: {fname} vX.Y.Z",
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
# 2. Factory reboot SIEMPRE (Dev Mode requiere reconstruir imagen)
|
| 134 |
+
api.restart_space("d1d4c/retro-cpu", factory_reboot=True)
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
**Secrets del Space** (configurados en HF, no en código):
|
| 138 |
+
|
| 139 |
+
| Secret | Propósito |
|
| 140 |
+
|--------|-----------|
|
| 141 |
+
| `TELEGRAM_BOT_TOKEN` | Bot API token de @BotFather |
|
| 142 |
+
| `HF_TOKEN` | Bearer auth a retro-space + retro-tts (privados) |
|
| 143 |
+
| `WEBHOOK_SECRET` | Secret token del webhook de Telegram |
|
| 144 |
+
| `ALLOWED_CHAT_IDS` | Allowlist de chat_ids (solo Dídac: `6622755810`) |
|
| 145 |
+
|
| 146 |
+
---
|
| 147 |
+
|
| 148 |
+
## SSH
|
| 149 |
+
|
| 150 |
+
```bash
|
| 151 |
+
ssh -i ~/.ssh/hf_space_new d1d4c-retro-cpu@ssh.hf.space
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
---
|
| 155 |
+
|
| 156 |
+
## Roadmap Bruno
|
| 157 |
+
|
| 158 |
+
| Fase | Estado | Descripción |
|
| 159 |
+
|------|--------|-------------|
|
| 160 |
+
| 0 | ✅ DONE | Chat text-only, grafo bruno/v1, persistencia bucket |
|
| 161 |
+
| 1 | ✅ DONE (0.6.0) | Síntesis ContextMemory (LLM background) + inyección en prompt |
|
| 162 |
+
| 1b | Pendiente | CF Worker relay → desbloquea voz in/out desde móvil |
|
| 163 |
+
| 2 | Opcional | Export async unidireccional HF→casa (NUNCA sync bidireccional) |
|
| 164 |
+
|
| 165 |
+
---
|
| 166 |
+
|
| 167 |
+
**Owner:** HFPilot | **Proyecto:** retro-hf | **Actualizado:** 2026-06-21
|