dsh-model-reasoning-defaults 0.0.3 → 0.0.4

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/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","createElement","Menu","Input","Button","IconChevronDownOutline14","IconTrashOutline16","IconPlusOutline16"],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * dsh-model-reasoning-defaults 的客户端半体。\n *\n * - 设置页 section:编辑本插件设置命名空间中的扁平路由表,经宿主统一的\n * settings API 写入。Host 每次请求实时读取该层,保存即生效。\n *\n * 控件一律使用官方 @deepseek-ai/dsh-client-ui-primitives(平台基线模块,\n * 宿主模块表直接应答),与设置壳视觉一致。\n */\nimport { createElement, useEffect, useState } from 'react'\nimport type { ChangeEvent, CSSProperties, ReactNode } from 'react'\nimport {\n Button,\n IconChevronDownOutline14,\n IconPlusOutline16,\n IconTrashOutline16,\n Input,\n Menu,\n} from '@deepseek-ai/dsh-client-ui-primitives'\n// Type-only:拉入 SlotMap 声明面,使下方 declare module 增强可解析。\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n\n/** 设置页 section 槽位类型由 ui-settings 壳声明;本插件不依赖该包,本地补齐面。 */\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n 'settings.section': { kind: 'list'; scope: 'root'; owner: { close: () => void } }\n }\n}\n\n/** 宿主 SlotRegistry 的窄面:本插件只用 inject/register 两个入口。 */\ninterface SlotRegistry {\n inject(name: string, callback: () => () => void): () => void\n register(options: unknown, component: unknown): () => void\n}\n\n/** 本插件的设置命名空间,与 Host 侧 UI_DEFAULTS_NAMESPACE 一致。 */\nconst SETTINGS_NS = 'model-reasoning-defaults'\n\n/** 已知推理等级集合(与 Host 侧 KNOWN_REASONING_EFFORTS 保持一致)。 */\nconst KNOWN_EFFORTS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const\n\ntype ClientContext = {\n slots: SlotRegistry\n remote: {\n settings: RemoteSettings\n }\n}\n\n/** 声明客户端所需的槽位与 settings Remote 命名空间。 */\nexport const inject = ['slots', 'remote', 'remote.settings'] as const\n\n/** settings Remote 的读写方法面(宿主 API Gateway 经 Typert Remote 生成)。 */\ninterface RemoteSettings {\n describe(): Promise<RemoteResult<SettingsDescribeValue>>\n replace(\n ns: string,\n section: Record<string, unknown>,\n expectedRevision: number | undefined,\n ): Promise<RemoteResult<SettingsNamespaceView>>\n}\n\n/** Typert Remote 统一应答形状。 */\ntype RemoteResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: { code: string; message: string; details: object } }\n\n/** describe 应答:每个已注册命名空间与写入能力标记。 */\ninterface SettingsDescribeValue {\n /** 设置文档是否可写。 */\n writable: boolean\n /** 是否有宿主侧的设置文档。 */\n hasDocument: boolean\n /** 已注册命名空间视图列表。 */\n namespaces: SettingsNamespaceView[]\n}\n\n/** 单个命名空间的描述视图。 */\ninterface SettingsNamespaceView {\n /** 命名空间键。 */\n ns: string\n /** 脱敏后的解析值。 */\n value: unknown\n /** 该视图读取时的修订号,写回时作为 expectedRevision。 */\n revision: number\n}\n\n/** apply 时捕获的设置 Remote;组件经模块级引用读取(单注册插件,热重载时随 apply 刷新)。 */\nlet currentApi: RemoteSettings | undefined\n\n/** 一条可编辑的路由行。 */\ninterface RouteRow {\n key: string\n effort: string\n}\n\n/** describe 应答中单个命名空间的视图子集。 */\ninterface NamespaceView {\n ns: string\n value: unknown\n revision: number\n}\n\n/**\n * 从 describe 应答中提取本插件命名空间的路由表与修订号。\n *\n * @param namespaces - describe 应答携带的命名空间视图列表。\n * @returns 路由字典副本与修订号;命名空间尚未注册时为 undefined。\n */\nfunction findOwnNamespace(namespaces: NamespaceView[]): { defaults: Record<string, string>; revision: number } | undefined {\n const own = namespaces.find(entry => entry.ns === SETTINGS_NS)\n if (own === undefined) return undefined\n const section = own.value as { defaults?: Record<string, string> } | null\n return {\n defaults: section !== null && typeof section === 'object'\n && section.defaults !== undefined && typeof section.defaults === 'object'\n ? { ...section.defaults }\n : {},\n revision: own.revision,\n }\n}\n\n/**\n * 从 describe 应答中收集已知 provider 与 provider:model 路由键,作为输入建议。\n *\n * @param namespaces - describe 应答携带的命名空间视图列表。\n * @returns 排序后的建议键列表。\n */\nfunction collectSuggestions(namespaces: NamespaceView[]): string[] {\n const suggestions = new Set<string>()\n const addProvider = (id: string, models: readonly string[]): void => {\n const providerId = id.trim()\n if (providerId.length === 0) return\n suggestions.add(providerId)\n for (const model of models) {\n const modelId = model.trim()\n if (modelId.length > 0) suggestions.add(providerId + ':' + modelId)\n }\n }\n\n const piAi = namespaces.find(entry => entry.ns === 'llm-pi-ai')?.value as {\n providers?: Record<string, { models?: Array<{ id?: unknown }> }>\n } | null\n if (piAi?.providers && typeof piAi.providers === 'object') {\n for (const [providerId, profile] of Object.entries(piAi.providers)) {\n const models = Array.isArray(profile?.models)\n ? profile.models.map(m => (typeof m?.id === 'string' ? m.id : '')).filter(id => id.length > 0)\n : []\n addProvider(providerId, models)\n }\n }\n\n if (namespaces.some(entry => entry.ns === 'llm-deepseek')) addProvider('deepseek-official', [])\n\n return [...suggestions].sort()\n}\n\n/** 建议弹层最多展示的条数;更多条目没有滚动价值,缩小查询即可。 */\nconst SUGGESTION_LIMIT = 12\n\n/**\n * 按当前输入过滤建议键:子串匹配(路由键常以 provider 前缀开头,前缀过滤\n * 会漏掉 model 段命中),排除与输入完全相同的项,空输入展示前若干条。\n *\n * @param value - 输入框当前值。\n * @param all - 全量建议键。\n * @returns 供弹层展示的建议键列表。\n */\nfunction suggestionMatches(value: string, all: string[]): string[] {\n const exact = value.trim()\n const query = exact.toLowerCase()\n const pool = query.length === 0\n ? all\n : all.filter(candidate => candidate !== exact && candidate.toLowerCase().includes(query))\n return pool.slice(0, SUGGESTION_LIMIT)\n}\n\n// 颜色一律使用 --dsw-alias-* 语义 token(随明暗主题翻转);布局遵循设置壳\n// 约定:.options 已提供 24px 内边距,section 只负责纵向节奏与最大宽度。\nconst sectionStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n maxWidth: 720,\n color: 'var(--dsw-alias-label-primary)',\n}\n\nconst titleStyle: CSSProperties = {\n margin: 0,\n fontSize: 16,\n fontWeight: 600,\n lineHeight: 1.4,\n}\n\nconst rowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n}\n\nconst keyFieldStyle: CSSProperties = {\n flex: 1,\n minWidth: 0,\n}\n\nconst mutedStyle: CSSProperties = {\n margin: 0,\n fontSize: 13,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst hintStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst errorStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-state-error-primary)',\n}\n\nconst okStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-state-success-primary)',\n}\n\n/**\n * 把官方 Input 的默认几何(h32 / r8 / 字号 14)对齐到行内 sm 胶囊控件\n * (Button.sm:h28 / r14 / 字号 12),使路由键输入框与右侧下拉、删除按钮同高同形。\n * 建议弹层的包裹 span 由 Menu 注入为 inline 布局,这里展开成块级并撑满列宽,\n * 让弹层与输入框左缘对齐。通过 data-plugin-css 标记幂等注入,遵循宿主插件样式约定。\n */\nconst COMPACT_INPUT_CSS = [\n '.dsh-mrd-input-sm {',\n // 宿主无全局 border-box reset:content-box 下 width:100% 只是内容宽,\n // 叠加 padding/border 后总宽超出父容器 22px,溢出盖住右侧按钮。\n ' box-sizing: border-box;',\n ' width: 100%;',\n ' height: 28px;',\n ' border-radius: 14px;',\n ' padding: 0 10px;',\n '}',\n '.dsh-mrd-input-sm > input {',\n ' font-size: 12px;',\n ' line-height: 18px;',\n '}',\n '.dsh-mrd-suggest {',\n ' display: block;',\n ' width: 100%;',\n '}',\n].join('\\n')\n\nconst COMPACT_INPUT_STYLE_TAG = 'dsh-model-reasoning-defaults/input-sm'\n\n// 样式注入必须脱离 React 渲染树(模块级副作用):若以组件节点渲染,每次\n// 渲染的 DOM 幂等检查会让 React 把该节点当作\"不再渲染\"而卸载/重挂,\n// 菜单展开等重渲染期间样式短暂丢失,`.dsh-mrd-suggest` 回退为 Menu 默认的\n// inline-flex,输入框宽度塌缩(跟随宿主 tsdown.client.ts 的注入约定)。\nif (\n typeof document !== 'undefined'\n && document.querySelector('style[data-plugin-css=\"' + COMPACT_INPUT_STYLE_TAG + '\"]') === null\n) {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-model-reasoning-defaults'\n tag.dataset.pluginCss = COMPACT_INPUT_STYLE_TAG\n tag.textContent = COMPACT_INPUT_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * 把路由字典展开成按键排序的可编辑行。\n *\n * @param defaults - 路由字典。\n * @returns 稳定排序的行列表。\n */\nfunction rowsOf(defaults: Record<string, string>): RouteRow[] {\n return Object.entries(defaults)\n .map(([key, effort]) => ({ key, effort }))\n .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n/**\n * 校验待保存的行集合并折叠回路由字典。\n *\n * @param rows - 当前编辑的行。\n * @returns 校验通过时返回字典;失败时返回错误消息。\n */\nfunction collapseRows(rows: RouteRow[]): Record<string, string> | { error: string } {\n const result: Record<string, string> = {}\n for (const row of rows) {\n const key = row.key.trim()\n // 空键行是编辑过程中的残渣:直接丢弃而不是拦住保存。\n if (key.length === 0) continue\n if (key in result) return { error: '存在重复路由键:' + key }\n result[key] = row.effort\n }\n return result\n}\n\n/**\n * 设置页「推理等级默认」编辑面板。\n *\n * 数据流:挂载时 describe 读一次(拿路由表、修订号、只读标记与建议键);\n * 保存时带 expectedRevision 做 update,陈旧编辑会被宿主拒绝而不是静默覆盖。\n * 外部改动(另一标签页、手工编辑 yaml)在本组件重新挂载时可见——设置壳每次\n * 打开都重新挂载 section,无需订阅失效事件。\n *\n * @param props - 无属主属性(close 由壳持有,本面板不离开设置页)。\n * @returns 面板节点树。\n */\nfunction ReasoningDefaultsSection(_props: { close: () => void }): ReactNode {\n const [rows, setRows] = useState<RouteRow[]>([])\n const [phase, setPhase] = useState<'loading' | 'ready' | 'unavailable'>('loading')\n const [writable, setWritable] = useState(true)\n const [saving, setSaving] = useState(false)\n const [feedback, setFeedback] = useState<{ kind: 'ok' | 'error'; text: string } | undefined>(undefined)\n const [suggestions, setSuggestions] = useState<string[]>([])\n const [openMenuIndex, setOpenMenuIndex] = useState<number | null>(null)\n const [suggestIndex, setSuggestIndex] = useState<number | null>(null)\n const revisionRef = useState<{ revision: number }>(() => ({ revision: 0 }))[0]\n\n useEffect(() => {\n let cancelled = false\n const load = async (): Promise<void> => {\n const api = currentApi\n if (api === undefined) {\n if (!cancelled) setPhase('unavailable')\n return\n }\n try {\n const response = await api.describe()\n if (cancelled) return\n if (!response.ok) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: response.error.message })\n return\n }\n const own = findOwnNamespace(response.value.namespaces)\n if (own === undefined) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: 'Host 未注册 ' + SETTINGS_NS + ' 命名空间,请确认插件已启用并刷新页面' })\n return\n }\n setRows(rowsOf(own.defaults))\n revisionRef.revision = own.revision\n setWritable(response.value.writable)\n setSuggestions(collectSuggestions(response.value.namespaces))\n setPhase('ready')\n } catch (error) {\n if (!cancelled) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: error instanceof Error ? error.message : String(error) })\n }\n }\n }\n void load()\n return () => { cancelled = true }\n }, [revisionRef])\n\n const save = async (): Promise<void> => {\n const api = currentApi\n if (api === undefined || saving) return\n const collapsed = collapseRows(rows)\n if ('error' in collapsed) {\n setFeedback({ kind: 'error', text: collapsed.error })\n return\n }\n setSaving(true)\n setFeedback(undefined)\n try {\n // 必须走 replace 而非 update:update 是 merge patch,普通对象递归合并、\n // 省略键不表达删除,删掉的路由会在存储层原样保留并在回读时\"复活\";\n // replace 整段替换 user section,删除才能落盘。修订号语义两种写法一致。\n const response = await api.replace(SETTINGS_NS, { defaults: collapsed }, revisionRef.revision)\n if (!response.ok) {\n setFeedback({ kind: 'error', text: response.error.message })\n return\n }\n // 写回应答里带新修订号;再 describe 一次让行视图与存储严格一致(含 trim)。\n const reread = await api.describe()\n if (reread.ok) {\n const own = findOwnNamespace(reread.value.namespaces)\n if (own !== undefined) {\n setRows(rowsOf(own.defaults))\n revisionRef.revision = own.revision\n }\n }\n setFeedback({ kind: 'ok', text: '已保存,对新请求即时生效' })\n } catch (error) {\n setFeedback({ kind: 'error', text: error instanceof Error ? error.message : String(error) })\n } finally {\n setSaving(false)\n }\n }\n\n if (phase === 'loading') {\n return createElement('div', { style: sectionStyle }, createElement('p', { style: mutedStyle }, '加载推理等级配置…'))\n }\n\n if (phase === 'unavailable') {\n return createElement(\n 'div',\n { style: sectionStyle },\n createElement('p', { style: errorStyle }, '无法读取推理等级配置。'),\n feedback === undefined\n ? null\n : createElement('p', { style: feedback.kind === 'error' ? errorStyle : okStyle }, feedback.text),\n )\n }\n\n const children: ReactNode[] = [\n createElement('div', { key: 'title', style: titleStyle }, '推理等级默认'),\n createElement(\n 'p',\n { key: 'intro', style: mutedStyle },\n '请求未显式指定 reasoningEffort 时按此路由表补齐。匹配顺序:provider:model → provider:* → provider/* → provider → *:model → *。',\n ),\n ]\n\n if (!writable) {\n children.push(createElement('p', { key: 'readonly', style: mutedStyle }, '当前设置文档为只读,修改需在 Host 侧完成。'))\n }\n\n for (let index = 0; index < rows.length; index += 1) {\n const row = rows[index]\n const menuOpen = openMenuIndex === index\n const keySuggestions = suggestionMatches(row.key, suggestions)\n children.push(createElement(\n 'div',\n { key: 'row-' + index, style: rowStyle },\n createElement('div', { key: 'key', style: keyFieldStyle },\n // 建议弹层与等级下拉一样走 portal:设置壳的 .panel 裁剪 overflow,\n // 原地弹层会被裁掉或顶出面板(原生 datalist 更是完全不受控)。\n createElement(Menu, {\n key: 'key-suggest',\n className: 'dsh-mrd-suggest',\n open: suggestIndex === index && keySuggestions.length > 0,\n portal: true,\n dense: true,\n items: keySuggestions.map(candidate => ({ id: candidate, label: candidate })),\n onSelect: (candidate: string) => {\n const next = [...rows]\n next[index] = { ...row, key: candidate }\n setRows(next)\n setSuggestIndex(null)\n },\n onClose: () => { setSuggestIndex(null) },\n anchor: createElement(Input, {\n className: 'dsh-mrd-input-sm',\n value: row.key,\n placeholder: 'provider:model / provider:* / *',\n 'aria-label': '路由键',\n style: { width: '100%' },\n onFocus: () => { setSuggestIndex(index) },\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n const next = [...rows]\n next[index] = { ...row, key: event.target.value }\n setRows(next)\n setSuggestIndex(index)\n },\n }),\n }),\n ),\n createElement(Menu, {\n key: 'effort',\n open: menuOpen,\n onClose: () => { setOpenMenuIndex(null) },\n portal: true,\n dense: true,\n items: KNOWN_EFFORTS.map(level => ({ id: level, label: level })),\n selectedId: row.effort,\n onSelect: (id: string) => {\n const next = [...rows]\n next[index] = { ...row, effort: id }\n setRows(next)\n setOpenMenuIndex(null)\n },\n anchor: createElement(Button, {\n variant: 'outline',\n size: 'sm',\n 'aria-haspopup': 'menu',\n 'aria-expanded': menuOpen,\n onClick: () => { setOpenMenuIndex(menuOpen ? null : index) },\n }, row.effort, createElement(IconChevronDownOutline14)),\n }),\n createElement(Button, {\n key: 'remove',\n variant: 'ghost',\n size: 'sm',\n icon: createElement(IconTrashOutline16),\n 'aria-label': '删除路由 ' + row.key,\n title: '删除该路由',\n onClick: () => {\n setRows(rows.filter((_, i) => i !== index))\n setOpenMenuIndex(null)\n setSuggestIndex(null)\n },\n }, '删除'),\n ))\n }\n\n children.push(createElement(\n 'div',\n { key: 'actions', style: rowStyle },\n createElement(Button, {\n variant: 'outline',\n size: 'sm',\n icon: createElement(IconPlusOutline16),\n onClick: () => setRows([...rows, { key: '', effort: 'medium' }]),\n }, '添加路由'),\n createElement(Button, {\n variant: 'primary',\n size: 'sm',\n disabled: saving || !writable || rows.length === 0,\n onClick: () => { void save() },\n }, saving ? '保存中…' : '保存'),\n ))\n\n children.push(createElement(\n 'p',\n { key: 'hint', style: hintStyle },\n '已知等级:' + KNOWN_EFFORTS.join(' / ')\n + '。生效优先级:patch 层插件配置 > 此处 > Host 各 Provider 已声明的思考等级。'\n + '路由键支持 * 通配,聚焦输入框可查看已配置 Provider/Model 的建议。',\n ))\n\n if (feedback !== undefined) {\n children.push(createElement(\n 'p',\n { key: 'feedback', style: { ...(feedback.kind === 'error' ? errorStyle : okStyle) } },\n feedback.text,\n ))\n }\n\n return createElement('div', { style: sectionStyle }, children)\n}\n\n/**\n * 注册设置页编辑 section。\n *\n * slots.inject 已绑定当前 fiber 的生命周期,不需要再套一层 effect,\n * 注册和注销由同一个 slot owner 管理,不会留下嵌套 disposer。\n *\n * @param ctx - 提供槽位与连接服务的客户端上下文。\n */\nexport function apply(ctx: ClientContext): void {\n // 宿主 API Gateway 装配了 settings Remote 命名空间后,ctx.remote.settings 才可用;\n // inject 声明保证了装配顺序,此处直接引用。\n currentApi = ctx.remote.settings\n\n ctx.slots.inject(\n 'settings.section',\n () => ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_NS,\n order: 15,\n label: () => '推理等级默认',\n }, ReasoningDefaultsSection),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useState","createElement","Menu","Input","Button","IconChevronDownOutlineRegular","IconTrashOutlineRegular","IconPlusOutlineRegular"],"sources":["../src/client/route-table.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 路由表的编辑模型(纯函数,不依赖 React 与 UI 组件)。\n *\n * 面板把存储中的 `defaults` 字典渲染成可编辑行,保存时再把行集合与基线做差异,\n * 折叠成宿主 settings Remote 的路径寻址操作。这里的每个函数都只做数据变换,\n * 便于直接覆盖\"增删改 / 冲突合并 / 异常键\"这些容易静默出错的边界。\n *\n * @module dsh-model-reasoning-defaults/client/route-table\n */\n\n/** 一条可编辑的路由行。 */\nexport interface RouteRow {\n /** 路由键(provider:model 等六级写法之一)。 */\n key: string\n /** 该路由的推理等级。 */\n effort: string\n}\n\n/** 一条路径寻址的写入操作,与宿主 `settings.mutate` 的入参一致。 */\nexport type SettingsPathOp =\n | { op: 'set'; path: readonly string[]; value: string }\n | { op: 'unset'; path: readonly string[] }\n\n/** 路由表在配置项中的字段名:操作路径的前缀。 */\nconst DEFAULTS_FIELD = 'defaults'\n\n/**\n * 把路由字典展开成按键排序的可编辑行。\n *\n * @param defaults - 路由字典。\n * @returns 稳定排序的行列表。\n */\nexport function rowsOf(defaults: Record<string, string>): RouteRow[] {\n return Object.entries(defaults)\n .map(([key, effort]) => ({ key, effort }))\n .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n/**\n * 把存储中\"外部新增\"的路由键并入当前编辑行,用于冲突后的重试保存。\n *\n * 只补 `latest` 相对 `previous` 新增的键:差异语义是\"行里没有即删除\",补进来的\n * 外部新增键可避免重试时顺手删掉它们;而用户自己删掉或改名的键本来就在\n * `previous` 里,绝不能再补回来——补回来等于把删除意图覆盖成\"键仍在行中\",\n * 用户会发现该路由怎么都删不掉,重命名也会退化成\"新增\"。\n *\n * @param rows - 当前编辑的行。\n * @param previous - 发起本次保存所用的基线,即用户编辑所依据的键集。\n * @param latest - 宿主当前生效的路由表。\n * @returns 合并后的行列表,按键排序(空键行是待填写的残渣,排在末尾)。\n */\nexport function mergeStoredRows(\n rows: RouteRow[],\n previous: Readonly<Record<string, string>>,\n latest: Readonly<Record<string, string>>,\n): RouteRow[] {\n const known = new Set(rows.map(row => row.key.trim()).filter(key => key.length > 0))\n const appended = Object.entries(latest)\n .filter(([key]) => !Object.hasOwn(previous, key) && !known.has(key))\n .map(([key, effort]) => ({ key, effort }))\n return [...rows, ...appended].sort((a, b) => {\n const left = a.key.trim()\n const right = b.key.trim()\n if (left.length === 0) return right.length === 0 ? 0 : 1\n if (right.length === 0) return -1\n return left < right ? -1 : left > right ? 1 : 0\n })\n}\n\n/**\n * 把编辑后的行集合与存储基线做差异,折叠成路径寻址的写入操作。\n *\n * 只写差异而不是整段覆盖:宿主按路径写入,删除路由才能落盘(省略键不表达删除),\n * 同时不动同一配置项里的结构化字段。\n *\n * @param baseline - 存储中当前生效的路由表。\n * @param rows - 当前编辑的行。\n * @returns 写入操作列表;路由键重复时返回错误消息。\n */\nexport function buildRouteOps(\n baseline: Readonly<Record<string, string>>,\n rows: RouteRow[],\n): SettingsPathOp[] | { error: string } {\n // 路由键是自由输入的字符串,可能撞上 Object.prototype 的成员名(toString、\n // constructor…):空原型对象 + Object.hasOwn 才不会把这类键误判成重复,\n // 也不会在删除它们时因为原型链命中而漏发 unset。\n const next: Record<string, string> = Object.create(null)\n for (const row of rows) {\n const key = row.key.trim()\n // 空键行是编辑过程中的残渣:直接丢弃而不是拦住保存。\n if (key.length === 0) continue\n if (Object.hasOwn(next, key)) return { error: '存在重复路由键:' + key }\n next[key] = row.effort\n }\n\n const ops: SettingsPathOp[] = []\n for (const [key, effort] of Object.entries(next)) {\n if (baseline[key] !== effort) ops.push({ op: 'set', path: [DEFAULTS_FIELD, key], value: effort })\n }\n for (const key of Object.keys(baseline)) {\n if (!Object.hasOwn(next, key)) ops.push({ op: 'unset', path: [DEFAULTS_FIELD, key] })\n }\n return ops\n}\n","/**\n * dsh-model-reasoning-defaults 的客户端半体。\n *\n * - 设置页 section:编辑本插件配置项(profile patch 中 id 为\n * model-reasoning-defaults 的 `defaults` 字段)里的扁平路由表,经宿主统一的\n * settings Remote 写入。Host 每次请求实时读取该 volatile 字段,保存即生效。\n *\n * 控件一律使用官方 @deepseek-ai/dsh-client-ui-primitives(平台基线模块,\n * 宿主模块表直接应答),与设置壳视觉一致。\n */\nimport { createElement, useEffect, useState } from 'react'\nimport type { ChangeEvent, CSSProperties, ReactNode } from 'react'\nimport {\n Button,\n IconChevronDownOutlineRegular,\n IconPlusOutlineRegular,\n IconTrashOutlineRegular,\n Input,\n Menu,\n} from '@deepseek-ai/dsh-client-ui-primitives'\n// Type-only:拉入 SlotMap 声明面,使下方 declare module 增强可解析。\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { buildRouteOps, mergeStoredRows, rowsOf } from './route-table.js'\nimport type { RouteRow, SettingsPathOp } from './route-table.js'\n\n/** 设置页 section 槽位类型由 ui-settings 壳声明;本插件不依赖该包,本地补齐面。 */\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n 'settings.section': { kind: 'list'; scope: 'root'; owner: { close: () => void } }\n }\n}\n\n/** 宿主 SlotRegistry 的窄面:本插件只用 inject/register 两个入口。 */\ninterface SlotRegistry {\n inject(name: string, callback: () => () => void): () => void\n register(options: unknown, component: unknown): () => void\n}\n\n/**\n * 本插件的设置命名空间。宿主侧的命名空间就是配置项 id——插件自带 bundle patch\n * 插入的那条 loader entry 的 id(见仓库根目录 cordis.patch.yml),不是插件名。\n */\nconst SETTINGS_NS = 'model-reasoning-defaults'\n\n/** 已知推理等级集合(与 Host 侧 KNOWN_REASONING_EFFORTS 保持一致)。 */\nconst KNOWN_EFFORTS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const\n\ntype ClientContext = {\n slots: SlotRegistry\n remote: {\n settings: RemoteSettings\n }\n}\n\n/** 声明客户端所需的槽位与 settings Remote 命名空间。 */\nexport const inject = ['slots', 'remote', 'remote.settings'] as const\n\n/** settings Remote 的读写方法面(宿主 API Gateway 经 Typert Remote 生成)。 */\ninterface RemoteSettings {\n describe(): Promise<RemoteResult<SettingsDescribeValue>>\n mutate(\n ns: string,\n ops: readonly SettingsPathOp[],\n expectedRevision: number | undefined,\n ): Promise<RemoteResult<SettingsNamespaceView>>\n}\n\n/** Typert Remote 统一应答形状。 */\ntype RemoteResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: { code: string; message: string; details: object } }\n\n/** describe 应答:每个已注册命名空间与写入能力标记。 */\ninterface SettingsDescribeValue {\n /** 设置文档是否可写。 */\n writable: boolean\n /** 是否有宿主侧的设置文档。 */\n hasDocument: boolean\n /** 已注册命名空间视图列表。 */\n namespaces: SettingsNamespaceView[]\n}\n\n/** 单个命名空间的描述视图。 */\ninterface SettingsNamespaceView {\n /** 命名空间键。 */\n ns: string\n /** 脱敏后的解析值。 */\n value: unknown\n /** 该视图读取时的修订号,写回时作为 expectedRevision。 */\n revision: number\n}\n\n/** apply 时捕获的设置 Remote;组件经模块级引用读取(单注册插件,热重载时随 apply 刷新)。 */\nlet currentApi: RemoteSettings | undefined\n\n/** 本插件命名空间的编辑状态:路由表与写回用的修订号。 */\ninterface NamespaceState {\n /** 存储中当前生效的路由表,作为差异写入的基线。 */\n defaults: Record<string, string>\n /** 基线读取时的修订号,写回时作为 expectedRevision。 */\n revision: number\n}\n\n/**\n * 从 describe 应答中提取本插件命名空间的路由表与修订号。\n *\n * @param namespaces - describe 应答携带的命名空间视图列表。\n * @returns 路由字典副本与修订号;命名空间尚未注册时为 undefined。\n */\nfunction findOwnNamespace(namespaces: SettingsNamespaceView[]): NamespaceState | undefined {\n const own = namespaces.find(entry => entry.ns === SETTINGS_NS)\n if (own === undefined) return undefined\n return { defaults: defaultsOf(own.value), revision: own.revision }\n}\n\n/**\n * 从命名空间解析值中取出路由字典。\n *\n * @param value - 命名空间视图的解析值。\n * @returns 路由字典副本;形状不符时为空对象。\n */\nfunction defaultsOf(value: unknown): Record<string, string> {\n const section = value as { defaults?: Record<string, string> } | null\n if (section === null || typeof section !== 'object') return {}\n const defaults = section.defaults\n return defaults !== undefined && typeof defaults === 'object' ? { ...defaults } : {}\n}\n\n/**\n * 从 describe 应答中收集已知 provider 与 provider:model 路由键,作为输入建议。\n *\n * @param namespaces - describe 应答携带的命名空间视图列表。\n * @returns 排序后的建议键列表。\n */\nfunction collectSuggestions(namespaces: SettingsNamespaceView[]): string[] {\n const suggestions = new Set<string>()\n const addProvider = (id: string, models: readonly string[]): void => {\n const providerId = id.trim()\n if (providerId.length === 0) return\n suggestions.add(providerId)\n for (const model of models) {\n const modelId = model.trim()\n if (modelId.length > 0) suggestions.add(providerId + ':' + modelId)\n }\n }\n\n const piAi = namespaces.find(entry => entry.ns === 'llm-pi-ai')?.value as {\n providers?: Record<string, { models?: Array<{ id?: unknown }> }>\n } | null\n if (piAi?.providers && typeof piAi.providers === 'object') {\n for (const [providerId, profile] of Object.entries(piAi.providers)) {\n const models = Array.isArray(profile?.models)\n ? profile.models.map(m => (typeof m?.id === 'string' ? m.id : '')).filter(id => id.length > 0)\n : []\n addProvider(providerId, models)\n }\n }\n\n if (namespaces.some(entry => entry.ns === 'llm-deepseek')) addProvider('deepseek-official', [])\n\n return [...suggestions].sort()\n}\n\n/** 建议弹层最多展示的条数;更多条目没有滚动价值,缩小查询即可。 */\nconst SUGGESTION_LIMIT = 12\n\n/**\n * 按当前输入过滤建议键:子串匹配(路由键常以 provider 前缀开头,前缀过滤\n * 会漏掉 model 段命中),排除与输入完全相同的项,空输入展示前若干条。\n *\n * @param value - 输入框当前值。\n * @param all - 全量建议键。\n * @returns 供弹层展示的建议键列表。\n */\nfunction suggestionMatches(value: string, all: string[]): string[] {\n const exact = value.trim()\n const query = exact.toLowerCase()\n const pool = query.length === 0\n ? all\n : all.filter(candidate => candidate !== exact && candidate.toLowerCase().includes(query))\n return pool.slice(0, SUGGESTION_LIMIT)\n}\n\n// 颜色一律使用 --dsw-alias-* 语义 token(随明暗主题翻转);布局遵循设置壳\n// 约定:.options 已提供 24px 内边距,section 只负责纵向节奏与最大宽度。\nconst sectionStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n maxWidth: 720,\n color: 'var(--dsw-alias-label-primary)',\n}\n\nconst titleStyle: CSSProperties = {\n margin: 0,\n fontSize: 16,\n fontWeight: 600,\n lineHeight: 1.4,\n}\n\nconst rowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n}\n\nconst keyFieldStyle: CSSProperties = {\n flex: 1,\n minWidth: 0,\n}\n\nconst mutedStyle: CSSProperties = {\n margin: 0,\n fontSize: 13,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst hintStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst errorStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-state-error-primary)',\n}\n\nconst okStyle: CSSProperties = {\n margin: 0,\n fontSize: 12,\n lineHeight: 1.6,\n color: 'var(--dsw-alias-state-success-primary)',\n}\n\n/**\n * 把官方 Input 的默认几何(h32 / r8 / 字号 14)对齐到行内 sm 胶囊控件\n * (Button.sm:h28 / r14 / 字号 12),使路由键输入框与右侧下拉、删除按钮同高同形。\n * 建议弹层的包裹 span 由 Menu 注入为 inline 布局,这里展开成块级并撑满列宽,\n * 让弹层与输入框左缘对齐。通过 data-plugin-css 标记幂等注入,遵循宿主插件样式约定。\n */\nconst COMPACT_INPUT_CSS = [\n '.dsh-mrd-input-sm {',\n // 宿主无全局 border-box reset:content-box 下 width:100% 只是内容宽,\n // 叠加 padding/border 后总宽超出父容器 22px,溢出盖住右侧按钮。\n ' box-sizing: border-box;',\n ' width: 100%;',\n ' height: 28px;',\n ' border-radius: 14px;',\n ' padding: 0 10px;',\n '}',\n '.dsh-mrd-input-sm > input {',\n ' font-size: 12px;',\n ' line-height: 18px;',\n '}',\n '.dsh-mrd-suggest {',\n ' display: block;',\n ' width: 100%;',\n '}',\n].join('\\n')\n\nconst COMPACT_INPUT_STYLE_TAG = 'dsh-model-reasoning-defaults/input-sm'\n\n// 样式注入必须脱离 React 渲染树(模块级副作用):若以组件节点渲染,每次\n// 渲染的 DOM 幂等检查会让 React 把该节点当作\"不再渲染\"而卸载/重挂,\n// 菜单展开等重渲染期间样式短暂丢失,`.dsh-mrd-suggest` 回退为 Menu 默认的\n// inline-flex,输入框宽度塌缩(跟随宿主 tsdown.client.ts 的注入约定)。\nif (\n typeof document !== 'undefined'\n && document.querySelector('style[data-plugin-css=\"' + COMPACT_INPUT_STYLE_TAG + '\"]') === null\n) {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-model-reasoning-defaults'\n tag.dataset.pluginCss = COMPACT_INPUT_STYLE_TAG\n tag.textContent = COMPACT_INPUT_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * 设置页「推理等级默认」编辑面板。\n *\n * 数据流:挂载时 describe 读一次(拿路由表、修订号、只读标记与建议键);\n * 保存时把编辑差异作为路径操作连同 expectedRevision 发出,陈旧编辑会被宿主\n * 拒绝而不是静默覆盖——撞上冲突时保留本地编辑并刷新修订号,用户再保存一次\n * 即可覆盖。外部改动(另一标签页、手工编辑 profile patch)在本组件重新挂载时\n * 可见——设置壳每次打开都重新挂载 section,无需订阅失效事件。\n *\n * @param props - 无属主属性(close 由壳持有,本面板不离开设置页)。\n * @returns 面板节点树。\n */\nfunction ReasoningDefaultsSection(_props: { close: () => void }): ReactNode {\n const [rows, setRows] = useState<RouteRow[]>([])\n const [phase, setPhase] = useState<'loading' | 'ready' | 'unavailable'>('loading')\n const [writable, setWritable] = useState(true)\n const [saving, setSaving] = useState(false)\n const [feedback, setFeedback] = useState<{ kind: 'ok' | 'error'; text: string } | undefined>(undefined)\n const [suggestions, setSuggestions] = useState<string[]>([])\n const [openMenuIndex, setOpenMenuIndex] = useState<number | null>(null)\n const [suggestIndex, setSuggestIndex] = useState<number | null>(null)\n const store = useState<NamespaceState>(() => ({ defaults: {}, revision: 0 }))[0]\n\n useEffect(() => {\n let cancelled = false\n const load = async (): Promise<void> => {\n const api = currentApi\n if (api === undefined) {\n if (!cancelled) setPhase('unavailable')\n return\n }\n try {\n const response = await api.describe()\n if (cancelled) return\n if (!response.ok) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: response.error.message })\n return\n }\n const own = findOwnNamespace(response.value.namespaces)\n if (own === undefined) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: 'Host 未提供 ' + SETTINGS_NS + ' 配置项,请确认插件已启用并刷新页面' })\n return\n }\n store.defaults = own.defaults\n store.revision = own.revision\n setRows(rowsOf(own.defaults))\n setWritable(response.value.writable)\n setSuggestions(collectSuggestions(response.value.namespaces))\n setPhase('ready')\n } catch (error) {\n if (!cancelled) {\n setPhase('unavailable')\n setFeedback({ kind: 'error', text: error instanceof Error ? error.message : String(error) })\n }\n }\n }\n void load()\n return () => { cancelled = true }\n }, [store])\n\n const save = async (): Promise<void> => {\n const api = currentApi\n if (api === undefined || saving) return\n const ops = buildRouteOps(store.defaults, rows)\n if ('error' in ops) {\n setFeedback({ kind: 'error', text: ops.error })\n return\n }\n if (ops.length === 0) {\n setFeedback({ kind: 'ok', text: '没有需要保存的改动' })\n return\n }\n setSaving(true)\n setFeedback(undefined)\n try {\n const response = await api.mutate(SETTINGS_NS, ops, store.revision)\n if (!response.ok) {\n // 冲突是\"读到的版本已过期\",不是请求非法:重新读取最新路由表作为新基线,\n // 本地编辑保留,用户再保存一次即可覆盖。\n if (response.error.code === 'settings/conflict') {\n const latest = await api.describe()\n const own = latest.ok ? findOwnNamespace(latest.value.namespaces) : undefined\n if (own === undefined) {\n // 刷新失败时不能宣称\"已刷新\":修订号还是旧值,再点保存只会再次冲突,\n // 用户会陷入同一句提示的循环,必须把真实原因说出来。\n setFeedback({\n kind: 'error',\n text: '配置已被其它页面或文件修改,且重新读取失败'\n + (latest.ok ? '(Host 未提供 ' + SETTINGS_NS + ' 配置项)' : ':' + latest.error.message)\n + ';请关闭并重新打开设置页后再试。',\n })\n return\n }\n const previous = store.defaults\n store.defaults = own.defaults\n store.revision = own.revision\n setRows(current => mergeStoredRows(current, previous, own.defaults))\n // 行集会被重排/追加,仍指向旧下标的弹层必须收起,否则会挂到别的行上。\n setOpenMenuIndex(null)\n setSuggestIndex(null)\n setFeedback({ kind: 'error', text: '配置已被其它页面或文件修改,已刷新最新版本;请再次保存以覆盖。' })\n return\n }\n setFeedback({ kind: 'error', text: response.error.message })\n return\n }\n // 写回应答携带写入后的路由表与修订号,直接作为新基线,无需再读一次。\n const stored = defaultsOf(response.value.value)\n store.defaults = stored\n store.revision = response.value.revision\n setRows(rowsOf(stored))\n setOpenMenuIndex(null)\n setSuggestIndex(null)\n setFeedback({ kind: 'ok', text: '已保存,对新请求即时生效' })\n } catch (error) {\n setFeedback({ kind: 'error', text: error instanceof Error ? error.message : String(error) })\n } finally {\n setSaving(false)\n }\n }\n\n if (phase === 'loading') {\n return createElement('div', { style: sectionStyle }, createElement('p', { style: mutedStyle }, '加载推理等级配置…'))\n }\n\n if (phase === 'unavailable') {\n return createElement(\n 'div',\n { style: sectionStyle },\n createElement('p', { style: errorStyle }, '无法读取推理等级配置。'),\n feedback === undefined\n ? null\n : createElement('p', { style: feedback.kind === 'error' ? errorStyle : okStyle }, feedback.text),\n )\n }\n\n const children: ReactNode[] = [\n createElement('div', { key: 'title', style: titleStyle }, '推理等级默认'),\n createElement(\n 'p',\n { key: 'intro', style: mutedStyle },\n '请求未显式指定 reasoningEffort 时按此路由表补齐。匹配顺序:provider:model → provider:* → provider/* → provider → *:model → *。',\n ),\n ]\n\n if (!writable) {\n children.push(createElement('p', { key: 'readonly', style: mutedStyle }, '当前 profile 不接受写入,修改需在 Host 侧完成。'))\n }\n\n for (let index = 0; index < rows.length; index += 1) {\n const row = rows[index]\n const menuOpen = openMenuIndex === index\n const keySuggestions = suggestionMatches(row.key, suggestions)\n children.push(createElement(\n 'div',\n { key: 'row-' + index, style: rowStyle },\n createElement('div', { key: 'key', style: keyFieldStyle },\n // 建议弹层与等级下拉一样走 portal:设置壳的 .panel 裁剪 overflow,\n // 原地弹层会被裁掉或顶出面板(原生 datalist 更是完全不受控)。\n createElement(Menu, {\n key: 'key-suggest',\n className: 'dsh-mrd-suggest',\n open: suggestIndex === index && keySuggestions.length > 0,\n portal: true,\n dense: true,\n items: keySuggestions.map(candidate => ({ id: candidate, label: candidate })),\n onSelect: (candidate: string) => {\n const next = [...rows]\n next[index] = { ...row, key: candidate }\n setRows(next)\n setSuggestIndex(null)\n },\n onClose: () => { setSuggestIndex(null) },\n anchor: createElement(Input, {\n className: 'dsh-mrd-input-sm',\n value: row.key,\n placeholder: 'provider:model / provider:* / *',\n 'aria-label': '路由键',\n style: { width: '100%' },\n onFocus: () => { setSuggestIndex(index) },\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n const next = [...rows]\n next[index] = { ...row, key: event.target.value }\n setRows(next)\n setSuggestIndex(index)\n },\n }),\n }),\n ),\n createElement(Menu, {\n key: 'effort',\n open: menuOpen,\n onClose: () => { setOpenMenuIndex(null) },\n portal: true,\n dense: true,\n items: KNOWN_EFFORTS.map(level => ({ id: level, label: level })),\n selectedId: row.effort,\n onSelect: (id: string) => {\n const next = [...rows]\n next[index] = { ...row, effort: id }\n setRows(next)\n setOpenMenuIndex(null)\n },\n anchor: createElement(Button, {\n variant: 'outline',\n size: 'sm',\n 'aria-haspopup': 'menu',\n 'aria-expanded': menuOpen,\n onClick: () => { setOpenMenuIndex(menuOpen ? null : index) },\n }, row.effort, createElement(IconChevronDownOutlineRegular, { size: 14 })),\n }),\n createElement(Button, {\n key: 'remove',\n variant: 'ghost',\n size: 'sm',\n icon: createElement(IconTrashOutlineRegular),\n 'aria-label': '删除路由 ' + row.key,\n title: '删除该路由',\n onClick: () => {\n setRows(rows.filter((_, i) => i !== index))\n setOpenMenuIndex(null)\n setSuggestIndex(null)\n },\n }, '删除'),\n ))\n }\n\n children.push(createElement(\n 'div',\n { key: 'actions', style: rowStyle },\n createElement(Button, {\n variant: 'outline',\n size: 'sm',\n icon: createElement(IconPlusOutlineRegular),\n onClick: () => setRows([...rows, { key: '', effort: 'medium' }]),\n }, '添加路由'),\n createElement(Button, {\n variant: 'primary',\n size: 'sm',\n disabled: saving || !writable,\n onClick: () => { void save() },\n }, saving ? '保存中…' : '保存'),\n ))\n\n children.push(createElement(\n 'p',\n { key: 'hint', style: hintStyle },\n '已知等级:' + KNOWN_EFFORTS.join(' / ')\n + '。路由表写入本插件的 profile 配置项,仅覆盖未显式指定 reasoningEffort 的请求。'\n + '路由键支持 * 通配,聚焦输入框可查看已配置 Provider/Model 的建议。',\n ))\n\n if (feedback !== undefined) {\n children.push(createElement(\n 'p',\n { key: 'feedback', style: { ...(feedback.kind === 'error' ? errorStyle : okStyle) } },\n feedback.text,\n ))\n }\n\n return createElement('div', { style: sectionStyle }, children)\n}\n\n/**\n * 注册设置页编辑 section。\n *\n * slots.inject 已绑定当前 fiber 的生命周期,不需要再套一层 effect,\n * 注册和注销由同一个 slot owner 管理,不会留下嵌套 disposer。\n *\n * @param ctx - 提供槽位与连接服务的客户端上下文。\n */\nexport function apply(ctx: ClientContext): void {\n // 宿主 API Gateway 装配了 settings Remote 命名空间后,ctx.remote.settings 才可用;\n // inject 声明保证了装配顺序,此处直接引用。\n currentApi = ctx.remote.settings\n\n ctx.slots.inject(\n 'settings.section',\n () => ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_NS,\n order: 15,\n label: () => '推理等级默认',\n }, ReasoningDefaultsSection),\n )\n}\n"],"mappings":"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"}
package/lib/index.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { LlmCallConfig } from "@deepseek-ai/dsh-llm";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { Context } from "@deepseek-ai/cordis";
4
+ import { Volatile } from "@deepseek-ai/cosmokit";
4
5
  //#region src/index.d.ts
