Skip to content

Commit f374b42

Browse files
chasef07claude
andcommitted
feat(assemblyai): new STT plugin for Universal-Streaming (v3)
Adds a new `@livekit/agents-plugin-assemblyai` STT plugin that connects to AssemblyAI's Universal-Streaming v3 WebSocket API. Ported line-by-line from the Python `livekit-plugins-assemblyai` plugin; every non-trivial block has a `// Ref: python .../stt.py - N-M lines` comment pointing back to the source so future syncs are straightforward. Architecture follows the existing Deepgram STT plugin: retry loop around a single WebSocket lifetime, concurrent sendTask/listenTask/configTask under `#runWS`, graceful close via `{"type":"Terminate"}`. Key features: - All AssemblyAI v3 options exposed: speechModel (universal-streaming-*, u3-rt-pro), sampleRate, bufferSizeMs, min/maxTurnSilence, formatTurns, keytermsPrompt, prompt (u3-rt-pro only), vadThreshold, endOfTurnConfidenceThreshold, speakerLabels, maxSpeakers, domain, custom baseUrl (for EU region). - Live config updates via `UpdateConfiguration` messages over the active socket — no reconnect needed, unlike Deepgram's updateOptions. - `forceEndpoint()` to finalize the current turn on demand. - `sessionId` / `expiresAt` getters for debugging; `sessionId` is also propagated as `requestId` on RECOGNITION_USAGE events. - Turn-event mapping: `Turn` message → INTERIM_TRANSCRIPT (cumulative), PREFLIGHT_TRANSCRIPT (chunk-based from `utterance`), FINAL_TRANSCRIPT + END_OF_SPEECH on `end_of_turn` (with `turn_is_formatted` check when `formatTurns` is enabled). - Word timestamps converted from ms → seconds for `createTimedString`. - `u3-pro` → `u3-rt-pro` rename handled with deprecation warning (matches Python). Known framework gap: `stt.SpeechData` in `@livekit/agents` doesn't yet have a `speakerId` field, so per-word speaker labels from AssemblyAI's diarization are not surfaced on emitted events. The `speakerLabels` option is exposed and sent to the server; a follow-up PR can wire through speaker ids once the base type is extended. See the TSDoc on `speakerLabels` for the exact forwarding point. Smoke-tested end-to-end against the AssemblyAI API using the shared `@livekit/agents-plugins-test` STT harness; transcription matches the reference transcript within the 20% Levenshtein threshold. Also adds `ASSEMBLYAI_API_KEY` to `turbo.json` globalEnv (required for lint to pass). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent adf2651 commit f374b42

12 files changed

Lines changed: 812 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@livekit/agents-plugin-assemblyai": patch
3+
---
4+
5+
feat(assemblyai): new STT plugin for AssemblyAI Universal-Streaming (v3) API. Ported from the Python livekit-plugins-assemblyai plugin.

