@karmaniverous/jeeves-watcher 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -8,6 +8,7 @@ var radash = require('radash');
8
8
  var node_crypto = require('node:crypto');
9
9
  var node_fs = require('node:fs');
10
10
  var ignore = require('ignore');
11
+ var jsonmap = require('@karmaniverous/jsonmap');
11
12
  var Handlebars = require('handlebars');
12
13
  var dayjs = require('dayjs');
13
14
  var hastUtilToMdast = require('hast-util-to-mdast');
@@ -18,7 +19,6 @@ var unified = require('unified');
18
19
  var chokidar = require('chokidar');
19
20
  var cosmiconfig = require('cosmiconfig');
20
21
  var zod = require('zod');
21
- var jsonmap = require('@karmaniverous/jsonmap');
22
22
  var googleGenai = require('@langchain/google-genai');
23
23
  var pino = require('pino');
24
24
  var uuid = require('uuid');
@@ -657,6 +657,251 @@ class GitignoreFilter {
657
657
  }
658
658
  }
659
659
 
660
+ /**
661
+ * @module rules/templates
662
+ * Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
663
+ */
664
+ /**
665
+ * Resolve `${template.vars}` in a value against the given attributes.
666
+ *
667
+ * @param value - The value to resolve.
668
+ * @param attributes - The file attributes for variable lookup.
669
+ * @returns The resolved value.
670
+ */
671
+ function resolveTemplateVars(value, attributes) {
672
+ if (typeof value !== 'string')
673
+ return value;
674
+ return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
675
+ const current = radash.get(attributes, varPath);
676
+ if (current === null || current === undefined)
677
+ return '';
678
+ return typeof current === 'string' ? current : JSON.stringify(current);
679
+ });
680
+ }
681
+ /**
682
+ * Resolve all template variables in a `set` object.
683
+ *
684
+ * @param setObj - The key-value pairs to resolve.
685
+ * @param attributes - The file attributes for variable lookup.
686
+ * @returns The resolved key-value pairs.
687
+ */
688
+ function resolveSet(setObj, attributes) {
689
+ const result = {};
690
+ for (const [key, value] of Object.entries(setObj)) {
691
+ result[key] = resolveTemplateVars(value, attributes);
692
+ }
693
+ return result;
694
+ }
695
+
696
+ /**
697
+ * @module rules/apply
698
+ * Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
699
+ */
700
+ /**
701
+ * Create the lib object for JsonMap transformations.
702
+ *
703
+ * @param configDir - Optional config directory for resolving relative file paths in lookups.
704
+ * @returns The lib object.
705
+ */
706
+ function createJsonMapLib(configDir, customLib) {
707
+ // Cache loaded JSON files within a single applyRules invocation.
708
+ const jsonCache = new Map();
709
+ const loadJson = (filePath) => {
710
+ const resolvedPath = configDir ? node_path.resolve(configDir, filePath) : filePath;
711
+ if (!jsonCache.has(resolvedPath)) {
712
+ const raw = node_fs.readFileSync(resolvedPath, 'utf-8');
713
+ jsonCache.set(resolvedPath, JSON.parse(raw));
714
+ }
715
+ return jsonCache.get(resolvedPath);
716
+ };
717
+ return {
718
+ split: (str, separator) => str.split(separator),
719
+ slice: (arr, start, end) => arr.slice(start, end),
720
+ join: (arr, separator) => arr.join(separator),
721
+ toLowerCase: (str) => str.toLowerCase(),
722
+ replace: (str, search, replacement) => str.replace(search, replacement),
723
+ get: (obj, path) => radash.get(obj, path),
724
+ /**
725
+ * Load a JSON file (relative to configDir) and look up a value by key,
726
+ * optionally drilling into a sub-path.
727
+ *
728
+ * @param filePath - Path to a JSON file (resolved relative to configDir).
729
+ * @param key - Top-level key to look up.
730
+ * @param field - Optional dot-path into the looked-up entry.
731
+ * @returns The resolved value, or null if not found.
732
+ */
733
+ lookupJson: (filePath, key, field) => {
734
+ const data = loadJson(filePath);
735
+ const entry = data[key];
736
+ if (entry === undefined || entry === null)
737
+ return null;
738
+ if (field)
739
+ return radash.get(entry, field) ?? null;
740
+ return entry;
741
+ },
742
+ /**
743
+ * Map an array of keys through a JSON lookup file, collecting a specific
744
+ * field from each matching entry. Non-matching keys are silently skipped.
745
+ * Array-valued fields are flattened into the result.
746
+ *
747
+ * @param filePath - Path to a JSON file (resolved relative to configDir).
748
+ * @param keys - Array of top-level keys to look up.
749
+ * @param field - Dot-path into each looked-up entry.
750
+ * @returns Flat array of resolved values.
751
+ */
752
+ mapLookup: (filePath, keys, field) => {
753
+ if (!Array.isArray(keys))
754
+ return [];
755
+ const data = loadJson(filePath);
756
+ const results = [];
757
+ for (const k of keys) {
758
+ if (typeof k !== 'string')
759
+ continue;
760
+ const entry = data[k];
761
+ if (entry === undefined || entry === null)
762
+ continue;
763
+ const val = radash.get(entry, field);
764
+ if (val === undefined || val === null)
765
+ continue;
766
+ if (Array.isArray(val)) {
767
+ for (const item of val) {
768
+ results.push(item);
769
+ }
770
+ }
771
+ else {
772
+ results.push(val);
773
+ }
774
+ }
775
+ return results;
776
+ },
777
+ ...customLib,
778
+ };
779
+ }
780
+ /**
781
+ * Load custom JsonMap lib functions from file paths.
782
+ *
783
+ * Each module should default-export an object of functions,
784
+ * or use named exports. Only function-valued exports are merged.
785
+ *
786
+ * @param paths - File paths to custom lib modules.
787
+ * @param configDir - Directory to resolve relative paths against.
788
+ * @returns The merged custom lib functions.
789
+ */
790
+ async function loadCustomMapHelpers(paths, configDir) {
791
+ const merged = {};
792
+ for (const p of paths) {
793
+ const resolved = node_path.resolve(configDir, p);
794
+ const mod = (await import(resolved));
795
+ const fns = typeof mod.default === 'object' && mod.default !== null
796
+ ? mod.default
797
+ : mod;
798
+ for (const [key, val] of Object.entries(fns)) {
799
+ if (typeof val === 'function') {
800
+ merged[key] = val;
801
+ }
802
+ }
803
+ }
804
+ return merged;
805
+ }
806
+ /**
807
+ * Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
808
+ *
809
+ * Rules are evaluated in order; later rules override earlier ones.
810
+ * If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
811
+ * and map output overrides set output on conflict.
812
+ *
813
+ * @param compiledRules - The compiled rules to evaluate.
814
+ * @param attributes - The file attributes to match against.
815
+ * @param namedMaps - Optional record of named JsonMap definitions.
816
+ * @param logger - Optional logger for warnings (falls back to console.warn).
817
+ * @param templateEngine - Optional template engine for rendering content templates.
818
+ * @param configDir - Optional config directory for resolving .json map file paths.
819
+ * @returns The merged metadata and optional rendered content.
820
+ */
821
+ async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir, customMapLib) {
822
+ // JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
823
+ // Our helper functions accept multiple args, which JsonMap supports at runtime.
824
+ const lib = createJsonMapLib(configDir, customMapLib);
825
+ let merged = {};
826
+ let renderedContent = null;
827
+ const log = logger ?? console;
828
+ for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
829
+ if (validate(attributes)) {
830
+ // Apply set resolution
831
+ const setOutput = resolveSet(rule.set, attributes);
832
+ merged = { ...merged, ...setOutput };
833
+ // Apply map transformation if present
834
+ if (rule.map) {
835
+ let mapDef;
836
+ // Resolve map reference
837
+ if (typeof rule.map === 'string') {
838
+ if (rule.map.endsWith('.json') && configDir) {
839
+ // File path: load from .json file
840
+ try {
841
+ const mapPath = node_path.resolve(configDir, rule.map);
842
+ const raw = node_fs.readFileSync(mapPath, 'utf-8');
843
+ mapDef = JSON.parse(raw);
844
+ }
845
+ catch (error) {
846
+ log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
847
+ continue;
848
+ }
849
+ }
850
+ else {
851
+ mapDef = namedMaps?.[rule.map];
852
+ if (!mapDef) {
853
+ log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
854
+ continue;
855
+ }
856
+ }
857
+ }
858
+ else {
859
+ mapDef = rule.map;
860
+ }
861
+ // Execute JsonMap transformation
862
+ try {
863
+ const jsonMap = new jsonmap.JsonMap(mapDef, lib);
864
+ const mapOutput = await jsonMap.transform(attributes);
865
+ if (mapOutput &&
866
+ typeof mapOutput === 'object' &&
867
+ !Array.isArray(mapOutput)) {
868
+ merged = { ...merged, ...mapOutput };
869
+ }
870
+ else {
871
+ log.warn(`JsonMap transformation did not return an object; skipping merge.`);
872
+ }
873
+ }
874
+ catch (error) {
875
+ log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
876
+ }
877
+ }
878
+ // Render template if present
879
+ if (rule.template && templateEngine) {
880
+ const templateKey = `rule-${String(ruleIndex)}`;
881
+ // Build template context: attributes (with json spread at top) + map output
882
+ const context = {
883
+ ...(attributes.json ?? {}),
884
+ ...attributes,
885
+ ...merged,
886
+ };
887
+ try {
888
+ const result = templateEngine.render(templateKey, context);
889
+ if (result && result.trim()) {
890
+ renderedContent = result;
891
+ }
892
+ else {
893
+ log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
894
+ }
895
+ }
896
+ catch (error) {
897
+ log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
898
+ }
899
+ }
900
+ }
901
+ }
902
+ return { metadata: merged, renderedContent };
903
+ }
904
+
660
905
  /**
661
906
  * @module templates/helpers
662
907
  * Registers built-in Handlebars helpers for content templates.
@@ -1164,11 +1409,11 @@ const jeevesWatcherConfigSchema = zod.z.object({
1164
1409
  .array(inferenceRuleSchema)
1165
1410
  .optional()
1166
1411
  .describe('Rules for inferring metadata from file attributes.'),
1167
- /** Reusable named JsonMap transformations. */
1412
+ /** Reusable named JsonMap transformations (inline objects or .json file paths). */
1168
1413
  maps: zod.z