5
6
  /** 插件名(同时作为 Cordis 插件 id)。 */
6
7
  declare const name = "dsh-model-reasoning-defaults";
7
- /** 声明依赖的服务:LLM 服务就绪后装配;注册/读取设置命名空间需要设置服务。 */
8
+ /** 声明依赖的服务:LLM 服务就绪后装配;声明设置页策略需要设置服务。 */
8
9
  declare const inject: readonly ["llm", "settings"];
9
10
  /** 单个模型的推理等级配置。 */
10
11
  interface ModelConfig {
@@ -25,9 +26,9 @@ interface ProviderConfig {
25
26
  /** Provider 配置输入:既可以是等级字符串简写(作用于该 Provider 所有模型),也可以是配置对象。 */
26
27
  type ProviderConfigInput = string | ProviderConfig;
27
28
  /**
28
- * 插件配置:支持按 Provider 与模型嵌入式层级配置,也可使用扁平路由字典 defaults。
29
- * 两种形式是同等的一等配置入口(本仓库无历史版本,不存在"新旧兼容"语义),
30
- * 归一化到同一张路由表后统一参与六级匹配。
29
+ * 用户书写的插件配置:支持按 Provider 与模型嵌入式层级配置,也可使用扁平路由
30
+ * 字典 defaults。两种形式是同等的一等配置入口,归一化到同一张路由表后统一参与
31
+ * 六级匹配。
31
32
  *
32
33
  * 合并后的匹配优先级从高到低为:
33
34
  * - "provider:model":精确匹配(来自 providers[p].models[m] 或 defaults["provider:model"])
@@ -38,7 +39,10 @@ type ProviderConfigInput = string | ProviderConfig;
38
39
  * - "*":全局匹配(来自 top-level reasoningEffort 或 defaults["*"])
39
40
  */
40
41
  interface Config {
41
- /** 扁平路由字典:键为上表六级路由键;与结构化配置产生同键时覆盖结构化项。 */
42
+ /**
43
+ * 扁平路由字典:键为上表六级路由键,值为推理等级。该字段是设置页的编辑对象,
44
+ * 与结构化配置产生同键时覆盖结构化项。
45
+ */
42
46
  defaults?: Record<string, string>;
43
47
  /** 按 Provider 组织的嵌入式配置。 */
44
48
  providers?: Record<string, ProviderConfigInput>;
@@ -47,12 +51,37 @@ interface Config {
47
51
  /** 全局默认推理等级(等价于 "*")。 */
48
52
  reasoningEffort?: string;
49
53
  }
50
- /** Cordis 配置模式(显式注解接口类型,满足声明产物的可命名要求)。 */
51
- declare const Config: z<Config>;
54
+ /**
55
+ * Cordis 解析后的配置形状:`defaults` 已由 schemastery 换成 volatile 引用,
56
+ * 其余字段与书写形状一致。
57
+ *
58
+ * 该接口显式写出而非由 `Schemastery.TypeT` 推导,是为了让 `Config` 的注解保持
59
+ * 可命名(推导会展开成内联结构类型,声明产物随之膨胀且绑死 schemastery 内部
60
+ * 泛型);注解本身由编译器与真实 schema 交叉校验,两者不一致会直接报错。
61
+ */
62
+ interface ResolvedConfig {
63
+ /** 设置页编辑的扁平路由表引用。 */
64
+ defaults: Volatile<Record<string, string>>;
65
+ /** 按 Provider 组织的嵌入式配置。 */
66
+ providers?: Record<string, ProviderConfigInput>;
67
+ /** 跨 Provider 适用的模型默认配置。 */
68
+ models?: Record<string, ModelConfigInput> | ModelConfigInput[];
69
+ /** 全局默认推理等级。 */
70
+ reasoningEffort?: string;
71
+ }
72
+ /**
73
+ * Cordis 配置模式。
74
+ *
75
+ * `defaults` 声明为 volatile:设置页经宿主 configEditor 改写 profile patch 后,
76
+ * 宿主原地更新该引用(见 cordis-plugin-loader 的 volatile 提交路径),插件无需
77
+ * 重载即可读到新值。其余结构化字段保持普通字段——它们是 patch 层的书写形式,
78
+ * 变更后正常走插件重载。
79
+ */
80
+ declare const Config: z<Config, ResolvedConfig>;
52
81
  /**
53
82
  * 将结构化 Config 归一化为统一的扁平路由字典。
54
83
  *
55
- * @param config - 插件配置对象。
84
+ * @param config - 插件配置对象(defaults 为普通字典形式)。
56
85
  * @returns 归一化后的键值映射;键与值均已去除首尾空白。
57
86
  */
58
87
  declare function normalizeConfig(config: Config): Record<string, string>;
@@ -74,57 +103,13 @@ type ReasoningConfig = Pick<LlmCallConfig, 'provider' | 'model' | 'reasoningEffo
74
103
  * @returns 原配置或带有 reasoningEffort 的浅复制对象。
75
104
  */
76
105
  declare function applyDefaultReasoningEffort<T extends ReasoningConfig>(config: T, defaults: Readonly<Record<string, string>>): T;
77
- /**
78
- * 设置页 UI 专用命名空间。该命名空间由本插件注册为可写设置项,浏览器设置
79
- * 面板通过宿主统一的 settings API 编辑它;生效优先级位于自动提取之上、
80
- * cordis 插件配置之下(见 resolveEffectiveDefaults)。
81
- */
82
- declare const UI_DEFAULTS_NAMESPACE = "model-reasoning-defaults";
83
- /** 设置命名空间的用户层数据形状:与 Config.defaults 同构的扁平路由字典。 */
84
- interface UiDefaultsSection {
85
- /** 扁平路由字典:键为六级路由键,值为推理等级。 */
86
- defaults?: Record<string, string>;
87
- }
88
- /**
89
- * 从当前 Cordis 上下文的设置服务中读取各 Provider 与模型的默认推理等级。
90
- *
91
- * 字段约定(每个来源只读单一规范字段,不做别名兜底):
92
- * - llm-pi-ai:provider 级 `reasoning` 是宿主原生声明字段;模型级
93
- * `models[].reasoning` 与 `modelOverrides.<model>.reasoning` 是本插件约定
94
- * 读取的扩展字段,依赖其设置解析对未声明键的透传行为。宿主在模型层另有
95
- * 原生字段 `reasoningEfforts`,但那是"可选等级集合 + 线格式映射"的能力
96
- * 声明而非默认值选择,语义不同,本插件不读取;若上游收紧对未声明键的
97
- * 解析策略,`reasoning` 这部分提取会静默失效。
98
- * - llm-deepseek:顶层 `reasoningEffort` 为宿主原生字段,映射到其注册的
99
- * provider 路由 deepseek-official(见 DEEPSEEK_OFFICIAL_PROVIDER_ID)。
100
- *
101
- * 整个函数不包 try/catch:宿主的 settings.get 对未注册命名空间返回 undefined
102
- * 且不会抛错,函数体已对全部外部数据做了形状防御,剩余可能抛错的只有代码
103
- * 缺陷,应当让它暴露,而不是吞掉后以空映射继续主流程。
104
- *
105
- * @param ctx - Cordis 上下文。
106
- * @returns 提取出的路由默认映射字典。
107
- */
108
- declare function extractSettingsDefaults(ctx: Context): Record<string, string>;
109
- /**
110
- * 从本插件专用的设置命名空间读取设置页 UI 写入的路由表。
111
- *
112
- * 该命名空间由 apply() 注册、浏览器设置面板写入,是"设置里改默认推理强度"
113
- * 的存储层;每次请求实时读取,改动即时生效。形状非法(非对象/缺字段)时
114
- * 返回空映射——schema 校验已保证写入合法,这里的防御只覆盖外部手工编辑
115
- * settings 文档的场景。
116
- *
117
- * @param ctx - Cordis 上下文。
118
- * @returns 路由映射字典。
119
- */
120
- declare function extractUiNamespaceDefaults(ctx: Context): Record<string, string>;
121
106
  /**
122
107
  * 安装默认推理等级注入逻辑。
123
108
  *
124
109
  * @param ctx - 当前插件上下文。
125
- * @param config - 插件配置。
110
+ * @param config - Cordis 解析后的插件配置。
126
111
  */
127
- declare function apply(ctx: Context, config?: Config): void;
112
+ declare function apply(ctx: Context, config: ResolvedConfig): void;
128
113
  //#endregion
129
- export { Config, ModelConfig, ModelConfigInput, ProviderConfig, ProviderConfigInput, UI_DEFAULTS_NAMESPACE, UiDefaultsSection, apply, applyDefaultReasoningEffort, extractSettingsDefaults, extractUiNamespaceDefaults, inject, name, normalizeConfig };
114
+ export { Config, ModelConfig, ModelConfigInput, ProviderConfig, ProviderConfigInput, ResolvedConfig, apply, applyDefaultReasoningEffort, inject, name, normalizeConfig };
130
115
  //# sourceMappingURL=index.d.mts.map