@vllm-sr/dsh 0.4.1 → 0.5.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 CHANGED
@@ -11,8 +11,8 @@ agent orders, is the work really done, which team should get each of these 230 t
11
11
  - **System One**, the decision model, decides: `vllm-sr/d3-ultra`. It answers typed questions (choice, yes/no, score,
12
12
  box) with probabilities, from one forward pass per question instead of generated text.
13
13
 
14
- Both come from [System Duo](https://app.vllm-sr.ai) with one free key, and the plugin gets that key for you: System One
15
- answers at `https://app.vllm-sr.ai/v1/systemone`, and `vllm-sr/auto` at `https://app.vllm-sr.ai/v1/chat/completions`.
14
+ Both take a free [System Duo](https://app.vllm-sr.ai) key, which the plugin gets for you: System One answers at `https://app.vllm-sr.ai/v1/systemone`,
15
+ and `vllm-sr/auto` at `https://app.vllm-sr.ai/v1/chat/completions`.
16
16
 
17
17
  ## Quick start
18
18
 
@@ -26,9 +26,10 @@ The first line installs DeepSeek Harness (0.2.1-rc.5 or a later 0.2 release, on
26
26
  `dsh plugin` uses to install plugins; without pnpm, `dsh plugin add` stops with "pnpm was not found". On a machine
27
27
  with git, the package can also come straight from GitHub: `dsh plugin --profile web add github:Xunzhuo/dsh-system-one-plugin`.
28
28
 
29
- On its first start the plugin gets one free System Duo key, with the same small proof of work the site's "Get your
30
- API key" button runs in the browser, and saves it in DeepSeek Harness's credential store and in `~/.dsh/.env` (readable
31
- by you only). A notice in the session says so. `vllm-sr/auto` becomes the default model; choose another model
29
+ On its first start the plugin gets a free System Duo key, with the same small proof of work the site's "Get your API
30
+ key" button runs in the browser, and saves it in DeepSeek Harness's credential store and in `~/.dsh/.env` (readable
31
+ by you only). A line under your first request says so: "Free API key created for app.vllm-sr.ai, saved in
32
+ ~/.dsh/.env", or why the site refused one. `vllm-sr/auto` becomes the default model; choose another model
32
33
  in the app and System One keeps working beside it.
33
34
 
34
35
  Headless:
@@ -56,6 +57,22 @@ of your profile patch stay as they are.
56
57
  | Done-check | `agent/turn-stopping` | complete?, checked?, waiting for you? | an unfinished turn goes back once or twice to finish and verify |
57
58
  | Tool | `system_one` | any rubric the LLM writes, over a file of items or images | one result row per item (JSONL or CSV) and a summary |
58
59
 
60
+ ### Span answers (vllm-sr/vela3-turbo)
61
+
62
+ `vllm-sr/vela3-turbo` also answers span questions: which words of a text carry a label, with code-point offsets and a
63
+ probability each. Once the Decision API lists `span` for it (`spans: auto`, the default), four reflexes point at words
64
+ instead of whole passages:
65
+
66
+ - **Shield**: in a passage it flagged, the sentences that give orders go, and the text around them stays. The passage
67
+ is scored again without them, and a cut that leaves an injection behind falls back to removing lines.
68
+ - **Guard**: a call it flags names the shell command that does the damage, in its card and its question.
69
+ - **Done-check**: words of the final answer that nothing the turn read supports (preset `halu`) are marked, and
70
+ System Two goes back to re-verify just those.
71
+ - **Privacy** (`privacy: true`, off by default): personal data in tool results (preset `pii`, 17 labels) becomes its
72
+ label, as `[EMAIL_ADDRESS]`, before System Two reads it: the nine sensitive labels by default (names, e-mail and
73
+ street addresses, phone, card, bank and ID numbers, IP addresses), while dates, organizations and places pass. A
74
+ small card shows what was masked, by label only.
75
+
59
76
  ## See it in the Web UI
60
77
 
61
78
  In `dsh web`, System One shows up in the conversation itself, where it acted, marked with an ember **System One**
@@ -70,11 +87,16 @@ badge that carries the decision's latency:
70
87
  - **Done-check**: a card after the answer: done, waiting on you, or sent back to finish.
71
88
  - **`system_one` tool**: its row shows the batch: items, questions, each question's answer distribution and the
72
89
  results file.
90
+ - **Free key**: a **System Duo** line under your first request when the plugin created a key, or could not.
91
+ - **Spans** (vllm-sr/vela3-turbo): the words a span answer marked, in the card that used them: removed injections
92
+ struck through, a command's dangerous part, unsupported claims of the answer underlined, and masked personal data as
93
+ label chips in a **Privacy** card.
73
94
 
74
95
  Routine checks (an allowed call, a clean result) are one line and fold with the turn's work. Cards stay visible when
75
96
  the work folds, so a finished turn still shows what System One decided. Parallel calls in one step share one line or
76
97
  card, and every decision pops in with a short animation the first time it appears (none with reduced motion). Each
77
- element shows the Decision API's own answer: the probabilities, the model and the measured latency.
98
+ element shows the Decision API's own answer: the probabilities, the model (in bold, as `vllm-sr/d3-ultra`) and the
99
+ measured latency.
78
100
 
79
101
  Set `systemOneUi` to `compact` for one line per decision, or to `off` to hide them.
80
102
 
@@ -85,21 +107,26 @@ attributes that say what it shows:
85
107
 
86
108
  | Attribute | Values |
87
109
  | --- | --- |
88
- | `data-system-one` | `card`, `compact` (one line) or `tool` (the `system_one` row) |
89
- | `data-system-one-reflex` | `triage`, `guard`, `shield`, `done` or `tool` |
90
- | `data-system-one-verdict` | triage `brief`; guard `allow`, `ask` or `deny`; shield `clean`, `removed` or `flagged`; done-check `stop` or `continue`; tool `running` or `done`; `error` when System One did not answer |
110
+ | `data-system-one` | `card`, `compact` (one line), `tool` (the `system_one` row) or `notice` (the free-key line) |
111
+ | `data-system-one-reflex` | `triage`, `guard`, `shield`, `privacy`, `done`, `tool` or `key` |
112
+ | `data-system-one-verdict` | triage `brief`; guard `allow`, `ask` or `deny`; shield `clean`, `removed` or `flagged`; privacy `masked`; done-check `stop` or `continue`; tool `running` or `done`; key `created` or `failed`; `error` when System One did not answer |
91
113
  | `data-system-one-outcome` | guard, once you answer its approval: `allowed`, `rejected`, `cancelled` or `failed` |
92
- | `data-system-one-why` | done-check: `complete`, `waiting`, `unfinished` or `unchecked` |
114
+ | `data-system-one-why` | done-check: `complete`, `waiting`, `unfinished`, `unchecked` or `unsupported` |
93
115
  | `data-system-one-tone` | its colour: `ok` (green), `info` (ember), `warn` (amber), `bad` (red) or `muted` |
94
116
  | `data-system-one-count` | how many decisions it holds (a batch: how many items) |
95
117
  | `data-system-one-turn`, `data-system-one-step` | where it acted (the step for guard and shield) |
118
+ | `data-system-one-model` | the model that answered, as `vllm-sr/d3-ultra` |
119
+ | `data-system-one-span-model`, `data-system-one-spans` | the span model that marked words, as `vllm-sr/vela3-turbo`, and how many |
96
120
 
97
121
  Inside a root, `data-system-one-part` marks `badge` (the ember **System One** pill), `latency`, `reflex`, `summary`,
98
122
  `model`, `head`, `toggle` (the button of a one-line element), `rows`, `verdict`, `outcome`, `subject`, `facts`,
99
- `note` and `more`, and in the batch row also `stack`, `legend`, `failed`, `results` and `raw`. Each number is a
100
- `data-system-one-fact`: `kind`, `effort`, `unclear` and `risky` (triage), `risk`, `leaks` and `on-task` (guard),
101
- `passages` and `injection` (shield), `complete`, `checked` and `waiting` (done-check). Decision rows carry
102
- `data-system-one-decision` and `data-system-one-call`, batch questions `data-system-one-question`. For example:
123
+ `note` and `more`, in the batch row also `stack`, `legend`, `failed`, `results` and `raw`, and in the free-key line
124
+ `badge` (the **System Duo** pill), `summary` and `links`. Span answers add `excerpts`, each `excerpt`, every marked
125
+ `span` (with `data-system-one-span-label` and `data-system-one-probability`) and, in a Privacy card, the `labels`. Each
126
+ number is a `data-system-one-fact`: `kind`, `effort`, `unclear` and `risky` (triage), `risk`, `leaks` and `on-task`
127
+ (guard), `passages`, `injection` and `spans` (shield), `masked` (privacy), `complete`, `checked`, `waiting` and
128
+ `unsupported` (done-check). Decision rows carry `data-system-one-decision` and `data-system-one-call`, batch questions
129
+ `data-system-one-question`. For example:
103
130
 
104
131
  | Element | Selector |
105
132
  | --- | --- |
@@ -113,11 +140,17 @@ Inside a root, `data-system-one-part` marks `badge` (the ember **System One** pi
113
140
  | The ember badge, and its latency | `[data-system-one-part="badge"]`, `[data-system-one-part="latency"]` |
114
141
  | "Dangerous", and "You rejected it" | `[data-system-one-fact="risk"]`, `[data-system-one-part="outcome"]` |
115
142
  | The reason | `[data-system-one-part="note"]` |
143
+ | The model's name (bold) | `[data-system-one-part="model"]` |
144
+ | Free key created | `[data-system-one="notice"][data-system-one-verdict="created"]` |
145
+ | The words a span answer marked | `[data-system-one-part="span"]` (`[data-system-one-span-label="INJECTION"]`, `"DANGEROUS"`, `"unsupported"`, a PII label) |
146
+ | Privacy card | `[data-system-one-reflex="privacy"][data-system-one-verdict="masked"]` |
147
+ | Done-check that sent claims back | `[data-system-one-reflex="done"][data-system-one-why="unsupported"]` |
116
148
 
117
149
  ## Your data
118
150
 
119
151
  System One sends what it decides about: the user's request (clipped), each tool call it reviews, the passages of tool
120
- results it screens, the end of the turn, and the items given to the `system_one` tool. System Two receives the
152
+ results it screens, the end of the turn (with what the turn read, for the done-check's span answer), and the items
153
+ given to the `system_one` tool. With `privacy: true`, the results of `privacyTools` go to System One whole. System Two receives the
121
154
  conversation as any LLM provider does. System Duo logs requests to improve the services and to train models; read its
122
155
  [terms](https://app.vllm-sr.ai/terms) and [privacy](https://app.vllm-sr.ai/privacy) pages before you use it with
123
156
  private code or data. Each reflex can be turned off.
@@ -151,11 +184,25 @@ Every field has a default. Set any of them on the `system-one` row of a profile
151
184
  tool: true
152
185
  trace: ~/.dsh/system-one/trace.jsonl # or off
153
186
  systemOneUi: card # how Chat shows System One: card, compact (one line each) or off
187
+ spans: auto # span answers: auto (once spanModel lists span), on or off
188
+ spanModel: vllm-sr/vela3-turbo
189
+ shieldSpans: true # remove injected words, not lines
190
+ guardSpans: true # name the dangerous part of a flagged call
191
+ doneHalu: true # send unsupported claims back
192
+ doneHaluAt: 0.6 # the lowest probability of an unsupported word that counts
193
+ privacy: false # mask personal data before System Two reads it
194
+ privacyAt: 0.5 # the lowest probability of a personal-data span that is masked
195
+ privacyLabels: [PERSON, EMAIL_ADDRESS, PHONE_NUMBER, STREET_ADDRESS, CREDIT_CARD, IBAN_CODE, US_SSN, US_DRIVER_LICENSE, IP_ADDRESS]
196
+ privacyTools: [web_fetch, bash, read]
154
197
  ```
155
198
 
156
199
  `systemOneUi` applies to decisions made after a restart with the new value; decisions already in a session keep the
157
200
  form they were recorded with. The tool's custom row is drawn for the default `toolName`, `system_one`.
158
201
 
202
+ With `privacy: true`, System Two sees `[LABEL]` placeholders instead of personal data in the results of
203
+ `privacyTools`, so it cannot copy a real value into a file it writes from what it read; it is told to ask you
204
+ instead. Span records keep labels and offsets of personal data, never the data itself.
205
+
159
206
  The key comes from the credential reference `VLLM_SR_API_KEY` (`llmKeyEnv`), which both systems use. A separate
160
207
  System One key in `DECISION_API_KEY` (`apiKeyEnv`), as plugin 0.2 kept it, still wins for System One; a 0.2 key for
161
208
  `vllm-sr/auto` keeps working too.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vllm-sr/dsh",
3
- "version": "0.4.1",
4
- "description": "System Duo for DeepSeek Harness. Thinking, fast and slow: System One decision models guard, shield, triage and check your agent around System Two (vllm-sr/auto), with one free app.vllm-sr.ai key.",
3
+ "version": "0.5.0",
4
+ "description": "System Duo for DeepSeek Harness. Thinking, fast and slow: System One decision models guard, shield, triage and check your agent around System Two (vllm-sr/auto), with a free app.vllm-sr.ai key.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "dsh",
package/src/client.js CHANGED
@@ -156,8 +156,8 @@ export function createDecisionClient(options) {
156
156
  * @param {AbortSignal} [request.signal]
157
157
  * @param {number} [request.timeoutMs] - per-call deadline (default: the client's)
158
158
  * @param {number} [request.retries] - per-call retries (default: the client's)
159
- * @returns {Promise<{answers: object[], ms: number, model: string, inputTokens: number, requestId?: string}>}
160
- * answers in input order
159
+ * @returns {Promise<{answers: object[], spans: object[], thresholds: object[], ms: number, model: string, inputTokens: number, requestId?: string}>}
160
+ * answers, span answers and the thresholds applied, in input order
161
161
  */
162
162
  async function ask({ model, questions, inputs, signal, timeoutMs: callTimeoutMs, retries: callRetries }) {
163
163
  if (inputs.length === 0 || inputs.length > ITEMS_PER_REQUEST) {
@@ -176,10 +176,10 @@ export function createDecisionClient(options) {
176
176
  const started = performance.now()
177
177
  try {
178
178
  const { json, requestId } = await post('/v1/decisions', body, signal, { timeoutMs: callTimeoutMs, retries: callRetries })
179
- const answers = [json.answers, ...rest.map((_, index) => json.states?.[`i${index + 1}`]?.answers ?? {})]
179
+ const each = field => [json[field] ?? {}, ...rest.map((_, index) => json.states?.[`i${index + 1}`]?.[field] ?? {})]
180
180
  let inputTokens = json.usage?.input_tokens ?? 0
181
181
  for (const entry of Object.values(json.states ?? {})) inputTokens += entry.usage?.input_tokens ?? 0
182
- return { answers, ms: performance.now() - started, model: json.model ?? model, inputTokens, requestId }
182
+ return { answers: each('answers'), spans: each('spans'), thresholds: each('thresholds'), ms: performance.now() - started, model: json.model ?? model, inputTokens, requestId }
183
183
  } finally {
184
184
  release()
185
185
  }
@@ -190,7 +190,15 @@ export function createDecisionClient(options) {
190
190
  send('/api/info', 'GET', undefined, {}, AbortSignal.timeout(timeoutMs)).catch(() => {})
191
191
  }
192
192
 
193
- return { ask, warm, hasKey: async () => Boolean(await apiKey()), close: () => agent.destroy() }
193
+ /** The published models' cards (no key needed): `[{id, question_types, presets}]`. */
194
+ async function models(signal) {
195
+ const response = await send('/v1/systemone/models', 'GET', undefined, { accept: 'application/json' }, signal ?? AbortSignal.timeout(timeoutMs))
196
+ if (response.status !== 200) throw new DecisionError(`Decision API ${response.status} listing models`, response.status)
197
+ const cards = JSON.parse(response.text)?.data
198
+ return Array.isArray(cards) ? cards : []
199
+ }
200
+
201
+ return { ask, warm, models, hasKey: async () => Boolean(await apiKey()), close: () => agent.destroy() }
194
202
  }
195
203
 
196
204
  /** Probability that a noul answer is yes, or undefined when the question failed. */