fffiloni commited on
Commit
0d18983
·
verified ·
1 Parent(s): 034cf37

Upload 6 files

Browse files
Files changed (2) hide show
  1. CHANGELOG.md +7 -0
  2. app.py +36 -0
CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
 
 
 
 
 
 
 
1
  ## v47 — Simple spec reset
2
 
3
  - Reset the custom UI to three top-level tabs only: Build, Runs, and Space Test.
 
1
+ ## v100 — Compact storage and billing cockpit
2
+
3
+ - Reworked the left-column Run storage block into a compact status card with clearer checking/creating/ready feedback.
4
+ - Added a Billing & compute card below New Build showing account, payment-readiness signal, inference-credit tier, and links to Hugging Face Billing, Jobs pricing, and Inference Providers pricing.
5
+ - Added `/api/billing/status` to expose only the billing signals available through OAuth, without pretending to mirror numeric usage totals from the HF Billing dashboard.
6
+ - Kept the build and validation workflows unchanged.
7
+
8
  ## v47 — Simple spec reset
9
 
10
  - Reset the custom UI to three top-level tabs only: Build, Runs, and Space Test.
app.py CHANGED
@@ -258,6 +258,40 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
258
  ctx = extract_oauth_context(request)
259
  return JSONResponse({"user": public_oauth_context(ctx), "token_identity": verify_token_identity(ctx)})
260
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  @fastapi_app.get("/api/bucket/status")
262
  async def api_bucket_status(request: Request, bucket_name: str = settings.bucket_name): # type: ignore[no-untyped-def]
263
  ctx = _oauth_context_from_request(request)
@@ -548,8 +582,10 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
548
  "job_logs_polled": bool(include_job_logs),
549
  "inference_gate": bundle.get("inference_gate") or {},
550
  "generation_smoke": bundle.get("generation_smoke") or {},
 
551
  "hardware_strategy": bundle.get("hardware_strategy") or {},
552
  "technical_blockers": bundle.get("technical_blockers") or {},
 
553
  "events": events[-20:],
554
  "view": view,
555
  "links": view.get("links") or _api_links(
 
258
  ctx = extract_oauth_context(request)
259
  return JSONResponse({"user": public_oauth_context(ctx), "token_identity": verify_token_identity(ctx)})
260
 
261
+ @fastapi_app.get("/api/billing/status")
262
+ async def api_billing_status(request: Request): # type: ignore[no-untyped-def]
263
+ """Return the billing signals available through HF OAuth.
264
+
265
+ Hugging Face's public docs describe the billing dashboard as the place
266
+ to monitor compute usage. The OAuth `read-billing` scope exposes whether
267
+ the account can pay, but this app should not pretend it can mirror the
268
+ dashboard's numeric usage totals.
269
+ """
270
+ ctx = _oauth_context_from_request(request)
271
+ scopes = set(ctx.get("scopes", []))
272
+ missing = set(ctx.get("missing_scopes", []))
273
+ can_read_billing = "read-billing" in scopes and "read-billing" not in missing
274
+ return JSONResponse(
275
+ {
276
+ "username": ctx["username"],
277
+ "profile": {
278
+ "is_pro": ctx.get("is_pro"),
279
+ "can_pay": ctx.get("can_pay"),
280
+ },
281
+ "can_read_billing": can_read_billing,
282
+ "can_pay": ctx.get("can_pay"),
283
+ "is_pro": ctx.get("is_pro"),
284
+ "missing_scopes": sorted(missing),
285
+ "links": {
286
+ "billing": "https://huggingface.co/settings/billing",
287
+ "jobs_pricing": "https://huggingface.co/docs/hub/jobs-pricing",
288
+ "inference_pricing": "https://huggingface.co/docs/inference-providers/pricing",
289
+ },
290
+ "usage_totals_available": False,
291
+ "note": "Compute usage totals are available in the Hugging Face Billing dashboard; this API only returns OAuth billing-readiness signals.",
292
+ }
293
+ )
294
+
295
  @fastapi_app.get("/api/bucket/status")
296
  async def api_bucket_status(request: Request, bucket_name: str = settings.bucket_name): # type: ignore[no-untyped-def]
297
  ctx = _oauth_context_from_request(request)
 
582
  "job_logs_polled": bool(include_job_logs),
583
  "inference_gate": bundle.get("inference_gate") or {},
584
  "generation_smoke": bundle.get("generation_smoke") or {},
585
+ "api_schema": bundle.get("api_schema") or {},
586
  "hardware_strategy": bundle.get("hardware_strategy") or {},
587
  "technical_blockers": bundle.get("technical_blockers") or {},
588
+ "run_documents": bundle.get("run_documents") or [],
589
  "events": events[-20:],
590
  "view": view,
591
  "links": view.get("links") or _api_links(