# text-classifier — text classification + labeling microservice

Text classification and labeling microservice: assign the best label to a piece of text from a caller-supplied label set, detect conversational intent, extract topics, and identify language. Tools...


Assign the best label to text from a caller-supplied label set, detect conversational intent, extract
topics, do zero-shot labeling, suggest tags, and identify language. A paid "intelligent microservice":
every tool has a deterministic bag-of-words / cosine / trigram core that runs **fully offline**;
`classify` and `zeroShot` additionally enrich with an LLM when reachable and silently fall back.
Results carry `{ mode: 'heuristic' | 'llm' }`.

## Tools

| Tool | Args | Returns |
|---|---|---|
| `classify` | `{ text, labels, options?:{threshold,multiLabel} }` | best `label` + ranked `scores` (softmax probs). `labels` = array \| comma-string \| `{label:[keywords]}` map (supervised, stronger). |
| `intent` | `{ text }` | `intent` ∈ question/request/complaint/praise/greeting/farewell/statement, `confidence`, per-intent `scores`. |
| `topics` | `{ text, top? }` | salient `topics` (phrases + terms) and top `terms` with counts. |
| `zeroShot` | `{ text, labels, hypothesis?, options? }` | zero-shot `best` label + `scores` against arbitrary labels with an NL hypothesis template (`{}` placeholder). |
| `tag` | `{ text, top? }` | suggested keyword `tags` + `hashtags`. |
| `language` | `{ text }` | `language` code + `name` + `confidence` via character-trigram profile (en/es/fr/de/it/pt/nl/sv/pl/la). |

## Usage

```js
import pack from './index.js';
await pack.adapters.classify({ text: 'my payment failed again', labels: ['billing', 'shipping', 'account'] });
await pack.adapters.classify({ text: 'reset my password', labels: { auth: ['password', 'login', 'reset'], billing: ['invoice', 'charge'] } });
pack.adapters.intent({ text: 'Could you please refund my order?' });
pack.adapters.topics({ text: 'machine learning models train on large datasets ...' });
pack.adapters.language({ text: 'Bonjour, comment allez-vous aujourd\'hui?' });
```

Every tool takes ONE args object (maps 1:1 onto an HTTP POST body). Bad input throws `TypeError`.

## Hybrid intelligence

Set `OPENAI_BASE_URL`+`OPENAI_API_KEY` or run a local Ollama to enable the AI path on `classify` and
`zeroShot`. With no model the heuristic core is used. A down model never throws.

## DRY boundary

- `nlp` owns generic corpus stats (sentiment, RAKE keywords, ngrams, readability, **stopword-based**
  language, word frequency). text-classifier is the **classification workflow**: map text → a caller's
  label set, intent, topics, zero-shot, tags, and **trigram** language ID (deliberately a different
  method than `nlp.languageDetect`).
- `embeddings` owns real vector cosine over learned vectors; here cosine is over surface term/trigram
  bags, self-contained.

Pure ESM, Node built-ins only, zero npm deps (only `../_shared/llm.js` for the optional AI path).


---
Source: shared/engines/adapters/domain/text-classifier/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/text-classifier
