Skip to content

Commit bb63ec2

Browse files
MLawlzclaude
andcommitted
feat: add effort to SpeakerIdentificationRequest
The speaker identification API accepts an `effort` field ("low" | "medium", default "low") to trade cost for quality on harder audio, but it was missing from the SDK types, so setting it failed to type check. Fixes #167 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5051f3e commit bb63ec2

4 files changed

Lines changed: 107 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [4.36.6]
4+
5+
- Add `effort` to `SpeakerIdentificationRequest``"low"` (default) or `"medium"`, matching the [speaker identification docs](https://www.assemblyai.com/docs/speech-understanding/speaker-identification#controlling-effort). The field was already accepted by the API but missing from the SDK types, so setting it failed to type check
6+
37
## [4.36.4]
48

59
- Add `aac` to the streaming `encoding` options — accepts an AAC stream in ADTS framing. Like `opus`/`ogg_opus`, AAC is self-describing, so `sampleRate` is optional for it (it remains required for PCM encodings and for dual-channel mode)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "assemblyai",
3-
"version": "4.36.5",
3+
"version": "4.36.6",
44
"description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription, as well as the latest LeMUR models.",
55
"engines": {
66
"node": ">=18"

src/types/openapi.generated.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,11 @@ export type SeverityScoreSummary = {
15081508
*/
15091509
export type SpeakerType = "role" | "name";
15101510

1511+
/**
1512+
* Effort level for speaker identification
1513+
*/
1514+
export type SpeakerIdentificationEffort = "low" | "medium";
1515+
15111516
/**
15121517
* Speaker identification configuration for speech understanding
15131518
*/
@@ -1520,6 +1525,14 @@ export type SpeakerIdentificationRequest = {
15201525
* Known speaker values (required when speaker_type is 'role')
15211526
*/
15221527
known_values?: string[];
1528+
/**
1529+
* How much effort to spend identifying speakers. Use 'medium' for higher complexity
1530+
* audio, such as meetings with interruptions or transcripts where names aren't
1531+
* clearly stated, at a higher cost.
1532+
*
1533+
* @defaultValue "low"
1534+
*/
1535+
effort?: SpeakerIdentificationEffort;
15231536
};
15241537

15251538
/**
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import fetchMock from "jest-fetch-mock";
2+
import {
3+
SpeakerIdentificationEffort,
4+
SpeakerIdentificationRequest,
5+
} from "../../src";
6+
import { createClient, requestMatches } from "./utils";
7+
8+
fetchMock.enableMocks();
9+
10+
const assembly = createClient();
11+
const transcriptId = "transcript_123";
12+
const remoteAudioURL = "https://assembly.ai/espn.m4a";
13+
14+
beforeEach(() => {
15+
jest.clearAllMocks();
16+
fetchMock.resetMocks();
17+
fetchMock.doMock();
18+
});
19+
20+
describe("speaker identification", () => {
21+
it("should create transcript with speaker_identification effort", async () => {
22+
const speakerIdentification: SpeakerIdentificationRequest = {
23+
speaker_type: "name",
24+
effort: "medium",
25+
};
26+
27+
fetchMock.doMockOnceIf(
28+
requestMatches({ url: "/v2/transcript", method: "POST" }),
29+
JSON.stringify({ id: transcriptId, status: "queued" }),
30+
);
31+
32+
const transcript = await assembly.transcripts.submit({
33+
audio_url: remoteAudioURL,
34+
speaker_labels: true,
35+
speech_understanding: {
36+
request: { speaker_identification: speakerIdentification },
37+
},
38+
});
39+
40+
expect(transcript.id).toBe(transcriptId);
41+
expect(transcript.status).toBe("queued");
42+
43+
const requestBody = JSON.parse(fetchMock.mock.calls[0][1]?.body as string);
44+
expect(
45+
requestBody.speech_understanding.request.speaker_identification,
46+
).toEqual(speakerIdentification);
47+
});
48+
49+
it("should create transcript with speaker_identification without effort", async () => {
50+
fetchMock.doMockOnceIf(
51+
requestMatches({ url: "/v2/transcript", method: "POST" }),
52+
JSON.stringify({ id: transcriptId, status: "queued" }),
53+
);
54+
55+
const transcript = await assembly.transcripts.submit({
56+
audio_url: remoteAudioURL,
57+
speaker_labels: true,
58+
speech_understanding: {
59+
request: {
60+
speaker_identification: {
61+
speaker_type: "role",
62+
known_values: ["Agent", "Customer"],
63+
},
64+
},
65+
},
66+
});
67+
68+
expect(transcript.id).toBe(transcriptId);
69+
70+
const requestBody = JSON.parse(fetchMock.mock.calls[0][1]?.body as string);
71+
const speakerIdentification =
72+
requestBody.speech_understanding.request.speaker_identification;
73+
expect(speakerIdentification.speaker_type).toBe("role");
74+
expect(speakerIdentification.known_values).toEqual(["Agent", "Customer"]);
75+
expect(speakerIdentification.effort).toBeUndefined();
76+
});
77+
78+
it("should accept every documented effort value", () => {
79+
const efforts: SpeakerIdentificationEffort[] = ["low", "medium"];
80+
81+
for (const effort of efforts) {
82+
const speakerIdentification: SpeakerIdentificationRequest = {
83+
speaker_type: "name",
84+
effort,
85+
};
86+
expect(speakerIdentification.effort).toBe(effort);
87+
}
88+
});
89+
});

0 commit comments

Comments
 (0)