Overview
dsh-llm-auto
auto 模型路由,内部按配置的有序链依次尝试多条「provider + model」;某条在产出任何内容之前失败时,先按官方同款策略在该路由内原地重试(瞬时错误白名单,默认 5 次),重试耗尽才静默切下一条,对上层表现为一个普通模型;并可用 compactWindow 把自动压缩点钉在指定 token 数附近。0.4.0 起自带浏览器半侧:插件页该 bundle 的卡片上直接渲染配置表单,可直接改 compactWindow(官方 SettingsForm 原语,保存才写入)。README / EN
Package documentation
Registry summary
auto 模型路由,内部按配置的有序链依次尝试多条「provider + model」;某条在产出任何内容之前失败时,先按官方同款策略在该路由内原地重试(瞬时错误白名单,默认 5 次),重试耗尽才静默切下一条,对上层表现为一个普通模型;并可用 compactWindow 把自动压缩点钉在指定 token 数附近。0.4.0 起自带浏览器半侧:插件页该 bundle 的卡片上直接渲染配置表单,可直接改 compactWindow(官方 SettingsForm 原语,保存才写入)。dsh.pub verifies the pinned bundle contract, runtime facts, and distribution semantics. The complete README remains in the source repository.
Read the full README on GitHubLIMITATIONS
Known limitations
- **只"失败时切换",不挑路由**:不做额度记账、不按价格/能力/内容挑路由。顺序完全由 `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` 功能相近(按内容分流 + 回退链)。本插件是 本机自建、只做失败回退,不依赖也不需要它。 ---
