dsh-local-ai 0.1.1 → 0.1.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/CHANGELOG.md +7 -0
- package/lib/index.js +57 -2
- 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 +1 -1
- package/src/translate.ts +59 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ 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.2] - 2026-08-20
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- 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`).
|
|
13
|
+
- 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.
|
|
14
|
+
|
|
8
15
|
## [0.1.1] - 2026-08-19
|
|
9
16
|
|
|
10
17
|
### Fixed
|
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 {
|
|
@@ -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.2";
|
|
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.2";
|
|
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.2",
|
|
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",
|
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