Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [4.42.1]

- Add `action_items` to the speech understanding feature requests and responses (`ActionItemsRequest`, `ActionItem`, `ActionItemsResponse`): `include_decisions` and `effort`. The feature was already accepted by the API and returned in transcripts, but was missing from the SDK types

## [4.42.0]

- Add Text-to-Speech: `client.tts.synthesize(request)` returns one text's complete audio (`TextToSpeechResponse`: `audio` plus the response headers as `requestId`, `region`, `model`, `voiceId`, `encoding`, `sampleRate`, `billedCharacters`, `audioDurationMs`, ...), `client.tts.stream(request)` returns it as the API generates it (`TextToSpeechStream`, an async iterable over a web `ReadableStream`), and `client.tts.session(params)` opens a WebSocket `TextToSpeechSession` for voice agents: `connect()`, `generate()` / `flush()` / `audio()`, `speak()`, `cancel()`, `keepAlive()`, `close()`, with `on("audio" | "flushDone" | "wordBoundaries" | "cancelled" | "termination" | "error" | "close", ...)`. Requests take the API's fields (`text`, `voice_id`, `model_id`, `language`, `sample_rate`, `encoding`, `container`, `bit_rate`); failures throw `TextToSpeechError` (`status`, `errorCode`, `param`, `requestId`, `retryable`, `retryAfter`, `errors`, `closeCode`, `body`). New client option `ttsBaseUrl` (default `https://tts.us.assemblyai.com`); `client.tts.createTemporaryToken()` mints a token for browser sessions. Text sent while `connect()` is pending is held and sent the moment the socket opens, so it need not wait for `Begin`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "assemblyai",
"version": "4.42.0",
"version": "4.42.1",
"description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription and text-to-speech.",
"engines": {
"node": ">=18"
Expand Down
72 changes: 63 additions & 9 deletions src/types/openapi.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,22 @@ export type SummarizationRequest = {
effort?: SpeechUnderstandingEffort;
};

/**
* Action items configuration for speech understanding
*/
export type ActionItemsRequest = {
/**
* Also extract key decisions made during the conversation
*/
include_decisions?: boolean;
/**
* How much effort to spend on this task
*
* @defaultValue "low"
*/
effort?: SpeechUnderstandingEffort;
};

/**
* Speech understanding feature requests grouped together
*/
Expand All @@ -1171,6 +1187,10 @@ export type SpeechUnderstandingFeatureRequests = {
* Summarization configuration
*/
summarization?: SummarizationRequest;
/**
* Action items configuration
*/
action_items?: ActionItemsRequest;
};

/**
Expand Down Expand Up @@ -1281,6 +1301,42 @@ export type SummarizationResponse = {
effort?: SpeechUnderstandingEffort;
};

/**
* An action item or decision extracted from the audio
*/
export type ActionItem = {
/**
* The extracted action item or decision
*/
action_item: string;
/**
* The source quote from the audio
*/
quote?: string;
/**
* Timestamp in milliseconds from audio start
*/
timestamp?: number;
};

/**
* Action items response containing status and extracted items
*/
export type ActionItemsResponse = {
/**
* Status of the action items feature
*/
status: string;
/**
* The effort level that was used
*/
effort?: SpeechUnderstandingEffort;
/**
* List of extracted action items
*/
items?: ActionItem[];
};

/**
* Speech understanding feature responses grouped together
*/
Expand All @@ -1301,6 +1357,10 @@ export type SpeechUnderstandingFeatureResponses = {
* Summarization results
*/
summarization?: SummarizationResponse;
/**
* Action items results
*/
action_items?: ActionItemsResponse;
};

/**
Expand Down Expand Up @@ -1433,11 +1493,7 @@ export type SummaryModel = "informative" | "conversational" | "catchy";
* The type of summary
*/
export type SummaryType =
| "bullets"
| "bullets_verbose"
| "gist"
| "headline"
| "paragraph";
"bullets" | "bullets_verbose" | "gist" | "headline" | "paragraph";

/**
* Timestamp containing a start and end property in milliseconds
Expand Down Expand Up @@ -3186,8 +3242,7 @@ export type TranscriptOptionalParams = {
* Speech understanding configuration/response for LLM Gateway features
*/
speech_understanding?:
| SpeechUnderstandingRequest
| SpeechUnderstandingResponse;
SpeechUnderstandingRequest | SpeechUnderstandingResponse;
/**
* The domain to use for the transcription (e.g. 'medical-v1').
*/
Expand Down Expand Up @@ -3580,8 +3635,7 @@ export type TranscriptUtterance = {
* The notifications sent to the webhook URL.
*/
export type TranscriptWebhookNotification =
| TranscriptReadyNotification
| RedactedAudioNotification;
TranscriptReadyNotification | RedactedAudioNotification;

/**
* @example
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/exports.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type {
ActionItem,
ActionItemsRequest,
ActionItemsResponse,
LiteralUnion,
TextToSpeechErrorFields,
TextToSpeechRequest,
Expand Down Expand Up @@ -73,4 +76,20 @@ describe("package root exports", () => {
expect(typeof infoOf).toBe("function");
expect(info.requestId).toBe("req-1");
});

it("exposes the Speech Understanding action items types from the root", () => {
const request: ActionItemsRequest = {
include_decisions: true,
effort: "low",
};
const item: ActionItem = {
action_item: "Review PR",
};
const response: ActionItemsResponse = {
status: "success",
items: [item],
};
expect(request.include_decisions).toBe(true);
expect(response.items?.[0].action_item).toBe("Review PR");
});
});
59 changes: 59 additions & 0 deletions tests/unit/speech-understanding-action-items.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import fetchMock from "jest-fetch-mock";
import { ActionItem, ActionItemsRequest, ActionItemsResponse } from "../../src";
import { createClient, requestMatches } from "./utils";

fetchMock.enableMocks();

const assembly = createClient();
const transcriptId = "transcript_123";
const remoteAudioURL = "https://assembly.ai/espn.m4a";

beforeEach(() => {
jest.clearAllMocks();
fetchMock.resetMocks();
fetchMock.doMock();
});

describe("speech understanding action items", () => {
it("should submit every action_items option", async () => {
const actionItems: ActionItemsRequest = {
include_decisions: true,
effort: "medium",
};
fetchMock.doMockOnceIf(
requestMatches({ url: "/v2/transcript", method: "POST" }),
JSON.stringify({ id: transcriptId, status: "queued" }),
);

await assembly.transcripts.submit({
audio_url: remoteAudioURL,
speech_understanding: { request: { action_items: actionItems } },
});

const body = JSON.parse(fetchMock.mock.calls[0][1]?.body as string);
expect(body.speech_understanding.request.action_items).toEqual(actionItems);
});

it("should type the action_items response", () => {
const item: ActionItem = {
action_item: "Schedule follow-up meeting with team",
quote: "Let's schedule a follow-up next Tuesday.",
timestamp: 45000,
};
const response: ActionItemsResponse = {
status: "success",
effort: "medium",
items: [item],
};

expect(response.status).toBe("success");
expect(response.effort).toBe("medium");
expect(response.items?.[0].action_item).toBe(
"Schedule follow-up meeting with team",
);
expect(response.items?.[0].quote).toBe(
"Let's schedule a follow-up next Tuesday.",
);
expect(response.items?.[0].timestamp).toBe(45000);
});
});