plugins/assemblyai/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!--
2+
SPDX-FileCopyrightText: 2026 LiveKit, Inc.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
-->
6+
# AssemblyAI plugin for LiveKit Agents
7+
8+
The Agents Framework is designed for building realtime, programmable
9+
participants that run on servers. Use it to create conversational, multi-modal
10+
voice agents that can see, hear, and understand.
11+
12+
This package contains the AssemblyAI plugin, which allows for speech recognition
13+
via AssemblyAI's Universal-Streaming (v3) API. Refer to the
14+
[documentation](https://docs.livekit.io/agents/overview/) for information on how
15+
to use it, or browse the [API
16+
reference](https://docs.livekit.io/agents-js/modules/plugins_agents_plugin_assemblyai.html).
17+
See the [repository](https://github.com/livekit/agents-js) for more information
18+
about the framework as a whole.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
3+
*/
4+
{
5+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
6+
7+
/**
8+
* Optionally specifies another JSON config file that this file extends from. This provides a way for
9+
* standard settings to be shared across multiple projects.
10+
*
11+
* If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
12+
* the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
13+
* resolved using NodeJS require().
14+
*
15+
* SUPPORTED TOKENS: none
16+
* DEFAULT VALUE: ""
17+
*/
18+
"extends": "../../api-extractor-shared.json",
19+
"mainEntryPointFilePath": "./dist/index.d.ts"
20+
}

plugins/assemblyai/package.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "@livekit/agents-plugin-assemblyai",
3+
"version": "1.2.4",
4+
"description": "AssemblyAI plugin for LiveKit Agents for Node.js",
5+
"main": "dist/index.js",
6+
"require": "dist/index.cjs",
7+
"types": "dist/index.d.ts",
8+
"exports": {
9+
"import": {
10+
"types": "./dist/index.d.ts",
11+
"default": "./dist/index.js"
12+
},
13+
"require": {
14+
"types": "./dist/index.d.cts",
15+
"default": "./dist/index.cjs"
16+
}
17+
},
18+
"author": "LiveKit",
19+
"type": "module",
20+
"repository": "git@github.com:livekit/agents-js.git",
21+
"license": "Apache-2.0",
22+
"files": [
23+
"dist",
24+
"src",
25+
"README.md"
26+
],
27+
"scripts": {
28+
"build": "tsup --onSuccess \"pnpm build:types\"",
29+
"build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js",
30+
"clean": "rm -rf dist",
31+
"clean:build": "pnpm clean && pnpm build",
32+
"lint": "eslint -f unix \"src/**/*.{ts,js}\"",
33+
"api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript",
34+
"api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose"
35+
},
36+
"devDependencies": {
37+
"@livekit/agents": "workspace:*",
38+
"@livekit/agents-plugin-silero": "workspace:*",
39+
"@livekit/agents-plugins-test": "workspace:*",
40+
"@livekit/rtc-node": "catalog:",
41+
"@microsoft/api-extractor": "^7.35.0",
42+
"@types/ws": "^8.5.10",
43+
"tsup": "^8.3.5",
44+
"typescript": "^5.0.0"
45+
},
46+
"dependencies": {
47+
"ws": "^8.16.0"
48+
},
49+
"peerDependencies": {
50+
"@livekit/agents": "workspace:*",
51+
"@livekit/rtc-node": "catalog:"
52+
}
53+
}

plugins/assemblyai/src/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
import { Plugin } from '@livekit/agents';
5+
6+
export * from './models.js';
7+
export * from './stt.js';
8+
9+
class AssemblyAIPlugin extends Plugin {
10+
constructor() {
11+
super({
12+
title: 'assemblyai',
13+
version: __PACKAGE_VERSION__,
14+
package: __PACKAGE_NAME__,
15+
});
16+
}
17+
}
18+
19+
Plugin.registerPlugin(new AssemblyAIPlugin());

plugins/assemblyai/src/models.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
// Ref: python livekit-plugins/livekit-plugins-assemblyai/livekit/plugins/assemblyai/stt.py - 48-54 lines
6+
export type STTModels =
7+
| 'universal-streaming-english'
8+
| 'universal-streaming-multilingual'
9+
| 'u3-rt-pro'
10+
// Deprecated alias — AssemblyAI maps this to `u3-rt-pro` server-side, but the
11+
// Python plugin emits a warning and rewrites it. Kept here so TS users don't
12+
// break if they already pass it.
13+
| 'u3-pro';
14+
15+
// Ref: python livekit-plugins/livekit-plugins-assemblyai/livekit/plugins/assemblyai/stt.py - 47 lines
16+
export type STTEncoding = 'pcm_s16le' | 'pcm_mulaw';

plugins/assemblyai/src/stt.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
import { VAD } from '@livekit/agents-plugin-silero';
5+
import { stt } from '@livekit/agents-plugins-test';
6+
import { describe, it } from 'vitest';
7+
import { STT } from './stt.js';
8+
9+
const hasAssemblyAIApiKey = Boolean(process.env.ASSEMBLYAI_API_KEY);
10+
11+
if (hasAssemblyAIApiKey) {
12+
describe('AssemblyAI', async () => {
13+
await stt(new STT(), await VAD.load(), { nonStreaming: false });
14+
});
15+
} else {
16+
describe('AssemblyAI', () => {
17+
it.skip('requires ASSEMBLYAI_API_KEY', () => {});
18+
});
19+
}

0 commit comments

Comments
 (0)