1169
- .record(zod.z.string(), jsonmap.jsonMapMapSchema)
1414
+ .record(zod.z.string(), jsonmap.jsonMapMapSchema.or(zod.z.string()))
1170
1415
  .optional()
1171
- .describe('Reusable named JsonMap transformations.'),
1416
+ .describe('Reusable named JsonMap transformations (inline definition or .json file path resolved relative to config directory).'),
1172
1417
  /** Reusable named Handlebars templates (inline strings or .hbs/.handlebars file paths). */
1173
1418
  templates: zod.z
1174
1419
  .record(zod.z.string(), zod.z.string())
@@ -1185,6 +1430,17 @@ const jeevesWatcherConfigSchema = zod.z.object({
1185
1430
  })
1186
1431
  .optional()
1187
1432
  .describe('Custom Handlebars helper registration.'),
1433
+ /** Custom JsonMap lib function registration. */
1434
+ mapHelpers: zod.z
1435
+ .object({
1436
+ /** File paths to custom lib modules (each exports functions to merge into the JsonMap lib). */
1437
+ paths: zod.z
1438
+ .array(zod.z.string())
1439
+ .optional()
1440
+ .describe('File paths to JS modules exporting functions to merge into the JsonMap lib.'),
1441
+ })
1442
+ .optional()
1443
+ .describe('Custom JsonMap lib function registration.'),
1188
1444
  /** Logging configuration. */
