@deepseek-ai/dsh-llm 0.0.1-rc.2 → 0.0.1-rc.5
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 +4 -4
- package/README.zh.md +10 -10
- package/lib/index.js +19 -3
- package/lib/types/index.d.ts +7 -7
- package/lib/types/index.js +3 -3
- package/lib/types/message.d.ts +11 -2
- package/lib/types/message.js +19 -0
- package/lib/types/types.d.ts +1 -1
- package/package.json +12 -12
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: 2cae9a05a58295b06d25382729a3304dbdc9a6fa
|
|
6
|
+
README.zh.md: bf86e703fc15231c6845364dc383b7fc6ad77297
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
|
|
|
4
4
|
|
|
5
5
|
Provider-neutral LLM vocabulary and abstract service. This package defines the canonical language spoken by the agent loop, session logs, and every plugin.
|
|
6
6
|
|
|
7
|
-
## Service: `
|
|
7
|
+
## Service: `LlmRuntime` (ctx key: `llm`)
|
|
8
8
|
|
|
9
9
|
An adapter registry plus a single streaming call API, interceptable via a waterfall event.
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@ An adapter registry plus a single streaming call API, interceptable via a waterf
|
|
|
24
24
|
- `ctx.llm.prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>` Resolve a config plus detached context metadata and markers for fields supplied by adapter defaults in one exact-model lookup, then capture its current adapter registration and immutable retry policy as one cancellable, one-shot call.
|
|
25
25
|
- `ctx.llm.stream(options: GenerateOptions): AsyncIterable<StreamChunk>` Stream one model call as raw chunks (token-level deltas). Consumers assemble the chunks into blocks/messages with `BlockAssembler`.
|
|
26
26
|
|
|
27
|
-
`
|
|
27
|
+
`LlmRuntime` normalizes failures from final adapter selection, synchronous dispatch, iterator construction, and iteration into the stream protocol's single terminal form: `finish { kind: 'error' | 'aborted', failure }`. A failure after partial deltas may leave content blocks open; consumers discard that incomplete output. Errors from `llm/stream` middleware, nested calls, adapter cleanup, and downstream consumers remain thrown because they are plugin or consumer failures rather than model-request outcomes. A prepared call exposes the immutable retry policy captured with its exact adapter registration; a route handled entirely by middleware has no serving policy.
|
|
28
28
|
|
|
29
29
|
Interrogating an endpoint is configuration-time work over a *draft*, keyed by settings namespace rather than by provider route — the provider a surface is adding does not exist yet, so there is no route to name. The request may still *name* a route it is editing, and an adapter that already describes that route answers from its own knowledge without a network call; `baseURL` is optional and one of the two is required. The request otherwise carries the endpoint, the protocol, and a credential the harness uses for that one interrogation and never stores — nothing here reads or writes settings or credentials, and the reply is candidate metadata a surface may offer for adoption, never a registered catalog. `LlmDiscoveredModel` makes every field but `id` optional because most provider listings disclose an id and nothing else; a surface adopting one still owes the capacities its adapter requires. Duplicate and unusable ids are dropped, an unserved namespace fails with `NO_DISCOVERY`, and a request naming neither a route nor an endpoint fails with `INVALID_DISCOVERY`.
|
|
30
30
|
|
|
@@ -51,7 +51,7 @@ Exact-model metadata is a separate correctness query, not a catalog decoration o
|
|
|
51
51
|
|
|
52
52
|
`Message` is the shared immutable value used by delivery, durable history, and model requests. Every message has a required `MessageId`, role, content, and typed source from creation onward. `createMessage(input)` mints the identity and returns a detached deep-frozen value; `createUserMessage({ content, source })` fixes the user role; `createAssistantMessage({ content, source })` fixes the assistant role and model source kind; `createToolResultMessage({ callId, content, isError })` fixes the user role and couples the tool source to its result block; `freezeMessage(message)` imports an identity that already exists and never replaces it. Message rewrites preserve the identity and produce another frozen value. Browser code imports these value constructors from the dependency-minimal `@deepseek-ai/dsh-llm/message` entry instead of the service-bearing package root.
|
|
53
53
|
|
|
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, `
|
|
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
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.
|
|
57
57
|
|
|
@@ -81,7 +81,7 @@ Every adapter that puts a credential in an HTTP header judges it the same way be
|
|
|
81
81
|
|
|
82
82
|
### Real adapters
|
|
83
83
|
|
|
84
|
-
Two adapters implement `LlmAdapter` on different internals: [`@deepseek-ai/dsh-llm-deepseek`](../llm-deepseek) uses direct fetch with `eventsource-parser` SSE framing for the `deepseek-official` route, while [`@deepseek-ai/dsh-llm-pi-ai`](../llm-pi-ai) dynamically resolves configured provider/model pairs through `@earendil-works/pi-ai`. Both follow the `StreamChunk` conventions in `types.ts`: usage precedes finish and tool arguments remain raw strings. Adapter implementations may throw or emit a failure finish internally; `
|
|
84
|
+
Two adapters implement `LlmAdapter` on different internals: [`@deepseek-ai/dsh-llm-deepseek`](../llm-deepseek) uses direct fetch with `eventsource-parser` SSE framing for the `deepseek-official` route, while [`@deepseek-ai/dsh-llm-pi-ai`](../llm-pi-ai) dynamically resolves configured provider/model pairs through `@earendil-works/pi-ai`. Both follow the `StreamChunk` conventions in `types.ts`: usage precedes finish and tool arguments remain raw strings. Adapter implementations may throw or emit a failure finish internally; `LlmRuntime` exposes both as a terminal failure finish. See [the twin LLM adapters](../../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md) for the adapter rationale and [the terminal-failure decision](../../../.agents/notes/implemented/architecture/2026-07-29-terminal-llm-stream-failures.md) for the service boundary.
|
|
85
85
|
|
|
86
86
|
## Model Experience
|
|
87
87
|
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
提供方无关的 LLM(大语言模型)词汇与抽象服务。本包定义 agent loop(智能体循环)、会话日志和每个插件使用的规范语言。
|
|
6
6
|
|
|
7
|
-
## 服务:`
|
|
7
|
+
## 服务:`LlmRuntime`(ctx key:`llm`)
|
|
8
8
|
|
|
9
9
|
一个适配器注册表加单一流式调用接口,可通过 waterfall(瀑布式事件)拦截。
|
|
10
10
|
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
- `ctx.llm.prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>` 在一次精确模型查询中解析配置、脱耦的上下文元数据以及标明哪些字段由适配器默认值填入的标记,再将当前适配器注册和不可变重试策略捕获为一次可取消、一次性调用。
|
|
25
25
|
- `ctx.llm.stream(options: GenerateOptions): AsyncIterable<StreamChunk>` 将一次模型调用流式输出为原始分片(token 级增量)。消费方使用 `BlockAssembler` 将分片组装为块/消息。
|
|
26
26
|
|
|
27
|
-
`
|
|
27
|
+
`LlmRuntime` 将最终适配器选择、同步 dispatch、iterator 构造与迭代中的失败规范化为流协议唯一的终止形式:`finish { kind: 'error' | 'aborted', failure }`。部分增量输出后发生失败时,内容块可能仍未闭合;消费方会丢弃这些不完整输出。`llm/stream` middleware、嵌套调用、适配器清理和下游消费方的错误仍会抛出,因为它们属于插件或消费方失败,而非模型请求结果。已准备调用会暴露随其确切适配器注册一同捕获的不可变重试策略;完全由 middleware 处理的路由没有服务策略。
|
|
28
28
|
|
|
29
29
|
询问端点属于配置期针对**草稿**的操作,以 settings namespace 而非提供方路由为键——界面正在新增的提供方还不存在,也就没有路由可点名。但请求仍可**点名**它正在编辑的路由,而已经描述该路由的适配器会用自己的知识作答,无需联网;`baseURL` 可选,两者至少要有一个。除此之外,请求携带端点、协议,以及一条 harness 只用于这一次询问、绝不存储的凭据——这里既不读也不写 settings 与 credentials,回复是界面可供用户采纳的候选元数据,而不是已注册的 catalog。`LlmDiscoveredModel` 除 `id` 外每个字段都是可选的,因为大多数提供方列表只公布 id;采纳其中一条的界面仍要补上其适配器所需的容量。重复与不可用的 id 会被丢弃,无人服务的 namespace 以 `NO_DISCOVERY` 失败,既不点名路由也不给端点的请求以 `INVALID_DISCOVERY` 失败。
|
|
30
30
|
|
|
31
|
-
提供方与模型元数据是发现接口,不是路由白名单。`registerAdapter()` 仍拥有提供方排他性,并为每条路由捕获适配器的重试策略;适配器则可以接受 `listModels()` 中不存在的模型 id,消费方禁止因模型未列出而拒绝请求。返回的 selector
|
|
31
|
+
提供方与模型元数据是发现接口,不是路由白名单。`registerAdapter()` 仍拥有提供方排他性,并为每条路由捕获适配器的重试策略;适配器则可以接受 `listModels()` 中不存在的模型 id,消费方禁止因模型未列出而拒绝请求。返回的 selector 元数据与输入脱离,无效或重复的适配器条目会以 `INVALID_ADAPTER` 或 `INVALID_CATALOG` 失败。
|
|
32
32
|
|
|
33
33
|
每个拓扑提交点——适配器路由注册或 dispose、目录条目出现或撤回——都会在变更之后发出无载荷的 `llm/adapters-updated` 事件,消费方因此重读 `listProviders()`/`listModels()`/`listConfigurableProviders()` 而非轮询。观察者故障会被隔离(记录日志、不否决);只有带 `INVARIANT` 码的故障会在扇出后重新抛出。
|
|
34
34
|
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
|
|
52
52
|
`Message` 是投递、持久历史和模型请求共享的不可变值。每条消息从创建起都必须具有 `MessageId`、角色、内容和带类型的来源。`createMessage(input)` 生成标识,并返回与输入分离且深度冻结的值;`createUserMessage({ content, source })` 固定 user 角色;`createAssistantMessage({ content, source })` 固定 assistant 角色与模型来源类别;`createToolResultMessage({ callId, content, isError })` 固定 user 角色,并将工具来源与其结果块耦合;`freezeMessage(message)` 导入已有标识,绝不将其替换。改写消息时会保留标识,并产生另一个冻结值。浏览器端代码会从依赖最少的 `@deepseek-ai/dsh-llm/message` 入口导入这些值构造函数,而不是从包含服务的包根入口导入。
|
|
53
53
|
|
|
54
|
-
消息内容是类型化内容块数组:`text`、`reasoning`、`tool-call`、`tool-result`。联合从可合并扩展的 `ContentBlockMap` 派生,因此插件可以通过 declaration merging 添加块类型。assistant 消息使用模型来源,其中携带生成该消息的提供方和模型,以及可选的适配器私有回放状态。dispatch 前,`
|
|
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`
|
|
56
|
+
流式输出是原始分片协议(`block-start`、`text-delta`、`reasoning-delta`、`tool-call-delta`、`block-end`、`usage`、`finish`)。每个适配器结果都以一个终止 `finish` 到达消费方;运行故障使用 `error` 或 `aborted` 作为结束原因,而不会跨流 API 抛出。`BlockAssembler` 是将分片组装为块/消息的唯一共享实现。
|
|
57
57
|
|
|
58
58
|
### 调用配置(`call-config.ts`)
|
|
59
59
|
|
|
@@ -71,17 +71,17 @@
|
|
|
71
71
|
|
|
72
72
|
- `LlmAdapter`:提供方适配器的抽象基类。唯一必需方法是 `stream()`。
|
|
73
73
|
- `BlockAssembler`:将原始分片逐步组装为完整内容块,并能据此创建带标识且冻结的 assistant 消息。agent loop 向它提供原始分片(同时记录以供回放),并读取已组装块以构建历史。
|
|
74
|
-
- `HarnessError`:harness 错误分类体系的基类,包含稳定 `code` 字符串(与面向人的 `message`
|
|
74
|
+
- `HarnessError`:harness 错误分类体系的基类,包含稳定 `code` 字符串(与面向人的 `message` 不同)以及 `cause` 链。它位于所有其他包都从中导入的叶子包中,因此可以共享单一基类,无需新的依赖边。各包的错误(`LlmError`、`ToolArgsError`、`InvariantError` 等)都继承自它。`isHarnessError(value)` 在进程边界处收窄类型。
|
|
75
75
|
- `LlmError`:继承自 `HarnessError`;其稳定 `code` 字符串(`NO_ADAPTER`、`DUPLICATE_ADAPTER` 与 `AUTH`/`RATE_LIMIT` 等适配器 code)与冻结可序列化 `failure.code` 匹配。Payload 还可以保留已验证状态、`Retry-After` 和品牌化提供方请求 id 事实;策略位于错误之外。
|
|
76
|
-
- `errorChain(value)`:渲染抛出值的完整 `cause` 链与 AggregateError
|
|
76
|
+
- `errorChain(value)`:渲染抛出值的完整 `cause` 链与 AggregateError 成员,供诊断输出使用,包括 UI 通知、logger 行和持久 `turn/end` 消息。因此 undici 的 `TypeError: fetch failed` 等传输包装层会显示底层 `ECONNREFUSED`/DNS/TLS 详细信息,而不是将其遮蔽。该函数只负责渲染:请按 `code` 路由,绝不解析结果。
|
|
77
77
|
- `CONTEXT_WINDOW_EXCEEDED_CODE`:当请求超过模型上下文窗口时,无论通过 HTTP 异常抛出还是带内 finish 交付,两个 DeepSeek 适配器都使用的提供方无关 code。`isContextWindowExceededError(detail)` 是它们针对 OpenAI 兼容提供方详细信息的共享保守分类器。
|
|
78
|
-
- `QUOTA_EXCEEDED_CODE
|
|
78
|
+
- `QUOTA_EXCEEDED_CODE`:帐户配额、余额、点数、预算或用量限制耗尽时使用的非暂时性提供方无关 code。`isQuotaExceededError(detail)` 使这些失败与请求速率限制保持区分。
|
|
79
79
|
- `EMPTY_RESPONSE_CODE`:两个适配器都使用的提供方无关 code,用于表示退化的提供方生成结果:一个未携带任何内容块的终止 `stop`。它会被分类为错误 finish(而非成功空消息),因为尝试未产生持久内容;`dsh-llm-retry` 默认重试它。
|
|
80
|
-
- `INVALID_CREDENTIAL_CODE`:已提供但无法使用的凭据所用的提供方无关 code
|
|
80
|
+
- `INVALID_CREDENTIAL_CODE`:已提供但无法使用的凭据所用的提供方无关 code——格式错误而非缺失,修复方式是改正已存储的值,而不是补充一个凭据,这正是它与 `MISSING_CREDENTIAL` 的区别。它被刻意排除在默认可重试集合之外:格式错误的凭据每次尝试都会以同样方式失败。`assertUsableApiKey(raw, pkg, ref)` 会以该 code 抛出 `LlmError`,是每个适配器判定已存储凭据不可用时共用的诊断。
|
|
81
81
|
|
|
82
82
|
### 真实适配器
|
|
83
83
|
|
|
84
|
-
两个适配器使用不同内部机制实现 `LlmAdapter`:[`@deepseek-ai/dsh-llm-deepseek`](../llm-deepseek) 针对 `deepseek-official` 路由使用直接 fetch 加 `eventsource-parser` SSE(Server-Sent Events)分帧,[`@deepseek-ai/dsh-llm-pi-ai`](../llm-pi-ai) 则通过 `@earendil-works/pi-ai` 动态解析已配置提供方/模型对。两者都遵循 `types.ts` 中的 `StreamChunk` 约定:usage 先于 finish,工具参数保持原始字符串。适配器实现在内部可以抛出异常或发出失败 finish;`
|
|
84
|
+
两个适配器使用不同内部机制实现 `LlmAdapter`:[`@deepseek-ai/dsh-llm-deepseek`](../llm-deepseek) 针对 `deepseek-official` 路由使用直接 fetch 加 `eventsource-parser` SSE(Server-Sent Events)分帧,[`@deepseek-ai/dsh-llm-pi-ai`](../llm-pi-ai) 则通过 `@earendil-works/pi-ai` 动态解析已配置提供方/模型对。两者都遵循 `types.ts` 中的 `StreamChunk` 约定:usage 先于 finish,工具参数保持原始字符串。适配器实现在内部可以抛出异常或发出失败 finish;`LlmRuntime` 会将两者都暴露为终止失败 finish。适配器理由见[双 LLM 适配器](../../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md),服务边界见[终止失败决策](../../../.agents/notes/implemented/architecture/2026-07-29-terminal-llm-stream-failures.md)。
|
|
85
85
|
|
|
86
86
|
## 模型体验
|
|
87
87
|
|
package/lib/index.js
CHANGED
|
@@ -213,6 +213,22 @@ function createToolResultMessage(input) {
|
|
|
213
213
|
}]
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Whether a stream chunk carries visible model output (the first-token
|
|
218
|
+
* boundary shared by client step timing and the whole-log sessionStats
|
|
219
|
+
* projection). Empty deltas (heartbeats, empty tool-call frames) do not count
|
|
220
|
+
* as a first token.
|
|
221
|
+
* @param chunk - the stream chunk to test.
|
|
222
|
+
* @returns true when the chunk contains a non-empty text/reasoning/tool delta.
|
|
223
|
+
*/
|
|
224
|
+
function isTokenDelta(chunk) {
|
|
225
|
+
switch (chunk.type) {
|
|
226
|
+
case "text-delta":
|
|
227
|
+
case "reasoning-delta": return chunk.text !== "";
|
|
228
|
+
case "tool-call-delta": return chunk.argumentsDelta !== "" || chunk.name !== void 0;
|
|
229
|
+
default: return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
216
232
|
//#endregion
|
|
217
233
|
//#region lib/types/error.js
|
|
218
234
|
/**
|
|
@@ -788,7 +804,7 @@ var BlockAssembler = class {
|
|
|
788
804
|
//#region lib/types/index.js
|
|
789
805
|
/**
|
|
790
806
|
* LLM service: adapter registry with a waterfall-interceptable streaming call
|
|
791
|
-
* API. Exports the `
|
|
807
|
+
* API. Exports the `LlmRuntime` default, the abstract `LlmAdapter` for
|
|
792
808
|
* provider backends, and `BlockAssembler` for chunk assembly.
|
|
793
809
|
*
|
|
794
810
|
* @module @deepseek-ai/dsh-llm
|
|
@@ -900,7 +916,7 @@ var LlmAdapter = class {
|
|
|
900
916
|
* The abstract `llm` service: an adapter registry plus a streaming model-call
|
|
901
917
|
* API, interceptable via the `llm/stream` waterfall.
|
|
902
918
|
*/
|
|
903
|
-
var
|
|
919
|
+
var LlmRuntime = class extends Service {
|
|
904
920
|
adapters = /* @__PURE__ */ new Map();
|
|
905
921
|
directory = /* @__PURE__ */ new Map();
|
|
906
922
|
discoveries = /* @__PURE__ */ new Map();
|
|
@@ -1388,4 +1404,4 @@ function adapterFailureChunk(error, signal) {
|
|
|
1388
1404
|
};
|
|
1389
1405
|
}
|
|
1390
1406
|
//#endregion
|
|
1391
|
-
export { APP_IDENTITY, BlockAssembler, CONTEXT_SUMMARY_MAX_CHARS, CONTEXT_WINDOW_EXCEEDED_CODE, CallId, EMPTY_RESPONSE_CODE, HarnessError, INVALID_CREDENTIAL_CODE, LlmAdapter, LlmError,
|
|
1407
|
+
export { APP_IDENTITY, BlockAssembler, CONTEXT_SUMMARY_MAX_CHARS, CONTEXT_WINDOW_EXCEEDED_CODE, CallId, EMPTY_RESPONSE_CODE, HarnessError, INVALID_CREDENTIAL_CODE, LlmAdapter, LlmError, LlmRuntime, LlmRuntime as default, MessageId, ProviderRequestId, QUOTA_EXCEEDED_CODE, ReasoningEffortId, RetryPolicySchema, assertNever, assertUsableApiKey, attributionHeaders, boundContextSummary, callConfigEquals, contentHasImage, createAssistantMessage, createMessage, createToolResultMessage, createUserMessage, deepFreeze, errorChain, freezeMessage, isAgentLoopRequest, isContextWindowExceededError, isHarnessError, isQuotaExceededError, isTokenDelta, markAgentLoopRequest, normalizeApiKey, resolveRetryPolicy, userAgent };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM service: adapter registry with a waterfall-interceptable streaming call
|
|
3
|
-
* API. Exports the `
|
|
3
|
+
* API. Exports the `LlmRuntime` default, the abstract `LlmAdapter` for
|
|
4
4
|
* provider backends, and `BlockAssembler` for chunk assembly.
|
|
5
5
|
*
|
|
6
6
|
* @module @deepseek-ai/dsh-llm
|
|
@@ -25,12 +25,12 @@ export { callConfigEquals, deepFreeze, isAgentLoopRequest, markAgentLoopRequest
|
|
|
25
25
|
export type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
|
|
26
26
|
declare module '@deepseek-ai/cordis' {
|
|
27
27
|
interface Context {
|
|
28
|
-
llm:
|
|
28
|
+
llm: LlmRuntime;
|
|
29
29
|
}
|
|
30
30
|
interface Events {
|
|
31
31
|
/**
|
|
32
32
|
* Waterfall around every streaming model call (retry, replay, routing).
|
|
33
|
-
* Bound to the {@link
|
|
33
|
+
* Bound to the {@link LlmRuntime}; call `next()` to reach the resolved
|
|
34
34
|
* adapter's stream, or yield your own chunks to short-circuit.
|
|
35
35
|
* @param options - the full request. A LOOP-built request carries the
|
|
36
36
|
* process-local {@link markAgentLoopRequest} identity and arrives deep-frozen
|
|
@@ -40,7 +40,7 @@ declare module '@deepseek-ai/cordis' {
|
|
|
40
40
|
* the immutable creation contract.
|
|
41
41
|
* @mode waterfall
|
|
42
42
|
*/
|
|
43
|
-
'llm/stream'(this:
|
|
43
|
+
'llm/stream'(this: LlmRuntime, options: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk>;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
/** Structured provider facts and cause accepted by {@link LlmError}. */
|
|
@@ -149,7 +149,7 @@ export declare abstract class LlmAdapter {
|
|
|
149
149
|
abstract stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
|
-
* What {@link
|
|
152
|
+
* What {@link LlmRuntime.registerAdapter} returns: the disposer, plus an
|
|
153
153
|
* atomic route replacement for the same adapter instance.
|
|
154
154
|
*/
|
|
155
155
|
export interface AdapterRegistrationHandle {
|
|
@@ -195,7 +195,7 @@ export interface DirectoryRegistrationHandle {
|
|
|
195
195
|
* The abstract `llm` service: an adapter registry plus a streaming model-call
|
|
196
196
|
* API, interceptable via the `llm/stream` waterfall.
|
|
197
197
|
*/
|
|
198
|
-
export declare class
|
|
198
|
+
export declare class LlmRuntime extends Service {
|
|
199
199
|
private adapters;
|
|
200
200
|
private directory;
|
|
201
201
|
private discoveries;
|
|
@@ -337,5 +337,5 @@ export declare class LlmService extends Service {
|
|
|
337
337
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
338
338
|
private streamWithRegistration;
|
|
339
339
|
}
|
|
340
|
-
export default
|
|
340
|
+
export default LlmRuntime;
|
|
341
341
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM service: adapter registry with a waterfall-interceptable streaming call
|
|
3
|
-
* API. Exports the `
|
|
3
|
+
* API. Exports the `LlmRuntime` default, the abstract `LlmAdapter` for
|
|
4
4
|
* provider backends, and `BlockAssembler` for chunk assembly.
|
|
5
5
|
*
|
|
6
6
|
* @module @deepseek-ai/dsh-llm
|
|
@@ -145,7 +145,7 @@ export class LlmAdapter {
|
|
|
145
145
|
* The abstract `llm` service: an adapter registry plus a streaming model-call
|
|
146
146
|
* API, interceptable via the `llm/stream` waterfall.
|
|
147
147
|
*/
|
|
148
|
-
export class
|
|
148
|
+
export class LlmRuntime extends Service {
|
|
149
149
|
adapters = new Map();
|
|
150
150
|
directory = new Map();
|
|
151
151
|
discoveries = new Map();
|
|
@@ -726,5 +726,5 @@ function adapterFailureChunk(error, signal) {
|
|
|
726
726
|
: { kind: 'error', failure },
|
|
727
727
|
};
|
|
728
728
|
}
|
|
729
|
-
export default
|
|
729
|
+
export default LlmRuntime;
|
|
730
730
|
//# sourceMappingURL=index.js.map
|
package/lib/types/message.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Message value types, identity, and immutable construction helpers. */
|
|
2
2
|
import { MessageId, type CallId } from './brand.ts';
|
|
3
|
-
import type { ContentBlock, ToolResultBlock } from './types.ts';
|
|
3
|
+
import type { ContentBlock, StreamChunk, ToolResultBlock } from './types.ts';
|
|
4
4
|
/** Provider/model identity and adapter-private replay data for an assistant message. */
|
|
5
5
|
export interface AssistantProvenance {
|
|
6
6
|
/** Provider route that produced the message. */
|
|
@@ -9,7 +9,7 @@ export interface AssistantProvenance {
|
|
|
9
9
|
model: string;
|
|
10
10
|
/**
|
|
11
11
|
* Lossless-JSON adapter state needed to replay the provider response.
|
|
12
|
-
* `
|
|
12
|
+
* `LlmRuntime` exposes it to a target adapter only when that adapter instance
|
|
13
13
|
* currently owns both this historical provider and the target provider.
|
|
14
14
|
*/
|
|
15
15
|
replayState?: unknown;
|
|
@@ -193,5 +193,14 @@ export interface ToolResultMessageInput {
|
|
|
193
193
|
* @returns an immutable user-role tool-result message.
|
|
194
194
|
*/
|
|
195
195
|
export declare function createToolResultMessage(input: ToolResultMessageInput): ToolResultMessage;
|
|
196
|
+
/**
|
|
197
|
+
* Whether a stream chunk carries visible model output (the first-token
|
|
198
|
+
* boundary shared by client step timing and the whole-log sessionStats
|
|
199
|
+
* projection). Empty deltas (heartbeats, empty tool-call frames) do not count
|
|
200
|
+
* as a first token.
|
|
201
|
+
* @param chunk - the stream chunk to test.
|
|
202
|
+
* @returns true when the chunk contains a non-empty text/reasoning/tool delta.
|
|
203
|
+
*/
|
|
204
|
+
export declare function isTokenDelta(chunk: StreamChunk): boolean;
|
|
196
205
|
export {};
|
|
197
206
|
//# sourceMappingURL=message.d.ts.map
|
package/lib/types/message.js
CHANGED
|
@@ -78,4 +78,23 @@ export function createToolResultMessage(input) {
|
|
|
78
78
|
}],
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether a stream chunk carries visible model output (the first-token
|
|
83
|
+
* boundary shared by client step timing and the whole-log sessionStats
|
|
84
|
+
* projection). Empty deltas (heartbeats, empty tool-call frames) do not count
|
|
85
|
+
* as a first token.
|
|
86
|
+
* @param chunk - the stream chunk to test.
|
|
87
|
+
* @returns true when the chunk contains a non-empty text/reasoning/tool delta.
|
|
88
|
+
*/
|
|
89
|
+
export function isTokenDelta(chunk) {
|
|
90
|
+
switch (chunk.type) {
|
|
91
|
+
case 'text-delta':
|
|
92
|
+
case 'reasoning-delta':
|
|
93
|
+
return chunk.text !== '';
|
|
94
|
+
case 'tool-call-delta':
|
|
95
|
+
return chunk.argumentsDelta !== '' || chunk.name !== undefined;
|
|
96
|
+
default:
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
81
100
|
//# sourceMappingURL=message.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -261,7 +261,7 @@ export interface LlmResolvedModelInfo extends LlmModelInfo {
|
|
|
261
261
|
* Block indexes correlate interleaved deltas, and `block-end` carries the
|
|
262
262
|
* assembled block. Adapters emit usage before the terminal finish and nothing
|
|
263
263
|
* afterward; tool arguments remain raw JSON strings. An adapter implementation
|
|
264
|
-
* may throw, but `
|
|
264
|
+
* may throw, but `LlmRuntime.stream()` normalizes that failure to a terminal
|
|
265
265
|
* `error` or `aborted` finish before exposing it to consumers.
|
|
266
266
|
*/
|
|
267
267
|
export type StreamChunk = {
|
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.0.1-rc.
|
|
4
|
+
"version": "0.0.1-rc.5",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "restricted"
|
|
7
7
|
},
|
|
@@ -45,20 +45,20 @@
|
|
|
45
45
|
],
|
|
46
46
|
"license": "BSD-3-Clause",
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@deepseek-ai/dsh-attachment": "^0.0.1-rc.
|
|
49
|
-
"@deepseek-ai/dsh-brand": "^0.0.1-rc.
|
|
50
|
-
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.
|
|
51
|
-
"@deepseek-ai/
|
|
52
|
-
"@deepseek-ai/
|
|
48
|
+
"@deepseek-ai/dsh-attachment": "^0.0.1-rc.5",
|
|
49
|
+
"@deepseek-ai/dsh-brand": "^0.0.1-rc.5",
|
|
50
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.5",
|
|
51
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.4",
|
|
52
|
+
"@deepseek-ai/dsh-timeout": "^0.0.1-rc.5"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@deepseek-ai/schemastery": "^3.18.1-rc.
|
|
55
|
+
"@deepseek-ai/schemastery": "^3.18.1-rc.4"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/cordis": "^4.0.1-rc.
|
|
58
|
+
"@deepseek-ai/dsh-brand": "^0.0.1-rc.5",
|
|
59
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.5",
|
|
60
|
+
"@deepseek-ai/dsh-attachment": "^0.0.1-rc.5",
|
|
61
|
+
"@deepseek-ai/dsh-timeout": "^0.0.1-rc.5",
|
|
62
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.4"
|
|
63
63
|
}
|
|
64
64
|
}
|