---
title: "Classifier | Micdrop"
description: "The Classifier class turns an input into typed decisions. Extend it to plug in any model, from an LLM with structured output to a list of keywords, in a call or on its own."
url: "https://micdrop.dev/docs/ai-integration/custom-integrations/custom-classifier"
---

*   [Getting Started](/docs/getting-started)
*   [Examples and demos](/docs/examples)
*   [Client (Browser)](/docs/client)
    
    *   [Installation](/docs/client/installation)
    *   [React Hooks](/docs/client/react-hooks)
    *   [Start/Stop Call](/docs/client/start-stop-call)
    *   [Pause/Resume Call](/docs/client/pause-resume-call)
    *   [Mute/Unmute Call](/docs/client/mute-unmute-call)
    *   [Call State](/docs/client/call-state)
    *   [Display Conversation Messages](/docs/client/display-conversation-messages)
    *   [Handling Tool Calls](/docs/client/handling-tool-calls)
    *   [Device Management](/docs/client/devices-management)
    *   [Voice Activity Detection (VAD)](/docs/client/vad)
    *   [Turn Detection](/docs/client/turn-detection)
    *   [Reducing Latency](/docs/client/latency)
    *   [Error Handling](/docs/client/error-handling)
    *   Utility Classes
        
        *   [Mic](/docs/client/utility-classes/mic)
        *   [MicdropClient](/docs/client/utility-classes/micdrop-client)
        *   [MicRecorder](/docs/client/utility-classes/mic-recorder)
        *   [Speaker](/docs/client/utility-classes/speaker)
        
    
*   [Client (React Native)](/docs/react-native)
    
    *   [Installation](/docs/react-native/installation)
    *   [Hooks and Call State](/docs/react-native/hooks)
    *   [Audio Output and Devices](/docs/react-native/audio-output)
    *   [Voice Activity Detection (VAD)](/docs/react-native/vad)
    *   [Turn Detection](/docs/react-native/turn-detection)
    *   [Using Another Audio Library](/docs/react-native/custom-audio)
    
*   [Server (Node.js)](/docs/server)
    
    *   [Installation](/docs/server/installation)
    *   [With Fastify](/docs/server/with-fastify)
    *   [With NestJS](/docs/server/with-nestjs)
    *   [Auth and Parameters](/docs/server/auth-and-parameters)
    *   [First Message](/docs/server/first-message)
    *   [Dictation and Text-Only Calls](/docs/server/dictation)
    *   [Realtime Models](/docs/server/realtime)
    *   [Partial Messages](/docs/server/partial-messages)
    *   [Save Messages](/docs/server/save-messages)
    *   [Resume a Conversation](/docs/server/resume-conversation)
    *   [Recording Audio](/docs/server/recording-audio)
    *   [Error Handling](/docs/server/error-handling)
    *   [Tools](/docs/server/tools)
    *   [Extract Value from Answer](/docs/server/extract)
    *   [Classifier](/docs/server/classifier)
    *   [Auto End Call](/docs/server/auto-end-call)
    *   [Semantic Turn Detection](/docs/server/semantic-turn-detection)
    *   [Noise Filtering](/docs/server/noise-filtering)
    *   [Micdrop Protocol](/docs/server/protocol)
    
