Claude-code-plugins assemblyai-sdk-patterns
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/assemblyai-pack/skills/assemblyai-sdk-patterns" ~/.claude/skills/jeremylongshore-claude-code-plugins-assemblyai-sdk-patterns && rm -rf "$T"
manifest:
plugins/saas-packs/assemblyai-pack/skills/assemblyai-sdk-patterns/SKILL.mdsource content
AssemblyAI SDK Patterns
Overview
Production-ready patterns for the
assemblyai npm package covering client initialization, type-safe wrappers, error handling, and multi-tenant architectures.
Prerequisites
package installed (assemblyai
)npm install assemblyai- Familiarity with async/await and TypeScript generics
Instructions
Step 1: Type-Safe Singleton Client
// src/assemblyai/client.ts import { AssemblyAI } from 'assemblyai'; let instance: AssemblyAI | null = null; export function getAssemblyAI(): AssemblyAI { if (!instance) { const apiKey = process.env.ASSEMBLYAI_API_KEY; if (!apiKey) throw new Error('ASSEMBLYAI_API_KEY is required'); instance = new AssemblyAI({ apiKey }); } return instance; }
Step 2: Transcription Service Wrapper
// src/assemblyai/transcription-service.ts import { AssemblyAI, type Transcript, type TranscriptParams } from 'assemblyai'; export interface TranscriptionResult { id: string; text: string; duration: number; words: Array<{ text: string; start: number; end: number; confidence: number }>; speakers?: Array<{ speaker: string; text: string }>; } export class TranscriptionService { constructor(private client: AssemblyAI) {} async transcribe( audio: string, options: Partial<TranscriptParams> = {} ): Promise<TranscriptionResult> { const transcript = await this.client.transcripts.transcribe({ audio, ...options, }); if (transcript.status === 'error') { throw new Error(`Transcription failed: ${transcript.error}`); } return { id: transcript.id, text: transcript.text ?? '', duration: transcript.audio_duration ?? 0, words: (transcript.words ?? []).map(w => ({ text: w.text, start: w.start, end: w.end, confidence: w.confidence, })), speakers: transcript.utterances?.map(u => ({ speaker: u.speaker, text: u.text, })), }; } async getTranscript(id: string): Promise<Transcript> { return this.client.transcripts.get(id); } async listTranscripts(params?: { limit?: number; status?: string }) { const page = await this.client.transcripts.list(params); return page.transcripts; } async deleteTranscript(id: string): Promise<void> { await this.client.transcripts.delete(id); } }
Step 3: Error Handling Wrapper
// src/assemblyai/errors.ts export class AssemblyAIServiceError extends Error { constructor( message: string, public readonly statusCode?: number, public readonly retryable: boolean = false, public readonly transcriptId?: string ) { super(message); this.name = 'AssemblyAIServiceError'; } } export async function safeTranscribe<T>( operation: () => Promise<T> ): Promise<{ data: T | null; error: AssemblyAIServiceError | null }> { try { const data = await operation(); return { data, error: null }; } catch (err: any) { const statusCode = err.status ?? err.statusCode; const retryable = statusCode === 429 || (statusCode >= 500 && statusCode < 600); return { data: null, error: new AssemblyAIServiceError( err.message ?? 'Unknown AssemblyAI error', statusCode, retryable ), }; } } // Usage const { data, error } = await safeTranscribe(() => client.transcripts.transcribe({ audio: audioUrl }) ); if (error?.retryable) { // Implement retry logic }
Step 4: Retry with Exponential Backoff
export async function withRetry<T>( operation: () => Promise<T>, maxRetries = 3, baseDelayMs = 1000 ): Promise<T> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (err: any) { if (attempt === maxRetries) throw err; const status = err.status ?? err.statusCode; // Only retry on rate limits (429) and server errors (5xx) if (status && status !== 429 && (status < 500 || status >= 600)) throw err; const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 500; console.warn(`Retry ${attempt + 1}/${maxRetries} in ${delay.toFixed(0)}ms`); await new Promise(r => setTimeout(r, delay)); } } throw new Error('Unreachable'); } // Usage const transcript = await withRetry(() => client.transcripts.transcribe({ audio: audioUrl }) );
Step 5: Multi-Tenant Client Factory
// For apps serving multiple customers with their own API keys const clients = new Map<string, AssemblyAI>(); export function getClientForTenant(tenantId: string): AssemblyAI { if (!clients.has(tenantId)) { const apiKey = getTenantApiKey(tenantId); // from your secrets store clients.set(tenantId, new AssemblyAI({ apiKey })); } return clients.get(tenantId)!; }
Python Patterns
import assemblyai as aai from dataclasses import dataclass from typing import Optional aai.settings.api_key = os.environ["ASSEMBLYAI_API_KEY"] @dataclass class TranscriptionResult: id: str text: str duration: float status: str def transcribe_audio(audio_url: str, speaker_labels: bool = False) -> TranscriptionResult: config = aai.TranscriptionConfig(speaker_labels=speaker_labels) transcriber = aai.Transcriber() transcript = transcriber.transcribe(audio_url, config=config) if transcript.status == aai.TranscriptStatus.error: raise RuntimeError(f"Transcription failed: {transcript.error}") return TranscriptionResult( id=transcript.id, text=transcript.text, duration=transcript.audio_duration, status=transcript.status.value, )
Output
- Type-safe singleton client with environment validation
- Transcription service wrapper with clean return types
- Error handling wrapper that classifies retryable vs non-retryable errors
- Exponential backoff with jitter for rate limits
- Multi-tenant client factory pattern
Error Handling
| Pattern | Use Case | Benefit |
|---|---|---|
| All API calls | Prevents uncaught exceptions, classifies errors |
| Rate-limited operations | Auto-retry on 429 and 5xx |
| Service wrapper | Domain logic | Clean types, hides SDK internals |
| Multi-tenant factory | SaaS apps | Per-customer isolation |
Resources
Next Steps
Apply patterns in
assemblyai-core-workflow-a (async transcription) and assemblyai-core-workflow-b (real-time streaming).