1189
1445
  logging: loggingConfigSchema.optional().describe('Logging configuration.'),
1190
1446
  /** Timeout in milliseconds for graceful shutdown. */
@@ -1282,6 +1538,18 @@ async function loadConfig(configPath) {
1282
1538
  }
1283
1539
  try {
1284
1540
  const validated = jeevesWatcherConfigSchema.parse(result.config);
1541
+ // Resolve file-path map references relative to config directory.
1542
+ // After this block, all map values are inline JsonMapMap objects.
1543
+ if (validated.maps) {
1544
+ const configDir = node_path.dirname(result.filepath);
1545
+ for (const [name, value] of Object.entries(validated.maps)) {
1546
+ if (typeof value === 'string') {
1547
+ const mapPath = node_path.resolve(configDir, value);
1548
+ const raw = node_fs.readFileSync(mapPath, 'utf-8');
1549
+ validated.maps[name] = JSON.parse(raw);
1550
+ }
1551
+ }
1552
+ }
1285
1553
  const withDefaults = applyDefaults(validated);
1286
1554
  return substituteEnvVars(withDefaults);
1287
1555
  }
@@ -1663,160 +1931,6 @@ async function extractText(filePath, extension) {
1663
1931
  return extractPlaintext(filePath);
1664
1932
  }
