multimodalart HF Staff commited on
Commit
8e346cd
·
verified ·
1 Parent(s): 1ccbe3f

Pay for the conditioner call with the first ZeroGPU identity that can: the caller's token, this Space's HF_TOKEN, then none

Browse files
Files changed (2) hide show
  1. README.md +19 -16
  2. app.py +61 -43
README.md CHANGED
@@ -161,27 +161,30 @@ one-time `PIPE.to("cuda")` is inside the first row's 339 s and does not reappear
161
  ## Whose GPU quota pays
162
 
163
  Two cards are booked per request: this Space's denoise loop and the conditioner's forward. ZeroGPU attributes a
164
- booking to the `X-IP-Token` header of the request that triggered it, so this Space forwards the caller's token to the
165
- conditioner (`gradio_client.Client(..., headers={"X-IP-Token": ...})`, off the `gr.Request` gradio injects — the UI
166
- path and the `/generate` API path alike). When ZeroGPU honours it, one user's request bills as one request across both
167
- halves and no org token is spent on it.
168
 
169
- It is best effort, though: ZeroGPU answers `401` for a proxy token it will not honour, which `spaces` surfaces as
170
- `Expired ZeroGPU proxy token`, and a token minted for one Space arriving at another is exactly that case on the API
171
- path. So a refused token falls back to calling the conditioner with no token at all, on an IP-based quota.
 
 
 
 
172
 
173
- That fallback is a working path rather than a degraded one, because the conditioner is sized for it. An unattributed
174
- caller may book at most 120 credits at a time and an `xlarge` booking costs **twice** its seconds, i.e. 60 s — so the
175
- conditioner books the encode (45 s) and a prompt upsample (60 s) as **two separate calls**, each under the ceiling,
176
- where one combined booking would be refused outright.
 
 
 
177
 
178
  ## Secrets
179
 
180
- Nothing this Space loads is private any more: the weights are the public
181
- [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) checkpoint, the compiled AoTI packages are the
182
- public [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti) model repo, and the
183
- conditioner is a public Space called without a token so that round trip runs on the caller's own quota rather than
184
- this org's. No `HF_TOKEN` is required.
185
 
186
  ## Where diffusers comes from
187
 
 
161
  ## Whose GPU quota pays
162
 
163
  Two cards are booked per request: this Space's denoise loop and the conditioner's forward. ZeroGPU attributes a
164
+ booking to the identity of the request that triggered it, so the conditioner call tries three in order:
 
 
 
165
 
166
+ 1. **the caller's own `X-IP-Token`**, forwarded off the `gr.Request` gradio injects (the UI path and the `/generate`
167
+ API path alike). The request then bills as one request across both halves and costs this org nothing. Best effort:
168
+ ZeroGPU answers `401` for a proxy token it will not honour which is what a token minted for *this* Space looks
169
+ like arriving at another one — and `spaces` surfaces that as `Expired ZeroGPU proxy token`.
170
+ 2. **this Space's `HF_TOKEN`**, which charges the account that owns the Space and has a real quota. This is what
171
+ carries the Space in practice.
172
+ 3. **no token**, an IP-based free quota shared by everything calling out of this Space's egress IP. A last resort.
173
 
174
+ Any of ZeroGPU's "this identity cannot pay" answers a refused proxy token, a duration past what the identity may
175
+ book, an exhausted quota moves on to the next identity instead of failing the request, and the log line
176
+ `conditioner call paid for by ...` records which one paid.
177
+
178
+ The conditioner is sized so that even (3) is legal: an unattributed caller may book at most 120 credits at a time and
179
+ an `xlarge` booking costs **twice** its seconds, so the conditioner books the encode (45 s) and a prompt upsample
180
+ (60 s) as **two separate calls**, where one combined booking would be refused outright.
181
 
182
  ## Secrets
183
 
184
+ `HF_TOKEN` used for exactly one thing: paying for the conditioner call when the caller's own ZeroGPU token cannot
185
+ (see above). Everything this Space downloads is public: the [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3)
186
+ checkpoint and the [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti) packages.
187
+ Without it, a call the caller cannot pay for falls back to a shared IP quota worth a couple of requests a day.
 
188
 
189
  ## Where diffusers comes from
190
 
app.py CHANGED
@@ -24,6 +24,9 @@ ATTENTION = os.environ.get("H3_ATTENTION", "_native_cudnn").lower()
24
  GPU_DURATION = int(os.environ.get("H3_GPU_DURATION", "900"))
