@ cacheplane/ 部分标记
流出 Markdown 解析器以获取不完整的输入 .
当 AI 模型、 代理、 编辑器或网络流 释放 Markdown a 时使用它 块一次。 标准Markdown分解器是围绕完整的文档构建的. 此解析器会让您在文档还在时打入 Markdown 树 到达,并保持节点对象身份的稳定,随着后方块更新树.
安装
npm install @cacheplane/partial-markdown
运行时间和包装 :
- 节点
>=20 - 包含类型脚本声明
- 无害环境管理和CJS捆绑在一起
- 零运行时间依赖关系
- 标出无副作用
30- 第二实例
import { createPartialMarkdownParser } from '@cacheplane/partial-markdown';
const parser = createPartialMarkdownParser();
parser.push('# Hello\n\nThis is **bold');
parser.push('** text.');
parser.finish();
const heading = parser.getByPath('/children/0');
const paragraph = parser.getByPath('/children/1');
console.log(heading);
// { type: 'heading', level: 1, status: 'complete', ... }
console.log(paragraph);
// { type: 'paragraph', status: 'complete', children: [...] }
解析器不只因为输入结束 中重点,中列表,
中间桌,或中线. 每个公共节点都带着
status: 'pending' | 'streaming' | 'complete',所以UI可以使存在的东西
现在让节点解决.
何时使用
使用 @cacheplane/partial-markdown 当您需要时:
- Render 流出 LLM 答覆后, 不重新解析每个文件的全部文件 块.
- 构建聊天、报告、代理日志、笔记本或编辑器视图 马克倒地.
- 保存反应器、角质、固态或自定义成像器记忆器.
- 随着模型输出的增长,跟踪引用、任务列表和表格.
- 将活的剖析树转换成简单的 JS快照而不替换 没有改变的子树.
它不是一个完整的通用马克遵守套装。 它有意支持 Markdown 构造最有利于 AI 生成的内容和开发者创建 造物师.
心理模式
剖析器构建出一幅克尼克勒克德克勒克德克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克勒克克勒克克克克克勒克勒克克克克克克克克克克克克克克克克克克勒克克克克克克克克克克克克克克克克克克克克克. 节点在 包括乐观的开放线预测。 每个 节点有:
id:在活节点中独一无二的非负数特征.type:Markdown节点类型.status数字 :pending, (中文(简体) ),streaming,或complete。 。 。 .parent: 父节点或null。 。 。 .index:大多数节点的兄弟姐妹指数.
文档、块和内置容器节点曝光 children叶子节点曝光
直接内容,例如: text, (中文(简体) ), url, (中文(简体) ), language,或 alt。 。 。 .
文档根也暴露:
citations: Map<string, CitationDefinition>
linkDefinitions: Map<string, LinkDefinition>
引文和链接参考定义从可见块中解出 树并收于根上.
继续节点保存他们的身份证 在明确的相同操作语法中 重新解释 替换者只有在 旧的化身完成。 独立解析器实例不需要指定 匹配ID,所以不要使用数字ID作为跨parser身份合同.
Push-Style API (英语)
使用push-style API来进行流式UI和长生节点引用.
import {
createPartialMarkdownParser,
materialize,
} from '@cacheplane/partial-markdown';
const parser = createPartialMarkdownParser();
for await (const chunk of llmStream) {
const events = parser.push(chunk);
for (const event of events) {
if (event.type === 'value-updated') {
// This is the canonical node object also exposed through parser.root.
}
}
const snapshot = materialize(parser.root);
render(snapshot);
}
parser.finish();
push(chunk) 财务报告和审定财务报表 finish() 返回时 ParseEvent[]数字 :
interface ParseEvent {
type: 'node-created' | 'value-updated' | 'node-completed';
node: MarkdownNode;
delta?: string;
}
活动及 parser.root 共享相同的条形节点对象; 解析器这样做
不创建单独的仅事件投影图。 当开线投影时
匹配已承诺的剖析,其节点对象贯穿到已承诺
而不是被替换。 A 类 node-completed 事件可以指: 节点
; 活动节点
指从当前根可以到达的对象.
事件命令是决定性的。 退役节点完成时间已过 创建, 然后更新值; 这些阶段中的倒转是稳定的树 秩序。 秩序。 保留节点的仅保存状态完成按预序排出.
路径查询
getByPath() 接受JSON协议 向上指向类似路径 children数字 :
parser.getByPath(''); // root
parser.getByPath('/children/0'); // first top-level block
parser.getByPath('/children/1/children/0');
缺少路径返回 null。 。 。 .
拉- 平面 API
使用拉动风格的 API , 当您想要不可移动的 parser 状态时, 如 减速器 , 撤销/重做堆栈,确定性测试,或国家机器集成.
import { create, push, finish, resolve } from '@cacheplane/partial-markdown';
let state = create();
state = push(state, '# Title\n\nParagraph');
state = finish(state);
const tree = resolve(state);
每个呼叫返回一个新的 StreamState。拉式状态也会暴露解析器
通过警告 state.warnings。 。 。 .
结构共享快照
materialize(node) 将 cononical 解析器节点图转换为平原
JavaScript 对象图. 它使用一个 WeakMap 以节点身份键入缓存, 所以
不变子树在呼叫中返回相同的已实现对象引用 .
当犬形节点改变位置时,那个节点和受影响的祖先
路径被再现实化,而无关的子树则保留它们的引用.
const before = materialize(parser.root);
parser.push('\n- New item');
const after = materialize(parser.root);
这在渲染出每个动画框或每个流块时都是有用的. 比较参考文献的消费者只重新提供实际变化的子树.
支持的语法
块级
- 文档
- 段 次 页 次
- ATX标题,
#结束###### - 块名
- 无序列表 :
-, (中文(简体) ),*, (中文(简体) ),+ - 有序名单:
1. - 任务列表 :
- [x], (中文(简体) ),- [ ] - 栅栏码块
- 缩进代码块
- 专题间断
- GFM 表格
- Pandoc 类引用定义
- 链接参考定义
- 显示数学 :
$$..$$财务报告和审定财务报表\[..\] - 原始 HTML 块
内入
- 文本
- 强调:
*x*, (中文(简体) ),_x_ - 强:
**x**, (中文(简体) ),__x__ - 切换:
~~x~~ - 内置代码
- 链接
- 自动链接
- 图像
- 软和硬行间断
- 引用参考文献
- 链接引用 :
[text][label], (中文(简体) ),[label][],以及[label] - 内置数学 :
$..$财务报告和审定财务报表\(..\) - 原始 HTML 标签、 注释、 声明、 处理指令和 CDATA
爱尔
LLM 语言 Markdown是有用的,但很少是原始的. 解说者是故意容忍的 其中AI输出通常不同于严格的Markdown预期.
嵌入式列表
嵌入式列表项目至少缩入2个时被确认 横过父标记栏或内容栏的列,以多者为准 宽容.
- Item 1
- Sub 1.1
- Sub 1.2
- Sub 1.2.1
- Item 2
标签提前到下一个四栏标签站。 支持混合标签和空格.
每个 MarkdownListNode 披露咨询 markerCol 财务报告和审定财务报表 contentCol 用于
了解布局的消费者.
表格
| Header | Aligned |
| :--- | ---: |
| left | right |
对齐行被消耗而不作为节点保留. 对齐数据是
存储于 MarkdownTableNode.alignments。身体行短于标题
加; 溢出行被截断并产生一个 table_overflow 警告进入
拉式状态.
引用
Some claim.[^src1]
[^src1]: Source title <https://example.com>
引用参考资料改为 citation-reference 内置节点。 定义为:
升入 root.citations. 参考文献使用第一触即发 1 个指数
秩序。 秩序。 如果一个定义在引用后到达, 现有的引用节点
翻转 resolved 从 false 改为 true 已经就位.
链接引用
Read [the guide][docs] or [docs].
[docs]: https://example.com "Docs"
完全,崩溃,快捷键引用链接变为 link-reference 内入
节点。 定义取消为 root.linkDefinitions 键入
正常标签 。 如果一个定义在提及之后到达,则现有的
引用节点变异设置为 resolved: true, (中文(简体) ), url,以及 title。 。 。 .
数学
Inline math uses $a+b$ or \(a+b\).
$$
\sum_i x_i
$$
内在数学变成 math-inline 不透明的节点 text (单位:千美元) delimiter
字段。 内含的数学在承诺时被识别,其中
匹配解析器现有的线性缓冲内含解析模型.
说明: 内置数学( E)
$..$, (中文(简体) ),\(..\)当含有下列物质时,即为犯罪: 线程完成——内置节点与status: 'complete'显示 数学( e)$$..$$, (中文(简体) ),\[..\]) 有完整字符的溪流字符streaming → complete弧并保留了节点身份横跨推.
显示数学变为 math-display 块节点. $$..$$ 财务报告和审定财务报表 \[..\]
分隔器家族默认启用,可以独立禁用 :
const parser = createPartialMarkdownParser({
math: { dollar: false, bracket: true },
});
原始 HTML 语言
Use <kbd>Esc</kbd>.
<details>
<summary>More</summary>
Raw HTML is captured as authored.
内置 HTML 变为 html-inline 节点 raw 来源. HTML 块变为
html-block 节点 raw 来源和 htmlKind,普通马克块类型
打开了节点.
2. 安全——在提供之前进行消毒
raw 是未卫生的 HTML 源。 直接通过
dangerouslySetInnerHTML (或任何等同物)是未信任的 XSS 风险
模型输出。 卫生第一:
import DOMPurify from 'dompurify';
if (node.type === 'html-block' || node.type === 'html-inline') {
return (
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(node.raw) }} />
);
}
对于无法保证在其中进行消毒的不可信流出输出 每个渲染,将 HTML 节点改为所逃出文本 :
if (node.type === 'html-block' || node.type === 'html-inline') {
return <code>{node.raw}</code>;
}
服务器侧/节点消费者可以使用 sanitize-html
或者说 rehype-sanitize 联 合 国
同样的目的.
保证
@cacheplane/partial-markdown 保证:
- 截断的Markdown可解释为在建输入.
- 推力式活动
parser.root暴露一个犬科公共节点图. - 公共推式节点对象身份跨推式稳定,包括 当匹配的预测节点承诺时.
- 数字节点ID是非负数的,在活节点中是独一无二的 节点保持他们的身份证。 在明确的相同操作语法中 重新解释 替换者只有在 旧的化身完成。 独立解析器实例不需要指定 匹配身份标识.
- 事件订单是决定性的,在替换前完成 创建和更新.
- 节点状态使用相同的
pending | streaming | complete寿命周期为@cacheplane/partial-json。 。 。 . materialize()为未改变的子树保留引用.- 引用参考文献时保持稳定身份
resolved旗帜改变. - 链接引用时保持稳定身份
resolved, (中文(简体) ),url,或title字段变化. - 显示数学块节点, 保持稳定的身份, 因其文本长出行 线条.
- HTML 块结保持稳定身份
raw逐行增长. - 任务列表项目节点在检查状态变化时保持稳定的身份.
- 表格,行和单元格引用在后行到达时保持稳定.
- 引用定义按插入顺序由一触即发引用存储 指数.
- 链接定义以正态-标签插入顺序存储.
限制
此软件包目前不支持 :
- 多行链接定义标题
- 列表项目中的链接定义
- 块或列表项目中的原始 HTML 块
- 自定义标记下扩展名
- 完全遵守通用程序
对于不支持的语法,倾向于渲染原始文本节点内容或应用 溪流完成后分开处理后通行证.
警告模式
拉风 StreamState 包含 warnings: MarkdownWarning[]。 。 。 .
当前警告代码 :
'unterminated_construct'
'unmatched_closer'
'invalid_link'
'unknown_construct'
'unresolved_citation_ref'
'unused_citation_def'
'duplicate_citation_def'
'table_overflow'
'malformed_table_alignment'
'unresolved_link_ref'
'unused_link_def'
'duplicate_link_def'
'unterminated_math'
'unterminated_html'
警告用于诊断、记录和可选的UI支付. 它们不是作为例外提出的.
节点形状
interface MarkdownNodeBase {
readonly id: number;
readonly type: MarkdownNodeType;
status: 'pending' | 'streaming' | 'complete';
parent: MarkdownNode | null;
index: number | null;
}
已输入容器节点 children 数组 :
interface MarkdownDocumentNode extends MarkdownNodeBase {
readonly type: 'document';
children: MarkdownBlockNode[];
citations: Map<string, CitationDefinition>;
linkDefinitions: Map<string, LinkDefinition>;
}
interface MarkdownParagraphNode extends MarkdownNodeBase {
readonly type: 'paragraph';
children: MarkdownInlineNode[];
}
interface MarkdownListNode extends MarkdownNodeBase {
readonly type: 'list';
ordered: boolean;
start: number | null;
tight: boolean;
markerCol: number;
contentCol: number;
children: MarkdownListItemNode[];
}
叶节带出直接内容:
interface MarkdownTextNode extends MarkdownNodeBase {
readonly type: 'text';
text: string;
}
interface MarkdownCodeBlockNode extends MarkdownNodeBase {
readonly type: 'code-block';
variant: 'fenced' | 'indented';
language: string;
text: string;
}
interface MarkdownMathDisplayNode extends MarkdownNodeBase {
readonly type: 'math-display';
text: string;
delimiter: '$$' | '\\[\\]';
}
interface MarkdownHtmlBlockNode extends MarkdownNodeBase {
readonly type: 'html-block';
raw: string;
htmlKind: 1 | 2 | 3 | 4 | 5 | 6 | 7;
}
interface MarkdownMathInlineNode extends MarkdownNodeBase {
readonly type: 'math-inline';
text: string;
delimiter: '$' | '\\(\\)';
}
interface MarkdownHtmlInlineNode extends MarkdownNodeBase {
readonly type: 'html-inline';
raw: string;
}
interface MarkdownImageNode extends MarkdownNodeBase {
readonly type: 'image';
url: string;
title: string;
alt: string;
}
引用定义:
interface CitationDefinition {
id: string;
index: number;
children: MarkdownInlineNode[];
status: 'pending' | 'streaming' | 'complete';
}
链接定义 :
interface LinkDefinition {
id: string;
label: string;
url: string;
title: string;
status: 'pending' | 'streaming' | 'complete';
}
类型警卫
import {
isDocumentNode,
isParagraphNode,
isHeadingNode,
isBlockquoteNode,
isListNode,
isListItemNode,
isCodeBlockNode,
isMathDisplayNode,
isHtmlBlockNode,
isThematicBreakNode,
isTableNode,
isTableRowNode,
isTableCellNode,
isTextNode,
isEmphasisNode,
isStrongNode,
isStrikethroughNode,
isInlineCodeNode,
isMathInlineNode,
isHtmlInlineNode,
isLinkNode,
isAutolinkNode,
isImageNode,
isSoftBreakNode,
isHardBreakNode,
isCitationReferenceNode,
isLinkReferenceNode,
isCompleteNode,
} from '@cacheplane/partial-markdown';
常见模式
流化反应
const parser = createPartialMarkdownParser();
for await (const chunk of llmStream) {
parser.push(chunk);
render(parser.root);
}
parser.finish();
斜体顶层块
parser.push(chunk);
for (const block of parser.root?.children ?? []) {
if (block.type === 'heading') {
renderHeading(block);
}
}
渲染任务列表
for (const block of parser.root?.children ?? []) {
if (block.type !== 'list') continue;
for (const item of block.children) {
if (item.task) {
renderCheckbox(item.task.checked);
}
}
}
查找引用
const def = parser.root?.citations.get('src1');
if (def && def.status === 'complete') {
renderCitation(def.index, def.children);
}
检测文档结束
parser.finish();
if (parser.root?.status === 'complete') {
const tree = materialize(parser.root);
}
解决问题
parser.root 实值 null。 。 。 .
尚未创建文档根 。 先推个非空块.
还有一段 streaming。 。 。 .
解析器可能仍然持有当前行打开. 按新行或调用
finish() 当溪出后.
引文参考未解决.
匹配定义尚未到来 。 如果定义在后面流入,则
相同的引用节点翻转 resolved 改为 true。 。 。 .
链接引用尚未解决 .
匹配定义尚未到来 。 如果定义在后面流入,则
相同的引用节点翻转 resolved 改为 true 并收到 url 财务报告和审定财务报表 title。 。 。 .
表格行的单元格比预期的少或多.
比标题短的行被加上. 过度流动的细胞被截断
报告为 table_overflow 在拉式警告中.
Markdown 构造成文本 . 它可能是不支持的语法. 见上文限制部分.
从 LLM 渲染出 Raw HTML 是不安全的 .
解析器保留了 HTML 源, 但并未将其消毒 。 卫生 raw 在此之前
渲染为 HTML,或将其变成未信任输出的隐藏文本.
API 参考
// Push-style
createPartialMarkdownParser(options?: PartialMarkdownParserOptions): PartialMarkdownParser
interface PartialMarkdownParser {
push(chunk: string): ParseEvent[];
finish(): ParseEvent[];
readonly root: MarkdownDocumentNode | null;
getByPath(path: string): MarkdownNode | null;
}
materialize(node: MarkdownNode | null): unknown;
// Pull-style
create(options?: PartialMarkdownParserOptions): StreamState;
push(state: StreamState, chunk: string): StreamState;
finish(state: StreamState): StreamState;
resolve(state: StreamState): unknown;
导出类型守护 :
isDocumentNode, isParagraphNode, isHeadingNode, isBlockquoteNode,
isListNode, isListItemNode, isCodeBlockNode, isThematicBreakNode,
isTableNode, isTableRowNode, isTableCellNode,
isTextNode, isEmphasisNode, isStrongNode, isStrikethroughNode,
isInlineCodeNode, isMathInlineNode, isMathDisplayNode,
isHtmlInlineNode, isHtmlBlockNode,
isLinkNode, isAutolinkNode, isImageNode,
isSoftBreakNode, isHardBreakNode,
isCitationReferenceNode, isLinkReferenceNode, isCompleteNode
导出类型 :
MarkdownNode, MarkdownDocumentNode, MarkdownBlockNode, MarkdownInlineNode,
MarkdownParagraphNode, MarkdownHeadingNode, MarkdownBlockquoteNode,
MarkdownListNode, MarkdownListItemNode, MarkdownCodeBlockNode,
MarkdownMathDisplayNode,
MarkdownHtmlBlockNode,
MarkdownThematicBreakNode, MarkdownTableNode, MarkdownTableRowNode,
MarkdownTableCellNode, MarkdownCitationReferenceNode,
MarkdownLinkReferenceNode,
MarkdownTextNode, MarkdownEmphasisNode, MarkdownStrongNode,
MarkdownStrikethroughNode, MarkdownInlineCodeNode, MarkdownMathInlineNode,
MarkdownHtmlInlineNode,
MarkdownLinkNode,
MarkdownAutolinkNode, MarkdownImageNode, MarkdownSoftBreakNode,
MarkdownHardBreakNode,
StreamStatus, StreamState, StreamError, ParseEvent, ParseEventType,
MarkdownWarning, Alignment, CitationDefinition, LinkDefinition,
PartialMarkdownParserOptions, ResolvedParserOptions,
AstNode, AstNodeKind, ParseMode, HtmlBlockKind
更改日志
见 CHANGELOG.md。 。 。 .
许可证
麻省理工学院