1665
1933
 
1666
- /**
1667
- * @module rules/templates
1668
- * Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
1669
- */
1670
- /**
1671
- * Resolve `${template.vars}` in a value against the given attributes.
1672
- *
1673
- * @param value - The value to resolve.
1674
- * @param attributes - The file attributes for variable lookup.
1675
- * @returns The resolved value.
1676
- */
1677
- function resolveTemplateVars(value, attributes) {
1678
- if (typeof value !== 'string')
1679
- return value;
1680
- return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
1681
- const current = radash.get(attributes, varPath);
1682
- if (current === null || current === undefined)
1683
- return '';
1684
- return typeof current === 'string' ? current : JSON.stringify(current);
1685
- });
1686
- }
1687
- /**
1688
- * Resolve all template variables in a `set` object.
1689
- *
1690
- * @param setObj - The key-value pairs to resolve.
1691
- * @param attributes - The file attributes for variable lookup.
1692
- * @returns The resolved key-value pairs.
1693
- */
1694
- function resolveSet(setObj, attributes) {
1695
- const result = {};
1696
- for (const [key, value] of Object.entries(setObj)) {
1697
- result[key] = resolveTemplateVars(value, attributes);
1698
- }
1699
- return result;
1700
- }
1701
-
1702
- /**
1703
- * @module rules/apply
1704
- * Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
1705
- */
1706
- /**
1707
- * Create the lib object for JsonMap transformations.
1708
- *
1709
- * @returns The lib object.
1710
- */
1711
- function createJsonMapLib() {
1712
- return {
1713
- split: (str, separator) => str.split(separator),
1714
- slice: (arr, start, end) => arr.slice(start, end),
1715
- join: (arr, separator) => arr.join(separator),
1716
- toLowerCase: (str) => str.toLowerCase(),
1717
- replace: (str, search, replacement) => str.replace(search, replacement),
1718
- get: (obj, path) => radash.get(obj, path),
1719
- };
1720
- }
1721
- /**
1722
- * Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
1723
- *
1724
- * Rules are evaluated in order; later rules override earlier ones.
1725
- * If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
1726
- * and map output overrides set output on conflict.
1727
- *
1728
- * @param compiledRules - The compiled rules to evaluate.
1729
- * @param attributes - The file attributes to match against.
1730
- * @param namedMaps - Optional record of named JsonMap definitions.
1731
- * @param logger - Optional logger for warnings (falls back to console.warn).
1732
- * @param templateEngine - Optional template engine for rendering content templates.
1733
- * @param configDir - Optional config directory for resolving .json map file paths.
1734
- * @returns The merged metadata and optional rendered content.
1735
- */
1736
- async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir) {
1737
- // JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
1738
- // Our helper functions accept multiple args, which JsonMap supports at runtime.
1739
- const lib = createJsonMapLib();
1740
- let merged = {};
1741
- let renderedContent = null;
1742
- const log = logger ?? console;
1743
- for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
1744
- if (validate(attributes)) {
1745
- // Apply set resolution
1746
- const setOutput = resolveSet(rule.set, attributes);
1747
- merged = { ...merged, ...setOutput };
1748
- // Apply map transformation if present
1749
- if (rule.map) {
1750
- let mapDef;
1751
- // Resolve map reference
1752
- if (typeof rule.map === 'string') {
1753
- if (rule.map.endsWith('.json') && configDir) {
1754
- // File path: load from .json file
1755
- try {
1756
- const mapPath = node_path.resolve(configDir, rule.map);
1757
- const raw = node_fs.readFileSync(mapPath, 'utf-8');
1758
- mapDef = JSON.parse(raw);
1759
- }
1760
- catch (error) {
1761
- log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
1762
- continue;
1763
- }
1764
- }
1765
- else {
1766
- mapDef = namedMaps?.[rule.map];
1767
- if (!mapDef) {
1768
- log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
1769
- continue;
1770
- }
1771
- }
1772
- }
1773
- else {
1774
- mapDef = rule.map;
1775
- }
1776
- // Execute JsonMap transformation
1777
- try {
1778
- const jsonMap = new jsonmap.JsonMap(mapDef, lib);
1779
- const mapOutput = await jsonMap.transform(attributes);
1780
- if (mapOutput &&
1781
- typeof mapOutput === 'object' &&
1782
- !Array.isArray(mapOutput)) {
1783
- merged = { ...merged, ...mapOutput };
1784
- }
1785
- else {
1786
- log.warn(`JsonMap transformation did not return an object; skipping merge.`);
1787
- }
1788
- }
1789
- catch (error) {
1790
- log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
1791
- }
1792
- }
1793
- // Render template if present
1794
- if (rule.template && templateEngine) {
1795
- const templateKey = `rule-${String(ruleIndex)}`;
1796
- // Build template context: attributes (with json spread at top) + map output
1797
- const context = {
1798
- ...(attributes.json ?? {}),
1799
- ...attributes,
1800
- ...merged,
1801
- };
1802
- try {
1803
- const result = templateEngine.render(templateKey, context);
1804
- if (result && result.trim()) {
1805
- renderedContent = result;
1806
- }
1807
- else {
1808
- log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
1809
- }
1810
- }
1811
- catch (error) {
1812
- log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
1813
- }
1814
- }
1815
- }
1816
- }
1817
- return { metadata: merged, renderedContent };
1818
- }
1819
-
1820
1934
  /**
1821
1935
  * @module rules/attributes
1822
1936
  * Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
@@ -1907,14 +2021,14 @@ function compileRules(rules) {
1907
2021
  * @param configDir - Optional config directory for resolving file paths.
1908
2022
  * @returns The merged metadata and intermediate data.
1909
2023
  */
