概览
dsh-llm-auto
auto 模型路由,内部按配置的有序链依次尝试多条「provider + model」;某条在产出任何内容之前失败时,先按官方同款策略在该路由内原地重试(瞬时错误白名单,默认 5 次),重试耗尽才静默切下一条,对上层表现为一个普通模型;并可用 compactWindow 把自动压缩点钉在指定 token 数附近。0.4.0 起自带浏览器半侧:插件页该 bundle 的卡片上直接渲染配置表单,可直接改 compactWindow(官方 SettingsForm 原语,保存才写入)。README / ZH
插件文档
目录摘要
auto 模型路由,内部按配置的有序链依次尝试多条「provider + model」;某条在产出任何内容之前失败时,先按官方同款策略在该路由内原地重试(瞬时错误白名单,默认 5 次),重试耗尽才静默切下一条,对上层表现为一个普通模型;并可用 compactWindow 把自动压缩点钉在指定 token 数附近。0.4.0 起自带浏览器半侧:插件页该 bundle 的卡片上直接渲染配置表单,可直接改 compactWindow(官方 SettingsForm 原语,保存才写入)。dsh.pub 核对固定版本的组合包契约、运行时事实与分发语义;完整 README 请查看源仓库。
在 GitHub 阅读完整 READMELIMITATIONS
已知限制
- **只"失败时切换",不挑路由**:不做额度记账、不按价格/能力/内容挑路由。顺序完全由 `routes` 决定; 失败时先在**该路由内**重试(见上节),重试耗尽或码不可重试才按顺序切下一条。 - **重试的计费/时延上限(默认参数下)**:单次 `auto` 请求最坏 = 链长 × 6 次上游调用、约 15.5s×链长 的退避(0.5+1+2+4+8+10s);想省就把 `retry.maxRetries` 调小或设 `0`。每次重试都是新的上游请求, 与自带 `dsh-llm-retry` 一样可能重复计费 input token。连带效应:ring 缓冲(默认 50 条)消耗也快 约 6 倍 —— 一条全瞬时失败的链一个请求就占 20+ 条,想多留历史就调大 `logLimit`。 - **只支持 `mode: 'normal'`**:`always`(无上限重试)在单请求内可能无上限计费,挂载时 warn 并回落默认。 - **插件卸载不 drain 在飞退避**:cordis 卸载本插件时,正在进行的退避(≤10s)会自然完成,不像官方 `dsh-llm-retry` 有 lifetime abort + drain(它挂在 agent loop 上,拿得到 session 生命周期)。 - **不做视觉/长上下文分流**:上游同类插件按"含图 / 超长"分流,本插件按用户明确要求只做失败重试/回退。 - **不声明 `inputModalities`**:目录里不宣称"支持图片"。"能不能收图"交给真正被选中的那条路由决定; 声明了反而会让运行时按声明去投影请求(把图片换成占位文本)。 - **`reasoningEffort` 一律用该路由可用的最高强度**(2026-09-23 用户指定):每跳前问一次该路由 `resolveModelInfo` 的 `reasoning.efforts`,取**最后一项**(DSH 的档位强度序固定为 `off→minimal→low→medium→high→xhigh→max`,适配器只保留该模型支持的档位 ⇒ "最后一项"就是它 能给的最高强度),**忽略调用方带来的档位**;该路由完全不声明档位(不思考的模型)时才原样透传, 让分发给出准确错误。这样既不因为"档位不匹配"让兜底路由白失败一次,也不需要调用方为每跳手动调档。 本插件自己**不声明** reasoning 能力,所以模型选择器不会给 `auto` 提供档位选项。 - **跨路由的 replay 元数据**:DSH 只在"历史 provider 与目标 provider 属于同一个适配器实例"时保留 replay 状态。`auto` 自己产出的消息(source.provider = `auto`)在嵌套调用里会被自动剥掉 replay —— 这正是上面「历史回放」一节修的缺陷:剥掉后 pi-ai 会把思考摊平进正文、把 `reasoning_content` 填成空串。本插件在嵌套调用前把 source 改回 replay 记录的真实路由来保住它;历史消息若本来就来自 `commandcode`,回退到同属 pi-ai 的 `ww` 时 replay 会被保留 —— 那是上游既有行为(手动切模型时 同样发生)。**跨模型**的历史(replay 路由 ≠ 本次路由)仍然会被 pi-ai 摊平,这是 pi-ai 自己的策略 (思考签名跨模型不可信),与直连时的行为一致。 - **路由日志是进程内内存**,重启即清空,不适合当审计账本。 - **压缩点靠"声明窗口"间接控制,依赖引擎默认常量**:`compactWindow` 的反算写死了 `thresholdRatio = 0.8` / `headroomTokens = 65536`,并按 `reserved = 0` 推算。若把 `@deepseek-ai/dsh-compaction-basic` 的 `thresholdRatio`/`headroomTokens` 改成别的值、 或给本插件补上 `defaultMaxTokens` 声明,压缩点就会偏离 `compactWindow` —— 挂载日志那行假设与 `/api/llm-auto/routes` 的两个复核字段是排查这类偏离的入口。 - **声明窗口是"压缩刻度"而不是上游承诺**:默认 625000 大于本机某些跳的真实窗口(如 `stepfun` 的可用输入 958464 没问题,但换成更小的通道就会超)—— 请求真正撞上游窗口时由那一条路由 如实报 `CONTEXT_WINDOW_EXCEEDED`(该码不回退)。要"声明真实窗口"就把 `compactWindow` 调成你想要的压缩点,或用旧键 `contextWindow` 直接声明。 - **失败原因摘要可能含上游返回的文本**(如报文片段),但不含凭据:各适配器按设计不把 key 写进消息。 - **未实测覆盖**:①"已产出内容后失败"只有单测(含真实 `LlmRuntime` + 真实流语法不变式)覆盖, 没有对真上游稳定复现过(需要一条"吐一半再断"的路由);②**路由内重试**同样只有单测/真实 `LlmRuntime` 覆盖,没有对真上游的瞬时抖动复现过(需要一条"抖几下再好"的路由);③非回环绑定下的 鉴权影响未评估(见 §5);④**导出 `Config` 之后的设置描述符**只有源码依据 + 单测(见 §2「效果边界」), 没有在重启后的真机上查过 `settings.describe()`(0.4.0 的插件页表单走的是同一套 schema, 注册在 `plugins.bundle.config`,见 §2)。 - **第三方同类插件**:`zhanghao3693/dsh-llm-router` 功能相近(按内容分流 + 回退链)。本插件是 本机自建、只做失败回退,不依赖也不需要它。 ---
