Spaces:
Sleeping
Sleeping
unknown commited on
Commit ·
f871d2e
1
Parent(s): d754586
Route Nemotron through Modal
Browse files- modal_refine.py +44 -38
- src/paper2lab/inference/nemotron_refiner.py +43 -3
modal_refine.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
|
|
|
|
|
|
| 3 |
import modal
|
| 4 |
from fastapi import Request
|
| 5 |
|
|
@@ -8,50 +10,54 @@ app = modal.App("paper2lab-nemotron")
|
|
| 8 |
image = (
|
| 9 |
modal.Image.debian_slim(python_version="3.11")
|
| 10 |
.pip_install("requests", "fastapi")
|
| 11 |
-
.add_local_dir("src/paper2lab", remote_path="/root/paper2lab")
|
| 12 |
)
|
| 13 |
|
| 14 |
secret = modal.Secret.from_name("nvidia-api-key")
|
| 15 |
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
secrets=[secret],
|
| 20 |
-
timeout=300,
|
| 21 |
-
)
|
| 22 |
@modal.fastapi_endpoint(method="POST")
|
| 23 |
async def refine_remote(request: Request):
|
| 24 |
-
from paper2lab.inference.nemotron_refiner import refine_with_nemotron
|
| 25 |
-
|
| 26 |
body = await request.json()
|
| 27 |
|
| 28 |
-
|
| 29 |
-
model = body.get("model",
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
+
import os
|
| 4 |
+
import requests
|
| 5 |
import modal
|
| 6 |
from fastapi import Request
|
| 7 |
|
|
|
|
| 10 |
image = (
|
| 11 |
modal.Image.debian_slim(python_version="3.11")
|
| 12 |
.pip_install("requests", "fastapi")
|
|
|
|
| 13 |
)
|
| 14 |
|
| 15 |
secret = modal.Secret.from_name("nvidia-api-key")
|
| 16 |
|
| 17 |
+
NVIDIA_CHAT_URL = "https://integrate.api.nvidia.com/v1/chat/completions"
|
| 18 |
+
DEFAULT_MODEL = "nvidia/nemotron-3-nano-30b-a3b"
|
| 19 |
|
| 20 |
+
|
| 21 |
+
@app.function(image=image, secrets=[secret], timeout=300)
|
|
|
|
|
|
|
|
|
|
| 22 |
@modal.fastapi_endpoint(method="POST")
|
| 23 |
async def refine_remote(request: Request):
|
|
|
|
|
|
|
| 24 |
body = await request.json()
|
| 25 |
|
| 26 |
+
prompt = body.get("prompt")
|
| 27 |
+
model = body.get("model", DEFAULT_MODEL)
|
| 28 |
+
|
| 29 |
+
if not prompt:
|
| 30 |
+
return {"status": "error", "error": "Missing prompt"}
|
| 31 |
+
|
| 32 |
+
api_key = os.environ["NVIDIA_API_KEY"]
|
| 33 |
+
|
| 34 |
+
response = requests.post(
|
| 35 |
+
NVIDIA_CHAT_URL,
|
| 36 |
+
headers={
|
| 37 |
+
"Authorization": f"Bearer {api_key}",
|
| 38 |
+
"Content-Type": "application/json",
|
| 39 |
+
},
|
| 40 |
+
json={
|
| 41 |
+
"model": model,
|
| 42 |
+
"messages": [
|
| 43 |
+
{
|
| 44 |
+
"role": "system",
|
| 45 |
+
"content": "You are a precise scientific JSON refiner. Return only valid JSON. No markdown.",
|
| 46 |
+
},
|
| 47 |
+
{"role": "user", "content": prompt},
|
| 48 |
+
],
|
| 49 |
+
"temperature": 0.1,
|
| 50 |
+
"top_p": 0.7,
|
| 51 |
+
"max_tokens": 8192,
|
| 52 |
+
},
|
| 53 |
+
timeout=180,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
if not response.ok:
|
| 57 |
+
return {"status": "error", "error": response.text[:1000]}
|
| 58 |
+
|
| 59 |
+
data = response.json()
|
| 60 |
+
return {
|
| 61 |
+
"status": "ok",
|
| 62 |
+
"content": data["choices"][0]["message"]["content"],
|
| 63 |
+
}
|
src/paper2lab/inference/nemotron_refiner.py
CHANGED
|
@@ -445,6 +445,39 @@ def diff_cards(before: Dict[str, Any], after: Dict[str, Any]) -> Dict[str, Any]:
|
|
| 445 |
|
| 446 |
|
| 447 |
def _call_nvidia(prompt: str, model: str, timeout: int = 180) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
api_key = (
|
| 449 |
os.getenv("NVIDIA_API_KEY")
|
| 450 |
or os.getenv("NVIDIA_API_KEY".lower())
|
|
@@ -452,14 +485,19 @@ def _call_nvidia(prompt: str, model: str, timeout: int = 180) -> str:
|
|
| 452 |
)
|
| 453 |
|
| 454 |
if not api_key:
|
| 455 |
-
raise RuntimeError(
|
|
|
|
|
|
|
| 456 |
|
| 457 |
payload = {
|
| 458 |
"model": model,
|
| 459 |
"messages": [
|
| 460 |
{
|
| 461 |
"role": "system",
|
| 462 |
-
"content":
|
|
|
|
|
|
|
|
|
|
| 463 |
},
|
| 464 |
{
|
| 465 |
"role": "user",
|
|
@@ -491,7 +529,9 @@ def _call_nvidia(prompt: str, model: str, timeout: int = 180) -> str:
|
|
| 491 |
try:
|
| 492 |
return data["choices"][0]["message"]["content"]
|
| 493 |
except Exception as exc:
|
| 494 |
-
raise RuntimeError(
|
|
|
|
|
|
|
| 495 |
|
| 496 |
|
| 497 |
def refine_with_nemotron(
|
|
|
|
| 445 |
|
| 446 |
|
| 447 |
def _call_nvidia(prompt: str, model: str, timeout: int = 180) -> str:
|
| 448 |
+
modal_url = os.getenv("MODAL_REFINE_URL")
|
| 449 |
+
print("MODAL_REFINE_URL:", modal_url)
|
| 450 |
+
|
| 451 |
+
# =========================
|
| 452 |
+
# Use Modal if configured
|
| 453 |
+
# =========================
|
| 454 |
+
if modal_url:
|
| 455 |
+
response = requests.post(
|
| 456 |
+
modal_url,
|
| 457 |
+
json={
|
| 458 |
+
"prompt": prompt,
|
| 459 |
+
"model": model,
|
| 460 |
+
},
|
| 461 |
+
timeout=timeout,
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
if not response.ok:
|
| 465 |
+
raise RuntimeError(
|
| 466 |
+
f"Modal error {response.status_code}: {response.text[:1000]}"
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
data = response.json()
|
| 470 |
+
|
| 471 |
+
if data.get("status") != "ok":
|
| 472 |
+
raise RuntimeError(
|
| 473 |
+
f"Modal refinement failed: {data}"
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
return data["content"]
|
| 477 |
+
|
| 478 |
+
# =========================
|
| 479 |
+
# Fallback: Direct NVIDIA
|
| 480 |
+
# =========================
|
| 481 |
api_key = (
|
| 482 |
os.getenv("NVIDIA_API_KEY")
|
| 483 |
or os.getenv("NVIDIA_API_KEY".lower())
|
|
|
|
| 485 |
)
|
| 486 |
|
| 487 |
if not api_key:
|
| 488 |
+
raise RuntimeError(
|
| 489 |
+
"Missing NVIDIA_API_KEY. Set it in your environment before using refinement_mode='nemotron'."
|
| 490 |
+
)
|
| 491 |
|
| 492 |
payload = {
|
| 493 |
"model": model,
|
| 494 |
"messages": [
|
| 495 |
{
|
| 496 |
"role": "system",
|
| 497 |
+
"content": (
|
| 498 |
+
"You are a precise scientific JSON refiner. "
|
| 499 |
+
"Return only valid JSON. No markdown."
|
| 500 |
+
),
|
| 501 |
},
|
| 502 |
{
|
| 503 |
"role": "user",
|
|
|
|
| 529 |
try:
|
| 530 |
return data["choices"][0]["message"]["content"]
|
| 531 |
except Exception as exc:
|
| 532 |
+
raise RuntimeError(
|
| 533 |
+
f"Unexpected NVIDIA response: {data}"
|
| 534 |
+
) from exc
|
| 535 |
|
| 536 |
|
| 537 |
def refine_with_nemotron(
|