Skip to main content

ClassifierProcessor

The ClassifierProcessor is a hybrid processor that runs a Classifier over message text and passes the typed answers to onResult. In onResult, call abort(reason) to stop the request with a tripwire, call filter() to drop the content and continue, or do nothing to let it through. The classifier provides evidence such as probabilities, choices, and scores. onResult owns application policy.

Abort reasons are always the string supplied by the caller. Provider-generated text is never included in the abort message.

Usage example
Direct link to Usage example

import { Classifier } from '@mastra/core/classifier'
import { ClassifierProcessor } from '@mastra/core/processors'

const safety = new Classifier({
id: 'safety',
model,
questions: {
unsafe: {
type: 'boolean',
criteria: { true: 'The message is unsafe', false: 'The message is safe' },
},
},
})

const processor = new ClassifierProcessor({
classifier: safety,
onResult: (answers, { abort }) => {
if (answers.unsafe.probability > 0.8) abort('Message rejected by safety policy')
},
lastMessageOnly: true,
})

Several conditions can share one onResult, or use separate processors when they need different classifiers. Give each processor a unique id. Processor instances with the same id share per-run state, including accumulated stream chunks.

inputProcessors: [
new ClassifierProcessor({
id: 'safety-check',
classifier: safety,
onResult: (a, { abort }) => {
if (a.unsafe.probability > 0.8) abort('Rejected by safety policy')
},
}),
new ClassifierProcessor({
id: 'topic-check',
classifier: topic,
onResult: (a, { abort }) => {
if (a.topic.choice === 'other') abort('Support questions only')
},
}),
]

Constructor parameters
Direct link to Constructor parameters

options:

ClassifierProcessorOptions
Configuration for the processor
ClassifierProcessorOptions

classifier:

Classifier | string
A Classifier instance with configured questions, or the key or ID of a classifier registered on the Mastra instance. Registered classifiers are resolved lazily through mastra.getClassifierById() on first use.

onResult:

(answers, context) => void | Promise<void>
Called after each classification. answers is typed from the classifier questions. context provides abort(reason) to stop the request with a tripwire, filter() to drop the message or chunk, phase, and the full ClassifierResult including usage and provider metadata.

id?:

string
Processor identifier. Use a unique ID for each ClassifierProcessor in the same phase because instances with the same ID share per-run processor state.

errorStrategy?:

'warn' | 'strict'
How to handle evaluation model failures. 'strict' is fail-closed: it aborts the request, including during streaming. 'warn' is fail-open: it logs the failure and lets the content through. Classifier configuration errors always throw.

lastMessageOnly?:

boolean
When true, only the last message is classified in the input and output phases.

chunkWindow?:

number
Non-negative integer number of trailing stream chunks to classify together, including the current text-delta chunk. A value of 0 classifies only the current chunk. Each text chunk triggers a classifier call, so larger windows increase cost.

maxInputLength?:

number
Non-negative integer maximum number of characters sent to the classifier. Longer text is truncated.

providerOptions?:

SharedV4ProviderOptions
Provider-specific options forwarded to the evaluation model.

Behavior by phase
Direct link to Behavior by phase

Phaseno callabort(reason)filter()
Input (inputProcessors)message passesabort before the LLMmessage removed from context
Output result (outputProcessors)message passesabort the responsemessage removed from the response
Stream (outputProcessors)chunk emittedabort the streamchunk not emitted

Messages with no text content are passed through without calling the classifier.

Extended usage examples
Direct link to Extended usage examples

Topic scoping
Direct link to Topic scoping

src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { Classifier } from '@mastra/core/classifier'
import { ClassifierProcessor } from '@mastra/core/processors'

const topic = new Classifier({
id: 'topic',
model,
questions: {
topic: {
type: 'choice',
criteria: {
billing: 'Questions about invoices or payments',
account: 'Questions about account settings',
other: 'Anything else',
},
},
},
})

export const agent = new Agent({
id: 'support-agent',
name: 'support-agent',
instructions: 'You help customers with billing and account questions.',
model: 'openai/gpt-5.6-sol',
inputProcessors: [
new ClassifierProcessor({
classifier: topic,
lastMessageOnly: true,
onResult: (answers, { abort }) => {
if (answers.topic.choice === 'other') {
abort('This assistant only handles billing and account questions')
}
},
}),
],
})

Output quality gate
Direct link to Output quality gate

src/mastra/agents/quality-gated-agent.ts
import { Agent } from '@mastra/core/agent'
import { Classifier } from '@mastra/core/classifier'
import { ClassifierProcessor } from '@mastra/core/processors'

const quality = new Classifier({
id: 'quality',
model,
questions: {
quality: {
type: 'score',
instructions: 'Rate how well the response answers the user',
criteria: ['Off-topic', 'Partial', 'Complete'],
},
},
})

export const agent = new Agent({
id: 'quality-gated-agent',
name: 'quality-gated-agent',
instructions: 'You are a helpful assistant',
model: 'openai/gpt-5.6-sol',
outputProcessors: [
new ClassifierProcessor({
classifier: quality,
onResult: (answers, { filter }) => {
if (answers.quality.score < 1) filter()
},
}),
],
})

Registered classifier
Direct link to Registered classifier

Register the classifier on the Mastra instance and reference it by key or ID. The processor resolves it on first use.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { Classifier } from '@mastra/core/classifier'

export const mastra = new Mastra({
classifiers: {
safety: new Classifier({ id: 'safety', model, questions: {/* ... */} }),
},
})
src/mastra/processors/safety-processor.ts
import { ClassifierProcessor } from '@mastra/core/processors'

type SafetyQuestions = {
unsafe: { type: 'boolean' }
}

const processor = new ClassifierProcessor<SafetyQuestions>({
classifier: 'safety',
onResult: (answers, { abort }) => {
if (answers.unsafe.probability > 0.8) abort('Message rejected by safety policy')
},
})

When using a registered classifier by string, answer types aren't inferred from the registered instance. Pass its question map as the ClassifierProcessor type argument to type the answers parameter.