llm-md
Version:
Convert JSON to Markdown optimized for LLM consumption
157 lines (156 loc) • 5.31 kB
JavaScript
;
/**
* Analyzer for detecting optimal conversion strategy based on JSON structure
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Analyzer = void 0;
exports.analyze = analyze;
const utils_1 = require("./utils");
class Analyzer {
/**
* Analyze JSON data and determine the best conversion strategy
* @param data Data to analyze
* @param options Conversion options
* @returns Analysis result with recommended strategy
*/
analyze(data, options) {
// Handle primitives and null/undefined
if (data === null || data === undefined) {
return {
strategy: 'key-value',
depth: 0,
uniformity: 1,
};
}
if (typeof data !== 'object') {
return {
strategy: 'key-value',
depth: 0,
uniformity: 1,
};
}
// Check if array
if (Array.isArray(data)) {
return this.analyzeArray(data, options);
}
// Must be object
return this.analyzeObject(data, options);
}
/**
* Analyze an array to determine conversion strategy
* @param arr Array to analyze
* @param options Conversion options
* @returns Analysis result
*/
analyzeArray(arr, options) {
const maxDepth = options?.maxDepth ?? 10;
const tableSimilarityThreshold = options?.tableSimilarityThreshold ?? 0.8;
const maxTableColumns = options?.maxTableColumns ?? 15;
// Empty array
if (arr.length === 0) {
return {
strategy: 'numbered-list',
depth: 0,
uniformity: 1,
};
}
// Check if all items are objects (not arrays)
const allObjects = arr.every((item) => item !== null &&
typeof item === 'object' &&
!Array.isArray(item));
if (!allObjects) {
// Mixed types or arrays - use numbered list
return {
strategy: 'numbered-list',
depth: (0, utils_1.getMaxDepth)(arr, 0, maxDepth),
uniformity: 0,
};
}
// Calculate key similarity
const keySimilarity = (0, utils_1.calculateKeySimilarity)(arr);
const avgKeys = (0, utils_1.getAverageKeyCount)(arr);
// Check if suitable for table
if (keySimilarity >= tableSimilarityThreshold &&
arr.length >= 2 &&
avgKeys <= maxTableColumns) {
return {
strategy: 'table',
depth: (0, utils_1.getMaxDepth)(arr, 0, maxDepth),
uniformity: keySimilarity,
arrayInfo: {
length: arr.length,
keySimilarity,
averageKeys: avgKeys,
},
};
}
// Not uniform enough for table - use numbered list
return {
strategy: 'numbered-list',
depth: (0, utils_1.getMaxDepth)(arr, 0, maxDepth),
uniformity: keySimilarity,
arrayInfo: {
length: arr.length,
keySimilarity,
averageKeys: avgKeys,
},
};
}
/**
* Analyze an object to determine conversion strategy
* Hybrid strategy uses headers + code blocks + YAML for medium-depth objects
* @param obj Object to analyze
* @param options Conversion options
* @returns Analysis result
*/
analyzeObject(obj, options) {
const maxDepthLimit = options?.maxDepth ?? 10;
const yamlThreshold = options?.yamlThreshold ?? 15; // Increased to 15 for better hybrid mode usage
const depth = (0, utils_1.getMaxDepth)(obj, 0, maxDepthLimit);
const keyCount = Object.keys(obj).length;
// Simple, shallow object with few keys → Key-value list
if (depth <= 1 && keyCount <= 10) {
return {
strategy: 'key-value',
depth,
uniformity: 1,
};
}
// Very deep nesting (>15) → Pure YAML block
// Only use pure YAML for extremely complex structures
if (depth > yamlThreshold) {
return {
strategy: 'yaml-block',
depth,
uniformity: 0.5,
};
}
// Medium to deep (2-15) or complex structure → Hybrid strategy
// HybridConverter uses headers + code blocks + YAML for deeply nested sub-objects
// This provides better navigation with headers at top level
if (depth >= 2 || keyCount > 10) {
return {
strategy: 'hybrid',
depth,
uniformity: 0.7,
};
}
// Default to key-value for very simple objects
return {
strategy: 'key-value',
depth,
uniformity: 1,
};
}
}
exports.Analyzer = Analyzer;
/**
* Convenience function to analyze data
* @param data Data to analyze
* @param options Conversion options
* @returns Analysis result
*/
function analyze(data, options) {
const analyzer = new Analyzer();
return analyzer.analyze(data, options);
}