| name | twinmind-sdk-patterns |
| description | Apply production-ready TwinMind SDK patterns for TypeScript and Python.
Use when implementing TwinMind integrations, refactoring API usage,
or establishing team coding standards for meeting AI integration.
Trigger with phrases like "twinmind SDK patterns", "twinmind best practices",
"twinmind code patterns", "idiomatic twinmind".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
TwinMind SDK Patterns
Overview
Production-ready patterns for TwinMind API integration in TypeScript and Python.
Prerequisites
- Completed
twinmind-install-auth setup
- Familiarity with async/await patterns
- Understanding of error handling best practices
- TwinMind Pro or Enterprise API access
Instructions
Step 1: Implement Singleton Pattern (Recommended)
import axios, { AxiosInstance } from 'axios';
let instance: TwinMindClient | null = null;
export interface TwinMindConfig {
apiKey: string;
baseUrl?: string;
timeout?: number;
retries?: number;
}
export class TwinMindClient {
private client: AxiosInstance;
private config: TwinMindConfig;
private constructor(config: TwinMindConfig) {
this.config = config;
this.client = axios.create({
baseURL: config.baseUrl || 'https://api.twinmind.com/v1',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
timeout: config.timeout || 30000,
});
}
static getInstance(config?: TwinMindConfig): TwinMindClient {
if (!instance) {
if (!config) {
throw new Error('TwinMindClient must be initialized with config');
}
instance = new TwinMindClient(config);
}
return instance;
}
static resetInstance(): void {
instance = null;
}
}
export function getTwinMindClient(): TwinMindClient {
return TwinMindClient.getInstance({
apiKey: process.env.TWINMIND_API_KEY!,
});
}
Step 2: Add Error Handling Wrapper
export class TwinMindError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode?: number,
public readonly retryable: boolean = false,
public readonly originalError?: Error
) {
super(message);
this.name = 'TwinMindError';
}
}
export class TranscriptionError extends TwinMindError {
constructor(message: string, originalError?: Error) {
super(message, 'TRANSCRIPTION_FAILED', 500, true, originalError);
}
}
export class RateLimitError extends TwinMindError {
constructor() {
(, , , );
}
}
{
() {
(, , , );
}
}
safeTwinMindCall<T>(
: <T>
): <{ : T | ; : | }> {
{
data = ();
{ data, : };
} (: ) {
error = (err);
.({
: error.,
: error.,
: error.,
: error.,
});
{ : , error };
}
}
(): {
(err.?. === ) {
();
}
(err.?. === ) {
((err..[] || ));
}
(err.?. >= ) {
(err., err);
}
(err., , err.?.);
}
Step 3: Implement Retry Logic with Backoff
interface RetryConfig {
maxRetries: number;
baseDelayMs: number;
maxDelayMs: number;
jitterMs: number;
}
const defaultRetryConfig: RetryConfig = {
maxRetries: 3,
baseDelayMs: 1000,
maxDelayMs: 30000,
jitterMs: 500,
};
async function withRetry<T>(
operation: () => Promise<T>,
config: Partial<RetryConfig> = {}
): Promise<T> {
const { maxRetries, baseDelayMs, maxDelayMs, jitterMs } = {
...defaultRetryConfig,
...config,
};
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (err: any) {
if (attempt === maxRetries) throw err;
const status = err.response?.status;
if (status && status !== 429 && status < ) err;
delay = .(
baseDelayMs * .(, attempt) + .() * jitterMs,
maxDelayMs
);
.();
( (r, delay));
}
}
();
}
Step 4: Implement Transcript Processing Pipeline
import { z } from 'zod';
const SegmentSchema = z.object({
start: z.number(),
end: z.number(),
text: z.string(),
confidence: z.number().min(0).max(1),
speaker_id: z.string().optional(),
});
const TranscriptSchema = z.object({
id: z.string(),
text: z.string(),
duration_seconds: z.number().positive(),
language: z.string(),
segments: z.array(SegmentSchema),
speakers: z.array(z.object({
id: z.string(),
name: z.string().optional(),
})),
created_at: z.string().datetime(),
});
export type = z.< >;
= z.< >;
{
: < > = [];
(: ): {
..(processor);
;
}
(: ): {
..( (t), transcript);
}
}
= () =>
(: ): ({
...transcript,
: transcript..( s. >= threshold),
});
= () =>
(: ): {
: [] = [];
: | = ;
( segment transcript.) {
duration = (segment. - segment.) * ;
(duration < minDurationMs && buffer) {
buffer = {
...buffer,
: segment.,
: ,
: (buffer. + segment.) / ,
};
} {
(buffer) merged.(buffer);
buffer = { ...segment };
}
}
(buffer) merged.(buffer);
{ ...transcript, : merged };
};
pipeline = ()
.(())
.(());
Step 5: Implement Caching Layer
interface CacheEntry<T> {
data: T;
expiresAt: number;
}
class TranscriptCache {
private cache = new Map<string, CacheEntry<any>>();
private defaultTtlMs: number;
constructor(defaultTtlMs = 3600000) {
this.defaultTtlMs = defaultTtlMs;
}
set<T>(key: string, data: T, ttlMs?: number): void {
this.cache.set(key, {
data,
expiresAt: Date.now() + (ttlMs ?? this.defaultTtlMs),
});
}
get<T>(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
..(key);
;
}
entry. T;
}
getOrFetch<T>(
: ,
: <T>,
?:
): <T> {
cached = .<T>(key);
(cached) cached;
data = ();
.(key, data, ttlMs);
data;
}
(: ): {
..(key);
}
(): {
..();
}
}
transcriptCache = ();
Output
- Type-safe client singleton
- Robust error handling with custom error classes
- Automatic retry with exponential backoff
- Transcript processing pipeline
- Caching layer for API responses
Error Handling
| Pattern | Use Case | Benefit |
|---|
| Safe wrapper | All API calls | Prevents uncaught exceptions |
| Retry logic | Transient failures | Improves reliability |
| Pipeline processing | Transcript cleanup | Flexible data transformation |
| Caching | Repeated queries | Reduces API calls |
| Zod validation | Response parsing | Runtime type safety |
Examples
Python Context Manager
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import httpx
class TwinMindClient:
def __init__(self, api_key: str, base_url: str = "https://api.twinmind.com/v1"):
self.api_key = api_key
self.base_url = base_url
self._client: httpx.AsyncClient | None = None
async def __aenter__(self):
self._client = httpx.AsyncClient(
base_url=self.base_url,
headers={"Authorization": f"Bearer {self.api_key}"},
timeout=30.0,
)
return self
async def __aexit__(self, *args):
if self._client:
await self._client.aclose()
async def transcribe(self, audio_url: str) -> dict:
response = await self._client.post("/transcribe", json={: audio_url})
response.raise_for_status()
response.json()
() -> AsyncGenerator[TwinMindClient, ]:
TwinMindClient(os.environ[]) client:
client
():
get_twinmind_client() client:
transcript = client.transcribe(audio_url)
transcript
Multi-Tenant Factory Pattern
const clients = new Map<string, TwinMindClient>();
export function getClientForOrganization(orgId: string): TwinMindClient {
if (!clients.has(orgId)) {
const apiKey = getOrganizationApiKey(orgId);
clients.set(orgId, TwinMindClient.getInstance({
apiKey,
baseUrl: process.env.TWINMIND_API_URL,
}));
}
return clients.get(orgId)!;
}
function getOrganizationApiKey(orgId: string): string {
return process.env[`TWINMIND_API_KEY_${orgId}`] ||
process.env.TWINMIND_API_KEY!;
}
Resources
Next Steps
Apply patterns in twinmind-core-workflow-a for meeting transcription workflows.