DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

EnhydraLutristus /

EnhydraLutristus/dsh-math-tex

Verified

DSH plugin: keeps streamed answers free of red (broken) KaTeX regions - validates every maths region with the client's own renderer before it is written

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@a7adfab2

dsh-math-tex

让 DSH 的数学回答始终可读:给模型一套排版规则,再在文本落盘之前拦截并修好每一个会被渲染成红字的公式区域。

这是一个 host 侧插件,没有客户端部分——渲染器改不了,所以修复发生在文本还属于我们的最后一刻。


它解决什么问题

DSH 的对话渲染用 KaTeX 渲染 Markdown 数学,只要遇到它解析不了的东西,就把原始源码用 KaTeX 的错误色 #cc0000 直接吐到屏幕上。没有崩溃,也没有日志:一个位置不对的 $$ 足以让整篇推导变成"一片血红",而用户只会看到一片红字,不知道哪一步错了、也不知道原因。

前端是 app.asar 里的一个整体 bundle,给 KaTeX 传的选项是空的(没有 errorColor、没有 maxExpand、没有 trust),所以事后无法配置。文本一旦落盘,屏幕上和会话记录里就都是坏的。

dsh-math-tex 因此在文本落盘之前动手。

两道防线

机制 作用
1 systemPrompt.section 事前规则:行间公式单独成段、$$ 内只有公式、标题不粘公式……让模型少犯这个错
2 llm/stream waterfall 兜底:用客户端同版本的 KaTeX 逐个区域判定,把会渲染成红色的区域改写掉,然后才落盘

第 2 道是关键:llm/stream 是每一次流式模型调用外面的 waterfall,改写过的 chunk 会同时进入持久化的消息体、实时前端帧和上下文压缩,所以屏幕和记录一起修好。

判定不是正则,是问渲染器

直觉上的启发式(公式太长、嵌套太深、用了重型宏)经实测两个方向都会错:300 字符的公式、8 层 \underbrace、\substack、公式里的中文都渲染正常,而一个 99 字符的公式却死了。

更麻烦的是,"渲染器抛没抛异常"也不是判据:

输入 第 1 段调用(严格) 第 2 段调用(宽松) 读者看到
\foobar{x} 抛错 不抛错,把 \foobar 用 #cc0000 画出来 红字
x_1_2(双下标) 抛错 不抛错 红字
\htmlClass{red}{x} 成功 — 红字

第三行是最阴的一种:严格调用完全成功,输出里却带错误色。只看异常的实现会把它判成健康。 所以判定看的是渲染产物里有没有错误色。

修复原则:不猜数学

一个区域会被渲染成红色时,按顺序尝试:

  1. 重新排版——公式本身没问题、只是框架错了(例如 $$ 开在段落中间),改成规范的 $$\n…\n$$ 块;
  2. 定点施救——只在两种有确定语义的情况下动:删掉渲染器不认识的命令的反斜杠(\foobar → foobar),或给重复的算子重新分组(x_1_2 → x_{1_2})。改完必须**重新渲染验证"严格通过且无红色"**才接受,所以永远不会出现"改一下只是把错误挪了个位置"的循环;
  3. 源码围栏——修不了的(括号不闭合、\begin 没有 \end)作为一个代码块原样给出。

数学永远不会被删掉。 最坏情况是读者看到公式源码(可复制、可恢复),而不是红字。猜结构性损坏等于替作者编造数学,这件事不做。

安装

node tools/install.mjs              # 复制到 profile 并注册 bundle
node tools/install.mjs --dry-run    # 只说会改什么
node tools/install.mjs --uninstall  # 从 bundle 列表移除

装完必须开新会话:bundle 栈是会话的 runner 进程启动时读的,当前会话不会重新加载。

手工挂载也可以:把本包放进 <profile>/node_modules/,再把名字加进 profile package.json 的 dsh.profile.bundles。

配置

在 profile 的 cordis.patch.yml 里按 id 设置:

- id: dsh-math-tex
  name: dsh-math-tex
  config:
    repair: auto
    logFile: C:/tmp/math-tex.log
