akhaliq HF Staff commited on
Commit
1b8ccaf
·
1 Parent(s): de1b9e6

compose_song: use visitor OAuth token for the LLM step

Browse files

- token: gr.OAuthToken param injected by gr.Workflow (excluded from canvas
ports); bills the signed-in visitor's own inference quota.
- Falls back to the Space's HF_TOKEN secret for anonymous/local runs, with a
clear gr.Error when neither is available.
- README: hf_oauth_scopes: [inference-api].

Files changed (2) hide show
  1. README.md +5 -1
  2. app.py +14 -4
README.md CHANGED
@@ -9,6 +9,8 @@ app_file: app.py
9
  pinned: true
10
  suggested_hardware: zero-a10g
11
  hf_oauth: true
 
 
12
  ---
13
 
14
  # MiniMax Music 3 — diffusers demo as a `gr.Workflow`
@@ -21,7 +23,9 @@ prompting guide) → `generate_song` (`@spaces.GPU` ZeroGPU worker: AR frames
21
  wav) → Output Song / Seed Used / Stats → `make_video` (CPU ffmpeg visualizer) → Share Video.**
22
 
23
  Seed, randomize-seed, flow-matching steps, guidance scale, duration, and video title are editable reference
24
- nodes. `hf_oauth: true` lets the owner edit the canvas; visitors can run the pipeline. The original Blocks app
 
 
25
  (with the live-streaming PCM player) is preserved as `app_blocks.py` — workflow fn nodes are plain callables,
26
  so chunk-by-chunk streaming is replaced by a final audio subject.
27
 
 
9
  pinned: true
10
  suggested_hardware: zero-a10g
11
  hf_oauth: true
12
+ hf_oauth_scopes:
13
+ - inference-api
14
  ---
15
 
16
  # MiniMax Music 3 — diffusers demo as a `gr.Workflow`
 
23
  wav) → Output Song / Seed Used / Stats → `make_video` (CPU ffmpeg visualizer) → Share Video.**
24
 
25
  Seed, randomize-seed, flow-matching steps, guidance scale, duration, and video title are editable reference
26
+ nodes. `hf_oauth: true` lets the owner edit the canvas; visitors sign in to run the pipeline, and the
27
+ `compose_song` LLM step uses the visitor's own OAuth token (`inference-api` scope), so inference bills the
28
+ visitor directly. Set an `HF_TOKEN` secret if you want anonymous/local runs to fall back to the owner's token. The original Blocks app
29
  (with the live-streaming PCM player) is preserved as `app_blocks.py` — workflow fn nodes are plain callables,
30
  so chunk-by-chunk streaming is replaced by a final audio subject.
31
 
app.py CHANGED
@@ -496,12 +496,20 @@ Given a sound instruction and/or lyrics, produce global_metadata, vocal_details
496
  Answer with ONLY a JSON object with keys: global_metadata, vocal_details, arrangement."""
497
 
498
 
499
- def _llm_json(system, user):
500
  import json as _json
501
 
502
  from openai import OpenAI
503
 
504
- client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=os.environ["HF_TOKEN"])
 
 
 
 
 
 
 
 
505
  completion = client.chat.completions.create(
506
  model="MiniMaxAI/MiniMax-M3:fastest",
507
  messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
@@ -512,10 +520,12 @@ def _llm_json(system, user):
512
  return _json.loads(text)
513
 
514
 
515
- def compose_song(description, duration):
 
 
516
  if not description.strip():
517
  raise gr.Error("Describe the song you want first.")
518
- data = _llm_json(_COMPOSER_SYSTEM, f"Song description: {description}\nTarget duration: {int(duration)} seconds.")
519
  return data["lyrics"], data["global_metadata"], data["vocal_details"], data["arrangement"]
520
 
521
 
 
496
  Answer with ONLY a JSON object with keys: global_metadata, vocal_details, arrangement."""
497
 
498
 
499
+ def _llm_json(system, user, token=None):
500
  import json as _json
501
 
502
  from openai import OpenAI
503
 
504
+ # Prefer the visitor's OAuth token (hf_oauth + inference-api scope bills the visitor
505
+ # directly); fall back to the Space owner's HF_TOKEN secret for local/anonymous runs.
506
+ api_key = getattr(token, "token", None) or os.environ.get("HF_TOKEN")
507
+ if not api_key:
508
+ raise gr.Error(
509
+ "Sign in with Hugging Face to compose (the LLM step bills your own account), "
510
+ "or ask the Space owner to add an HF_TOKEN secret."
511
+ )
512
+ client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=api_key)
513
  completion = client.chat.completions.create(
514
  model="MiniMaxAI/MiniMax-M3:fastest",
515
  messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
 
520
  return _json.loads(text)
521
 
522
 
523
+ def compose_song(description, duration, token: gr.OAuthToken = None):
524
+ # `token` is injected by gr.Workflow (OAuthToken params are excluded from the canvas
525
+ # ports and filled with the signed-in visitor's OAuth token at run time).
526
  if not description.strip():
527
  raise gr.Error("Describe the song you want first.")
528
+ data = _llm_json(_COMPOSER_SYSTEM, f"Song description: {description}\nTarget duration: {int(duration)} seconds.", token)
529
  return data["lyrics"], data["global_metadata"], data["vocal_details"], data["arrangement"]
530
 
531