Polyweave

@polyweave/elevenlabs (1.0.9)

Published 2026-07-11 15:24:10 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/elevenlabs@1.0.9
"@polyweave/elevenlabs": "1.0.9"

About this package

@polyweave/elevenlabs

ElevenLabs multi-modal audio adapter for Polyweave — TTS (Text-to-Speech), STT (Speech-to-Text), and voice management via HTTP and WebSocket streaming. Built on the Polyweave frame duplex protocol.

Synopsis

import { createElevenLabsAdapter } from '@polyweave/elevenlabs';

const adapter = createElevenLabsAdapter({
  apiKey: process.env.ELEVENLABS_API_KEY,
  region: 'us'
});

adapter.source.pipe(consumerSink);

// Text-to-Speech (WebSocket streaming, default)
adapter.sink.write({
  type: 'TEXT',
  id: 'msg-1',
  streamId: 'stream-1',
  content: { text: 'Hello, how can I help you today?' }
});

// Speech-to-Text (HTTP batch)
adapter.sink.write({
  type: 'PARAM',
  content: { key: 'task', value: 'stt' }
});
adapter.sink.write({
  type: 'AUDIO',
  id: 'audio-1',
  streamId: 'stream-1',
  content: { data: audioBuffer }
});

// Realtime streaming STT
adapter.sink.write({
  type: 'PARAM',
  content: { key: 'streaming_stt', value: true }
});
adapter.sink.write({
  type: 'AUDIO',
  id: 'audio-2',
  streamId: 'stream-2',
  content: { data: pcmChunk }
});
// ... more AUDIO chunks ...
adapter.sink.write({
  type: 'END',
  streamId: 'stream-2'
});

// Voice management
adapter.sink.write({
  type: 'PARAM',
  content: { key: 'task', value: 'list_voices' }
});
adapter.sink.write({
  type: 'TEXT',
  id: 'voices-1',
  streamId: 'voices-1',
  content: { text: '' }
});

Install

npm install @polyweave/elevenlabs

Why

ElevenLabs offers TTS, STT, and voice cloning — but with three different API surfaces (HTTP REST, WebSocket streaming TTS, WebSocket realtime STT). This adapter unifies them behind a single frame-duplex interface: you send TEXT or AUDIO frames, configure behavior with PARAM frames, and receive AUDIO, TEXT, RECEIPT, and END frames in response. Streaming WebSocket sessions (LLM→TTS, microphone→STT) are managed automatically — the adapter creates sessions per streamId, queues audio before the WebSocket handshake completes, and emits partial transcripts in real time.

API

Primary export

createElevenLabsAdapter(config?)

createElevenLabsAdapter(config?: {
  apiKey?: string,
  region?: 'default' | 'us' | 'eu' | 'india',
  baseURL?: string,
  wsBaseURL?: string,
  timeout?: number,
  name?: string
}): IFrameDuplex

Creates the ElevenLabs frame-duplex adapter. Returns immediately; the adapter enters a buffered state until an API key is provided (via config or PARAM frame).

Name Type Required Default Description
config.apiKey string no process.env.ELEVENLABS_API_KEY ElevenLabs API key
config.region string no 'default' API region: 'default', 'us', 'eu', 'india'
config.baseURL string no Auto from region Override HTTP base URL
config.wsBaseURL string no Auto from region Override WebSocket base URL
config.timeout number no 30000 Request timeout in milliseconds
config.name string no null Adapter name (reflected in topology reports)

Return value: IFrameDuplex with { source, sink, setReady, ready, apiKey, name }.

  • duplex.source — push-stream source that emits response frames (AUDIO, TEXT, RECEIPT, ERROR, END)
  • duplex.sink — push-stream sink that accepts TEXT, AUDIO, PARAM, START, END, TOPOLOGY_SCAN, TOPOLOGY_REPORT, TOPOLOGY_COMPLETE, TEST_INJECT, TEST_SNAPSHOT frames
  • duplex.setReady(apiKey) — provide API key after construction; flushes the internal frame buffer
  • duplex.ready — boolean getter; true once an API key is set
  • duplex.apiKey — the currently configured API key

Frame protocol — Inputs (sink.write):

Frame type Purpose
TEXT TTS input (text to synthesize; defaults to TTS unless task is set to 'stt' or voice operations). content.text or content (fallback). If streaming_tts param is truthy, uses WebSocket streaming; otherwise HTTP batch.
AUDIO STT input (audio to transcribe; defaults to STT unless task is set to 'tts' or voice ops). content.data (Buffer) or content (fallback). If streaming_stt param is truthy, uses realtime WebSocket; otherwise HTTP multipart.
PARAM Sets configuration. { key, value }. Supported keys: apiKey, task, stt_model, language_code, include_timestamps, include_language_detection, commit_strategy, vad_silence_threshold_secs, vad_threshold, min_speech_duration_ms, min_silence_duration_ms, audio_format, voice_id, model_id, stability, similarity_boost, style, use_speaker_boost, speed, output_format, chunk_length_schedule, auto_mode, streaming_stt, streaming_tts, voice_operation, voice_name, voice_description, receipt_mode, receipt_interval
END Terminates a streaming session (STT or TTS). Routed by streamId to the active session.
START Pass-through (no-op).
TOPOLOGY_SCAN Returns a TOPOLOGY_REPORT describing the adapter's receives/generates and state.
TOPOLOGY_REPORT / TOPOLOGY_COMPLETE Pass-through to source.
TEST_INJECT Injects an array of test frames from content.frames.
TEST_SNAPSHOT Returns a TEST_SNAPSHOT frame with the adapter's paused state.

