Spaces:
Sleeping
Sleeping
Cyber Catalyst Team commited on
Commit ·
1e1e634
1
Parent(s): da93542
Add new NIM models + background health check loop
Browse files- backend.py +70 -0
backend.py
CHANGED
|
@@ -27,6 +27,7 @@ from fastapi import FastAPI, Request, Header, HTTPException
|
|
| 27 |
from fastapi.responses import StreamingResponse, JSONResponse
|
| 28 |
from fastapi.middleware.cors import CORSMiddleware
|
| 29 |
from openai import AsyncOpenAI
|
|
|
|
| 30 |
import asyncpg
|
| 31 |
|
| 32 |
# ---------------------------------------------------------------------------
|
|
@@ -41,6 +42,12 @@ MAX_TOOL_ROUNDS = int(os.environ.get("MAX_TOOL_ROUNDS", "10"))
|
|
| 41 |
|
| 42 |
# NIM models that reliably support tool/function calling
|
| 43 |
TOOL_CAPABLE_MODELS = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
"meta/llama-3.1-70b-instruct": "Llama 3.1 70B (Agentic)",
|
| 45 |
"meta/llama-3.1-405b-instruct": "Llama 3.1 405B (Agentic)",
|
| 46 |
"qwen/qwen2.5-coder-32b-instruct": "Qwen 2.5 Coder 32B (Agentic)",
|
|
@@ -55,6 +62,8 @@ ALL_MODELS = {
|
|
| 55 |
"mistralai/mistral-large-2-instruct": "Mistral Large 2 (Chat only)",
|
| 56 |
}
|
| 57 |
|
|
|
|
|
|
|
| 58 |
# Ensure workspace exists
|
| 59 |
Path(WORKSPACE_DIR).mkdir(parents=True, exist_ok=True)
|
| 60 |
|
|
@@ -400,10 +409,70 @@ def auth(authorization: str = None):
|
|
| 400 |
raise HTTPException(status_code=401, detail="Unauthorized")
|
| 401 |
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
@app.on_event("startup")
|
| 404 |
async def startup():
|
| 405 |
await init_db()
|
| 406 |
Path(WORKSPACE_DIR).mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
| 407 |
print(f"[Backend] Started. Workspace: {WORKSPACE_DIR}")
|
| 408 |
print(f"[Backend] Tool-capable models: {list(TOOL_CAPABLE_MODELS.keys())}")
|
| 409 |
print(f"[Backend] DB connected: {db_pool is not None}")
|
|
@@ -656,6 +725,7 @@ async def health():
|
|
| 656 |
"workspace_exists": Path(WORKSPACE_DIR).exists(),
|
| 657 |
"db_connected": db_pool is not None,
|
| 658 |
"models_count": len(ALL_MODELS),
|
|
|
|
| 659 |
}
|
| 660 |
|
| 661 |
|
|
|
|
| 27 |
from fastapi.responses import StreamingResponse, JSONResponse
|
| 28 |
from fastapi.middleware.cors import CORSMiddleware
|
| 29 |
from openai import AsyncOpenAI
|
| 30 |
+
import anyio
|
| 31 |
import asyncpg
|
| 32 |
|
| 33 |
# ---------------------------------------------------------------------------
|
|
|
|
| 42 |
|
| 43 |
# NIM models that reliably support tool/function calling
|
| 44 |
TOOL_CAPABLE_MODELS = {
|
| 45 |
+
"nvidia/nemotron-3-ultra-550b-a55b": "Nemotron 3 Ultra 550B (Agentic)",
|
| 46 |
+
"z-ai/glm-5.1": "GLM 5.1 (Agentic)",
|
| 47 |
+
"moonshotai/kimi-k2.6": "Kimi K2.6 (Agentic)",
|
| 48 |
+
"minimaxai/minimax-m3": "MiniMax M3 (Agentic)",
|
| 49 |
+
"stepfun-ai/step-3.7-flash": "Step 3.7 Flash (Agentic)",
|
| 50 |
+
"minimaxai/minimax-m2.7": "MiniMax M2.7 (Agentic)",
|
| 51 |
"meta/llama-3.1-70b-instruct": "Llama 3.1 70B (Agentic)",
|
| 52 |
"meta/llama-3.1-405b-instruct": "Llama 3.1 405B (Agentic)",
|
| 53 |
"qwen/qwen2.5-coder-32b-instruct": "Qwen 2.5 Coder 32B (Agentic)",
|
|
|
|
| 62 |
"mistralai/mistral-large-2-instruct": "Mistral Large 2 (Chat only)",
|
| 63 |
}
|
| 64 |
|
| 65 |
+
RECOMMENDED_MODEL = "nvidia/llama-3.1-nemotron-70b-instruct"
|
| 66 |
+
|
| 67 |
# Ensure workspace exists
|
| 68 |
Path(WORKSPACE_DIR).mkdir(parents=True, exist_ok=True)
|
| 69 |
|
|
|
|
| 409 |
raise HTTPException(status_code=401, detail="Unauthorized")
|
| 410 |
|
| 411 |
|
| 412 |
+
async def check_models_health():
|
| 413 |
+
global RECOMMENDED_MODEL
|
| 414 |
+
# Top models to test
|
| 415 |
+
models_to_test = [
|
| 416 |
+
"nvidia/nemotron-3-ultra-550b-a55b",
|
| 417 |
+
"z-ai/glm-5.1",
|
| 418 |
+
"moonshotai/kimi-k2.6",
|
| 419 |
+
"minimaxai/minimax-m3",
|
| 420 |
+
"stepfun-ai/step-3.7-flash",
|
| 421 |
+
"minimaxai/minimax-m2.7",
|
| 422 |
+
"nvidia/llama-3.1-nemotron-70b-instruct",
|
| 423 |
+
"meta/llama-3.1-405b-instruct",
|
| 424 |
+
"qwen/qwen2.5-coder-32b-instruct",
|
| 425 |
+
"meta/llama-3.3-70b-instruct",
|
| 426 |
+
]
|
| 427 |
+
|
| 428 |
+
best_model = None
|
| 429 |
+
best_latency = 999.0
|
| 430 |
+
|
| 431 |
+
print("[Health Check] Starting periodic model verification...")
|
| 432 |
+
for model in models_to_test:
|
| 433 |
+
start_time = time.time()
|
| 434 |
+
try:
|
| 435 |
+
# Send a fast test prompt
|
| 436 |
+
async with anyio.fail_after(15.0): # 15 seconds max timeout
|
| 437 |
+
await nim_client.chat.completions.create(
|
| 438 |
+
model=model,
|
| 439 |
+
messages=[{"role": "user", "content": "1+1="}],
|
| 440 |
+
max_tokens=3,
|
| 441 |
+
)
|
| 442 |
+
latency = time.time() - start_time
|
| 443 |
+
print(f"[Health Check] Model {model} is ONLINE. Latency: {latency:.2f}s")
|
| 444 |
+
|
| 445 |
+
# We want the model that is within 15 seconds
|
| 446 |
+
# and is the fastest (lowest latency)
|
| 447 |
+
if latency < best_latency:
|
| 448 |
+
best_latency = latency
|
| 449 |
+
best_model = model
|
| 450 |
+
|
| 451 |
+
except Exception as e:
|
| 452 |
+
print(f"[Health Check] Model {model} is OFFLINE or TIMEOUT: {e}")
|
| 453 |
+
|
| 454 |
+
if best_model:
|
| 455 |
+
RECOMMENDED_MODEL = best_model
|
| 456 |
+
print(f"[Health Check] Best model found: {RECOMMENDED_MODEL} ({best_latency:.2f}s)")
|
| 457 |
+
else:
|
| 458 |
+
print("[Health Check] Warning: All checked models failed or timed out!")
|
| 459 |
+
|
| 460 |
+
async def periodic_health_check_loop():
|
| 461 |
+
# Wait 30 seconds after startup before the first check to let the space boot fully
|
| 462 |
+
await asyncio.sleep(30)
|
| 463 |
+
while True:
|
| 464 |
+
try:
|
| 465 |
+
await check_models_health()
|
| 466 |
+
except Exception as e:
|
| 467 |
+
print(f"[Health Check] Loop error: {e}")
|
| 468 |
+
await asyncio.sleep(300) # every 5 minutes
|
| 469 |
+
|
| 470 |
@app.on_event("startup")
|
| 471 |
async def startup():
|
| 472 |
await init_db()
|
| 473 |
Path(WORKSPACE_DIR).mkdir(parents=True, exist_ok=True)
|
| 474 |
+
# Start background health checking
|
| 475 |
+
asyncio.create_task(periodic_health_check_loop())
|
| 476 |
print(f"[Backend] Started. Workspace: {WORKSPACE_DIR}")
|
| 477 |
print(f"[Backend] Tool-capable models: {list(TOOL_CAPABLE_MODELS.keys())}")
|
| 478 |
print(f"[Backend] DB connected: {db_pool is not None}")
|
|
|
|
| 725 |
"workspace_exists": Path(WORKSPACE_DIR).exists(),
|
| 726 |
"db_connected": db_pool is not None,
|
| 727 |
"models_count": len(ALL_MODELS),
|
| 728 |
+
"recommended_model": RECOMMENDED_MODEL,
|
| 729 |
}
|
| 730 |
|
| 731 |
|