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 +30 -61
- package/bin/open-classify.mjs +35 -51
- package/docs/adding-a-classifier.md +1 -8
- package/docs/manifests.md +4 -5
- package/open-classify.config.example.json +3 -6
- package/package.json +1 -1
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
|
|
47
|
-
|
|
48
|
-
**1. Install**
|
|
46
|
+
Prerequisites: Node 18+, [Ollama](https://ollama.com), and the default classifier model:
|
|
49
47
|
|
|
50
48
|
```sh
|
|
51
|
-
|
|
49
|
+
ollama pull gemma4:e4b-it-q4_K_M
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
**
|
|
52
|
+
**1. Scaffold (from your project root)**
|
|
55
53
|
|
|
56
54
|
```sh
|
|
57
55
|
npx open-classify init
|
|
58
56
|
```
|
|
59
57
|
|
|
60
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
88
|
+
Set the toggle to `true` to run the package-owned version. `npm update open-classify` keeps the prompt current.
|
|
93
89
|
|
|
94
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
309
|
+
## Configuration
|
|
314
310
|
|
|
315
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
##
|
|
357
|
+
## Contributing
|
|
387
358
|
|
|
388
|
-
|
|
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.
|
package/bin/open-classify.mjs
CHANGED
|
@@ -35,55 +35,29 @@ const TEMPLATE_DESCRIPTIONS = {
|
|
|
35
35
|
|
|
36
36
|
const CLASSIFIERS_README = `# classifiers/
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Each classifier is a folder with two files:
|
|
39
39
|
|
|
40
|
-
- \`manifest.json\` —
|
|
41
|
-
- \`prompt.md\` — the
|
|
40
|
+
- \`manifest.json\` — declares the output shape and fallback
|
|
41
|
+
- \`prompt.md\` — the classification instructions
|
|
42
42
|
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
+
Each template mirrors a package-owned stock classifier. You have two ways
|
|
48
|
+
to use them:
|
|
47
49
|
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
\`
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
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
|
-
|
|
406
|
-
|
|
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
|
|
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
|
|
3
|
+
Every classifier is a directory with exactly two files:
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
"
|
|
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",
|