选项 默认 说明
enabled true false 时两道防线都不挂载
promptRules full full / minimal / off,规则集大小
repair auto auto 改写 / check-only 只记日志不改写 / off 完全放行
tailBuffer 15 最少扣住多少个字符,避免完成记号被逐字泄漏
maxHold 600 单个未闭合区域最多等多少字符,超了就原样放行(宁可不管,不可卡住)
logFile '' 诊断日志路径;留空则只走 host logger
katexPath '' 指定 KaTeX 位置,默认自动按 profile 解析
macros {} 传给渲染器的宏,与客户端行为保持一致

未知键报错而不是静默忽略——写错的选项看起来像生效了,比没有这个选项更糟。

日志能证明它真的在工作

oracle: katex 0.16.47
prompt section "math-output-format" registered (full)
llm/stream hooked model=deepseek-flash mode=auto
guarded stream: 214 text delta(s) in, 231 out

最后一行是真实回合的判据:in 必须远大于 1(自测喂的假流是 1)。

测试

npm test
文件 测什么 断言
guard-test 判定与区域切分是否和渲染器一致 39
red-render-test 三类红字是否都能抓到 48
e2e-test 真实事故文本端到端是否可读 39
wire-test 流式输出是否等于整篇修复 97
fuzz-test 该性质在 236 种任意切分下是否成立 236
config-test 每个文档化选项是否真的有效 29
smoke-test 包是否按宿主的加载方式加载 22
installed-test profile 里那份安装副本(不是源码目录)能否加载并自修 11
regression-test 已修缺陷不再回归(用例逐字引自真实会话) 23
live-path-test 验收:装好的那份 + profile 的真实配置 + 宿主的挂载路径,坏答案是否真的变成干净文本 22

合计 10 个文件、566 条断言,约 4 秒,不需要网络。

最有说服力的是 fuzz-test:它要求真实事故文本的任意切分(1 字符一帧到 1600 字符一帧,外加随机切分)最终都重组为逐字相同的整篇修复结果。

live-path-test 是唯一一个把"装好的副本、profile 的配置、宿主的挂载路径、真实的增量流"接起来测的文件。它每个用例跑两遍:关掉修复时缺陷必须原样到达读者,开着时必须消失——前一遍是后一遍的前提,否则一个根本没坏的用例也会"通过"。

regression-test 是唯一断言"输出来的文本渲染后没有红色"的文件——其余文件盯的是判定与结构,它是最后一道"判定对了、但改完仍然红"的防线。把修复回退掉,它会红 4 条。

关于 test/fixtures/real-broken.txt

这份夹具复刻的是事故文本的结构(同一批缺陷形态:段落中间的 $$ 配错对、## 粘在行间公式里、未闭合的 \begin、结尾的代码围栏),内容已替换为与具体题目无关的通用推导,因此它测的是缺陷形态而不是任何一次具体事故。

想直接看一份文本是怎么被判定的:

node tools/inspect-fixture.mjs                    # 默认检查上面这份夹具
node tools/inspect-fixture.mjs path/to/text.md    # 换成任何一份文本

它会打印区域数量、被标记的区域及其原因、修复动作,以及修复后是否还有区域会被渲染成红色。

排查真实会话时另有三件工具:

node tools/find-red-session.mjs                   # 扫本机所有会话,列出还会渲染成红色的回答
node tools/inspect-session.mjs <session.jsonl>    # 单条会话逐条消息:区域、判定、守卫会怎么修
node tools/probe-region.mjs 'd\alpha=0'           # 把候选写法逐个送进渲染器,看哪种被接受

设计边界

  • 渲染器不可改:这是设计前提,不是让步。前端是打包好的 bundle,没有数学相关的 slot 可以替换。
  • 只做 host 侧:唯一能同时影响屏幕、会话记录和压缩的拦截点在 llm/stream。
  • 宁可不管,不可卡住:所有等待都有界(maxHold),超限即原样放行——那正是没有插件时的行为,因此安全。
  • 不改写非数学文本:不含 $ 和 \ 的文本逐字节直通。

文档

  • STATUS.md——现在装了什么、证明了什么、还差什么(交接用)。
  • docs/——判据的来源:FINDINGS.md(到底坏在哪)、KATEX-MEASUREMENTS.md(逐个用例的实测数字)、DESIGN.md(方案怎么定的、否掉了什么)。

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit a7adfab2270d

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout