dsh-qwen38-local-qol
English · 中文
The settings tab on the NInfer line — per-effort budgets greyed out, single all-efforts budget
A QoL plugin for DSH (DeepSeek Harness) for people running Qwen3.8
locally — Qwen3.8-27B on llama.cpp llama-server or on NInfer
(Neroued/ninfer — source build or
self-built Docker image, as of 2026-09-02 — and the ninfer-windows 0.5.0 /
0.6.x native Windows builds; both serve the same OpenAI-compatible /v1
API, so one plugin config covers either), and, at the config level,
Qwen3.8-Flash-Next (same OpenAI-compatible wire, same dialect logic).
It gives stock DSH (no core patches, no pi-ai patchfile) two things the local
Qwen line needs:
- Per-request thinking budgets. Each request carries the selected
reasoning effort and its hard thinking-token cap:
llamacpp dialect sends
chat_template_kwargs.reasoning_effort + top-level
reasoning_budget_tokens (llama.cpp's per-request budget, which overrides
any --reasoning-budget CLI flag); ninfer dialect sends the top-level
reasoning_effort (NInfer 0.5.0's effort whitelist; the budget field is
sent but the server caps thinking with --default-thinking-budget).
off sends chat_template_kwargs.enable_thinking: false — the toggle
this llama.cpp build actually reads.
- A compaction backend that stops burning the output cap on thinking.
The stock engine's sole
summarize() hook is overridden so the summarizer
prefill is trimmed first (recent reasoning only, images stripped, tool
results capped). Thinking-off and the output cap are enforced on the wire
for every purpose: 'compaction' call regardless of the preset in use —
the checkpoint gets the line's whole output cap instead of a truncated
"incomplete checkpoint".
One package, three registrations:
| Registration | Seam | Mount |
|---|
QwenLocalAdapter (provider route qwen38) | ctx.llm.registerAdapter() | the bundle patch (cordis.patch.yml) on the profile root |
QwenLocalCompaction (compaction backend) | subclass of @deepseek-ai/dsh-compaction-basic | the generated user preset ~/.dsh/.agent-presets/qwen38-qol/agent.cordis.yml (the per-session agent preset owns the isolated compaction group; profile-level patches do not reach it) |
| settings tab (Qwen3.8 Local) | user-settings namespace qwen38-local-qol + the browser settings.section slot | the settings provider's installSection (host) and the plugin's dsh.client manifest (browser, the ./client export) |
Install
# same as any other plugin (add --profile <name> for a non-default profile):
dsh plugin --profile web add github:Yunado/dsh-qwen38-local-qol
Restart DSH: the compaction wiring self-applies at boot — the generated
qwen38-qol preset is created from the standard preset's composition, and
the default agent preset is set only when none is configured yet (an
explicit choice is respected on every later boot). New sessions then use
qwen38-qol automatically. Existing sessions keep the preset they were
created with — select qwen38-qol in the GUI to switch one, or remove
the agent-presets: section from ~/.dsh/settings.yaml to keep standard
as the default (the boot step respects it).
setup.js performs the same write manually (regenerates the preset from the
live installed standard, forces the default, dated backups of every file it
changes):
# without --src the standard preset resolves from the installed
# @deepseek-ai/dsh-agent-presets package (or DSH_QWEN38_PRESET_SRC):
node_modules/dsh-qwen38-local-qol/src/setup.js
dsh --profile <name> --patch <plugin>/cordis.patch.yml --dump-config shows
the composed provider row without booting.
Update
# the plugin is a `github:` dependency; update re-resolves it against the
# default branch:
dsh plugin --profile web update dsh-qwen38-local-qol
If the profile lockfile still pins the commit the plugin was first installed
from (github: specs are resolution-pinned to an exact commit), force a fresh
resolution by re-installing:
dsh plugin --profile web remove dsh-qwen38-local-qol
dsh plugin --profile web add github:Yunado/dsh-qwen38-local-qol
Client-side changes ship as the committed lib/client.js build artifact (the
web loader serves the bundle, never src/), so consumers need no build step.
An update does not touch the generated user preset or the qwen38-local-qol:
settings section — re-running setup.js is only required when the preset
shape itself changes.
Uninstall
# 1. remove the dependency (the profile's bundle stack reconciles itself
# against the installed state):
dsh plugin --profile web remove dsh-qwen38-local-qol
# 2. delete the generated compaction user preset (created by setup.js):
rm -rf ~/.dsh/.agent-presets/qwen38-qol
# Windows: C:\Users\<you>\.dsh\.agent-presets\qwen38-qol
# 3. tidy settings.yaml — the preset default must go, the rest is optional:
# - agent-presets: { default: qwen38-qol } (written by setup.js, which kept
# a settings.yaml.bak-* copy; a default pointing at a deleted preset
# breaks preset resolution)
# - the qwen38-local-qol: section block (orphaned namespace; harmless if
# left, cleaner removed)
# - any DSH_QWEN38_* environment variables you set
# 4. restart the DSH host (plugin code loads at host start)
Uninstalling touches nothing else: session history, transcripts, your model
lines, and the engine are not plugin-owned state.
Configuration
The provider row config (from the bundle patch or an overlay; an id-targeted
patch replaces the whole config object, so environment fallbacks apply to the
fields the patch leaves out):
| Field | Env fallback | Default | Meaning |
|---|
baseURL | DSH_QWEN38_BASE_URL | http://localhost:8082/v1 | server base, including /v1 |
model | DSH_QWEN38_MODEL | qwen3.8-27b-nvfp4-uncensored | model id sent when a request omits one (the NInfer 0.5.0 artifact id — same for the Docker and the Windows build; verify against the running server with GET /v1/models; the llama.cpp line serves its own id — set this field or the env there) |
displayName | DSH_QWEN38_DISPLAY_NAME | the model id | human-readable name for the GUI model selector (the wire id is an artifact alias) |
apiKey | DSH_QWEN38_API_KEY | — | server --api-key, when set |
dialect | DSH_QWEN38_DIALECT | llamacpp | ninfer or llamacpp (the thinking wire); a fresh install opens on the llama.cpp line — switch the line in the tab |
contextWindow | DSH_QWEN38_CONTEXT_WINDOW | 229376 | declared context capacity (pressure compaction requires it) |
maxTokens | DSH_QWEN38_MAX_TOKENS | 24576 | declared per-request output cap |
thinkingBudgets | — | { low: 4096, medium: 8192, xhigh: 16384 } | per-effort hard thinking budgets; the declared effort vocabulary is off + these keys (NInfer line: sent but ignored — the effective cap is defaultThinkingBudget / the server flag) |
defaultThinkingBudget | — | 16384 | the NInfer line's effective thinking cap: the server's --default-thinking-budget flag value, one value for all efforts (the NInfer endpoint has no per-request budget field). Recorded by the settings tab — keep it in sync with the server startup flags |
defaultEffort | |
Compaction trim knobs (environment only, so the preset row carries no keys the
stock config schema does not know):
| Env | Default | Meaning |
|---|
DSH_QWEN38_SUMMARIZE_IMAGES | strip | strip reduces image blocks to text placeholders — prefer this when the vision mmproj is offloaded to a second device (common local setups, e.g. llama.cpp --mmproj-device <iGPU>): the summarizer then never re-encodes images on the offload device. keep retains them so the checkpoint can describe the pixels (every compaction re-encodes the images — slower + vision tokens) |
DSH_QWEN38_SUMMARIZE_KEEP_TURNS | 5 | assistant turns at the region tail whose reasoning is kept |
DSH_QWEN38_SUMMARIZE_TOOL_CHARS | 2000 | per-tool-result character cap (JS string length: one CJK character = one ASCII letter = 1 — characters, not tokens); 0 disables |
The generated preset pins the backend row's maxTokens to 24576 (the stock
8192 default truncates long checkpoints; the wire additionally raises any
compaction call to the line's own maxTokens cap, so a larger row value only
helps lines with a bigger output cap).
Settings tab
On a profile with the settings provider (the web surface), the plugin registers
the user-settings namespace qwen38-local-qol and a Qwen3.8 Local page in
the settings dialog. The tab exposes the provider config a human actually
adjusts: the server line selector (llama.cpp / NInfer, i.e. the dialect
field — ports stay out of the labels because they are user-chosen), the
connection fields (baseURL, model, displayName) per dialect,
contextWindow, maxTokens, the per-effort thinkingBudgets (greyed out on
the NInfer line) plus the single defaultThinkingBudget (NInfer line only),
and the
compaction trim knobs (summarize.images / summarize.keepTurns /
summarize.toolChars) — plus a revision indicator and conflict handling for
concurrent edits.
The tab, both lines:
| llama.cpp line (per-effort budgets live) | NInfer line (per-effort greyed out, single all-efforts budget) |
|---|
| llama.cpp line | NInfer line |
- Compaction wiring status: the Compaction section starts with the status
of the local compaction backend as of DSH startup: whether the generated
qwen38-qol preset exists and which agent preset new sessions default to.
It matters because the trim knobs apply only to sessions that use the
qwen38-qol preset (the preset layer), while the wire-layer rules
(compaction thinking-off and the output cap) apply to every qwen38 session
regardless of the preset. The wiring self-applies at every DSH start: a
missing generated preset regenerates from the standard preset's
composition, and the default agent preset is set only when none is
configured yet (an explicit choice is respected on every later boot). The
dot reads at a glance — green: the preset exists and is the default
(compaction active for new sessions); amber: the preset exists but the
default is another preset (available — switch the default on the Agent
presets page); grey: the preset is missing (it regenerates on the next
start, or run the setup.js CLI for a manual re-run). The startup snapshot
covers the preset's existence; the default half is read live, so a change
on the Agent presets page flips the dot without a restart.
- Per-dialect line memory: the section persists a
lines block
(lines.ninfer / lines.llamacpp) where each line remembers its own
connection (baseURL / model / displayName), its own window numbers
(contextWindow / maxTokens / thinkingBudgets), its thinking budget
(defaultThinkingBudget) and its compaction trim knobs (summarize). The
context window is a property of the line's server build (its -c, bounded
by that line's VRAM and quantization), not of the model — two lines of the
same model may legitimately carry different windows, and a shared window
would miscalibrate the compaction threshold of the smaller one; the
thinking budget is the same kind of property (e.g. NInfer's server-side
--default-thinking-budget cap differs per line), and the trim knobs are a
per-line preference. The top-level copies of all of these stay the
adapter's authority (the tab writes them in sync with the active line), so
the host side needs no line awareness; a section saved before lines
existed is migrated transparently. Switching the line in the tab swaps the
two remembered lines; switching back restores the previous line's values.
- Dialect-aware thinking-budget fields: on the NInfer line the per-effort
numbers are greyed out (NInfer has no per-request thinking budget —
ninfer as of 2026-09-02 / ninfer-windows 0.5.0; the values are sent
but ignored), and a single thinking-budget (all efforts) field records
the server's flag value — keep the two in sync.
On the llama.cpp line the per-effort fields stay live (honored per request).
Wire map
Both dialects speak OpenAI-compatible /v1/chat/completions with:
max_tokens (not max_completion_tokens), standard tools array, stop.
- assistant reasoning round-trip via the standard
reasoning_content field
(the #1198 hardening: signature-less thinking blocks are not silently
dropped).
finish_reason: length → harness max-tokens (a budget or output
truncation is not presented as a complete answer).
- Auxiliary calls (
purpose: 'compaction' | 'session-title') force
enable_thinking: false regardless of the caller's reasoningEffort —
their bounded output cap is reserved for the visible result. A compaction
call also takes the larger of the engine-pinned maxTokens and the line's
configured output cap.
- usage:
completion_tokens_details.reasoning_tokens → per-turn reasoning
tokens in the GUI, when the server reports it (the llama.cpp
reasoning-budget build does; NInfer 0.5.0 reports it with
stream_options.include_usage — verified 2026-09; the field is optional
everywhere).
- user image blocks →
image_url data URLs through the attachment seam;
an unreadable image degrades to a [image: name w×h] text placeholder so
one missing store entry never fails the request.
- token-meter image pricing (
imageRequestPricing, synchronous, no I/O):
the NInfer line prices with its exact patch formula (W/32)×(H/32)+2
visual tokens; the llama.cpp line is clamped server-side into its
--image-min-tokens/--image-max-tokens window, so every image prices at
the clamp maximum (1536) — the conservative bound. The adapter supplies
the method because the rc.2 LlmAdapter base predates the seam and the
newer token meter resolves it unguarded.
Develop
pnpm install
pnpm test # node --test (host + client + built artifact)
pnpm run build:client # re-build lib/client.js after editing the src/client* sources
Raw ESM JavaScript with JSDoc for the host half (the dsh-llamacpp shipping
pattern). The browser half (src/client.js, plus src/client-entry.js
which pulls in the client.css page sheet) is React.createElement source,
styled the way the host's own settings sections are: the shared
@deepseek-ai/dsh-client-ui-primitives controls (Button, Input, Switch,
StateDot) and the --dsw-alias-* design tokens. It is built by
scripts/build-client.mjs (esbuild entry src/client-entry.js, with
react and the primitives package external — the host's module table
supplies both identities) into the DSH client-module format — a
self-registering script the web loader executes as a classic script — and
shipped as the committed lib/client.js. Rebuild and commit after any
browser-half change. Peer pins: @deepseek-ai/cordis ^4.0.1, @deepseek-ai/dsh-llm ^0.1.1-rc.2 (verified against the npm
0.1.1-rc.2 line; developed and machine-verified on the 0.1.2-alpha.3 source
tree; re-verified live on 0.1.5-alpha.1 after the session-log V3 upgrade
(2026-09-09)), plus @deepseek-ai/schemastery ^3.18.1 and react ^18.2.0 for
the settings section.
Known Limitations and Deferred Work
- Flash-Next is config-compatible, not artifact-verified. Same wire and
dialect logic; no NInfer Flash-Next artifact exists yet (0.5.0 ships 27B
NVFP4 only), so Flash-Next runs on the
llamacpp dialect (Unsloth
qwen4exp branch) with its own contextWindow/budget values.
- Summarizer behavior depends on the engine version. The backend trims
the prefill itself and delegates the one-shot call to the stock engine
path. The wire's auxiliary-call rules (thinking forced off, compaction
max_tokens raised to the line cap) apply to every engine version,
including stock 0.1.5-alpha.1, which no longer sends reasoningEffort: off
on its own — but the engine-internal behavior (e.g. a future engine that
re-introduces per-summary options) is not controlled by this plugin.
- The preset seam is a Web-surface feature. Headless profiles do not
mount the
agent-presets row, so their sessions are bare agents and the
generated preset's compaction backend does not apply there; the provider
route works in both surfaces. Until the upstream opens a preset/settings
seam for headless, headless users keep the compaction change on the core
patch chain.
- Default preset. The compaction wiring self-applies at every DSH start
(a missing generated preset regenerates from the standard preset; the
default is set only when none is configured yet — an explicit choice is
respected).
setup.js performs the same write manually (a dated backup of
settings.yaml is kept). Existing sessions select the preset per session
in the GUI; to keep a different default, remove or edit the agent-presets:
section in settings.yaml.
中文
给本地跑 Qwen3.8 的人用的 DSH(DeepSeek Harness)QoL 插件——
Qwen3.8-27B 跑在 llama.cpp llama-server 或 NInfer(Neroued/ninfer——源码构建
或自构 Docker 镜像,as of 2026-09-02——与 ninfer-windows 0.5.0 / 0.6.x
(原生 Windows build);两者都提供同一套 OpenAI 兼容 /v1 API,一份插件
配置通吃),以及配置层面的 Qwen3.8-Flash-Next(同一 OpenAI 兼容 wire,
同一方言逻辑)。
它给原版 DSH(无核心补丁、无 pi-ai 补丁文件)补上本地 Qwen 线需要的
两样东西:
- 逐请求 thinking 预算。 每个请求携带所选 reasoning effort 及其
thinking token 硬帽:
llamacpp 方言发 chat_template_kwargs.reasoning_effort
- 顶层
reasoning_budget_tokens(llama.cpp 的逐请求预算,覆盖任何
--reasoning-budget CLI 参数);ninfer 方言发顶层 reasoning_effort
(NInfer 0.5.0 的 effort 白名单;预算字段照发但服务端用
--default-thinking-budget 帽住 thinking)。off 发
chat_template_kwargs.enable_thinking: false——这个 llama.cpp 构建
实际读取的开关。
- 不再把输出帽烧在 thinking 上的压缩(compaction)后端。 覆盖原版
引擎唯一的
summarize() 钩子:摘要 prefill 先裁剪(只留近期
reasoning、图片剔除、工具结果截断)。thinking-off 与输出帽在 wire 层
对每个 purpose: 'compaction' 调用强制(与你用哪个 preset 无关)——
checkpoint 拿到该线完整输出帽,而不是被截断的 "incomplete checkpoint"。
一个包,三处注册:
| 注册 | 接缝 | 挂载点 |
|---|
QwenLocalAdapter(provider 路由 qwen38) | ctx.llm.registerAdapter() | bundle 补丁(cordis.patch.yml),挂在 profile 根 |
QwenLocalCompaction(压缩后端) | @deepseek-ai/dsh-compaction-basic 子类 | 生成的用户 preset ~/.dsh/.agent-presets/qwen38-qol/agent.cordis.yml(每会话 agent preset 拥有隔离的压缩组;profile 级补丁够不到) |
| 设置 tab(Qwen3.8 本地) | 用户设置命名空间 qwen38-local-qol + 浏览器 settings.section 槽位 | settings provider 的 installSection(宿主侧)与插件的 dsh.client manifest(浏览器侧,./client 导出) |
安装
# 与其他插件相同(非默认 profile 加 --profile <name>):
dsh plugin --profile web add github:Yunado/dsh-qwen38-local-qol
重启 DSH:压缩接线在启动时自动生效——从 standard preset 的组成生成
qwen38-qol 用户 preset,且仅当尚未配置默认 agent preset 时才设默认
(显式选择之后每次启动都尊重)。新会话随后自动使用 qwen38-qol。
已有会话保留创建时的 preset——在 GUI 里选择 qwen38-qol 切换单个会话,
或从 ~/.dsh/settings.yaml 删掉 agent-presets: 段保持 standard 为默认
(启动步骤尊重它)。
setup.js 手动执行同样的写入(从已安装的 live standard 重新生成 preset、
强制设默认、对改动的每个文件留日期备份):
# 不带 --src 时,standard preset 从已安装的 @deepseek-ai/dsh-agent-presets
# 包解析(或 DSH_QWEN38_PRESET_SRC):
node_modules/dsh-qwen38-local-qol/src/setup.js
dsh --profile <name> --patch <plugin>/cordis.patch.yml --dump-config
可在不启动的情况下查看组合后的 provider 行。
更新
# 插件是 `github:` 依赖;update 会重新解析默认分支:
dsh plugin --profile web update dsh-qwen38-local-qol
若 profile 锁文件仍钉在首次安装时的 commit(github: 依赖按精确 commit
解析),重装即可强制重新解析:
dsh plugin --profile web remove dsh-qwen38-local-qol
dsh plugin --profile web add github:Yunado/dsh-qwen38-local-qol
client 面改动以已提交的 lib/client.js 构建产物出货(web loader 只服务
bundle,不服务 src/),使用方无需构建步骤。更新不会触碰生成的用户
preset 与 qwen38-local-qol: 设置节——只有 preset 形状本身变化时才需要
重跑 setup.js。
卸载
# 1. 移除依赖(profile 的 bundle 栈会按已安装状态自动对齐):
dsh plugin --profile web remove dsh-qwen38-local-qol
# 2. 删除 setup.js 生成的压缩用户 preset:
rm -rf ~/.dsh/.agent-presets/qwen38-qol
# Windows:C:\Users\<你>\.dsh\.agent-presets\qwen38-qol
# 3. 清理 settings.yaml——preset 默认项必须删,其余可选:
# - agent-presets: { default: qwen38-qol }(setup.js 写入,留有
# settings.yaml.bak-* 备份;默认项指向已删除的 preset 会让
# preset 解析报错)
# - qwen38-local-qol: 设置节(孤儿命名空间,留着无害、删了更干净)
# - 你设置过的 DSH_QWEN38_* 环境变量
# 4. 重启 DSH host(插件代码在 host 启动时加载)
卸载不触碰其他任何东西:会话历史、transcript、模型线、引擎都不是插件
持有的状态。
配置
provider 行配置(来自 bundle 补丁或 overlay;按 id 定向的补丁替换整个
config 对象,所以环境回退只作用于补丁没写的字段):
| 字段 | 环境回退 | 默认值 | 含义 |
|---|
baseURL | DSH_QWEN38_BASE_URL | http://localhost:8082/v1 | 服务器地址(含 /v1) |
model | DSH_QWEN38_MODEL | qwen3.8-27b-nvfp4-uncensored | 请求未带 model 时发送的 id(NInfer 0.5.0 工件 id——Docker 与 Windows build 相同;可用 GET /v1/models 对运行中的服务器核实;llama.cpp 线用自己的 id——在那边设此字段或环境变量) |
displayName | DSH_QWEN38_DISPLAY_NAME | model id | GUI 模型选择器的可读名(wire id 是工件别名) |
apiKey | DSH_QWEN38_API_KEY | — | 服务器 --api-key(如设置) |
dialect | DSH_QWEN38_DIALECT | llamacpp | ninfer 或 llamacpp(thinking wire 方言);新装默认打开 llama.cpp 线——在 tab 里切线 |
contextWindow | DSH_QWEN38_CONTEXT_WINDOW | 229376 | 声明的上下文容量(压力压缩需要它) |
maxTokens | DSH_QWEN38_MAX_TOKENS | 24576 | 声明的每请求输出帽 |
thinkingBudgets | — | { low: 4096, medium: 8192, xhigh: 16384 } | 每档 effort 的 thinking 硬帽;声明的 effort 词汇 = off + 这些键(NInfer 线:发送但被忽略——实际帽 = defaultThinkingBudget / 服务端参数) |
defaultThinkingBudget | — | 16384 | NInfer 线的实际 thinking 帽:服务端 --default-thinking-budget 参数值,全部 effort 共用一个值(NInfer 端点无逐请求预算字段)。由设置 tab 记录——与服务器启动参数保持同步 |
defaultEffort | DSH_QWEN38_DEFAULT_EFFORT | medium | 注入未带 effort 的请求;必须是 off 或 thinkingBudgets 键。声明它(任意值)会抑制核心选择器的 "Default" 行——在这条线上它与 off 冗余 |
thinkingLevelMap | — | 恒等 | effort id → wire effort 名 |
includeUsage | — |
压缩裁剪旋钮(仅环境变量,让 preset 行不携带原版 config schema 不认识的键):
| 环境变量 | 默认值 | 含义 |
|---|
DSH_QWEN38_SUMMARIZE_IMAGES | strip | strip 把图片块降为文本占位符——vision mmproj offload 到第二设备时优选(常见本地配置,如 llama.cpp --mmproj-device <iGPU>):摘要器不再在离卡设备上重编码图片。keep 保留,checkpoint 能描述像素(每次压缩都重编码——更慢 + 视觉 token 开销) |
DSH_QWEN38_SUMMARIZE_KEEP_TURNS | 5 | 区域尾部保留 reasoning 的 assistant 轮数 |
DSH_QWEN38_SUMMARIZE_TOOL_CHARS | 2000 | 单条工具结果字符帽(JS 字符串长度:一个中文字 = 一个英文字母 = 1——是字符不是 token);0 禁用 |
生成的 preset 把后端行的 maxTokens 钉在 24576(原版 8192 默认帽截断
长 checkpoint;wire 层还会把任何 compaction 调用的 max_tokens 提到该线
自身的 maxTokens 帽——更大的钉值只对输出帽更大的线有意义)。
设置 tab
在带 settings provider 的 profile(web 面)上,插件注册用户设置命名空间
qwen38-local-qol,并在设置弹窗里注册 Qwen3.8 本地 页。tab 暴露人
真正会调的 provider 配置:服务器线选择器(llama.cpp / NInfer,即
dialect 字段——标签不带端口,因为端口是用户选的)、按方言的连接字段
(baseURL、model、displayName)、contextWindow、maxTokens、
每档 thinkingBudgets(NInfer 线置灰)+ 单个 defaultThinkingBudget
(仅 NInfer 线显示)、压缩裁剪旋钮(summarize.images /
summarize.keepTurns / summarize.toolChars)——外加版本号指示器与并发
编辑冲突处理。
tab 实况(两条线):
- 压缩接线状态:压缩段开头显示本地压缩后端在 DSH 启动时的状态:生成的
qwen38-qol 预设是否存在、新会话默认用哪个 agent 预设。有意义是因为裁剪
旋钮只对使用 qwen38-qol 预设的会话生效(preset 层),而 wire 层规则
(压缩 thinking off 与输出帽)对所有 qwen38 会话常开、与预设无关。接线
在每次 DSH 启动时自动生效:生成的 preset 缺失时从 standard preset 的
组成重新生成;仅当尚未配置默认 agent preset 时才设默认(显式选择之后
每次启动都尊重)。圆点一眼可读——绿:preset 存在且为默认(新会话压缩
生效);琥珀:preset 存在但默认是别的 preset(可用——在 Agent 预设页
切换默认);灰:preset 缺失(下次启动重新生成,或跑 setup.js CLI 手动
重跑)。启动快照覆盖 preset 存在性;默认半实时读取,在 Agent 预设页
改动后圆点无需重启即翻转。
- 按方言的线记忆:section 持久化
lines 块(lines.ninfer /
lines.llamacpp),每条线记住自己的连接(baseURL / model /
displayName)、窗口数字(contextWindow / maxTokens /
thinkingBudgets)、thinking 预算(defaultThinkingBudget)和压缩裁剪
旋钮(summarize)。上下文窗口是线(其服务器的 -c,受该线 VRAM 与
量化约束)的属性,不是模型的属性——同模型的两条线可以合理地不同窗口,
共享窗口会把较小那线的压缩阈值算错;thinking 预算是同类属性(如 NInfer
服务端 --default-thinking-budget 帽逐线不同),裁剪旋钮是逐线偏好。
这些字段的顶层副本保持 adapter 权威(tab 与活跃线同步写),所以宿主侧
无需线感知;lines 出现前保存的 section 透明迁移。tab 里切线 = 两条
记忆互换;切回 = 恢复该线原值。
- 随方言的 thinking 预算字段:NInfer 线把按 effort 的数字置灰(NInfer
没有逐请求 thinking 预算——ninfer as of 2026-09-02 / ninfer-windows 0.5.0;
数值照发但被忽略),另有一个 thinking 预算(全部 effort) 字段记录
服务端
--default-thinking-budget 参数值——两者保持同步。llama.cpp
线按 effort 的字段保持可用(逐请求生效)。
- 填一次默认值:所有窗口数字(每线 229376 / 24576 /
4096-8192-16384)、裁剪旋钮(strip / 5 / 2000)、每条线的生产连接
(NInfer 8082 + 27B NVFP4 工件 id;llama.cpp 8080 + GGUF 基名)都是
schema 默认值——新安装默认打开 llama.cpp 线(general 默认),整表预填,
只需填与默认不同的字段。
includeUsage
(默认 true)与 defaultEffort(默认 medium)刻意不是 tab
控件——留在 schema/config 层(补丁行/环境),在专用本地线上按设计
关闭。
- 持久化 = 设置文档(
settings.yaml,热加载);写路径携带命名空间
版本号,过期写入表现为冲突(重读),绝不静默覆盖。
- 生效即时、免重启:adapter 每请求读解析值,压缩后端每次 summarize
读——保存的改动在下一次 wire 调用生效。(新的聊天会话仍需要于模型
目录字段——
contextWindow / maxTokens / displayName 在会话开始时
解析。)
- 优先级:设置 tab(用户层)→ 补丁行/环境 → 内置默认值。无 settings
provider(headless profile)时,上表的行/环境/默认链仍生效,裁剪旋钮
回退到环境变量。
- 注册是声明式注入,不是 store 读取:apply 体用
ctx.inject(['settings'], …)(和 ctx.inject(['attachments'], …))
而不是 ctx.get(…)。apply 时的 store 读取与启动顺序竞争——本插件 apply
先于 settings provider 注册服务运行时,section 会静默装不上
(llm-pi-ai / tool-fs / agent-loop 先例是声明注入形式;缺失的可选服务
让子 fiber 保持 pending 而非失败)。
Wire 对照
双方言都讲 OpenAI 兼容 /v1/chat/completions:
max_tokens(不是 max_completion_tokens)、标准 tools 数组、stop。
- assistant reasoning 往返走标准
reasoning_content 字段(#1198 加固:
无签名 thinking 块不再被静默丢弃)。
finish_reason: length → harness max-tokens(预算或输出截断不表现为
完整回答)。
- 辅助调用(
purpose: 'compaction' | 'session-title')无条件发
enable_thinking: false(不依赖调用方的 reasoningEffort)——其有界
输出帽留给可见结果。compaction 调用的 max_tokens 取引擎钉值与该线
配置输出帽的较大者。
- usage:
completion_tokens_details.reasoning_tokens → GUI 逐轮
reasoning tokens(服务端报告时;llama.cpp reasoning-budget 构建报告;
NInfer 0.5.0 在 stream_options.include_usage 下报告——2026-09 验证;
该字段处处可选)。
- 用户图片块 → 经 attachment 接缝的
image_url data URL;读不到的图降为
[image: name w×h] 文本占位——单个 store 条目缺失从不搞挂请求。
- token 仪表图片计价(
imageRequestPricing,同步、无 I/O):NInfer 线用
其精确 patch 公式 (W/32)×(H/32)+2 视觉 token;llama.cpp 线被服务端
钳在 --image-min-tokens/--image-max-tokens 窗口内,所以每张图都按
钳位上限(1536)计价——保守上界。adapter 自带该方法,因为 rc.2
LlmAdapter 基类早于该接缝、而新版 token 仪表无守卫地解析它。
开发
pnpm install
pnpm test # node --test(host + client + 构建产物)
pnpm run build:client # 改完 src/client* 源码后重建 lib/client.js
宿主半边是带 JSDoc 的裸 ESM JavaScript(dsh-llamacpp 的出货模式)。
浏览器半边(src/client.js,外加引入 client.css 页面样式的
src/client-entry.js)是 React.createElement 源码,样式走宿主自带
settings section 的同一套做法:共享
@deepseek-ai/dsh-client-ui-primitives 控件(Button、Input、Switch、
StateDot)+ --dsw-alias-* 设计令牌。由 scripts/build-client.mjs
(esbuild,入口 src/client-entry.js,react 与 primitives 包
external——宿主 module table 提供这两个身份)构建为 DSH client 模块
格式——web loader 作为经典脚本执行的自注册脚本——以已提交的 lib/client.js
出货。浏览器半边任何改动后重建并提交。Peer 钉版:
@deepseek-ai/cordis ^4.0.1、@deepseek-ai/dsh-llm ^0.1.1-rc.2(对 npm
0.1.1-rc.2 线验证;在 0.1.2-alpha.3 源码树上开发与机器验证;2026-09-09
session-log V3 升级后在 0.1.5-alpha.1 上实跑复验),设置
section 另有 @deepseek-ai/schemastery ^3.18.1 与 react ^18.2.0。
已知限制与暂缓工作
- Flash-Next 是配置兼容,未工件验证。 同一 wire 与方言逻辑;NInfer
Flash-Next 工件尚不存在(0.5.0 只出 27B NVFP4),所以 Flash-Next 跑在
llamacpp 方言(Unsloth qwen4exp 分支),用自己的
contextWindow/预算值。
- 摘要器行为依赖引擎版本。 后端自己裁 prefill,一次性调用委托给
原版引擎路径。wire 层辅助调用规则(thinking 强制 off、compaction
max_tokens 提到线帽)对所有引擎版本生效(含不再自带
reasoningEffort: off 的 stock 0.1.5-alpha.1),但引擎内部行为(例如
未来引擎重新引入 per-summary options)不在本插件控制之内。
- preset 接缝是 web 面功能。 headless profile 不挂
agent-presets
行,其会话是裸 agent,生成的 preset 压缩后端在那里不生效;provider 路由
两面都工作。上游为 headless 打开 preset/settings 接缝之前,headless
用户的压缩改动保留在核心补丁链上。
- 默认 preset。 压缩接线在每次 DSH 启动时自动生效(生成的 preset 缺失
时从 standard preset 重新生成;仅当尚未配置默认时才设默认——显式选择被
尊重)。
setup.js 手动执行同样的写入(settings.yaml 留日期备份)。
已有会话在 GUI 里每会话选择;想保持别的默认,删掉或改 settings.yaml
的 agent-presets: 一节即可。