UNPKG

magnitude-core

Version:
128 lines (127 loc) 4.94 kB
/** * Convert observations to form that can be passed as BAML context, rendered as a custom XML-like structure. */ import { Image as BamlImage } from '@boundaryml/baml'; import { Image } from '@/memory/image'; //export type BamlRenderable = BamlImage | string; // export type BamlRenderable = { // role: '' // content: (BamlImage | string)[] // } async function buildXmlPartsRecursive(data, indentLevel, partsList, isInsideList = false) { const indent = ' '.repeat(indentLevel); if (data instanceof Image) { const bamlImg = await data.toBaml(); if (bamlImg) { partsList.push(bamlImg); } return; } if (data instanceof BamlImage) { partsList.push(data); return; } if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || data === null) { partsList.push(String(data)); // No XML escaping, direct string conversion return; } if (data === undefined) { return; // Undefined values are omitted } if (Array.isArray(data)) { // data.forEach(async (item, index) => { // await buildXmlPartsRecursive(item, indentLevel, partsList, true); // if (index < data.length - 1) { // partsList.push("\n"); // } // }); for (let index = 0; index < data.length; index++) { await buildXmlPartsRecursive(data[index], indentLevel, partsList, true); if (index < data.length - 1) { partsList.push("\n"); } } return; } if (typeof data === 'object' && data !== null) { const objectEntries = Object.entries(data).filter(([, val]) => val !== undefined); objectEntries.forEach(async ([key, value], entryIndex) => { const tagName = key; // Use key directly as tag name const currentValueParts = []; await buildXmlPartsRecursive(value, indentLevel + 1, currentValueParts, false); // Merge adjacent strings in currentValueParts for easier analysis const mergedValueParts = []; let currentStr = ""; for (const part of currentValueParts) { if (typeof part === 'string') { currentStr += part; } else { if (currentStr) mergedValueParts.push(currentStr); currentStr = ""; mergedValueParts.push(part); } } if (currentStr) mergedValueParts.push(currentStr); // Apply styling rules if (mergedValueParts.length === 1 && mergedValueParts[0] instanceof BamlImage) { partsList.push(`${indent}<${tagName}>`); partsList.push(mergedValueParts[0]); // The BamlImage partsList.push(`</${tagName}>`); } else if (mergedValueParts.length === 1 && typeof mergedValueParts[0] === 'string') { const contentStr = mergedValueParts[0]; if (contentStr.includes('\n')) { partsList.push(`${indent}<${tagName}>\n${contentStr}\n${indent}</${tagName}>`); } else { partsList.push(`${indent}<${tagName}>${contentStr}</${tagName}>`); } } else { // Empty content or multiple parts (complex/nested) partsList.push(`${indent}<${tagName}>\n`); mergedValueParts.forEach((part, partIdx) => { partsList.push(part); if (partIdx < mergedValueParts.length - 1) { partsList.push("\n"); } }); partsList.push(`\n${indent}</${tagName}>`); } if (entryIndex < objectEntries.length - 1) { partsList.push("\n"); // Newline between sibling XML elements } }); return; } throw new Error(`Object type not supported for LLM context: ${typeof data}`); } export async function renderXmlParts(data) { const rawList = []; await buildXmlPartsRecursive(data, 0, rawList); // Merge adjacent strings if (rawList.length === 0) { return []; } const mergedList = []; let currentString = ""; for (const item of rawList) { if (typeof item === 'string') { currentString += item; } else { // It's a BamlImage if (currentString.length > 0) { mergedList.push(currentString); } currentString = ""; // Reset accumulator mergedList.push(item); // Push the BamlImage } } // Add any trailing string if (currentString.length > 0) { mergedList.push(currentString); } return mergedList; }