Skip to navigation

Python quickstart

Install the Python SDK, ask four questions about a recording and read the answers.

Ask questions about a recording from Python. The script below asks one question of each primitive about the same audio and prints every answer.

Packagehume-audio-decisions on PyPI
Sourcehume-audio-decisions-python-sdk on GitHub
Python3.10 or later

Prerequisites

  1. Python 3.10 or later.
  2. An API key from the API keys page of the Hume Platform. See Authentication.
  3. A recording of speech 5 seconds to 5 minutes long, in MP3 or WAV.

Install

Create a project and install the SDK in it. To add the SDK to an existing project, run only the last command.

uv
mkdir decisions-quickstart
cd decisions-quickstart
uv init
uv add hume-audio-decisions

Set your API key

The client reads the key from the HUME_API_KEY environment variable and sends it in the X-Hume-Api-Key header of every request.

Shell
export HUME_API_KEY=your_api_key

To pass the key explicitly instead, construct the client with DecisionsClient(api_key="...").

Ask questions about a recording

  1. Create the client

    Create ask_questions.py. It asks four questions about speech.wav in one request and prints each answer.

    1. DecisionsClient() reads your API key from HUME_API_KEY. The script imports a question class and an answer class for each primitive, and ApiError for failed requests.

  2. Write the questions

    1. Each question is keyed by an ID you choose, and its class names its primitive. noul asks yes or no, choice picks from unordered options, score places the audio on ordered levels, and span finds when an event occurs. To describe the recording, such as the setting or who is speaking, pass context to DecisionsRequest alongside questions. Primitives covers what each question takes.

  3. Send the recording

    1. client.decisions.create() sends the questions and the audio as a multipart/form-data request and returns the parsed response. The tuple gives the file part a filename and a content type. To send an MP3 file, pass audio/mpeg.

  4. Handle errors

    1. A failed request raises an ApiError. status_code holds the HTTP status, and body holds the error body, whose code says what went wrong and whose param says where in the request it was found. See Errors.

  5. Read the answers

    1. The response returns each answer under its question’s ID, and the run_id of the model run that answered them. A noul answer is the probability of yes, choice and score answers give the probability of each option or level, and a span answer lists the time spans where the event may occur.

from hume_audio_decisions import (
Answer_Choice,
Answer_Noul,
Answer_Score,
Answer_Span,
Criterion,
DecisionsClient,
DecisionsRequest,
Question_Choice,
Question_Noul,
Question_Score,
Question_Span,
)
from hume_audio_decisions.core import ApiError
client = DecisionsClient()
request = DecisionsRequest(
questions={
"hesitant": Question_Noul(
instructions="Does the speaker sound hesitant?",
),
"tone": Question_Choice(
instructions="Which best describes the speaker's tone?",
criteria=[
Criterion(name="warm"),
Criterion(name="neutral"),
Criterion(name="curt"),
],
),
"naturalness": Question_Score(
instructions="How natural does the voice sound?",
criteria=["Not at all", "2", "3", "4", "Extremely"],
),
"vocal_bursts": Question_Span(
instructions="When does the speaker laugh, sigh or make another vocal burst?",
),
},
)
try:
with open("speech.wav", "rb") as audio:
response = client.decisions.create(
file=("speech.wav", audio, "audio/wav"),
request=request,
)
except ApiError as error:
print(f"{error.status_code}: {error.body}")
else:
for question_id, answer in response.answers.items():
if isinstance(answer, Answer_Noul):
print(f"{question_id}: yes {answer.noul:.2f}")
elif isinstance(answer, Answer_Choice):
print(
f"{question_id}: {answer.choice}, "
f"confidence {answer.confidence:.2f}"
)
elif isinstance(answer, Answer_Score):
level = max(
answer.probabilities,
key=answer.probabilities.__getitem__,
)
print(
f"{question_id}: {answer.score:.2f}, "
f"most likely {answer.legend[level]!r}"
)
elif isinstance(answer, Answer_Span):
for span in answer.spans:
print(
f"{question_id}: {span.audio_start_ms} to "
f"{span.audio_end_ms} ms, {span.probability:.2f}"
)
print(f"run {response.run_id}")

Run

uv
uv run ask_questions.py

Each probability estimates the share of human raters who would give that answer, so a noul answer near 0.5 means raters would split on the audio, not that the model is unsure. Ask the same questions in the same order when you compare recordings, since an answer can change when other questions are added, removed or reordered.

Audio helpers

create converts a WAV file to 16 kHz mono before sending it, because the model hears audio as one channel at 16 kHz. The converted file is smaller, so a WAV file over the size limit of 25,000,000 bytes can still fit in a request. Pass downmix_strategy="first_channel" to keep only the first channel, such as to judge one speaker of a call apart from the other, or downmix_strategy=None to send the file as it is. MP3 files are always sent as they are. See Preparing audio in the SDK README.

A request takes up to 5 minutes of audio. To send a longer recording, create_chunked cuts it into chunks of up to max_chunk_ms and sends each chunk in its own request.

long_recording.py
from hume_audio_decisions import (
Answer_Span,
DecisionsClient,
DecisionsRequest,
Question_Span,
)
client = DecisionsClient()
with open("long_call.wav", "rb") as audio:
result = client.decisions.create_chunked(
file=("long_call.wav", audio, "audio/wav"),
request=DecisionsRequest(
questions={
"vocal_bursts": Question_Span(
instructions="When does the speaker laugh, sigh or make another vocal burst?",
),
},
),
max_chunk_ms=300_000,
)
for chunk in result.chunks:
if chunk.response is None:
print(f"{chunk.start_ms} to {chunk.end_ms} ms: {chunk.status}")
continue
answer = chunk.response.answers["vocal_bursts"]
if isinstance(answer, Answer_Span):
for span in answer.spans:
start_ms = chunk.start_ms + span.audio_start_ms
end_ms = chunk.start_ms + span.audio_end_ms
print(
f"vocal_bursts: {start_ms} to {end_ms} ms, "
f"{span.probability:.2f}"
)

The model answers each chunk on its own, and nothing combines the answers across chunks. A span’s times count from the start of its chunk, so the script adds the chunk’s start_ms to place each span in the recording. Most failed requests stop the job, and the chunks after one are not sent; result.resume_ms says where to continue. See Long recordings in the SDK README for choosing a chunk length, where the cuts fall and resuming.

Next steps