Any-to-Any
Transformers
Safetensors
multilingual
spev
feature-extraction
multimodal
text
audio
image
video
decision-model
choice
qwen3-omni
lora
custom-code
custom_code
Instructions to use SpragAI/SPEV with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use SpragAI/SPEV with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("SpragAI/SPEV", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download decision_utils.py from SpragAI/SPEV: direct link, hf CLI and curl.
- Browser
- Download file 3.87 kB
-
https://huggingface.co/SpragAI/SPEV/resolve/main/decision_utils.py
- Command line
-
hf download hf://SpragAI/SPEV/decision_utils.py
-
curl -L -o decision_utils.py https://huggingface.co/SpragAI/SPEV/resolve/main/decision_utils.py
3.87 kB
| """Question rendering, calibrated answers and audio preprocessing.""" | |
| import json | |
| import math | |
| from typing import Literal | |
| import numpy as np | |
| import torch | |
| from pydantic import BaseModel, ConfigDict, Field | |
| MARKER = "\n<spev_score>\n" | |
| class ChoiceQuestion(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| type: Literal["choice"] = "choice" | |
| instructions: str | |
| criteria: dict[str, str] = Field(min_length=2, max_length=255) | |
| class ChoiceQuestions(BaseModel): | |
| model_config = ConfigDict(extra="forbid", strict=True) | |
| questions: dict[str, ChoiceQuestion] = Field(min_length=1) | |
| def literal(value): | |
| return ( | |
| json.dumps(value, ensure_ascii=False) | |
| .replace("<", "\\u003c") | |
| .replace(">", "\\u003e") | |
| ) | |
| def render(state, questions): | |
| if not questions: | |
| raise ValueError("At least one question is required") | |
| schema = [] | |
| mapping = [] | |
| for key, question in questions.items(): | |
| criteria = question["criteria"] | |
| if not 2 <= len(criteria) <= 255: | |
| raise ValueError("Each question requires 2–255 criteria") | |
| schema.append( | |
| { | |
| "instructions": question["instructions"], | |
| "criteria": list(criteria.values()), | |
| } | |
| ) | |
| mapping.extend((key, name) for name in criteria) | |
| # IDs only route outputs. Descriptions carry the criterion semantics. | |
| text = "Use the audio and state as evidence for the supplied questions.\n" | |
| text += "State: " + literal(state) + "\nQuestions: " + literal(schema) + "\n" | |
| for question in questions.values(): | |
| for description in question["criteria"].values(): | |
| text += ( | |
| literal( | |
| {"question": question["instructions"], "criterion": description} | |
| ) | |
| + MARKER | |
| ) | |
| return text, mapping | |
| def locate_markers(ids, marker, expected): | |
| positions = [ | |
| i + len(marker) - 1 | |
| for i in range(len(ids) - len(marker) + 1) | |
| if ids[i : i + len(marker)] == marker | |
| ] | |
| if len(positions) != expected: | |
| raise ValueError("Scoring anchor count mismatch") | |
| return positions | |
| def answers(logits, mapping, temperature=1.0): | |
| if not math.isfinite(temperature) or temperature <= 0: | |
| raise ValueError("Temperature must be positive and finite") | |
| if not torch.isfinite(logits).all(): | |
| raise ValueError("Nonfinite logits") | |
| grouped = {} | |
| for i, (question, criterion) in enumerate(mapping): | |
| grouped.setdefault(question, []).append((criterion, i)) | |
| result = {} | |
| for key, options in grouped.items(): | |
| probabilities = ( | |
| (logits[[i for _, i in options]].double() / temperature).softmax(0).tolist() | |
| ) | |
| p = dict(zip([name for name, _ in options], probabilities)) | |
| choice = min(p, key=lambda name: (-p[name], name)) | |
| result[key] = { | |
| "choice": choice, | |
| "probabilities": p, | |
| "confidence": (p[choice] - 1 / len(p)) / (1 - 1 / len(p)), | |
| } | |
| return result | |
| def prepare_audio(audio, sample_rate): | |
| from scipy.signal import resample_poly | |
| wave = np.asarray(audio, dtype=np.float32) | |
| if wave.ndim == 2: | |
| wave = wave.mean(axis=1) | |
| if wave.ndim != 1 or not len(wave) or not np.isfinite(wave).all(): | |
| raise ValueError( | |
| "Audio must be finite mono samples or frames-by-channels samples" | |
| ) | |
| if ( | |
| not isinstance(sample_rate, int) | |
| or isinstance(sample_rate, bool) | |
| or sample_rate <= 0 | |
| ): | |
| raise ValueError("sample_rate must be a positive integer") | |
| if sample_rate != 16000: | |
| divisor = math.gcd(sample_rate, 16000) | |
| wave = resample_poly(wave, 16000 // divisor, sample_rate // divisor).astype( | |
| np.float32 | |
| ) | |
| return wave | |