ruslanmv commited on
Commit
874d7cd
·
1 Parent(s): 820c76e

First commit

Browse files
Files changed (2) hide show
  1. Dockerfile +10 -11
  2. app/main.py +19 -23
Dockerfile CHANGED
@@ -4,8 +4,7 @@ FROM python:3.11-slim
4
  # --- base env ---
5
  ENV PYTHONDONTWRITEBYTECODE=1 \
6
  PYTHONUNBUFFERED=1 \
7
- PIP_NO_CACHE_DIR=1 \
8
- UVICORN_WORKERS=2
9
 
10
  # --- system deps ---
11
  RUN apt-get update \
@@ -15,21 +14,21 @@ RUN apt-get update \
15
  # --- app dir ---
16
  WORKDIR /app
17
 
18
- # --- python deps layer (better cache) ---
19
- COPY requirements.txt ./
20
  RUN pip install --upgrade pip && pip install -r requirements.txt
21
 
22
  # --- copy app ---
23
  COPY . .
24
 
25
- # --- Spaces sets $PORT dynamically; honor it ---
26
- ARG PORT=7860
27
- ENV PORT=${PORT}
28
- EXPOSE ${PORT}
29
 
30
- # Optional: run as non-root (safer)
31
  # RUN useradd -ms /bin/bash appuser && chown -R appuser:appuser /app
32
  # USER appuser
33
 
34
- # --- start ---
35
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "${PORT}"]
 
 
4
  # --- base env ---
5
  ENV PYTHONDONTWRITEBYTECODE=1 \
6
  PYTHONUNBUFFERED=1 \
7
+ PIP_NO_CACHE_DIR=1
 
8
 
9
  # --- system deps ---
10
  RUN apt-get update \
 
14
  # --- app dir ---
15
  WORKDIR /app
16
 
17
+ # --- python deps (cache friendly layer) ---
18
+ COPY requirements.txt ./
19
  RUN pip install --upgrade pip && pip install -r requirements.txt
20
 
21
  # --- copy app ---
22
  COPY . .
23
 
24
+ # Hugging Face sets $PORT at runtime; keep a sane default for local runs
25
+ ENV PORT=7860
26
+ EXPOSE 7860
 
27
 
28
+ # Optional: run as non-root
29
  # RUN useradd -ms /bin/bash appuser && chown -R appuser:appuser /app
30
  # USER appuser
31
 
32
+ # --- start (shell form so $PORT expands) ---
33
+ # --proxy-headers is helpful behind HF’s proxy
34
+ CMD uvicorn app.main:app --host 0.0.0.0 --port $PORT --proxy-headers
app/main.py CHANGED
@@ -7,12 +7,22 @@ from contextlib import asynccontextmanager
7
  from typing import Any, Dict
8
 
9
  from fastapi import FastAPI
10
- from fastapi.responses import JSONResponse, RedirectResponse
11
 
12
- # Your existing middleware bundle (req id, rate limit, etag, etc.)
13
- from .middleware import attach_middlewares
14
-
15
- # Core API routers
 
 
 
 
 
 
 
 
 
 
16
  from .routers import health, plan, chat
17
 
18
  # Optional UI (Home/Chat/Dev). If missing, we gracefully fall back to a JSON root.
@@ -22,7 +32,6 @@ try:
22
  except Exception: # pragma: no cover
23
  HAS_UI = False
24
 
25
-
26
  TAGS_METADATA = [
27
  {"name": "Health", "description": "Liveness / readiness probes and basic service metadata."},
28
  {"name": "Planning", "description": "AI plan generation for Matrix Guardian (/v1/plan)."},
@@ -30,17 +39,12 @@ TAGS_METADATA = [
30
  {"name": "UI", "description": "Minimal web UI (Home, Chat, Dev) if enabled."},
31
  ]
32
 
33
-
34
  @asynccontextmanager
35
  async def lifespan(app: FastAPI):
36
- """
37
- Lightweight startup/shutdown hooks.
38
- Stores process start time for basic diagnostics and logs boot/shutdown.
39
- """
40
  app.state.started_at = time.time()
41
  app.state.version = os.getenv("APP_VERSION", "1.0.0")
42
  logging.getLogger("uvicorn.error").info(
43
- "matrix-ai starting (version=%s)", app.state.version
44
  )
45
  try:
46
  yield
@@ -50,9 +54,7 @@ async def lifespan(app: FastAPI):
50
  "matrix-ai shutting down (uptime=%.2fs)", uptime
51
  )
52
 
53
-
54
  def create_app() -> FastAPI:
