@deepseek-ai/dsh-llm 0.2.0-rc.2 → 0.2.1-alpha.1
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 +2 -8
- package/lib/typert.host.js +4 -4
- package/lib/typert.remote-client.js +3 -3
- package/lib/types/attribution.d.ts +1 -1
- package/lib/types/attribution.js +1 -1
- package/lib/types/error.d.ts +1 -1
- package/lib/types/error.js +1 -1
- package/lib/types/index.js +2 -10
- package/package.json +10 -16
- package/lib/invariant.js +0 -84
- package/lib/types/invariant.d.ts +0 -13
- package/lib/types/invariant.js +0 -100
package/README.i18n.yaml
CHANGED
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
en: ae1618577f4e8364
|
|
40
40
|
zh: a6e8ab1563c08dfb
|
|
41
41
|
/deepseek-ai-dsh-llm/understand-the-implementation/main-flow:
|
|
42
|
-
en:
|
|
43
|
-
zh:
|
|
42
|
+
en: da12553c289e1705
|
|
43
|
+
zh: 28827a81cd63ff84
|
|
44
44
|
/deepseek-ai-dsh-llm/understand-the-implementation/invariants:
|
|
45
45
|
en: 0163a050239248e5
|
|
46
46
|
zh: 885c95bedcbe2ff4
|
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ The service is built on one separation: **the logical contract is provider-neutr
|
|
|
107
107
|
|
|
108
108
|
A request is validated against its exact model's capability — context window, output default, reasoning efforts, input modalities, and `systemPromptUpdate` mode — and any adapter-configured defaults are materialized. The runtime preserves freezing for already-frozen input; hand-built callers own input immutability. `prepareCall()` binds those facts, detached context, and retry policy to the exact adapter generation that performs terminal dispatch, so HMR or dynamic settings cannot combine one generation's image capability with another generation's endpoint. An image-capable adapter projects durable references into route-specific request versions; `resolveImageAttachmentAccess()` separately maps an attachment provider's optional host object into the current tool execution world without changing the request image or its `variantId`. A text-only route receives deterministic per-image placeholders, including tool-role result images, without rewriting append-only session history. Durable `FileBlock` references never reach any adapter: request assembly replaces each one, including tool-role result occurrences, with deterministic handle text naming the file and its saved read-only path, resolved through the mounted attachment and filesystem providers. `ctx.llm.fileRequestText(ref)` exposes that exact synchronous projection to request measurement. An image occurrence derived with `offloaded: true` reaches every route as placeholder text through `projectOffloadedImages()`. An image-capable route whose retained occurrences exceed its `LlmImageRequestBudget` at their exact bytes fails with `IMAGE_OFFLOAD_REQUIRED` naming the additional oldest occurrences (`requiredImageOffload()`), never with an unlogged projection; `dsh-compaction-image-offload` logs the selected occurrences in one `image/offload` event and retries. Adapters that charge visual tokens declare per-route `imageRequestPricing`, which `ctx.llm.imageRequestPricing(provider, model)` resolves synchronously for the token meter. Dispatch goes through the `llm/stream` waterfall, then chunks return as token-level deltas and every adapter outcome reaches the consumer as one terminal `finish` chunk.
|
|
109
109
|
|
|
110
|
-
File detection reads current content, including tool-role result content, on every request without caching message identities or freeze state. The [file-scan decision](../../../.agents/notes/
|
|
110
|
+
File detection reads current content, including tool-role result content, on every request without caching message identities or freeze state. The [archived file-scan decision](../../../.agents/notes/archived/simplification/2026-09-07-file-content-scan.md) records the measured traversal cost.
|
|
111
111
|
|
|
112
112
|
### Invariants
|
|
113
113
|
|
package/README.zh.md
CHANGED
|
@@ -107,7 +107,7 @@ for await (const chunk of ctx.llm.stream({
|
|
|
107
107
|
|
|
108
108
|
请求会对照其精确模型的能力校验,包括上下文窗口、输出默认值、推理强度、输入模态与 `systemPromptUpdate` 模式,并填入任何适配器配置的默认值。运行时保留已冻结输入的冻结状态;手动构建请求的调用方负责保证输入不可变。`prepareCall()` 把这些事实、分离的上下文与重试策略绑定到执行最终分发的精确适配器代次,因此 HMR(热模块替换)或动态设置无法把一个代次的图片能力与另一代次的端点混用。支持图片的适配器把持久引用投影为路由专用请求版本;`resolveImageAttachmentAccess()` 会单独把附件提供方的可选宿主对象映射进当前工具执行世界,而不改变请求图片或其 `variantId`。纯文本路由接收确定性的逐图片占位符,包括 tool-role 结果图片,而不会改写仅追加会话历史。持久 `FileBlock` 引用永远不会到达任何适配器:请求组装把每个引用(包括 tool-role 结果中的出现)替换为确定性句柄文本,指出文件与其只读保存路径,路径经由挂载的附件与文件系统提供方解析。`ctx.llm.fileRequestText(ref)` 向请求计量公开相同的同步投影。派生后带有 `offloaded: true` 的图片出现位置,经 `projectOffloadedImages()` 以占位文本到达每条路由。支持图片的路由在保留的出现位置按精确字节超过其 `LlmImageRequestBudget` 时,以 `IMAGE_OFFLOAD_REQUIRED` 失败并说明还需省略多少最老的出现位置(`requiredImageOffload()`),绝不发送未记录的投影;`dsh-compaction-image-offload` 用一条 `image/offload` 事件记录所选位置并重试。对视觉 token 收费的适配器声明按路由的 `imageRequestPricing`,`ctx.llm.imageRequestPricing(provider, model)` 为 token meter 同步解析它。分发经过 `llm/stream` waterfall(瀑布式事件),随后分片以 token 级增量返回,每个适配器结果都以唯一一个终止 `finish` 分片到达消费方。
|
|
109
109
|
|
|
110
|
-
文件检测在每次请求时读取当前内容,包括 tool-role 结果内容,不缓存消息身份或冻结状态。[
|
|
110
|
+
文件检测在每次请求时读取当前内容,包括 tool-role 结果内容,不缓存消息身份或冻结状态。[已归档的文件扫描决策](../../../.agents/notes/archived/simplification/2026-09-07-file-content-scan.md)记录了实测遍历成本。
|
|
111
111
|
|
|
112
112
|
### 不变式
|
|
113
113
|
|
package/lib/index.js
CHANGED
|
@@ -119,7 +119,7 @@ function createToolResultMessage(input) {
|
|
|
119
119
|
*/
|
|
120
120
|
/**
|
|
121
121
|
* Base class for all harness errors. Carries a `code` (stable, programmatic —
|
|
122
|
-
* e.g. `NO_ADAPTER`, `INVALID_ARGS
|
|
122
|
+
* e.g. `NO_ADAPTER`, `INVALID_ARGS`) distinct from the
|
|
123
123
|
* human-readable `message`, and supports `cause` chaining via the standard
|
|
124
124
|
* `ErrorOptions`. `name` defaults to the subclass constructor name.
|
|
125
125
|
*/
|
|
@@ -840,7 +840,7 @@ function projectToolUpdates(messages, tools, toolUpdate, history) {
|
|
|
840
840
|
/**
|
|
841
841
|
* Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
|
|
842
842
|
* adapters from drifting. See
|
|
843
|
-
*
|
|
843
|
+
* `docs/subsystems/llm-streaming.md#appidentity--app-attribution`.
|
|
844
844
|
*
|
|
845
845
|
* App-attribution vocabulary for provider requests.
|
|
846
846
|
* @module @deepseek-ai/dsh-llm/attribution
|
|
@@ -1802,20 +1802,14 @@ let LlmRuntime = (() => {
|
|
|
1802
1802
|
}
|
|
1803
1803
|
/** Notify topology observers without letting one broken listener veto the commit. */
|
|
1804
1804
|
emitAdaptersUpdated() {
|
|
1805
|
-
let invariantFailure;
|
|
1806
1805
|
for (const listener of this.ctx.events.dispatch("emit", ["llm/adapters-updated"])) try {
|
|
1807
1806
|
const returned = listener();
|
|
1808
1807
|
if (returned != null && typeof returned.then === "function") Promise.resolve(returned).then(void 0, (error) => {
|
|
1809
1808
|
this.warnAdaptersListenerFailure(error);
|
|
1810
1809
|
});
|
|
1811
1810
|
} catch (error) {
|
|
1812
|
-
if (error?.code === "INVARIANT") {
|
|
1813
|
-
invariantFailure ??= error;
|
|
1814
|
-
continue;
|
|
1815
|
-
}
|
|
1816
1811
|
this.warnAdaptersListenerFailure(error);
|
|
1817
1812
|
}
|
|
1818
|
-
if (invariantFailure !== void 0) throw invariantFailure;
|
|
1819
1813
|
}
|
|
1820
1814
|
/** Contained-listener diagnostic shared by the sync and async failure paths. */
|
|
1821
1815
|
warnAdaptersListenerFailure(error) {
|
package/lib/typert.host.js
CHANGED
|
@@ -74,7 +74,7 @@ export const TYPERT = {
|
|
|
74
74
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:result',
|
|
75
75
|
create: _deepseek_ai_dsh_llm_llm_discoverModels_result$schema,
|
|
76
76
|
},
|
|
77
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
77
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":631,"column":9},
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
id: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders',
|
|
@@ -89,7 +89,7 @@ export const TYPERT = {
|
|
|
89
89
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders:result',
|
|
90
90
|
create: _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema,
|
|
91
91
|
},
|
|
92
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
92
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":543,"column":3},
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
id: '@deepseek-ai/dsh-llm#llm/listProviders',
|
|
@@ -104,7 +104,7 @@ export const TYPERT = {
|
|
|
104
104
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/listProviders:result',
|
|
105
105
|
create: _deepseek_ai_dsh_llm_llm_listProviders_result$schema,
|
|
106
106
|
},
|
|
107
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
107
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":471,"column":3},
|
|
108
108
|
},
|
|
109
109
|
],
|
|
110
110
|
model: {
|
|
@@ -414,7 +414,7 @@ export const TYPERT = {
|
|
|
414
414
|
},
|
|
415
415
|
{
|
|
416
416
|
"name": "MessageSourceMap",
|
|
417
|
-
"declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n
|
|
417
|
+
"declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n 'compact-checkpoint': CompactionCheckpointSource;\n 'user-question-reply': { kind: 'user-question-reply'; callId: ToolCallId; outcome: 'answered'; };\n goal: GoalMessageSource;\n schedule: { kind: 'schedule'; } & ContextFormed;\n 'session-reference': SessionReferenceSource;\n}"
|
|
418
418
|
},
|
|
419
419
|
{
|
|
420
420
|
"name": "ModelMessageSource",
|
|
@@ -71,7 +71,7 @@ export const TYPERT_REMOTE = {
|
|
|
71
71
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:result',
|
|
72
72
|
create: _deepseek_ai_dsh_llm_llm_discoverModels_result$schema,
|
|
73
73
|
},
|
|
74
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
74
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":631,"column":9},
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
id: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders',
|
|
@@ -86,7 +86,7 @@ export const TYPERT_REMOTE = {
|
|
|
86
86
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders:result',
|
|
87
87
|
create: _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema,
|
|
88
88
|
},
|
|
89
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
89
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":543,"column":3},
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
id: '@deepseek-ai/dsh-llm#llm/listProviders',
|
|
@@ -101,7 +101,7 @@ export const TYPERT_REMOTE = {
|
|
|
101
101
|
typeSymbol: '@deepseek-ai/dsh-llm#llm/listProviders:result',
|
|
102
102
|
create: _deepseek_ai_dsh_llm_llm_listProviders_result$schema,
|
|
103
103
|
},
|
|
104
|
-
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":
|
|
104
|
+
sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":471,"column":3},
|
|
105
105
|
},
|
|
106
106
|
],
|
|
107
107
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
|
|
3
3
|
* adapters from drifting. See
|
|
4
|
-
*
|
|
4
|
+
* `docs/subsystems/llm-streaming.md#appidentity--app-attribution`.
|
|
5
5
|
*
|
|
6
6
|
* App-attribution vocabulary for provider requests.
|
|
7
7
|
* @module @deepseek-ai/dsh-llm/attribution
|
package/lib/types/attribution.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
|
|
3
3
|
* adapters from drifting. See
|
|
4
|
-
*
|
|
4
|
+
* `docs/subsystems/llm-streaming.md#appidentity--app-attribution`.
|
|
5
5
|
*
|
|
6
6
|
* App-attribution vocabulary for provider requests.
|
|
7
7
|
* @module @deepseek-ai/dsh-llm/attribution
|
package/lib/types/error.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Base class for all harness errors. Carries a `code` (stable, programmatic —
|
|
8
|
-
* e.g. `NO_ADAPTER`, `INVALID_ARGS
|
|
8
|
+
* e.g. `NO_ADAPTER`, `INVALID_ARGS`) distinct from the
|
|
9
9
|
* human-readable `message`, and supports `cause` chaining via the standard
|
|
10
10
|
* `ErrorOptions`. `name` defaults to the subclass constructor name.
|
|
11
11
|
*/
|
package/lib/types/error.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Base class for all harness errors. Carries a `code` (stable, programmatic —
|
|
8
|
-
* e.g. `NO_ADAPTER`, `INVALID_ARGS
|
|
8
|
+
* e.g. `NO_ADAPTER`, `INVALID_ARGS`) distinct from the
|
|
9
9
|
* human-readable `message`, and supports `cause` chaining via the standard
|
|
10
10
|
* `ErrorOptions`. `name` defaults to the subclass constructor name.
|
|
11
11
|
*/
|
package/lib/types/index.js
CHANGED
|
@@ -237,30 +237,22 @@ let LlmRuntime = (() => {
|
|
|
237
237
|
emitAdaptersUpdated() {
|
|
238
238
|
// Cordis emit uses Array.map: one synchronous throw starves later
|
|
239
239
|
// listeners. Registry notifications are non-vetoing, so contain each
|
|
240
|
-
// callback independently
|
|
241
|
-
let invariantFailure;
|
|
240
|
+
// callback independently.
|
|
242
241
|
for (const listener of this.ctx.events.dispatch('emit', ['llm/adapters-updated'])) {
|
|
243
242
|
try {
|
|
244
243
|
const returned = listener();
|
|
245
244
|
if (returned != null && typeof returned.then === 'function') {
|
|
246
245
|
// An emit listener may still be an async function; its rejection
|
|
247
|
-
//
|
|
248
|
-
// contained here instead of becoming an unhandled rejection.
|
|
246
|
+
// is contained here instead of becoming an unhandled rejection.
|
|
249
247
|
void Promise.resolve(returned).then(undefined, (error) => {
|
|
250
248
|
this.warnAdaptersListenerFailure(error);
|
|
251
249
|
});
|
|
252
250
|
}
|
|
253
251
|
}
|
|
254
252
|
catch (error) {
|
|
255
|
-
if (error?.code === 'INVARIANT') {
|
|
256
|
-
invariantFailure ??= error;
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
253
|
this.warnAdaptersListenerFailure(error);
|
|
260
254
|
}
|
|
261
255
|
}
|
|
262
|
-
if (invariantFailure !== undefined)
|
|
263
|
-
throw invariantFailure;
|
|
264
256
|
}
|
|
265
257
|
/** Contained-listener diagnostic shared by the sync and async failure paths. */
|
|
266
258
|
warnAdaptersListenerFailure(error) {
|
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.2.
|
|
4
|
+
"version": "0.2.1-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
"types": "./lib/types/index.d.ts",
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
20
|
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/invariant.js"
|
|
24
|
-
},
|
|
25
21
|
"./types": {
|
|
26
22
|
"types": "./lib/types/types.d.ts",
|
|
27
23
|
"default": "./lib/types/types.js"
|
|
@@ -51,7 +47,6 @@
|
|
|
51
47
|
},
|
|
52
48
|
"files": [
|
|
53
49
|
"lib/index.js",
|
|
54
|
-
"lib/invariant.js",
|
|
55
50
|
"lib/types/**/*.js",
|
|
56
51
|
"lib/types/**/*.d.ts",
|
|
57
52
|
"lib/typert.host.js",
|
|
@@ -61,20 +56,19 @@
|
|
|
61
56
|
],
|
|
62
57
|
"license": "MIT",
|
|
63
58
|
"peerDependencies": {
|
|
64
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
59
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
65
60
|
},
|
|
66
61
|
"dependencies": {
|
|
67
62
|
"zod": "^4.4.3",
|
|
68
|
-
"@deepseek-ai/dsh-util-crypto": "0.2.
|
|
69
|
-
"@deepseek-ai/
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
63
|
+
"@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.1",
|
|
64
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
65
|
+
"@deepseek-ai/dsh-timeout": "0.2.1-alpha.1",
|
|
66
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
|
|
67
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.1",
|
|
68
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1"
|
|
74
69
|
},
|
|
75
70
|
"devDependencies": {
|
|
76
|
-
"@deepseek-ai/dsh-
|
|
77
|
-
"@deepseek-ai/
|
|
78
|
-
"@deepseek-ai/cordis": "~4.0.4"
|
|
71
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-alpha.1",
|
|
72
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
79
73
|
}
|
|
80
74
|
}
|
package/lib/invariant.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/** Package-owned LLM stream-protocol invariants. @module @deepseek-ai/dsh-llm/invariant */
|
|
3
|
-
const PACKAGE_NAME = "@deepseek-ai/dsh-llm";
|
|
4
|
-
/** Cordis companion plugin name. */
|
|
5
|
-
const name = "llm-invariant";
|
|
6
|
-
/** Service required before the companion can reserve package ownership. */
|
|
7
|
-
const inject = ["invariants"];
|
|
8
|
-
/** Require one chunk index to be a non-negative safe integer. */
|
|
9
|
-
function validateIndex(index, fail) {
|
|
10
|
-
if (!Number.isSafeInteger(index) || index < 0) fail(`LLM stream block index must be a non-negative safe integer, got ${index}`);
|
|
11
|
-
}
|
|
12
|
-
/** Require a delta to address an open block of its matching type. */
|
|
13
|
-
function validateDelta(open, index, expected, fail) {
|
|
14
|
-
validateIndex(index, fail);
|
|
15
|
-
const actual = open.get(index);
|
|
16
|
-
if (actual !== expected) fail(`${expected} delta at index ${index} requires an open ${expected} block, got ${String(actual)}`);
|
|
17
|
-
}
|
|
18
|
-
/** Wrap one provider stream and enforce its grammar as chunks are consumed. */
|
|
19
|
-
async function* validateStream(source, fail) {
|
|
20
|
-
const open = /* @__PURE__ */ new Map();
|
|
21
|
-
let usageSeen = false;
|
|
22
|
-
let finished = false;
|
|
23
|
-
for await (const chunk of source) {
|
|
24
|
-
if (finished) fail(`LLM stream emitted ${chunk.type} after terminal finish`);
|
|
25
|
-
switch (chunk.type) {
|
|
26
|
-
case "block-start":
|
|
27
|
-
validateIndex(chunk.index, fail);
|
|
28
|
-
if (open.has(chunk.index)) fail(`LLM stream repeated block-start index ${chunk.index}`);
|
|
29
|
-
open.set(chunk.index, chunk.blockType);
|
|
30
|
-
break;
|
|
31
|
-
case "text-delta":
|
|
32
|
-
validateDelta(open, chunk.index, "text", fail);
|
|
33
|
-
break;
|
|
34
|
-
case "reasoning-delta":
|
|
35
|
-
validateDelta(open, chunk.index, "reasoning", fail);
|
|
36
|
-
break;
|
|
37
|
-
case "tool-call-delta":
|
|
38
|
-
validateDelta(open, chunk.index, "tool-call", fail);
|
|
39
|
-
break;
|
|
40
|
-
case "block-end": {
|
|
41
|
-
validateIndex(chunk.index, fail);
|
|
42
|
-
const blockType = open.get(chunk.index);
|
|
43
|
-
if (blockType === void 0) fail(`LLM stream block-end index ${chunk.index} has no open block`);
|
|
44
|
-
if (chunk.block.type !== blockType) fail(`LLM stream block-end index ${chunk.index} closes ${chunk.block.type}, expected ${blockType}`);
|
|
45
|
-
open.delete(chunk.index);
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
case "usage":
|
|
49
|
-
if (usageSeen) fail("LLM stream emitted usage more than once");
|
|
50
|
-
usageSeen = true;
|
|
51
|
-
break;
|
|
52
|
-
case "finish":
|
|
53
|
-
if (open.size > 0 && chunk.reason.kind !== "error" && chunk.reason.kind !== "aborted") fail(`LLM stream finished with ${open.size} open block(s)`);
|
|
54
|
-
finished = true;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
yield chunk;
|
|
58
|
-
}
|
|
59
|
-
if (!finished) fail("LLM stream ended without a terminal finish chunk");
|
|
60
|
-
}
|
|
61
|
-
/** Install validation around every provider stream. */
|
|
62
|
-
const install = (ctx, fail) => {
|
|
63
|
-
ctx.on("llm/stream", (_options, next) => validateStream(next(), fail), {
|
|
64
|
-
global: true,
|
|
65
|
-
prepend: true
|
|
66
|
-
});
|
|
67
|
-
ctx.on("llm/adapters-updated", () => {
|
|
68
|
-
const llm = ctx.get("llm");
|
|
69
|
-
if (llm === void 0) return;
|
|
70
|
-
for (const provider of llm.listProviders()) try {
|
|
71
|
-
llm.providerRetryPolicy(provider.id);
|
|
72
|
-
} catch {
|
|
73
|
-
fail(`llm/adapters-updated fired while provider "${provider.id}" has no readable registration`);
|
|
74
|
-
}
|
|
75
|
-
}, { global: true });
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Register the LLM invariant companion.
|
|
79
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
80
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
81
|
-
*/
|
|
82
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
83
|
-
//#endregion
|
|
84
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** Package-owned LLM stream-protocol invariants. @module @deepseek-ai/dsh-llm/invariant */
|
|
2
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
/** Cordis companion plugin name. */
|
|
4
|
-
export declare const name = "llm-invariant";
|
|
5
|
-
/** Service required before the companion can reserve package ownership. */
|
|
6
|
-
export declare const inject: string[];
|
|
7
|
-
/**
|
|
8
|
-
* Register the LLM invariant companion.
|
|
9
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
10
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
11
|
-
*/
|
|
12
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
13
|
-
//# sourceMappingURL=invariant.d.ts.map
|
package/lib/types/invariant.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/** Package-owned LLM stream-protocol invariants. @module @deepseek-ai/dsh-llm/invariant */
|
|
2
|
-
const PACKAGE_NAME = '@deepseek-ai/dsh-llm';
|
|
3
|
-
/** Cordis companion plugin name. */
|
|
4
|
-
export const name = 'llm-invariant';
|
|
5
|
-
/** Service required before the companion can reserve package ownership. */
|
|
6
|
-
export const inject = ['invariants'];
|
|
7
|
-
/** Require one chunk index to be a non-negative safe integer. */
|
|
8
|
-
function validateIndex(index, fail) {
|
|
9
|
-
if (!Number.isSafeInteger(index) || index < 0) {
|
|
10
|
-
fail(`LLM stream block index must be a non-negative safe integer, got ${index}`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/** Require a delta to address an open block of its matching type. */
|
|
14
|
-
function validateDelta(open, index, expected, fail) {
|
|
15
|
-
validateIndex(index, fail);
|
|
16
|
-
const actual = open.get(index);
|
|
17
|
-
if (actual !== expected) {
|
|
18
|
-
fail(`${expected} delta at index ${index} requires an open ${expected} block, got ${String(actual)}`);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/** Wrap one provider stream and enforce its grammar as chunks are consumed. */
|
|
22
|
-
async function* validateStream(source, fail) {
|
|
23
|
-
const open = new Map();
|
|
24
|
-
let usageSeen = false;
|
|
25
|
-
let finished = false;
|
|
26
|
-
for await (const chunk of source) {
|
|
27
|
-
if (finished)
|
|
28
|
-
fail(`LLM stream emitted ${chunk.type} after terminal finish`);
|
|
29
|
-
switch (chunk.type) {
|
|
30
|
-
case 'block-start':
|
|
31
|
-
validateIndex(chunk.index, fail);
|
|
32
|
-
if (open.has(chunk.index))
|
|
33
|
-
fail(`LLM stream repeated block-start index ${chunk.index}`);
|
|
34
|
-
open.set(chunk.index, chunk.blockType);
|
|
35
|
-
break;
|
|
36
|
-
case 'text-delta':
|
|
37
|
-
validateDelta(open, chunk.index, 'text', fail);
|
|
38
|
-
break;
|
|
39
|
-
case 'reasoning-delta':
|
|
40
|
-
validateDelta(open, chunk.index, 'reasoning', fail);
|
|
41
|
-
break;
|
|
42
|
-
case 'tool-call-delta':
|
|
43
|
-
validateDelta(open, chunk.index, 'tool-call', fail);
|
|
44
|
-
break;
|
|
45
|
-
case 'block-end': {
|
|
46
|
-
validateIndex(chunk.index, fail);
|
|
47
|
-
const blockType = open.get(chunk.index);
|
|
48
|
-
if (blockType === undefined)
|
|
49
|
-
fail(`LLM stream block-end index ${chunk.index} has no open block`);
|
|
50
|
-
if (chunk.block.type !== blockType) {
|
|
51
|
-
fail(`LLM stream block-end index ${chunk.index} closes ${chunk.block.type}, expected ${blockType}`);
|
|
52
|
-
}
|
|
53
|
-
open.delete(chunk.index);
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
case 'usage':
|
|
57
|
-
if (usageSeen)
|
|
58
|
-
fail('LLM stream emitted usage more than once');
|
|
59
|
-
usageSeen = true;
|
|
60
|
-
break;
|
|
61
|
-
case 'finish':
|
|
62
|
-
if (open.size > 0 && chunk.reason.kind !== 'error' && chunk.reason.kind !== 'aborted') {
|
|
63
|
-
fail(`LLM stream finished with ${open.size} open block(s)`);
|
|
64
|
-
}
|
|
65
|
-
finished = true;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
yield chunk;
|
|
69
|
-
}
|
|
70
|
-
if (!finished)
|
|
71
|
-
fail('LLM stream ended without a terminal finish chunk');
|
|
72
|
-
}
|
|
73
|
-
/** Install validation around every provider stream. */
|
|
74
|
-
const install = (ctx, fail) => {
|
|
75
|
-
ctx.on('llm/stream', (_options, next) => validateStream(next(), fail), { global: true, prepend: true });
|
|
76
|
-
ctx.on('llm/adapters-updated', () => {
|
|
77
|
-
// A disposer-time emit can outlive the service-store entry during whole-
|
|
78
|
-
// context teardown; only a live service promises a readable registry.
|
|
79
|
-
const llm = ctx.get('llm');
|
|
80
|
-
if (llm === undefined)
|
|
81
|
-
return;
|
|
82
|
-
for (const provider of llm.listProviders()) {
|
|
83
|
-
try {
|
|
84
|
-
llm.providerRetryPolicy(provider.id);
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
// Reaching here IS the violation: the notification promised a readable
|
|
88
|
-
// registry, and only that broken promise can make the lookup throw.
|
|
89
|
-
fail(`llm/adapters-updated fired while provider "${provider.id}" has no readable registration`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}, { global: true });
|
|
93
|
-
};
|
|
94
|
-
/**
|
|
95
|
-
* Register the LLM invariant companion.
|
|
96
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
97
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
98
|
-
*/
|
|
99
|
-
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
100
|
-
//# sourceMappingURL=invariant.js.map
|