dsh-local-ai 0.1.1 → 0.1.3
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/CHANGELOG.md +16 -0
- package/README.es.md +4 -4
- package/README.hi.md +4 -4
- package/README.md +4 -4
- package/README.pt.md +4 -4
- package/README.zh.md +4 -4
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/lib/index.js +58 -3
- package/lib/types/translate.d.ts +13 -0
- package/lib/types/translate.d.ts.map +1 -1
- package/lib/types/translate.js +64 -1
- package/lib/types/translate.js.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/lib/types/version.js +1 -1
- package/package.json +15 -15
- package/src/translate.ts +59 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.3] - 2026-08-21
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Upgraded every `@deepseek-ai/dsh-*` peer and dev dependency from `0.1.0-rc.6` to `0.1.0-rc.8` (peer ranges now `>=0.1.0-rc.8 <0.2.0`) for DeepSeek Harness rc8 compatibility; no adapter, routing, or tool API changes were required.
|
|
13
|
+
- Workspace build policy: allowed the `koffi` native build (introduced by the rc8 `dsh-subprocess-local` dependency chain) and repinned `minimumReleaseAgeExclude` to the rc.8 peer family.
|
|
14
|
+
- Synchronized rc.8 baseline references across the five-language READMEs, AGENTS.md, THIRD_PARTY_NOTICES.md, the CI workflow name, and the compat workflow baseline.
|
|
15
|
+
- Merged the 0.1.2 bug-fix line (tool-call id minting and slot-reuse block handling) so this release carries both fixes.
|
|
16
|
+
|
|
17
|
+
## [0.1.2] - 2026-08-20
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Tool calls are emitted with a minted `CallId` instead of an empty one. Ollama's `/api/chat` wire format carries no tool-call id, so `OpenBlock.callId` was never assigned and both the `tool-call-delta` chunks and the closed block ended up as `CallId('')`. Live turns still paired the call with its result positionally, but the persisted `tool/result` carried an empty `source.callId` and the session failed validation on resume (`message must have tool source`).
|
|
22
|
+
- A tool-call array slot reused for a second, unrelated call now opens its own block instead of being diffed as a continuation. Ollama can reuse the same slot across chunks, and the longest-common-prefix diff previously emitted a fragment that reconstructed into invalid JSON while collapsing the two calls into one block.
|
|
23
|
+
|
|
8
24
|
## [0.1.1] - 2026-08-19
|
|
9
25
|
|
|
10
26
|
### Fixed
|
package/README.es.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Superficie | Estado |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.0-rc.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.0-rc.8` |
|
|
28
28
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
29
29
|
| Backend | [Ollama](https://ollama.com) (API HTTP local + sonda CLI) |
|
|
30
30
|
| Modelo | Ruta solo texto (`inputModalities: ['text']`); se admiten llamadas y resultados de herramientas |
|
|
@@ -143,7 +143,7 @@ Todos los ajustes son campos `Config` de Schemastery (modificables desde cordis.
|
|
|
143
143
|
|
|
144
144
|
## Known limitations
|
|
145
145
|
|
|
146
|
-
- **Solo rc.
|
|
146
|
+
- **Solo rc.8** — desarrollado y probado contra `@deepseek-ai/dsh@0.1.0-rc.8`; se espera que baselines más nuevos funcionen, pero los verifica el workflow mensual de compat.
|
|
147
147
|
- **Ruta solo texto** — el contenido de imagen se rechaza (`UNSUPPORTED_CONTENT`); los modelos locales multimodales aún no están conectados.
|
|
148
148
|
- **Respaldo a mitad de flujo** — una vez que una ruta local empezó a producir contenido, un fallo posterior se reenvía (no se retira); solo un fallo antes del primer token respalda a la nube.
|
|
149
149
|
|
|
@@ -151,8 +151,8 @@ Todos los ajustes son campos `Config` de Schemastery (modificables desde cordis.
|
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
153
|
pnpm install # node ^22.19 || >=24
|
|
154
|
-
pnpm run typecheck # tsc: src + tests contra los tipos publicados 0.1.0-rc.
|
|
155
|
-
pnpm run typecheck:ci # tsc estricto contra los tipos publicados rc.
|
|
154
|
+
pnpm run typecheck # tsc: src + tests contra los tipos publicados 0.1.0-rc.8
|
|
155
|
+
pnpm run typecheck:ci # tsc estricto contra los tipos publicados rc.8 (skipLibCheck off)
|
|
156
156
|
pnpm test # vitest: costuras reales Context/LlmRuntime/ToolRuntime/CommandRuntime/subprocess
|
|
157
157
|
pnpm run test:coverage # puerta de cobertura (90/80/90/90)
|
|
158
158
|
pnpm run build # bundle tsdown + declaraciones tsc (lib/)
|
package/README.hi.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| सतह | स्थिति |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.0-rc.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.0-rc.8` |
|
|
28
28
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
29
29
|
| Backend | [Ollama](https://ollama.com) (स्थानीय HTTP API + CLI जाँच) |
|
|
30
30
|
| Model | केवल-पाठ रूट (`inputModalities: ['text']`); टूल कॉल व परिणाम समर्थित हैं |
|
|
@@ -143,7 +143,7 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-local-ai'
|
|
|
143
143
|
|
|
144
144
|
## Known limitations
|
|
145
145
|
|
|
146
|
-
- **केवल rc.
|
|
146
|
+
- **केवल rc.8** — `@deepseek-ai/dsh@0.1.0-rc.8` के विरुद्ध विकसित व परीक्षित; नए harness बेसलाइन काम करने की अपेक्षा है, पर मासिक compat workflow उन्हें सत्यापित करता है।
|
|
147
147
|
- **केवल-पाठ रूट** — छवि सामग्री अस्वीकृत होती है (`UNSUPPORTED_CONTENT`); मल्टीमॉडल स्थानीय मॉडल अभी जुड़े नहीं हैं।
|
|
148
148
|
- **मध्य-स्ट्रीम वापसी** — एक बार स्थानीय रूट सामग्री बनाना शुरू कर दे, तो बाद की विफलता आगे भेजी जाती है (वापस नहीं ली जाती); केवल पहले टोकन से पहले की विफलता क्लाउड पर लौटती है।
|
|
149
149
|
|
|
@@ -151,8 +151,8 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-local-ai'
|
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
153
|
pnpm install # node ^22.19 || >=24
|
|
154
|
-
pnpm run typecheck # tsc: src + परीक्षण, प्रकाशित 0.1.0-rc.
|
|
155
|
-
pnpm run typecheck:ci # सख्त tsc, प्रकाशित rc.
|
|
154
|
+
pnpm run typecheck # tsc: src + परीक्षण, प्रकाशित 0.1.0-rc.8 प्रकारों के विरुद्ध
|
|
155
|
+
pnpm run typecheck:ci # सख्त tsc, प्रकाशित rc.8 प्रकारों के विरुद्ध (skipLibCheck बंद)
|
|
156
156
|
pnpm test # vitest: वास्तविक Context/LlmRuntime/ToolRuntime/CommandRuntime/subprocess सीम
|
|
157
157
|
pnpm run test:coverage # कवरेज द्वार (90/80/90/90)
|
|
158
158
|
pnpm run build # tsdown बंडल + tsc घोषणाएँ (lib/)
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Surface | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.0-rc.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.0-rc.8` |
|
|
28
28
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
29
29
|
| Backend | [Ollama](https://ollama.com) (local HTTP API + CLI probe) |
|
|
30
30
|
| Model | Text-only route (`inputModalities: ['text']`); tool calls and tool results are supported |
|
|
@@ -143,7 +143,7 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
143
143
|
|
|
144
144
|
## Known limitations
|
|
145
145
|
|
|
146
|
-
- **rc.
|
|
146
|
+
- **rc.8 only** — developed and tested against `@deepseek-ai/dsh@0.1.0-rc.8`; newer harness baselines are expected to work but are verified by the monthly compat workflow.
|
|
147
147
|
- **Text-only route** — image content is rejected (`UNSUPPORTED_CONTENT`); multimodal local models are not wired up yet.
|
|
148
148
|
- **Mid-stream fallback** — once a local route has started producing content, a later failure is forwarded (not retracted); only a failure before the first token falls back to the cloud.
|
|
149
149
|
|
|
@@ -151,8 +151,8 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
153
|
pnpm install # node ^22.19 || >=24
|
|
154
|
-
pnpm run typecheck # tsc: src + tests against the published 0.1.0-rc.
|
|
155
|
-
pnpm run typecheck:ci # strict tsc against published rc.
|
|
154
|
+
pnpm run typecheck # tsc: src + tests against the published 0.1.0-rc.8 types
|
|
155
|
+
pnpm run typecheck:ci # strict tsc against published rc.8 types (skipLibCheck off)
|
|
156
156
|
pnpm test # vitest: real Context/LlmRuntime/ToolRuntime/CommandRuntime/subprocess seams
|
|
157
157
|
pnpm run test:coverage # coverage gate (90/80/90/90)
|
|
158
158
|
pnpm run build # tsdown bundle + tsc declarations (lib/)
|
package/README.pt.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Superfície | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.0-rc.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.0-rc.8` |
|
|
28
28
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
29
29
|
| Backend | [Ollama](https://ollama.com) (API HTTP local + sonda CLI) |
|
|
30
30
|
| Modelo | Rota somente texto (`inputModalities: ['text']`); chamadas e resultados de ferramentas são suportados |
|
|
@@ -143,7 +143,7 @@ Todos os ajustes são campos `Config` de Schemastery (modificáveis pelo cordis.
|
|
|
143
143
|
|
|
144
144
|
## Known limitations
|
|
145
145
|
|
|
146
|
-
- **Somente rc.
|
|
146
|
+
- **Somente rc.8** — desenvolvido e testado contra `@deepseek-ai/dsh@0.1.0-rc.8`; espera-se que baselines mais novos funcionem, mas são verificados pelo workflow mensal de compat.
|
|
147
147
|
- **Rota somente texto** — conteúdo de imagem é rejeitado (`UNSUPPORTED_CONTENT`); modelos locais multimodais ainda não estão conectados.
|
|
148
148
|
- **Fallback no meio do fluxo** — uma vez que uma rota local começou a produzir conteúdo, uma falha posterior é reencaminhada (não retirada); apenas uma falha antes do primeiro token faz fallback para a nuvem.
|
|
149
149
|
|
|
@@ -151,8 +151,8 @@ Todos os ajustes são campos `Config` de Schemastery (modificáveis pelo cordis.
|
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
153
|
pnpm install # node ^22.19 || >=24
|
|
154
|
-
pnpm run typecheck # tsc: src + testes contra os tipos publicados 0.1.0-rc.
|
|
155
|
-
pnpm run typecheck:ci # tsc estrito contra os tipos publicados rc.
|
|
154
|
+
pnpm run typecheck # tsc: src + testes contra os tipos publicados 0.1.0-rc.8
|
|
155
|
+
pnpm run typecheck:ci # tsc estrito contra os tipos publicados rc.8 (skipLibCheck off)
|
|
156
156
|
pnpm test # vitest: costuras reais Context/LlmRuntime/ToolRuntime/CommandRuntime/subprocess
|
|
157
157
|
pnpm run test:coverage # porta de cobertura (90/80/90/90)
|
|
158
158
|
pnpm run build # bundle tsdown + declarações tsc (lib/)
|
package/README.zh.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| 项目 | 状态 |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.0-rc.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.0-rc.8` |
|
|
28
28
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
29
29
|
| 后端 | [Ollama](https://ollama.com)(本地 HTTP API + CLI 探测) |
|
|
30
30
|
| 模型 | 纯文本路由(`inputModalities: ['text']`);支持工具调用与工具结果 |
|
|
@@ -143,7 +143,7 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-local-ai'
|
|
|
143
143
|
|
|
144
144
|
## Known limitations
|
|
145
145
|
|
|
146
|
-
- **仅 rc.
|
|
146
|
+
- **仅 rc.8** —— 针对 `@deepseek-ai/dsh@0.1.0-rc.8` 开发与测试;更新版本的 harness 基线预期可用,但由每月 compat workflow 验证。
|
|
147
147
|
- **纯文本路由** —— 图片内容会被拒绝(`UNSUPPORTED_CONTENT`);多模态本地模型尚未接入。
|
|
148
148
|
- **中途失败不回退** —— 本地路由一旦开始产出内容,之后的失败会透传(无法撤回);只有首个 token 前的失败才回退云端。
|
|
149
149
|
|
|
@@ -151,8 +151,8 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-local-ai'
|
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
153
|
pnpm install # node ^22.19 || >=24
|
|
154
|
-
pnpm run typecheck # tsc:src + 测试,对发布版 0.1.0-rc.
|
|
155
|
-
pnpm run typecheck:ci # 严格 tsc,对发布版 rc.
|
|
154
|
+
pnpm run typecheck # tsc:src + 测试,对发布版 0.1.0-rc.8 类型
|
|
155
|
+
pnpm run typecheck:ci # 严格 tsc,对发布版 rc.8 类型(关闭 skipLibCheck)
|
|
156
156
|
pnpm test # vitest:真实 Context/LlmRuntime/ToolRuntime/CommandRuntime/subprocess 机制
|
|
157
157
|
pnpm run test:coverage # 覆盖率门禁(90/80/90/90)
|
|
158
158
|
pnpm run build # tsdown 打包 + tsc 声明(lib/)
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -13,7 +13,7 @@ published tarball; these are install-time dependencies:
|
|
|
13
13
|
| [typescript](https://github.com/microsoft/TypeScript) | `^5.9.0` | Apache-2.0 | Build-time declaration emission (`lib/types/`) |
|
|
14
14
|
| [@deepseek-ai/cordis](https://www.npmjs.com/package/@deepseek-ai/cordis) | `^4.0.1` (peer) | See package | The plugin runtime |
|
|
15
15
|
| [@deepseek-ai/schemastery](https://www.npmjs.com/package/@deepseek-ai/schemastery) | `^3.18.0` (peer) | See package | Configuration schema |
|
|
16
|
-
| `@deepseek-ai/dsh-*` peers | `0.1.0-rc.
|
|
16
|
+
| `@deepseek-ai/dsh-*` peers | `0.1.0-rc.8` (peer) | See packages | Official harness seams (`dsh-llm`, `dsh-tools`, `dsh-subprocess`, `dsh-commands`, `dsh-timeout`) |
|
|
17
17
|
|
|
18
18
|
At runtime the plugin only talks to the Ollama endpoint you configure (over
|
|
19
19
|
its HTTP API and, for the process-liveness probe, its CLI); it performs no
|
package/lib/index.js
CHANGED
|
@@ -590,6 +590,18 @@ function closeBlock(block) {
|
|
|
590
590
|
}
|
|
591
591
|
}
|
|
592
592
|
/**
|
|
593
|
+
* Mint a session-unique tool-call id for a freshly opened block. Ollama's
|
|
594
|
+
* wire format carries no tool-call id, so one must be synthesized: without it
|
|
595
|
+
* the emitted chunks and the persisted tool result all carry an empty CallId,
|
|
596
|
+
* and the session fails validation on resume. The block index keeps
|
|
597
|
+
* same-millisecond parallel calls apart.
|
|
598
|
+
* @param block - the freshly opened tool-call block.
|
|
599
|
+
* @returns the minted id.
|
|
600
|
+
*/
|
|
601
|
+
function mintCallId(block) {
|
|
602
|
+
return `ollama-${Date.now().toString(36)}-${block.index}-${Math.random().toString(36).slice(2, 8)}`;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
593
605
|
* Compute the append delta from a cumulative JSON string, so the harness's
|
|
594
606
|
* delta-concatenating assembler reconstructs the full arguments. Ollama grows
|
|
595
607
|
* the arguments object monotonically, so the delta is everything past the
|
|
@@ -605,6 +617,36 @@ function argumentsDelta(previous, next) {
|
|
|
605
617
|
return next.slice(index);
|
|
606
618
|
}
|
|
607
619
|
/**
|
|
620
|
+
* Whether the previous cumulative arguments object is a key/value subset of
|
|
621
|
+
* the next one, i.e. one call grew monotonically. While one call streams,
|
|
622
|
+
* Ollama only adds keys; when it reuses a slot for a new call, existing keys
|
|
623
|
+
* reappear with different values, so the subset check fails and the slot must
|
|
624
|
+
* open a fresh block. Non-object arguments (or either side failing to parse,
|
|
625
|
+
* which cannot happen for JSON.stringify output but keeps the guard total)
|
|
626
|
+
* fall back to treating the chunk as a continuation.
|
|
627
|
+
* @param previous - the previously seen cumulative JSON (or `''`).
|
|
628
|
+
* @param next - the new cumulative JSON.
|
|
629
|
+
* @returns `true` when `next` extends `previous` monotonically.
|
|
630
|
+
*/
|
|
631
|
+
function isMonotonicExtension(previous, next) {
|
|
632
|
+
if (previous === "") return true;
|
|
633
|
+
let prevObj;
|
|
634
|
+
let nextObj;
|
|
635
|
+
try {
|
|
636
|
+
prevObj = JSON.parse(previous);
|
|
637
|
+
nextObj = JSON.parse(next);
|
|
638
|
+
} catch {
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
if (typeof prevObj !== "object" || prevObj === null || Array.isArray(prevObj)) return true;
|
|
642
|
+
if (typeof nextObj !== "object" || nextObj === null || Array.isArray(nextObj)) return true;
|
|
643
|
+
for (const [key, value] of Object.entries(prevObj)) {
|
|
644
|
+
if (!(key in nextObj)) return false;
|
|
645
|
+
if (JSON.stringify(nextObj[key]) !== JSON.stringify(value)) return false;
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
608
650
|
* Consume Ollama NDJSON lines and yield StreamChunks. Text and reasoning deltas
|
|
609
651
|
* stream as they arrive; tool-call deltas are diffed from the cumulative wire
|
|
610
652
|
* arguments; `block-end`, `usage`, and `finish` are deferred to the `done`
|
|
@@ -680,6 +722,7 @@ async function* translate(lines) {
|
|
|
680
722
|
let block = toolBlocks.get(callIndex);
|
|
681
723
|
if (!block) {
|
|
682
724
|
block = open("tool-call");
|
|
725
|
+
block.callId = mintCallId(block);
|
|
683
726
|
toolBlocks.set(callIndex, block);
|
|
684
727
|
toolArguments.set(callIndex, "");
|
|
685
728
|
yield {
|
|
@@ -692,7 +735,19 @@ async function* translate(lines) {
|
|
|
692
735
|
const cumulative = JSON.stringify(call?.function?.arguments ?? {});
|
|
693
736
|
const previous = toolArguments.get(callIndex) ?? "";
|
|
694
737
|
if (cumulative !== previous) {
|
|
695
|
-
|
|
738
|
+
if (!isMonotonicExtension(previous, cumulative)) {
|
|
739
|
+
block = open("tool-call");
|
|
740
|
+
block.callId = mintCallId(block);
|
|
741
|
+
if (call?.function?.name !== void 0) block.name = call.function.name;
|
|
742
|
+
toolBlocks.set(callIndex, block);
|
|
743
|
+
toolArguments.set(callIndex, "");
|
|
744
|
+
yield {
|
|
745
|
+
type: "block-start",
|
|
746
|
+
index: block.index,
|
|
747
|
+
blockType: "tool-call"
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
const fragment = argumentsDelta(toolArguments.get(callIndex) ?? "", cumulative);
|
|
696
751
|
toolArguments.set(callIndex, cumulative);
|
|
697
752
|
block.text += fragment;
|
|
698
753
|
yield {
|
|
@@ -737,7 +792,7 @@ async function* translate(lines) {
|
|
|
737
792
|
throw new LlmError("Ollama NDJSON stream ended without a done chunk", "STREAM_CLOSED");
|
|
738
793
|
}
|
|
739
794
|
//#endregion
|
|
740
|
-
//#region \0@oxc-project+runtime@0.
|
|
795
|
+
//#region \0@oxc-project+runtime@0.146.0/helpers/esm/usingCtx.js
|
|
741
796
|
function _usingCtx() {
|
|
742
797
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
743
798
|
var n = Error();
|
|
@@ -1101,7 +1156,7 @@ async function checkHealth(baseURL, fetchImpl, subprocess, requestTimeoutMs, gra
|
|
|
1101
1156
|
* The `dsh-local-ai` plugin version. The release script bumps this string
|
|
1102
1157
|
* alongside `package.json`; `test/version.spec.ts` trips when the two drift.
|
|
1103
1158
|
*/
|
|
1104
|
-
const VERSION = "0.1.
|
|
1159
|
+
const VERSION = "0.1.3";
|
|
1105
1160
|
//#endregion
|
|
1106
1161
|
//#region src/index.ts
|
|
1107
1162
|
const name = "local-ai";
|
package/lib/types/translate.d.ts
CHANGED
|
@@ -44,6 +44,19 @@ export declare function mapFinishReason(reason: string): FinishReason;
|
|
|
44
44
|
* @returns the fragment to append.
|
|
45
45
|
*/
|
|
46
46
|
export declare function argumentsDelta(previous: string, next: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the previous cumulative arguments object is a key/value subset of
|
|
49
|
+
* the next one, i.e. one call grew monotonically. While one call streams,
|
|
50
|
+
* Ollama only adds keys; when it reuses a slot for a new call, existing keys
|
|
51
|
+
* reappear with different values, so the subset check fails and the slot must
|
|
52
|
+
* open a fresh block. Non-object arguments (or either side failing to parse,
|
|
53
|
+
* which cannot happen for JSON.stringify output but keeps the guard total)
|
|
54
|
+
* fall back to treating the chunk as a continuation.
|
|
55
|
+
* @param previous - the previously seen cumulative JSON (or `''`).
|
|
56
|
+
* @param next - the new cumulative JSON.
|
|
57
|
+
* @returns `true` when `next` extends `previous` monotonically.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isMonotonicExtension(previous: string, next: string): boolean;
|
|
47
60
|
/**
|
|
48
61
|
* Consume Ollama NDJSON lines and yield StreamChunks. Text and reasoning deltas
|
|
49
62
|
* stream as they arrive; tool-call deltas are diffed from the cumulative wire
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAgB,YAAY,EAAE,WAAW,EAAc,MAAM,sBAAsB,CAAA;AAE/F,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,CAAA;SAAE,CAAC,CAAA;KAC1F,CAAA;IACD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAYD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAW5D;
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAgB,YAAY,EAAE,WAAW,EAAc,MAAM,sBAAsB,CAAA;AAE/F,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,CAAA;SAAE,CAAC,CAAA;KAC1F,CAAA;IACD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAYD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAW5D;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiB5E;AAED;;;;;;;;GAQG;AACH,wBAAuB,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,WAAW,CAAC,CA0H1F"}
|
package/lib/types/translate.js
CHANGED
|
@@ -38,6 +38,18 @@ function closeBlock(block) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Mint a session-unique tool-call id for a freshly opened block. Ollama's
|
|
43
|
+
* wire format carries no tool-call id, so one must be synthesized: without it
|
|
44
|
+
* the emitted chunks and the persisted tool result all carry an empty CallId,
|
|
45
|
+
* and the session fails validation on resume. The block index keeps
|
|
46
|
+
* same-millisecond parallel calls apart.
|
|
47
|
+
* @param block - the freshly opened tool-call block.
|
|
48
|
+
* @returns the minted id.
|
|
49
|
+
*/
|
|
50
|
+
function mintCallId(block) {
|
|
51
|
+
return `ollama-${Date.now().toString(36)}-${block.index}-${Math.random().toString(36).slice(2, 8)}`;
|
|
52
|
+
}
|
|
41
53
|
/**
|
|
42
54
|
* Compute the append delta from a cumulative JSON string, so the harness's
|
|
43
55
|
* delta-concatenating assembler reconstructs the full arguments. Ollama grows
|
|
@@ -55,6 +67,42 @@ export function argumentsDelta(previous, next) {
|
|
|
55
67
|
index += 1;
|
|
56
68
|
return next.slice(index);
|
|
57
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether the previous cumulative arguments object is a key/value subset of
|
|
72
|
+
* the next one, i.e. one call grew monotonically. While one call streams,
|
|
73
|
+
* Ollama only adds keys; when it reuses a slot for a new call, existing keys
|
|
74
|
+
* reappear with different values, so the subset check fails and the slot must
|
|
75
|
+
* open a fresh block. Non-object arguments (or either side failing to parse,
|
|
76
|
+
* which cannot happen for JSON.stringify output but keeps the guard total)
|
|
77
|
+
* fall back to treating the chunk as a continuation.
|
|
78
|
+
* @param previous - the previously seen cumulative JSON (or `''`).
|
|
79
|
+
* @param next - the new cumulative JSON.
|
|
80
|
+
* @returns `true` when `next` extends `previous` monotonically.
|
|
81
|
+
*/
|
|
82
|
+
export function isMonotonicExtension(previous, next) {
|
|
83
|
+
if (previous === '')
|
|
84
|
+
return true;
|
|
85
|
+
let prevObj;
|
|
86
|
+
let nextObj;
|
|
87
|
+
try {
|
|
88
|
+
prevObj = JSON.parse(previous);
|
|
89
|
+
nextObj = JSON.parse(next);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (typeof prevObj !== 'object' || prevObj === null || Array.isArray(prevObj))
|
|
95
|
+
return true;
|
|
96
|
+
if (typeof nextObj !== 'object' || nextObj === null || Array.isArray(nextObj))
|
|
97
|
+
return true;
|
|
98
|
+
for (const [key, value] of Object.entries(prevObj)) {
|
|
99
|
+
if (!(key in nextObj))
|
|
100
|
+
return false;
|
|
101
|
+
if (JSON.stringify(nextObj[key]) !== JSON.stringify(value))
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
58
106
|
/**
|
|
59
107
|
* Consume Ollama NDJSON lines and yield StreamChunks. Text and reasoning deltas
|
|
60
108
|
* stream as they arrive; tool-call deltas are diffed from the cumulative wire
|
|
@@ -115,6 +163,7 @@ export async function* translate(lines) {
|
|
|
115
163
|
let block = toolBlocks.get(callIndex);
|
|
116
164
|
if (!block) {
|
|
117
165
|
block = open('tool-call');
|
|
166
|
+
block.callId = mintCallId(block);
|
|
118
167
|
toolBlocks.set(callIndex, block);
|
|
119
168
|
toolArguments.set(callIndex, '');
|
|
120
169
|
yield { type: 'block-start', index: block.index, blockType: 'tool-call' };
|
|
@@ -125,7 +174,21 @@ export async function* translate(lines) {
|
|
|
125
174
|
const cumulative = JSON.stringify(call?.function?.arguments ?? {});
|
|
126
175
|
const previous = toolArguments.get(callIndex) ?? '';
|
|
127
176
|
if (cumulative !== previous) {
|
|
128
|
-
|
|
177
|
+
if (!isMonotonicExtension(previous, cumulative)) {
|
|
178
|
+
// The cumulative arguments no longer extend the ones this slot has
|
|
179
|
+
// seen: Ollama reused the array slot for a second, unrelated call.
|
|
180
|
+
// Diffing against the old call would emit a fragment that
|
|
181
|
+
// reconstructs into invalid JSON and collapse the two calls into
|
|
182
|
+
// one block, so open a fresh block for the new call instead.
|
|
183
|
+
block = open('tool-call');
|
|
184
|
+
block.callId = mintCallId(block);
|
|
185
|
+
if (call?.function?.name !== undefined)
|
|
186
|
+
block.name = call.function.name;
|
|
187
|
+
toolBlocks.set(callIndex, block);
|
|
188
|
+
toolArguments.set(callIndex, '');
|
|
189
|
+
yield { type: 'block-start', index: block.index, blockType: 'tool-call' };
|
|
190
|
+
}
|
|
191
|
+
const fragment = argumentsDelta(toolArguments.get(callIndex) ?? '', cumulative);
|
|
129
192
|
toolArguments.set(callIndex, cumulative);
|
|
130
193
|
block.text += fragment;
|
|
131
194
|
yield {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/translate.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/translate.ts"],"names":[],"mappings":"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"}
|
package/lib/types/version.d.ts
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* The `dsh-local-ai` plugin version. The release script bumps this string
|
|
3
3
|
* alongside `package.json`; `test/version.spec.ts` trips when the two drift.
|
|
4
4
|
*/
|
|
5
|
-
export declare const VERSION = "0.1.
|
|
5
|
+
export declare const VERSION = "0.1.3";
|
|
6
6
|
//# sourceMappingURL=version.d.ts.map
|
package/lib/types/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-local-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Local-model (Ollama) integration for DeepSeek Harness: discover, pull, remove, and inspect local models, route requests to them by task type or keyword with automatic fallback to the cloud, and get a one-shot status overview via /ollama.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
],
|
|
65
65
|
"compatibility": {
|
|
66
66
|
"dshVersions": [
|
|
67
|
-
"0.1.0-rc.
|
|
67
|
+
"0.1.0-rc.8"
|
|
68
68
|
]
|
|
69
69
|
},
|
|
70
70
|
"capability": {
|
|
@@ -99,11 +99,11 @@
|
|
|
99
99
|
"packageManager": "pnpm@11.7.0",
|
|
100
100
|
"peerDependencies": {
|
|
101
101
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
102
|
-
"@deepseek-ai/dsh-commands": "0.1.0-rc.
|
|
103
|
-
"@deepseek-ai/dsh-llm": "0.1.0-rc.
|
|
104
|
-
"@deepseek-ai/dsh-subprocess": "0.1.0-rc.
|
|
105
|
-
"@deepseek-ai/dsh-timeout": "0.1.0-rc.
|
|
106
|
-
"@deepseek-ai/dsh-tools": "0.1.0-rc.
|
|
102
|
+
"@deepseek-ai/dsh-commands": ">=0.1.0-rc.8 <0.2.0",
|
|
103
|
+
"@deepseek-ai/dsh-llm": ">=0.1.0-rc.8 <0.2.0",
|
|
104
|
+
"@deepseek-ai/dsh-subprocess": ">=0.1.0-rc.8 <0.2.0",
|
|
105
|
+
"@deepseek-ai/dsh-timeout": ">=0.1.0-rc.8 <0.2.0",
|
|
106
|
+
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.8 <0.2.0",
|
|
107
107
|
"@deepseek-ai/schemastery": "^3.18.0"
|
|
108
108
|
},
|
|
109
109
|
"dependencies": {
|
|
@@ -114,14 +114,14 @@
|
|
|
114
114
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
115
115
|
"@deepseek-ai/cordis-plugin-include": "1.0.6",
|
|
116
116
|
"@deepseek-ai/cordis-plugin-loader": "1.0.2",
|
|
117
|
-
"@deepseek-ai/dsh-commands": "0.1.0-rc.
|
|
118
|
-
"@deepseek-ai/dsh-llm": "0.1.0-rc.
|
|
119
|
-
"@deepseek-ai/dsh-session": "0.1.0-rc.
|
|
120
|
-
"@deepseek-ai/dsh-subprocess": "0.1.0-rc.
|
|
121
|
-
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.
|
|
122
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.
|
|
123
|
-
"@deepseek-ai/dsh-timeout": "0.1.0-rc.
|
|
124
|
-
"@deepseek-ai/dsh-tools": "0.1.0-rc.
|
|
117
|
+
"@deepseek-ai/dsh-commands": "0.1.0-rc.8",
|
|
118
|
+
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
|
|
119
|
+
"@deepseek-ai/dsh-session": "0.1.0-rc.8",
|
|
120
|
+
"@deepseek-ai/dsh-subprocess": "0.1.0-rc.8",
|
|
121
|
+
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.8",
|
|
122
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.0-rc.8",
|
|
123
|
+
"@deepseek-ai/dsh-timeout": "0.1.0-rc.8",
|
|
124
|
+
"@deepseek-ai/dsh-tools": "0.1.0-rc.8",
|
|
125
125
|
"@deepseek-ai/schemastery": "^3.18.0",
|
|
126
126
|
"@types/node": "^22.19.0",
|
|
127
127
|
"@vitest/coverage-v8": "^3.2.0",
|
package/src/translate.ts
CHANGED
|
@@ -69,6 +69,19 @@ function closeBlock(block: OpenBlock): ContentBlock {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Mint a session-unique tool-call id for a freshly opened block. Ollama's
|
|
74
|
+
* wire format carries no tool-call id, so one must be synthesized: without it
|
|
75
|
+
* the emitted chunks and the persisted tool result all carry an empty CallId,
|
|
76
|
+
* and the session fails validation on resume. The block index keeps
|
|
77
|
+
* same-millisecond parallel calls apart.
|
|
78
|
+
* @param block - the freshly opened tool-call block.
|
|
79
|
+
* @returns the minted id.
|
|
80
|
+
*/
|
|
81
|
+
function mintCallId(block: OpenBlock): string {
|
|
82
|
+
return `ollama-${Date.now().toString(36)}-${block.index}-${Math.random().toString(36).slice(2, 8)}`
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
/**
|
|
73
86
|
* Compute the append delta from a cumulative JSON string, so the harness's
|
|
74
87
|
* delta-concatenating assembler reconstructs the full arguments. Ollama grows
|
|
@@ -85,6 +98,37 @@ export function argumentsDelta(previous: string, next: string): string {
|
|
|
85
98
|
return next.slice(index)
|
|
86
99
|
}
|
|
87
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Whether the previous cumulative arguments object is a key/value subset of
|
|
103
|
+
* the next one, i.e. one call grew monotonically. While one call streams,
|
|
104
|
+
* Ollama only adds keys; when it reuses a slot for a new call, existing keys
|
|
105
|
+
* reappear with different values, so the subset check fails and the slot must
|
|
106
|
+
* open a fresh block. Non-object arguments (or either side failing to parse,
|
|
107
|
+
* which cannot happen for JSON.stringify output but keeps the guard total)
|
|
108
|
+
* fall back to treating the chunk as a continuation.
|
|
109
|
+
* @param previous - the previously seen cumulative JSON (or `''`).
|
|
110
|
+
* @param next - the new cumulative JSON.
|
|
111
|
+
* @returns `true` when `next` extends `previous` monotonically.
|
|
112
|
+
*/
|
|
113
|
+
export function isMonotonicExtension(previous: string, next: string): boolean {
|
|
114
|
+
if (previous === '') return true
|
|
115
|
+
let prevObj: unknown
|
|
116
|
+
let nextObj: unknown
|
|
117
|
+
try {
|
|
118
|
+
prevObj = JSON.parse(previous)
|
|
119
|
+
nextObj = JSON.parse(next)
|
|
120
|
+
} catch {
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
if (typeof prevObj !== 'object' || prevObj === null || Array.isArray(prevObj)) return true
|
|
124
|
+
if (typeof nextObj !== 'object' || nextObj === null || Array.isArray(nextObj)) return true
|
|
125
|
+
for (const [key, value] of Object.entries(prevObj)) {
|
|
126
|
+
if (!(key in nextObj)) return false
|
|
127
|
+
if (JSON.stringify((nextObj as Record<string, unknown>)[key]) !== JSON.stringify(value)) return false
|
|
128
|
+
}
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
88
132
|
/**
|
|
89
133
|
* Consume Ollama NDJSON lines and yield StreamChunks. Text and reasoning deltas
|
|
90
134
|
* stream as they arrive; tool-call deltas are diffed from the cumulative wire
|
|
@@ -150,6 +194,7 @@ export async function* translate(lines: AsyncIterable<string>): AsyncGenerator<S
|
|
|
150
194
|
let block = toolBlocks.get(callIndex)
|
|
151
195
|
if (!block) {
|
|
152
196
|
block = open('tool-call')
|
|
197
|
+
block.callId = mintCallId(block)
|
|
153
198
|
toolBlocks.set(callIndex, block)
|
|
154
199
|
toolArguments.set(callIndex, '')
|
|
155
200
|
yield { type: 'block-start', index: block.index, blockType: 'tool-call' }
|
|
@@ -160,7 +205,20 @@ export async function* translate(lines: AsyncIterable<string>): AsyncGenerator<S
|
|
|
160
205
|
const cumulative = JSON.stringify(call?.function?.arguments ?? {})
|
|
161
206
|
const previous = toolArguments.get(callIndex) ?? ''
|
|
162
207
|
if (cumulative !== previous) {
|
|
163
|
-
|
|
208
|
+
if (!isMonotonicExtension(previous, cumulative)) {
|
|
209
|
+
// The cumulative arguments no longer extend the ones this slot has
|
|
210
|
+
// seen: Ollama reused the array slot for a second, unrelated call.
|
|
211
|
+
// Diffing against the old call would emit a fragment that
|
|
212
|
+
// reconstructs into invalid JSON and collapse the two calls into
|
|
213
|
+
// one block, so open a fresh block for the new call instead.
|
|
214
|
+
block = open('tool-call')
|
|
215
|
+
block.callId = mintCallId(block)
|
|
216
|
+
if (call?.function?.name !== undefined) block.name = call.function.name
|
|
217
|
+
toolBlocks.set(callIndex, block)
|
|
218
|
+
toolArguments.set(callIndex, '')
|
|
219
|
+
yield { type: 'block-start', index: block.index, blockType: 'tool-call' }
|
|
220
|
+
}
|
|
221
|
+
const fragment = argumentsDelta(toolArguments.get(callIndex) ?? '', cumulative)
|
|
164
222
|
toolArguments.set(callIndex, cumulative)
|
|
165
223
|
block.text += fragment
|
|
166
224
|
yield {
|
package/src/version.ts
CHANGED