SKILL.md
AssemblyAI Local Dev Loop
Overview
Set up a fast, reproducible local development workflow for AssemblyAI transcription and LeMUR projects with mocking, caching, and hot reload.
Prerequisites
- Completed
assemblyai-install-authsetup - Node.js 18+ with npm/pnpm
- TypeScript project with
tsxorts-node
Instructions
Step 1: Project Structure
my-assemblyai-project/
├── src/
│ ├── assemblyai/
│ │ ├── client.ts # Singleton client
│ │ ├── transcribe.ts # Transcription helpers
│ │ └── lemur.ts # LeMUR helpers
│ └── index.ts
├── tests/
│ ├── transcribe.test.ts
│ └── fixtures/
│ └── sample-transcript.json # Cached API response
├── .env.local # Local secrets (git-ignored)
├── .env.example # Template for team
└── package.json
Step 2: Dev Scripts
{
"scripts": {
"dev": "tsx watch src/index.ts",
"test": "vitest",
"test:watch": "vitest --watch",
"transcribe": "tsx src/assemblyai/transcribe.ts"
},
"devDependencies": {
"tsx": "^4.7.0",
"vitest": "^1.6.0",
"dotenv": "^16.4.0"
},
"dependencies": {
"assemblyai": "^4.8.0"
}
}
Step 3: Singleton Client with Env Loading
// src/assemblyai/client.ts
import 'dotenv/config';
import { AssemblyAI } from 'assemblyai';
let instance: AssemblyAI | null = null;
export function getClient(): AssemblyAI {
if (!instance) {
if (!process.env.ASSEMBLYAI_API_KEY) {
throw new Error('ASSEMBLYAI_API_KEY not set. Copy .env.example to .env.local');
}
instance = new AssemblyAI({
apiKey: process.env.ASSEMBLYAI_API_KEY,
});
}
return instance;
}
Step 4: Cache Transcription Results for Fast Iteration
// src/assemblyai/transcribe.ts
import fs from 'fs';
import path from 'path';
import { getClient } from './client';
import type { Transcript } from 'assemblyai';
const CACHE_DIR = path.join(process.cwd(), '.assemblyai-cache');
export async function transcribeWithCache(
audioUrl: string,
options: Record<string, any> = {}
): Promise<Transcript> {
const cacheKey = Buffer.from(audioUrl + JSON.stringify(options))
.toString('base64url')
.slice(0, 32);
const cachePath = path.join(CACHE_DIR, `${cacheKey}.json`);
// Return cached result in dev
if (process.env.NODE_ENV !== 'production' && fs.existsSync(cachePath)) {
console.log('[dev] Using cached transcript:', cacheKey);
return JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
}
const client = getClient();
const transcript = await client.transcripts.transcribe({
audio: audioUrl,
...options,
});
// Cache for next run
fs.mkdirSync(CACHE_DIR, { recursive: true });
fs.writeFileSync(cachePath, JSON.stringify(transcript, null, 2));
console.log('[dev] Cached transcript:', cacheKey);
return transcript;
}
