dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
搜索增强的会话模型选择器:两段式宽松匹配(先归一化子串,未命中再退子序列,glm53 命中 GLM-5.3、ds 命中 DeepSeek)、近七天成功调用过的供应商置顶,推理档位面板记住每个模型上次显式选择的档位。
DSH 会话模型选择器(搜索增强),独立 cordis client 插件。替换原生 conversation.input.model 座位(priority: -1 遮蔽原生 0,官方 shadows-shipped-ui 路径),提供:
-/_/.///空格,「glm53」命中「GLM-5.3」),未命中再退子序列兜底(fzf 式按序不连续,「ds」命中「DeepSeek」);有查询时命中组按梯队稳定排序(子串命中组在前,组内模型行同样子串优先);纯分隔符查询视为无搜索;匹配片段高亮仅对原始子串命中生效(归一化/子序列才命中的不高亮);组名或 id 命中时显示全组模型;匹配字段随目录快照预归一化(useMemo)roundrobin/ 开头)在组头、模型行、pill/root 展示名统一加「轮询·」品牌色前缀(显示层标记,不改目录数据,原生 /model 弹窗不受影响){provider, model},换档保留 provider/model 只带 reasoningEffort,供应商默认省略该键;触发器显示「模型名 · 档位」failures 每供应商警告行;选择被拒(注入面 false)时读 store.error 在菜单内显示,列表保留(load 失败才整页报错,用 lastAction 区分——select 失败也会把 status 置 error)modelDirectories 服务实例的 directoryFor(...).select(原生 /model 弹窗与 composer 座位的共同 seam,原生代码零改动)——选模型 = 该模型上次显式档位 ?? max(无 max 声明取最高声明档);显式换档(≠ 声明 defaultEffort)在选择成功后写入记忆,选择失败不污染;记忆值须仍在模型声明档位集内,配置变更后失效回落 max。本插件 choose() 不再自动填档(策略统一收口在拦截层,「未填档位」即自动语义)。原生弹窗对新模型自动填 defaultEffort 的行为在此归一为同一策略overscroll-behavior: contain 滚动不穿透DSH 理念:一切皆插件、slots 即替换 seam、每个占座者一个独立单元。此前选择器耦合在 model-channel-manager 的 client bundle 里(同一个 apply 同时注册 conversation.view 页签与座位遮蔽),导致无法独立启停/替换。拆出后:
inject: ['slots', 'connection'](+ ctx.inject(['modelDirectories','sessions']) 拿原生目录服务)inject(sessionId) 返回 {available, directory: directoryFor(sessionId).store, load, select}——injected face 来自注册 options 的 inject 字段,不传 → directory null → uSES 崩(踩坑 #20)api.settings.describe() 过滤 model-channel-health 命名空间。该 ns 由 model-channel-manager(host 半)写入;不存在时优雅降级为不置顶,本插件不依赖 model-channel-manager 也能独立工作model-channels ns 的 effortMemory 字段({['provider::model']: effortId})。该 ns 已过 apiproxy 白名单(mcm 面板同用它保存轮询组),schema loose 保留未知字段;读写失败静默降级为无记忆(max 兜底不受影响)。档位写路径本身仍是 directory.select → sessions.selectModel(per-session,host 单一事实源),记忆层只改写/旁听负载从 npm 装(发布版):
dsh plugin --profile web add @arcaneorion/dsh-model-selector-search
# 然后重启 dsh --profile web 并刷新页面
本地开发用 link:(改源码即时生效)——~/.dsh/profiles/web/package.json:
dependencies 加 "@arcaneorion/dsh-model-selector-search": "link:/home/arcaneorion/AI/AI-DSH/plugin/model-selector-search"dsh.profile.bundles 加 "@arcaneorion/dsh-model-selector-search"pnpm install 后重启 dsh --profile webclient-only 插件:无 host 半、无 cordis.patch.yml(client-modules 经 exports['./client'] 自动扫描挂载)。
当前工作树已适配 DSH
0.2.0-rc.1(peer 按0.2.0-rc.1声明;版本0.3.0)。下列0.1.1-rc.2记录仅作历史基线。
| 宿主包 | 声明 | 用途 |
|---|---|---|
@deepseek-ai/dsh-client-ui-model-selection | 0.2.0-rc.1 | 复刻其 inject(sessionId) 契约、包装 modelDirectories.directoryFor().select(档位记忆拦截层) |
@deepseek-ai/dsh-client-ui-conversation | 0.2.0-rc.1 | 座位 conversation.input.model(priority: -1 遮蔽) |
@deepseek-ai/dsh-api-remotes | 0.2.0-rc.1 | ctx.remote.settings 读健康流水(近 7 天置顶、档位记忆) |
@deepseek-ai/cordis | ^4.0.4 | 插件生命周期 |
react | ^18.3.1 | client 半 require('react')(平台模块表键名) |
connection.api 改为 ctx.remote,且参数从对象改位置参数、结果统一为 RemoteResult。
本插件内置一层兼容门面(makeLegacyApi)保留旧的 {result:{ok,value}} 调用形状。model-channels / model-channel-health 两套命名空间
现由 model-channel-manager 的实例配置承载(groups / providerOrder / effortMemory / health),
门面把它们合成回旧视图,故档位记忆仍然可用。modelDirectories.directoryFor() 的一方必须自己声明
inject: ['remote.session', ...],否则抛 cannot get property "remote.session" without inject,
座位渲染直接崩(原生已被遮蔽 → 整个座位消失)。本插件已在插件级 inject 中声明。本插件是最吃宿主契约的一个:它刻意贴着 dsh-client-ui-model-selection 的 private-ish seam 工作,
跨 DSH 版本最先失效的就是它。换版本请先跑一遍「打开菜单 / 搜索 / 换档记忆」再放宽 peer。
roundrobin/<组> 虚拟路由自身不入健康流水,虚拟组不会出现在「最近」置顶provider::model 键:mcm 面板重命名 provider 后旧键失配 → 回落 max(与历史健康流水同理,不随改名迁移)modelDirectories.directoryFor 服务面 seam:原生重构该方法需重审(包装带 __mcmEffortMemWrapped 幂等标记 + WeakSet 防重复包)node tests/search-select.test.cjs # 置顶排序/过滤四维/7 天聚合/组名全组显示(10 用例)
node tests/slot-priority.test.cjs # 复刻 ui-slots SlotCore occupancy + 选举语义(4 用例,T4 静态断言本包源码)
node tests/effort-memory.test.cjs # 档位记忆:augmentRule 纯逻辑(显式放行/自动改写/记忆校验/回落)+ seam 静态断言
-1。同 id/同 cell + 同 priority 精确撞格直接抛错 → 整插件加载失败。mock 验证不暴露(mock 的 register 不做 occupancy 检查)——必须复刻真实 SlotCore 语义(tests/slot-priority.test.cjs)。useSyncExternalStore 当场 TypeError → slot entry crashed,且原生已被遮蔽、无 fallback,座位整体消失。bottom: calc(100% + 6px) 向上展开;top: calc(100% + 6px) 会把 420px 菜单整体弹到视口外。status:'error' + error(code: message),组件若不渲染该分支会把连接断开显示成「暂无可用模型」——与「真的没有模型」无法区分(实测:服务器死亡后残留标签页里打开菜单就是空列表)。渲染 status==='error' 分支后,断连一目了然。DSH_HOME=/tmp/dsh-diag dsh --profile web --no-open --port 3081。临时实例与主实例共用 ~/.dsh 时会并发写同一会话日志(appendLines 是 O_APPEND 追加,两进程各自的 seq 计数器交错 → 日志中出现重复 seq → corrupt session log: seq gap in committed region,resume 直接拒绝)和同一 session_projcache.json。实测踩坑:一次共享 home 的临时实例验证把主实例某会话日志写出了 seq 重复,选择器在该会话内表现为「暂无可用模型」。/model 是原生 commandUi popupSelect shell——register 同名抛错、decorate 只挂 host 目录命令、filterOptions 是 shell 模块内部函数(ESM 绑定插件侧改不了),插件 seam 覆盖不到,只能补丁原生。曾在 0.1.1-rc.2 上打过两处(harness 源码 packages/client/ui-commands/src/client/popup.ts 的 filterOptions,以及运行时产物 @deepseek-ai+dsh-client-ui-commands@0.1.1-rc.2/lib/client.js)。
升级到 0.2.0-rc.1 后该补丁已失效:运行时产物换成
@deepseek-ai+dsh-client-ui-commands@0.2.0-rc.1,实测其中不含归一化/子序列逻辑——/model弹窗的搜索退回原生平铺匹配,pill(本插件)不受影响。要恢复需按 0.2 源码重新打补丁并重启。