UNPKG

llmxml

Version:

Convert between markdown and LLM-friendly pseudo-XML

106 lines (96 loc) 2.84 kB
import { ASTNode, TagNode, TextNode, HeadingNode } from '../types'; import logger from '../utils/logger'; /** * Transforms LLM-XML AST back into Markdown AST format */ export class LLMToMarkdownTransformer { /** * Convert LLM-XML AST to Markdown AST * * @param nodes - Array of LLM-XML AST nodes * @returns Array of Markdown AST nodes */ public transform(nodes: ASTNode[]): ASTNode[] { const result: ASTNode[] = []; for (const node of nodes) { const converted = this.convertNode(node); if (Array.isArray(converted)) { result.push(...converted); } else if (converted) { result.push(converted); } } return result; } /** * Convert a single LLM-XML AST node to Markdown AST node(s) * * @param node - LLM-XML AST node * @returns Single node, array of nodes, or null if node should be skipped */ private convertNode(node: ASTNode): ASTNode | ASTNode[] | null { switch (node.type) { case 'tag': return this.convertTagNode(node as TagNode); case 'text': return this.convertTextNode(node as TextNode); default: logger.warn('Unknown node type', { type: node.type }); return null; } } /** * Convert a tag node to Markdown AST nodes * * @param node - LLM-XML tag node * @returns Array of Markdown AST nodes */ private convertTagNode(node: TagNode): ASTNode[] { const result: ASTNode[] = []; // Create heading from tag const heading: HeadingNode = { type: 'heading', depth: parseInt(node.attributes?.hlevel || '1', 10), text: node.attributes?.title || node.name, children: [], }; result.push(heading); // Convert children if (node.children?.length) { for (const child of node.children) { if (child.type === 'tag') { // For tag nodes, ensure they have the correct heading level const tagNode = child as TagNode; if (!tagNode.attributes) { tagNode.attributes = {}; } // If hlevel is not set, use parent's level + 1 if (!tagNode.attributes.hlevel) { tagNode.attributes.hlevel = String(heading.depth + 1); } } const converted = this.convertNode(child); if (Array.isArray(converted)) { result.push(...converted); } else if (converted) { result.push(converted); } } } return result; } /** * Convert a text node to Markdown AST node * * @param node - LLM-XML text node * @returns Text node */ private convertTextNode(node: TextNode): TextNode { return { type: 'text', value: node.value || '', }; } } // Export a singleton instance export const llmToMarkdown = new LLMToMarkdownTransformer();