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), }