open-classify 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,23 +43,21 @@ Every classifier uses the same manifest shape and emits the same output envelope
43
43
 
44
44
  ## Getting started
45
45
 
46
- Node 18+. The packaged runner uses local Ollama with `gemma4:e4b-it-q4_K_M` as the zero-config classifier model. Pluggable via `open-classify.config.json` or a custom `RunClassifier`.
47
-
48
- **1. Install**
46
+ Prerequisites: Node 18+, [Ollama](https://ollama.com), and the default classifier model:
49
47
 
50
48
  ```sh
51
- npm install open-classify
49
+ ollama pull gemma4:e4b-it-q4_K_M
52
50
  ```
53
51
 
54
- **2. Scaffold**
52
+ **1. Scaffold (from your project root)**
55
53
 
56
54
  ```sh
57
55
  npx open-classify init
58
56
  ```
59
57
 
60
- This creates `open-classify.config.json` and a `classifiers/` directory in your project root. You'll see exactly what will be written and asked to confirm. Re-run safe: existing files are skipped.
58
+ If the package isn't installed yet, `init` offers to add it. It writes `open-classify.config.json`, `downstream-models.json`, and a `classifiers/` directory. Re-run safe: existing files are skipped. Verify the install at any time with `npx open-classify doctor`.
61
59
 
62
- **3. Use it**
60
+ **2. Use it**
63
61
 
64
62
  ```ts
65
63
  import { createClassifier } from "open-classify";
@@ -75,31 +73,29 @@ else if (result.action === "block") handleBlock(result.block_reason); // inj
75
73
  else callDownstream(result.model_id, result.tools, result.reply?.text); // route the real request
76
74
  ```
77
75
 
78
- **4. Enable or customize optional classifiers**
76
+ `createClassifier()` looks for `open-classify.config.json` in the working directory, so the scaffolded layout works with no further wiring.
77
+
78
+ **3. Enable or customize optional classifiers**
79
+
80
+ Four mandatory base classifiers (`preflight`, `model_tier`, `model_specialization`, `prompt_injection`) always run from the package. Four more (`tools`, `memory_retrieval_queries`, `conversation_digest`, `context_shift`) are optional and default to off.
79
81
 
80
- `init` writes `open-classify.config.json`, which enables package-owned stock classifiers without copying them into your project. They default to `false` so the four mandatory base classifiers run automatically:
82
+ You have two ways to use the optional ones:
81
83
 
82
84
  ```json
83
- {
84
- "classifiers": {
85
- "stock": {
86
- "tools": false
87
- }
88
- }
89
- }
85
+ { "classifiers": { "stock": { "tools": true } } }
90
86
  ```
91
87
 
92
- Set `"tools": true` to run the package-owned stock `tools` classifier. Because it stays in `node_modules/open-classify`, `npm update open-classify` can improve its prompt later without touching your project files.
88
+ Set the toggle to `true` to run the package-owned version. `npm update open-classify` keeps the prompt current.
93
89
 
94
- Inside `classifiers/` you'll also find four `_<name>/` directories editable copies of the stock classifiers, inactive because of the underscore prefix. To customize one, keep the matching `classifiers.stock.<name>` value `false`, edit the copy, then drop the underscore:
90
+ Or customize a local copy. `init` scaffolds editable templates in `classifiers/_<name>/` (inactive because of the underscore prefix). To take one over, keep the stock toggle off and rename the folder:
95
91
 
96
92
  ```sh
97
93
  mv classifiers/_tools classifiers/tools
98
94
  ```
99
95
 
100
- Edit `manifest.json` first if you need to (e.g. trim `allowed_tools` for your app). The same underscore convention works the other way too: rename `my_classifier/` → `_my_classifier/` to take any copied/custom classifier out of the active set without deleting it.
96
+ The same convention works in reverse: rename any active classifier `<name>/` `_<name>/` to deactivate without deleting.
101
97
 
102
- To write a new classifier from scratch, drop a `<name>/manifest.json` + `<name>/prompt.md` in `classifiers/`. See [docs/adding-a-classifier.md](docs/adding-a-classifier.md).
98
+ To write a new classifier, drop a `<name>/manifest.json` + `<name>/prompt.md` in `classifiers/`. See [docs/adding-a-classifier.md](docs/adding-a-classifier.md).
103
99
 
104
100
  ### Classifying assistant output
105
101
 
@@ -310,45 +306,20 @@ The resolver picks the cheapest model matching `model_specialization` and `model
310
306
  - Open Classify keeps whole messages only, drops oldest first to fit a 5,000-char budget, and caps history at 20 messages.
311
307
  - Unknown fields are rejected, not passed through.
312
308
 
313
- ## Local setup
309
+ ## Configuration
314
310
 
315
- ```sh
316
- npm run setup
317
- ```
318
-
319
- Checks prerequisites (Node, npm, Ollama), confirms the base model is pulled, installs dependencies, and builds. Idempotent — safe to re-run.
320
-
321
- Optional Ollama runtime config:
311
+ `npx open-classify init` writes a working `open-classify.config.json` for you. To customize, edit it directly — the full set of supported fields (with realistic example values) lives in [open-classify.config.example.json](open-classify.config.example.json).
322
312
 
323
- ```sh
324
- cp open-classify.config.example.json open-classify.config.json
325
- ```
326
-
327
- ```json
328
- {
329
- "runner": {
330
- "provider": "ollama",
331
- "defaultModel": "gemma4:e4b-it-q4_K_M",
332
- "models": {
333
- "model_tier": "qwen2.5:7b-instruct-q4_K_M",
334
- "prompt_injection": "llama-guard3:8b",
335
- "memory_retrieval_queries": "qwen2.5:7b-instruct-q4_K_M"
336
- }
337
- },
338
- "catalog": "downstream-models.json",
339
- "classifiers": {
340
- "dirs": ["classifiers"],
341
- "stock": {
342
- "tools": false,
343
- "memory_retrieval_queries": false,
344
- "conversation_digest": false,
345
- "context_shift": false
346
- }
347
- }
348
- }
349
- ```
350
-
351
- `runner.provider` currently supports `"ollama"` only. `runner.defaultModel` applies to any classifier without an explicit `runner.models` entry. `runner.models` is a flat map keyed by classifier name. `classifiers.dirs` is for user-owned copied/custom classifiers; `classifiers.stock` toggles package-owned optional classifiers.
313
+ | Field | What it controls |
314
+ |---|---|
315
+ | `runner.provider` | Backend. Currently `"ollama"` only. |
316
+ | `runner.host` | Ollama host URL. Defaults to `http://127.0.0.1:11434`. |
317
+ | `runner.defaultModel` | Classifier model used when there is no per-classifier override. |
318
+ | `runner.options` | Ollama generation options: `temperature`, `top_p`, `seed`, `num_ctx`. |
319
+ | `runner.models` | Per-classifier model overrides. Flat map keyed by classifier name. |
320
+ | `catalog` | Path to the downstream model catalog (relative to the config file). |
321
+ | `classifiers.dirs` | Directories of user-owned classifiers to load. |
322
+ | `classifiers.stock` | Toggles for package-owned optional stock classifiers. |
352
323
 
353
324
  ## Bring your own backend
354
325
 
@@ -383,8 +354,6 @@ For the lowest-level entry points, `classifyOpenClassifyInput(input, { runClassi
383
354
  - [docs/resolver.md](docs/resolver.md) — aggregation and model resolution
384
355
  - [docs/adding-a-classifier.md](docs/adding-a-classifier.md) — author guide
385
356
 
386
- ## Development
357
+ ## Contributing
387
358
 
388
- ```sh
389
- npm test # build + run the Node test runner suite
390
- ```
359
+ Clone the repo, then `npm run setup` (checks Node/Ollama, pulls the base model, installs and builds) and `npm test` (build + Node test runner). PRs welcome.
@@ -35,55 +35,29 @@ const TEMPLATE_DESCRIPTIONS = {
35
35
 
36
36
  const CLASSIFIERS_README = `# classifiers/
37
37
 
38
- Drop a folder here per classifier. Each folder needs:
38
+ Each classifier is a folder with two files:
39
39
 
40
- - \`manifest.json\` — see [open-classify docs](https://github.com/taylorbayouth/open-classify/blob/main/docs/adding-a-classifier.md)
41
- - \`prompt.md\` — the classifier-specific instructions
40
+ - \`manifest.json\` — declares the output shape and fallback
41
+ - \`prompt.md\` — the classification instructions
42
42
 
43
- ## Quickstart
43
+ The loader skips any folder whose name starts with \`_\`. That's how the
44
+ four \`_<name>/\` templates here stay inactive until you opt in: drop the
45
+ underscore (\`mv _tools tools\`) and the classifier runs on the next start.
44
46
 
45
- \`\`\`js
46
- import { createClassifier } from "open-classify";
47
+ Each template mirrors a package-owned stock classifier. You have two ways
48
+ to use them:
47
49
 
48
- const { classify } = createClassifier();
49
- \`\`\`
50
+ 1. **Enable in place** set \`classifiers.stock.<name>: true\` in
51
+ \`open-classify.config.json\`. The package-owned version runs and is
52
+ updated by \`npm update open-classify\`.
53
+ 2. **Customize a local copy** — keep the stock toggle off, drop the
54
+ underscore on the template here, and edit \`prompt.md\` /
55
+ \`manifest.json\` to taste.
50
56
 
51
- Place this in your server entry point. Call \`classify(input)\` for each user message.
52
- \`open-classify.config.json\` wires in \`./classifiers\` automatically.
53
-
54
- ## Stock classifiers
55
-
56
- \`tools\`, \`memory_retrieval_queries\`, \`conversation_digest\`, and \`context_shift\`
57
- ship with the package but are disabled by default in \`open-classify.config.json\`.
58
- Enable a package-owned stock classifier by setting it to \`true\`:
59
-
60
- \`\`\`json
61
- {
62
- "classifiers": {
63
- "stock": {
64
- "tools": true
65
- }
66
- }
67
- }
68
- \`\`\`
69
-
70
- Package-owned stock classifiers are updated by \`npm update open-classify\`.
71
-
72
- ## Customizing a stock classifier
73
-
74
- The four \`_<name>/\` directories below are editable copies of the stock classifiers.
75
- They are inactive because the loader skips any folder starting with \`_\`. To customize one,
76
- keep the matching \`classifiers.stock.<name>\` value \`false\`, edit the files, then drop the underscore:
77
-
78
- \`\`\`sh
79
- mv _tools tools
80
- \`\`\`
81
-
82
- You probably also want to edit its \`manifest.json\` first to fit your app (e.g. trim the \`allowed_tools\` list).
83
-
84
- ## Deactivating without deleting
85
-
86
- Same trick in reverse — rename \`my_classifier\` → \`_my_classifier\` to take it out of the active set without losing your work.
57
+ To write your own classifier, drop a new \`<name>/\` folder here with its
58
+ own \`manifest.json\` and \`prompt.md\`. The folder name must match the
59
+ manifest's \`name\` field. See the
60
+ [author guide](https://github.com/taylorbayouth/open-classify/blob/main/docs/adding-a-classifier.md).
87
61
  `;
88
62
 
89
63
  const DEFAULT_CONFIG = {
@@ -389,21 +363,31 @@ async function runInit({ cwd, yes, minimal, dryRun, force, noInstall, packageMan
389
363
  }
390
364
  }
391
365
 
392
- const classifierDirRel = relative(cwd, resolvedClassifierDir);
393
366
  process.stdout.write(`
394
367
  Next steps:
395
368
 
396
- 1. Pull the default model:
369
+ 1. Pull the default classifier model:
370
+
397
371
  ollama pull ${config.runner.defaultModel}
398
372
 
399
- 2. Wire it into your server (example for a Node entrypoint):
400
- see ./${classifierDirRel}/README.md → "Quickstart"
373
+ 2. Verify everything is wired up:
401
374
 
402
- 3. Verify the install:
403
375
  npx open-classify doctor
404
376
 
405
- 4. Run a one-shot classification against your config:
406
- npx open-classify try "hello world"
377
+ 3. Try it without writing any code:
378
+
379
+ npx open-classify try "hello"
380
+
381
+ 4. Use it from your code:
382
+
383
+ import { createClassifier } from "open-classify";
384
+ const { classify } = createClassifier();
385
+ const result = await classify({
386
+ messages: [{ role: "user", text: "hello" }],
387
+ });
388
+
389
+ The factory finds open-classify.config.json in your working
390
+ directory and wires in the classifiers/ folder automatically.
407
391
 
408
392
  Docs: https://github.com/taylorbayouth/open-classify#readme
409
393
  `);
@@ -1,13 +1,6 @@
1
1
  # Adding a classifier
2
2
 
3
- Every classifier — package-owned or your own — uses the same two-file layout. The runtime only cares about which reserved fields a classifier opts into; ownership just decides whether `npm update open-classify` can replace the prompt.
4
-
5
- There are two places a classifier can live:
6
-
7
- - **In your own app**, in a directory listed in `open-classify.config.json` under `classifiers.dirs` (almost always `./classifiers/` after `npx open-classify init`). This is the right path when you've installed Open Classify as a dependency.
8
- - **In this repo**, under `src/classifiers/<name>/`. Only do this when you're contributing a new mandatory built-in back to Open Classify.
9
-
10
- Either way, the layout and contract are identical.
3
+ Every classifier uses the same two-file layout. Drop a folder into a directory listed under `classifiers.dirs` in `open-classify.config.json` (defaults to `./classifiers/` after `npx open-classify init`) and the runtime picks it up on the next start.
11
4
 
12
5
  ## 1. Create the directory
13
6
 
package/docs/manifests.md CHANGED
@@ -1,16 +1,15 @@
1
1
  # Manifest reference
2
2
 
3
- Every classifier lives in `src/classifiers/<name>/` and contains exactly two files:
3
+ Every classifier is a directory with exactly two files:
4
4
 
5
5
  ```
6
- src/classifiers/
7
- _prompts/ # shared base markdown (base.md, reason.md, confidence.md)
6
+ classifiers/
8
7
  <classifier_name>/
9
8
  manifest.json
10
9
  prompt.md
11
10
  ```
12
11
 
13
- The loader skips any top-level directory whose name starts with `_` (those are shared assets, not classifiers).
12
+ Folders whose names start with `_` are skipped by the loader — that's how the scaffolded `_<name>/` templates stay inactive until you drop the underscore.
14
13
 
15
14
  ## Fields
16
15
 
@@ -145,7 +144,7 @@ A manifest may declare both reserved fields and custom properties; they sit alon
145
144
 
146
145
  `prompt.md` is the classifier-specific instruction text. The runtime composes the system prompt at load time from:
147
146
 
148
- 1. Shared base sections (JSON-only contract, `reason` + `certainty` rules) from `src/classifiers/_prompts/`
147
+ 1. Shared base sections (JSON-only contract, `reason` + `certainty` rules)
149
148
  2. The classifier header (name and purpose, with the purpose stated as a hard scope boundary)
150
149
  3. Auto-injected fragments for each declared reserved field (canonical enum values included, so you can't drift)
151
150
  4. Your `prompt.md`
@@ -4,16 +4,13 @@
4
4
  "host": "http://127.0.0.1:11434",
5
5
  "defaultModel": "gemma4:e4b-it-q4_K_M",
6
6
  "options": {
7
- "num_ctx": 4096,
8
7
  "temperature": 0,
9
8
  "top_p": 1,
10
- "seed": 0
9
+ "seed": 0,
10
+ "num_ctx": 4096
11
11
  },
12
12
  "models": {
13
- "preflight": "gemma4:e4b-it-q4_K_M",
14
- "model_tier": "gemma4:e4b-it-q4_K_M",
15
- "model_specialization": "gemma4:e4b-it-q4_K_M",
16
- "prompt_injection": "gemma4:e4b-it-q4_K_M"
13
+ "prompt_injection": "llama-guard3:8b"
17
14
  }
18
15
  },
19
16
  "catalog": "downstream-models.json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-classify",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Manifest-driven classifier runtime for routing user messages to downstream AI models",
5
5
  "license": "MIT",
6
6
  "author": "Taylor Bayouth",