25
  GPU_SIZE = os.environ.get("H3_GPU_SIZE", "xlarge")
26
  ON_SPACES = bool(os.environ.get("SPACE_ID"))
 
 
 
27
 
28
  CANVASES = {
29
  # 16:9
@@ -65,7 +68,9 @@ PIPE = None
65
  MANAGER = None
66
  LOAD_ERROR: str | None = None
67
  LOADED_IN: float | None = None
68
- CLIENTS: dict[str | None, object] = {}
 
 
69
 
70
 
71
  def status() -> str:
@@ -170,33 +175,70 @@ def _arm_decode_hooks(pipe):
170
  module.decode = armed
171
 
172
 
173
- def conditioner(ip_token: str | None = None):
174
- """The other half, over the gradio API booked against *the caller's* ZeroGPU quota, not this org's.
175
-
176
- ZeroGPU attributes a booking to the `X-IP-Token` header of the request that triggered it
177
- (`spaces/zero/client.py`), which the Spaces router puts on every browser request. That header is what pays for
178
- this Space's own `@spaces.GPU` call, and forwarding it to the conditioner makes the same identity pay for the
179
- conditioner's — the two halves of one user's request then bill as one request, the way they would if this were a
180
- single Space.
181
 
182
- Without it the conditioner falls back to an IP-based quota, whose ceiling is low enough that an `xlarge` booking
183
- is refused outright ("The requested GPU duration (Ns) is larger than the maximum allowed"), so a call that does
184
- not forward a token only works because the conditioner keeps its own reservation small.
185
-
186
- Cached per token: building a `Client` costs a round trip to the Space config, and a token is per user session.
187
  """
188
  from gradio_client import Client
189
 
190
- if ip_token in CLIENTS:
191
- return CLIENTS[ip_token]
192
- # No org token: the request runs on the caller side quota, which is the point of forwarding theirs.
193
- client = Client(CONDITIONER_SPACE, headers={"X-IP-Token": ip_token} if ip_token else None)
 
 
 
 
194
  if len(CLIENTS) >= 32:
195
  CLIENTS.pop(next(iter(CLIENTS)))
196
- CLIENTS[ip_token] = client
197
  return client
198
 
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  def ip_token_of(request) -> str | None:
201
  """The caller's ZeroGPU identity, as the Spaces router put it on this request.
202
 
@@ -210,30 +252,6 @@ def ip_token_of(request) -> str | None:
210
  return token
211
 
212
 
213
- LOG_TAG = "gen"
214
-
215
-
216
- def call_conditioner(ip_token, **arguments):
217
- """One conditioner call, on the caller's ZeroGPU identity when that is accepted and anonymously when it is not.
218
-
219
- Forwarding is best effort. ZeroGPU's `/schedule` answers `401` for a proxy token it will not honour — which is
220
- what a token minted for *this* Space looks like when it arrives at the conditioner — and `spaces` surfaces that as
221
- `Expired ZeroGPU proxy token`. So the forwarded call is tried first, and a rejected token falls back to no token
222
- at all rather than failing the request. The conditioner's own bookings are sized to fit the unattributed ceiling,
223
- so the fallback is a working path and not a degraded one.
224
- """
225
- api_name = arguments.pop("api_name")
226
- if ip_token is not None:
227
- try:
228
- return conditioner(ip_token).predict(**arguments, api_name=api_name)
229
- except Exception as error:
230
- if "proxy token" not in str(error):
231
- raise
232
- print(f"[{LOG_TAG}] the forwarded ZeroGPU token was refused ({error}); retrying anonymously", flush=True)
233
- CLIENTS.pop(ip_token, None)
234
- return conditioner(None).predict(**arguments, api_name=api_name)
235
-
236
-
237
  def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, rewrite_prompt=False, ip_token=None):
238
  """Ask the conditioner Space for `prompt_embeds` + `text_token_tags`. Off this Space's GPU time entirely.
239
 
 
24
  GPU_DURATION = int(os.environ.get("H3_GPU_DURATION", "900"))
25
  GPU_SIZE = os.environ.get("H3_GPU_SIZE", "xlarge")
26
  ON_SPACES = bool(os.environ.get("SPACE_ID"))
27
+ # Used for one thing only: paying for the conditioner call when the caller's own token cannot. Everything
28
+ # this Space downloads — the checkpoint and the AoTI packages — is public.
29
+ HF_TOKEN = os.environ.get("HF_TOKEN")
30
 
31
  CANVASES = {
32
  # 16:9
 
68
  MANAGER = None
69
  LOAD_ERROR: str | None = None
70
  LOADED_IN: float | None = None
71
+ LOG_TAG = "gen"
72
+ # One `gradio_client.Client` per ZeroGPU identity the conditioner is called with; see `call_conditioner`.
73
+ CLIENTS: dict[tuple[str | None, str | None], object] = {}
74
 
75
 
76
  def status() -> str:
 
175
  module.decode = armed
176
 
177
 
178
+ def conditioner(ip_token: str | None = None, hf_token: str | None = None):
179
+ """A client for the other half, bound to one ZeroGPU identity.
 
 
 
 
 
 
180
 
181
+ Cached per identity: building a `Client` costs a round trip to the Space config, and a forwarded token is per
182
+ user session.
 
 
 
183
  """
184
  from gradio_client import Client
185
 
186
+ key = (ip_token, hf_token)
187
+ if key in CLIENTS:
188
+ return CLIENTS[key]
189
+ client = Client(
190
+ CONDITIONER_SPACE,
191
+ token=hf_token,
192
+ headers={"X-IP-Token": ip_token} if ip_token else None,
193
+ )
194
  if len(CLIENTS) >= 32:
195
  CLIENTS.pop(next(iter(CLIENTS)))
196
+ CLIENTS[key] = client
197
  return client
198
 
199
 
200
+ # What ZeroGPU says when an identity cannot pay for the booking, in any of its forms: a proxy token it will not
201
+ # honour (`401`, surfaced as "Expired ZeroGPU proxy token"), a duration past what that identity may book, and an
202
+ # exhausted quota. All three mean "try the next identity" rather than "fail the request".
203
+ _UNPAYABLE = ("proxy token", "ZeroGPU quota", "larger than the maximum allowed", "GPU limit")
204
+
205
+
206
+ def call_conditioner(ip_token, **arguments):
207
+ """One conditioner call, against the first ZeroGPU identity that can pay for it.
208
+
209
+ In order of preference:
210
+
211
+ 1. **the caller's own forwarded `X-IP-Token`** — the request bills as one request across both halves and costs
212
+ this org nothing. Best effort: a proxy token minted for this Space is not necessarily honoured when it
213
+ arrives at another one, and ZeroGPU answers `401` when it is not.
214
+ 2. **this Space's `HF_TOKEN`** — the booking is charged to the account that owns the Space, which has a real
215
+ quota. This is what carries the Space in practice.
216
+ 3. **no token at all** — an IP-based free quota, shared by everything calling out of this Space's egress IP and
217
+ worth a couple of requests a day. A last resort, not a design.
218
+
219
+ The conditioner's own bookings are sized to fit even (3): it books an encode and a prompt upsample as two calls
220
+ of 45 s and 60 s, because an unattributed caller may book at most 60 s of `xlarge` at a time.
221
+ """
222
+ api_name = arguments.pop("api_name")
223
+ attempts = []
224
+ if ip_token:
225
+ attempts.append(("the caller's forwarded ZeroGPU token", {"ip_token": ip_token}))
226
+ if HF_TOKEN:
227
+ attempts.append(("this Space's HF_TOKEN", {"hf_token": HF_TOKEN}))
228
+ attempts.append(("no token, on an IP quota", {}))
229
+
230
+ for index, (label, identity) in enumerate(attempts):
231
+ try:
232
+ result = conditioner(**identity).predict(**arguments, api_name=api_name)
233
+ print(f"[{LOG_TAG}] conditioner call paid for by {label}", flush=True)
234
+ return result
235
+ except Exception as error:
236
+ if index == len(attempts) - 1 or not any(reason in str(error) for reason in _UNPAYABLE):
237
+ raise
238
+ print(f"[{LOG_TAG}] {label}: {error}; trying the next identity", flush=True)
239
+ CLIENTS.pop((identity.get("ip_token"), identity.get("hf_token")), None)
240
+
241
+
242
  def ip_token_of(request) -> str | None:
243
  """The caller's ZeroGPU identity, as the Spaces router put it on this request.
244
 
 
252
  return token
253
 
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  def encode_remote(prompt, image_path, last_image_path, canvas, num_frames, rewrite_prompt=False, ip_token=None):
256
  """Ask the conditioner Space for `prompt_embeds` + `text_token_tags`. Off this Space's GPU time entirely.
257