Frame protocol — Outputs (source emits):

Frame type Emitted when
AUDIO TTS audio chunk received (HTTP streaming or WebSocket). Each chunk includes metadata.streaming, format, chunkIndex, ttft.
TEXT STT transcript received. metadata.partial for realtime partials, metadata.committed for final utterances. HTTP STT emits a single TEXT frame. Voice operations emit result JSON as TEXT frames with metadata.type (voice_list, voice_info, voice_cloned, voice_deleted).
RECEIPT Final receipt frame after operation completes. Contains characters, audioSeconds, totalCost (estimated), totalTime, ttft.
ERROR On API errors, network failures, WebSocket errors, or misrouted frames. Includes content.code (ErrorCode.PROVIDER_ERROR or mapped code) and metadata.statusCode.
END Sent after every completed operation or error. metadata.error: true for error endings.
TOPOLOGY_REPORT Response to TOPOLOGY_SCAN.
TOPOLOGY_COMPLETE Pass-through.
TEST_SNAPSHOT Response to TEST_SNAPSHOT.

Task routing (task param):

task value Frame type Handler Description
'tts', 't2a', 'text_to_speech' TEXT handleTtsWebSocket / handleTtsHttp Text-to-Speech
'stt', 'a2t', 'speech_to_text', 'transcribe' AUDIO handleSttRealtime / handleSttHttp Speech-to-Text
'voice_clone', 'clone' TEXT or AUDIO handleVoices Clone a voice (audio from AUDIO frame)
'voice_design', 'design_voice' TEXT handleVoices Design a voice
'voices', 'list_voices' TEXT handleVoices List all voices
(empty/unset) TEXT handleTtsWebSocket / handleTtsHttp Default: TTS
(empty/unset) AUDIO handleSttRealtime / handleSttHttp Default: STT

Error conditions:

  • If frame.type is neither TEXT nor AUDIO and no voice task is set: throws Error('Unsupported frame type: ...') — caught by sink and emitted as ERROR + END
  • If TEXT content has no text: ERROR('No text provided for TTS') + END
  • If AUDIO content has no data: ERROR('No audio data provided for STT') + END
  • HTTP 400+ responses from ElevenLabs: ERROR frame with message from detail.message and mapped statusCode
  • WebSocket connection errors: ERROR with err.message
  • WebSocket close before connection: ERROR('WebSocket closed before connection: {code} {reason}')
  • JSON parse failure on WebSocket message: ERROR('Failed to parse WebSocket message: ...')
  • Invalid audio data format in STT: ERROR('Invalid audio data format')
  • Voice operations with missing voice_id: ERROR('voice_id required for get/delete operation')
  • Voice clone with missing audio data: ERROR('Audio data required for voice cloning')
  • PARAM with key 'apiKey' triggers setReady(value) and is not stored in params

Internal exports

getModelCost(model) (./costs.js)

getModelCost(model: string): { perCharacter?: number, perMinute?: number } | null

Returns the pricing config for a TTS/STT model, or null if unknown.

Model Cost
tts-multilingual-v2 $0.00003/character
tts-turbo-v2 $0.00002/character
stt-v1 $0.006/minute

calculateCost(model, units)

calculateCost(model: string, units?: number): number

Calculates cost for the given model and units (characters for TTS, minutes for STT). Returns 0 for unknown models.

mapElevenLabsError(error, frame) (./utils/errorMapper.js)

Maps ElevenLabs API error types to canonical ErrorCode values and returns an error frame. Handles auth_error, quota_exceeded, rate_limited, input_error, chunk_size_exceeded, resource_exhausted, queue_overflow, session_time_limit_exceeded, unaccepted_terms, transcriber_error, and mapped HTTP status codes.

isRetryable(error)

Returns true if the error type (rate_limited, resource_exhausted, queue_overflow, commit_throttled) or HTTP status (429, 502, 503) indicates a retryable error.

createAdapterBase(config, options?) (./base.js)

Base factory for creating new adapters. Buffers frames, manages ready state, and provides _translateFrameToProvider/_sendToProvider/_readFromProvider hooks. Not used directly by the ElevenLabs adapter (which has its own implementation) but exported for adapter builders.

Tests

npm test

Tests cover adapter creation, API key configuration (config, env var, PARAM frame), frame buffering, task routing (TTS, STT, voice operations), parameter accumulation, topology scan/report, test inject/snapshot, ready state toggling, and error emission on invalid frames.

Requirements

Node.js 22 or later. ESM only. Depends on @polyweave/core, @polyweave/errors, and @polyweave/push-websocket.

Dependencies

Dependencies

ID Version
@polyweave/push-websocket *
ws ^8.19.0

Development Dependencies

ID Version
@rigor/core *

Peer Dependencies

ID Version
@polyweave/core *
@polyweave/errors *

Keywords

polyweave elevenlabs tts stt ai adapter
Details
npm
2026-07-11 15:24:10 +00:00
4
John Dvorak
SEE LICENSE IN LICENSE
latest
17 KiB
Assets (1)
Versions (5) View all
1.0.9 2026-07-11
1.0.6 2026-07-10
1.0.5 2026-07-10
1.0.4 2026-07-10
1.0.3 2026-07-10