Documentation Index Fetch the complete documentation index at: https://mintlify.com/visible/cruel/llms.txt
Use this file to discover all available pages before exploring further.
cruelModel() wraps individual AI SDK v6 model instances to inject chaos into both generation and streaming operations.
API Reference
function cruelModel < T extends LanguageModelV3 >(
model : T ,
options ?: CruelModelOptions
) : T
The AI SDK model instance to wrap. Works with any provider:
@ai-sdk/openai
@ai-sdk/anthropic
@ai-sdk/google
@ai-sdk/mistral
@ai-sdk/gateway
Any AI SDK v6 compatible provider
Returns a wrapped model with the same type signature as the input model.
Basic Usage
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'
import { cruelModel } from 'cruel/ai-sdk'
const model = cruelModel ( openai ( 'gpt-4' ), {
rateLimit: 0.2 ,
overloaded: 0.1 ,
delay: [ 100 , 500 ],
})
const result = await generateText ({
model ,
prompt: 'Explain distributed systems' ,
})
Chaos Options
API Errors
Simulate common API failures:
rateLimit
number | { rate: number; retryAfter?: number }
Probability of rate limit errors (0-1). // Simple: 20% chance of rate limit
rateLimit : 0.2
// Advanced: Custom retry-after header
rateLimit : { rate : 0.2 , retryAfter : 30 }
Throws: CruelAPIError with statusCode: 429
Probability of model overload errors (0-1). overloaded : 0.1 // 10% chance
Throws: CruelAPIError with statusCode: 529
Probability of invalid API key errors (0-1). invalidApiKey : 0.05 // 5% chance
Throws: CruelAPIError with statusCode: 401, not retryable
Probability of quota exceeded errors (0-1). quotaExceeded : 0.03 // 3% chance
Throws: CruelAPIError with statusCode: 402, not retryable
Probability of model unavailable errors (0-1). modelUnavailable : 0.08 // 8% chance
Throws: CruelAPIError with statusCode: 503
Content Issues
Probability of context length exceeded errors (0-1). contextLength : 0.05 // 5% chance
Throws: CruelAPIError with statusCode: 400, not retryable
Probability of content filter errors (0-1). contentFilter : 0.02 // 2% chance
Throws: CruelAPIError with statusCode: 400, not retryable
Probability of empty response errors (0-1). emptyResponse : 0.01 // 1% chance
Throws: CruelAPIError with statusCode: 200, not retryable
Probability of truncated text responses (0-1). Only applies to generateText(). partialResponse : 0.1 // 10% chance
Returns truncated content (10-70% of original length)
Latency
delay
number | [number, number]
Add latency before responses. // Fixed delay
delay : 500 // Always 500ms
// Random delay range
delay : [ 100 , 1000 ] // Between 100-1000ms
Probability of request timeouts (0-1). Request never completes. timeout : 0.05 // 5% chance of timeout
Streaming Chaos
Options that only affect streamText() and streaming operations:
Probability of stream interruption during token streaming (0-1). streamCut : 0.1 // 10% chance of stream cut
Throws: CruelAPIError mid-stream
slowTokens
number | [number, number]
Add delay between streamed tokens (milliseconds). // Fixed delay per token
slowTokens : 50 // 50ms per token
// Random delay per token
slowTokens : [ 20 , 100 ] // 20-100ms per token
Probability of corrupted text chunks (0-1). Injects replacement character (U+FFFD). corruptChunks : 0.05 // 5% chance per chunk
Response Modification
finishReason
'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'
Override the finish reason in responses. finishReason : 'length' // Always report max tokens reached
tokenUsage
{ inputTokens?: number; outputTokens?: number }
Override token usage reporting. tokenUsage : {
inputTokens : 999 ,
outputTokens : 999 ,
}
Generic Failures
Probability of generic generation failure (0-1). Throws: CruelAPIError with statusCode: 500
Event Monitoring
onChaos
(event: ChaosEvent) => void
Callback fired when chaos is injected. onChaos : ( event ) => {
console . log ( `[ ${ event . modelId } ] ${ event . type } ` , event )
}
See ChaosEvent types
Streaming Example
import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'
import { cruelModel } from 'cruel/ai-sdk'
const model = cruelModel ( openai ( 'gpt-4' ), {
slowTokens: [ 20 , 80 ],
streamCut: 0.1 ,
corruptChunks: 0.02 ,
onChaos : ( event ) => {
if ( event . type === 'slowTokens' ) {
console . log ( `Token delay: ${ event . ms } ms` )
}
},
})
const result = streamText ({
model ,
prompt: 'Write a haiku about testing' ,
})
try {
for await ( const chunk of result . textStream ) {
process . stdout . write ( chunk )
}
} catch ( error ) {
console . error ( 'Stream error:' , error . message )
}
Other Model Types
Cruel provides specialized wrappers for all AI SDK model types:
Embedding Models
import { openai } from '@ai-sdk/openai'
import { embed } from 'ai'
import { cruelEmbeddingModel } from 'cruel/ai-sdk'
const model = cruelEmbeddingModel ( openai . embedding ( 'text-embedding-3-small' ), {
rateLimit: 0.1 ,
delay: [ 50 , 150 ],
})
const result = await embed ({
model ,
value: 'Hello world' ,
})
Image Models
import { openai } from '@ai-sdk/openai'
import { experimental_generateImage } from 'ai'
import { cruelImageModel } from 'cruel/ai-sdk'
const model = cruelImageModel ( openai . image ( 'dall-e-3' ), {
overloaded: 0.2 ,
delay: [ 1000 , 3000 ],
})
const result = await experimental_generateImage ({
model ,
prompt: 'A sunset over mountains' ,
})
Speech Models
import { openai } from '@ai-sdk/openai'
import { experimental_generateSpeech } from 'ai'
import { cruelSpeechModel } from 'cruel/ai-sdk'
const model = cruelSpeechModel ( openai . speech ( 'tts-1' ), {
rateLimit: 0.15 ,
delay: [ 500 , 1500 ],
})
const result = await experimental_generateSpeech ({
model ,
text: 'Hello from Cruel' ,
})
Transcription Models
import { openai } from '@ai-sdk/openai'
import { experimental_transcribe } from 'ai'
import { cruelTranscriptionModel } from 'cruel/ai-sdk'
const model = cruelTranscriptionModel ( openai . transcription ( 'whisper-1' ), {
overloaded: 0.1 ,
delay: [ 200 , 800 ],
})
const result = await experimental_transcribe ({
model ,
audio: audioBuffer ,
})
Video Models
import { cruelVideoModel } from 'cruel/ai-sdk'
const model = cruelVideoModel ( someProvider . video ( 'video-model' ), {
delay: [ 2000 , 5000 ],
overloaded: 0.3 ,
})
Embedding, Image, Speech, Transcription, and Video models support a subset of chaos options:
rateLimit, overloaded, invalidApiKey, quotaExceeded, delay, timeout, and fail. Streaming-specific options like streamCut and slowTokens only apply to language models.
Model ID Override
Cruel respects the MODEL environment variable for testing different models:
# Override model in wrapped instances
MODEL = gpt-3.5-turbo npm test
// Original: openai('gpt-4')
// With MODEL=gpt-3.5-turbo: openai('gpt-3.5-turbo')
const model = cruelModel ( openai ( 'gpt-4' ), { rateLimit: 0.1 })
For provider syntax:
# Override just the model name, keep provider
MODEL = gpt-3.5-turbo npm test
// Original: gateway('openai/gpt-4')
// With MODEL=gpt-3.5-turbo: gateway('openai/gpt-3.5-turbo')
const model = cruelModel ( gateway ( 'openai/gpt-4' ), { rateLimit: 0.1 })
Error Types
All errors thrown by cruelModel are instances of CruelAPIError:
import { CruelAPIError } from 'cruel/ai-sdk'
try {
const result = await generateText ({ model , prompt: 'Hello' })
} catch ( error ) {
if ( error instanceof CruelAPIError ) {
console . log ( 'Status:' , error . statusCode ) // HTTP status code
console . log ( 'Retryable:' , error . isRetryable ) // Can AI SDK retry?
console . log ( 'Message:' , error . message ) // Error description
console . log ( 'Data:' , error . data ) // Additional data (e.g., retryAfter)
}
}
See CruelAPIError source for implementation details.
TypeScript
cruelModel preserves the exact type of the wrapped model:
import { openai } from '@ai-sdk/openai'
import type { LanguageModelV3 } from 'cruel/ai-sdk'
const original = openai ( 'gpt-4' )
const wrapped = cruelModel ( original , { rateLimit: 0.1 })
// Both have identical types
type Original = typeof original // LanguageModelV3
type Wrapped = typeof wrapped // LanguageModelV3
Next Steps
Provider Wrapping Apply chaos at the provider level
Middleware Use AI SDK middleware for chaos
Tool Chaos Inject failures into tools
Presets Use built-in presets