> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpeek.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Classify

> Classify documents at query time using built-in tasks (like NSFW content safety) or a custom model deployed as a plugin

The Classify stage labels documents at query time. It supports two modes:

* **Built-in tasks** (via `task`) — managed classifiers Mixpeek hosts for you. The first available task is `nsfw`, a content-safety classifier for text, image, and video. No plugin to build or deploy.

* **Custom classifiers** (via `feature_uri`) — your own classifier model deployed as a [custom extractor](https://docs.mixpeek.com/docs/processing/custom-extractors.md). The stage sends document text to your extractor's inference endpoint and attaches predicted labels with confidence scores to each document.

* **Hosted classifiers with your labels** (via `inference_name` and `labels`). Set `inference_name: "typesafe__jev"` to classify with [Jev](https://docs.mixpeek.com/docs/processing/jev.md) into labels you list. Each document gets every label with its probability, highest first.

Set exactly one of `task`, `feature_uri` or `inference_name`.

> [!NOTE]
>
> **Stage Category**: APPLY (Enriches documents)
>
> **Transformation**: N documents → N documents (with classification results added). A built-in task can optionally drop documents (e.g. `drop_if_unsafe`), making it N → ≤N.

## When to Use

| Use Case | Description |
| - | - |
| **Custom classification** | Apply your own trained classifier to search results |
| **Content labeling** | Tag documents with domain-specific categories |
| **Compliance scoring** | Score documents against compliance criteria |
| **Intent detection** | Classify user queries or document intent |

## When NOT to Use

| Scenario | Recommended Alternative |
| - | - |
| Predefined taxonomy classification | [`taxonomy_enrich`](https://docs.mixpeek.com/docs/retrieval/stages/taxonomy-enrich) (no custom model needed) |
| LLM-based classification | [`llm_enrich`](https://docs.mixpeek.com/docs/retrieval/stages/llm-enrich) with `output_schema` |
| Simple keyword matching | [`attribute_filter`](https://docs.mixpeek.com/docs/retrieval/stages/attribute-filter) |

## Available Classification Tasks

Built-in tasks are managed classifiers Mixpeek hosts — set `task` and skip `feature_uri` entirely. This list grows over time as more built-in tasks ship.

| Task | What it does | Model | Modalities | Outputs |
| - | - | - | - | - |
| `nsfw` | Content-safety classification — scores how likely a document is unsafe (not safe for work) | `mixpeek/content-classifier-v1` | Text, image, video | `nsfw_score` (0–1), `label`, `is_nsfw` |

The `mixpeek/content-classifier-v1` model is a CPU-only multimodal classifier: DistilBERT for text, ViT for images, and frame sampling for video — so the `nsfw` task runs without a GPU. By default it annotates each document with the outputs above; set `drop_if_unsafe: true` to filter unsafe documents out of the result set instead.

> [!NOTE]
>
> The same `mixpeek/content-classifier-v1` model also powers a **tenant-level upload gate**: when `nsfw_check_enabled` is on for a shared-plane org, NSFW image, video, and text uploads are rejected at upload time. The Classify stage applies the same model at query time over search results. See [Uploads](https://docs.mixpeek.com/docs/ingestion/uploads.md).

## Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `task` | string (enum) | `null` | Built-in classification task to run (e.g. `"nsfw"`). Mutually exclusive with `feature_uri`. See [Available Classification Tasks](#available-classification-tasks). |
| `feature_uri` | string | *Required unless `task` or `inference_name` is set* | Feature URI of your custom classifier plugin. Mutually exclusive with `task` and `inference_name`. |
| `inference_name` | string | `null` | Hosted classifier to call by name, e.g. `typesafe__jev` or `mixpeek__decision`. Requires `labels`. |
| `decision_model` | string | `null` | Model that answers for `mixpeek__decision` or `typesafe__jev`, e.g. `jev-latest` or `gemini-2.5-flash-lite`. See [Swap the decision model](https://docs.mixpeek.com/docs/processing/jev.md#swap-the-decision-model). |
| `labels` | list or object | `null` | Labels to classify into: a list, or a map of label to a one-line description. 2 to 255 labels. |
| `instruction` | string | `null` | Optional question for the classifier, e.g. `"Which support team should handle this ticket?"` |
| `document_field` | string | `"content"` | Document field path containing text to classify |
| `image_field` | string | `null` | Document field path containing an image URL to classify instead of text (built-in tasks only) |
| `video_field` | string | `null` | Document field path containing a video URL to classify instead of text (built-in tasks only) |
| `output_field` | string | `"classification"` | Field path to store classification results |
| `nsfw_threshold` | float | `0.7` | Score at or above which a document is flagged `is_nsfw` (`nsfw` task only) |
| `drop_if_unsafe` | boolean | `false` | Drop documents flagged unsafe instead of annotating them (`nsfw` task only) |
| `max_document_chars` | integer | `5000` | Maximum characters sent for classification (100–50000) |
| `top_k_labels` | integer | `null` | Keep only the top-k labels by confidence (custom classifiers) |
| `min_confidence` | float | `null` | Minimum confidence threshold (0.0–1.0, custom classifiers) |
| `batch_size` | integer | `10` | Documents per inference call (1–100) |
| `max_concurrency` | integer | `5` | Maximum concurrent inference requests (1–20) |

## Plugin Contract

Your classifier plugin must accept `{text: str}` and return `{labels: [{label: str, confidence: float}]}`.

```python theme={null}
# In your plugin's realtime.py
class ClassifierService(BaseInferenceService):
    def _process_single(self, inputs: dict, parameters: dict) -> dict:
        text = inputs["text"]
        # Your classification logic here
        return {
            "labels": [
                {"label": "technology", "confidence": 0.92},
                {"label": "business", "confidence": 0.78},
                {"label": "science", "confidence": 0.45},
            ]
        }
```

> [!TIP]
>
> Set `inference_type: "classify"` in your plugin's manifest to declare compatibility with the classify stage.

## Configuration Examples

### Built-in NSFW Task

```json Annotate (flag NSFW) theme={null}
{
  "stage_name": "nsfw_check",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "task": "nsfw",
      "document_field": "content",
      "output_field": "safety",
      "nsfw_threshold": 0.7
    }
  }
}
```

```json Filter (drop unsafe docs) theme={null}
{
  "stage_name": "nsfw_filter",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "task": "nsfw",
      "document_field": "content",
      "drop_if_unsafe": true,
      "nsfw_threshold": 0.7
    }
  }
}
```

```json Classify a Media URL theme={null}
{
  "stage_name": "nsfw_image_check",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "task": "nsfw",
      "image_field": "metadata.thumbnail_url",
      "output_field": "safety"
    }
  }
}
```

The annotate example writes a result like `{"nsfw_score": 0.03, "label": "safe", "is_nsfw": false}` to `output_field` on every document. The filter example drops any document whose `nsfw_score` meets `nsfw_threshold` instead of annotating it.

A document the classifier could not score gets `{"nsfw_score": null, "label": null, "is_nsfw": null}`, and the response carries a warning with the reason and a `degraded` status. Treat `null` as unknown, because it says nothing about safety. With `drop_if_unsafe: true` the stage refuses the request instead.

### Classify with Jev

```json theme={null}
{
  "stage_name": "classify",
  "stage_type": "enrich",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "inference_name": "typesafe__jev",
      "labels": {
        "product_demo": "Someone shows how a product works",
        "testimonial": "A customer describes their experience",
        "tutorial": "Step-by-step instructions"
      },
      "document_field": "text",
      "top_k_labels": 1
    }
  }
}
```

Each document gets `classification: [{"label": "product_demo", "confidence": 0.76}]`. `min_confidence` and `top_k_labels` apply as they do for custom classifiers.

### Custom Classifier

```json Basic Classification theme={null}
{
  "stage_name": "my_classifier",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "feature_uri": "mixpeek://my_classifier@1.0.0/classify",
      "document_field": "content",
      "output_field": "classification"
    }
  }
}
```

```json With Confidence Filtering theme={null}
{
  "stage_name": "my_classifier",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "feature_uri": "mixpeek://my_classifier@1.0.0/classify",
      "document_field": "metadata.description",
      "output_field": "labels",
      "min_confidence": 0.5,
      "top_k_labels": 3
    }
  }
}
```

```json High-Throughput theme={null}
{
  "stage_name": "my_classifier",
  "config": {
    "stage_id": "classify",
    "parameters": {
      "feature_uri": "mixpeek://my_classifier@1.0.0/classify",
      "batch_size": 20,
      "max_concurrency": 10,
      "max_document_chars": 2000
    }
  }
}
```

## Output

For a **custom classifier**, each document gets the predicted labels added at `output_field`:

```json theme={null}
{
  "document_id": "doc_123",
  "content": "Apple Inc. announced new AI features...",
  "classification": [
    {"label": "technology", "confidence": 0.95},
    {"label": "business", "confidence": 0.82}
  ]
}
```

For the built-in **`nsfw` task**, each document gets a safety result at `output_field` (unless `drop_if_unsafe` removed it):

```json theme={null}
{
  "document_id": "doc_123",
  "content": "Apple Inc. announced new AI features...",
  "safety": {
    "nsfw_score": 0.03,
    "label": "safe",
    "is_nsfw": false
  }
}
```

## Performance

| Metric | Value |
| - | - |
| **Latency** | Depends on your plugin model |
| **Batch size** | 10 documents default |
| **Concurrency** | 5 parallel requests default |
| **Max document chars** | 5000 default |

## Common Pipeline Patterns

### Search + Classify + Filter by Label

```json theme={null}
[
  {
    "stage_name": "search",
    "config": {
      "stage_id": "feature_search",
      "parameters": {
        "searches": [{
          "feature_uri": "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1",
          "query": {"input_mode": "text", "value": "{{INPUT.query}}"},
          "top_k": 100
        }],
        "final_top_k": 25
      }
    }
  },
  {
    "stage_name": "classify",
    "config": {
      "stage_id": "classify",
      "parameters": {
        "feature_uri": "mixpeek://my_classifier@1.0.0/classify",
        "min_confidence": 0.7,
        "top_k_labels": 1
      }
    }
  },
  {
    "stage_name": "filter_by_label",
    "config": {
      "stage_id": "attribute_filter",
      "parameters": {
        "field": "classification.0.label",
        "operator": "eq",
        "value": "{{INPUT.target_category}}"
      }
    }
  }
]
```

## Related

* [Taxonomy Enrich](https://docs.mixpeek.com/docs/retrieval/stages/taxonomy-enrich.md) - Predefined taxonomy classification (no custom model)
* [LLM Enrich](https://docs.mixpeek.com/docs/retrieval/stages/llm-enrich.md) - LLM-based enrichment and classification
* [Custom Extractors](https://docs.mixpeek.com/docs/processing/custom-extractors.md) - Build and deploy custom inference models
* [Uploads](https://docs.mixpeek.com/docs/ingestion/uploads.md) - Tenant-level NSFW upload gate using the same `mixpeek/content-classifier-v1` model


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.