dsh-ofm-addition
Our Free Model 的附属插件 — 不改内核、零依赖、无构建步骤。
第一项能力:截断自动续写。当回答因为达到输出 token 上限被截断时,插件替你发送
「继续」,让模型从中断处接着输出,不用你盯着屏幕手动补一刀。连续次数可以设上限,也可以
选「不限」—— 那时由上下文保护接手,在窗口快满之前停下。
看到的那条提示 —— “已达到输出 token 上限 回答被截断,已有输出保留在对话中。发送
“继续”可让模型接着输出。” —— 就是这个插件在替你按下发送。
第二项能力:每个模型的请求参数。每个 Our Free Model 路由都可以有自己的思考档位、
输出上限(也是「思考预算」的大小)、温度与停止序列;写一次,之后每次请求都按它走。
可以逐个调,可以「全部顶配」,也可以让一个模型读着每个模型的声明把参数填好,确认后再写入。
它做什么
模型输出到了上限
↓ turn/end { reason: { kind: 'max-tokens' } } ← 只有真的停在输出上限才算
↓ 会话转为空闲(agent/status → idle)
↓ 等待一小段时间(默认 0.35s)
↓ 重新检查一遍全部条件 ← 你永远优先
↓ 用量:连续次数还没到上限?(上限可以是「不限」)
↓ 余量:下一条请求还放得进模型窗口?(上下文保护)
↓ agent.followup("继续")
只作用于 Our Free Model。 默认开着「只对 Our Free Model 生效」,其它供应商的会话这个
插件完全不碰 —— 不续写、不折叠、不标注。关掉它才轮到下面的白名单。
只有 finish_reason = length 会触发。 正常结束、报错、你按下的停止都不会。停止
会让这一轮的计数直接归零,下一次截断重新开始算。
每次发送前都重新看一遍状态。 等待窗口里你打字、点停止、排入了新输入、或者切了
模型,本次续写就取消。插件从不和你的手抢方向盘。
两道刹车,至少留一道
| 刹车 | 判定 | 配置 |
|---|
| 连续上限 | 同一次截断已经自动续写了几次 | 不限 / 1 / 3 / 5 / 10 / 自定义,默认 3 |
| 上下文保护 | 下一条请求的 prompt 是否已经逼近模型上下文窗口 | 默认开,阈值默认窗口可用额的 90% |
把上限设为**「不限」**时,上下文保护就是唯一的刹车。两者都关掉页面会给出警告 ——
那意味着一直续到上游报出上下文溢出,插件不替你兜底。
上下文保护怎么算:向宿主取下一条请求会发送的 prompt 大小(tokenMeter.measure(),
也就是内核压缩上下文时用的同一个数),和路由模型的窗口减去本次请求预留的输出相比。
量不出来时(没挂 token meter、或适配器不声明窗口)放行而不是拦死,最后由实测的溢出
收尾:一旦真的撞上 CONTEXT_WINDOW_EXCEEDED,本轮的续写立即结束,并在运行状态里
记为「上下文溢出,已停止」。
内核自己的上下文压缩(compaction-basic)默认在窗口压力的 80% 处动手。它挂着的时候
通常会在你之前把上下文压回去,保护就很少触发;它没挂(例如 minimal 预设)时,
这道保护就是唯一的闸。
设置页
设置 → OFM 增强 与 设置 → OFM 模型参数
OFM 增强
| 分组 | 内容 |
|---|
| 自动续写 | 总开关;连续续写上限(含不限);上下文保护开关与阈值;发送前等待时长;提示词(明确 / 简单 / 自定义,带预览) |
| 对话呈现 | 折叠自动续写(把插件发的「继续」和它已接手的截断提示收起来);显示自动续写次数(在最后一行输出的时间后面标注「(已自动继续 x 次)」,点一下可临时展开被折叠的内容) |
| 生效范围 | 只对 Our Free Model 生效(默认开,其它供应商完全不碰);关闭后才有 provider 与模型白名单(支持 * 通配)、是否处理子代理、截断落在工具调用上时是否跳过、消息在对话里呈现为「用户气泡」还是「自动化标记」 |
| 运行状态 | 已发送次数、最近 24 小时、预算用尽次数、上下文停下次数、活跃会话列表(含最近一次决策结果与「上下文已满」标记,可点「立即续写」手动补一刀)、最近决策记录 |
「立即续写」不需要先有截断 —— 只要你点了,它就发。人是比策略更高的权威。
OFM 模型参数
每个模型一行:名称 · provider|model · 上下文 · 输出上限 · 当前参数
▸ 思考档位 模型默认 / 该模型声明的每一档(light、balanced、deep、high、max…)
▸ 会话临时选择优先 (可选:让模型选择器里临时选的那一档压过本页)
▸ 输出上限 模型默认 / 一个数字 / 「顶配 N」(N = 适配器为该模型报出的容量)
▸ 温度 默认 / 0–2
▸ 停止序列 一行一个,最多 8 条
▸ 恢复默认 · 移除规则
三个按钮:全部顶配(每个模型拉到它能给的最高档位与最大输出上限)、
AI 智能填写(让一个模型读着每个模型的声明给参数)、撤销上一次改动。
两者都先出预览再写入,预览里逐条列出会变成什么、哪几条被拒绝以及为什么。
| 事实 | 说明 |
|---|
| 什么时候生效 | 写入 agent/request(请求组装完成之后、落日志与派发之前),所以改完的下一次请求就带着新参数,不用重启 |
| 谁说了算 | 默认「本页为准」。想临时用模型选择器那一档,给该模型开「会话临时选择优先」 |
| 「恢复默认」做什么 | 保留这一行,把这四个字段交还模型:下一次请求会清掉上一次记录的值,让它回到模型自己的默认 |
| 档位从哪来 | 每次打开页面时向适配器现取(resolveModelInfo)。模型不再声明的档位存不进去;万一磁盘上留着旧的,请求前还会再校验一次,宁可跳过也不会把非法档位发出去(那会让整个回合失败) |
| 会碰哪些模型 | 只碰你配置过的那些路由。没配置过的模型,本插件一个字段都不动 |
配置
存三份 JSON,都在 $DSH_HOME(默认 ~/.dsh)下:
ofm-addition/settings.json —— 设置页写的那些字段
ofm-addition/stats.json —— 计数与最近 60 条决策记录
ofm-addition/params.json —— 逐模型参数表:{ version, scope, entries: { 'provider|model': {...} }, ai }
params.json 是可以在 profile 里预置的:
也可以在 ~/.dsh/profiles/desktop/cordis.patch.yml 里给这一行挂初始值(文件里的值
优先):
- id: ofm-addition
name: dsh-ofm-addition
config:
maxContinuations: 0 # 0 = 不限
contextGuard: true # 不限时唯一的刹车,别关
contextRatio: 0.9
providers: ['our-free-model*']
# 逐模型参数的初始值;文件里已有的值优先(页面里改过的以页面为准)
modelParams:
scope: ['our-free-model*']
entries:
'our-free-model|space-bunny-free':
enabled: true
effort: deep # 必须是该模型声明过的档位
maxTokens: 32768
temperature: null # null = 模型默认
stop: []
ai:
provider: '' # 留空 = 用当前默认模型
model: ''
maxTokens: 4096
安装
node install.mjs # 安装 / 修复
node install.mjs --uninstall # 卸载(保留插件目录)
node install.mjs --dry # 只看要改什么
脚本做三件事:在 profile 的 node_modules/ 下建目录联接、往 package.json 的
dependencies 写一行 link:、把 dsh-ofm-addition 加进 dsh.profile.bundles。
不跑 pnpm —— 本插件零依赖,运行时跑 pnpm 只会重建 node_modules 并打断会话。
装完必须完全退出 DSH(含托盘)再启动:bundle 清单和浏览器模块表都是启动期读的。
离线验证台
node --test test/model-params.test.mjs test/policy.test.mjs test/host.test.mjs test/client.test.mjs
114 项,不需要启动 DSH:
test/policy.test.mjs —— 纯决策逻辑。含关键回归:插件自己发的「继续」绝不能被
当成用户输入,否则预算每一轮都会被重置,循环永不停止;以及「不限」真的不限、
上下文阈值恰好卡在边界上、量不出来时放行而不是拦死。
test/host.test.mjs —— 假 cordis ctx + 假 agent + 假 tokenMeter / llm,按真实时序回放
turn/end → agent/status 事件流,验证接线、预算、上下文保护的三段边界、真实溢出的记录、
竞争条件、HTTP 路由与信任围栏。
test/ledger.test.mjs —— 从会话日志推导「一次运行」:开合边界、上下文溢出的命名、
你接手时收尾、截断没有续写时不开运行。
test/client.test.mjs —— 假 React + 假 __ModuleLoader__,验证插槽注册形状、
中英文字典键齐平、以及用真实 /summary 载荷渲染出元素树。
test/model-params.test.mjs —— 参数表本身:档位校验(模型未声明的档位绝不发出去)、
「恢复默认」真的清掉上一次记录的值、顶配不越过适配器报出的容量、AI 回答里编造的档位会被丢掉。
test/transcript.test.mjs —— jsdom 真实 DOM:折叠只命中运行自己的行
(按消息 id 命中行键)、别的会话/别的轮次不受影响、样式只装一次、
conversation.chat.assistant-actions 徽章注册形状。
版本
- 0.4.0 —— 新增OFM 模型参数设置分区:逐模型的思考档位 / 输出上限 / 温度 / 停止序列,
「全部顶配」与「AI 智能填写」(都先预览再写入)、一键撤销;参数在
agent/request 上生效,
只作用于配置过的路由。
- 0.3.0 —— 新增折叠自动续写与次数标注(两个独立开关);新增只对 Our Free Model
生效作用域闸(默认开);拒绝原因改为 info 级日志并显示在会话行上。
- 0.2.0 —— 连续上限加「不限」(0 = 不限);新增上下文保护(预判 + 实测溢出两道)。
- 0.1.0 —— 截断自动续写。
⚠️ 0.1.0 的 0 是「一次都不许续」,0.2.0 起才是「不限」。 如果改过设置后行为完全相反,
先完全退出 DSH 再启动 —— 旧代码在内存里不会自己换。
它不做什么
- 不包装、不替换任何宿主服务。只监听事件,然后调用
agent.followup() —— 和内置的
目标驱动、定时任务、webhook 桥用的是同一个公开入口。删掉这个插件,组合和从没装过
它一样健康。
- 不碰 Our Free Model 自己的目录、代码或数据。两个插件各自独立安装、独立升级;
信任围栏是复制一份而不是 import,谁也不因为对方缺失或版本落后而启动失败。
许可
MIT