examples/processors-response-length-limiter/README.md
This example shows how to create a custom output processor that monitors and limits the length of AI responses during streaming. This processor tracks cumulative response length and aborts generation when a specified character limit is reached, helping control costs and response quality.
A custom output processor in Mastra implements the Processor interface with the processOutputStream method for streaming responses. This processor tracks the cumulative length of text deltas and terminates the stream when the limit is exceeded.
import type { Processor } from '@mastra/core/processors';
import type { ChunkType } from '@mastra/core/stream';
type ResponseLengthLimiterOptions = {
maxLength?: number;
strategy?: 'block' | 'warn' | 'truncate';
};
export class ResponseLengthLimiter implements Processor {
readonly id = 'response-length-limiter';
readonly name = 'Response Length Limiter';
private maxLength: number;
private strategy: 'block' | 'warn' | 'truncate';
constructor(options: ResponseLengthLimiterOptions | number = {}) {
if (typeof options === 'number') {
this.maxLength = options;
this.strategy = 'block';
} else {
this.maxLength = options.maxLength ?? 1000;
this.strategy = options.strategy ?? 'block';
}
}
async processOutputStream({
part,
streamParts,
state,
abort,
}: {
part: ChunkType;
streamParts: ChunkType[];
state: Record<string, unknown>;
abort: (reason?: string) => never;
}): Promise<ChunkType | null | undefined> {
if (!state.cumulativeLength) {
state.cumulativeLength = 0;
}
if (part.type === 'text-delta') {
const newLength = state.cumulativeLength + part.payload.text.length;
if (newLength > this.maxLength) {
switch (this.strategy) {
case 'block':
abort(`Response too long: ${newLength} characters (max: ${this.maxLength})`);
break;
case 'warn':
console.warn(
`Warning: Response length ${newLength} exceeds recommended limit of ${this.maxLength} characters`,
);
state.cumulativeLength = newLength;
return part;
case 'truncate':
const remainingChars = this.maxLength - state.cumulativeLength;
if (remainingChars > 0) {
const truncatedText = part.payload.text.substring(0, remainingChars);
state.cumulativeLength = this.maxLength;
return {
...part,
payload: { ...part.payload, text: truncatedText },
};
}
return null;
}
}
state.cumulativeLength = newLength;
}
return part;
}
}
'block': Stop generation and abort the stream (default)'warn': Log warning but continue streaming'truncate': Cut off text at the exact limittext-delta parts in the character limitUsing the options object approach with explicit strategy configuration:
import { Agent } from '@mastra/core/agent';
import { ResponseLengthLimiter } from '../processors/response-length-limiter';
export const blockingAgent = new Agent({
id: 'blocking-agent',
name: 'Blocking Agent',
instructions: 'You are a helpful assistant with response length limits',
model: 'openai/gpt-5.1',
outputProcessors: [new ResponseLengthLimiter({ maxLength: 1000, strategy: 'block' })],
});
Using the simple number approach (defaults to 'block' strategy):
import { Agent } from '@mastra/core/agent';
import { ResponseLengthLimiter } from '../processors/response-length-limiter';
export const simpleAgent = new Agent({
id: 'simple-agent',
name: 'Simple Agent',
instructions: 'You are a helpful assistant',
model: 'openai/gpt-5.1',
outputProcessors: [new ResponseLengthLimiter(300)],
});
This example shows a response that stays within the configured character limit and streams successfully to completion.
import { Agent } from '@mastra/core/agent';
import { ResponseLengthLimiter } from './mastra/processors/response-length-limiter';
// Create agent with generous response limit
export const agent = new Agent({
id: 'response-limited-agent',
name: 'Response Limited Agent',
instructions: 'You are a helpful assistant. Keep responses concise.',
model: 'openai/gpt-5.1',
outputProcessors: [
new ResponseLengthLimiter(300), // 300 character limit
],
});
const result = await agent.generate('What is the capital of France?');
console.log(result.text);
console.log('Character count:', result.text.length);
The response completes successfully because it stays under the 300-character limit:
"The capital of France is Paris. It's located in the north-central part of the country and serves as the political, economic, and cultural center of France."
Character count: 156
This example shows what happens when a response reaches exactly the character limit during generation.
import { Agent } from '@mastra/core/agent';
import { ResponseLengthLimiter } from './mastra/processors/response-length-limiter';
// Reuse same agent but with stricter response limit
export const agent = new Agent({
id: 'response-limited-agent',
name: 'Response Limited Agent',
instructions: 'You are a helpful assistant.',
model: 'openai/gpt-5.1',
outputProcessors: [
new ResponseLengthLimiter(200), // Strict 200 character limit
],
});
const result = await agent.generate('Explain machine learning in detail.');
if (result.tripwire) {
console.log('Response blocked:', result.tripwire.reason);
console.log('Partial response received:', result.text);
} else {
console.log(result.text);
}
console.log('Character count:', result.text.length);
The response is cut off when it hits the 200-character limit:
Response blocked: Response too long: 201 characters (max: 200)
Partial response received: "Machine learning is a subset of artificial intelligence that enables computers to learn and improve from experience without being explicitly programmed. It uses algori"
Character count: 200
This example demonstrates streaming behavior when the response limit is exceeded.
import { Agent } from '@mastra/core/agent';
import { ResponseLengthLimiter } from './mastra/processors/response-length-limiter';
// Reuse same agent but with very strict response limit
export const agent = new Agent({
id: 'response-limited-agent',
name: 'Response Limited Agent',
instructions: 'You are a verbose assistant who provides detailed explanations.',
model: 'openai/gpt-5.1',
outputProcessors: [
new ResponseLengthLimiter(100), // Very strict 100 character limit
],
});
const stream = await agent.stream('Write a comprehensive essay about artificial intelligence.');
let responseText = '';
let wasBlocked = false;
let blockReason = '';
for await (const part of stream.fullStream) {
if (part.type === 'text-delta') {
responseText += part.payload.text;
process.stdout.write(part.payload.text);
} else if (part.type === 'tripwire') {
wasBlocked = true;
blockReason = part.payload.tripwire.reason;
console.log('\n\nStream blocked:', blockReason);
break;
}
}
if (wasBlocked) {
console.log('Final response length:', responseText.length);
console.log('Reason:', blockReason);
}
The stream is blocked when the response exceeds the 100-character limit:
Artificial intelligence represents one of the most transformative technologies of our time. It encom
Stream blocked: Response too long: 101 characters (max: 100)
Final response length: 100
Reason: Response too long: 101 characters (max: 100)
When using ResponseLengthLimiter, the processor:
result.tripwire = true or stream emits tripwire chunkresult.tripwire or handle tripwire chunks in streamsThis processor is particularly valuable for applications that need predictable response lengths, whether for cost management, user interface constraints, or maintaining consistent response quality.