@deepseek-ai/dsh-llm 0.2.1-alpha.1 → 0.2.1-alpha.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/README.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: c3ecb5a1421f27db
22
22
  zh: 4bbd3c0e513845e1
23
23
  /deepseek-ai-dsh-llm/use-this-package/minimal-composition:
24
- en: 7550d716b90fcb09
25
- zh: 77891a44ee623c7a
24
+ en: 9d481d19dc52e404
25
+ zh: aa09cf4462e93ba6
26
26
  /deepseek-ai-dsh-llm/use-this-package/what-you-can-do:
27
27
  en: b45d7a414141734e
28
28
  zh: a641c8ea3827cee6
package/README.md CHANGED
@@ -60,6 +60,25 @@ After a successful mount, `ctx.llm.listProviders()` reports the registered route
60
60
 
61
61
  `GenerateOptions.messages` accepts durable `Message` values and request-only `RequestUserInput` values. Request-only inputs carry user-role content with no `id` or `source`; Session writes and Agent delivery still require durable messages. Callers keep auxiliary inputs unchanged until the stream settles. A caller that records its exact request, such as session-title generation, must use durable messages.
62
62
 
63
+ `prepareCall(config, signal, configure?)` accepts concrete `LlmCallConfig` values. The optional synchronous, pure `ConfigureCall` function receives detached, deeply frozen `LlmCallControls` and model metadata from the captured adapter generation. Callers can compose ordinary functions to choose concrete controls; later writes replace earlier ones. Preparation retains the captured route, applies defaults to omitted controls, validates the result, and returns a frozen configuration for recording and dispatch. Callback failures or cancellation reject before dispatch. Preparation requires a registered adapter.
64
+
65
+ Configuration functions select controls before adapter defaults are materialized. For example, combine least-effort selection with an explicit output cap, then pass `configure(maxTokens)` as the third argument:
66
+
67
+ ```ts
68
+ import type { ConfigureCall } from '@deepseek-ai/dsh-llm'
69
+
70
+ const leastReasoning: ConfigureCall = (controls, model) => {
71
+ const first = model.reasoning?.efforts[0]
72
+ return first === undefined ? controls : { ...controls, reasoningEffort: first.id }
73
+ }
74
+
75
+ const outputLimit = (maxTokens: number): ConfigureCall =>
76
+ controls => ({ ...controls, maxTokens })
77
+
78
+ const configure = (maxTokens: number): ConfigureCall =>
79
+ (controls, model) => outputLimit(maxTokens)(leastReasoning(controls, model), model)
80
+ ```
81
+
63
82
  ### What you can do
64
83
 
65
84
  - **Stream one model call** — `ctx.llm.stream(options)` yields raw chunks (token-level deltas) for any registered provider and model; consumers assemble them with `BlockAssembler`.
