APISAvant3 / main.py
antonypamo's picture
Update main.py
ad5462e verified
Raw
History Blame
9.43 kB
import os
import sys
from typing import Optional, Dict, Any
import numpy as np
from numpy.linalg import norm
from scipy.linalg import expm
from fastapi import FastAPI
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
from huggingface_hub import hf_hub_download
import joblib
# ============================
# Configuración de modelos
# ============================
ENCODER_MODEL_ID = "antonypamo/RRFSAVANTMADE"
META_LOGIT_REPO = "antonypamo/RRFSavantMetaLogit"
META_LOGIT_FILENAME = "logreg_rrf_savant.joblib"
print("🔄 [Startup] Cargando encoder RRFSAVANTMADE...", flush=True)
try:
encoder = SentenceTransformer(ENCODER_MODEL_ID)
print("✅ [Startup] Encoder cargado.", flush=True)
except Exception as e:
print(f"❌ [Startup] Error al cargar encoder: {e}", file=sys.stderr, flush=True)
raise
print("🔄 [Startup] Descargando meta-logit desde HF Hub...", flush=True)
try:
meta_logit_path = hf_hub_download(
repo_id=META_LOGIT_REPO,
filename=META_LOGIT_FILENAME,
token=os.environ.get("HF_TOKEN"), # si el repo es público, puede ser None
)
print("🔄 [Startup] Cargando modelo meta-logit...", flush=True)
meta_logit = joblib.load(meta_logit_path)
print("✅ [Startup] Meta-logit cargado.", flush=True)
except Exception as e:
print(f"❌ [Startup] Error al cargar meta-logit: {e}", file=sys.stderr, flush=True)
raise
# ============================
# Geometría icosaédrica Φ12.0
# ============================
phi = (1 + np.sqrt(5)) / 2
nodes = np.array([
[0, 1, phi], [0, -1, phi], [0, 1, -phi], [0, -1, -phi],
[1, phi, 0], [-1, phi, 0], [1, -phi, 0], [-1, -phi, 0],
[phi, 0, 1], [phi, 0, -1], [-phi, 0, 1], [-phi, 0, -1]
], dtype=float)
nodes /= norm(nodes, axis=1, keepdims=True)
N = nodes.shape[0] # 12 nodos
sigma_x = np.array([[0, 1], [1, 0]], dtype=complex)
sigma_y = np.array([[0, -1j], [1j, 0]], dtype=complex)
sigma_z = np.array([[1, 0], [0, -1]], dtype=complex)
def kron_IN(M, N_sites):
return np.kron(M, np.eye(N_sites, dtype=complex))
def site_op(block_2x2, i, j, N_sites):
K = np.zeros((N_sites, N_sites), dtype=complex)
K[i, j] = 1.0
return np.kron(K, block_2x2)
def geodesic_kernel(nodes, sigma=0.618, alpha_log=0.10):
diff = nodes[:, None, :] - nodes[None, :, :]
dist = norm(diff, axis=-1)
W = np.exp(-(dist ** 2) / (sigma ** 2))
np.fill_diagonal(W, 0.0)
if alpha_log > 0.0:
corr = 1.0 + alpha_log * np.log1p(dist ** 2)
corr[range(N), range(N)] = 1.0
W = W / corr
row_sums = W.sum(axis=1, keepdims=True)
row_sums[row_sums == 0] = 1.0
return W / row_sums
def u1_edge_phases(nodes, flux_vector=(0.0, 0.0, 0.0), q=1.0, gauge_scale=1.0):
A = gauge_scale * np.asarray(flux_vector, dtype=float)
midpoints = (nodes[:, None, :] + nodes[None, :, :]) / 2.0
theta = (midpoints @ A).astype(float)
theta = 0.5 * (theta - theta.T)
return theta * q
def build_dirac_hamiltonian(
m=0.25,
v=1.0,
sigma=0.618,
alpha_log=0.10,
q=1.0,
flux_vector=(0.0, 0.0, 0.0),
gauge_scale=0.0,
):
W = geodesic_kernel(nodes, sigma=sigma, alpha_log=alpha_log)
if gauge_scale != 0.0 and any(flux_vector):
theta = u1_edge_phases(nodes, flux_vector=flux_vector,
q=q, gauge_scale=gauge_scale)
U = np.exp(1j * theta)
else:
U = np.ones((N, N), dtype=complex)
H = np.kron(np.eye(N, dtype=complex), m * sigma_z)
diff = nodes[:, None, :] - nodes[None, :, :]
dist = norm(diff, axis=-1) + 1e-12
d_hat = diff / dist[..., None]
for i in range(N):
for j in range(N):
if i == j or W[i, j] == 0:
continue
nvec = d_hat[i, j]
S = (nvec[0] * sigma_x +
nvec[1] * sigma_y +
nvec[2] * sigma_z)
H += v * W[i, j] * U[i, j] * site_op(S, i, j, N)
H = 0.5 * (H + H.conj().T)
return H
def site_probs(psi):
N2 = psi.shape[0]
n = N2 // 2
psi_mat = psi.reshape(n, 2)
return np.sum(np.abs(psi_mat) ** 2, axis=1).real
def chirality(psi):
S = kron_IN(sigma_z, N)
return float(np.vdot(psi, S @ psi).real)
def energy_expectation(psi, H):
return float(np.vdot(psi, H @ psi).real)
def spatial_entropy(p):
p = np.clip(p, 1e-12, 1.0)
return float(-np.sum(p * np.log(p)).real)
def evolve_dirac_shell(psi0, H, dt=0.05, steps=100, record_every=25):
U = expm(-1j * dt * H)
psi = psi0.copy()
probs_hist = []
energy_hist = []
chir_hist = []
ent_hist = []
for t in range(steps + 1):
if t % record_every == 0:
p = site_probs(psi)
probs_hist.append(p)
energy_hist.append(energy_expectation(psi, H))
chir_hist.append(chirality(psi))
ent_hist.append(spatial_entropy(p))
psi = U @ psi
psi /= np.sqrt(np.vdot(psi, psi))
return {
"probs": np.array(probs_hist, dtype=float),
"energy": np.array(energy_hist, dtype=float),
"chirality": np.array(chir_hist, dtype=float),
"entropy": np.array(ent_hist, dtype=float),
"dt": dt,
"record_every": record_every,
}
# ============================
# Core RRF: embeddings + features + scores
# ============================
def get_embedding(text: str) -> np.ndarray:
emb = encoder.encode([text], convert_to_numpy=True, normalize_embeddings=True)
return emb[0]
def compute_rrf_features(prompt: str, answer: str) -> Dict[str, float]:
e_p = get_embedding(prompt)
e_a = get_embedding(answer)
cosine_pa = float(np.dot(e_p, e_a))
len_ratio = len(answer) / (len(prompt) + 1.0)
rng = np.random.default_rng(abs(hash(prompt + answer)) % (2 ** 32))
vec = rng.normal(0, 1, (2 * N,)) + 1j * rng.normal(0, 1, (2 * N,))
vec /= np.sqrt(np.vdot(vec, vec))
psi0 = vec
H = build_dirac_hamiltonian(
m=0.25, v=1.0, sigma=0.618,
alpha_log=0.10, q=1.0,
flux_vector=(0.0, 0.0, 0.0),
gauge_scale=0.0,
)
out = evolve_dirac_shell(psi0, H, dt=0.05, steps=100, record_every=25)
entropy = out["entropy"]
energy = out["energy"]
chir = out["chirality"]
S_final = float(entropy[-1])
S_initial = float(entropy[0])
S_delta = S_final - S_initial
C_final = float(chir[-1])
E_mean = float(np.mean(energy))
E_std = float(np.std(energy))
return {
"cosine_pa": cosine_pa,
"len_ratio": len_ratio,
"dirac_entropy_final": S_final,
"dirac_entropy_delta": S_delta,
"dirac_chirality_final": C_final,
"dirac_energy_mean": E_mean,
"dirac_energy_std": E_std,
}
def features_to_vector(feats: Dict[str, float]) -> np.ndarray:
keys = [
"cosine_pa",
"len_ratio",
"dirac_entropy_final",
"dirac_entropy_delta",
"dirac_chirality_final",
"dirac_energy_mean",
"dirac_energy_std",
]
return np.array([feats[k] for k in keys], dtype=float)
def compute_scores_srff_crff_ephi(prompt: str, answer: str):
feats = compute_rrf_features(prompt, answer)
x = features_to_vector(feats).reshape(1, -1)
proba = meta_logit.predict_proba(x)[0]
p_good = float(proba[1])
SRRF = p_good
CRRF = p_good * feats["cosine_pa"]
S_final = feats["dirac_entropy_final"]
S_max = np.log(N)
norm_entropy = float(S_final / S_max)
E_phi = 0.5 * (SRRF + norm_entropy)
scores = {
"SRRF": SRRF,
"CRRF": CRRF,
"E_phi": E_phi,
"p_good": p_good,
}
return scores, feats
# ============================
# FastAPI app
# ============================
class EvaluateRequest(BaseModel):
prompt: str
answer: str
model_label: Optional[str] = None
class EvaluateResponse(BaseModel):
scores: Dict[str, float]
features: Dict[str, float]
sim_summary: Dict[str, Any]
app = FastAPI(
title="Savant RRF Φ12.0 API",
description="Dirac-Resonant conceptual quality layer for LLM-generated text.",
version="1.0.0",
)
@app.get("/")
def root():
return {"message": "Savant RRF Φ12.0 API running", "docs": "/docs"}
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/evaluate", response_model=EvaluateResponse)
def evaluate(req: EvaluateRequest):
scores, feats = compute_scores_srff_crff_ephi(req.prompt, req.answer)
H = build_dirac_hamiltonian(
m=0.25, v=1.0, sigma=0.618,
alpha_log=0.10, q=1.0,
flux_vector=(0.0, 0.0, 0.0),
gauge_scale=0.0,
)
rng = np.random.default_rng(abs(hash(req.prompt + req.answer + "sim")) % (2 ** 32))
vec = rng.normal(0, 1, (2 * N,)) + 1j * rng.normal(0, 1, (2 * N,))
vec /= np.sqrt(np.vdot(vec, vec))
psi0 = vec
sim = evolve_dirac_shell(psi0, H, dt=0.05, steps=60, record_every=20)
sim_summary = {
"entropy_initial": float(sim["entropy"][0]),
"entropy_final": float(sim["entropy"][-1]),
"chirality_initial": float(sim["chirality"][0]),
"chirality_final": float(sim["chirality"][-1]),
"energy_mean": float(np.mean(sim["energy"])),
"energy_std": float(np.std(sim["energy"])),
"N_sites": int(N),
}
return EvaluateResponse(
scores=scores,
features=feats,
sim_summary=sim_summary,
)