Skip to navigation

TypeScript quickstart

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

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

Package@humeai/audio-decisions on npm
Sourcehume-audio-decisions-typescript-sdk on GitHub
Node.js18 or later, or 22.18 or later for TypeScript

The package ships TypeScript types for every request and answer. The script contains no type annotations, so on Node.js earlier than 22.18 it also runs as a .js file.

Prerequisites

  1. Node.js 22.18 or later, which runs TypeScript directly, or 18 or later to run the script as JavaScript.
  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. The script is an ES module, so the commands set "type": "module" in the project’s package.json, and they install the Node.js type definitions, which the SDK’s types refer to. To add the SDK to an existing project, run only the commands your project still needs.

bun
mkdir decisions-quickstart
cd decisions-quickstart
bun init --yes --minimal
bun pm pkg set type=module
bun add @humeai/audio-decisions
bun add --dev @types/node

Yarn has no command to edit package.json, so the yarn commands set the module type with npm, which ships with Node.js. nodeLinker node-modules installs packages into node_modules, where node finds them.

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 new DecisionsClient({ apiKey: "..." }).

Ask questions about a recording

  1. Create the client

    Create ask-questions.ts. It asks four questions about speech.wav in one request and prints each answer.

    1. new DecisionsClient() reads your API key from HUME_API_KEY. The script also imports DecisionsError, which a failed request throws.

  2. Send the recording

    1. client.decisions.create() sends the questions and the audio as a multipart/form-data request and resolves to the parsed response. contentType labels the file part as WAV. To send an MP3 file, pass its path with audio/mpeg.

  3. Write the questions

    1. Each question is keyed by an ID you choose, and its type 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, add context to request alongside questions. Primitives covers what each question takes.

  4. Read the answers

    1. The response returns each answer under its question’s ID, and the runId of the model run that answered them. Switching on type narrows each answer to its fields. 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.

  5. Handle errors

    1. A failed request throws a DecisionsError. Its message includes the HTTP status and the error body, whose code says what went wrong and whose param says where in the request it was found. To branch on them, read statusCode and body. See Errors.

import { DecisionsClient, DecisionsError } from "@humeai/audio-decisions";
const client = new DecisionsClient();
try {
const response = await client.decisions.create({
file: {
path: "speech.wav",
contentType: "audio/wav",
},
request: {
questions: {
hesitant: {
type: "noul",
instructions: "Does the speaker sound hesitant?",
},
tone: {
type: "choice",
instructions: "Which best describes the speaker's tone?",
criteria: [
{ name: "warm" },
{ name: "neutral" },
{ name: "curt" },
],
},
naturalness: {
type: "score",
instructions: "How natural does the voice sound?",
criteria: ["Not at all", "2", "3", "4", "Extremely"],
},
vocal_bursts: {
type: "span",
instructions: "When does the speaker laugh, sigh or make another vocal burst?",
},
},
},
});
for (const [questionId, answer] of Object.entries(
response.answers,
)) {
switch (answer.type) {
case "noul":
console.log(`${questionId}: yes ${answer.noul.toFixed(2)}`);
break;
case "choice":
console.log(
`${questionId}: ${answer.choice}, ` +
`confidence ${answer.confidence.toFixed(2)}`,
);
break;
case "score": {
const [level] = Object.entries(answer.probabilities).reduce(
(most, entry) => (entry[1] > most[1] ? entry : most),
);
console.log(
`${questionId}: ${answer.score.toFixed(2)}, ` +
`most likely "${answer.legend[level]}"`,
);
break;
}
case "span":
for (const span of answer.spans) {
console.log(
`${questionId}: ${span.audioStartMs} to ` +
`${span.audioEndMs} ms, ${span.probability.toFixed(2)}`,
);
}
break;
}
}
console.log(`run ${response.runId}`);
} catch (error) {
if (!(error instanceof DecisionsError)) throw error;
console.error(error.message);
}

Run

Shell
node ask-questions.ts

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 downmixStrategy: "first_channel" to keep only the first channel, such as to judge one speaker of a call apart from the other, or downmixStrategy: "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, createChunked cuts it into chunks of up to maxChunkMs and sends each chunk in its own request.

long-recording.ts
import { DecisionsClient } from "@humeai/audio-decisions";
const client = new DecisionsClient();
const result = await client.decisions.createChunked({
file: {
path: "long_call.wav",
contentType: "audio/wav",
},
request: {
questions: {
vocal_bursts: {
type: "span",
instructions: "When does the speaker laugh, sigh or make another vocal burst?",
},
},
},
maxChunkMs: 300_000,
});
for (const chunk of result.chunks) {
if (chunk.status !== "succeeded") {
console.log(`${chunk.startMs} to ${chunk.endMs} ms: ${chunk.status}`);
continue;
}
const answer = chunk.response.answers.vocal_bursts;
if (answer.type === "span") {
for (const span of answer.spans) {
const startMs = chunk.startMs + span.audioStartMs;
const endMs = chunk.startMs + span.audioEndMs;
console.log(
`vocal_bursts: ${startMs} to ${endMs} ms, ${span.probability.toFixed(2)}`,
);
}
}
}

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 startMs to place each span in the recording. Most failed requests stop the job, and the chunks after one are not sent; result.resumeMs says where to continue. See Long recordings in the SDK README for choosing a chunk length, where the cuts fall and resuming.

Next steps