ai-dash
v0.1.1
Published
A lightweight TypeScript toolkit for AI output parsing — extract code blocks from Markdown and parse streaming XML from LLM responses.
Maintainers
Readme
ai-dash
轻量级 TypeScript 工具库,用于解析 AI 输出 —— 从 Markdown 中提取代码块,以及流式解析 LLM 响应中的 XML 标签。
特性
- Markdown 代码块提取 — 提取、过滤和操作 fenced code blocks
- 流式 XML 解析器 — 增量解析 LLM 输出中嵌入的 XML 标签,支持回调实时处理
- 双格式输出 — 同时提供 ESM 和 CJS 构建产物
- 零依赖 — 无运行时依赖
- TypeScript 优先 — 内置完整类型定义
安装
pnpm install ai-dash或
npm install ai-dash使用
代码块工具
import { extractCodeBlocks, extractCodeBlocksByLangs, fastExtractCodeInFirstBlock, getCodeBlockLangs, removeCodeBlocks, extractJSON } from 'ai-dash';
const markdown = `
# My Document
\`\`\`javascript
console.log('Hello, World!');
\`\`\`
\`\`\`python
print("Hello from Python!")
\`\`\`
`;
// 提取所有代码块
const blocks = extractCodeBlocks(markdown);
// [{ lang: 'javascript', code: "console.log('Hello, World!');", startLine: 4, endLine: 6 }, ...]
// 按语言过滤(不区分大小写)
const jsBlocks = extractCodeBlocksByLangs(markdown, ['javascript']);
// 获取第一个匹配块的代码(无匹配时返回 undefined)
const firstCode = fastExtractCodeInFirstBlock(markdown, ['javascript', 'python']);
// 获取唯一语言列表
const langs = getCodeBlockLangs(markdown);
// ['javascript', 'python']
// 移除所有代码块,保留周围文本
const textOnly = removeCodeBlocks(markdown);
// 提取并解析第一个 JSON 代码块
const data = extractJSON(markdownWithJSON);流式 XML 解析器
解析 LLM 输出中嵌入的 XML 标签(如 <plan>、<file>、<function_call>)。解析器支持处理不完整的分块、自闭合标签、嵌套元素,以及属性值中包含 > 的情况。
import { createParserState, parseStreamXML, parseXML, getInnerText, fastExtractCodeInFirstXMLTag } from 'ai-dash';
// 一次性解析
const root = parseXML(content, new Set(['plan', 'file', 'progress']));
for (const child of root.children) {
if (child.type === 1 /* ELEMENT */) {
console.log(child.tag, child.attrs, child.completed);
}
}
// 流式解析(带回调)
const state = createParserState();
const opts = {
limitTagSet: new Set(['plan', 'file']),
onElementStart: (node) => console.log('open', node.tag),
onElementEnd: (node) => console.log('close', node.tag),
onTextProgress: (node, delta) => process.stdout.write(delta),
};
for await (const chunk of stream) {
parseStreamXML(state, chunk, opts);
}
state.status = 'end';
parseStreamXML(state, '', opts);
// 快速提取:获取第一个匹配 XML 标签的文本
const code = fastExtractCodeInFirstXMLTag(content, new Set(['code', 'file']));API 参考
代码块函数
| 函数 | 说明 |
|---|---|
| extractCodeBlocks(md) | 提取所有 fenced 代码块 → CodeBlock[] |
| extractCodeBlocksByLangs(md, langs) | 按语言过滤代码块(不区分大小写) |
| fastExtractCodeInFirstBlock(md, langs) | 获取第一个匹配块的代码 → string \| undefined |
| getCodeBlockLangs(md) | 获取代码块中的唯一语言列表 |
| removeCodeBlocks(md) | 移除所有代码块,返回剩余文本 |
| extractJSON(md) | 解析第一个 ```json 块为 JSON 对象 |
CodeBlock
interface CodeBlock {
lang: string;
langExtra?: string; // 语言后的附加信息(如路径、标题)
code: string;
startLine: number;
endLine: number;
}流式 XML 函数
| 函数 | 说明 |
|---|---|
| createParserState() | 创建流式解析器状态 |
| parseStreamXML(state, chunk, opts?) | 向解析器输入一个分块 |
| parseXML(content, limitTagSet?) | 一次性解析(便捷封装) |
| fastExtractCodeInFirstXMLTag(content, limitTagSet) | 获取第一个匹配标签的文本 → string |
| getInnerText(node) | 递归获取节点的所有文本内容 |
| getOnlyText(node) | 仅获取直接文本子节点(跳过嵌套元素) |
XMLParserOptions
interface XMLParserOptions {
limitTagSet?: Set<string>; // 仅解析指定标签为 XML,其余视为文本
onElementStart: (node: XMLElement) => void;
onElementEnd: (node: XMLElement) => void;
onTextStart: (node: XMLText, delta: string) => void;
onTextProgress: (node: XMLText, delta: string) => void;
onTextEnd: (node: XMLText, delta: string) => void;
}开发
# 安装依赖
pnpm install
# 构建(ESM + CJS)
pnpm build
# 类型检查(不输出文件)
pnpm typecheck
# 运行测试
pnpm test
# 监听模式运行测试
pnpm test:watch
# 测试覆盖率
pnpm test:coverage
# 代码检查
pnpm lint项目结构
src/
├── index.ts # 公共导出
├── codeBlock.ts # Markdown 代码块工具
└── parseStreamXML.ts # 流式 XML 解析器
tests/
├── testUtils.ts # 共享测试工具(loadCase, runStreamParser)
├── codeBlock.test.ts # 代码块测试
├── codeBlock-cases/ # 代码块测试用的 Markdown fixture
├── parseStreamXML.test.ts # XML 解析器测试(一次性 + 流式)
└── parseStreamXML-cases/ # XML 解析器测试用的 fixture许可证
MIT © Forrest
