@deepseek-ai/dsh-llm 0.1.0-rc.6 → 0.1.0-rc.7
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.i18n.yaml +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/index.js +34 -5
- package/lib/types/assembler.d.ts +13 -3
- package/lib/types/assembler.js +31 -7
- package/lib/types/types.d.ts +22 -2
- package/package.json +10 -10
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/llm/llm/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: fb6bd84240b41dd730d45b3eb34c35827dc4c991
|
|
6
|
+
README.zh.md: 5c22767a7c654972cbf614505382fa4755d7d318
|
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ Exact-model metadata is a separate correctness query, not a catalog decoration o
|
|
|
53
53
|
|
|
54
54
|
Message content is an array of typed blocks: `text`, `reasoning`, `tool-call`, `tool-result`. The union is derived from the merge-extensible `ContentBlockMap`, so plugins can add block types via declaration merging. Assistant messages use a model source carrying the provider and model that produced them plus optional adapter-private replay state. Before dispatch, `LlmRuntime` retains that state only when the historical provider route and target provider route are currently owned by the exact same adapter instance; the adapter then decides whether it can restore or convert the state across models/providers. The core block set is limited to blocks every shipping path honors — multimodal content (images, audio, …) has no core block type; a feature that needs one adds it via the map together with the adapter/UI/compaction support that honors it.
|
|
55
55
|
|
|
56
|
-
Streaming is a raw chunk protocol (`block-start`, `text-delta`, `reasoning-delta`, `tool-call-delta`, `block-end`, `usage`, `finish`). Every adapter outcome reaches consumers as one terminal `finish`; operational failure uses its `error` or `aborted` reason rather than throwing across the stream API. `BlockAssembler` is the single shared implementation that assembles chunks into blocks/messages.
|
|
56
|
+
Streaming is a raw chunk protocol (`block-start`, `text-delta`, `reasoning-delta`, `tool-call-delta`, `block-end`, `usage`, `finish`). Every adapter outcome reaches consumers as one terminal `finish`; operational failure uses its `error` or `aborted` reason rather than throwing across the stream API. `BlockAssembler` is the single shared implementation that assembles chunks into blocks/messages. A successful `finish` may carry a `ReplayEnvelope` — opaque response-level replay metadata plus optional per-block entries aligned with the emitted block sequence. Assembly makes one keep/drop decision for content and metadata together: a `max-tokens` finish drops tool calls that may have been truncated, and the envelope loses the entry at each dropped position, so stored metadata always describes stored content.
|
|
57
57
|
|
|
58
58
|
### Call configuration (`call-config.ts`)
|
|
59
59
|
|
package/README.zh.md
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
消息内容是类型化内容块数组:`text`、`reasoning`、`tool-call`、`tool-result`。联合从可合并扩展的 `ContentBlockMap` 派生,因此插件可以通过 declaration merging 添加块类型。assistant 消息使用模型来源,其中携带生成该消息的提供方和模型,以及可选的适配器私有回放状态。dispatch 前,`LlmRuntime` 只在历史提供方路由与目标提供方路由当前由完全相同的适配器实例拥有时才保留该状态;随后由适配器判定能否在模型/提供方间恢复或转换该状态。核心块集只包含每条已发布路径都支持的块。多模态内容(图像、音频等)没有核心块类型;需要它的功能会通过 map 添加,并一并添加相应的适配器/UI/压缩(compaction)支持。
|
|
55
55
|
|
|
56
|
-
流式输出是原始分片协议(`block-start`、`text-delta`、`reasoning-delta`、`tool-call-delta`、`block-end`、`usage`、`finish`)。每个适配器结果都以一个终止 `finish` 到达消费方;运行故障使用 `error` 或 `aborted` 作为结束原因,而不会跨流 API 抛出。`BlockAssembler`
|
|
56
|
+
流式输出是原始分片协议(`block-start`、`text-delta`、`reasoning-delta`、`tool-call-delta`、`block-end`、`usage`、`finish`)。每个适配器结果都以一个终止 `finish` 到达消费方;运行故障使用 `error` 或 `aborted` 作为结束原因,而不会跨流 API 抛出。`BlockAssembler` 是将分片组装为块/消息的唯一共享实现。成功的 `finish` 可以携带 `ReplayEnvelope`——不透明的响应级回放元数据,加上与发射块序列对齐的可选逐块条目。组装对内容与元数据只做一次保留/丢弃决定:`max-tokens` 结束会丢弃可能被截断的工具调用,数据在每个被丢弃的位置同步失去对应条目,因此存储的元数据始终描述存储的内容。
|
|
57
57
|
|
|
58
58
|
### 调用配置(`call-config.ts`)
|
|
59
59
|
|
package/lib/index.js
CHANGED
|
@@ -675,7 +675,7 @@ var BlockAssembler = class {
|
|
|
675
675
|
order = [];
|
|
676
676
|
_usage;
|
|
677
677
|
_finish;
|
|
678
|
-
_replayState
|
|
678
|
+
_replayState;
|
|
679
679
|
/**
|
|
680
680
|
* Feed one chunk into the assembly state.
|
|
681
681
|
* @param chunk - the next raw chunk, in stream order.
|
|
@@ -763,14 +763,39 @@ var BlockAssembler = class {
|
|
|
763
763
|
return partial;
|
|
764
764
|
}
|
|
765
765
|
/**
|
|
766
|
+
* The one shared keep/drop decision over all seen blocks: max-token
|
|
767
|
+
* truncation drops tool calls that cannot be executed safely. Emitted blocks
|
|
768
|
+
* and replay metadata both derive from this result, so they cannot disagree.
|
|
769
|
+
*/
|
|
770
|
+
assembled() {
|
|
771
|
+
const all = this.order.map((index) => this.assemble(this.mustGet(index), index));
|
|
772
|
+
const kept = this.finish.kind === "max-tokens" ? all.map((block) => block.type !== "tool-call") : void 0;
|
|
773
|
+
const blocks = kept === void 0 ? all : all.filter((_, position) => kept[position]);
|
|
774
|
+
const envelope = this._replayState;
|
|
775
|
+
if (envelope?.blocks === void 0) return {
|
|
776
|
+
blocks,
|
|
777
|
+
replay: envelope
|
|
778
|
+
};
|
|
779
|
+
if (envelope.blocks.length !== all.length) return {
|
|
780
|
+
blocks,
|
|
781
|
+
replay: void 0
|
|
782
|
+
};
|
|
783
|
+
return {
|
|
784
|
+
blocks,
|
|
785
|
+
replay: kept === void 0 || blocks.length === all.length ? envelope : {
|
|
786
|
+
response: envelope.response,
|
|
787
|
+
blocks: envelope.blocks.filter((_, position) => kept[position])
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
766
792
|
* Assemble all blocks seen so far, in stream order.
|
|
767
793
|
* @returns one block per seen index, except that max-token truncation drops
|
|
768
794
|
* tool calls that cannot be executed safely; an open block assembles from
|
|
769
795
|
* its accumulated deltas (an unknown block type never closed by `block-end` throws).
|
|
770
796
|
*/
|
|
771
797
|
blocks() {
|
|
772
|
-
|
|
773
|
-
return this.finish.kind === "max-tokens" ? blocks.filter((block) => block.type !== "tool-call") : blocks;
|
|
798
|
+
return this.assembled().blocks;
|
|
774
799
|
}
|
|
775
800
|
/** Usage from the `usage` chunk; undefined until one arrives. */
|
|
776
801
|
get usage() {
|
|
@@ -780,9 +805,13 @@ var BlockAssembler = class {
|
|
|
780
805
|
get finish() {
|
|
781
806
|
return this._finish ?? { kind: "stop" };
|
|
782
807
|
}
|
|
783
|
-
/**
|
|
808
|
+
/**
|
|
809
|
+
* Replay metadata from the terminal finish chunk, if any, with per-block
|
|
810
|
+
* entries pruned in step with {@link blocks}. Undefined when the envelope's
|
|
811
|
+
* entries do not align with the emitted blocks.
|
|
812
|
+
*/
|
|
784
813
|
get replayState() {
|
|
785
|
-
return this.
|
|
814
|
+
return this.assembled().replay;
|
|
786
815
|
}
|
|
787
816
|
/**
|
|
788
817
|
* The assembled assistant message.
|
package/lib/types/assembler.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module @deepseek-ai/dsh-llm/assembler
|
|
7
7
|
*/
|
|
8
8
|
import type { Message, MessageSource } from './message.ts';
|
|
9
|
-
import type { ContentBlock, FinishReason, StreamChunk, TokenUsage } from './types.ts';
|
|
9
|
+
import type { ContentBlock, FinishReason, ReplayEnvelope, StreamChunk, TokenUsage } from './types.ts';
|
|
10
10
|
/**
|
|
11
11
|
* Incrementally assembles raw {@link StreamChunk}s into complete
|
|
12
12
|
* {@link ContentBlock}s and a final assistant {@link Message}.
|
|
@@ -33,6 +33,12 @@ export declare class BlockAssembler {
|
|
|
33
33
|
private assemble;
|
|
34
34
|
/** Invariant accessor: every index in `order` has a partial. */
|
|
35
35
|
private mustGet;
|
|
36
|
+
/**
|
|
37
|
+
* The one shared keep/drop decision over all seen blocks: max-token
|
|
38
|
+
* truncation drops tool calls that cannot be executed safely. Emitted blocks
|
|
39
|
+
* and replay metadata both derive from this result, so they cannot disagree.
|
|
40
|
+
*/
|
|
41
|
+
private assembled;
|
|
36
42
|
/**
|
|
37
43
|
* Assemble all blocks seen so far, in stream order.
|
|
38
44
|
* @returns one block per seen index, except that max-token truncation drops
|
|
@@ -44,8 +50,12 @@ export declare class BlockAssembler {
|
|
|
44
50
|
get usage(): TokenUsage | undefined;
|
|
45
51
|
/** Finish reason from the `finish` chunk; `{kind: 'stop'}` when the stream ended without one. */
|
|
46
52
|
get finish(): FinishReason;
|
|
47
|
-
/**
|
|
48
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Replay metadata from the terminal finish chunk, if any, with per-block
|
|
55
|
+
* entries pruned in step with {@link blocks}. Undefined when the envelope's
|
|
56
|
+
* entries do not align with the emitted blocks.
|
|
57
|
+
*/
|
|
58
|
+
get replayState(): ReplayEnvelope | undefined;
|
|
49
59
|
/**
|
|
50
60
|
* The assembled assistant message.
|
|
51
61
|
* @param source - producer attribution for the assembled message.
|
package/lib/types/assembler.js
CHANGED
|
@@ -24,7 +24,7 @@ export class BlockAssembler {
|
|
|
24
24
|
order = [];
|
|
25
25
|
_usage;
|
|
26
26
|
_finish;
|
|
27
|
-
_replayState
|
|
27
|
+
_replayState;
|
|
28
28
|
/**
|
|
29
29
|
* Feed one chunk into the assembly state.
|
|
30
30
|
* @param chunk - the next raw chunk, in stream order.
|
|
@@ -112,6 +112,29 @@ export class BlockAssembler {
|
|
|
112
112
|
throw new Error(`BlockAssembler invariant violated: no partial for index ${index}`);
|
|
113
113
|
return partial;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* The one shared keep/drop decision over all seen blocks: max-token
|
|
117
|
+
* truncation drops tool calls that cannot be executed safely. Emitted blocks
|
|
118
|
+
* and replay metadata both derive from this result, so they cannot disagree.
|
|
119
|
+
*/
|
|
120
|
+
assembled() {
|
|
121
|
+
const all = this.order.map(index => this.assemble(this.mustGet(index), index));
|
|
122
|
+
const kept = this.finish.kind === 'max-tokens'
|
|
123
|
+
? all.map(block => block.type !== 'tool-call')
|
|
124
|
+
: undefined;
|
|
125
|
+
const blocks = kept === undefined ? all : all.filter((_, position) => kept[position]);
|
|
126
|
+
const envelope = this._replayState;
|
|
127
|
+
if (envelope?.blocks === undefined)
|
|
128
|
+
return { blocks, replay: envelope };
|
|
129
|
+
if (envelope.blocks.length !== all.length)
|
|
130
|
+
return { blocks, replay: undefined };
|
|
131
|
+
return {
|
|
132
|
+
blocks,
|
|
133
|
+
replay: kept === undefined || blocks.length === all.length
|
|
134
|
+
? envelope
|
|
135
|
+
: { response: envelope.response, blocks: envelope.blocks.filter((_, position) => kept[position]) },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
115
138
|
/**
|
|
116
139
|
* Assemble all blocks seen so far, in stream order.
|
|
117
140
|
* @returns one block per seen index, except that max-token truncation drops
|
|
@@ -119,10 +142,7 @@ export class BlockAssembler {
|
|
|
119
142
|
* its accumulated deltas (an unknown block type never closed by `block-end` throws).
|
|
120
143
|
*/
|
|
121
144
|
blocks() {
|
|
122
|
-
|
|
123
|
-
return this.finish.kind === 'max-tokens'
|
|
124
|
-
? blocks.filter(block => block.type !== 'tool-call')
|
|
125
|
-
: blocks;
|
|
145
|
+
return this.assembled().blocks;
|
|
126
146
|
}
|
|
127
147
|
/** Usage from the `usage` chunk; undefined until one arrives. */
|
|
128
148
|
get usage() {
|
|
@@ -132,9 +152,13 @@ export class BlockAssembler {
|
|
|
132
152
|
get finish() {
|
|
133
153
|
return this._finish ?? { kind: 'stop' };
|
|
134
154
|
}
|
|
135
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Replay metadata from the terminal finish chunk, if any, with per-block
|
|
157
|
+
* entries pruned in step with {@link blocks}. Undefined when the envelope's
|
|
158
|
+
* entries do not align with the emitted blocks.
|
|
159
|
+
*/
|
|
136
160
|
get replayState() {
|
|
137
|
-
return this.
|
|
161
|
+
return this.assembled().replay;
|
|
138
162
|
}
|
|
139
163
|
/**
|
|
140
164
|
* The assembled assistant message.
|
package/lib/types/types.d.ts
CHANGED
|
@@ -256,6 +256,26 @@ export interface LlmResolvedModelInfo extends LlmModelInfo {
|
|
|
256
256
|
/** Adapter-owned selectable reasoning levels when exposed. */
|
|
257
257
|
reasoning?: LlmModelReasoningInfo;
|
|
258
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Adapter-private lossless-JSON state for replaying a successful response,
|
|
261
|
+
* carried by a terminal `finish` chunk and stored on the assembled assistant
|
|
262
|
+
* message's model source. Both halves stay opaque to the harness; only the
|
|
263
|
+
* split is shared vocabulary, so assembly can keep stored metadata aligned
|
|
264
|
+
* with stored content without reading either half.
|
|
265
|
+
*/
|
|
266
|
+
export interface ReplayEnvelope {
|
|
267
|
+
/** Response-level adapter-private metadata (ids, native stop reason). */
|
|
268
|
+
response: unknown;
|
|
269
|
+
/**
|
|
270
|
+
* Per-block adapter-private metadata, one entry per emitted block in
|
|
271
|
+
* first-seen stream order. When assembly drops a block it drops the entry at
|
|
272
|
+
* the same position; entries whose length does not match the emitted block
|
|
273
|
+
* count discard the whole envelope. An adapter whose metadata is independent
|
|
274
|
+
* of block structure omits this field and the envelope passes through
|
|
275
|
+
* assembly unchanged.
|
|
276
|
+
*/
|
|
277
|
+
blocks?: readonly unknown[];
|
|
278
|
+
}
|
|
259
279
|
/**
|
|
260
280
|
* Raw streaming protocol emitted by adapters.
|
|
261
281
|
* Block indexes correlate interleaved deltas, and `block-end` carries the
|
|
@@ -292,8 +312,8 @@ export type StreamChunk = {
|
|
|
292
312
|
} | {
|
|
293
313
|
type: 'finish';
|
|
294
314
|
reason: FinishReason;
|
|
295
|
-
/**
|
|
296
|
-
replayState?:
|
|
315
|
+
/** Replay metadata for a successful response; see {@link ReplayEnvelope}. */
|
|
316
|
+
replayState?: ReplayEnvelope;
|
|
297
317
|
};
|
|
298
318
|
/**
|
|
299
319
|
* JSON-schema description of a tool, as sent to the model.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-llm",
|
|
3
3
|
"description": "Provider-neutral LLM service interface for the DeepSeek Harness",
|
|
4
|
-
"version": "0.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.7",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -45,20 +45,20 @@
|
|
|
45
45
|
],
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.
|
|
49
|
-
"@deepseek-ai/dsh-
|
|
50
|
-
"@deepseek-ai/dsh-
|
|
51
|
-
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.
|
|
48
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.7",
|
|
49
|
+
"@deepseek-ai/dsh-brand": "^0.1.0-rc.7",
|
|
50
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
|
|
51
|
+
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.7",
|
|
52
52
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.
|
|
59
|
-
"@deepseek-ai/dsh-brand": "^0.1.0-rc.
|
|
60
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.
|
|
61
|
-
"@deepseek-ai/
|
|
62
|
-
"@deepseek-ai/
|
|
58
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.7",
|
|
59
|
+
"@deepseek-ai/dsh-brand": "^0.1.0-rc.7",
|
|
60
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
|
|
61
|
+
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.7",
|
|
62
|
+
"@deepseek-ai/cordis": "^4.0.1"
|
|
63
63
|
}
|
|
64
64
|
}
|