Spaces:
Running on Zero
Running on Zero
File size: 6,370 Bytes
f821a4a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """Pieces of the Inter-2-Audio playground: audio, chat layout, parsing, the result summary.
Kept apart from app.py (which loads the model and the UI) so they can be tested
without a GPU. The task presets (prompts + JSON schemas) live in tasks.py.
Decoding defaults: audio before text, greedy, repetition penalty 1.0.
"""
from __future__ import annotations
import ast
import html
import json
import re
from typing import Any
import numpy as np
TARGET_SR = 16_000
ENCODER_WINDOW_S = 30.0
DEFAULT_REPETITION_PENALTY = 1.0
DEFAULT_MAX_NEW_TOKENS = 768
def build_messages(user_prompt: str, system_prompt: str = "", audio_first: bool = True) -> list[dict[str, Any]]:
"""One user turn holding the audio placeholder and the prompt, plus a system turn only if given."""
text = {"type": "text", "text": user_prompt}
audio = {"type": "audio", "audio": "clip"} # placeholder; the waveform goes to the processor separately
messages = [{"role": "system", "content": [{"type": "text", "text": system_prompt}]}] if system_prompt else []
return messages + [{"role": "user", "content": [audio, text] if audio_first else [text, audio]}]
class AudioInputError(ValueError):
"""A problem with the uploaded audio; its message is safe to show to the user."""
def load_audio(path: str, truncate: bool = True) -> tuple[np.ndarray, float]:
"""Mono float32 at 16 kHz, cut to the encoder window. Returns (waveform, original duration in s)."""
import librosa
try:
wav, _ = librosa.load(path, sr=TARGET_SR, mono=True)
except Exception as exc:
raise AudioInputError("Could not read this audio file. Try a WAV, MP3 or FLAC file.") from exc
duration = len(wav) / TARGET_SR
if duration == 0:
raise AudioInputError("The audio file is empty.")
if duration > ENCODER_WINDOW_S + 1e-3:
if not truncate:
raise AudioInputError(f"The clip is {duration:.1f}s; the model hears at most {ENCODER_WINDOW_S:.0f}s.")
wav = wav[: int(ENCODER_WINDOW_S * TARGET_SR)]
return wav.astype(np.float32), duration
def parse_json(text: str) -> Any | None:
"""The model's JSON answer, tolerating a code fence, surrounding prose or Python-style literals."""
text = text.strip()
fence = re.match(r"```(?:json)?\s*(.*?)\s*```", text, re.DOTALL)
if fence:
text = fence.group(1).strip()
match = re.search(r"[\[{].*[\]}]", text, re.DOTALL)
for candidate in (text, match.group(0) if match else None):
if candidate is None:
continue
try:
return json.loads(candidate)
except json.JSONDecodeError:
pass
try:
return ast.literal_eval(candidate)
except (ValueError, SyntaxError):
pass
return None
def _esc(value: Any) -> str:
return html.escape(str(value))
def _badge(level: Any) -> str:
if not level:
return ""
return f'<span class="ih-badge ih-badge-{_esc(level)}">{_esc(level)}</span>'
def _tile(caption: str, value: Any, level: Any = None) -> str:
return (
f'<div class="ih-tile"><div class="ih-caption">{caption}</div>'
f'<div class="ih-value">{_esc(value)}{_badge(level)}</div></div>'
)
def _primary_emotion(payload: dict) -> str:
confidence = payload.get("confidence")
meta = f"Confidence {float(confidence):.2f}" if isinstance(confidence, (int, float)) else ""
evidence = f'<p class="ih-note">{_esc(payload["evidence"])}</p>' if payload.get("evidence") else ""
meta_html = f'<div class="ih-meta">{meta}</div>' if meta else ""
return (
f'<div class="ih-caption">Primary emotion</div>'
f'<div class="ih-headline">{_esc(payload["primary_emotion"])}</div>{meta_html}{evidence}'
)
def _combined(payload: dict) -> str:
"""Tiles for the single-label fields, the emotion's rationale, then one row per social signal."""
emotion = next((e for e in payload.get("emotions") or [] if isinstance(e, dict)), None)
tiles = []
if emotion:
tiles.append(_tile("Emotion", emotion.get("name", "?"), emotion.get("probability")))
if "engagement_status" in payload:
tiles.append(_tile("Engagement", payload["engagement_status"]))
if "delivery" in payload:
tiles.append(_tile("Delivery", payload["delivery"]))
out = f'<div class="ih-tiles">{"".join(tiles)}</div>' if tiles else ""
if emotion and emotion.get("rationale"):
out += f'<p class="ih-note">{_esc(emotion["rationale"])}</p>'
if "signals" in payload:
rows = [
f'<div class="ih-signal"><div class="ih-signal-head">{_esc(sig.get("name", "?"))}'
f"{_badge(sig.get('probability'))}</div>"
+ (f'<div class="ih-note">{_esc(sig["rationale"])}</div>' if sig.get("rationale") else "")
+ "</div>"
for sig in payload["signals"] or []
if isinstance(sig, dict)
]
body = "".join(rows) or '<div class="ih-note">None evidenced.</div>'
out += f'<div class="ih-caption ih-section">Social signals</div>{body}'
return out
def format_summary(payload: Any, truncated_from: float | None, hit_token_limit: bool = False) -> str:
"""The result card as HTML: the known fields laid out, otherwise the top-level keys of whatever came back.
Everything from the model is HTML-escaped."""
notes = []
if truncated_from is not None:
notes.append(f"Clip was {truncated_from:.1f}s; only the first {ENCODER_WINDOW_S:.0f}s were used.")
if hit_token_limit:
notes.append("Hit the max-new-tokens limit; the output may be cut off.")
if payload is None:
body = '<p class="ih-note">Could not parse JSON from the output. See the raw text.</p>'
elif isinstance(payload, dict) and "primary_emotion" in payload:
body = _primary_emotion(payload)
elif isinstance(payload, dict) and payload.keys() & {"emotions", "engagement_status", "signals", "delivery"}:
body = _combined(payload)
elif isinstance(payload, dict):
body = '<p class="ih-note">Parsed JSON with keys: ' + ", ".join(_esc(k) for k in payload) + "</p>"
else:
body = f'<p class="ih-note">Parsed JSON ({type(payload).__name__}).</p>'
notes_html = "".join(f'<p class="ih-warning">{n}</p>' for n in notes)
return f'<div class="ih-card">{notes_html}{body}</div>'
|