unknown commited on
Commit
f871d2e
·
1 Parent(s): d754586

Route Nemotron through Modal

Browse files
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
- @app.function(
18
- image=image,
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
- llm_evidence_pack = body.get("llm_evidence_pack")
29
- model = body.get("model", "nvidia/nemotron-3-nano-30b-a3b")
30
- return_comparison = body.get("return_comparison", True)
31
-
32
- if not llm_evidence_pack:
33
- return {
34
- "status": "error",
35
- "error": "Missing llm_evidence_pack",
36
- }
37
-
38
- try:
39
- result = refine_with_nemotron(
40
- llm_evidence_pack=llm_evidence_pack,
41
- model=model,
42
- return_comparison=return_comparison,
43
- )
44
- return {
45
- "status": "ok",
46
- "result": result,
47
- }
48
- except Exception as exc:
49
- return {
50
- "status": "error",
51
- "error": str(exc),
52
- }
53
-
54
-
55
- @app.local_entrypoint()
56
- def main():
57
- print("Deploy this app with: modal deploy modal_refine.py")
 
 
 
 
 
 
 
 
 
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("Missing NVIDIA_API_KEY. Set it in your environment before using refinement_mode='nemotron'.")
 
 
456
 
457
  payload = {
458
  "model": model,
459
  "messages": [
460
  {
461
  "role": "system",
462
- "content": "You are a precise scientific JSON refiner. Return only valid JSON. No markdown.",
 
 
 
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(f"Unexpected NVIDIA response: {data}") from exc
 
 
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(