Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -6,8 +6,9 @@ import asyncio
|
|
| 6 |
import threading
|
| 7 |
import json
|
| 8 |
import re
|
|
|
|
| 9 |
from fastapi import FastAPI, Request
|
| 10 |
-
from fastapi.responses import JSONResponse
|
| 11 |
|
| 12 |
# ====================================================================
|
| 13 |
# Configuration
|
|
@@ -15,7 +16,7 @@ from fastapi.responses import JSONResponse
|
|
| 15 |
API_SECRET_KEY = os.getenv("API_SECRET_KEY", "change-me-secret")
|
| 16 |
POOL_SIZE = int(os.getenv("POOL_SIZE", "5"))
|
| 17 |
MAX_REQUESTS = int(os.getenv("MAX_REQUESTS", "30"))
|
| 18 |
-
QUEUE_TIMEOUT = int(os.getenv("QUEUE_TIMEOUT", "300"))
|
| 19 |
|
| 20 |
DUCK_MODELS = {
|
| 21 |
"gpt-5-mini": "GPT-5 mini",
|
|
@@ -136,10 +137,8 @@ class BrowserWorker:
|
|
| 136 |
self.busy = False
|
| 137 |
|
| 138 |
async def _do_chat(self, model_label: str, prompt: str) -> str:
|
| 139 |
-
# تأخير عشوائي لتبدو الطلبات طبيعية
|
| 140 |
await asyncio.sleep(random.uniform(0.5, 2.0))
|
| 141 |
|
| 142 |
-
# تجديد context دورياً
|
| 143 |
self._request_count += 1
|
| 144 |
self._total_count += 1
|
| 145 |
if self._request_count >= MAX_REQUESTS:
|
|
@@ -324,7 +323,7 @@ class BrowserPool:
|
|
| 324 |
self._thread = threading.Thread(target=self._run, daemon=True)
|
| 325 |
self._queue: asyncio.Queue | None = None
|
| 326 |
self._total_requests = 0
|
| 327 |
-
self._rejected = 0
|
| 328 |
|
| 329 |
def start(self):
|
| 330 |
self._thread.start()
|
|
@@ -350,8 +349,6 @@ class BrowserPool:
|
|
| 350 |
|
| 351 |
async def _process(self, model_label: str, prompt: str) -> str:
|
| 352 |
self._total_requests += 1
|
| 353 |
-
|
| 354 |
-
# انتظر worker حر — بحد أقصى QUEUE_TIMEOUT ثانية
|
| 355 |
try:
|
| 356 |
worker: BrowserWorker = await asyncio.wait_for(
|
| 357 |
self._queue.get(), timeout=QUEUE_TIMEOUT
|
|
@@ -365,8 +362,7 @@ class BrowserPool:
|
|
| 365 |
f"Please retry in a moment. (rejected total: {self._rejected})"
|
| 366 |
)
|
| 367 |
|
| 368 |
-
print(f"[POOL] Assigned W{worker.id} "
|
| 369 |
-
f"(total req: {self._total_requests}) ✓")
|
| 370 |
try:
|
| 371 |
return await worker.chat(model_label, prompt)
|
| 372 |
finally:
|
|
@@ -598,27 +594,42 @@ async def health():
|
|
| 598 |
busy = sum(1 for w in pool.workers if w.busy)
|
| 599 |
stats = [
|
| 600 |
{
|
| 601 |
-
"id":
|
| 602 |
-
"busy":
|
| 603 |
-
"total_requests":
|
| 604 |
"requests_until_rotation": MAX_REQUESTS - w._request_count,
|
| 605 |
}
|
| 606 |
for w in pool.workers
|
| 607 |
]
|
|
|
|
| 608 |
return {
|
| 609 |
-
"status":
|
| 610 |
-
"message":
|
| 611 |
-
"models":
|
| 612 |
-
"pool_size":
|
| 613 |
-
"workers_busy":
|
| 614 |
-
"workers_free":
|
| 615 |
-
"total_requests":
|
| 616 |
"rejected_requests": pool._rejected,
|
| 617 |
"queue_timeout_sec": QUEUE_TIMEOUT,
|
| 618 |
-
"workers":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
}
|
| 620 |
|
| 621 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
if __name__ == "__main__":
|
| 623 |
import uvicorn
|
| 624 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 6 |
import threading
|
| 7 |
import json
|
| 8 |
import re
|
| 9 |
+
import psutil
|
| 10 |
from fastapi import FastAPI, Request
|
| 11 |
+
from fastapi.responses import JSONResponse, HTMLResponse
|
| 12 |
|
| 13 |
# ====================================================================
|
| 14 |
# Configuration
|
|
|
|
| 16 |
API_SECRET_KEY = os.getenv("API_SECRET_KEY", "change-me-secret")
|
| 17 |
POOL_SIZE = int(os.getenv("POOL_SIZE", "5"))
|
| 18 |
MAX_REQUESTS = int(os.getenv("MAX_REQUESTS", "30"))
|
| 19 |
+
QUEUE_TIMEOUT = int(os.getenv("QUEUE_TIMEOUT", "300"))
|
| 20 |
|
| 21 |
DUCK_MODELS = {
|
| 22 |
"gpt-5-mini": "GPT-5 mini",
|
|
|
|
| 137 |
self.busy = False
|
| 138 |
|
| 139 |
async def _do_chat(self, model_label: str, prompt: str) -> str:
|
|
|
|
| 140 |
await asyncio.sleep(random.uniform(0.5, 2.0))
|
| 141 |
|
|
|
|
| 142 |
self._request_count += 1
|
| 143 |
self._total_count += 1
|
| 144 |
if self._request_count >= MAX_REQUESTS:
|
|
|
|
| 323 |
self._thread = threading.Thread(target=self._run, daemon=True)
|
| 324 |
self._queue: asyncio.Queue | None = None
|
| 325 |
self._total_requests = 0
|
| 326 |
+
self._rejected = 0
|
| 327 |
|
| 328 |
def start(self):
|
| 329 |
self._thread.start()
|
|
|
|
| 349 |
|
| 350 |
async def _process(self, model_label: str, prompt: str) -> str:
|
| 351 |
self._total_requests += 1
|
|
|
|
|
|
|
| 352 |
try:
|
| 353 |
worker: BrowserWorker = await asyncio.wait_for(
|
| 354 |
self._queue.get(), timeout=QUEUE_TIMEOUT
|
|
|
|
| 362 |
f"Please retry in a moment. (rejected total: {self._rejected})"
|
| 363 |
)
|
| 364 |
|
| 365 |
+
print(f"[POOL] Assigned W{worker.id} (total req: {self._total_requests}) ✓")
|
|
|
|
| 366 |
try:
|
| 367 |
return await worker.chat(model_label, prompt)
|
| 368 |
finally:
|
|
|
|
| 594 |
busy = sum(1 for w in pool.workers if w.busy)
|
| 595 |
stats = [
|
| 596 |
{
|
| 597 |
+
"id": w.id,
|
| 598 |
+
"busy": w.busy,
|
| 599 |
+
"total_requests": w._total_count,
|
| 600 |
"requests_until_rotation": MAX_REQUESTS - w._request_count,
|
| 601 |
}
|
| 602 |
for w in pool.workers
|
| 603 |
]
|
| 604 |
+
mem = psutil.virtual_memory()
|
| 605 |
return {
|
| 606 |
+
"status": "running",
|
| 607 |
+
"message": "Duck.ai API Pool Server is active!",
|
| 608 |
+
"models": ALL_MODELS,
|
| 609 |
+
"pool_size": POOL_SIZE,
|
| 610 |
+
"workers_busy": busy,
|
| 611 |
+
"workers_free": POOL_SIZE - busy,
|
| 612 |
+
"total_requests": pool._total_requests,
|
| 613 |
"rejected_requests": pool._rejected,
|
| 614 |
"queue_timeout_sec": QUEUE_TIMEOUT,
|
| 615 |
+
"workers": stats,
|
| 616 |
+
"ram": {
|
| 617 |
+
"used_gb": round(mem.used / 1024**3, 2),
|
| 618 |
+
"total_gb": round(mem.total / 1024**3, 2),
|
| 619 |
+
"percent": mem.percent,
|
| 620 |
+
},
|
| 621 |
+
"cpu": psutil.cpu_percent(interval=None),
|
| 622 |
}
|
| 623 |
|
| 624 |
|
| 625 |
+
@app.get("/dashboard", response_class=HTMLResponse)
|
| 626 |
+
async def dashboard(request: Request):
|
| 627 |
+
if not _auth(request):
|
| 628 |
+
return HTMLResponse("<h1>401 Unauthorized</h1>", status_code=401)
|
| 629 |
+
with open("dashboard.html", "r", encoding="utf-8") as f:
|
| 630 |
+
return HTMLResponse(f.read())
|
| 631 |
+
|
| 632 |
+
|
| 633 |
if __name__ == "__main__":
|
| 634 |
import uvicorn
|
| 635 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|