Spaces:
Running on Zero
Running on Zero
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].
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
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|