> ## 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](/processing/custom-extractors). The stage sends document text to your extractor's inference endpoint and attaches predicted labels with confidence scores to each document.

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

<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.
</Note>

## 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`](/retrieval/stages/taxonomy-enrich) (no custom model needed) |
| LLM-based classification           | [`llm_enrich`](/retrieval/stages/llm-enrich) with `output_schema`               |
| Simple keyword matching            | [`attribute_filter`](/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](/ingestion/uploads).
</Note>

## 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` is set* | Feature URI of your custom classifier plugin. Mutually exclusive with `task`.                                                                                      |
| `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.
</Tip>

## Configuration Examples

### Built-in NSFW Task

<CodeGroup>
  ```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"
      }
    }
  }
  ```
</CodeGroup>

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.

### Custom Classifier

<CodeGroup>
  ```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
      }
    }
  }
  ```
</CodeGroup>

## 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](/retrieval/stages/taxonomy-enrich) - Predefined taxonomy classification (no custom model)
* [LLM Enrich](/retrieval/stages/llm-enrich) - LLM-based enrichment and classification
* [Custom Extractors](/processing/custom-extractors) - Build and deploy custom inference models
* [Uploads](/ingestion/uploads) - Tenant-level NSFW upload gate using the same `mixpeek/content-classifier-v1` model