package/README.zh.md CHANGED
@@ -60,6 +60,25 @@ for await (const chunk of ctx.llm.stream({
60
60
 
61
61
  `GenerateOptions.messages` 接受持久 `Message` 值和仅供请求使用的 `RequestUserInput` 值。仅供请求使用的输入包含 user-role 内容,不含 `id` 或 `source`;Session 写入和 Agent 投递仍然要求持久消息。调用方必须在流结束前保持辅助输入不变。会记录完整请求的调用方(例如会话标题生成)必须使用持久消息。
62
62
 
63
+ `prepareCall(config, signal, configure?)` 接受具体的 `LlmCallConfig` 值。可选的同步纯函数 `ConfigureCall` 接收已分离且深度冻结的 `LlmCallControls`,以及捕获的适配器代次所提供的模型元数据。调用方可组合普通函数来选择具体控制项;后执行的写入替换先前的值。准备操作保留捕获的路由,为省略的控制项应用默认值,校验结果,再返回用于记录与分发的冻结配置。回调失败或取消会在分发前拒绝。准备操作要求注册适配器。
64
+
65
+ 配置函数在适配器默认值物化之前选择控制项。例如,将最低强度选择与显式输出上限组合,再将 `configure(maxTokens)` 作为第三个参数传入:
66
+
67
+ ```ts
68
+ import type { ConfigureCall } from '@deepseek-ai/dsh-llm'
69
+
70
+ const leastReasoning: ConfigureCall = (controls, model) => {
71
+ const first = model.reasoning?.efforts[0]
72
+ return first === undefined ? controls : { ...controls, reasoningEffort: first.id }
73
+ }
74
+
75
+ const outputLimit = (maxTokens: number): ConfigureCall =>
76
+ controls => ({ ...controls, maxTokens })
77
+
78
+ const configure = (maxTokens: number): ConfigureCall =>
79
+ (controls, model) => outputLimit(maxTokens)(leastReasoning(controls, model), model)
80
+ ```
81
+
63
82
  ### 你可以做什么
64
83
 
65
84
  - **流式发起一次模型调用**——`ctx.llm.stream(options)` 为任何已注册提供方与模型产出原始分片(token 级增量);消费方用 `BlockAssembler` 组装。
package/lib/index.js CHANGED
@@ -2190,20 +2190,33 @@ let LlmRuntime = (() => {
2190
2190
  * Resolve one call under its current adapter registration. The returned
2191
2191
  * one-shot handle keeps that registration across header logging and dispatch,
2192
2192
  * so HMR cannot combine one adapter's capability result with another adapter.
2193
- * @param config - provider/model route and optional request controls.
2193
+ * An optional synchronous callback selects concrete controls using captured
2194
+ * model metadata. Defaults and validation apply to its result. Callback
2195
+ * failures and cancellation reject preparation before dispatch.
2196
+ * @param config - provider/model route and optional concrete request controls.
2194
2197
  * @param signal - optional cancellation for adapter-owned capability lookup.
2198
+ * @param configure - pure control selection, called once before defaults and validation.
2195
2199
  * @returns a prepared config and its registration-bound stream entry point.
2196
2200
  */
2197
- async prepareCall(config, signal) {
2198
- const registration = this.registration(config.provider);
2199
- const adapterCall = await registration.adapter.prepareCall(config.provider, config.model, signal);
2200
- const modelInfo = this.normalizeModelInfo(registration, config.model, adapterCall.model);
2201
- const resolved = this.resolveCallWithInfo(config, modelInfo);
2201
+ async prepareCall(config, signal, configure) {
2202
+ signal?.throwIfAborted();
2203
+ const { provider, model, ...controls } = config;
2204
+ const registration = this.registration(provider);
2205
+ const adapterCall = await registration.adapter.prepareCall(provider, model, signal);
2206
+ signal?.throwIfAborted();
2207
+ const modelInfo = this.normalizeModelInfo(registration, model, adapterCall.model);
2208
+ const proposed = configure === void 0 ? config : {
2209
+ ...configure(deepFreeze(structuredClone(controls)), deepFreeze(modelInfo)),
2210
+ provider,
2211
+ model
2212
+ };
2213
+ signal?.throwIfAborted();
2214
+ const resolved = this.resolveCallWithInfo(proposed, modelInfo);
2202
2215
  const resolvedConfig = deepFreeze(structuredClone(resolved.config));
2203
2216
  const context = resolved.context === void 0 ? void 0 : deepFreeze(structuredClone(resolved.context));
2204
2217
  const adapterDefaults = deepFreeze({
2205
- ...config.reasoningEffort === void 0 && resolvedConfig.reasoningEffort !== void 0 ? { reasoningEffort: true } : {},
2206
- ...config.maxTokens === void 0 && resolvedConfig.maxTokens !== void 0 ? { maxTokens: true } : {}
2218
+ ...proposed.reasoningEffort === void 0 && resolvedConfig.reasoningEffort !== void 0 ? { reasoningEffort: true } : {},
2219
+ ...proposed.maxTokens === void 0 && resolvedConfig.maxTokens !== void 0 ? { maxTokens: true } : {}
2207
2220
  });
2208
2221
  let dispatched = false;
2209
2222
  return Object.freeze({
@@ -211,9 +211,9 @@ export const TYPERT = {
211
211
  {
212
212
  "kind": "method",
213
213
  "name": "prepareCall",
214
- "signature": "async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>",
214
+ "signature": "async prepareCall( config: LlmCallConfig, signal?: AbortSignal, configure?: ConfigureCall, ): Promise<PreparedLlmCall>",
215
215
  "summary": "Resolve one call under its current adapter registration.",
216
- "jsDoc": "/**\n * Resolve one call under its current adapter registration. The returned\n * one-shot handle keeps that registration across header logging and dispatch,\n * so HMR cannot combine one adapter's capability result with another adapter.\n * @param config - provider/model route and optional request controls.\n * @param signal - optional cancellation for adapter-owned capability lookup.\n * @returns a prepared config and its registration-bound stream entry point.\n */"
216
+ "jsDoc": "/**\n * Resolve one call under its current adapter registration. The returned\n * one-shot handle keeps that registration across header logging and dispatch,\n * so HMR cannot combine one adapter's capability result with another adapter.\n * An optional synchronous callback selects concrete controls using captured\n * model metadata. Defaults and validation apply to its result. Callback\n * failures and cancellation reject preparation before dispatch.\n * @param config - provider/model route and optional concrete request controls.\n * @param signal - optional cancellation for adapter-owned capability lookup.\n * @param configure - pure control selection, called once before defaults and validation.\n * @returns a prepared config and its registration-bound stream entry point.\n */"
217
217
  },
218
218
  {
219
219
  "kind": "method",
@@ -264,6 +264,10 @@ export const TYPERT = {
264
264
  "name": "CompactionId",
265
265
  "declaration": "export type CompactionId = Branded<'CompactionId'>;"
266
266
  },
267
+ {
268
+ "name": "ConfigureCall",
269
+ "declaration": "export type ConfigureCall = (controls: Readonly<LlmCallControls>, model: Readonly<LlmResolvedModelInfo>) => LlmCallControls;"
270
+ },
267
271
  {
268
272
  "name": "ContentBlock",
269
273
  "declaration": "export type ContentBlock = ContentBlockMap[ContentBlockType];"
@@ -338,12 +342,16 @@ export const TYPERT = {
338
342
  },
339
343
  {
340
344
  "name": "LlmCallConfig",
341
- "declaration": "export interface LlmCallConfig {\n provider: string;\n model: string;\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
345
+ "declaration": "export interface LlmCallConfig extends LlmCallControls {\n provider: string;\n model: string;\n}"
342
346
  },
343
347
  {
344
348
  "name": "LlmCallConfigAdapterDefaults",
345
349
  "declaration": "export interface LlmCallConfigAdapterDefaults {\n reasoningEffort?: true;\n maxTokens?: true;\n}"
346
350
  },
351
+ {
352
+ "name": "LlmCallControls",
353
+ "declaration": "export interface LlmCallControls {\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
354
+ },
347
355
  {
348
356
  "name": "LlmConfigurableProvider",
349
357
  "declaration": "export interface LlmConfigurableProvider {\n provider: string;\n displayName: string;\n settingsNs: string;\n settingsPath: readonly string[];\n declared?: boolean;\n error?: string;\n}"
@@ -414,7 +422,7 @@ export const TYPERT = {
414
422
  },
415
423
  {
416
424
  "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 '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}"
425
+ "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 'working-directory': { kind: 'working-directory'; };\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
426
  },
419
427
  {
420
428
  "name": "ModelMessageSource",
@@ -5,22 +5,35 @@
5
5
  * changed snapshots instead of allowing silent per-call drift.
6
6
  * @module dsh-llm/call-config
7
7
  */
8
- import type { GenerateOptions } from './types.ts';
8
+ import type { GenerateOptions, LlmResolvedModelInfo } from './types.ts';
9
9
  import type { ReasoningEffortId } from './brand.ts';
10
+ /** Concrete generation settings; omitted controls use the selected route's defaults. */
11
+ export interface LlmCallControls {
12
+ reasoningEffort?: ReasoningEffortId;
13
+ temperature?: number;
14
+ maxTokens?: number;
15
+ stop?: string[];
16
+ }
10
17
  /**
11
18
  * Provider, model, reasoning effort, and sampling scalars of one conversation's
12
19
  * requests. Every field maps 1:1 onto the same-named `GenerateOptions` field;
13
20
  * the loop builds requests from the logged header rather than accepting these
14
21
  * per call.
15
22
  */
16
- export interface LlmCallConfig {
23
+ export interface LlmCallConfig extends LlmCallControls {
17
24
  provider: string;
18
25
  model: string;
19
- reasoningEffort?: ReasoningEffortId;
20
- temperature?: number;
21
- maxTokens?: number;
22
- stop?: string[];
23
26
  }
27
+ /**
28
+ * Synchronous, pure configuration of one call before defaults and validation.
29
+ * Compose functions in the desired order; later writes replace earlier ones.
30
+ * The returned controls cannot change the captured route. Errors reject
31
+ * preparation before dispatch; only the resolved configuration is recordable.
32
+ * @param controls - detached, deeply frozen proposed controls, without defaults.
33
+ * @param model - detached, deeply frozen metadata from the captured adapter generation.
34
+ * @returns concrete controls; omitted fields receive the route's defaults.
35
+ */
36
+ export type ConfigureCall = (controls: Readonly<LlmCallControls>, model: Readonly<LlmResolvedModelInfo>) => LlmCallControls;
24
37
  /**
25
38
  * Effective config fields supplied by exact-model adapter resolution rather
26
39
  * than by the caller's request proposal.
@@ -10,7 +10,7 @@ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
10
10
  import type { GenerateOptions, LlmConfigurableProvider, LlmDiscoveredModel, LlmFailure, LlmImageRequestPricing, LlmModelContext, LlmModelDiscoveryRequest, LlmModelInfo, LlmResolvedModelInfo, LlmProviderInfo, ModelModality, StreamChunk, SystemPromptUpdate, ToolUpdate } from './types.ts';
11
11
  import type { ResolvedRetryPolicy } from './retry-policy.ts';
12
12
  import type { ProviderRequestId } from './brand.ts';
13
- import type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
13
+ import type { ConfigureCall, LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
14
14
  import { HarnessError } from './error.ts';
15
15
  import type { FileAttachmentRef } from '@deepseek-ai/dsh-attachment';
16
16
  export * from './attribution.ts';
@@ -24,7 +24,7 @@ export * from './message.ts';
24
24
  export * from './retry-policy.ts';
25
25
  export { BlockAssembler } from './assembler.ts';
26
26
  export { callConfigEquals, isAgentLoopRequest, markAgentLoopRequest } from './call-config.ts';
27
- export type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
27
+ export type { ConfigureCall, LlmCallConfig, LlmCallConfigAdapterDefaults, LlmCallControls } from './call-config.ts';
28
28
  declare module '@deepseek-ai/cordis' {
29
29
  interface Context {
30
30
  llm: LlmRuntime;
@@ -379,11 +379,15 @@ export declare class LlmRuntime extends TypertRemoteService {
379
379
  * Resolve one call under its current adapter registration. The returned
380
380
  * one-shot handle keeps that registration across header logging and dispatch,
381
381
  * so HMR cannot combine one adapter's capability result with another adapter.
382
- * @param config - provider/model route and optional request controls.
382
+ * An optional synchronous callback selects concrete controls using captured
383
+ * model metadata. Defaults and validation apply to its result. Callback
384
+ * failures and cancellation reject preparation before dispatch.
385
+ * @param config - provider/model route and optional concrete request controls.
383
386
  * @param signal - optional cancellation for adapter-owned capability lookup.
387
+ * @param configure - pure control selection, called once before defaults and validation.
384
388
  * @returns a prepared config and its registration-bound stream entry point.
385
389
  */
386
- prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>;
390
+ prepareCall(config: LlmCallConfig, signal?: AbortSignal, configure?: ConfigureCall): Promise<PreparedLlmCall>;
387
391
  private registration;
388
392
  /** Remove replay state whose historical route is owned by another adapter. */
389
393
  private forAdapter;
@@ -716,24 +716,35 @@ let LlmRuntime = (() => {
716
716
  * Resolve one call under its current adapter registration. The returned
717
717
  * one-shot handle keeps that registration across header logging and dispatch,
718
718
  * so HMR cannot combine one adapter's capability result with another adapter.
719
- * @param config - provider/model route and optional request controls.
719
+ * An optional synchronous callback selects concrete controls using captured
720
+ * model metadata. Defaults and validation apply to its result. Callback
721
+ * failures and cancellation reject preparation before dispatch.
722
+ * @param config - provider/model route and optional concrete request controls.
720
723
  * @param signal - optional cancellation for adapter-owned capability lookup.
724
+ * @param configure - pure control selection, called once before defaults and validation.
721
725
  * @returns a prepared config and its registration-bound stream entry point.
722
726
  */
723
- async prepareCall(config, signal) {
724
- const registration = this.registration(config.provider);
725
- const adapterCall = await registration.adapter.prepareCall(config.provider, config.model, signal);
726
- const modelInfo = this.normalizeModelInfo(registration, config.model, adapterCall.model);
727
- const resolved = this.resolveCallWithInfo(config, modelInfo);
727
+ async prepareCall(config, signal, configure) {
728
+ signal?.throwIfAborted();
729
+ const { provider, model, ...controls } = config;
730
+ const registration = this.registration(provider);
731
+ const adapterCall = await registration.adapter.prepareCall(provider, model, signal);
732
+ signal?.throwIfAborted();
733
+ const modelInfo = this.normalizeModelInfo(registration, model, adapterCall.model);
734
+ const proposed = configure === undefined
735
+ ? config
736
+ : { ...configure(deepFreeze(structuredClone(controls)), deepFreeze(modelInfo)), provider, model };
737
+ signal?.throwIfAborted();
738
+ const resolved = this.resolveCallWithInfo(proposed, modelInfo);
728
739
  const resolvedConfig = deepFreeze(structuredClone(resolved.config));
729
740
  const context = resolved.context === undefined
730
741
  ? undefined
731
742
  : deepFreeze(structuredClone(resolved.context));
732
743
  const adapterDefaults = deepFreeze({
733
- ...config.reasoningEffort === undefined && resolvedConfig.reasoningEffort !== undefined
744
+ ...proposed.reasoningEffort === undefined && resolvedConfig.reasoningEffort !== undefined
734
745
  ? { reasoningEffort: true }
735
746
  : {},
736
- ...config.maxTokens === undefined && resolvedConfig.maxTokens !== undefined
747
+ ...proposed.maxTokens === undefined && resolvedConfig.maxTokens !== undefined
737
748
  ? { maxTokens: true }
738
749
  : {},
739
750
  });
@@ -348,7 +348,7 @@ export interface LlmReasoningEffortInfo {
348
348
  }
349
349
  /** Selectable reasoning efforts for one exact provider/model route. */
350
350
  export interface LlmModelReasoningInfo {
351
- /** Supported efforts in adapter-preferred display order. */
351
+ /** Supported efforts from least to greatest selectable reasoning effort, not predicted token use or latency. */
352
352
  efforts: readonly LlmReasoningEffortInfo[];
353
353
  /**
354
354
  * Adapter-configured default materialized into requests when callers omit
@@ -524,8 +524,8 @@ export interface GenerateOptions {
524
524
  sessionId?: Branded<'SessionId'>;
525
525
  /**
526
526
  * Provider-neutral classification for an auxiliary model call. Adapters may
527
- * map the purpose to model-hidden transport metadata or purpose-specific
528
- * generation policy. Ordinary conversation requests leave it unset.
527
+ * map the purpose to model-hidden transport metadata. Ordinary conversation
528
+ * requests leave it unset.
529
529
  */
530
530
  purpose?: 'compaction' | 'session-title';
531
531
  }
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.1-alpha.1",
4
+ "version": "0.2.1-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -59,16 +59,16 @@
59
59
  "@deepseek-ai/cordis": "~4.0.5-alpha.1"
60
60
  },
61
61
  "dependencies": {
62
- "zod": "^4.4.3",
63
- "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.1",
62
+ "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.2",
64
63
  "@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"
64
+ "zod": "^4.4.3",
65
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.2",
66
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
67
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.2",
68
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2"
69
69
  },
70
70
  "devDependencies": {
71
- "@deepseek-ai/dsh-attachment": "0.2.1-alpha.1",
71
+ "@deepseek-ai/dsh-attachment": "0.2.1-alpha.2",
72
72
  "@deepseek-ai/cordis": "~4.0.5-alpha.1"
73
73
  }
74
74
  }