All checks were successful
CD / build (pull_request) Successful in 5m2s
Adequa o repo ao guia de promoção lab -> homolog mantendo Python (Gate 0 via stackException). Cumpre os gates aplicáveis: - promotion-manifest.yaml: stackException (Python/Vanna), Gate B N/A, Gate D/G declarados, capacidade e deltas. - k8s/hml/: overlay homolog — secret DB dedicado (vanna-clubpetro-db, Gate G), probes httpGet /health (Gate E), resources.requests, replicas:1, non-root securityContext; host homologation.clubpetro.com (sem host de lab). - server.py: rota GET /health (alvo das probes). - Dockerfile: usuário non-root uid/gid 10001 + HOME/cache graváveis (Gate E). - .env.example: remove literal lab.clubpetro.com do CORS (Gate D). - k8s/ flat movido para k8s/lab/ (simetria lab/hml); cd.yml aplica k8s/lab/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""FastAPI server: oficial VannaFastAPIServer + estáticos do web component."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from fastapi.responses import FileResponse, HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from vanna.servers.fastapi import VannaFastAPIServer
|
|
|
|
import csv_cleanup
|
|
from agent import RequestContextUserResolver, build_agent
|
|
from chat_filter import FilteringChatHandler
|
|
from events_sink import EventSink
|
|
|
|
|
|
def _build_app():
|
|
agent = build_agent(user_resolver=RequestContextUserResolver())
|
|
cors_origins = [
|
|
o.strip()
|
|
for o in os.environ.get("VANNA_CORS_ORIGINS", "*").split(",")
|
|
if o.strip()
|
|
] or ["*"]
|
|
server = VannaFastAPIServer(
|
|
agent,
|
|
config={
|
|
"cors": {
|
|
"allow_origins": cors_origins,
|
|
"allow_methods": ["*"],
|
|
"allow_headers": ["*"],
|
|
"allow_credentials": True,
|
|
},
|
|
"api_base_url": "",
|
|
},
|
|
)
|
|
server.chat_handler = FilteringChatHandler(agent, event_sink=EventSink())
|
|
fastapi_app = server.create_app()
|
|
|
|
here = os.path.dirname(os.path.abspath(__file__))
|
|
dist = os.path.join(here, "vanna", "frontends", "webcomponent", "dist")
|
|
if os.path.isdir(dist):
|
|
fastapi_app.mount("/static", StaticFiles(directory=dist), name="static")
|
|
|
|
@fastapi_app.get("/health")
|
|
async def health():
|
|
# Alvo das probes de liveness/readiness (Gate E). Leve, sem tocar
|
|
# ClickHouse/LLM — só sinaliza que o processo subiu e serve HTTP.
|
|
return {"status": "ok"}
|
|
|
|
@fastapi_app.get("/vanna-theme.css")
|
|
async def vanna_theme():
|
|
path = os.path.join(here, "static", "vanna-theme.css")
|
|
return FileResponse(path, media_type="text/css")
|
|
|
|
@fastapi_app.get("/vanna-embed-bootstrap.js")
|
|
async def vanna_embed_bootstrap():
|
|
path = os.path.join(here, "static", "vanna-embed-bootstrap.js")
|
|
return FileResponse(path, media_type="application/javascript")
|
|
|
|
@fastapi_app.get("/clubpetro-logo.png")
|
|
async def clubpetro_logo():
|
|
path = os.path.join(here, "static", "clubpetro-logo.png")
|
|
return FileResponse(path, media_type="image/png")
|
|
|
|
@fastapi_app.get("/clubpetro-logo.svg")
|
|
async def clubpetro_logo_svg():
|
|
path = os.path.join(here, "static", "clubpetro-logo.svg")
|
|
return FileResponse(path, media_type="image/svg+xml")
|
|
|
|
@fastapi_app.get("/dashboard-bg.png")
|
|
async def dashboard_bg():
|
|
path = os.path.join(here, "static", "dashboard-bg.png")
|
|
return FileResponse(path, media_type="image/png")
|
|
|
|
@fastapi_app.get("/embed-demo.html", response_class=HTMLResponse)
|
|
async def embed_demo():
|
|
path = os.path.join(here, "static", "embed-demo.html")
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
html = f.read()
|
|
return (
|
|
html
|
|
.replace("__PROGRAM_ID__", os.environ.get("RLS_PROGRAM_ID", ""))
|
|
.replace("__STORE_ID__", os.environ.get("RLS_STORE_ID", ""))
|
|
.replace("__USER_ID__", os.environ.get("RLS_USER_ID", ""))
|
|
)
|
|
|
|
return fastapi_app
|
|
|
|
|
|
app = _build_app()
|
|
|
|
|
|
@app.on_event("startup")
|
|
async def _csv_cleanup_startup() -> None:
|
|
await csv_cleanup.startup()
|
|
|
|
|
|
@app.on_event("shutdown")
|
|
async def _csv_cleanup_shutdown() -> None:
|
|
await csv_cleanup.shutdown()
|