open-classify 0.4.0 → 0.6.0
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 +145 -105
- package/dist/src/aggregator.d.ts +8 -17
- package/dist/src/aggregator.js +127 -218
- package/dist/src/classifiers/{custom/context_shift → context_shift}/manifest.json +6 -11
- package/dist/src/classifiers/{custom/context_shift → context_shift}/prompt.md +1 -1
- package/dist/src/classifiers/{custom/conversation_digest → conversation_digest}/manifest.json +7 -12
- package/dist/src/classifiers/{custom/conversation_digest → conversation_digest}/prompt.md +2 -2
- package/dist/src/classifiers/{custom/memory_retrieval_queries → memory_retrieval_queries}/manifest.json +6 -11
- package/dist/src/classifiers/{custom/memory_retrieval_queries → memory_retrieval_queries}/prompt.md +2 -2
- package/dist/src/classifiers/{stock/model_specialization → model_specialization}/manifest.json +2 -2
- package/dist/src/classifiers/model_specialization/prompt.md +5 -0
- package/dist/src/classifiers/model_tier/manifest.json +11 -0
- package/dist/src/classifiers/model_tier/prompt.md +5 -0
- package/dist/src/classifiers/preflight/manifest.json +35 -0
- package/dist/src/classifiers/preflight/prompt.md +16 -0
- package/dist/src/classifiers/prompt_injection/manifest.json +15 -0
- package/dist/src/classifiers/prompt_injection/prompt.md +14 -0
- package/dist/src/classifiers/{stock/tools → tools}/manifest.json +3 -3
- package/dist/src/classifiers/tools/prompt.md +5 -0
- package/dist/src/classifiers.js +31 -29
- package/dist/src/classify.d.ts +9 -3
- package/dist/src/classify.js +26 -14
- package/dist/src/config.d.ts +1 -6
- package/dist/src/config.js +7 -57
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -3
- package/dist/src/input.d.ts +4 -1
- package/dist/src/input.js +12 -10
- package/dist/src/manifest.d.ts +29 -70
- package/dist/src/pipeline.d.ts +9 -2
- package/dist/src/pipeline.js +42 -83
- package/dist/src/reserved-fields.d.ts +18 -0
- package/dist/src/reserved-fields.js +175 -0
- package/dist/src/stock-prompt.d.ts +9 -2
- package/dist/src/stock-prompt.js +165 -45
- package/dist/src/stock-validation.d.ts +16 -17
- package/dist/src/stock-validation.js +267 -236
- package/dist/src/stock.d.ts +24 -60
- package/dist/src/stock.js +7 -14
- package/docs/adding-a-classifier.md +76 -32
- package/docs/manifests.md +113 -72
- package/docs/resolver.md +23 -56
- package/docs/signals.md +48 -57
- package/open-classify.config.example.json +9 -14
- package/package.json +1 -1
- package/dist/src/classifiers/stock/preflight/manifest.json +0 -11
- package/dist/src/classifiers/stock/prompt_injection/manifest.json +0 -12
- package/dist/src/classifiers/stock/prompts/classifier-header.md +0 -4
- package/dist/src/classifiers/stock/prompts/custom-output.md +0 -7
- package/dist/src/classifiers/stock/prompts/model_specialization.md +0 -7
- package/dist/src/classifiers/stock/prompts/preflight-output.md +0 -10
- package/dist/src/classifiers/stock/prompts/preflight.md +0 -47
- package/dist/src/classifiers/stock/prompts/prompt-injection-output.md +0 -5
- package/dist/src/classifiers/stock/prompts/prompt_injection.md +0 -24
- package/dist/src/classifiers/stock/prompts/routing-output.md +0 -5
- package/dist/src/classifiers/stock/prompts/routing.md +0 -9
- package/dist/src/classifiers/stock/prompts/specialty.md +0 -12
- package/dist/src/classifiers/stock/prompts/tier.md +0 -7
- package/dist/src/classifiers/stock/prompts/tools-output.md +0 -11
- package/dist/src/classifiers/stock/prompts/tools.md +0 -10
- package/dist/src/classifiers/stock/routing/manifest.json +0 -11
- /package/dist/src/classifiers/{stock/prompts → _prompts}/base.md +0 -0
- /package/dist/src/classifiers/{stock/prompts → _prompts}/confidence.md +0 -0
- /package/dist/src/classifiers/{stock/prompts → _prompts}/reason.md +0 -0
package/README.md
CHANGED
|
@@ -6,12 +6,10 @@
|
|
|
6
6
|
Decide what should happen to a user message <em>before</em> it reaches your downstream model.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
Open Classify is a pre-routing layer for AI products. It runs a small set of fast classifiers in parallel against the latest user message, then
|
|
9
|
+
Open Classify is a pre-routing layer for AI products. It runs a small set of fast classifiers in parallel against the latest user message, then returns a single `PipelineResult` your app can act on: an action (`route`, `block`, or `reply`), a downstream model recommendation, a tool exposure list, an optional immediate reply, and any custom signals your own classifiers contribute.
|
|
10
10
|
|
|
11
11
|
Use it when your frontier model should not be the first thing every request touches. Open Classify can handle tiny terminal replies before they hit an expensive model, recommend the right downstream model for the actual task, suggest what tools or context the downstream model should receive, and add a focused prompt-injection pass.
|
|
12
12
|
|
|
13
|
-
The result is a small, auditable decision envelope your app can act on before spending the big tokens.
|
|
14
|
-
|
|
15
13
|
```
|
|
16
14
|
message
|
|
17
15
|
│
|
|
@@ -19,29 +17,29 @@ message
|
|
|
19
17
|
normalize + trim classifier context
|
|
20
18
|
│
|
|
21
19
|
├─► preflight ─────────────► final_reply? / ack_reply?
|
|
22
|
-
├─►
|
|
23
|
-
├─► model_specialization ──►
|
|
20
|
+
├─► model_tier ────────────► model_tier?
|
|
21
|
+
├─► model_specialization ──► model_specialization?
|
|
24
22
|
├─► tools ─────────────────► tools?
|
|
25
23
|
├─► prompt_injection ─────► risk_level?
|
|
26
|
-
└─►
|
|
27
|
-
(run in parallel)
|
|
24
|
+
└─► your own classifiers ──► any JSON-Schema-validated payload
|
|
25
|
+
(all run in parallel, capped by maxConcurrency)
|
|
28
26
|
│
|
|
29
27
|
▼
|
|
30
28
|
aggregator + model catalog
|
|
31
29
|
│
|
|
32
30
|
▼
|
|
33
|
-
|
|
31
|
+
PipelineResult { action, model_id, tools, reply, ... }
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
Every classifier uses the same manifest shape and emits the same output envelope: `{ reason, certainty, ...payload }`. Some payload fields are **reserved** — like `model_tier`, `final_reply`, and `risk_level` — and the aggregator knows how to consume them into a routing decision. Everything else is your classifier's own data and passes through to the caller untouched.
|
|
37
35
|
|
|
38
36
|
## Why Open Classify
|
|
39
37
|
|
|
40
|
-
- **Spend frontier tokens only when they matter.** Simple greetings, thanks, spelling checks, and small arithmetic can
|
|
41
|
-
- **Keep the user interface responsive.** For complex work, preflight
|
|
38
|
+
- **Spend frontier tokens only when they matter.** Simple greetings, thanks, spelling checks, and small arithmetic can be answered immediately (`action: "reply"`) without sending the request downstream.
|
|
39
|
+
- **Keep the user interface responsive.** For complex work, preflight emits an `ack_reply` — a task-specific acknowledgement your UI can show while routing the real request.
|
|
42
40
|
- **Pick the right model per message.** Classifiers emit soft constraints like tier and specialization; your catalog turns those into a concrete model optimized for cost, capability, and fit.
|
|
43
41
|
- **Shape downstream context intentionally.** Built-in and custom classifiers can recommend tools, retrieval queries, summaries, or other context hints without passing the full conversation history back to the caller.
|
|
44
|
-
- **Add another defensive layer.** The `prompt_injection` classifier
|
|
42
|
+
- **Add another defensive layer.** The `prompt_injection` classifier surfaces instruction-override attempts. High-risk or unknown injection risk automatically sets `action: "block"`.
|
|
45
43
|
|
|
46
44
|
## Install
|
|
47
45
|
|
|
@@ -56,7 +54,7 @@ Node 18+. The packaged runner is local Ollama and ships with `gemma4:e4b-it-q4_K
|
|
|
56
54
|
```ts
|
|
57
55
|
import { createClassifier } from "open-classify";
|
|
58
56
|
|
|
59
|
-
const classify = createClassifier();
|
|
57
|
+
const { classify, inspect } = createClassifier();
|
|
60
58
|
|
|
61
59
|
const result = await classify({
|
|
62
60
|
messages: [
|
|
@@ -64,120 +62,172 @@ const result = await classify({
|
|
|
64
62
|
],
|
|
65
63
|
});
|
|
66
64
|
|
|
67
|
-
if (result.action === "
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
if (result.action === "block") {
|
|
66
|
+
// classification error or prompt injection — handle appropriately
|
|
67
|
+
console.error(result.block_reason, result.failed_classifiers);
|
|
68
|
+
} else if (result.action === "reply") {
|
|
69
|
+
// preflight can answer this immediately — skip the downstream model
|
|
70
|
+
respondToUser(result.reply.text);
|
|
71
|
+
} else {
|
|
72
|
+
// route to the downstream model
|
|
73
|
+
callDownstream(result.model_id, result.tools);
|
|
74
|
+
respondToUser(result.reply?.text); // show the ack while it works
|
|
71
75
|
}
|
|
76
|
+
|
|
77
|
+
const queries = result.classifier_outputs.memory_retrieval_queries?.queries;
|
|
72
78
|
```
|
|
73
79
|
|
|
74
|
-
`createClassifier` builds the runner and loads the model catalog once. Reuse the returned `classify`
|
|
80
|
+
`createClassifier` builds the runner and loads the model catalog once. Reuse the returned `classify` and `inspect` functions across your app — every call is a plain function invocation, no re-initialization.
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
### Classifying assistant output
|
|
83
|
+
|
|
84
|
+
`inspect()` is a lean second pass for the **assistant's reply**. It only runs classifiers tagged `applies_to: "both"` (or `"assistant"`) in their manifest, and returns the per-classifier outputs plus the message that was inspected — no routing, no action, no block logic.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const result = await inspect({
|
|
88
|
+
messages: [
|
|
89
|
+
{ role: "user", text: "Summarize the contract." },
|
|
90
|
+
{ role: "assistant", text: "The contract has three notable risks…" },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
77
93
|
|
|
78
|
-
|
|
94
|
+
// result.message is { role: "assistant", text: "..." }
|
|
95
|
+
const risk = result.classifier_outputs.prompt_injection?.risk_level;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use it for things like prompt-injection checks on model output, summarized slugs, or any classifier you want to apply post-hoc. The built-in `prompt_injection` classifier ships tagged `"both"`, so it runs in both passes; everything else is `"user"` by default.
|
|
79
99
|
|
|
80
|
-
|
|
81
|
-
|---|---|---|
|
|
82
|
-
| `route` | Default — downstream work should continue | `downstream.{model_id, target_message, tools}`, `audit.ack_reply?` |
|
|
83
|
-
| `reply` | Preflight had a tiny terminal reply | `reply.text` |
|
|
84
|
-
| `block` | Prompt injection flagged confident `high_risk` / `unknown`, or the certainty gate fired | `reason.kind` plus prompt-injection or low-certainty details |
|
|
100
|
+
## What you get back
|
|
85
101
|
|
|
86
|
-
|
|
102
|
+
Every `classify()` call returns a `PipelineResult`:
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
| Field | What it is |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `action` | `"route"` \| `"block"` \| `"reply"` |
|
|
107
|
+
| `block_reason` | `"prompt_injection"` \| `"classification_error"` (only when `action === "block"`) |
|
|
108
|
+
| `target_message_hash` | Stable 8-hex fingerprint of the target message |
|
|
109
|
+
| `model_id` | Concrete model id chosen from your catalog (or `null` if unresolvable) |
|
|
110
|
+
| `tools` | Recommended tool ids (always an array; empty if not emitted) |
|
|
111
|
+
| `reply` | `{ text }` — the `ack_reply` or `final_reply` text, if any |
|
|
112
|
+
| `prompt_injection` | `{ risk_level }` from the injection classifier, or `null` |
|
|
113
|
+
| `avg_certainty` | Arithmetic mean certainty score (float 0–1) across all classifiers |
|
|
114
|
+
| `min_certainty` | Minimum certainty score (float 0–1) across all classifiers |
|
|
115
|
+
| `failed_classifiers` | Names of classifiers that errored or timed out (always present; may be empty) |
|
|
116
|
+
| `classifier_outputs` | Each classifier's payload with `reason` (string) and `certainty` (float) |
|
|
89
117
|
|
|
90
|
-
Example
|
|
118
|
+
Example result:
|
|
91
119
|
|
|
92
120
|
```json
|
|
93
121
|
{
|
|
94
122
|
"action": "route",
|
|
95
123
|
"target_message_hash": "b11d5268",
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
124
|
+
"model_id": "gpt-5.5",
|
|
125
|
+
"tools": ["workspace"],
|
|
126
|
+
"reply": { "text": "On it — I'll review the contract now." },
|
|
127
|
+
"prompt_injection": { "risk_level": "normal" },
|
|
128
|
+
"avg_certainty": 0.84,
|
|
129
|
+
"min_certainty": 0.75,
|
|
130
|
+
"failed_classifiers": [],
|
|
101
131
|
"classifier_outputs": {
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"model_specialization": { "specialization": "coding" },
|
|
108
|
-
"tools": { "tools": ["workspace"] },
|
|
109
|
-
"model_recommendation": {
|
|
110
|
-
"id": "gpt-5.5",
|
|
111
|
-
"context_window": 1050000,
|
|
112
|
-
"input_tokens_cpm": 5,
|
|
113
|
-
"cached_tokens_cpm": 0.5,
|
|
114
|
-
"output_tokens_cpm": 30,
|
|
115
|
-
"resolution": { "...": "..." }
|
|
116
|
-
},
|
|
117
|
-
"meta": { "classifiers": { "...": "..." } }
|
|
132
|
+
"model_tier": { "model_tier": "frontier_strong", "reason": "...", "certainty": 0.88 },
|
|
133
|
+
"model_specialization": { "model_specialization": "coding", "reason": "...", "certainty": 0.75 },
|
|
134
|
+
"tools": { "tools": ["workspace"], "reason": "...", "certainty": 0.88 },
|
|
135
|
+
"prompt_injection": { "risk_level": "normal", "reason": "...", "certainty": 0.97 },
|
|
136
|
+
"memory_retrieval_queries": { "queries": ["user code review preferences"], "reason": "...", "certainty": 0.75 }
|
|
118
137
|
}
|
|
119
138
|
}
|
|
120
139
|
```
|
|
121
140
|
|
|
122
|
-
##
|
|
141
|
+
## Classifier model
|
|
123
142
|
|
|
124
|
-
|
|
143
|
+
Open Classify ships with eight built-in classifiers; all use the same manifest shape. There is no distinction between "stock" and "custom" — the runtime only cares about which **reserved fields** a classifier declares.
|
|
125
144
|
|
|
126
|
-
|
|
145
|
+
| Name | dispatch_order | Reserved fields | What the aggregator does with it |
|
|
146
|
+
|---|---|---|---|
|
|
147
|
+
| `preflight` | 10 | `final_reply`, `ack_reply` | Sets `action: "reply"` or populates `result.reply` |
|
|
148
|
+
| `model_tier` | 20 | `model_tier` | Feeds the catalog resolver as a soft constraint |
|
|
149
|
+
| `model_specialization` | 30 | `model_specialization` | Feeds the catalog resolver as a soft constraint |
|
|
150
|
+
| `tools` | 40 | `tools` | Sets `result.tools` |
|
|
151
|
+
| `prompt_injection` | 50 | `risk_level` | High-risk/unknown → `action: "block"`; suspicious → advisory |
|
|
152
|
+
| `memory_retrieval_queries` | 60 | — | Passes through to `classifier_outputs` |
|
|
153
|
+
| `conversation_digest` | 70 | — | Passes through |
|
|
154
|
+
| `context_shift` | 80 | — | Passes through |
|
|
127
155
|
|
|
128
|
-
-
|
|
129
|
-
- `model_specialization` chooses only `specialization`
|
|
130
|
-
- `prompt_injection` is only for prompt injection, not harmfulness, authorization, contradiction, feasibility, or freshness checks
|
|
156
|
+
Reserved fields are well-known output keys with canonical JSON Schemas and prompt fragments baked into the runtime. When you declare one in your manifest, you don't have to redeclare its enum values or shape — the runtime injects them.
|
|
131
157
|
|
|
132
|
-
|
|
133
|
-
|---|---|---|
|
|
134
|
-
| `preflight` | `final_reply?` / `ack_reply?` | `final_reply` → `reply` |
|
|
135
|
-
| `routing` | `model_tier?` | no |
|
|
136
|
-
| `model_specialization` | `specialization?` | no |
|
|
137
|
-
| `tools` | `{ tools[] }` | no |
|
|
138
|
-
| `prompt_injection` | `{ risk_level }` | confident `high_risk` or `unknown` → `block` |
|
|
158
|
+
## Adding a classifier
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
Every classifier is two files in `src/classifiers/<name>/`:
|
|
141
161
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
A custom classifier is two files in `src/classifiers/custom/<name>/`:
|
|
145
|
-
|
|
146
|
-
`manifest.json`:
|
|
162
|
+
`manifest.json` describes the output shape and a fallback for when the classifier errors:
|
|
147
163
|
|
|
148
164
|
```json
|
|
149
165
|
{
|
|
150
|
-
"
|
|
151
|
-
"name": "memory_retrieval_queries",
|
|
166
|
+
"name": "topic_tags",
|
|
152
167
|
"version": "1.0.0",
|
|
153
|
-
"purpose": "
|
|
154
|
-
"
|
|
155
|
-
"fallback": {
|
|
156
|
-
"reason": "Classifier failed; no memory queries generated.",
|
|
157
|
-
"certainty": "no_signal",
|
|
158
|
-
"output": { "queries": [] }
|
|
159
|
-
},
|
|
168
|
+
"purpose": "Tag the message with a small set of topic labels for analytics.",
|
|
169
|
+
"dispatch_order": 70,
|
|
160
170
|
"output_schema": {
|
|
161
|
-
"
|
|
162
|
-
"additionalProperties": false,
|
|
163
|
-
"required": ["queries"],
|
|
171
|
+
"required": ["tags"],
|
|
164
172
|
"properties": {
|
|
165
|
-
"
|
|
173
|
+
"tags": {
|
|
166
174
|
"type": "array", "maxItems": 5,
|
|
167
|
-
"items": { "type": "string", "minLength": 1, "maxLength":
|
|
175
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 40 }
|
|
168
176
|
}
|
|
169
177
|
}
|
|
178
|
+
},
|
|
179
|
+
"fallback": {
|
|
180
|
+
"reason": "Classifier failed; no tags generated.",
|
|
181
|
+
"certainty": "no_signal",
|
|
182
|
+
"tags": []
|
|
170
183
|
}
|
|
171
184
|
}
|
|
172
185
|
```
|
|
173
186
|
|
|
174
|
-
`prompt.md
|
|
187
|
+
`prompt.md` describes the classification rule in plain language. You don't need to write JSON examples — the runtime synthesizes one from your schema and shows it to the model — and you don't paste enum values for reserved fields. Just describe what each output key means and when to abstain:
|
|
188
|
+
|
|
189
|
+
```markdown
|
|
190
|
+
You are the topic_tags classifier.
|
|
191
|
+
|
|
192
|
+
`tags` are short single-word topic labels (lowercase, no spaces). Use at most five.
|
|
193
|
+
Return an empty array when no clear topic applies.
|
|
194
|
+
Do not invent tags for vague or ambiguous messages.
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Rules:
|
|
198
|
+
|
|
199
|
+
- `name` must match the directory name.
|
|
200
|
+
- Reserved field names cannot appear in `output_schema.properties` — declare them in `reserved_fields` instead.
|
|
201
|
+
- `fallback` requires only `reason` and `certainty`; reserved and custom required fields are exempt from the fallback check.
|
|
202
|
+
- If you want hand-picked examples (preflight does this), add an `output_schema.examples` array. Each entry must validate against the composed schema at load time. Otherwise the runtime synthesizes a skeleton example for you.
|
|
203
|
+
|
|
204
|
+
Consume your output:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const result = await classify(input);
|
|
208
|
+
const tags = result.classifier_outputs.topic_tags?.tags ?? [];
|
|
209
|
+
```
|
|
175
210
|
|
|
176
|
-
|
|
211
|
+
See [docs/adding-a-classifier.md](docs/adding-a-classifier.md) for a full walkthrough and [docs/manifests.md](docs/manifests.md) for the field reference.
|
|
177
212
|
|
|
178
|
-
|
|
213
|
+
## Using reserved fields in your own classifier
|
|
179
214
|
|
|
180
|
-
|
|
215
|
+
Any classifier can emit reserved fields. If you write your own `task_router` that emits `model_tier`, the aggregator will fold it into the model resolution alongside the built-in `model_tier` classifier — highest-certainty contributor wins, ties broken by manifest `dispatch_order` ascending.
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"name": "task_router",
|
|
220
|
+
"version": "1.0.0",
|
|
221
|
+
"purpose": "Pick the downstream model tier and specialization for code-heavy tasks.",
|
|
222
|
+
"dispatch_order": 25,
|
|
223
|
+
"reserved_fields": ["model_tier", "model_specialization"],
|
|
224
|
+
"fallback": { "reason": "Classifier failed.", "certainty": "no_signal" }
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The runtime injects canonical sub-schemas and prompt fragments for each declared reserved field — the model is told the exact enum values it may emit. You don't paste enum values into `prompt.md`, and you don't have to hand-write a JSON example; the runtime synthesizes one from the schema and shows it to the model.
|
|
229
|
+
|
|
230
|
+
The available reserved fields are: `final_reply`, `ack_reply`, `model_tier`, `model_specialization`, `tools`, `risk_level`. The `tools` field additionally requires an `allowed_tools` array on the manifest listing the tool ids the classifier may pick from.
|
|
181
231
|
|
|
182
232
|
## Model catalog
|
|
183
233
|
|
|
@@ -212,9 +262,7 @@ Classifiers never emit model ids. They emit constraints; your catalog maps const
|
|
|
212
262
|
}
|
|
213
263
|
```
|
|
214
264
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The resolver picks the cheapest model matching `specialization` and `tier`, relaxing constraints in order when nothing fits, and reports what it dropped on `audit.model_recommendation.resolution`. See [docs/resolver.md](docs/resolver.md) for ranking details.
|
|
265
|
+
The resolver picks the cheapest model matching `model_specialization` and `model_tier`, relaxing constraints in order when nothing fits. See [docs/resolver.md](docs/resolver.md) for ranking details.
|
|
218
266
|
|
|
219
267
|
## Input contract
|
|
220
268
|
|
|
@@ -244,24 +292,16 @@ cp open-classify.config.example.json open-classify.config.json
|
|
|
244
292
|
"provider": "ollama",
|
|
245
293
|
"defaultModel": "gemma4:e4b-it-q4_K_M",
|
|
246
294
|
"models": {
|
|
247
|
-
"
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
},
|
|
251
|
-
"custom": {
|
|
252
|
-
"memory_retrieval_queries": "qwen2.5:7b-instruct-q4_K_M"
|
|
253
|
-
}
|
|
295
|
+
"model_tier": "qwen2.5:7b-instruct-q4_K_M",
|
|
296
|
+
"prompt_injection": "llama-guard3:8b",
|
|
297
|
+
"memory_retrieval_queries": "qwen2.5:7b-instruct-q4_K_M"
|
|
254
298
|
}
|
|
255
299
|
},
|
|
256
|
-
"aggregator": {
|
|
257
|
-
"certaintyThreshold": 0.65,
|
|
258
|
-
"certaintyGate": "min_score"
|
|
259
|
-
},
|
|
260
300
|
"catalog": "downstream-models.json"
|
|
261
301
|
}
|
|
262
302
|
```
|
|
263
303
|
|
|
264
|
-
`runner.provider` currently supports `"ollama"` only. `runner.defaultModel` applies to any classifier without an explicit
|
|
304
|
+
`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.
|
|
265
305
|
|
|
266
306
|
## Bring your own backend
|
|
267
307
|
|
|
@@ -284,15 +324,15 @@ const runClassifier: RunClassifier = async (name, input, signal) => {
|
|
|
284
324
|
// call your provider of choice, return a ClassifierOutput
|
|
285
325
|
};
|
|
286
326
|
|
|
287
|
-
const classify = createClassifier({ runClassifier });
|
|
327
|
+
const { classify, inspect } = createClassifier({ runClassifier });
|
|
288
328
|
```
|
|
289
329
|
|
|
290
|
-
For the lowest-level entry
|
|
330
|
+
For the lowest-level entry points, `classifyOpenClassifyInput(input, { runClassifier, catalog })` and `inspectOpenClassifyInput(input, { runClassifier })` skip the factory entirely.
|
|
291
331
|
|
|
292
332
|
## Further reading
|
|
293
333
|
|
|
294
|
-
- [docs/signals.md](docs/signals.md) —
|
|
295
|
-
- [docs/manifests.md](docs/manifests.md) — manifest reference
|
|
334
|
+
- [docs/signals.md](docs/signals.md) — reserved field reference
|
|
335
|
+
- [docs/manifests.md](docs/manifests.md) — manifest reference
|
|
296
336
|
- [docs/resolver.md](docs/resolver.md) — aggregation and model resolution
|
|
297
337
|
- [docs/adding-a-classifier.md](docs/adding-a-classifier.md) — author guide
|
|
298
338
|
|
package/dist/src/aggregator.d.ts
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { AckReplySignal,
|
|
3
|
-
|
|
4
|
-
export declare const DEFAULT_CERTAINTY_THRESHOLD = 0.65;
|
|
5
|
-
/** @deprecated Use DEFAULT_CERTAINTY_THRESHOLD. */
|
|
6
|
-
export declare const DEFAULT_CONFIDENCE_THRESHOLD = 0.65;
|
|
7
|
-
export interface ComposeEnvelopeArgs {
|
|
1
|
+
import type { Catalog, ClassifierPublicOutputs, ClassifierRegistry, ClassifierResults, PipelineResult } from "./manifest.js";
|
|
2
|
+
import type { AckReplySignal, FinalReplySignal, ToolsSignal } from "./stock.js";
|
|
3
|
+
export interface AssembleResultArgs {
|
|
8
4
|
readonly registry: ClassifierRegistry;
|
|
9
5
|
readonly results: ClassifierResults;
|
|
6
|
+
readonly failedClassifiers: ReadonlyArray<string>;
|
|
10
7
|
readonly catalog: Catalog;
|
|
11
|
-
readonly input: ClassifierInput;
|
|
12
|
-
readonly config?: AggregatorConfig;
|
|
13
8
|
}
|
|
14
|
-
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
17
|
-
export
|
|
18
|
-
routing?: RoutingClassifierOutput;
|
|
19
|
-
model_specialization?: ModelSpecializationClassifierOutput;
|
|
20
|
-
}>, catalog: Catalog, threshold: number): ModelRecommendation;
|
|
21
|
-
export type { FinalReplySignal, AckReplySignal };
|
|
9
|
+
type AssembledResult = Omit<PipelineResult, "target_message_hash">;
|
|
10
|
+
export declare function assembleResult(args: AssembleResultArgs): AssembledResult;
|
|
11
|
+
export declare function buildPublicOutputs(registry: ClassifierRegistry, results: ClassifierResults): ClassifierPublicOutputs;
|
|
12
|
+
export type { FinalReplySignal, AckReplySignal, ToolsSignal };
|