From assemblyai-pack
Applies AssemblyAI SDK patterns in TypeScript for type-safe clients, transcription services, error handling, and polling in speech-to-text integrations.
How this skill is triggered — by the user, by Claude, or both
Slash command
/assemblyai-pack:assemblyai-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 `assemblyai` npm package covering client initialization, type-safe wrappers, error handling, and multi-tenant architectures.
Production-ready patterns for the assemblyai npm package covering client initialization, type-safe wrappers, error handling, and multi-tenant architectures.
assemblyai package installed (npm install assemblyai)// 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;
}
// 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);
}
}
// 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
}
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 })
);
// 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)!;
}
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,
)
| Pattern | Use Case | Benefit |
|---|---|---|
safeTranscribe | All API calls | Prevents uncaught exceptions, classifies errors |
withRetry | 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 |
Apply patterns in assemblyai-core-workflow-a (async transcription) and assemblyai-core-workflow-b (real-time streaming).
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin assemblyai-packInstalls AssemblyAI SDK for Node.js/Python, configures API key authentication, initializes client, and verifies connection for speech-to-text transcription.
Applies production Deepgram SDK patterns for TypeScript and Python: singleton clients, Aura TTS, audio pipelines, error handling, v5 migration.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.