mgoeckel commited on
Commit
2d82037
·
verified ·
1 Parent(s): 11191cb

zerogpu scaffold: shared GPU entry point + cpu fallback

Browse files
Files changed (2) hide show
  1. app.py +57 -14
  2. requirements.txt +1 -0
app.py CHANGED
@@ -1,15 +1,15 @@
1
  """Oscar-1 demo Space: typed decisions (choice / score / noul) with calibrated probabilities.
2
 
3
  Layout and demo patterns adapted from convaiinnovations/laya-demo (Apache-2.0).
4
- This Space runs on CPU: the Oscar-1 checkpoints are small non-autoregressive encoders
5
- (17m / 32m), so a CPU tier is genuinely enough — RTX-5060-Ti p50 latency is 2.5 ms.
6
  """
7
  import os
8
 
9
- DEVICE = os.environ.get("RL_AGENT_DEVICE", "cpu") # "cpu" (default) or "zerogpu"
10
  ZERO = False
11
  if DEVICE == "zerogpu":
12
- try:
13
  import spaces
14
  GPU = spaces.GPU(duration=30)
15
  os.environ["RL_AGENT_CUDA"] = "1"
@@ -23,17 +23,60 @@ if not ZERO:
23
  return fn
24
 
25
  import json # noqa: E402 (everything below may import torch)
 
26
  import time # noqa: E402
27
 
 
 
28
  import gradio as gr # noqa: E402
29
 
30
  import rl_agent_demo as D # noqa: E402
31
 
32
  # Download and build the weights while the app boots, not on someone's first click.
33
- D.warmup()
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  _GPU_BROKEN = {"why": None}
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  MODEL_KEYS = list(D.MODEL_REPOS)
38
  ANSWER_COLS = ["question", "answer", "confidence"]
39
 
@@ -63,48 +106,48 @@ NOTE = """> **Preview checkpoints: 17M and 32M parameters**, trained with RLCD o
63
 
64
 
65
  def run_triage(model_key, message, tier, threshold):
66
- rows, action, r = D.triage(model_key, message, tier, threshold)
67
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
68
 
69
 
70
  def run_email(model_key, sender, subject, body):
71
- rows, action, cleaned, r = D.email_triage(model_key, sender, subject, body)
72
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), cleaned, json.dumps(r, indent=2)
73
 
74
 
75
  def run_intents(model_key, message):
76
- rows, action, r = D.bank77(model_key, message)
77
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
78
 
79
 
80
  def run_classify(model_key, text):
81
- rows, action, r = D.classify(model_key, text)
82
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
83
 
84
 
85
  def run_guard(model_key, prompt, threshold):
86
- rows, action, r = D.guardrail(model_key, prompt, threshold)
87
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
88
 
89
 
90
  def run_rag(model_key, query, passages, threshold):
91
- table, summary = D.rag_filter(model_key, query, passages, threshold)
92
  return table, "**%s**" % summary
93
 
94
 
95
  def run_mod(model_key, post, threshold):
96
- rows, action, r = D.moderate(model_key, post, threshold)
97
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
98
 
99
 
100
  def run_router(model_key, request, small, large):
101
- rows, action, r = D.route_model(model_key, request, small, large)
102
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
103
 
104
 
105
  def run_playground(model_key, state_text, questions_text):
106
  try:
107
- rows, raw = D.playground(model_key, state_text, questions_text)
108
  return rows, raw
109
  except Exception as e:
110
  return [], "%s: %s" % (type(e).__name__, e)
 
1
  """Oscar-1 demo Space: typed decisions (choice / score / noul) with calibrated probabilities.
2
 
3
  Layout and demo patterns adapted from convaiinnovations/laya-demo (Apache-2.0).
