APISAvant3 / app.py
antonypamo's picture
Update app.py
d6502c9 verified
# ======================================================
# Savant RRF Φ12.5 — FULL APP (PRODUCTION ALIGNED)
# ======================================================
from __future__ import annotations
import os, json, math, time
from typing import Optional, Dict, Any, List
import numpy as np
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
from huggingface_hub import hf_hub_download
import joblib
# ======================================================
# CONFIG
# ======================================================
MAX_PROMPT_CHARS = 8000
MAX_ANSWER_CHARS = 12000
MAX_DOCS = 50
MAX_DOC_CHARS = 6000
ENCODER_MODEL_ID = "antonypamo/RRFSAVANTMADE"
META_LOGIT_REPO = "antonypamo/RRFSavantMetaLogicV2"
META_LOGIT_FILENAME = "logreg_rrf_savant.joblib"
HF_TOKEN = os.environ.get("HF_TOKEN", None)
# ======================================================
# LOAD MODELS
# ======================================================
embedder = SentenceTransformer(ENCODER_MODEL_ID)
meta_logit_path = hf_hub_download(
repo_id=META_LOGIT_REPO,
filename=META_LOGIT_FILENAME,
token=HF_TOKEN
)
meta_logit = joblib.load(meta_logit_path)
# ======================================================
# CORE FEATURES
# ======================================================
def coherence(vec):
if len(vec) < 4:
return 0.0, 0.0
spectrum = np.fft.rfft(vec)
power = np.abs(spectrum) ** 2
total = power.sum() + 1e-9
C = power.max() / total
freqs = np.fft.rfftfreq(len(vec))
f_mean = (freqs * power).sum() / total
S = 1 - min(1, f_mean / (freqs.max() + 1e-9))
return S, C
def extract_features(text):
vec = embedder.encode([text])[0]
energy = float(np.dot(vec, vec))
dom_freq = float(np.argmax(np.abs(np.fft.rfft(vec))))
phi = 1 - math.exp(-energy)
omega = math.tanh(dom_freq)
S, C = coherence(vec)
coh = 0.5 * (S + C)
# dummy phi node (simplified)
phi_node = int(abs(hash(text)) % 8)
onehot = np.zeros(8)
onehot[phi_node] = 1
base = np.array([phi, omega, coh, S, C, energy, dom_freq])
return np.concatenate([base, onehot]), phi_node
def compute_scores(prompt, answer):
if not prompt or not answer:
raise ValueError("Empty input")
if len(prompt) > MAX_PROMPT_CHARS or len(answer) > MAX_ANSWER_CHARS:
raise HTTPException(status_code=413, detail="Too large")
qa = f"{prompt}\n{answer}"
features, phi_node = extract_features(qa)
p_good = float(meta_logit.predict_proba([features])[0][1])
e_p = embedder.encode([prompt], normalize_embeddings=True)[0]
e_a = embedder.encode([answer], normalize_embeddings=True)[0]
cosine = float(np.dot(e_p, e_a))
resonance_score = 0.5 * p_good + 0.5 * abs(cosine)
return {
"p_good": p_good,
"cosine": cosine,
"resonance_score": resonance_score,
"phi_node": f"Φ{phi_node}",
}
# ======================================================
# API MODELS
# ======================================================
class EvalRequest(BaseModel):
prompt: str
answer: str
class RerankRequest(BaseModel):
query: str
documents: List[str]
# ======================================================
# APP
# ======================================================
app = FastAPI(title="Savant RRF Φ12.5")
@app.get("/")
def root():
return {
"status": "ok",
"endpoints": [
"/quality",
"/evaluate",
"/evaluate_and_improve",
"/self_learn",
"/v1/rerank"
]
}
# ======================================================
# QUALITY
# ======================================================
@app.post("/quality")
def quality(req: EvalRequest):
return compute_scores(req.prompt, req.answer)
@app.post("/evaluate")
def evaluate(req: EvalRequest):
return compute_scores(req.prompt, req.answer)
# ======================================================
# IMPROVEMENT LOOP
# ======================================================
@app.post("/evaluate_and_improve")
def evaluate_and_improve(req: EvalRequest):
base = compute_scores(req.prompt, req.answer)
if base["p_good"] > 0.75:
return {
"final_answer": req.answer,
"improved": False,
"scores": base
}
improved = req.answer + "\n\n(Refined for clarity and reasoning)"
new_score = compute_scores(req.prompt, improved)
if new_score["p_good"] > base["p_good"]:
return {
"final_answer": improved,
"improved": True,
"scores": new_score
}
return {
"final_answer": req.answer,
"improved": False,
"scores": base
}
# ======================================================
# SELF LEARNING (BASIC)
# ======================================================
@app.post("/self_learn")
def self_learn(req: EvalRequest):
result = compute_scores(req.prompt, req.answer)
if result["resonance_score"] > 0.8:
with open("rrf_memory.jsonl", "a") as f:
f.write(json.dumps({
"prompt": req.prompt,
"answer": req.answer,
"score": result["resonance_score"]
}) + "\n")
return {"stored": True, "score": result["resonance_score"]}
return {"stored": False, "score": result["resonance_score"]}
# ======================================================
# RERANK
# ======================================================
@app.post("/v1/rerank")
def rerank(req: RerankRequest):
if len(req.documents) > MAX_DOCS:
raise HTTPException(status_code=413)
texts = [req.query] + req.documents
embs = embedder.encode(texts, normalize_embeddings=True)
q = embs[0]
docs = embs[1:]
scores = docs @ q
ranked = sorted(
[{"id": i, "score": float(s)} for i, s in enumerate(scores)],
key=lambda x: x["score"],
reverse=True
)
return {"results": ranked}