inter2-audio-playground / examples /run_inter2_audio.py
abargum's picture
Inter-2 Audio playground
f821a4a
Raw History Blame Contribute Delete
4.36 kB
"""Run audio clips through the Inter-2 Audio playground's /generate API with one of its task presets.
Emotion one primary emotion out of seven; free output by default (--constrain for strict JSON)
Combined engagement, social signals, emotion and delivery in one answer; constrained by default
The prompts and each task's schema default come from the Space's own tasks.py, so a run sends
exactly what the UI sends. One JSON line per clip is appended to --output; re-running skips
clips already there, so a run that hits your daily ZeroGPU quota can be resumed.
Usage (from a download of this Space's files; HF_TOKEN is your Hugging Face token):
pip install gradio_client huggingface_hub # in a fresh virtual environment
HF_TOKEN=hf_... python examples/run_inter2_audio.py --task Combined --output combined.jsonl clips/ one.wav
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from pathlib import Path
from gradio_client import Client, handle_file
sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # the Space folder, for tasks.py
from tasks import TASKS # noqa: E402
SPACE = "Interhuman/inter2-audio-playground"
AUDIO_SUFFIXES = {".wav", ".mp3", ".flac", ".ogg", ".m4a"}
def audio_files(paths: list[str]) -> list[Path]:
files: list[Path] = []
for raw in paths:
path = Path(raw)
if path.is_dir():
files += sorted(p for p in path.rglob("*") if p.suffix.lower() in AUDIO_SUFFIXES)
elif path.is_file():
files.append(path)
else:
raise SystemExit(f"Not found: {path}")
return files
def one_line(task: str, parsed: dict | None) -> str:
"""A short console summary of the parsed answer."""
if not isinstance(parsed, dict):
return "(unparsed)"
if task == "Emotion":
return f"{parsed.get('primary_emotion', '?')} ({parsed.get('confidence', '?')})"
emotion = (parsed.get("emotions") or [{}])[0]
signals = ", ".join(f"{s.get('name')} ({s.get('probability')})" for s in parsed.get("signals") or []) or "none"
return (
f"{parsed.get('engagement_status', '?')} | emotion {emotion.get('name', '?')} | "
f"delivery {parsed.get('delivery', '?')} | signals {signals}"
)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("audio", nargs="+", help="audio files and/or folders (searched recursively)")
parser.add_argument("--task", choices=list(TASKS), default="Emotion")
parser.add_argument("--output", required=True, help="JSONL file; existing rows are kept and skipped")
parser.add_argument(
"--constrain",
action=argparse.BooleanOptionalAction,
default=None,
help="force the task's JSON schema (default: the task's own default, as in the UI)",
)
parser.add_argument("--max-new-tokens", type=int, default=768)
parser.add_argument("--space", default=SPACE)
args = parser.parse_args()
task = TASKS[args.task]
constrain = task.constrain if args.constrain is None else args.constrain
out = Path(args.output)
done = {json.loads(line)["file"] for line in out.read_text().splitlines()} if out.exists() else set()
files = [f for f in audio_files(args.audio) if str(f) not in done]
print(f"{args.task} (constrain={constrain}): {len(done)} clips already done, {len(files)} to run", flush=True)
client = Client(args.space, token=os.getenv("HF_TOKEN"), verbose=False)
with out.open("a", encoding="utf-8") as f:
for i, path in enumerate(files, 1):
start = time.time()
_, parsed_json, raw = client.predict(
handle_file(str(path)), task.system_prompt, task.user_prompt, True, constrain,
args.max_new_tokens, 0.0, 1.0, 1.0, args.task,
api_name="/generate",
) # fmt: skip
parsed = json.loads(parsed_json) if parsed_json else None
record = {"file": str(path), "task": args.task, "constrained": constrain, "parsed": parsed}
f.write(json.dumps({**record, "raw_output": raw}, ensure_ascii=False) + "\n")
f.flush()
print(f"[{i}/{len(files)}] {path.name}: {one_line(args.task, parsed)} ({time.time() - start:.1f}s)")
if __name__ == "__main__":
main()