4
+ ZeroGPU when available (premium quota), CPU fallback otherwise — the Oscar-1 checkpoints are
5
+ small non-autoregressive encoders (17m / 32m): RTX-5060-Ti p50 latency is 2.5 ms.
6
  """
7
  import os
8
 
9
+ DEVICE = os.environ.get("RL_AGENT_DEVICE", "zerogpu") # "zerogpu" (default) or "cpu"
10
  ZERO = False
11
  if DEVICE == "zerogpu":
12
+ try: # ZeroGPU attaches a GPU only while a decorated function runs
13
  import spaces
14
  GPU = spaces.GPU(duration=30)
15
  os.environ["RL_AGENT_CUDA"] = "1"
 
23
  return fn
24
 
25
  import json # noqa: E402 (everything below may import torch)
26
+ import sys # noqa: E402
27
  import time # noqa: E402
28
 
29
+ print("import order: spaces before torch =", "torch" not in sys.modules, flush=True)
30
+
31
  import gradio as gr # noqa: E402
32
 
33
  import rl_agent_demo as D # noqa: E402
34
 
35
  # Download and build the weights while the app boots, not on someone's first click.
36
+ for _k in D.MODEL_REPOS:
37
+ try:
38
+ D.get_agent(_k)
39
+ except Exception as _e:
40
+ print("pre-load %s failed: %s %s" % (_k, type(_e).__name__, _e), flush=True)
41
+
42
+
43
+ @GPU
44
+ def _gpu_call(fn, *args):
45
+ """The one ZeroGPU entry point every tab shares — a single decorated function means the
46
+ start-up warm-up covers all of them, instead of each tab paying its own first-call attach."""
47
+ return fn(*args)
48
+
49
 
50
  _GPU_BROKEN = {"why": None}
51
 
52
+
53
+ def gpu_call(fn, *args):
54
+ """Run `fn` on the GPU when one can be attached, otherwise on CPU.
55
+
56
+ ZeroGPU can fail to hand out a GPU — quota, capacity, or a torch/spaces version mismatch —
57
+ and it surfaces as `RuntimeError: No CUDA GPUs are available` raised inside worker_init.
58
+ The models run perfectly well on CPU, just slower, so fall back rather than fail,
59
+ and only say so once."""
60
+ t = time.perf_counter()
61
+ if ZERO and _GPU_BROKEN["why"] is None:
62
+ try:
63
+ out = _gpu_call(fn, *args)
64
+ print("%s: %.0f ms (gpu)" % (getattr(fn, "__name__", "call"),
65
+ (time.perf_counter() - t) * 1000), flush=True)
66
+ return out
67
+ except Exception as e:
68
+ _GPU_BROKEN["why"] = "%s: %s" % (type(e).__name__, str(e)[:160])
69
+ print("ZeroGPU unavailable (%s) - serving on CPU from here on" % _GPU_BROKEN["why"], flush=True)
70
+ os.environ.pop("RL_AGENT_CUDA", None)
71
+ t = time.perf_counter()
72
+ out = fn(*args)
73
+ print("%s: %.0f ms (cpu)" % (getattr(fn, "__name__", "call"),
74
+ (time.perf_counter() - t) * 1000), flush=True)
75
+ return out
76
+
77
+
78
+ D.warmup() # CPU warm-up: weights built at boot; ZeroGPU warm-up rides the first gpu_call
79
+
80
  MODEL_KEYS = list(D.MODEL_REPOS)
81
  ANSWER_COLS = ["question", "answer", "confidence"]
82
 
 
106
 
107
 
108
  def run_triage(model_key, message, tier, threshold):
109
+ rows, action, r = gpu_call(D.triage, model_key, message, tier, threshold)
110
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
111
 
112
 
113
  def run_email(model_key, sender, subject, body):
114
+ rows, action, cleaned, r = gpu_call(D.email_triage, model_key, sender, subject, body)
115
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), cleaned, json.dumps(r, indent=2)
116
 
117
 
118
  def run_intents(model_key, message):
119
+ rows, action, r = gpu_call(D.bank77, model_key, message)
120
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
121
 
122
 
123
  def run_classify(model_key, text):
124
+ rows, action, r = gpu_call(D.classify, model_key, text)
125
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
126
 
127
 
128
  def run_guard(model_key, prompt, threshold):
129
+ rows, action, r = gpu_call(D.guardrail, model_key, prompt, threshold)
130
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
131
 
132
 
133
  def run_rag(model_key, query, passages, threshold):
134
+ table, summary = gpu_call(D.rag_filter, model_key, query, passages, threshold)
135
  return table, "**%s**" % summary
136
 
137
 
138
  def run_mod(model_key, post, threshold):
139
+ rows, action, r = gpu_call(D.moderate, model_key, post, threshold)
140
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
141
 
142
 
143
  def run_router(model_key, request, small, large):
144
+ rows, action, r = gpu_call(D.route_model, model_key, request, small, large)
145
  return rows, "**%s** · %.0f ms" % (action, r["latency_ms"]), json.dumps(r, indent=2)
146
 
147
 
148
  def run_playground(model_key, state_text, questions_text):
149
  try:
150
+ rows, raw = gpu_call(D.playground, model_key, state_text, questions_text)
151
  return rows, raw
152
  except Exception as e:
153
  return [], "%s: %s" % (type(e).__name__, e)
requirements.txt CHANGED
@@ -4,3 +4,4 @@ transformers>=4.48,<5
4
  safetensors>=0.4
5
  huggingface_hub>=0.30
6
  numpy>=1.24
 
 
4
  safetensors>=0.4
5
  huggingface_hub>=0.30
6
  numpy>=1.24
7
+ spaces>=0.30