dsh-model-reasoning-defaults 0.0.2 → 0.0.3

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.md CHANGED
@@ -37,7 +37,7 @@ dsh plugin --profile web remove dsh-model-reasoning-defaults
37
37
  - **设置页可视化编辑**:在 Web 设置页「推理等级默认」中直接增删路由并选择等级,保存即时生效(带修订号并发保护)。
38
38
  - **全入口统一注入**:无缝覆盖 Agent 主循环(`agent/request`)、直接调用(`llm.stream`)以及预准备入口(`prepareCall` / `resolveCallConfig`)。
39
39
  - **对象安全与不可变性保证**:采用非破坏性浅复制机制,安全处理 `Object.freeze` 深冻结请求,原请求对象与未命中的配置始终保持只读与纯净。
40
- - **宿主标识直读**:loop 请求识别直接使用 DSH 导出的 `isAgentLoopRequest`(基于宿主 `markAgentLoopRequest` 进程标识),不维护自建登记表,跨模块副本与热重载天然一致。
40
+ - **跨副本的 Loop 边界识别**:不依赖 `@deepseek-ai/dsh-llm` 副本私有的 `WeakSet`,而按公开请求契约识别同时具备 `sessionId` 与顶层冻结状态的 Loop 请求,避免宿主与插件解析到不同模块副本时误复制请求。
41
41
  - **无缝热重载(Hot-Reload Safe)**:基于 `RuntimePatchState` 管理多 Fiber 状态,配置变更即时生效,仅在最后一个插件 Fiber 卸载时还原原始方法,杜绝方法悬挂。
42
42
 
43
43
  ---
@@ -180,7 +180,7 @@ llm-deepseek:
180
180
  【Agent Loop 请求】 【手写 / 辅助请求】
181
181
  (agent/request Waterfall) (llm.stream / prepareCall)
182
182
  │ │
183
- 注入默认 reasoningEffort isAgentLoopRequest 判定
183
+ 注入默认 reasoningEffort sessionId + 冻结边界判定
184
184
  到调用配置种子(loop 随后据此构建完整 │
185
185
  请求并深冻结、打宿主进程标识) ┌─────────────┴─────────────┐
186
186
  │ ▼ ▼
@@ -197,12 +197,12 @@ llm-deepseek:
197
197
 
198
198
  ### 1. Agent 主循环请求处理
199
199
  - 注册全局 `agent/request` waterfall 监听器。此阶段操作的是调用配置种子,完整请求对象尚未构建;插件按路由计算默认等级并注入 `reasoningEffort`。
200
- - loop 随后将该配置连同派生消息组装为完整请求,深冻结并打上宿主 `markAgentLoopRequest` 进程标识。
201
- - 请求进入 `llm.stream` 入口时,入口包装用宿主导出的 `isAgentLoopRequest` 识别 loop 请求,**直接按原引用透传**——浅复制会丢失该进程标识,导致 compaction 与请求观察者把会话请求误判为手写一次性请求。
200
+ - loop 随后将该配置连同派生消息组装为完整请求,写入 `sessionId`、深冻结并打上宿主 `markAgentLoopRequest` 进程标识。
201
+ - 请求进入 `llm.stream` 入口时,入口包装按公开的 `sessionId + Object.freeze` 契约识别 loop 请求,**直接按原引用透传**。不读取模块副本私有的 `isAgentLoopRequest` 标识,避免宿主与插件加载不同 `dsh-llm` 副本时误复制请求。
202
202
 
203
203
  ### 2. 手写及辅助调用处理
204
204
  - 直接调用 `llm.stream(options)` 时,入口包装在 waterfall **开始前**执行拦截。
205
- - 对不带 loop 标识的请求(包括带 `purpose: 'compaction'` 的辅助请求),通过浅复制补齐 `reasoningEffort` 后再进入下游链路。
205
+ - 对不带该 Loop 边界契约的请求(包括带 `purpose: 'compaction'` 的辅助请求),通过浅复制补齐 `reasoningEffort` 后再进入下游链路。
206
206
 
207
207
  ### 3. 配置准备与解析入口
208
208
  - 同步拦截 `llm.prepareCall` 与 `llm.resolveCallConfig`,在预计算阶段统一应用相同的路由查找规则,并完整透传 `AbortSignal`。
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { ReasoningEffortId, isAgentLoopRequest } from "@deepseek-ai/dsh-llm";
1
+ import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  //#region src/index.ts
4
4
  /** 插件名(同时作为 Cordis 插件 id)。 */
@@ -464,6 +464,21 @@ function applyCurrentDefault(state, config) {
464
464
  return Object.keys(defaults).length === 0 ? config : applyDefaultReasoningEffort(config, defaults);
465
465
  }
