remsky commited on
Commit
071c2c6
·
unverified ·
1 Parent(s): 6fd86cc

feat: version update, rate limit, etc

Browse files
Files changed (5) hide show
  1. .gitignore +1 -0
  2. Dockerfile +10 -15
  3. README.md +1 -0
  4. hf_app.py +47 -0
  5. hf_start.py +0 -12
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pyc
Dockerfile CHANGED
@@ -1,19 +1,14 @@
1
- FROM ghcr.io/remsky/kokoro-fastapi-cpu:latest
2
 
3
- # Environment setup
4
- ENV HOST=0.0.0.0
5
- ENV PORT=7860
6
 
7
- # ONNX optimization for 2 CPU environment
8
- ENV ONNX_MAX_INSTANCES=2
9
- ENV ONNX_NUM_THREADS=2
10
- ENV ONNX_INTER_OP_THREADS=1
11
- ENV VOICE_CACHE_SIZE=1
12
 
13
- # Copy startup script
14
- COPY hf_start.py /app/hf_start.py
15
 
16
- EXPOSE 7860
17
-
18
- # Run FastAPI with root UI
19
- CMD ["python", "/app/hf_start.py"]
 
1
+ FROM ghcr.io/remsky/kokoro-fastapi-cpu:v0.7.2
2
 
3
+ # 2 vCPU Space: keep torch from oversubscribing threads
4
+ ENV OMP_NUM_THREADS=2
5
+ ENV MKL_NUM_THREADS=2
6
 
7
+ # Default is DEBUG, which logs every caller's submitted text
8
+ ENV API_LOG_LEVEL=INFO
 
 
 
9
 
10
+ # Root redirect to the bundled web player
11
+ COPY hf_app.py /app/hf_app.py
12
 
13
+ EXPOSE 8880
14
+ CMD ["python", "-m", "uvicorn", "hf_app:app", "--host", "0.0.0.0", "--port", "8880"]
 
 
README.md CHANGED
@@ -4,6 +4,7 @@ emoji: 📼
4
  colorFrom: gray
5
  colorTo: pink
6
  sdk: docker
 
7
  pinned: true
8
  short_description: Web Utility that's living on Kokoro-FastAPI
9
  models:
 
4
  colorFrom: gray
5
  colorTo: pink
6
  sdk: docker
7
+ app_port: 8880
8
  pinned: true
9
  short_description: Web Utility that's living on Kokoro-FastAPI
10
  models:
hf_app.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HF Space shim: serve the web player at / instead of a bare 404.
2
+ import os
3
+ import time
4
+ from collections import deque
5
+
6
+ from fastapi import Request
7
+ from fastapi.responses import JSONResponse, RedirectResponse
8
+
9
+ from api.src.main import app
10
+
11
+ MAX_BODY_BYTES = int(os.getenv("MAX_BODY_BYTES", 8192))
12
+ RATE_LIMIT_PER_MIN = int(os.getenv("RATE_LIMIT_PER_MIN", 20))
13
+
14
+ _hits: dict[str, deque] = {}
15
+
16
+
17
+ def _client_ip(request: Request) -> str:
18
+ forwarded = request.headers.get("x-forwarded-for", "").split(",")[-1].strip()
19
+ return forwarded or (request.client.host if request.client else "unknown")
20
+
21
+
22
+ @app.middleware("http")
23
+ async def throttle(request: Request, call_next):
24
+ if request.method != "POST":
25
+ return await call_next(request)
26
+
27
+ length = request.headers.get("content-length")
28
+ if length and int(length) > MAX_BODY_BYTES:
29
+ return JSONResponse({"detail": "Request body too large"}, status_code=413)
30
+
31
+ now = time.monotonic()
32
+ for stale in [k for k, v in _hits.items() if not v or v[-1] < now - 60]:
33
+ del _hits[stale]
34
+ hits = _hits.setdefault(_client_ip(request), deque())
35
+ while hits and hits[0] < now - 60:
36
+ hits.popleft()
37
+ if len(hits) >= RATE_LIMIT_PER_MIN:
38
+ return JSONResponse(
39
+ {"detail": "Rate limit exceeded"}, status_code=429, headers={"Retry-After": "60"}
40
+ )
41
+ hits.append(now)
42
+ return await call_next(request)
43
+
44
+
45
+ @app.get("/", include_in_schema=False)
46
+ async def root():
47
+ return RedirectResponse(url="/web/")
hf_start.py DELETED
@@ -1,12 +0,0 @@
1
- from api.src.main import app
2
- from fastapi.staticfiles import StaticFiles
3
- from fastapi.responses import RedirectResponse
4
-
5
- # Add root redirect to web UI
6
- @app.get("/")
7
- async def root():
8
- return RedirectResponse(url="/web/")
9
-
10
- if __name__ == "__main__":
11
- import uvicorn
12
- uvicorn.run(app, host="0.0.0.0", port=7860)