1910
- async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir) {
2024
+ async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir, customMapLib) {
1911
2025
  const ext = node_path.extname(filePath);
1912
2026
  const stats = await promises.stat(filePath);
1913
2027
  // 1. Extract text and structured data
1914
2028
  const extracted = await extractText(filePath, ext);
1915
2029
  // 2. Build attributes + apply rules
1916
2030
  const attributes = buildAttributes(filePath, stats, extracted.frontmatter, extracted.json);
1917
- const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir);
2031
+ const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir, customMapLib);
1918
2032
  // 3. Read enrichment metadata (merge, enrichment wins)
1919
2033
  const enrichment = await readMetadata(filePath, metadataDir);
1920
2034
  const metadata = {
@@ -2027,7 +2141,7 @@ class DocumentProcessor {
2027
2141
  try {
2028
2142
  const ext = node_path.extname(filePath);
2029
2143
  // 1. Build merged metadata + extract text
2030
- const { metadata, extracted, renderedContent } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
2144
+ const { metadata, extracted, renderedContent } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir, this.config.customMapLib);
2031
2145
  // Use rendered template content if available, otherwise raw extracted text
2032
2146
  const textToEmbed = renderedContent ?? extracted.text;
2033
2147
  if (!textToEmbed.trim()) {
@@ -2141,7 +2255,7 @@ class DocumentProcessor {
2141
2255
  return null;
2142
2256
  }
2143
2257
  // Build merged metadata (lightweight — no embedding)
2144
- const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
2258
+ const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir, this.config.customMapLib);
2145
2259
  // Update all chunk payloads
2146
2260
  const totalChunks = getChunkCount(existingPayload);
2147
2261
  const ids = chunkIds(filePath, totalChunks);
@@ -2155,21 +2269,20 @@ class DocumentProcessor {
2155
2269
  }
2156
2270
  }
2157
2271
  /**
2158
- * Update compiled inference rules for subsequent file processing.
2159
- *
2160
- * @param compiledRules - The newly compiled rules.
2161
- */
2162
- /**
2163
- * Update compiled inference rules and optionally the template engine.
2272
+ * Update compiled inference rules, template engine, and custom map lib.
2164
2273
  *
2165
2274
  * @param compiledRules - The newly compiled rules.
2166
2275
  * @param templateEngine - Optional updated template engine.
2276
+ * @param customMapLib - Optional updated custom JsonMap lib functions.
2167
2277
  */
2168
- updateRules(compiledRules, templateEngine) {
2278
+ updateRules(compiledRules, templateEngine, customMapLib) {
2169
2279
  this.compiledRules = compiledRules;
2170
2280
  if (templateEngine) {
2171
2281
  this.templateEngine = templateEngine;
2172
2282
  }
2283
+ if (customMapLib !== undefined) {
2284
+ this.config = { ...this.config, customMapLib };
2285
+ }
2173
2286
  this.logger.info({ rules: compiledRules.length }, 'Inference rules updated');
2174
2287
  }
2175
2288
  }
@@ -3049,12 +3162,17 @@ class JeevesWatcher {
3049
3162
  const compiledRules = this.factories.compileRules(this.config.inferenceRules ?? []);
3050
3163
  const configDir = this.configPath ? node_path.dirname(this.configPath) : '.';
3051
3164
  const templateEngine = await buildTemplateEngine(this.config.inferenceRules ?? [], this.config.templates, this.config.templateHelpers?.paths, configDir);
3165
+ // Load custom JsonMap lib functions
3166
+ const customMapLib = this.config.mapHelpers?.paths?.length && configDir
3167
+ ? await loadCustomMapHelpers(this.config.mapHelpers.paths, configDir)
3168
+ : undefined;
3052
3169
  const processor = this.factories.createDocumentProcessor({
3053
3170
  metadataDir: this.config.metadataDir ?? '.jeeves-metadata',
3054
3171
  chunkSize: this.config.embedding.chunkSize,
3055
3172
  chunkOverlap: this.config.embedding.chunkOverlap,
3056
3173
  maps: this.config.maps,
3057
3174
  configDir,
3175
+ customMapLib,
3058
3176
  }, embeddingProvider, vectorStore, compiledRules, logger, templateEngine);
3059
3177
  this.processor = processor;
3060
3178
  this.queue = this.factories.createEventQueue({
@@ -3173,7 +3291,10 @@ class JeevesWatcher {
3173
3291
  const compiledRules = this.factories.compileRules(newConfig.inferenceRules ?? []);
3174
3292
  const reloadConfigDir = node_path.dirname(this.configPath);
3175
3293
  const newTemplateEngine = await buildTemplateEngine(newConfig.inferenceRules ?? [], newConfig.templates, newConfig.templateHelpers?.paths, reloadConfigDir);
3176
- processor.updateRules(compiledRules, newTemplateEngine);
3294
+ const newCustomMapLib = newConfig.mapHelpers?.paths?.length && reloadConfigDir
3295
+ ? await loadCustomMapHelpers(newConfig.mapHelpers.paths, reloadConfigDir)
3296
+ : undefined;
3297
+ processor.updateRules(compiledRules, newTemplateEngine, newCustomMapLib);
3177
3298
  logger.info({ configPath: this.configPath, rules: compiledRules.length }, 'Config reloaded');
3178
3299
  }
3179
3300
  catch (error) {