55
- """Create and configure the FastAPI application instance."""
56
  app = FastAPI(
57
  title="matrix-ai",
58
  version=os.getenv("APP_VERSION", "1.0.0"),
@@ -63,7 +65,7 @@ def create_app() -> FastAPI:
63
  lifespan=lifespan,
64
  )
65
 
66
- # Middlewares (request-id, gzip, rate-limit, idempotency headers, etc.)
67
  attach_middlewares(app)
68
 
69
  # Core routers
@@ -75,7 +77,7 @@ def create_app() -> FastAPI:
75
  if HAS_UI:
76
  app.include_router(ui_router, tags=["UI"])
77
  else:
78
- # Minimal root so HF Spaces / root health probes pass even without UI
79
  @app.get("/", include_in_schema=False)
80
  async def root() -> Dict[str, Any]:
81
  return {
@@ -83,19 +85,13 @@ def create_app() -> FastAPI:
83
  "service": "matrix-ai",
84
  "version": app.version,
85
  "docs": "/docs",
86
- "endpoints": {
87
- "plan": "/v1/plan",
88
- "chat": "/v1/chat",
89
- "healthz": "/healthz",
90
- },
91
  }
92
 
93
- # Optional convenience redirect to API docs
94
  @app.get("/home", include_in_schema=False)
95
  async def home_redirect():
96
  return RedirectResponse(url="/docs", status_code=302)
97
 
98
  return app
99
 
100
-
101
  app = create_app()
 
7
  from typing import Any, Dict
8
 
9
  from fastapi import FastAPI
10
+ from fastapi.responses import RedirectResponse
11
 
12
+ # --- Middlewares ---
13
+ # Prefer the canonical package name; if your repo uses "middlewares/", this tries both.
14
+ try:
15
+ from .middleware import attach_middlewares # singular
16
+ except Exception:
17
+ try:
18
+ from .middlewares import attach_middlewares # plural
19
+ except Exception:
20
+ def attach_middlewares(app: FastAPI) -> None: # no-op fallback
21
+ logging.getLogger("uvicorn.error").warning(
22
+ "attach_middlewares not found; continuing without custom middlewares."
23
+ )
24
+
25
+ # --- Routers ---
26
  from .routers import health, plan, chat
27
 
28
  # Optional UI (Home/Chat/Dev). If missing, we gracefully fall back to a JSON root.
 
32
  except Exception: # pragma: no cover
33
  HAS_UI = False
34
 
 
35
  TAGS_METADATA = [
36
  {"name": "Health", "description": "Liveness / readiness probes and basic service metadata."},
37
  {"name": "Planning", "description": "AI plan generation for Matrix Guardian (/v1/plan)."},
 
39
  {"name": "UI", "description": "Minimal web UI (Home, Chat, Dev) if enabled."},
40
  ]
41
 
 
42
  @asynccontextmanager
43
  async def lifespan(app: FastAPI):
 
 
 
 
44
  app.state.started_at = time.time()
45
  app.state.version = os.getenv("APP_VERSION", "1.0.0")
46
  logging.getLogger("uvicorn.error").info(
47
+ "matrix-ai starting (version=%s, port=%s)", app.state.version, os.getenv("PORT", "7860")
48
  )
49
  try:
50
  yield
 
54
  "matrix-ai shutting down (uptime=%.2fs)", uptime
55
  )
56
 
 
57
  def create_app() -> FastAPI:
 
58
  app = FastAPI(
59
  title="matrix-ai",
60
  version=os.getenv("APP_VERSION", "1.0.0"),
 
65
  lifespan=lifespan,
66
  )
67
 
68
+ # Middlewares (request-id, gzip, rate-limit, etc.)
69
  attach_middlewares(app)
70
 
71
  # Core routers
 
77
  if HAS_UI:
78
  app.include_router(ui_router, tags=["UI"])
79
  else:
80
+ # Minimal root so HF root probes pass even without UI
81
  @app.get("/", include_in_schema=False)
82
  async def root() -> Dict[str, Any]:
83
  return {
 
85
  "service": "matrix-ai",
86
  "version": app.version,
87
  "docs": "/docs",
88
+ "endpoints": {"plan": "/v1/plan", "chat": "/v1/chat", "healthz": "/healthz"},
 
 
 
 
89
  }
90
 
 
91
  @app.get("/home", include_in_schema=False)
92
  async def home_redirect():
93
  return RedirectResponse(url="/docs", status_code=302)
94
 
95
  return app
96
 
 
97
  app = create_app()