*   [AI Integrations](/docs/ai-integration)
    
    *   Provided Integrations
        
        *   [AI SDK](/docs/ai-integration/provided-integrations/ai-sdk)
        *   [Cartesia](/docs/ai-integration/provided-integrations/cartesia)
        *   [ElevenLabs](/docs/ai-integration/provided-integrations/elevenlabs)
        *   [Gemini](/docs/ai-integration/provided-integrations/gemini)
        *   [Gladia](/docs/ai-integration/provided-integrations/gladia)
        *   [Gradium](/docs/ai-integration/provided-integrations/gradium)
        *   [Kokoro](/docs/ai-integration/provided-integrations/kokoro)
        *   [Mistral](/docs/ai-integration/provided-integrations/mistral)
        *   [OpenAI](/docs/ai-integration/provided-integrations/openai)
        *   [Piper](/docs/ai-integration/provided-integrations/piper)
        *   [Pocket TTS](/docs/ai-integration/provided-integrations/pocket-tts)
        *   [Qwen3-TTS](/docs/ai-integration/provided-integrations/qwen-tts)
        *   [TypeSafe](/docs/ai-integration/provided-integrations/typesafe)
        *   [Whisper](/docs/ai-integration/provided-integrations/whisper)
        
    *   Custom Integrations
        
        *   [Agent (LLM)](/docs/ai-integration/custom-integrations/custom-agent)
        *   [Speech-to-Text (STT)](/docs/ai-integration/custom-integrations/custom-stt)
        *   [Text-to-Speech (TTS)](/docs/ai-integration/custom-integrations/custom-tts)
        *   [Classifier](/docs/ai-integration/custom-integrations/custom-classifier)
        
    *   Fallback Strategies
        
        *   [FallbackAgent](/docs/ai-integration/fallback-strategies/agent-fallback)
        *   [FallbackSTT](/docs/ai-integration/fallback-strategies/stt-fallback)
        *   [FallbackTTS](/docs/ai-integration/fallback-strategies/tts-fallback)
        
    *   [Local Models](/docs/ai-integration/local-models)
        
        *   [Local LLM](/docs/ai-integration/local-models/agent)
        *   [Local STT](/docs/ai-integration/local-models/speech-to-text)
        *   [Local TTS](/docs/ai-integration/local-models/text-to-speech)
        *   [Latency and Memory](/docs/ai-integration/local-models/performance)
        *   [Explorations](/docs/ai-integration/local-models/explorations)
            
            *   [MiniCPM5-2B](/docs/ai-integration/local-models/explorations/minicpm)
            *   [Mistral 7B](/docs/ai-integration/local-models/explorations/mistral-7b)
            *   [Voxtral Mini 3B](/docs/ai-integration/local-models/explorations/voxtral-stt)
            *   [Voxtral TTS 4B](/docs/ai-integration/local-models/explorations/voxtral-tts)
            *   [AuK and AuK-Flash](/docs/ai-integration/local-models/explorations/auk)
            
        
    *   [IA Vocale Souveraine 🇫🇷🇪🇺](/docs/ai-integration/sovereign-voice-ai)
    
*   [Migration](/docs/migration)
    
    *   [Upgrade to v3](/docs/migration/v3)
    

[Micdrop](/) › [Documentation](/docs/getting-started) › [AI Integrations](/docs/ai-integration) › Custom Integrations

# Classifier

The `Classifier` class is the abstraction behind the [`classifier`](/docs/server/classifier) option of the server. It reads an input and returns a typed result: an intent, a score, a yes or no.

In a call, the server classifies each turn of the user when it ends. The class works on its own just as well: call `classify()` with any text or JSON.

The base class keeps track of the classifications in progress, so the server can wait for them or cancel them. A provider implements a single method, `evaluate()`.

## Available Implementations

