final_project / app /main.py
Ana2012
Deploy backend FastAPI para HF Spaces
614aa6b
import os
import threading
from pathlib import Path
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, RedirectResponse
from pydantic import BaseModel
from typing import Optional
from .agent import ShoppingAgent
from .feedback import caminho_feedback, salvar_feedback
from .logger import salvar_log_busca
from .memory import caminho_memoria_negativa
EMBEDDING_PROVIDER = os.getenv("EMBEDDING_PROVIDER", "transformers").strip().lower()
HF_MODEL_REPO = os.getenv("HF_MODEL_REPO", "Ana2012/bertimbau-buscador").strip()
def _env_flag(name, default="true"):
return os.getenv(name, default).strip().lower() in {"1", "true", "yes", "on"}
PRELOAD_AGENT = _env_flag("PRELOAD_AGENT", "true")
LOGS_DIR = os.getenv("LOGS_DIR", "/data/logs")
DATA_DIR = "/data"
app = FastAPI(title="TCC2 Agent API")
app.add_middleware(
CORSMiddleware,
# Libera temporariamente a comunicacao entre frontend na Cloudflare e backend no Fly.io.
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
agent = None
agent_lock = threading.Lock()
def get_agent():
global agent
if agent is None:
with agent_lock:
if agent is None:
agent = ShoppingAgent()
return agent
@app.on_event("startup")
def preload_agent():
if PRELOAD_AGENT:
get_agent()
class ChatRequest(BaseModel):
query: Optional[str] = None
message: Optional[str] = None
top_k: int = 5
class FeedbackRequest(BaseModel):
query: str
product_id: str
product_name: str
rating: Optional[int] = None
is_helpful: Optional[bool] = None
@app.get("/health")
def health():
runtime = get_agent().runtime_info() if agent is not None else None
return {
"status": "ok",
"agent_ready": agent is not None,
"embedding_provider": EMBEDDING_PROVIDER,
"model_repo": HF_MODEL_REPO,
"preload_agent": PRELOAD_AGENT,
"runtime": runtime,
}
@app.get("/", include_in_schema=False)
def root():
return RedirectResponse(url="/docs")
@app.get("/favicon.ico", include_in_schema=False)
def favicon():
return Response(status_code=204)
@app.get("/debug/files")
def debug_files():
data_path = Path(DATA_DIR)
logs_path = Path(LOGS_DIR)
feedback_path = Path(caminho_feedback())
memory_path = Path(caminho_memoria_negativa())
return {
"data_exists": data_path.exists(),
"logs_exists": logs_path.exists(),
"feedback_exists": feedback_path.exists(),
"negative_memory_exists": memory_path.exists(),
"data_files": sorted(p.name for p in data_path.iterdir()) if data_path.exists() else [],
"logs_files": sorted(p.name for p in logs_path.iterdir()) if logs_path.exists() else [],
"feedback_file": str(feedback_path),
"negative_memory_file": str(memory_path),
}
@app.get("/debug/feedback")
def debug_feedback():
feedback_path = Path(caminho_feedback())
if not feedback_path.exists():
return {"error": "arquivo nao existe"}
return {"conteudo": feedback_path.read_text(encoding="utf-8")}
@app.get("/download/feedback")
def download_feedback():
feedback_path = caminho_feedback()
if not os.path.exists(feedback_path):
return {"error": "arquivo nao existe"}
return FileResponse(feedback_path, filename="feedback.csv")
@app.get("/debug/memory")
def debug_memory():
memory_path = Path(caminho_memoria_negativa())
if not memory_path.exists():
return {"status": "missing", "file": str(memory_path)}
return {
"status": "ok",
"file": str(memory_path),
"content": memory_path.read_text(encoding="utf-8"),
}
@app.post("/chat")
def chat(request: ChatRequest):
texto = request.query or request.message
if not texto:
return {"error": "query ou message deve ser informado"}
resultado = get_agent().responder(texto, top_k=request.top_k)
salvar_log_busca(resultado)
return resultado
@app.post("/feedback")
def feedback(request: FeedbackRequest):
feedback_file = caminho_feedback()
print(
"Salvando feedback:",
{
"query": request.query,
"product_id": request.product_id,
"feedback_file": feedback_file,
"logs_dir_exists": os.path.exists(LOGS_DIR),
},
)
try:
return salvar_feedback(
query=request.query,
product_id=request.product_id,
product_name=request.product_name,
rating=request.rating,
is_helpful=request.is_helpful
)
except Exception as exc:
return {
"status": "error",
"message": "Erro ao salvar feedback.",
"detail": str(exc),
"feedback_file": feedback_file,
"logs_dir_exists": os.path.exists(LOGS_DIR),
}