Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files
app.py
CHANGED
|
@@ -2,9 +2,12 @@ from fastapi import FastAPI, Request
|
|
| 2 |
from fastapi.responses import JSONResponse, HTMLResponse
|
| 3 |
import httpx, json
|
| 4 |
import time
|
|
|
|
|
|
|
| 5 |
import sys
|
| 6 |
import os
|
| 7 |
import sqlite3
|
|
|
|
| 8 |
|
| 9 |
# Configuración del Sistema Emocional
|
| 10 |
DB_PATH = "/tmp/cma_memory.db"
|
|
@@ -73,7 +76,12 @@ async def dashboard_home():
|
|
| 73 |
<div class="card">
|
| 74 |
<canvas id="emotionChart"></canvas>
|
| 75 |
</div>
|
| 76 |
-
<p style="text-align: center; font-size: 0.7rem; color: #475569;">Arquitectura Emocional
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
</div>
|
| 78 |
|
| 79 |
<script>
|
|
@@ -116,12 +124,27 @@ async def dashboard_home():
|
|
| 116 |
document.getElementById('val-qualia').innerText = parseFloat(latest.qualia).toFixed(3);
|
| 117 |
document.getElementById('val-confianza').innerText = parseFloat(latest.confianza).toFixed(3);
|
| 118 |
|
| 119 |
-
chart.data.labels = data.map(d =>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
chart.data.datasets[0].data = data.map(d => d.qualia);
|
| 121 |
chart.data.datasets[1].data = data.map(d => d.frustracion);
|
| 122 |
chart.data.datasets[2].data = data.map(d => d.confianza);
|
| 123 |
chart.update('none');
|
| 124 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
} catch(e) { console.error("Error cargando datos:", e); }
|
| 126 |
}
|
| 127 |
|
|
@@ -154,6 +177,35 @@ async def get_history():
|
|
| 154 |
except Exception as e:
|
| 155 |
return {"error": str(e), "msg": "Error leyendo historial"}
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
@app.post("/v1/chat/completions")
|
| 158 |
async def chat_proxy(request: Request):
|
| 159 |
GROQ_KEY = os.getenv("GROQ_API_KEY")
|
|
@@ -161,11 +213,16 @@ async def chat_proxy(request: Request):
|
|
| 161 |
return JSONResponse(status_code=500, content={"error": "Falta GROQ_API_KEY en los Secrets de HF"})
|
| 162 |
|
| 163 |
body = await request.json()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
config = modulador_global.modular(vector_global)
|
| 165 |
|
| 166 |
openai_body = {
|
| 167 |
"model": body.get("model", "llama-3.1-8b-instant"),
|
| 168 |
-
"messages":
|
| 169 |
"temperature": config.get("temperature", 0.7),
|
| 170 |
"top_p": config.get("top_p", 0.9)
|
| 171 |
}
|
|
@@ -175,16 +232,44 @@ async def chat_proxy(request: Request):
|
|
| 175 |
headers = {"Authorization": f"Bearer {GROQ_KEY}", "Content-Type": "application/json"}
|
| 176 |
r = await client.post("https://api.groq.com/openai/v1/chat/completions", json=openai_body, headers=headers)
|
| 177 |
|
| 178 |
-
# Procesar respuesta para el sistema emocional
|
| 179 |
resp_json = r.json()
|
| 180 |
texto = resp_json["choices"][0]["message"].get("content", "")
|
| 181 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
senales = {
|
| 183 |
-
"novedad":
|
| 184 |
-
"
|
|
|
|
|
|
|
|
|
|
| 185 |
}
|
| 186 |
-
|
| 187 |
-
vector_global.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
return resp_json
|
| 190 |
except Exception as e:
|
|
|
|
| 2 |
from fastapi.responses import JSONResponse, HTMLResponse
|
| 3 |
import httpx, json
|
| 4 |
import time
|
| 5 |
+
import hashlib
|
| 6 |
+
import datetime
|
| 7 |
import sys
|
| 8 |
import os
|
| 9 |
import sqlite3
|
| 10 |
+
import re
|
| 11 |
|
| 12 |
# Configuración del Sistema Emocional
|
| 13 |
DB_PATH = "/tmp/cma_memory.db"
|
|
|
|
| 76 |
<div class="card">
|
| 77 |
<canvas id="emotionChart"></canvas>
|
| 78 |
</div>
|
| 79 |
+
<p style="text-align: center; font-size: 0.7rem; color: #475569;">Arquitectura Emocional v3.0 • Maxi Speranza</p>
|
| 80 |
+
|
| 81 |
+
<div class="card">
|
| 82 |
+
<h2 style="color: #38bdf8; font-size: 1rem; margin: 0 0 10px 0;">📝 Últimas Interacciones</h2>
|
| 83 |
+
<div id="log-box" style="max-height: 200px; overflow-y: auto; font-size: 0.75rem; color: #94a3b8; line-height: 1.6;">Cargando...</div>
|
| 84 |
+
</div>
|
| 85 |
</div>
|
| 86 |
|
| 87 |
<script>
|
|
|
|
| 124 |
document.getElementById('val-qualia').innerText = parseFloat(latest.qualia).toFixed(3);
|
| 125 |
document.getElementById('val-confianza').innerText = parseFloat(latest.confianza).toFixed(3);
|
| 126 |
|
| 127 |
+
chart.data.labels = data.map(d => {
|
| 128 |
+
let t = new Date(d.timestamp);
|
| 129 |
+
// Ajustar a Argentina (UTC-3)
|
| 130 |
+
t.setHours(t.getHours() - 3);
|
| 131 |
+
return t.toLocaleTimeString('es-AR', {hour: '2-digit', minute: '2-digit'});
|
| 132 |
+
});
|
| 133 |
chart.data.datasets[0].data = data.map(d => d.qualia);
|
| 134 |
chart.data.datasets[1].data = data.map(d => d.frustracion);
|
| 135 |
chart.data.datasets[2].data = data.map(d => d.confianza);
|
| 136 |
chart.update('none');
|
| 137 |
}
|
| 138 |
+
|
| 139 |
+
// Cargar logs
|
| 140 |
+
const logRes = await fetch('/api/logs');
|
| 141 |
+
const logData = await logRes.json();
|
| 142 |
+
if (logData.logs && logData.logs.length > 0) {
|
| 143 |
+
const logBox = document.getElementById('log-box');
|
| 144 |
+
logBox.innerHTML = logData.logs.map(l =>
|
| 145 |
+
`<div style='margin-bottom:8px; border-bottom:1px solid #334155; padding-bottom:6px;'><span style='color:#f59e0b;'>❓</span> ${l.pregunta.substring(0,80)}...<br><span style='color:#10b981;'>💬</span> ${l.respuesta.substring(0,120)}...<br><span style='color:#475569; font-size:0.65rem;'>Novedad: ${l.novedad} | Complejidad: ${l.complejidad} | Época #${l.epoca}</span></div>`
|
| 146 |
+
).join('');
|
| 147 |
+
}
|
| 148 |
} catch(e) { console.error("Error cargando datos:", e); }
|
| 149 |
}
|
| 150 |
|
|
|
|
| 177 |
except Exception as e:
|
| 178 |
return {"error": str(e), "msg": "Error leyendo historial"}
|
| 179 |
|
| 180 |
+
@app.get("/api/logs")
|
| 181 |
+
async def get_logs():
|
| 182 |
+
"""Devuelve las últimas interacciones con preguntas y respuestas"""
|
| 183 |
+
try:
|
| 184 |
+
conn = sqlite3.connect(DB_PATH)
|
| 185 |
+
conn.row_factory = sqlite3.Row
|
| 186 |
+
cursor = conn.cursor()
|
| 187 |
+
cursor.execute("SELECT id, timestamp, metadata FROM snapshots WHERE metadata != '{}' ORDER BY id DESC LIMIT 10")
|
| 188 |
+
rows = cursor.fetchall()
|
| 189 |
+
conn.close()
|
| 190 |
+
|
| 191 |
+
logs = []
|
| 192 |
+
for r in rows:
|
| 193 |
+
try:
|
| 194 |
+
meta = json.loads(r["metadata"])
|
| 195 |
+
logs.append({
|
| 196 |
+
"epoca": r["id"],
|
| 197 |
+
"timestamp": r["timestamp"],
|
| 198 |
+
"pregunta": meta.get("pregunta", "N/A"),
|
| 199 |
+
"respuesta": meta.get("respuesta", "N/A"),
|
| 200 |
+
"novedad": round(meta.get("novedad", 0), 3),
|
| 201 |
+
"complejidad": round(meta.get("complejidad", 0), 3)
|
| 202 |
+
})
|
| 203 |
+
except:
|
| 204 |
+
continue
|
| 205 |
+
return {"logs": logs[::-1]}
|
| 206 |
+
except Exception as e:
|
| 207 |
+
return {"error": str(e)}
|
| 208 |
+
|
| 209 |
@app.post("/v1/chat/completions")
|
| 210 |
async def chat_proxy(request: Request):
|
| 211 |
GROQ_KEY = os.getenv("GROQ_API_KEY")
|
|
|
|
| 213 |
return JSONResponse(status_code=500, content={"error": "Falta GROQ_API_KEY en los Secrets de HF"})
|
| 214 |
|
| 215 |
body = await request.json()
|
| 216 |
+
|
| 217 |
+
# Extraer pregunta original para logging
|
| 218 |
+
mensajes = body.get("messages", [])
|
| 219 |
+
pregunta_original = mensajes[-1].get("content", "") if mensajes else ""
|
| 220 |
+
|
| 221 |
config = modulador_global.modular(vector_global)
|
| 222 |
|
| 223 |
openai_body = {
|
| 224 |
"model": body.get("model", "llama-3.1-8b-instant"),
|
| 225 |
+
"messages": mensajes,
|
| 226 |
"temperature": config.get("temperature", 0.7),
|
| 227 |
"top_p": config.get("top_p", 0.9)
|
| 228 |
}
|
|
|
|
| 232 |
headers = {"Authorization": f"Bearer {GROQ_KEY}", "Content-Type": "application/json"}
|
| 233 |
r = await client.post("https://api.groq.com/openai/v1/chat/completions", json=openai_body, headers=headers)
|
| 234 |
|
|
|
|
| 235 |
resp_json = r.json()
|
| 236 |
texto = resp_json["choices"][0]["message"].get("content", "")
|
| 237 |
|
| 238 |
+
# ====== SEÑALES REALES (no hardcodeadas) ======
|
| 239 |
+
# Novedad: basada en diversidad léxica (type-token ratio)
|
| 240 |
+
palabras = re.findall(r'\w+', texto.lower())
|
| 241 |
+
tipo_token = len(set(palabras)) / max(len(palabras), 1)
|
| 242 |
+
novedad_real = min(tipo_token * 1.2, 1.0) # TTR escalado
|
| 243 |
+
|
| 244 |
+
# Complejidad: largo promedio de oraciones + palabras únicas
|
| 245 |
+
oraciones = [s for s in re.split(r'[.!?]', texto) if s.strip()]
|
| 246 |
+
largo_prom = sum(len(s.split()) for s in oraciones) / max(len(oraciones), 1)
|
| 247 |
+
complejidad_real = min(largo_prom / 25.0, 1.0) # Normalizado a ~25 palabras
|
| 248 |
+
|
| 249 |
+
# Confianza: ausencia de hedging ("quizás", "tal vez", "no estoy seguro")
|
| 250 |
+
hedges = len(re.findall(r'(quizás|tal vez|no estoy segur|podría ser|es posible|maybe|perhaps)', texto.lower()))
|
| 251 |
+
confianza_real = max(0.3, 1.0 - (hedges * 0.15))
|
| 252 |
+
|
| 253 |
+
# Éxito: si la respuesta tiene sustancia (más de 20 palabras)
|
| 254 |
+
exito_real = len(palabras) > 20
|
| 255 |
+
|
| 256 |
senales = {
|
| 257 |
+
"novedad": novedad_real,
|
| 258 |
+
"complejidad": complejidad_real,
|
| 259 |
+
"confianza_real": confianza_real,
|
| 260 |
+
"confianza_esperada": 0.6,
|
| 261 |
+
"exito_tarea": exito_real
|
| 262 |
}
|
| 263 |
+
|
| 264 |
+
vector_global.actualizar(senales, utilidad_externa=novedad_real * 0.8)
|
| 265 |
+
vector_global.persistir_snapshot(metadata={
|
| 266 |
+
"source": "api",
|
| 267 |
+
"pregunta": pregunta_original[:200],
|
| 268 |
+
"respuesta": texto[:300],
|
| 269 |
+
"novedad": round(novedad_real, 3),
|
| 270 |
+
"complejidad": round(complejidad_real, 3),
|
| 271 |
+
"confianza": round(confianza_real, 3)
|
| 272 |
+
})
|
| 273 |
|
| 274 |
return resp_json
|
| 275 |
except Exception as e:
|