From elevenlabs-pack
Applies production-ready ElevenLabs SDK patterns for TypeScript and Python, including singleton clients, type-safe TTS with voice presets, retries, and error handling for audio AI integrations.
How this skill is triggered — by the user, by Claude, or both
Slash command
/elevenlabs-pack:elevenlabs-sdk-patternsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Production-ready patterns for the ElevenLabs TypeScript and Python SDKs. Covers singleton clients, type-safe wrappers, error handling, retry logic, and multi-tenant patterns.
Production-ready patterns for the ElevenLabs TypeScript and Python SDKs. Covers singleton clients, type-safe wrappers, error handling, retry logic, and multi-tenant patterns.
@elevenlabs/elevenlabs-js installed (TypeScript) or elevenlabs (Python)// src/elevenlabs/client.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
let instance: ElevenLabsClient | null = null;
export function getClient(): ElevenLabsClient {
if (!instance) {
if (!process.env.ELEVENLABS_API_KEY) {
throw new Error("ELEVENLABS_API_KEY environment variable is required");
}
instance = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY,
maxRetries: 3, // Auto-retry on 429/5xx (default: 2)
timeoutInSeconds: 60, // Per-request timeout
});
}
return instance;
}
// Reset for testing
export function resetClient(): void {
instance = null;
}
// src/elevenlabs/tts-service.ts
import { getClient } from "./client";
import { Readable } from "stream";
import { pipeline } from "stream/promises";
import { createWriteStream } from "fs";
export type VoicePreset = "narration" | "conversational" | "dramatic" | "neutral";
const VOICE_PRESETS: Record<VoicePreset, {
stability: number;
similarity_boost: number;
style: number;
}> = {
narration: { stability: 0.6, similarity_boost: 0.75, style: 0.0 },
conversational: { stability: 0.4, similarity_boost: 0.6, style: 0.3 },
dramatic: { stability: 0.3, similarity_boost: 0.8, style: 0.7 },
neutral: { stability: 0.8, similarity_boost: 0.5, style: 0.0 },
};
export interface TTSOptions {
voiceId: string;
text: string;
modelId?: "eleven_v3" | "eleven_multilingual_v2" | "eleven_flash_v2_5" | "eleven_turbo_v2_5";
preset?: VoicePreset;
outputFormat?: string;
languageCode?: string;
}
export async function generateSpeech(options: TTSOptions): Promise<ReadableStream> {
const client = getClient();
const settings = VOICE_PRESETS[options.preset || "narration"];
return client.textToSpeech.convert(options.voiceId, {
text: options.text,
model_id: options.modelId || "eleven_multilingual_v2",
voice_settings: settings,
output_format: options.outputFormat || "mp3_44100_128",
language_code: options.languageCode,
});
}
export async function generateToFile(options: TTSOptions, outputPath: string): Promise<void> {
const audio = await generateSpeech(options);
await pipeline(
Readable.fromWeb(audio as any),
createWriteStream(outputPath)
);
}
// src/elevenlabs/errors.ts
export type ElevenLabsErrorCode =
| "auth_failed"
| "quota_exceeded"
| "rate_limited"
| "concurrent_limit"
| "voice_not_found"
| "invalid_request"
| "server_error"
| "network_error";
export class ElevenLabsServiceError extends Error {
constructor(
message: string,
public readonly code: ElevenLabsErrorCode,
public readonly httpStatus: number | null,
public readonly retryable: boolean,
public readonly originalError?: Error
) {
super(message);
this.name = "ElevenLabsServiceError";
}
}
export function classifyError(error: unknown): ElevenLabsServiceError {
if (error instanceof Error) {
const status = (error as any).statusCode || (error as any).status;
if (status === 401) {
const msg = (error as any).body?.detail?.message || error.message;
if (msg?.includes("quota")) {
return new ElevenLabsServiceError(msg, "quota_exceeded", 401, false, error);
}
return new ElevenLabsServiceError(msg, "auth_failed", 401, false, error);
}
if (status === 429) {
const msg = (error as any).body?.detail?.message || error.message;
const code = msg?.includes("concurrent") ? "concurrent_limit" : "rate_limited";
return new ElevenLabsServiceError(msg, code, 429, true, error);
}
if (status === 404) {
return new ElevenLabsServiceError(error.message, "voice_not_found", 404, false, error);
}
if (status === 400) {
return new ElevenLabsServiceError(error.message, "invalid_request", 400, false, error);
}
if (status >= 500) {
return new ElevenLabsServiceError(error.message, "server_error", status, true, error);
}
return new ElevenLabsServiceError(error.message, "network_error", null, true, error);
}
return new ElevenLabsServiceError(String(error), "network_error", null, true);
}
// src/elevenlabs/queue.ts
import PQueue from "p-queue";
import { classifyError, ElevenLabsServiceError } from "./errors";
// Concurrency limits by plan:
// Free: 2, Starter: 3, Creator: 5, Pro: 10, Scale: 15
const queue = new PQueue({
concurrency: 5, // Match your plan's concurrent request limit
interval: 1000, // Window in ms
intervalCap: 10, // Max requests per window
});
export async function queuedRequest<T>(
operation: () => Promise<T>,
maxRetries = 3
): Promise<T> {
return queue.add(async () => {
let lastError: ElevenLabsServiceError | undefined;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (err) {
lastError = classifyError(err);
if (!lastError.retryable) throw lastError;
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
const jitter = Math.random() * 500;
await new Promise(r => setTimeout(r, delay + jitter));
}
}
throw lastError;
}) as Promise<T>;
}
// src/elevenlabs/multi-tenant.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
const clients = new Map<string, ElevenLabsClient>();
export function getClientForTenant(tenantId: string, apiKey: string): ElevenLabsClient {
if (!clients.has(tenantId)) {
clients.set(tenantId, new ElevenLabsClient({
apiKey,
maxRetries: 3,
}));
}
return clients.get(tenantId)!;
}
export function removeTenantClient(tenantId: string): void {
clients.delete(tenantId);
}
# elevenlabs_service.py
import os
import asyncio
from elevenlabs.client import AsyncElevenLabsClient
_client: AsyncElevenLabsClient | None = None
def get_async_client() -> AsyncElevenLabsClient:
global _client
if _client is None:
_client = AsyncElevenLabsClient(
api_key=os.environ["ELEVENLABS_API_KEY"]
)
return _client
async def generate_speech(text: str, voice_id: str, output_path: str):
client = get_async_client()
audio = await client.text_to_speech.convert(
voice_id=voice_id,
text=text,
model_id="eleven_multilingual_v2",
voice_settings={
"stability": 0.5,
"similarity_boost": 0.75,
},
)
with open(output_path, "wb") as f:
async for chunk in audio:
f.write(chunk)
# Usage
asyncio.run(generate_speech("Hello!", "21m00Tcm4TlvDq8ikWAM", "out.mp3"))
| Pattern | Use Case | Key Benefit |
|---|---|---|
| Singleton client | Single-tenant apps | Memory efficiency, shared retries |
| Type-safe TTS | All TTS work | Voice presets, compile-time checks |
| Error classification | Production error handling | Actionable error codes |
| Concurrency queue | High-throughput apps | Respects plan limits automatically |
| Multi-tenant factory | SaaS platforms | Per-customer isolation |
| Python async | Python backends | Non-blocking I/O |
| Pattern | Error Type | Benefit |
|---|---|---|
classifyError() | All API errors | Maps HTTP to actionable codes |
queuedRequest() | 429, 5xx | Auto-retry with exponential backoff |
| Singleton guard | Missing env var | Fails fast at startup, not at first call |
Apply patterns in elevenlabs-core-workflow-a for TTS, or see elevenlabs-rate-limits for advanced throttling.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin elevenlabs-packImplements production reference architecture for ElevenLabs TTS/voice apps with TypeScript project structure, service layers, caching, API routes, queues, and monitoring.
API authentication patterns, SDK installation scripts, environment variable management, and connection testing for ElevenLabs. Use when setting up ElevenLabs authentication, installing ElevenLabs SDK, configuring API keys, testing ElevenLabs connection, or when user mentions ElevenLabs authentication, xi-api-key, ELEVENLABS_API_KEY, or ElevenLabs setup.
Builds ElevenLabs conversational AI voice agents: configure via CLI/dashboard, add tools/knowledge, integrate React/React Native/Swift/JS SDKs, test/deploy. For voice AI, phone systems, or ElevenLabs errors.