---
title: "Classifier | Micdrop"
description: "Turn each turn of the user into typed decisions (intent, mood, yes or no) in a few hundred ms, and route the answer before the LLM writes a word."
url: "https://micdrop.dev/docs/server/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) › [Server (Node.js)](/docs/server)

# Classifier

A classifier reads each turn of the user and answers typed questions about it: which intent, how frustrated, whether they ask for a human. It runs next to the agent and leaves the answer to it.

Three things come out of it that an LLM gives slowly or at a high price:

*   **Decisions in a few hundred ms.** A small model answering a fixed set of questions is far faster than an LLM writing a reply, and costs a fraction of it.
*   **Signals for the page.** Each classification can reach the client, so an interface can show the intent or the mood of the caller, or act on it.
*   **Routing before the answer.** The server can wait for the classification of the turn, and the agent can pick who answers: the LLM, a scripted line, a human, or plain code.

## Usage

Pass a classifier to the server as `classifier`, next to the speech to text, the agent and the voice:

```
import { MicdropServer } from '@micdrop/server'import { choice, noul, TypesafeClassifier } from '@micdrop/typesafe'
const classifier = new TypesafeClassifier({  apiKey: process.env.TYPESAFE_API_KEY || '',  questions: {    intent: choice('What does the user in `turn` want?', {      billing: 'A charge, an invoice, a refund',      outage: 'The service is down or slow',      other: null,    }),    wantsHuman: noul('Does the user in `turn` ask for a human?'),  },})
new MicdropServer(socket, {  stt,  agent,  tts,  classifier,  classifierOptions: { sendToClient: true, waitBeforeAnswer: true },})
```

Micdrop ships [`TypesafeClassifier`](/docs/ai-integration/provided-integrations/typesafe), which runs TypeSafe’s Jev model. Any other model fits by extending the [`Classifier` base class](/docs/ai-integration/custom-integrations/custom-classifier), an LLM with structured output as much as a list of keywords.

The agent, the voice and even the answer are optional. A server with only a speech to text and a classifier turns each thing the user says into a typed command for the page.

## Options

`classifierOptions` sets how the server uses the classifier:

Option

Type

Default

Description

`sendToClient`

`boolean`

`false`

Sends every classification to the client as `Classification`.

`waitBeforeAnswer`

`boolean`

`false`

Holds each answer until the classification of its turn is done, so `onBeforeAnswer` can read it.

`maxWait`

`number`

`1000`

How long `waitBeforeAnswer` holds an answer, in ms. Past it, the answer goes on without the result.

`history`

`number`

`1`

How many turns of the user before this one the input holds, with the answers that followed them.

`sendToClient` stays off by default, since a result can hold what the user should not see, a manipulation score for instance. Without `waitBeforeAnswer`, the answer starts right away and the classification runs next to it.

## When it classifies

The server classifies each turn of the user when it ends. A turn can hold several transcripts: the speech to text sends one per pause while the user speaks, and the [turn detection](/docs/server/semantic-turn-detection) can hold the turn open for the rest of the sentence. The classifier reads them joined into one text.

When the user speaks again before the answer, the server cancels the answer and the classification in progress. The turn goes on, and the whole of it is classified again at its next end.

## What it reads

At the end of each turn, the server builds a `MicdropTurnInput`:

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

`history` holds the turn before and the answer that followed it, so words like “it” or “there” make sense:

```
{  "history": [    { "role": "user", "text": "My internet is down again." },    { "role": "assistant", "text": "Sorry to hear that. Since when?" }  ],  "turn": "Since this morning. And this is the third time this month."}
```

Raise `history` to give it more turns, or set it to `0` to classify the turn on its own. The exported `turnInput(conversation, history)` builds the same input from any conversation.

## Routing the answer

The server keeps each classification in the metadata of the last user message of its turn, under `classification`. `getTurnClassification(conversation)` reads the one of the last turn, and returns `undefined` until a classification has read the whole turn.

With `waitBeforeAnswer`, the agent calls `onBeforeAnswer` once the classification is done. The hook reads it and decides who answers. Returning a string gives that text as the answer, spoken without the LLM. Returning nothing lets the LLM answer:

```
import { getTurnClassification, MicdropServer } from '@micdrop/server'
const agent = new OpenaiAgent({  apiKey: process.env.OPENAI_API_KEY || '',  systemPrompt: 'You are the support assistant of Nova Fiber.',
  onBeforeAnswer() {    const classification = getTurnClassification(this.conversation)    // The LLM answers when the classification is late    if (!classification) return
    const { answers } = classification.result    if (answers.wantsHuman.noul > 0.8) {      return 'I am transferring you to a colleague right away.'    }  },})
new MicdropServer(socket, {  stt,  agent,  tts,  classifier,  classifierOptions: { waitBeforeAnswer: true },})
```

The wait runs in the queue of the server, so an answer cancelled meanwhile is dropped with it. Past `maxWait`, the answer goes on and `getTurnClassification` returns `undefined` for that turn, which leaves the answer to the LLM.

The same hook can do more than replace the LLM. Adding a system message with `this.addMessage('system', hint)` lets the LLM answer with a hint the classifier gave it, such as a customer thinking of leaving.

## Reading it in the client

With `sendToClient: true`, each classification reaches the client as a `Classification` event:

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

In React, the [`useMicdropClassification`](/docs/client/react-hooks#usemicdropclassification) hook subscribes to it:

```
import { useMicdropClassification } from '@micdrop/react'import { useCallback, useState } from 'react'
function IntentBadge() {  const [intent, setIntent] = useState<string>()
  useMicdropClassification(    useCallback(({ result }) => setIntent(result.answers.intent.choice), [])  )
  return <span>{intent}</span>}
```

It works the same with `@micdrop/web` and `@micdrop/react-native`.

The payload is a `MicdropClassification`:

```
interface MicdropClassification<Result = any, Input = any> {  input: Input // What was classified, a MicdropTurnInput in a call  result: Result // Answers, in the shape the provider returns  duration: number // Time the classification took, in ms}
```

## With a realtime model

A [realtime model](/docs/server/realtime) takes a classifier as well. Its transcript of the user turn comes in one piece, and the server classifies each turn as it lands. The model answers on its own, so `waitBeforeAnswer` applies to the speech to text, agent and voice pipeline. With a realtime model, the classifier feeds the client and your own code.

## Demos

The demos that run on a classifier are listed with the other examples, in [Examples and demos](/docs/examples#classifier-demos).

[Previous← Extract Value from Answer](/docs/server/extract)[NextAuto End Call →](/docs/server/auto-end-call)

On this page

*   [Usage](#usage)
*   [Options](#options)
*   [When it classifies](#when-it-classifies)
*   [What it reads](#what-it-reads)
*   [Routing the answer](#routing-the-answer)
*   [Reading it in the client](#reading-it-in-the-client)
*   [With a realtime model](#with-a-realtime-model)
*   [Demos](#demos)
