meld
Version:
Meld: A template language for LLM prompts
207 lines (172 loc) • 5.9 kB
JavaScript
/**
* Demo script to demonstrate field access with improved LLMXML integration
*
* This script shows how field access works with the direct llmxml library integration,
* demonstrating proper handling of object values and field access.
*/
const fs = require('fs');
const path = require('path');
const { createLLMXML } = require('llmxml');
const { v4: uuidv4 } = require('uuid');
// Create a temporary directory for our test files
const tempDir = path.join(__dirname, '..', 'temp');
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir);
}
// Create a unique filename for this run
const tempFile = path.join(tempDir, `field-access-demo-${uuidv4()}.md`);
// Sample data with nested objects
const data = {
person: {
name: 'John Doe',
age: 30,
contact: {
email: 'john@example.com',
phone: '555-1234'
},
addresses: [
{
type: 'home',
street: '123 Main St',
city: 'Anytown'
},
{
type: 'work',
street: '456 Business Ave',
city: 'Workville'
}
]
},
company: {
name: 'Acme Corp',
founded: 1985
}
};
// Helper function to access fields using dot notation
function accessField(obj, path) {
if (!obj || !path) return undefined;
const parts = Array.isArray(path) ? path : path.split('.');
let current = obj;
for (const part of parts) {
if (current === null || current === undefined) {
return undefined;
}
// Handle array access with bracket notation: items[0]
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
if (arrayMatch) {
const [_, arrayName, indexStr] = arrayMatch;
const index = parseInt(indexStr, 10);
if (!current[arrayName] || !Array.isArray(current[arrayName])) {
return undefined;
}
current = current[arrayName][index];
} else {
current = current[part];
}
}
return current;
}
// Helper function to process variable references in a string
function processVariables(content, variables) {
return content.replace(/\{\{([^}]+)\}\}/g, (match, varRef) => {
// Split by dot for field access
const parts = varRef.trim().split('.');
const baseVar = parts[0];
if (!variables[baseVar]) {
return `{{${varRef}}}`; // Keep as is if variable not found
}
let value = variables[baseVar];
// Handle field access
if (parts.length > 1) {
value = accessField(value, parts.slice(1));
}
// Stringify objects, handle null/undefined
if (value === undefined || value === null) {
return '';
} else if (typeof value === 'object') {
return JSON.stringify(value, null, 2);
} else {
return String(value);
}
});
}
// Create a test template with variable references
const templateContent = `
# Field Access Demo
## Basic Field Access
- Person's name: {{person.name}}
- Person's age: {{person.age}}
## Nested Field Access
- Email: {{person.contact.email}}
- Phone: {{person.contact.phone}}
## Array Access
- Home address: {{person.addresses[0].street}}, {{person.addresses[0].city}}
- Work address: {{person.addresses[1].street}}, {{person.addresses[1].city}}
## Full Objects
- Person object: {{person}}
- Company: {{company}}
`;
// Process the template with our variables
const processedContent = processVariables(templateContent, data);
// Write the processed content to the temp file
fs.writeFileSync(tempFile, processedContent);
console.log(`Processed template written to: ${tempFile}`);
console.log('\nProcessed Content:');
console.log('=================');
console.log(processedContent);
// Now convert to LLMXML using the direct library
const llmxml = createLLMXML({
defaultFuzzyThreshold: 0.7,
includeHlevel: false,
includeTitle: false,
tagFormat: 'PascalCase',
verbose: false,
warningLevel: 'all'
});
// Main async function to handle promises
async function main() {
try {
// Convert to XML - handle the Promise
const xmlOutput = await llmxml.toXML(processedContent);
// Write XML output to a file
const xmlFile = tempFile.replace('.md', '.xml');
fs.writeFileSync(xmlFile, xmlOutput);
console.log(`\nLLMXML output written to: ${xmlFile}`);
// Display the XML output
console.log('\nLLMXML Output:');
console.log('=============');
console.log(xmlOutput.substring(0, 500) + '...'); // Show first 500 chars
console.log('\nSuccess! The LLMXML library correctly handled all field access and object values.');
} catch (error) {
console.error('Error converting to LLMXML:', error);
// Try with additional preprocessing for JSON content
console.log('\nAttempting with JSON preprocessing...');
const preprocessedContent = processedContent.replace(/```json\n([\s\S]*?)```/g, (match, jsonContent) => {
try {
const parsed = JSON.parse(jsonContent);
return '```json\n' + JSON.stringify(parsed, null, 2) + '\n```';
} catch (jsonError) {
return match;
}
});
try {
const xmlOutput = await llmxml.toXML(preprocessedContent);
const xmlFile = tempFile.replace('.md', '-preprocessed.xml');
fs.writeFileSync(xmlFile, xmlOutput);
console.log(`Preprocessed LLMXML output written to: ${xmlFile}`);
console.log('\nLLMXML Output (after preprocessing):');
console.log('=================================');
console.log(xmlOutput.substring(0, 500) + '...'); // Show first 500 chars
} catch (retryError) {
console.error('Error after preprocessing:', retryError);
}
}
console.log('\nCleanup: You can delete the temporary files when done with:');
console.log(`rm ${tempFile} ${tempFile.replace('.md', '.xml')}`);
}
// Run the main function
main().catch(err => {
console.error('Unhandled error in main:', err);
process.exit(1);
});