File size: 3,293 Bytes
e7d7c61
 
 
 
 
 
 
 
 
 
 
 
99fa656
 
e7d7c61
99fa656
 
 
e7d7c61
99fa656
e7d7c61
 
370553a
 
 
 
 
 
c1bc4eb
99fa656
570f7bd
e7d7c61
 
570f7bd
 
5b7b403
 
570f7bd
e7d7c61
99fa656
e7d7c61
 
 
 
 
 
 
 
 
 
 
 
 
c1bc4eb
99fa656
e7d7c61
 
 
 
 
99fa656
e7d7c61
 
 
 
 
 
 
99fa656
 
e7d7c61
 
 
 
99fa656
 
 
 
e7d7c61
 
 
 
 
 
 
99fa656
 
 
 
e7d7c61
 
 
 
99fa656
 
 
 
e7d7c61
 
 
570f7bd
c1bc4eb
99fa656
570f7bd
 
 
c1bc4eb
99fa656
570f7bd
c1bc4eb
e7d7c61
 
99fa656
e7d7c61
 
 
ccefd8e
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import os
import time
from fastapi import FastAPI, Request, Response, HTTPException
from fastapi.responses import PlainTextResponse
from prometheus_client import (
    Counter,
    Histogram,
    CollectorRegistry,
    generate_latest,
    CONTENT_TYPE_LATEST,
)

try:
    from dotenv import load_dotenv

    load_dotenv()
except Exception:
    pass

from app.routers import nl2sql

# ---- Optionally restore uploaded DB map ----
try:
    from app.routers.nl2sql import _load_db_map

    _load_db_map()
except Exception as e:
    print(f"⚠️ DB map not restored: {e}")

application: FastAPI = FastAPI(
    title="NL2SQL Copilot Prototype",
    version=os.getenv("APP_VERSION", "0.1.0"),
    description="Convert natural language to safe & verified SQL",
)

application.include_router(nl2sql.router, prefix="/api/v1")  # e.g. /api/v1/nl2sql
application.include_router(nl2sql.router)  # e.g. /nl2sql

# ---- Prometheus metrics ----
REGISTRY = CollectorRegistry()
REQUEST_COUNT = Counter(
    "http_requests_total",
    "Total HTTP requests",
    ["path", "method", "status_code"],
    registry=REGISTRY,
)
REQUEST_LATENCY = Histogram(
    "http_request_latency_seconds",
    "Request latency",
    ["path", "method"],
    registry=REGISTRY,
)


@application.middleware("http")
async def metrics_middleware(request: Request, call_next):
    start = time.perf_counter()
    response: Response = await call_next(request)
    elapsed = time.perf_counter() - start
    route = request.scope.get("route")
    path = route.path if route else request.url.path
    REQUEST_COUNT.labels(
        path=path, method=request.method, status_code=str(response.status_code)
    ).inc()
    REQUEST_LATENCY.labels(path=path, method=request.method).observe(elapsed)
    return response


# --- Liveness ---
@application.get("/healthz", response_class=PlainTextResponse, tags=["system"])
def healthz() -> str:
    return "ok"


# --- Readiness ---


@application.get("/readyz", response_class=PlainTextResponse, tags=["system"])
def readyz() -> str:
    mode = os.getenv("DB_MODE", "sqlite").lower()
    try:
        if mode == "postgres":
            from adapters.db.postgres_adapter import PostgresAdapter

            dsn = os.environ["POSTGRES_DSN"]
            pg = PostgresAdapter(dsn)
            ping = getattr(pg, "ping", None)
            if callable(ping):
                ping()
        else:
            from adapters.db.sqlite_adapter import SQLiteAdapter

            db_path = os.getenv("SQLITE_DB_PATH", "data/chinook.db")
            sq = SQLiteAdapter(db_path)
            ping = getattr(sq, "ping", None)
            if callable(ping):
                ping()
        return "ready"
    except Exception:
        raise HTTPException(status_code=503, detail="not ready")


@application.get("/")
def root():
    return {"status": "ok", "message": "NL2SQL Copilot API is running"}


@application.get("/health")
def health():
    return {"status": "ok", "db": "connected", "llm": "reachable", "uptime_sec": 123.4}


@application.get("/metrics", tags=["system"])
def metrics():
    data = generate_latest(REGISTRY)
    return Response(content=data, media_type=CONTENT_TYPE_LATEST)


# Backward compatibility for tests & uvicorn targets
app: FastAPI = application
__all__ = ["application", "app"]