*   [TypesafeClassifier](https://github.com/Godefroy/micdrop/blob/main/packages/typesafe/src/TypesafeClassifier.ts) from [@micdrop/typesafe](/docs/ai-integration/provided-integrations/typesafe)

## Overview

```
// What a classifier reads: a text, or JSON with named fieldsexport type ClassifierInput = string | { [key: string]: any } | any[]
export abstract class Classifier<  Result = any,  Input extends ClassifierInput = ClassifierInput,> extends EventEmitter<ClassifierEvents<Result, Input>> {  public logger?: Logger
  // The last classification  public lastClassification?: MicdropClassification<Result, Input>
  // Answers the questions of this classifier about an input  protected abstract evaluate(input: Input, signal: AbortSignal): Promise<Result>
  // Classifies an input, and emits the result as Classification  classify(input: Input): Promise<MicdropClassification<Result, Input> | undefined>
  // The last classification started, until it is done  get pending(): Promise<MicdropClassification<Result, Input> | undefined> | undefined
  // Drops every classification in progress, which then emit nothing  cancel(): void
  destroy(): void}
```

`classify()` resolves with the classification, or with `undefined` when it was cancelled or failed. A failure goes to the logger, and the call carries on without it.

The `signal` of `evaluate()` is aborted once the result is no longer wanted: the user spoke again and the turn will be classified whole at its next end, or the call ended. Pass it to your HTTP client so the request stops too.

The constructor takes no options. A provider declares its own, and the server reads its settings from [`classifierOptions`](/docs/server/classifier#options).

## The input in a call

`MicdropServer` classifies a `MicdropTurnInput`:

```
interface MicdropTurnInput {  // The turn before, and the answer that followed it  history: Array<{ role: 'user' | 'assistant'; text: string }>  // What the user said in this turn, transcripts joined  turn: string}
```

A classifier made for calls types its input with it, and reads `turn` and `history` by name. A classifier accepting any `ClassifierInput` receives the same object, and can pass it as is to a model that reads JSON.

## The result

`Result` is whatever `evaluate()` returns. Each classification wraps it:

```
interface MicdropClassification<Result = any, Input = any> {  input: Input // What was classified  result: Result // What evaluate() returned  duration: number // Time the classification took, in ms}
```

In a call, the server keeps it in the metadata of the last user message of the turn, and can send it to the client as JSON, so keep `Result` serializable.

## Events

### Classification

Emitted for each classification that completes. A cancelled one emits nothing.

```
classifier.on('Classification', ({ input, result, duration }) => {  console.log(input, result, `${duration} ms`)})
```

## Debug Logging

```
classifier.logger = new Logger('Classifier')
```

Each classification is then logged with its input and duration.

## Custom Classifier Implementation

### With an LLM and structured output

Any LLM able to return a typed object makes a classifier. This one uses the [AI SDK](https://ai-sdk.dev) with a zod schema, and reads the turn and its history:

```
import { openai } from '@ai-sdk/openai'import { Classifier, MicdropTurnInput } from '@micdrop/server'import { generateText, Output } from 'ai'import { z } from 'zod'
const schema = z.object({  intent: z.enum(['billing', 'outage', 'cancel', 'other']),  frustrated: z.boolean(),})
type Decision = z.infer<typeof schema>
export class LlmClassifier extends Classifier<Decision, MicdropTurnInput> {  protected async evaluate(    { history, turn }: MicdropTurnInput,    signal: AbortSignal  ): Promise<Decision> {    const context = history      .map(({ role, text }) => `${role}: ${text}`)      .join('\n')
    const { output } = await generateText({      model: openai('gpt-5-mini'),      output: Output.object({ schema }),      system:        'Classify the last turn of a customer calling an internet provider.',      prompt: `${context}\n\nCustomer: ${turn}`,      abortSignal: signal,    })    return output  }}
```

```
new MicdropServer(socket, {  stt,  agent,  tts,  classifier: new LlmClassifier(),  classifierOptions: { waitBeforeAnswer: true, maxWait: 1500 },})
```

A general LLM takes longer than a dedicated classification model, often over a second. Pick a small model, and give `maxWait` a limit that matches its latency.

### With keywords

A classifier needs no model at all. This one flags a few words, in well under a millisecond, and accepts a plain text as well as a turn:

```
import { Classifier, MicdropTurnInput } from '@micdrop/server'
export class KeywordClassifier extends Classifier<  { wantsHuman: boolean },  string | MicdropTurnInput> {  protected async evaluate(input: string | MicdropTurnInput) {    const text = typeof input === 'string' ? input : input.turn    return {      wantsHuman: /\b(human|agent|advisor|manager|real person)\b/i.test(text),    }  }}
```

Keywords miss every sentence phrased another way, which is where a model earns its place. They make a fine first layer, or a test double.

## Usage without MicdropServer

`classify()` takes any input the classifier accepts, and resolves with its classification:

```
const classifier = new KeywordClassifier()
const classification = await classifier.classify('Can I talk to a human?')console.log(classification?.result.wantsHuman) // true
await classifier.classify({  history: [],  turn: 'My internet is down, get me a manager.',})
```

To classify a conversation you hold yourself, `turnInput(conversation, history)` from `@micdrop/server` builds the same `MicdropTurnInput` as the server. It returns the input, and the last message of the turn:

```
import { turnInput } from '@micdrop/server'
const { input } = turnInput(conversation, 1)await classifier.classify(input)
```

[Previous← Text-to-Speech (TTS)](/docs/ai-integration/custom-integrations/custom-tts)[NextFallbackAgent →](/docs/ai-integration/fallback-strategies/agent-fallback)

On this page

*   [Available Implementations](#available-implementations)
*   [Overview](#overview)
*   [The input in a call](#the-input-in-a-call)
*   [The result](#the-result)
*   [Events](#events)
*   [Classification](#classification)
*   [Debug Logging](#debug-logging)
*   [Custom Classifier Implementation](#custom-classifier-implementation)
*   [With an LLM and structured output](#with-an-llm-and-structured-output)
*   [With keywords](#with-keywords)
*   [Usage without MicdropServer](#usage-without-micdropserver)
