File size: 3,229 Bytes
e465928
 
 
 
 
 
 
 
fed7eb0
e465928
 
 
fed7eb0
e465928
 
fed7eb0
 
e465928
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fed7eb0
e465928
fed7eb0
 
e465928
 
 
 
 
 
fed7eb0
e465928
 
fed7eb0
e465928
 
fed7eb0
 
 
e465928
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fed7eb0
 
e465928
fed7eb0
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
from __future__ import annotations

import logging
import os
import time
from contextlib import asynccontextmanager
from typing import Any, Dict

from fastapi import FastAPI
from fastapi.responses import JSONResponse, RedirectResponse

# Your existing middleware bundle (req id, rate limit, etag, etc.)
from .middleware import attach_middlewares

# Core API routers
from .routers import health, plan, chat

# Optional UI (Home/Chat/Dev). If missing, we gracefully fall back to a JSON root.
try:
    from .ui import router as ui_router  # type: ignore
    HAS_UI = True
except Exception:  # pragma: no cover
    HAS_UI = False


TAGS_METADATA = [
    {"name": "Health", "description": "Liveness / readiness probes and basic service metadata."},
    {"name": "Planning", "description": "AI plan generation for Matrix Guardian (/v1/plan)."},
    {"name": "Chat", "description": "Lightweight RAG/Q&A about Matrix System (/v1/chat)."},
    {"name": "UI", "description": "Minimal web UI (Home, Chat, Dev) if enabled."},
]


@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    Lightweight startup/shutdown hooks.
    Stores process start time for basic diagnostics and logs boot/shutdown.
    """
    app.state.started_at = time.time()
    app.state.version = os.getenv("APP_VERSION", "1.0.0")
    logging.getLogger("uvicorn.error").info(
        "matrix-ai starting (version=%s)", app.state.version
    )
    try:
        yield
    finally:
        uptime = time.time() - getattr(app.state, "started_at", time.time())
        logging.getLogger("uvicorn.error").info(
            "matrix-ai shutting down (uptime=%.2fs)", uptime
        )


def create_app() -> FastAPI:
    """Create and configure the FastAPI application instance."""
    app = FastAPI(
        title="matrix-ai",
        version=os.getenv("APP_VERSION", "1.0.0"),
        description="AI planning microservice for the Matrix EcoSystem",
        openapi_tags=TAGS_METADATA,
        docs_url="/docs",
        redoc_url=None,
        lifespan=lifespan,
    )

    # Middlewares (request-id, gzip, rate-limit, idempotency headers, etc.)
    attach_middlewares(app)

    # Core routers
    app.include_router(health.router, tags=["Health"])
    app.include_router(plan.router, prefix="/v1", tags=["Planning"])
    app.include_router(chat.router, prefix="/v1", tags=["Chat"])

    # Optional UI (adds '/', '/chat', '/dev')
    if HAS_UI:
        app.include_router(ui_router, tags=["UI"])
    else:
        # Minimal root so HF Spaces / root health probes pass even without UI
        @app.get("/", include_in_schema=False)
        async def root() -> Dict[str, Any]:
            return {
                "ok": True,
                "service": "matrix-ai",
                "version": app.version,
                "docs": "/docs",
                "endpoints": {
                    "plan": "/v1/plan",
                    "chat": "/v1/chat",
                    "healthz": "/healthz",
                },
            }

        # Optional convenience redirect to API docs
        @app.get("/home", include_in_schema=False)
        async def home_redirect():
            return RedirectResponse(url="/docs", status_code=302)

    return app


app = create_app()