466
466
  /**
467
+ * 判断请求是否已经进入 Agent Loop 的不可变边界。
468
+ *
469
+ * `isAgentLoopRequest()` 背后是 `@deepseek-ai/dsh-llm` 模块副本私有的
470
+ * `WeakSet`。宿主和插件若解析到不同副本,宿主写入的标识不会被插件读取,
471
+ * 入口包装就会错误地复制 Loop 请求。这里改用 DSH 公开的请求契约:Loop
472
+ * 在交给 LLM 前会把完整请求顶层冻结,并写入用于路由的 `sessionId`;冻结的
473
+ * 手写辅助请求(例如 compaction)没有 `sessionId`,仍应允许插件复制并补齐默认值。
474
+ *
475
+ * @param options - 进入 `llm.stream` 的完整请求。
476
+ * @returns 是否应按原引用透传该请求。
477
+ */
478
+ function isAgentLoopRequestBoundary(options) {
479
+ return options.sessionId !== void 0 && Object.isFrozen(options);
480
+ }
481
+ /**
467
482
  * 从当前 stream 包装上恢复跨热重载共享的状态。
468
483
  *
469
484
  * @param llm - LLM 服务实例。
@@ -477,7 +492,7 @@ function existingRuntimePatchState(llm) {
477
492
  *
478
493
  * 手写请求在 stream 入口浅复制补齐后再进 waterfall,保证每个监听器只处理
479
494
  * 一次;loop 构建的请求由宿主打 markAgentLoopRequest 进程标识并深冻结,
480
- * 经 isAgentLoopRequest 判定后按原引用透传——浅复制会丢失该进程标识。
495
+ * 经公开的冻结与 sessionId 契约判定后按原引用透传——浅复制会丢失请求身份。
481
496
  *
482
497
  * @param ctx - 当前插件上下文。
483
498
  * @param llm - LLM 服务实例。
@@ -489,7 +504,7 @@ function createRuntimePatchState(ctx, llm) {
489
504
  const originalResolveCallConfig = llm.resolveCallConfig;
490
505
  let state;
491
506
  const stream = (options) => {
492
- const patched = isAgentLoopRequest(options) ? options : applyCurrentDefault(state, options);
507
+ const patched = isAgentLoopRequestBoundary(options) ? options : applyCurrentDefault(state, options);
493
508
  return originalStream.call(llm, patched);
494
509
  };
495
510
  const prepareCall = async (config, signal) => {
package/lib/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * dsh-model-reasoning-defaults\n *\n * 为每个模型配置默认推理等级。请求已显式指定 reasoningEffort 时保持原值;\n * 其余按 provider/model 路由查找默认值并转换为 DSH 的品牌类型。\n *\n * agent-loop 请求在 agent/request waterfall 中注入(此后到达 llm/stream 时\n * 已深冻结);手写调用在 llm.stream 入口复制后再进入 waterfall,调用配置\n * 方法走同一共享包装层。\n *\n * @module dsh-model-reasoning-defaults\n */\nimport type { Context, EventOptions } from '@deepseek-ai/cordis'\nimport { isAgentLoopRequest, ReasoningEffortId } from '@deepseek-ai/dsh-llm'\nimport type {\n LlmCallConfig,\n LlmRuntime,\n} from '@deepseek-ai/dsh-llm'\nimport z from '@deepseek-ai/schemastery'\n\n/** 插件名(同时作为 Cordis 插件 id)。 */\nexport const name = 'dsh-model-reasoning-defaults'\n\n/** 声明依赖的服务:LLM 服务就绪后装配;注册/读取设置命名空间需要设置服务。 */\nexport const inject = ['llm', 'settings'] as const\n\n/** 单个模型的推理等级配置。 */\nexport interface ModelConfig {\n /** 模型 ID(数组形式必填;字典形式以键名为准,可省略)。 */\n id?: string\n /** 默认推理等级(如 off | minimal | low | medium | high | xhigh | max)。 */\n reasoningEffort?: string\n}\n\n/** 单个模型配置输入:既可以是等级字符串简写,也可以是配置对象。 */\nexport type ModelConfigInput = string | ModelConfig\n\n/** 单个 Provider 的推理等级配置。 */\nexport interface ProviderConfig {\n /** 该 Provider 下所有模型的默认推理等级。 */\n reasoningEffort?: string\n /** 该 Provider 下各模型的推理等级配置(字典或对象列表)。 */\n models?: Record<string, ModelConfigInput> | ModelConfigInput[]\n}\n\n/** Provider 配置输入:既可以是等级字符串简写(作用于该 Provider 所有模型),也可以是配置对象。 */\nexport type ProviderConfigInput = string | ProviderConfig\n\n/**\n * 插件配置:支持按 Provider 与模型嵌入式层级配置,也可使用扁平路由字典 defaults。\n * 两种形式是同等的一等配置入口(本仓库无历史版本,不存在\"新旧兼容\"语义),\n * 归一化到同一张路由表后统一参与六级匹配。\n *\n * 合并后的匹配优先级从高到低为:\n * - \"provider:model\":精确匹配(来自 providers[p].models[m] 或 defaults[\"provider:model\"])\n * - \"provider:*\":该 provider 下所有模型(来自 providers[p].reasoningEffort 或 defaults[\"provider:*\"])\n * - \"provider/*\":斜杠写法(来自 defaults[\"provider/*\"])\n * - \"provider\":裸 provider 写法(来自 defaults[\"provider\"])\n * - \"*:model\":跨 provider 的同名模型(来自 top-level models[m] 或 defaults[\"*:model\"])\n * - \"*\":全局匹配(来自 top-level reasoningEffort 或 defaults[\"*\"])\n */\nexport interface Config {\n /** 扁平路由字典:键为上表六级路由键;与结构化配置产生同键时覆盖结构化项。 */\n defaults?: Record<string, string>\n /** 按 Provider 组织的嵌入式配置。 */\n providers?: Record<string, ProviderConfigInput>\n /** 跨 Provider 适用的模型默认配置(等价于 \"*:model\")。 */\n models?: Record<string, ModelConfigInput> | ModelConfigInput[]\n /** 全局默认推理等级(等价于 \"*\")。 */\n reasoningEffort?: string\n}\n\nconst nonBlankPattern = /^\\S+$/\n\n// 已知推理等级集合:llm-pi-ai 的 ModelThinkingLevel(off/minimal/low/medium/high/\n// xhigh/max)与 llm-deepseek(off/low/high/max,为其子集)的并集。仅用于激活期与\n// 提取期的告警提示而不阻断注入——最终合法性由适配器逐字匹配裁决,本地白名单\n// 收窄反而会在上游新增等级时挡住合法配置。\nconst KNOWN_REASONING_EFFORTS: ReadonlySet<string> = new Set([\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n])\n\n// llm-deepseek 注册的唯一 provider 路由名(宿主 packages/llm/llm-deepseek 的\n// PROVIDER 常量);llm-pi-ai 家族无法注册同名路由,宿主会拒绝重名 adapter family。\nconst DEEPSEEK_OFFICIAL_PROVIDER_ID = 'deepseek-official'\n\nconst modelConfigSchema = z.object({\n id: z.string().pattern(nonBlankPattern),\n reasoningEffort: z.string().pattern(nonBlankPattern),\n})\n\nconst modelEntrySchema = z.union([\n z.string().pattern(nonBlankPattern),\n modelConfigSchema,\n])\n\nconst modelsFieldSchema = z.union([\n z.dict(modelEntrySchema, z.string().pattern(nonBlankPattern)),\n z.array(modelEntrySchema),\n])\n\nconst providerConfigSchema = z.object({\n reasoningEffort: z.string().pattern(nonBlankPattern),\n models: modelsFieldSchema,\n})\n\nconst providerEntrySchema = z.union([\n z.string().pattern(nonBlankPattern),\n providerConfigSchema,\n])\n\n/** Cordis 配置模式(显式注解接口类型,满足声明产物的可命名要求)。 */\nexport const Config: z<Config> = z.object({\n // 路由键与等级值都要求非空白:空白键永远不会被 candidateKeys 命中,空白值\n // 会被适配器逐字匹配拒绝,都在配置阶段直接报错暴露最省排查成本。\n // .default({}) 与 schemastery 对 dict 的内建空对象默认重复,保留它是为了让\n // 编程直调路径(绕过 cordis resolveConfig)也能拿到确定的形状。\n defaults: z.dict(\n z.string().pattern(nonBlankPattern),\n z.string().pattern(nonBlankPattern),\n ).default({}),\n providers: z.dict(\n providerEntrySchema,\n z.string().pattern(nonBlankPattern),\n ).default({}),\n models: modelsFieldSchema,\n reasoningEffort: z.string().pattern(nonBlankPattern),\n})\n\n/**\n * 从字符串简写或配置对象中提取非空推理等级。\n *\n * 只认单一规范字段 reasoningEffort,不做多字段兜底链:同义多源读取会在字段\n * 冲突时按书写顺序静默裁决(例如 reasoningEffort 与 effort 同时配置时无声地\n * 二选一),掩盖配置错误;确需别名应由用户层显式映射后再传入。\n *\n * @param val - 等级字符串,或 ModelConfig/ProviderConfig 形状的对象。\n * @returns trim 后非空的等级字符串;无法提取时返回 undefined。\n */\nfunction extractEffort(val: unknown): string | undefined {\n const candidate = typeof val === 'string'\n ? val\n : (typeof val === 'object' && val !== null)\n ? (val as { reasoningEffort?: unknown }).reasoningEffort\n : undefined\n if (typeof candidate !== 'string') return undefined\n const trimmed = candidate.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * 将结构化 Config 归一化为统一的扁平路由字典。\n *\n * @param config - 插件配置对象。\n * @returns 归一化后的键值映射;键与值均已去除首尾空白。\n */\nexport function normalizeConfig(config: Config): Record<string, string> {\n const result: Record<string, string> = {}\n\n // 1. 全局默认 (对应 \"*\")\n const globalEffort = extractEffort(config.reasoningEffort)\n if (globalEffort) {\n result['*'] = globalEffort\n }\n\n // 2. 顶层跨 Provider 模型默认 (对应 \"*:model\")\n if (config.models) {\n if (Array.isArray(config.models)) {\n for (const item of config.models) {\n // 数组形式必须携带非空白 id 才能生成路由键;缺失项由 apply() 的\n // 激活期诊断告警上报,这里只负责安静地跳过无效形状。\n const id = typeof item === 'object' && item !== null && typeof item.id === 'string' ? item.id.trim() : ''\n if (id.length === 0) continue\n const effort = extractEffort(item)\n if (effort) result['*:' + id] = effort\n }\n } else if (typeof config.models === 'object') {\n for (const [rawModelId, modelVal] of Object.entries(config.models)) {\n const modelId = rawModelId.trim()\n if (modelId.length === 0) continue\n const effort = extractEffort(modelVal)\n if (effort) result['*:' + modelId] = effort\n }\n }\n }\n\n // 3. Provider 层级配置\n if (config.providers && typeof config.providers === 'object') {\n for (const [rawProviderId, providerVal] of Object.entries(config.providers)) {\n const providerId = rawProviderId.trim()\n if (providerId.length === 0) continue\n\n // 3.1 Provider 级默认 (对应 \"provider:*\")\n const providerEffort = extractEffort(providerVal)\n if (providerEffort) {\n result[providerId + ':*'] = providerEffort\n }\n\n // 3.2 Provider 内的具体模型 (对应 \"provider:model\")\n if (typeof providerVal === 'object' && providerVal !== null && providerVal.models) {\n const models = providerVal.models\n if (Array.isArray(models)) {\n for (const item of models) {\n const id = typeof item === 'object' && item !== null && typeof item.id === 'string' ? item.id.trim() : ''\n if (id.length === 0) continue\n const effort = extractEffort(item)\n if (effort) result[providerId + ':' + id] = effort\n }\n } else if (typeof models === 'object') {\n for (const [rawModelId, modelVal] of Object.entries(models)) {\n const modelId = rawModelId.trim()\n if (modelId.length === 0) continue\n const effort = extractEffort(modelVal)\n if (effort) result[providerId + ':' + modelId] = effort\n }\n }\n }\n }\n }\n\n // 4. 扁平 defaults 字典:与结构化配置同等的一等入口,写在最后使其在与结构化\n // 配置同键冲突时胜出(显式路由键的表达意图更具体)。键值均 trim 后判断,\n // 与结构化路径(id 空则跳过)保持一致:空白键与空白值一样永不命中任何\n // 请求路由(cordis 路径由 schema 拒绝,此处覆盖绕开 schema 的直调路径)。\n if (config.defaults && typeof config.defaults === 'object') {\n for (const [rawKey, value] of Object.entries(config.defaults)) {\n const key = rawKey.trim()\n const trimmedValue = value.trim()\n if (key.length === 0 || trimmedValue.length === 0) continue\n result[key] = trimmedValue\n }\n }\n\n return result\n}\n\ntype ReasoningConfig = Pick<LlmCallConfig, 'provider' | 'model' | 'reasoningEffort'>\n\n/**\n * 静态体检插件配置,返回人类可读的问题清单(纯函数,不修改任何数据)。\n *\n * schemastery 的对象属性全部可选且对未知键透传,\"拼错字段\"\"模型项缺 id\"\n * 这类错误能通过校验却在 normalizeConfig 中被静默忽略;此清单在插件激活时\n * 一次性告警,把这类静默降级变成可见信息。等级值不在已知集合也在此提示,\n * 但仍会注入——最终是否可用交给适配器逐字匹配裁决。\n *\n * @param config - 用户原始配置(未经归一化)。\n * @returns 问题描述列表;无问题时为空数组。\n */\nfunction collectConfigIssues(config: Config): string[] {\n const issues: string[] = []\n const knownLevels = [...KNOWN_REASONING_EFFORTS].join('/')\n\n const checkLevel = (route: string, level: string | undefined): void => {\n if (level !== undefined && !KNOWN_REASONING_EFFORTS.has(level)) {\n issues.push('路由 ' + route + ' 的推理等级 \"' + level + '\" 不在已知集合 (' + knownLevels + ') 内,请确认目标模型支持')\n }\n }\n\n /**\n * 检查单个模型条目:字符串简写直接校验等级;\n * 对象形式要求携带有效等级,数组元素还要求有效 id。\n */\n const checkModelEntry = (route: string, entry: ModelConfigInput, requireId: boolean): void => {\n if (typeof entry === 'string') {\n // 数组位置没有键名可用作 id,字符串简写永远生成不了路由键;字典位置的\n // 字符串值以键名为 id(requireId=false),不受此限制。\n if (requireId) {\n issues.push('数组形式模型条目缺少有效 id(字符串简写无键名可用),已被忽略:' + JSON.stringify(entry))\n return\n }\n checkLevel(route, extractEffort(entry))\n return\n }\n const id = typeof entry.id === 'string' ? entry.id.trim() : ''\n if (requireId && id.length === 0) {\n issues.push('模型条目缺少有效 id,已被忽略:' + JSON.stringify(entry))\n return\n }\n const effort = extractEffort(entry)\n if (effort === undefined) {\n issues.push('模型条目未包含 reasoningEffort,已被忽略:' + JSON.stringify(entry))\n return\n }\n checkLevel(route, effort)\n }\n\n // 全局等级\n checkLevel('*', extractEffort(config.reasoningEffort))\n\n // 顶层跨 Provider 模型\n if (config.models) {\n if (Array.isArray(config.models)) {\n config.models.forEach((entry, index) => {\n checkModelEntry('*:<models[' + index + ']>', entry, true)\n })\n } else if (typeof config.models === 'object') {\n for (const [modelId, entry] of Object.entries(config.models)) {\n checkModelEntry('*:' + modelId, entry, false)\n }\n }\n }\n\n // Provider 层级\n if (config.providers && typeof config.providers === 'object') {\n for (const [providerId, providerVal] of Object.entries(config.providers)) {\n if (typeof providerVal === 'string') {\n checkLevel(providerId + ':*', extractEffort(providerVal))\n continue\n }\n const hasModels = providerVal.models !== undefined\n const providerEffort = extractEffort(providerVal)\n if (!hasModels && providerEffort === undefined) {\n issues.push('provider ' + providerId + ' 未包含 reasoningEffort 或 models,整条配置已被忽略')\n continue\n }\n checkLevel(providerId + ':*', providerEffort)\n if (hasModels) {\n const models = providerVal.models!\n if (Array.isArray(models)) {\n models.forEach((entry, index) => {\n checkModelEntry(providerId + ':<models[' + index + ']>', entry, true)\n })\n } else if (typeof models === 'object') {\n for (const [modelId, entry] of Object.entries(models)) {\n checkModelEntry(providerId + ':' + modelId, entry, false)\n }\n }\n }\n }\n }\n\n // 扁平 defaults 字典:schema 在 cordis 路径直接拒绝空白键值,但编程直调\n // 会绕过校验;这里的空键检查与 normalizeConfig 的跳过逻辑保持一致,把\n // 静默降级变成可见告警。\n if (config.defaults && typeof config.defaults === 'object') {\n for (const [rawKey, value] of Object.entries(config.defaults)) {\n const key = rawKey.trim()\n if (key.length === 0) {\n issues.push('扁平路由键为空白,已被忽略:' + JSON.stringify(rawKey))\n continue\n }\n checkLevel(key, extractEffort(value))\n }\n }\n\n return issues\n}\n\n// agent/request 的 loop 侧 emit 实参是 { turn, step, signal };core/agent 的派发器\n// 会把路由主体 agent(Scoped<Agent>)融合进 payload 并同时作为监听器 this,因此\n// 实收 payload 含 agent 字段。本监听器不读取任何 payload 字段,此处仅按本插件\n// 用到的形状窄化 ctx.on 的签名。\ntype AgentRequestPayload = {\n turn: number\n step: number\n signal: AbortSignal\n}\n\ntype AgentRequestListener = (\n this: object,\n payload: AgentRequestPayload,\n next: () => Promise<LlmCallConfig>,\n) => Promise<LlmCallConfig>\n\ntype AgentRequestRegistrar = (\n name: 'agent/request',\n listener: AgentRequestListener,\n options?: boolean | EventOptions,\n) => () => boolean\n\n/**\n * 注册全局 agent/request waterfall。\n *\n * cordis 的 ctx.on 内部会把监听器注册为当前 fiber 上的 effect(见 events\n * 服务的 register()),无论 global 标志如何都会在 fiber 卸载时自动清理;\n * global 只影响事件派发时的上下文过滤。因此无需再包一层 ctx.effect。\n *\n * 该事件的运行时接收者是 Scoped<Agent>,Cordis 根 Context 的事件泛型表不会\n * 暴露它,因此在最小边界内窄化签名(仅约束本插件用到的事件名与参数)。\n *\n * @param ctx - 当前插件上下文。\n * @param listener - agent/request waterfall 监听器。\n */\nfunction registerAgentRequestListener(ctx: Context, listener: AgentRequestListener): void {\n // 窄化仅覆盖本插件实际使用的事件名与参数;内部转调仍通过 ctx.on 的真实实现。\n const register = ctx.on.bind(ctx) as AgentRequestRegistrar\n register('agent/request', listener, { global: true })\n}\n\n/**\n * 生成一个路由的候选配置键。\n *\n * 兼容键保留在明确的优先级表中,避免不同调用入口各自实现一套匹配规则。\n *\n * @param provider - 请求使用的 provider。\n * @param model - 请求使用的 model。\n * @returns 按匹配优先级排列的候选键。\n */\nfunction candidateKeys(provider: string, model: string): readonly string[] {\n return [\n provider + ':' + model,\n provider + ':*',\n provider + '/*',\n provider,\n '*:' + model,\n '*',\n ]\n}\n\n/**\n * 查找一个路由对应的默认推理等级。\n *\n * @param defaults - 归一化后的扁平路由映射。\n * @param provider - 请求使用的 provider。\n * @param model - 请求使用的 model。\n * @returns 找到的非空推理等级,未找到时返回 undefined。\n */\nfunction lookupEffort(\n defaults: Readonly<Record<string, string>>,\n provider: string,\n model: string,\n): string | undefined {\n for (const key of candidateKeys(provider, model)) {\n const effort = defaults[key]\n if (typeof effort === 'string' && effort.length > 0) return effort\n }\n return undefined\n}\n\n/**\n * 在不修改原对象的前提下应用默认推理等级。\n *\n * 该函数同时用于 GenerateOptions 和 LlmCallConfig,因此调用方可以保留\n * 原请求对象中的 messages、signal 等其它字段,并安全处理深冻结输入。\n *\n * 第二参数只接受归一化后的扁平路由表;结构化 Config 必须先经 normalizeConfig。\n * 曾尝试用对象形状嗅探让本函数同时接纳两种入参,但路由表允许任意非空白键,\n * 含 \"models\"/\"providers\" 等键的路由表会被误判成结构化配置(整表归一化为空、\n * 注入全部失效),反之仅含顶层 reasoningEffort 的最小结构化配置又会漏判——\n * 两类误判都是静默失效,因此彻底拆掉形状嗅探。\n *\n * @param config - 待补齐的 LLM 请求或调用配置。\n * @param defaults - normalizeConfig 产出的扁平路由表。\n * @returns 原配置或带有 reasoningEffort 的浅复制对象。\n */\nexport function applyDefaultReasoningEffort<T extends ReasoningConfig>(\n config: T,\n defaults: Readonly<Record<string, string>>,\n): T {\n if (config.reasoningEffort !== undefined || !config.provider || !config.model) return config\n const effort = lookupEffort(defaults, config.provider, config.model)\n if (effort === undefined) return config\n return { ...config, reasoningEffort: ReasoningEffortId(effort) }\n}\n\ntype ReasoningDefaults = Readonly<Record<string, string>>\n\n/**\n * 设置页 UI 专用命名空间。该命名空间由本插件注册为可写设置项,浏览器设置\n * 面板通过宿主统一的 settings API 编辑它;生效优先级位于自动提取之上、\n * cordis 插件配置之下(见 resolveEffectiveDefaults)。\n */\nexport const UI_DEFAULTS_NAMESPACE = 'model-reasoning-defaults'\n\n/** 设置命名空间的用户层数据形状:与 Config.defaults 同构的扁平路由字典。 */\nexport interface UiDefaultsSection {\n /** 扁平路由字典:键为六级路由键,值为推理等级。 */\n defaults?: Record<string, string>\n}\n\n// 命名空间 schema 与 Config.defaults 复用同一约束(非空白键值),保证两条\n// 配置入口的合法性标准完全一致。\nconst uiDefaultsSectionSchema = z.object({\n defaults: z.dict(\n z.string().pattern(nonBlankPattern),\n z.string().pattern(nonBlankPattern),\n ).default({}),\n})\n\ntype SettingsService = {\n get: (ns: string) => unknown\n /**\n * 注册可写命名空间(真实签名见 @deepseek-ai/dsh-settings 的 SettingsProvider.register;\n * 此处只声明本插件用到的参数面)。可选:设置服务存在才可注册。\n */\n register?: (\n ns: string,\n schema: z<UiDefaultsSection>,\n options?: { applies?: 'live' | 'restart' },\n ) => unknown\n}\n\n/**\n * 取当前上下文上的设置服务;不存在或 get 不可调用时返回 undefined。\n *\n * apply() 的注入门槛判定与本提取路径共用这一个函数,避免两处标准不一致\n * 造成\"装了包装层却永远提取不到数据\"的错位。\n */\nfunction settingsService(ctx: Context): SettingsService | undefined {\n const settings = (ctx as { settings?: SettingsService }).settings\n if (!settings || typeof settings.get !== 'function') return undefined\n return settings\n}\n\n// 提取期告警去重:settings 提取位于每次请求的热路径上,同一问题只告警一次,\n// 避免日志刷屏;进程生命周期内出现一次提示即足以定位配置错误。\nconst warnedMessages = new Set<string>()\n\n/** 经当前上下文的 logger 发出一次性告警。 */\nfunction warnOnce(ctx: Context, message: string): void {\n if (warnedMessages.has(message)) return\n warnedMessages.add(message)\n ctx.logger?.warn?.('[' + name + '] ' + message)\n}\n\n/**\n * 创建一条提取结果的记录器:等级 trim 后写入映射;不在已知集合时一次性告警\n * 但仍注入,让适配器的逐字匹配给出最终裁决(避免本地白名单过期后挡住上游\n * 新增的等级)。设置提取位于每次请求的热路径上,告警按消息去重防止刷屏。\n *\n * @param ctx - Cordis 上下文。\n * @param extracted - 提取结果的目标映射。\n * @returns 记录函数。\n */\nfunction createEffortRecorder(\n ctx: Context,\n extracted: Record<string, string>,\n): (routeKey: string, level: unknown) => void {\n return (routeKey, level) => {\n if (typeof level !== 'string') return\n const trimmedLevel = level.trim()\n if (trimmedLevel.length === 0) return\n if (!KNOWN_REASONING_EFFORTS.has(trimmedLevel)) {\n warnOnce(ctx, '设置中路由 ' + routeKey + ' 的推理等级 \"' + trimmedLevel + '\" 不在已知集合内,请确认目标模型支持')\n }\n extracted[routeKey] = trimmedLevel\n }\n}\n\n/**\n * 从当前 Cordis 上下文的设置服务中读取各 Provider 与模型的默认推理等级。\n *\n * 字段约定(每个来源只读单一规范字段,不做别名兜底):\n * - llm-pi-ai:provider 级 `reasoning` 是宿主原生声明字段;模型级\n * `models[].reasoning` 与 `modelOverrides.<model>.reasoning` 是本插件约定\n * 读取的扩展字段,依赖其设置解析对未声明键的透传行为。宿主在模型层另有\n * 原生字段 `reasoningEfforts`,但那是\"可选等级集合 + 线格式映射\"的能力\n * 声明而非默认值选择,语义不同,本插件不读取;若上游收紧对未声明键的\n * 解析策略,`reasoning` 这部分提取会静默失效。\n * - llm-deepseek:顶层 `reasoningEffort` 为宿主原生字段,映射到其注册的\n * provider 路由 deepseek-official(见 DEEPSEEK_OFFICIAL_PROVIDER_ID)。\n *\n * 整个函数不包 try/catch:宿主的 settings.get 对未注册命名空间返回 undefined\n * 且不会抛错,函数体已对全部外部数据做了形状防御,剩余可能抛错的只有代码\n * 缺陷,应当让它暴露,而不是吞掉后以空映射继续主流程。\n *\n * @param ctx - Cordis 上下文。\n * @returns 提取出的路由默认映射字典。\n */\nexport function extractSettingsDefaults(ctx: Context): Record<string, string> {\n const extracted: Record<string, string> = {}\n const settings = settingsService(ctx)\n if (!settings) return extracted\n const record = createEffortRecorder(ctx, extracted)\n\n // 1. 读取 llm-pi-ai 命名空间设置(涵盖所有 OpenAI-compatible、Anthropic、自定义网关等提供商)\n const piAi = settings.get('llm-pi-ai') as {\n providers?: Record<string, {\n reasoning?: unknown\n models?: Array<{ id?: unknown; reasoning?: unknown }>\n modelOverrides?: Record<string, { reasoning?: unknown }>\n }>\n } | undefined\n\n if (piAi?.providers && typeof piAi.providers === 'object') {\n for (const [providerId, providerProfile] of Object.entries(piAi.providers)) {\n if (!providerId || !providerProfile || typeof providerProfile !== 'object') continue\n\n // pi-ai 家族无法占用 deepseek-official 路由(宿主拒绝重名 adapter family),\n // 这里提取的值永远不会命中;跳过并提示一次,避免稍后被 llm-deepseek\n // 来源的同名键无声覆盖、留下难以解释的死配置。\n if (providerId === DEEPSEEK_OFFICIAL_PROVIDER_ID) {\n warnOnce(ctx, 'llm-pi-ai 中名为 ' + DEEPSEEK_OFFICIAL_PROVIDER_ID + ' 的 provider 无法注册该路由,已跳过其推理等级提取')\n continue\n }\n\n // Provider 级别默认思考等级(原生字段 reasoning)\n record(providerId + ':*', providerProfile.reasoning)\n\n // 模型列表中的思考等级(扩展字段,见函数头注释)\n if (Array.isArray(providerProfile.models)) {\n for (const m of providerProfile.models) {\n const id = m && typeof m === 'object' && typeof m.id === 'string' ? m.id.trim() : ''\n if (id.length === 0) continue\n record(providerId + ':' + id, m.reasoning)\n }\n }\n\n // modelOverrides 中的思考等级(扩展字段,见函数头注释)\n if (providerProfile.modelOverrides && typeof providerProfile.modelOverrides === 'object') {\n for (const [modelId, override] of Object.entries(providerProfile.modelOverrides)) {\n const id = modelId.trim()\n if (id.length === 0 || !override || typeof override !== 'object') continue\n record(providerId + ':' + id, override.reasoning)\n }\n }\n }\n }\n\n // 2. 读取 llm-deepseek 命名空间设置(原生字段 reasoningEffort → deepseek-official 路由)\n const deepseek = settings.get('llm-deepseek') as { reasoningEffort?: unknown } | undefined\n if (deepseek && typeof deepseek === 'object') {\n record(DEEPSEEK_OFFICIAL_PROVIDER_ID + ':*', deepseek.reasoningEffort)\n }\n\n return extracted\n}\n\n/**\n * 从本插件专用的设置命名空间读取设置页 UI 写入的路由表。\n *\n * 该命名空间由 apply() 注册、浏览器设置面板写入,是\"设置里改默认推理强度\"\n * 的存储层;每次请求实时读取,改动即时生效。形状非法(非对象/缺字段)时\n * 返回空映射——schema 校验已保证写入合法,这里的防御只覆盖外部手工编辑\n * settings 文档的场景。\n *\n * @param ctx - Cordis 上下文。\n * @returns 路由映射字典。\n */\nexport function extractUiNamespaceDefaults(ctx: Context): Record<string, string> {\n const extracted: Record<string, string> = {}\n const settings = settingsService(ctx)\n if (!settings) return extracted\n\n const section = settings.get(UI_DEFAULTS_NAMESPACE) as UiDefaultsSection | undefined\n if (!section || typeof section !== 'object') return extracted\n\n const record = createEffortRecorder(ctx, extracted)\n const defaults = section.defaults\n if (defaults && typeof defaults === 'object') {\n for (const [rawKey, level] of Object.entries(defaults)) {\n const key = rawKey.trim()\n if (key.length === 0) continue\n record(key, level)\n }\n }\n return extracted\n}\n\n// 注册重试参数:热重载期间旧代 fiber 仍持有命名空间(注册清理挂在调用方\n// fiber 上,见 SettingsProvider.register 文档),新代直接 register 会撞\n// \"already registered\";旧代卸载后键位释放,轮询即可补上。\nconst UI_NS_RETRY_INTERVAL_MS = 250\nconst UI_NS_RETRY_MAX_ATTEMPTS = 240\n\n/**\n * 在设置服务上注册本插件的可编辑命名空间(跨热重载安全)。\n *\n * 每次激活都先直接尝试注册——不缓存\"已注册\"标记,因为旧代的卸载会把命名空间\n * 一并注销,缓存的真值会在\"停用后再启用\"的场景下变成谎言。撞上\n * \"already registered\" 说明旧代尚未释放,进入短周期轮询直到键位空出;\n * 其它错误告警后放弃,不阻断主流程(patch 层配置仍然生效)。\n *\n * @param ctx - 当前插件上下文。\n * @param settings - 当前上下文的设置服务。\n */\nfunction registerUiDefaultsNamespace(ctx: Context, settings: SettingsService): void {\n if (settings.register === undefined) {\n warnOnce(ctx, '当前设置服务不支持命名空间注册,设置页将无法编辑推理等级默认值')\n return\n }\n\n const attempt = (): 'ok' | 'busy' => {\n try {\n settings.register!(UI_DEFAULTS_NAMESPACE, uiDefaultsSectionSchema, { applies: 'live' })\n return 'ok'\n } catch (error) {\n if (!String(error).includes('already registered')) {\n ctx.logger?.warn?.('[' + name + '] 注册设置命名空间失败,设置页将无法编辑推理等级默认值:' + String(error))\n return 'ok'\n }\n return 'busy'\n }\n }\n\n if (attempt() !== 'busy') return\n\n let attempts = 0\n const timer = setInterval(() => {\n attempts += 1\n if (attempts > UI_NS_RETRY_MAX_ATTEMPTS) {\n clearInterval(timer)\n warnOnce(ctx, '等待旧代释放设置命名空间超时,本次运行设置页不可用;请重启宿主或再次重载本插件')\n return\n }\n if (attempt() === 'ok') clearInterval(timer)\n }, UI_NS_RETRY_INTERVAL_MS)\n ctx.effect(() => () => {\n clearInterval(timer)\n }, name + ': stop UI namespace registration retry')\n}\n\ntype RuntimePatchState = {\n /** 创建共享包装层的 fiber 上下文;热重载复用包装层时随最新 fiber 刷新。 */\n ctx: Context\n defaults: Map<symbol, ReasoningDefaults>\n originalStream: LlmRuntime['stream']\n originalPrepareCall: LlmRuntime['prepareCall']\n originalResolveCallConfig: LlmRuntime['resolveCallConfig']\n stream: LlmRuntime['stream']\n prepareCall: LlmRuntime['prepareCall']\n resolveCallConfig: LlmRuntime['resolveCallConfig']\n}\n\n// Symbol.for 让热重载后的模块副本复用同一层状态,而不是再包一层旧包装。\nconst runtimePatchStateKey = Symbol.for('dsh-model-reasoning-defaults.runtime-patch-state')\n\n/**\n * 取最后激活的配置层。热重载期间新 fiber 覆盖旧 fiber,旧 fiber 卸载后\n * 新配置仍保持生效;最后一个 fiber 卸载时才还原原始服务方法。\n *\n * @param state - 同一 LLM 服务上的包装状态。\n * @returns 当前生效的 defaults;没有活跃层时返回 undefined。\n */\nfunction currentDefaults(state: RuntimePatchState): ReasoningDefaults | undefined {\n let current: ReasoningDefaults | undefined\n for (const defaults of state.defaults.values()) current = defaults\n return current\n}\n\n/**\n * 解析当前生效的完整 defaults 映射。三个配置层按优先级从低到高合并:\n * 设置服务自动提取项(llm-pi-ai / llm-deepseek 原生字段)→ 本插件设置\n * 命名空间(设置页 UI 写入)→ 插件自身 cordis 配置(patch 层)。\n * 同一路由键上高优先级层覆盖低优先级层。\n *\n * @param ctx - 当前共享包装层绑定的上下文(热重载后为新 fiber 的 ctx)。\n * @param state - 同一 LLM 服务上的包装状态。\n * @returns 合并后的路由映射;三层均为空时为空对象。\n */\nfunction resolveEffectiveDefaults(\n ctx: Context,\n state: RuntimePatchState,\n): ReasoningDefaults {\n const pluginDefaults = currentDefaults(state) ?? {}\n const uiStoreDefaults = extractUiNamespaceDefaults(ctx)\n const settingsDefaults = extractSettingsDefaults(ctx)\n return {\n ...settingsDefaults,\n ...uiStoreDefaults,\n ...pluginDefaults,\n }\n}\n\n/**\n * 将当前活跃层应用到配置。保留原对象是卸载后仍被外部持有的旧包装的必要语义,\n * 也避免把一个空对象伪装成真实配置来源。\n *\n * @param state - 同一 LLM 服务上的包装状态(ctx 从 state 读取,保证热重载后\n * 提取路径跟随最新 fiber)。\n * @param config - 待处理的调用配置。\n * @returns 原配置或带默认推理等级的浅复制对象。\n */\nfunction applyCurrentDefault<T extends ReasoningConfig>(\n state: RuntimePatchState,\n config: T,\n): T {\n const defaults = resolveEffectiveDefaults(state.ctx, state)\n return Object.keys(defaults).length === 0 ? config : applyDefaultReasoningEffort(config, defaults)\n}\n\n/**\n * 从当前 stream 包装上恢复跨热重载共享的状态。\n *\n * @param llm - LLM 服务实例。\n * @returns 现有包装状态;服务尚未被本插件包装时返回 undefined。\n */\nfunction existingRuntimePatchState(llm: LlmRuntime): RuntimePatchState | undefined {\n return (llm.stream as unknown as Record<PropertyKey, unknown>)[runtimePatchStateKey] as RuntimePatchState | undefined\n}\n\n/**\n * 创建覆盖三个公开入口的 LLM 服务包装。\n *\n * 手写请求在 stream 入口浅复制补齐后再进 waterfall,保证每个监听器只处理\n * 一次;loop 构建的请求由宿主打 markAgentLoopRequest 进程标识并深冻结,\n * 经 isAgentLoopRequest 判定后按原引用透传——浅复制会丢失该进程标识。\n *\n * @param ctx - 当前插件上下文。\n * @param llm - LLM 服务实例。\n * @returns 可复用的包装状态。\n */\nfunction createRuntimePatchState(ctx: Context, llm: LlmRuntime): RuntimePatchState {\n const originalStream = llm.stream\n const originalPrepareCall = llm.prepareCall\n const originalResolveCallConfig = llm.resolveCallConfig\n let state!: RuntimePatchState\n\n const stream: LlmRuntime['stream'] = (options) => {\n const patched = isAgentLoopRequest(options) ? options : applyCurrentDefault(state, options)\n return originalStream.call(llm, patched)\n }\n const prepareCall: LlmRuntime['prepareCall'] = async (config, signal) => {\n return originalPrepareCall.call(llm, applyCurrentDefault(state, config), signal)\n }\n const resolveCallConfig: LlmRuntime['resolveCallConfig'] = async (config, signal) => {\n return originalResolveCallConfig.call(llm, applyCurrentDefault(state, config), signal)\n }\n\n state = {\n ctx,\n defaults: new Map(),\n originalStream,\n originalPrepareCall,\n originalResolveCallConfig,\n stream,\n prepareCall,\n resolveCallConfig,\n }\n Object.defineProperty(stream, runtimePatchStateKey, { value: state })\n return state\n}\n\n/**\n * 安装一层可卸载的 LLM 服务包装。\n *\n * 每个插件 fiber 只持有自己的配置层。这样新 fiber 先装配、旧 fiber 后卸载时,\n * 不会把服务方法恢复为已经失效的旧包装。\n *\n * @param ctx - 当前插件上下文。\n * @param llm - LLM 服务实例。\n * @param defaults - 默认推理等级映射。\n */\nfunction installRuntimePatches(\n ctx: Context,\n llm: LlmRuntime,\n defaults: ReasoningDefaults,\n): RuntimePatchState {\n const state = existingRuntimePatchState(llm) ?? createRuntimePatchState(ctx, llm)\n // 复用跨热重载共享的包装层时刷新 ctx:设置服务本身是全局单例,但\n // ctx.logger 等 fiber 级能力应跟随最新装配层,保证告警走当前 fiber 的日志。\n state.ctx = ctx\n if (llm.stream !== state.stream) llm.stream = state.stream\n if (llm.prepareCall !== state.prepareCall) llm.prepareCall = state.prepareCall\n if (llm.resolveCallConfig !== state.resolveCallConfig) llm.resolveCallConfig = state.resolveCallConfig\n\n const token = Symbol(name)\n state.defaults.set(token, defaults)\n ctx.effect(() => () => {\n state.defaults.delete(token)\n if (state.defaults.size !== 0) return\n if (llm.stream === state.stream) llm.stream = state.originalStream\n if (llm.prepareCall === state.prepareCall) llm.prepareCall = state.originalPrepareCall\n if (llm.resolveCallConfig === state.resolveCallConfig) llm.resolveCallConfig = state.originalResolveCallConfig\n }, name + ': restore LLM method wrappers')\n return state\n}\n\n/**\n * 安装默认推理等级注入逻辑。\n *\n * @param ctx - 当前插件上下文。\n * @param config - 插件配置。\n */\nexport function apply(ctx: Context, config: Config = {}): void {\n // 默认参数仅为编程直调便利;cordis 配置路径总先经 Config 校验并把可选集合补全。\n const defaults = normalizeConfig(config)\n\n // 结构化条目若因缺 id / 缺等级被归一化忽略,在此一次性上报,\n // 避免\"配了却不生效\"的静默降级无从排查。\n for (const issue of collectConfigIssues(config)) {\n ctx.logger?.warn?.('[' + name + '] ' + issue)\n }\n\n const settings = settingsService(ctx)\n\n // 注册设置页可编辑命名空间;失败不阻断主流程(patch 层配置仍然生效)。\n if (settings) registerUiDefaultsNamespace(ctx, settings)\n\n if (Object.keys(defaults).length === 0 && !settings) {\n ctx.logger?.info?.('[' + name + '] 未配置推理等级默认值且无设置服务,跳过注入')\n return\n }\n\n const llm = ctx.llm\n\n // 先建立共享配置层;agent/request 和三个公开 LLM 入口都从这层读取最新 defaults。\n const state = installRuntimePatches(ctx, llm, defaults)\n\n // agent-loop 在这里仍允许替换配置;此阶段操作的是调用配置种子,loop 随后\n // 才据此构建并深冻结真正的请求对象,loop 识别由 stream 入口的\n // isAgentLoopRequest 完成(见 createRuntimePatchState)。\n registerAgentRequestListener(ctx, async function (_payload, next) {\n return applyCurrentDefault(state, await next())\n })\n\n // 激活日志保留生效路由清单,排障时可直接对照请求的 provider/model。\n ctx.logger?.info?.('[' + name + '] 已激活:routes=' + Object.keys(defaults).join(', '))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * dsh-model-reasoning-defaults\n *\n * 为每个模型配置默认推理等级。请求已显式指定 reasoningEffort 时保持原值;\n * 其余按 provider/model 路由查找默认值并转换为 DSH 的品牌类型。\n *\n * agent-loop 请求在 agent/request waterfall 中注入(此后到达 llm/stream 时\n * 已深冻结);手写调用在 llm.stream 入口复制后再进入 waterfall,调用配置\n * 方法走同一共享包装层。\n *\n * @module dsh-model-reasoning-defaults\n */\nimport type { Context, EventOptions } from '@deepseek-ai/cordis'\nimport { ReasoningEffortId } from '@deepseek-ai/dsh-llm'\nimport type {\n GenerateOptions,\n LlmCallConfig,\n LlmRuntime,\n} from '@deepseek-ai/dsh-llm'\nimport z from '@deepseek-ai/schemastery'\n\n/** 插件名(同时作为 Cordis 插件 id)。 */\nexport const name = 'dsh-model-reasoning-defaults'\n\n/** 声明依赖的服务:LLM 服务就绪后装配;注册/读取设置命名空间需要设置服务。 */\nexport const inject = ['llm', 'settings'] as const\n\n/** 单个模型的推理等级配置。 */\nexport interface ModelConfig {\n /** 模型 ID(数组形式必填;字典形式以键名为准,可省略)。 */\n id?: string\n /** 默认推理等级(如 off | minimal | low | medium | high | xhigh | max)。 */\n reasoningEffort?: string\n}\n\n/** 单个模型配置输入:既可以是等级字符串简写,也可以是配置对象。 */\nexport type ModelConfigInput = string | ModelConfig\n\n/** 单个 Provider 的推理等级配置。 */\nexport interface ProviderConfig {\n /** 该 Provider 下所有模型的默认推理等级。 */\n reasoningEffort?: string\n /** 该 Provider 下各模型的推理等级配置(字典或对象列表)。 */\n models?: Record<string, ModelConfigInput> | ModelConfigInput[]\n}\n\n/** Provider 配置输入:既可以是等级字符串简写(作用于该 Provider 所有模型),也可以是配置对象。 */\nexport type ProviderConfigInput = string | ProviderConfig\n\n/**\n * 插件配置:支持按 Provider 与模型嵌入式层级配置,也可使用扁平路由字典 defaults。\n * 两种形式是同等的一等配置入口(本仓库无历史版本,不存在\"新旧兼容\"语义),\n * 归一化到同一张路由表后统一参与六级匹配。\n *\n * 合并后的匹配优先级从高到低为:\n * - \"provider:model\":精确匹配(来自 providers[p].models[m] 或 defaults[\"provider:model\"])\n * - \"provider:*\":该 provider 下所有模型(来自 providers[p].reasoningEffort 或 defaults[\"provider:*\"])\n * - \"provider/*\":斜杠写法(来自 defaults[\"provider/*\"])\n * - \"provider\":裸 provider 写法(来自 defaults[\"provider\"])\n * - \"*:model\":跨 provider 的同名模型(来自 top-level models[m] 或 defaults[\"*:model\"])\n * - \"*\":全局匹配(来自 top-level reasoningEffort 或 defaults[\"*\"])\n */\nexport interface Config {\n /** 扁平路由字典:键为上表六级路由键;与结构化配置产生同键时覆盖结构化项。 */\n defaults?: Record<string, string>\n /** 按 Provider 组织的嵌入式配置。 */\n providers?: Record<string, ProviderConfigInput>\n /** 跨 Provider 适用的模型默认配置(等价于 \"*:model\")。 */\n models?: Record<string, ModelConfigInput> | ModelConfigInput[]\n /** 全局默认推理等级(等价于 \"*\")。 */\n reasoningEffort?: string\n}\n\nconst nonBlankPattern = /^\\S+$/\n\n// 已知推理等级集合:llm-pi-ai 的 ModelThinkingLevel(off/minimal/low/medium/high/\n// xhigh/max)与 llm-deepseek(off/low/high/max,为其子集)的并集。仅用于激活期与\n// 提取期的告警提示而不阻断注入——最终合法性由适配器逐字匹配裁决,本地白名单\n// 收窄反而会在上游新增等级时挡住合法配置。\nconst KNOWN_REASONING_EFFORTS: ReadonlySet<string> = new Set([\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n])\n\n// llm-deepseek 注册的唯一 provider 路由名(宿主 packages/llm/llm-deepseek 的\n// PROVIDER 常量);llm-pi-ai 家族无法注册同名路由,宿主会拒绝重名 adapter family。\nconst DEEPSEEK_OFFICIAL_PROVIDER_ID = 'deepseek-official'\n\nconst modelConfigSchema = z.object({\n id: z.string().pattern(nonBlankPattern),\n reasoningEffort: z.string().pattern(nonBlankPattern),\n})\n\nconst modelEntrySchema = z.union([\n z.string().pattern(nonBlankPattern),\n modelConfigSchema,\n])\n\nconst modelsFieldSchema = z.union([\n z.dict(modelEntrySchema, z.string().pattern(nonBlankPattern)),\n z.array(modelEntrySchema),\n])\n\nconst providerConfigSchema = z.object({\n reasoningEffort: z.string().pattern(nonBlankPattern),\n models: modelsFieldSchema,\n})\n\nconst providerEntrySchema = z.union([\n z.string().pattern(nonBlankPattern),\n providerConfigSchema,\n])\n\n/** Cordis 配置模式(显式注解接口类型,满足声明产物的可命名要求)。 */\nexport const Config: z<Config> = z.object({\n // 路由键与等级值都要求非空白:空白键永远不会被 candidateKeys 命中,空白值\n // 会被适配器逐字匹配拒绝,都在配置阶段直接报错暴露最省排查成本。\n // .default({}) 与 schemastery 对 dict 的内建空对象默认重复,保留它是为了让\n // 编程直调路径(绕过 cordis resolveConfig)也能拿到确定的形状。\n defaults: z.dict(\n z.string().pattern(nonBlankPattern),\n z.string().pattern(nonBlankPattern),\n ).default({}),\n providers: z.dict(\n providerEntrySchema,\n z.string().pattern(nonBlankPattern),\n ).default({}),\n models: modelsFieldSchema,\n reasoningEffort: z.string().pattern(nonBlankPattern),\n})\n\n/**\n * 从字符串简写或配置对象中提取非空推理等级。\n *\n * 只认单一规范字段 reasoningEffort,不做多字段兜底链:同义多源读取会在字段\n * 冲突时按书写顺序静默裁决(例如 reasoningEffort 与 effort 同时配置时无声地\n * 二选一),掩盖配置错误;确需别名应由用户层显式映射后再传入。\n *\n * @param val - 等级字符串,或 ModelConfig/ProviderConfig 形状的对象。\n * @returns trim 后非空的等级字符串;无法提取时返回 undefined。\n */\nfunction extractEffort(val: unknown): string | undefined {\n const candidate = typeof val === 'string'\n ? val\n : (typeof val === 'object' && val !== null)\n ? (val as { reasoningEffort?: unknown }).reasoningEffort\n : undefined\n if (typeof candidate !== 'string') return undefined\n const trimmed = candidate.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * 将结构化 Config 归一化为统一的扁平路由字典。\n *\n * @param config - 插件配置对象。\n * @returns 归一化后的键值映射;键与值均已去除首尾空白。\n */\nexport function normalizeConfig(config: Config): Record<string, string> {\n const result: Record<string, string> = {}\n\n // 1. 全局默认 (对应 \"*\")\n const globalEffort = extractEffort(config.reasoningEffort)\n if (globalEffort) {\n result['*'] = globalEffort\n }\n\n // 2. 顶层跨 Provider 模型默认 (对应 \"*:model\")\n if (config.models) {\n if (Array.isArray(config.models)) {\n for (const item of config.models) {\n // 数组形式必须携带非空白 id 才能生成路由键;缺失项由 apply() 的\n // 激活期诊断告警上报,这里只负责安静地跳过无效形状。\n const id = typeof item === 'object' && item !== null && typeof item.id === 'string' ? item.id.trim() : ''\n if (id.length === 0) continue\n const effort = extractEffort(item)\n if (effort) result['*:' + id] = effort\n }\n } else if (typeof config.models === 'object') {\n for (const [rawModelId, modelVal] of Object.entries(config.models)) {\n const modelId = rawModelId.trim()\n if (modelId.length === 0) continue\n const effort = extractEffort(modelVal)\n if (effort) result['*:' + modelId] = effort\n }\n }\n }\n\n // 3. Provider 层级配置\n if (config.providers && typeof config.providers === 'object') {\n for (const [rawProviderId, providerVal] of Object.entries(config.providers)) {\n const providerId = rawProviderId.trim()\n if (providerId.length === 0) continue\n\n // 3.1 Provider 级默认 (对应 \"provider:*\")\n const providerEffort = extractEffort(providerVal)\n if (providerEffort) {\n result[providerId + ':*'] = providerEffort\n }\n\n // 3.2 Provider 内的具体模型 (对应 \"provider:model\")\n if (typeof providerVal === 'object' && providerVal !== null && providerVal.models) {\n const models = providerVal.models\n if (Array.isArray(models)) {\n for (const item of models) {\n const id = typeof item === 'object' && item !== null && typeof item.id === 'string' ? item.id.trim() : ''\n if (id.length === 0) continue\n const effort = extractEffort(item)\n if (effort) result[providerId + ':' + id] = effort\n }\n } else if (typeof models === 'object') {\n for (const [rawModelId, modelVal] of Object.entries(models)) {\n const modelId = rawModelId.trim()\n if (modelId.length === 0) continue\n const effort = extractEffort(modelVal)\n if (effort) result[providerId + ':' + modelId] = effort\n }\n }\n }\n }\n }\n\n // 4. 扁平 defaults 字典:与结构化配置同等的一等入口,写在最后使其在与结构化\n // 配置同键冲突时胜出(显式路由键的表达意图更具体)。键值均 trim 后判断,\n // 与结构化路径(id 空则跳过)保持一致:空白键与空白值一样永不命中任何\n // 请求路由(cordis 路径由 schema 拒绝,此处覆盖绕开 schema 的直调路径)。\n if (config.defaults && typeof config.defaults === 'object') {\n for (const [rawKey, value] of Object.entries(config.defaults)) {\n const key = rawKey.trim()\n const trimmedValue = value.trim()\n if (key.length === 0 || trimmedValue.length === 0) continue\n result[key] = trimmedValue\n }\n }\n\n return result\n}\n\ntype ReasoningConfig = Pick<LlmCallConfig, 'provider' | 'model' | 'reasoningEffort'>\n\n/**\n * 静态体检插件配置,返回人类可读的问题清单(纯函数,不修改任何数据)。\n *\n * schemastery 的对象属性全部可选且对未知键透传,\"拼错字段\"\"模型项缺 id\"\n * 这类错误能通过校验却在 normalizeConfig 中被静默忽略;此清单在插件激活时\n * 一次性告警,把这类静默降级变成可见信息。等级值不在已知集合也在此提示,\n * 但仍会注入——最终是否可用交给适配器逐字匹配裁决。\n *\n * @param config - 用户原始配置(未经归一化)。\n * @returns 问题描述列表;无问题时为空数组。\n */\nfunction collectConfigIssues(config: Config): string[] {\n const issues: string[] = []\n const knownLevels = [...KNOWN_REASONING_EFFORTS].join('/')\n\n const checkLevel = (route: string, level: string | undefined): void => {\n if (level !== undefined && !KNOWN_REASONING_EFFORTS.has(level)) {\n issues.push('路由 ' + route + ' 的推理等级 \"' + level + '\" 不在已知集合 (' + knownLevels + ') 内,请确认目标模型支持')\n }\n }\n\n /**\n * 检查单个模型条目:字符串简写直接校验等级;\n * 对象形式要求携带有效等级,数组元素还要求有效 id。\n */\n const checkModelEntry = (route: string, entry: ModelConfigInput, requireId: boolean): void => {\n if (typeof entry === 'string') {\n // 数组位置没有键名可用作 id,字符串简写永远生成不了路由键;字典位置的\n // 字符串值以键名为 id(requireId=false),不受此限制。\n if (requireId) {\n issues.push('数组形式模型条目缺少有效 id(字符串简写无键名可用),已被忽略:' + JSON.stringify(entry))\n return\n }\n checkLevel(route, extractEffort(entry))\n return\n }\n const id = typeof entry.id === 'string' ? entry.id.trim() : ''\n if (requireId && id.length === 0) {\n issues.push('模型条目缺少有效 id,已被忽略:' + JSON.stringify(entry))\n return\n }\n const effort = extractEffort(entry)\n if (effort === undefined) {\n issues.push('模型条目未包含 reasoningEffort,已被忽略:' + JSON.stringify(entry))\n return\n }\n checkLevel(route, effort)\n }\n\n // 全局等级\n checkLevel('*', extractEffort(config.reasoningEffort))\n\n // 顶层跨 Provider 模型\n if (config.models) {\n if (Array.isArray(config.models)) {\n config.models.forEach((entry, index) => {\n checkModelEntry('*:<models[' + index + ']>', entry, true)\n })\n } else if (typeof config.models === 'object') {\n for (const [modelId, entry] of Object.entries(config.models)) {\n checkModelEntry('*:' + modelId, entry, false)\n }\n }\n }\n\n // Provider 层级\n if (config.providers && typeof config.providers === 'object') {\n for (const [providerId, providerVal] of Object.entries(config.providers)) {\n if (typeof providerVal === 'string') {\n checkLevel(providerId + ':*', extractEffort(providerVal))\n continue\n }\n const hasModels = providerVal.models !== undefined\n const providerEffort = extractEffort(providerVal)\n if (!hasModels && providerEffort === undefined) {\n issues.push('provider ' + providerId + ' 未包含 reasoningEffort 或 models,整条配置已被忽略')\n continue\n }\n checkLevel(providerId + ':*', providerEffort)\n if (hasModels) {\n const models = providerVal.models!\n if (Array.isArray(models)) {\n models.forEach((entry, index) => {\n checkModelEntry(providerId + ':<models[' + index + ']>', entry, true)\n })\n } else if (typeof models === 'object') {\n for (const [modelId, entry] of Object.entries(models)) {\n checkModelEntry(providerId + ':' + modelId, entry, false)\n }\n }\n }\n }\n }\n\n // 扁平 defaults 字典:schema 在 cordis 路径直接拒绝空白键值,但编程直调\n // 会绕过校验;这里的空键检查与 normalizeConfig 的跳过逻辑保持一致,把\n // 静默降级变成可见告警。\n if (config.defaults && typeof config.defaults === 'object') {\n for (const [rawKey, value] of Object.entries(config.defaults)) {\n const key = rawKey.trim()\n if (key.length === 0) {\n issues.push('扁平路由键为空白,已被忽略:' + JSON.stringify(rawKey))\n continue\n }\n checkLevel(key, extractEffort(value))\n }\n }\n\n return issues\n}\n\n// agent/request 的 loop 侧 emit 实参是 { turn, step, signal };core/agent 的派发器\n// 会把路由主体 agent(Scoped<Agent>)融合进 payload 并同时作为监听器 this,因此\n// 实收 payload 含 agent 字段。本监听器不读取任何 payload 字段,此处仅按本插件\n// 用到的形状窄化 ctx.on 的签名。\ntype AgentRequestPayload = {\n turn: number\n step: number\n signal: AbortSignal\n}\n\ntype AgentRequestListener = (\n this: object,\n payload: AgentRequestPayload,\n next: () => Promise<LlmCallConfig>,\n) => Promise<LlmCallConfig>\n\ntype AgentRequestRegistrar = (\n name: 'agent/request',\n listener: AgentRequestListener,\n options?: boolean | EventOptions,\n) => () => boolean\n\n/**\n * 注册全局 agent/request waterfall。\n *\n * cordis 的 ctx.on 内部会把监听器注册为当前 fiber 上的 effect(见 events\n * 服务的 register()),无论 global 标志如何都会在 fiber 卸载时自动清理;\n * global 只影响事件派发时的上下文过滤。因此无需再包一层 ctx.effect。\n *\n * 该事件的运行时接收者是 Scoped<Agent>,Cordis 根 Context 的事件泛型表不会\n * 暴露它,因此在最小边界内窄化签名(仅约束本插件用到的事件名与参数)。\n *\n * @param ctx - 当前插件上下文。\n * @param listener - agent/request waterfall 监听器。\n */\nfunction registerAgentRequestListener(ctx: Context, listener: AgentRequestListener): void {\n // 窄化仅覆盖本插件实际使用的事件名与参数;内部转调仍通过 ctx.on 的真实实现。\n const register = ctx.on.bind(ctx) as AgentRequestRegistrar\n register('agent/request', listener, { global: true })\n}\n\n/**\n * 生成一个路由的候选配置键。\n *\n * 兼容键保留在明确的优先级表中,避免不同调用入口各自实现一套匹配规则。\n *\n * @param provider - 请求使用的 provider。\n * @param model - 请求使用的 model。\n * @returns 按匹配优先级排列的候选键。\n */\nfunction candidateKeys(provider: string, model: string): readonly string[] {\n return [\n provider + ':' + model,\n provider + ':*',\n provider + '/*',\n provider,\n '*:' + model,\n '*',\n ]\n}\n\n/**\n * 查找一个路由对应的默认推理等级。\n *\n * @param defaults - 归一化后的扁平路由映射。\n * @param provider - 请求使用的 provider。\n * @param model - 请求使用的 model。\n * @returns 找到的非空推理等级,未找到时返回 undefined。\n */\nfunction lookupEffort(\n defaults: Readonly<Record<string, string>>,\n provider: string,\n model: string,\n): string | undefined {\n for (const key of candidateKeys(provider, model)) {\n const effort = defaults[key]\n if (typeof effort === 'string' && effort.length > 0) return effort\n }\n return undefined\n}\n\n/**\n * 在不修改原对象的前提下应用默认推理等级。\n *\n * 该函数同时用于 GenerateOptions 和 LlmCallConfig,因此调用方可以保留\n * 原请求对象中的 messages、signal 等其它字段,并安全处理深冻结输入。\n *\n * 第二参数只接受归一化后的扁平路由表;结构化 Config 必须先经 normalizeConfig。\n * 曾尝试用对象形状嗅探让本函数同时接纳两种入参,但路由表允许任意非空白键,\n * 含 \"models\"/\"providers\" 等键的路由表会被误判成结构化配置(整表归一化为空、\n * 注入全部失效),反之仅含顶层 reasoningEffort 的最小结构化配置又会漏判——\n * 两类误判都是静默失效,因此彻底拆掉形状嗅探。\n *\n * @param config - 待补齐的 LLM 请求或调用配置。\n * @param defaults - normalizeConfig 产出的扁平路由表。\n * @returns 原配置或带有 reasoningEffort 的浅复制对象。\n */\nexport function applyDefaultReasoningEffort<T extends ReasoningConfig>(\n config: T,\n defaults: Readonly<Record<string, string>>,\n): T {\n if (config.reasoningEffort !== undefined || !config.provider || !config.model) return config\n const effort = lookupEffort(defaults, config.provider, config.model)\n if (effort === undefined) return config\n return { ...config, reasoningEffort: ReasoningEffortId(effort) }\n}\n\ntype ReasoningDefaults = Readonly<Record<string, string>>\n\n/**\n * 设置页 UI 专用命名空间。该命名空间由本插件注册为可写设置项,浏览器设置\n * 面板通过宿主统一的 settings API 编辑它;生效优先级位于自动提取之上、\n * cordis 插件配置之下(见 resolveEffectiveDefaults)。\n */\nexport const UI_DEFAULTS_NAMESPACE = 'model-reasoning-defaults'\n\n/** 设置命名空间的用户层数据形状:与 Config.defaults 同构的扁平路由字典。 */\nexport interface UiDefaultsSection {\n /** 扁平路由字典:键为六级路由键,值为推理等级。 */\n defaults?: Record<string, string>\n}\n\n// 命名空间 schema 与 Config.defaults 复用同一约束(非空白键值),保证两条\n// 配置入口的合法性标准完全一致。\nconst uiDefaultsSectionSchema = z.object({\n defaults: z.dict(\n z.string().pattern(nonBlankPattern),\n z.string().pattern(nonBlankPattern),\n ).default({}),\n})\n\ntype SettingsService = {\n get: (ns: string) => unknown\n /**\n * 注册可写命名空间(真实签名见 @deepseek-ai/dsh-settings 的 SettingsProvider.register;\n * 此处只声明本插件用到的参数面)。可选:设置服务存在才可注册。\n */\n register?: (\n ns: string,\n schema: z<UiDefaultsSection>,\n options?: { applies?: 'live' | 'restart' },\n ) => unknown\n}\n\n/**\n * 取当前上下文上的设置服务;不存在或 get 不可调用时返回 undefined。\n *\n * apply() 的注入门槛判定与本提取路径共用这一个函数,避免两处标准不一致\n * 造成\"装了包装层却永远提取不到数据\"的错位。\n */\nfunction settingsService(ctx: Context): SettingsService | undefined {\n const settings = (ctx as { settings?: SettingsService }).settings\n if (!settings || typeof settings.get !== 'function') return undefined\n return settings\n}\n\n// 提取期告警去重:settings 提取位于每次请求的热路径上,同一问题只告警一次,\n// 避免日志刷屏;进程生命周期内出现一次提示即足以定位配置错误。\nconst warnedMessages = new Set<string>()\n\n/** 经当前上下文的 logger 发出一次性告警。 */\nfunction warnOnce(ctx: Context, message: string): void {\n if (warnedMessages.has(message)) return\n warnedMessages.add(message)\n ctx.logger?.warn?.('[' + name + '] ' + message)\n}\n\n/**\n * 创建一条提取结果的记录器:等级 trim 后写入映射;不在已知集合时一次性告警\n * 但仍注入,让适配器的逐字匹配给出最终裁决(避免本地白名单过期后挡住上游\n * 新增的等级)。设置提取位于每次请求的热路径上,告警按消息去重防止刷屏。\n *\n * @param ctx - Cordis 上下文。\n * @param extracted - 提取结果的目标映射。\n * @returns 记录函数。\n */\nfunction createEffortRecorder(\n ctx: Context,\n extracted: Record<string, string>,\n): (routeKey: string, level: unknown) => void {\n return (routeKey, level) => {\n if (typeof level !== 'string') return\n const trimmedLevel = level.trim()\n if (trimmedLevel.length === 0) return\n if (!KNOWN_REASONING_EFFORTS.has(trimmedLevel)) {\n warnOnce(ctx, '设置中路由 ' + routeKey + ' 的推理等级 \"' + trimmedLevel + '\" 不在已知集合内,请确认目标模型支持')\n }\n extracted[routeKey] = trimmedLevel\n }\n}\n\n/**\n * 从当前 Cordis 上下文的设置服务中读取各 Provider 与模型的默认推理等级。\n *\n * 字段约定(每个来源只读单一规范字段,不做别名兜底):\n * - llm-pi-ai:provider 级 `reasoning` 是宿主原生声明字段;模型级\n * `models[].reasoning` 与 `modelOverrides.<model>.reasoning` 是本插件约定\n * 读取的扩展字段,依赖其设置解析对未声明键的透传行为。宿主在模型层另有\n * 原生字段 `reasoningEfforts`,但那是\"可选等级集合 + 线格式映射\"的能力\n * 声明而非默认值选择,语义不同,本插件不读取;若上游收紧对未声明键的\n * 解析策略,`reasoning` 这部分提取会静默失效。\n * - llm-deepseek:顶层 `reasoningEffort` 为宿主原生字段,映射到其注册的\n * provider 路由 deepseek-official(见 DEEPSEEK_OFFICIAL_PROVIDER_ID)。\n *\n * 整个函数不包 try/catch:宿主的 settings.get 对未注册命名空间返回 undefined\n * 且不会抛错,函数体已对全部外部数据做了形状防御,剩余可能抛错的只有代码\n * 缺陷,应当让它暴露,而不是吞掉后以空映射继续主流程。\n *\n * @param ctx - Cordis 上下文。\n * @returns 提取出的路由默认映射字典。\n */\nexport function extractSettingsDefaults(ctx: Context): Record<string, string> {\n const extracted: Record<string, string> = {}\n const settings = settingsService(ctx)\n if (!settings) return extracted\n const record = createEffortRecorder(ctx, extracted)\n\n // 1. 读取 llm-pi-ai 命名空间设置(涵盖所有 OpenAI-compatible、Anthropic、自定义网关等提供商)\n const piAi = settings.get('llm-pi-ai') as {\n providers?: Record<string, {\n reasoning?: unknown\n models?: Array<{ id?: unknown; reasoning?: unknown }>\n modelOverrides?: Record<string, { reasoning?: unknown }>\n }>\n } | undefined\n\n if (piAi?.providers && typeof piAi.providers === 'object') {\n for (const [providerId, providerProfile] of Object.entries(piAi.providers)) {\n if (!providerId || !providerProfile || typeof providerProfile !== 'object') continue\n\n // pi-ai 家族无法占用 deepseek-official 路由(宿主拒绝重名 adapter family),\n // 这里提取的值永远不会命中;跳过并提示一次,避免稍后被 llm-deepseek\n // 来源的同名键无声覆盖、留下难以解释的死配置。\n if (providerId === DEEPSEEK_OFFICIAL_PROVIDER_ID) {\n warnOnce(ctx, 'llm-pi-ai 中名为 ' + DEEPSEEK_OFFICIAL_PROVIDER_ID + ' 的 provider 无法注册该路由,已跳过其推理等级提取')\n continue\n }\n\n // Provider 级别默认思考等级(原生字段 reasoning)\n record(providerId + ':*', providerProfile.reasoning)\n\n // 模型列表中的思考等级(扩展字段,见函数头注释)\n if (Array.isArray(providerProfile.models)) {\n for (const m of providerProfile.models) {\n const id = m && typeof m === 'object' && typeof m.id === 'string' ? m.id.trim() : ''\n if (id.length === 0) continue\n record(providerId + ':' + id, m.reasoning)\n }\n }\n\n // modelOverrides 中的思考等级(扩展字段,见函数头注释)\n if (providerProfile.modelOverrides && typeof providerProfile.modelOverrides === 'object') {\n for (const [modelId, override] of Object.entries(providerProfile.modelOverrides)) {\n const id = modelId.trim()\n if (id.length === 0 || !override || typeof override !== 'object') continue\n record(providerId + ':' + id, override.reasoning)\n }\n }\n }\n }\n\n // 2. 读取 llm-deepseek 命名空间设置(原生字段 reasoningEffort → deepseek-official 路由)\n const deepseek = settings.get('llm-deepseek') as { reasoningEffort?: unknown } | undefined\n if (deepseek && typeof deepseek === 'object') {\n record(DEEPSEEK_OFFICIAL_PROVIDER_ID + ':*', deepseek.reasoningEffort)\n }\n\n return extracted\n}\n\n/**\n * 从本插件专用的设置命名空间读取设置页 UI 写入的路由表。\n *\n * 该命名空间由 apply() 注册、浏览器设置面板写入,是\"设置里改默认推理强度\"\n * 的存储层;每次请求实时读取,改动即时生效。形状非法(非对象/缺字段)时\n * 返回空映射——schema 校验已保证写入合法,这里的防御只覆盖外部手工编辑\n * settings 文档的场景。\n *\n * @param ctx - Cordis 上下文。\n * @returns 路由映射字典。\n */\nexport function extractUiNamespaceDefaults(ctx: Context): Record<string, string> {\n const extracted: Record<string, string> = {}\n const settings = settingsService(ctx)\n if (!settings) return extracted\n\n const section = settings.get(UI_DEFAULTS_NAMESPACE) as UiDefaultsSection | undefined\n if (!section || typeof section !== 'object') return extracted\n\n const record = createEffortRecorder(ctx, extracted)\n const defaults = section.defaults\n if (defaults && typeof defaults === 'object') {\n for (const [rawKey, level] of Object.entries(defaults)) {\n const key = rawKey.trim()\n if (key.length === 0) continue\n record(key, level)\n }\n }\n return extracted\n}\n\n// 注册重试参数:热重载期间旧代 fiber 仍持有命名空间(注册清理挂在调用方\n// fiber 上,见 SettingsProvider.register 文档),新代直接 register 会撞\n// \"already registered\";旧代卸载后键位释放,轮询即可补上。\nconst UI_NS_RETRY_INTERVAL_MS = 250\nconst UI_NS_RETRY_MAX_ATTEMPTS = 240\n\n/**\n * 在设置服务上注册本插件的可编辑命名空间(跨热重载安全)。\n *\n * 每次激活都先直接尝试注册——不缓存\"已注册\"标记,因为旧代的卸载会把命名空间\n * 一并注销,缓存的真值会在\"停用后再启用\"的场景下变成谎言。撞上\n * \"already registered\" 说明旧代尚未释放,进入短周期轮询直到键位空出;\n * 其它错误告警后放弃,不阻断主流程(patch 层配置仍然生效)。\n *\n * @param ctx - 当前插件上下文。\n * @param settings - 当前上下文的设置服务。\n */\nfunction registerUiDefaultsNamespace(ctx: Context, settings: SettingsService): void {\n if (settings.register === undefined) {\n warnOnce(ctx, '当前设置服务不支持命名空间注册,设置页将无法编辑推理等级默认值')\n return\n }\n\n const attempt = (): 'ok' | 'busy' => {\n try {\n settings.register!(UI_DEFAULTS_NAMESPACE, uiDefaultsSectionSchema, { applies: 'live' })\n return 'ok'\n } catch (error) {\n if (!String(error).includes('already registered')) {\n ctx.logger?.warn?.('[' + name + '] 注册设置命名空间失败,设置页将无法编辑推理等级默认值:' + String(error))\n return 'ok'\n }\n return 'busy'\n }\n }\n\n if (attempt() !== 'busy') return\n\n let attempts = 0\n const timer = setInterval(() => {\n attempts += 1\n if (attempts > UI_NS_RETRY_MAX_ATTEMPTS) {\n clearInterval(timer)\n warnOnce(ctx, '等待旧代释放设置命名空间超时,本次运行设置页不可用;请重启宿主或再次重载本插件')\n return\n }\n if (attempt() === 'ok') clearInterval(timer)\n }, UI_NS_RETRY_INTERVAL_MS)\n ctx.effect(() => () => {\n clearInterval(timer)\n }, name + ': stop UI namespace registration retry')\n}\n\ntype RuntimePatchState = {\n /** 创建共享包装层的 fiber 上下文;热重载复用包装层时随最新 fiber 刷新。 */\n ctx: Context\n defaults: Map<symbol, ReasoningDefaults>\n originalStream: LlmRuntime['stream']\n originalPrepareCall: LlmRuntime['prepareCall']\n originalResolveCallConfig: LlmRuntime['resolveCallConfig']\n stream: LlmRuntime['stream']\n prepareCall: LlmRuntime['prepareCall']\n resolveCallConfig: LlmRuntime['resolveCallConfig']\n}\n\n// Symbol.for 让热重载后的模块副本复用同一层状态,而不是再包一层旧包装。\nconst runtimePatchStateKey = Symbol.for('dsh-model-reasoning-defaults.runtime-patch-state')\n\n/**\n * 取最后激活的配置层。热重载期间新 fiber 覆盖旧 fiber,旧 fiber 卸载后\n * 新配置仍保持生效;最后一个 fiber 卸载时才还原原始服务方法。\n *\n * @param state - 同一 LLM 服务上的包装状态。\n * @returns 当前生效的 defaults;没有活跃层时返回 undefined。\n */\nfunction currentDefaults(state: RuntimePatchState): ReasoningDefaults | undefined {\n let current: ReasoningDefaults | undefined\n for (const defaults of state.defaults.values()) current = defaults\n return current\n}\n\n/**\n * 解析当前生效的完整 defaults 映射。三个配置层按优先级从低到高合并:\n * 设置服务自动提取项(llm-pi-ai / llm-deepseek 原生字段)→ 本插件设置\n * 命名空间(设置页 UI 写入)→ 插件自身 cordis 配置(patch 层)。\n * 同一路由键上高优先级层覆盖低优先级层。\n *\n * @param ctx - 当前共享包装层绑定的上下文(热重载后为新 fiber 的 ctx)。\n * @param state - 同一 LLM 服务上的包装状态。\n * @returns 合并后的路由映射;三层均为空时为空对象。\n */\nfunction resolveEffectiveDefaults(\n ctx: Context,\n state: RuntimePatchState,\n): ReasoningDefaults {\n const pluginDefaults = currentDefaults(state) ?? {}\n const uiStoreDefaults = extractUiNamespaceDefaults(ctx)\n const settingsDefaults = extractSettingsDefaults(ctx)\n return {\n ...settingsDefaults,\n ...uiStoreDefaults,\n ...pluginDefaults,\n }\n}\n\n/**\n * 将当前活跃层应用到配置。保留原对象是卸载后仍被外部持有的旧包装的必要语义,\n * 也避免把一个空对象伪装成真实配置来源。\n *\n * @param state - 同一 LLM 服务上的包装状态(ctx 从 state 读取,保证热重载后\n * 提取路径跟随最新 fiber)。\n * @param config - 待处理的调用配置。\n * @returns 原配置或带默认推理等级的浅复制对象。\n */\nfunction applyCurrentDefault<T extends ReasoningConfig>(\n state: RuntimePatchState,\n config: T,\n): T {\n const defaults = resolveEffectiveDefaults(state.ctx, state)\n return Object.keys(defaults).length === 0 ? config : applyDefaultReasoningEffort(config, defaults)\n}\n\n/**\n * 判断请求是否已经进入 Agent Loop 的不可变边界。\n *\n * `isAgentLoopRequest()` 背后是 `@deepseek-ai/dsh-llm` 模块副本私有的\n * `WeakSet`。宿主和插件若解析到不同副本,宿主写入的标识不会被插件读取,\n * 入口包装就会错误地复制 Loop 请求。这里改用 DSH 公开的请求契约:Loop\n * 在交给 LLM 前会把完整请求顶层冻结,并写入用于路由的 `sessionId`;冻结的\n * 手写辅助请求(例如 compaction)没有 `sessionId`,仍应允许插件复制并补齐默认值。\n *\n * @param options - 进入 `llm.stream` 的完整请求。\n * @returns 是否应按原引用透传该请求。\n */\nfunction isAgentLoopRequestBoundary(options: GenerateOptions): boolean {\n return options.sessionId !== undefined && Object.isFrozen(options)\n}\n\n/**\n * 从当前 stream 包装上恢复跨热重载共享的状态。\n *\n * @param llm - LLM 服务实例。\n * @returns 现有包装状态;服务尚未被本插件包装时返回 undefined。\n */\nfunction existingRuntimePatchState(llm: LlmRuntime): RuntimePatchState | undefined {\n return (llm.stream as unknown as Record<PropertyKey, unknown>)[runtimePatchStateKey] as RuntimePatchState | undefined\n}\n\n/**\n * 创建覆盖三个公开入口的 LLM 服务包装。\n *\n * 手写请求在 stream 入口浅复制补齐后再进 waterfall,保证每个监听器只处理\n * 一次;loop 构建的请求由宿主打 markAgentLoopRequest 进程标识并深冻结,\n * 经公开的冻结与 sessionId 契约判定后按原引用透传——浅复制会丢失请求身份。\n *\n * @param ctx - 当前插件上下文。\n * @param llm - LLM 服务实例。\n * @returns 可复用的包装状态。\n */\nfunction createRuntimePatchState(ctx: Context, llm: LlmRuntime): RuntimePatchState {\n const originalStream = llm.stream\n const originalPrepareCall = llm.prepareCall\n const originalResolveCallConfig = llm.resolveCallConfig\n let state!: RuntimePatchState\n\n const stream: LlmRuntime['stream'] = (options) => {\n const patched = isAgentLoopRequestBoundary(options) ? options : applyCurrentDefault(state, options)\n return originalStream.call(llm, patched)\n }\n const prepareCall: LlmRuntime['prepareCall'] = async (config, signal) => {\n return originalPrepareCall.call(llm, applyCurrentDefault(state, config), signal)\n }\n const resolveCallConfig: LlmRuntime['resolveCallConfig'] = async (config, signal) => {\n return originalResolveCallConfig.call(llm, applyCurrentDefault(state, config), signal)\n }\n\n state = {\n ctx,\n defaults: new Map(),\n originalStream,\n originalPrepareCall,\n originalResolveCallConfig,\n stream,\n prepareCall,\n resolveCallConfig,\n }\n Object.defineProperty(stream, runtimePatchStateKey, { value: state })\n return state\n}\n\n/**\n * 安装一层可卸载的 LLM 服务包装。\n *\n * 每个插件 fiber 只持有自己的配置层。这样新 fiber 先装配、旧 fiber 后卸载时,\n * 不会把服务方法恢复为已经失效的旧包装。\n *\n * @param ctx - 当前插件上下文。\n * @param llm - LLM 服务实例。\n * @param defaults - 默认推理等级映射。\n */\nfunction installRuntimePatches(\n ctx: Context,\n llm: LlmRuntime,\n defaults: ReasoningDefaults,\n): RuntimePatchState {\n const state = existingRuntimePatchState(llm) ?? createRuntimePatchState(ctx, llm)\n // 复用跨热重载共享的包装层时刷新 ctx:设置服务本身是全局单例,但\n // ctx.logger 等 fiber 级能力应跟随最新装配层,保证告警走当前 fiber 的日志。\n state.ctx = ctx\n if (llm.stream !== state.stream) llm.stream = state.stream\n if (llm.prepareCall !== state.prepareCall) llm.prepareCall = state.prepareCall\n if (llm.resolveCallConfig !== state.resolveCallConfig) llm.resolveCallConfig = state.resolveCallConfig\n\n const token = Symbol(name)\n state.defaults.set(token, defaults)\n ctx.effect(() => () => {\n state.defaults.delete(token)\n if (state.defaults.size !== 0) return\n if (llm.stream === state.stream) llm.stream = state.originalStream\n if (llm.prepareCall === state.prepareCall) llm.prepareCall = state.originalPrepareCall\n if (llm.resolveCallConfig === state.resolveCallConfig) llm.resolveCallConfig = state.originalResolveCallConfig\n }, name + ': restore LLM method wrappers')\n return state\n}\n\n/**\n * 安装默认推理等级注入逻辑。\n *\n * @param ctx - 当前插件上下文。\n * @param config - 插件配置。\n */\nexport function apply(ctx: Context, config: Config = {}): void {\n // 默认参数仅为编程直调便利;cordis 配置路径总先经 Config 校验并把可选集合补全。\n const defaults = normalizeConfig(config)\n\n // 结构化条目若因缺 id / 缺等级被归一化忽略,在此一次性上报,\n // 避免\"配了却不生效\"的静默降级无从排查。\n for (const issue of collectConfigIssues(config)) {\n ctx.logger?.warn?.('[' + name + '] ' + issue)\n }\n\n const settings = settingsService(ctx)\n\n // 注册设置页可编辑命名空间;失败不阻断主流程(patch 层配置仍然生效)。\n if (settings) registerUiDefaultsNamespace(ctx, settings)\n\n if (Object.keys(defaults).length === 0 && !settings) {\n ctx.logger?.info?.('[' + name + '] 未配置推理等级默认值且无设置服务,跳过注入')\n return\n }\n\n const llm = ctx.llm\n\n // 先建立共享配置层;agent/request 和三个公开 LLM 入口都从这层读取最新 defaults。\n const state = installRuntimePatches(ctx, llm, defaults)\n\n // agent-loop 在这里仍允许替换配置;此阶段操作的是调用配置种子,loop 随后\n // 才据此构建并深冻结真正的请求对象,stream 入口按公开冻结边界透传它。\n registerAgentRequestListener(ctx, async function (_payload, next) {\n return applyCurrentDefault(state, await next())\n })\n\n // 激活日志保留生效路由清单,排障时可直接对照请求的 provider/model。\n ctx.logger?.info?.('[' + name + '] 已激活:routes=' + Object.keys(defaults).join(', '))\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-model-reasoning-defaults",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "DSH (DeepSeek Harness) 插件:为每个 LLM 模型配置默认推理等级(reasoningEffort),并在请求进入底层调用链前按路由规则自动补齐",
5
5
  "keywords": [
6
6
  "dsh",
@@ -36,12 +36,12 @@
36
36
  "@deepseek-ai/schemastery": "^3.18.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@deepseek-ai/cordis": "^4.0.1",
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
40
  "@deepseek-ai/cosmokit": "^1.8.2",
41
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.1-rc.2",
42
- "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
43
- "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
44
- "@deepseek-ai/schemastery": "^3.18.1",
41
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.4",
42
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.4",
43
+ "@deepseek-ai/dsh-llm": "0.1.2-alpha.4",
44
+ "@deepseek-ai/schemastery": "^3.18.2",
45
45
  "@types/node": "^24.13.3",
46
46
  "@types/react": "~18.3.1",
47
47
  "react": "^18.2.0",