@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/mjs/index.js CHANGED
@@ -2,10 +2,11 @@ import Fastify from 'fastify';
2
2
  import { readdir, stat, rm, readFile, mkdir, writeFile } from 'node:fs/promises';
3
3
  import { resolve, dirname, join, relative, extname, basename } from 'node:path';
4
4
  import picomatch from 'picomatch';
5
- import { omit, capitalize, title, camel, snake, dash, isEqual, get } from 'radash';
5
+ import { omit, get, capitalize, title, camel, snake, dash, isEqual } from 'radash';
6
6
  import { createHash } from 'node:crypto';
7
7
  import { existsSync, statSync, readdirSync, readFileSync } from 'node:fs';
8
8
  import ignore from 'ignore';
9
+ import { JsonMap, jsonMapMapSchema } from '@karmaniverous/jsonmap';
9
10
  import Handlebars from 'handlebars';
10
11
  import dayjs from 'dayjs';
11
12
  import { toMdast } from 'hast-util-to-mdast';
@@ -16,7 +17,6 @@ import { unified } from 'unified';
16
17
  import chokidar from 'chokidar';
17
18
  import { cosmiconfig } from 'cosmiconfig';
18
19
  import { z, ZodError } from 'zod';
19
- import { jsonMapMapSchema, JsonMap } from '@karmaniverous/jsonmap';
20
20
  import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai';
21
21
  import pino from 'pino';
22
22
  import { v5 } from 'uuid';
@@ -636,6 +636,251 @@ class GitignoreFilter {
636
636
  }
637
637
  }
638
638
 
639
+ /**
640
+ * @module rules/templates
641
+ * Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
642
+ */
643
+ /**
644
+ * Resolve `${template.vars}` in a value against the given attributes.
645
+ *
646
+ * @param value - The value to resolve.
647
+ * @param attributes - The file attributes for variable lookup.
648
+ * @returns The resolved value.
649
+ */
650
+ function resolveTemplateVars(value, attributes) {
651
+ if (typeof value !== 'string')
652
+ return value;
653
+ return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
654
+ const current = get(attributes, varPath);
655
+ if (current === null || current === undefined)
656
+ return '';
657
+ return typeof current === 'string' ? current : JSON.stringify(current);
658
+ });
659
+ }
660
+ /**
661
+ * Resolve all template variables in a `set` object.
662
+ *
663
+ * @param setObj - The key-value pairs to resolve.
664
+ * @param attributes - The file attributes for variable lookup.
665
+ * @returns The resolved key-value pairs.
666
+ */
667
+ function resolveSet(setObj, attributes) {
668
+ const result = {};
669
+ for (const [key, value] of Object.entries(setObj)) {
670
+ result[key] = resolveTemplateVars(value, attributes);
671
+ }
672
+ return result;
673
+ }
674
+
675
+ /**
676
+ * @module rules/apply
677
+ * Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
678
+ */
679
+ /**
680
+ * Create the lib object for JsonMap transformations.
681
+ *
682
+ * @param configDir - Optional config directory for resolving relative file paths in lookups.
683
+ * @returns The lib object.
684
+ */
685
+ function createJsonMapLib(configDir, customLib) {
686
+ // Cache loaded JSON files within a single applyRules invocation.
687
+ const jsonCache = new Map();
688
+ const loadJson = (filePath) => {
689
+ const resolvedPath = configDir ? resolve(configDir, filePath) : filePath;
690
+ if (!jsonCache.has(resolvedPath)) {
691
+ const raw = readFileSync(resolvedPath, 'utf-8');
692
+ jsonCache.set(resolvedPath, JSON.parse(raw));
693
+ }
694
+ return jsonCache.get(resolvedPath);
695
+ };
696
+ return {
697
+ split: (str, separator) => str.split(separator),
698
+ slice: (arr, start, end) => arr.slice(start, end),
699
+ join: (arr, separator) => arr.join(separator),
700
+ toLowerCase: (str) => str.toLowerCase(),
701
+ replace: (str, search, replacement) => str.replace(search, replacement),
702
+ get: (obj, path) => get(obj, path),
703
+ /**
704
+ * Load a JSON file (relative to configDir) and look up a value by key,
705
+ * optionally drilling into a sub-path.
706
+ *
707
+ * @param filePath - Path to a JSON file (resolved relative to configDir).
708
+ * @param key - Top-level key to look up.
709
+ * @param field - Optional dot-path into the looked-up entry.
710
+ * @returns The resolved value, or null if not found.
711
+ */
712
+ lookupJson: (filePath, key, field) => {
713
+ const data = loadJson(filePath);
714
+ const entry = data[key];
715
+ if (entry === undefined || entry === null)
716
+ return null;
717
+ if (field)
718
+ return get(entry, field) ?? null;
719
+ return entry;
720
+ },
721
+ /**
722
+ * Map an array of keys through a JSON lookup file, collecting a specific
723
+ * field from each matching entry. Non-matching keys are silently skipped.
724
+ * Array-valued fields are flattened into the result.
725
+ *
726
+ * @param filePath - Path to a JSON file (resolved relative to configDir).
727
+ * @param keys - Array of top-level keys to look up.
728
+ * @param field - Dot-path into each looked-up entry.
729
+ * @returns Flat array of resolved values.
730
+ */
731
+ mapLookup: (filePath, keys, field) => {
732
+ if (!Array.isArray(keys))
733
+ return [];
734
+ const data = loadJson(filePath);
735
+ const results = [];
736
+ for (const k of keys) {
737
+ if (typeof k !== 'string')
738
+ continue;
739
+ const entry = data[k];
740
+ if (entry === undefined || entry === null)
741
+ continue;
742
+ const val = get(entry, field);
743
+ if (val === undefined || val === null)
744
+ continue;
745
+ if (Array.isArray(val)) {
746
+ for (const item of val) {
747
+ results.push(item);
748
+ }
749
+ }
750
+ else {
751
+ results.push(val);
752
+ }
753
+ }
754
+ return results;
755
+ },
756
+ ...customLib,
757
+ };
758
+ }
759
+ /**
760
+ * Load custom JsonMap lib functions from file paths.
761
+ *
762
+ * Each module should default-export an object of functions,
763
+ * or use named exports. Only function-valued exports are merged.
764
+ *
765
+ * @param paths - File paths to custom lib modules.
766
+ * @param configDir - Directory to resolve relative paths against.
767
+ * @returns The merged custom lib functions.
768
+ */
769
+ async function loadCustomMapHelpers(paths, configDir) {
770
+ const merged = {};
771
+ for (const p of paths) {
772
+ const resolved = resolve(configDir, p);
773
+ const mod = (await import(resolved));
774
+ const fns = typeof mod.default === 'object' && mod.default !== null
775
+ ? mod.default
776
+ : mod;
777
+ for (const [key, val] of Object.entries(fns)) {
778
+ if (typeof val === 'function') {
779
+ merged[key] = val;
780
+ }
781
+ }
782
+ }
783
+ return merged;
784
+ }
785
+ /**
786
+ * Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
787
+ *
788
+ * Rules are evaluated in order; later rules override earlier ones.
789
+ * If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
790
+ * and map output overrides set output on conflict.
791
+ *
792
+ * @param compiledRules - The compiled rules to evaluate.
793
+ * @param attributes - The file attributes to match against.
794
+ * @param namedMaps - Optional record of named JsonMap definitions.
795
+ * @param logger - Optional logger for warnings (falls back to console.warn).
796
+ * @param templateEngine - Optional template engine for rendering content templates.
797
+ * @param configDir - Optional config directory for resolving .json map file paths.
798
+ * @returns The merged metadata and optional rendered content.
799
+ */
800
+ async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir, customMapLib) {
801
+ // JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
802
+ // Our helper functions accept multiple args, which JsonMap supports at runtime.
803
+ const lib = createJsonMapLib(configDir, customMapLib);
804
+ let merged = {};
805
+ let renderedContent = null;
806
+ const log = logger ?? console;
807
+ for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
808
+ if (validate(attributes)) {
809
+ // Apply set resolution
810
+ const setOutput = resolveSet(rule.set, attributes);
811
+ merged = { ...merged, ...setOutput };
812
+ // Apply map transformation if present
813
+ if (rule.map) {
814
+ let mapDef;
815
+ // Resolve map reference
816
+ if (typeof rule.map === 'string') {
817
+ if (rule.map.endsWith('.json') && configDir) {
818
+ // File path: load from .json file
819
+ try {
820
+ const mapPath = resolve(configDir, rule.map);
821
+ const raw = readFileSync(mapPath, 'utf-8');
822
+ mapDef = JSON.parse(raw);
823
+ }
824
+ catch (error) {
825
+ log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
826
+ continue;
827
+ }
828
+ }
829
+ else {
830
+ mapDef = namedMaps?.[rule.map];
831
+ if (!mapDef) {
832
+ log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
833
+ continue;
834
+ }
835
+ }
836
+ }
837
+ else {
838
+ mapDef = rule.map;
839
+ }
840
+ // Execute JsonMap transformation
841
+ try {
842
+ const jsonMap = new JsonMap(mapDef, lib);
843
+ const mapOutput = await jsonMap.transform(attributes);
844
+ if (mapOutput &&
845
+ typeof mapOutput === 'object' &&
846
+ !Array.isArray(mapOutput)) {
847
+ merged = { ...merged, ...mapOutput };
848
+ }
849
+ else {
850
+ log.warn(`JsonMap transformation did not return an object; skipping merge.`);
851
+ }
852
+ }
853
+ catch (error) {
854
+ log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
855
+ }
856
+ }
857
+ // Render template if present
858
+ if (rule.template && templateEngine) {
859
+ const templateKey = `rule-${String(ruleIndex)}`;
860
+ // Build template context: attributes (with json spread at top) + map output
861
+ const context = {
862
+ ...(attributes.json ?? {}),
863
+ ...attributes,
864
+ ...merged,
865
+ };
866
+ try {
867
+ const result = templateEngine.render(templateKey, context);
868
+ if (result && result.trim()) {
869
+ renderedContent = result;
870
+ }
871
+ else {
872
+ log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
873
+ }
874
+ }
875
+ catch (error) {
876
+ log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
877
+ }
878
+ }
879
+ }
880
+ }
881
+ return { metadata: merged, renderedContent };
882
+ }
883
+
639
884
  /**
640
885
  * @module templates/helpers
641
886
  * Registers built-in Handlebars helpers for content templates.
@@ -1143,11 +1388,11 @@ const jeevesWatcherConfigSchema = z.object({
1143
1388
  .array(inferenceRuleSchema)
1144
1389
  .optional()
1145
1390
  .describe('Rules for inferring metadata from file attributes.'),
1146
- /** Reusable named JsonMap transformations. */
1391
+ /** Reusable named JsonMap transformations (inline objects or .json file paths). */
1147
1392
  maps: z
1148
- .record(z.string(), jsonMapMapSchema)
1393
+ .record(z.string(), jsonMapMapSchema.or(z.string()))
1149
1394
  .optional()
1150
- .describe('Reusable named JsonMap transformations.'),
1395
+ .describe('Reusable named JsonMap transformations (inline definition or .json file path resolved relative to config directory).'),
1151
1396
  /** Reusable named Handlebars templates (inline strings or .hbs/.handlebars file paths). */
1152
1397
  templates: z
1153
1398
  .record(z.string(), z.string())
@@ -1164,6 +1409,17 @@ const jeevesWatcherConfigSchema = z.object({
1164
1409
  })
1165
1410
  .optional()
1166
1411
  .describe('Custom Handlebars helper registration.'),
1412
+ /** Custom JsonMap lib function registration. */
1413
+ mapHelpers: z
1414
+ .object({
1415
+ /** File paths to custom lib modules (each exports functions to merge into the JsonMap lib). */
1416
+ paths: z
1417
+ .array(z.string())
1418
+ .optional()
1419
+ .describe('File paths to JS modules exporting functions to merge into the JsonMap lib.'),
1420
+ })
1421
+ .optional()
1422
+ .describe('Custom JsonMap lib function registration.'),
1167
1423
  /** Logging configuration. */
1168
1424
  logging: loggingConfigSchema.optional().describe('Logging configuration.'),
1169
1425
  /** Timeout in milliseconds for graceful shutdown. */
@@ -1261,6 +1517,18 @@ async function loadConfig(configPath) {
1261
1517
  }
1262
1518
  try {
1263
1519
  const validated = jeevesWatcherConfigSchema.parse(result.config);
1520
+ // Resolve file-path map references relative to config directory.
1521
+ // After this block, all map values are inline JsonMapMap objects.
1522
+ if (validated.maps) {
1523
+ const configDir = dirname(result.filepath);
1524
+ for (const [name, value] of Object.entries(validated.maps)) {
1525
+ if (typeof value === 'string') {
1526
+ const mapPath = resolve(configDir, value);
1527
+ const raw = readFileSync(mapPath, 'utf-8');
1528
+ validated.maps[name] = JSON.parse(raw);
1529
+ }
1530
+ }
1531
+ }
1264
1532
  const withDefaults = applyDefaults(validated);
1265
1533
  return substituteEnvVars(withDefaults);
1266
1534
  }
@@ -1642,160 +1910,6 @@ async function extractText(filePath, extension) {
1642
1910
  return extractPlaintext(filePath);
1643
1911
  }
1644
1912
 
1645
- /**
1646
- * @module rules/templates
1647
- * Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
1648
- */
1649
- /**
1650
- * Resolve `${template.vars}` in a value against the given attributes.
1651
- *
1652
- * @param value - The value to resolve.
1653
- * @param attributes - The file attributes for variable lookup.
1654
- * @returns The resolved value.
1655
- */
1656
- function resolveTemplateVars(value, attributes) {
1657
- if (typeof value !== 'string')
1658
- return value;
1659
- return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
1660
- const current = get(attributes, varPath);
1661
- if (current === null || current === undefined)
1662
- return '';
1663
- return typeof current === 'string' ? current : JSON.stringify(current);
1664
- });
1665
- }
1666
- /**
1667
- * Resolve all template variables in a `set` object.
1668
- *
1669
- * @param setObj - The key-value pairs to resolve.
1670
- * @param attributes - The file attributes for variable lookup.
1671
- * @returns The resolved key-value pairs.
1672
- */
1673
- function resolveSet(setObj, attributes) {
1674
- const result = {};
1675
- for (const [key, value] of Object.entries(setObj)) {
1676
- result[key] = resolveTemplateVars(value, attributes);
1677
- }
1678
- return result;
1679
- }
1680
-
1681
- /**
1682
- * @module rules/apply
1683
- * Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
1684
- */
1685
- /**
1686
- * Create the lib object for JsonMap transformations.
1687
- *
1688
- * @returns The lib object.
1689
- */
1690
- function createJsonMapLib() {
1691
- return {
1692
- split: (str, separator) => str.split(separator),
1693
- slice: (arr, start, end) => arr.slice(start, end),
1694
- join: (arr, separator) => arr.join(separator),
1695
- toLowerCase: (str) => str.toLowerCase(),
1696
- replace: (str, search, replacement) => str.replace(search, replacement),
1697
- get: (obj, path) => get(obj, path),
1698
- };
1699
- }
1700
- /**
1701
- * Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
1702
- *
1703
- * Rules are evaluated in order; later rules override earlier ones.
1704
- * If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
1705
- * and map output overrides set output on conflict.
1706
- *
1707
- * @param compiledRules - The compiled rules to evaluate.
1708
- * @param attributes - The file attributes to match against.
1709
- * @param namedMaps - Optional record of named JsonMap definitions.
1710
- * @param logger - Optional logger for warnings (falls back to console.warn).
1711
- * @param templateEngine - Optional template engine for rendering content templates.
1712
- * @param configDir - Optional config directory for resolving .json map file paths.
1713
- * @returns The merged metadata and optional rendered content.
1714
- */
1715
- async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir) {
1716
- // JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
1717
- // Our helper functions accept multiple args, which JsonMap supports at runtime.
1718
- const lib = createJsonMapLib();
1719
- let merged = {};
1720
- let renderedContent = null;
1721
- const log = logger ?? console;
1722
- for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
1723
- if (validate(attributes)) {
1724
- // Apply set resolution
1725
- const setOutput = resolveSet(rule.set, attributes);
1726
- merged = { ...merged, ...setOutput };
1727
- // Apply map transformation if present
1728
- if (rule.map) {
1729
- let mapDef;
1730
- // Resolve map reference
1731
- if (typeof rule.map === 'string') {
1732
- if (rule.map.endsWith('.json') && configDir) {
1733
- // File path: load from .json file
1734
- try {
1735
- const mapPath = resolve(configDir, rule.map);
1736
- const raw = readFileSync(mapPath, 'utf-8');
1737
- mapDef = JSON.parse(raw);
1738
- }
1739
- catch (error) {
1740
- log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
1741
- continue;
1742
- }
1743
- }
1744
- else {
1745
- mapDef = namedMaps?.[rule.map];
1746
- if (!mapDef) {
1747
- log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
1748
- continue;
1749
- }
1750
- }
1751
- }
1752
- else {
1753
- mapDef = rule.map;
1754
- }
1755
- // Execute JsonMap transformation
1756
- try {
1757
- const jsonMap = new JsonMap(mapDef, lib);
1758
- const mapOutput = await jsonMap.transform(attributes);
1759
- if (mapOutput &&
1760
- typeof mapOutput === 'object' &&
1761
- !Array.isArray(mapOutput)) {
1762
- merged = { ...merged, ...mapOutput };
1763
- }
1764
- else {
1765
- log.warn(`JsonMap transformation did not return an object; skipping merge.`);
1766
- }
1767
- }
1768
- catch (error) {
1769
- log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
1770
- }
1771
- }
1772
- // Render template if present
1773
- if (rule.template && templateEngine) {
1774
- const templateKey = `rule-${String(ruleIndex)}`;
1775
- // Build template context: attributes (with json spread at top) + map output
1776
- const context = {
1777
- ...(attributes.json ?? {}),
1778
- ...attributes,
1779
- ...merged,
1780
- };
1781
- try {
1782
- const result = templateEngine.render(templateKey, context);
1783
- if (result && result.trim()) {
1784
- renderedContent = result;
1785
- }
1786
- else {
1787
- log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
1788
- }
1789
- }
1790
- catch (error) {
1791
- log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
1792
- }
1793
- }
1794
- }
1795
- }
1796
- return { metadata: merged, renderedContent };
1797
- }
1798
-
1799
1913
  /**
1800
1914
  * @module rules/attributes
1801
1915
  * Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
@@ -1886,14 +2000,14 @@ function compileRules(rules) {
1886
2000
  * @param configDir - Optional config directory for resolving file paths.
1887
2001
  * @returns The merged metadata and intermediate data.
1888
2002
  */
1889
- async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir) {
2003
+ async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir, customMapLib) {
1890
2004
  const ext = extname(filePath);
1891
2005
  const stats = await stat(filePath);
1892
2006
  // 1. Extract text and structured data
1893
2007
  const extracted = await extractText(filePath, ext);
1894
2008
  // 2. Build attributes + apply rules
1895
2009
  const attributes = buildAttributes(filePath, stats, extracted.frontmatter, extracted.json);
1896
- const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir);
2010
+ const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir, customMapLib);
1897
2011
  // 3. Read enrichment metadata (merge, enrichment wins)
1898
2012
  const enrichment = await readMetadata(filePath, metadataDir);
1899
2013
  const metadata = {
@@ -2006,7 +2120,7 @@ class DocumentProcessor {
2006
2120
  try {
2007
2121
  const ext = extname(filePath);
2008
2122
  // 1. Build merged metadata + extract text
2009
- const { metadata, extracted, renderedContent } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
2123
+ 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);
2010
2124
  // Use rendered template content if available, otherwise raw extracted text
2011
2125
  const textToEmbed = renderedContent ?? extracted.text;
2012
2126
  if (!textToEmbed.trim()) {
@@ -2120,7 +2234,7 @@ class DocumentProcessor {
2120
2234
  return null;
2121
2235
  }
2122
2236
  // Build merged metadata (lightweight — no embedding)
2123
- const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
2237
+ const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir, this.config.customMapLib);
2124
2238
  // Update all chunk payloads
2125
2239
  const totalChunks = getChunkCount(existingPayload);
2126
2240
  const ids = chunkIds(filePath, totalChunks);
@@ -2134,21 +2248,20 @@ class DocumentProcessor {
2134
2248
  }
2135
2249
  }
2136
2250
  /**
2137
- * Update compiled inference rules for subsequent file processing.
2138
- *
2139
- * @param compiledRules - The newly compiled rules.
2140
- */
2141
- /**
2142
- * Update compiled inference rules and optionally the template engine.
2251
+ * Update compiled inference rules, template engine, and custom map lib.
2143
2252
  *
2144
2253
  * @param compiledRules - The newly compiled rules.
2145
2254
  * @param templateEngine - Optional updated template engine.
2255
+ * @param customMapLib - Optional updated custom JsonMap lib functions.
2146
2256
  */
2147
- updateRules(compiledRules, templateEngine) {
2257
+ updateRules(compiledRules, templateEngine, customMapLib) {
2148
2258
  this.compiledRules = compiledRules;
2149
2259
  if (templateEngine) {
2150
2260
  this.templateEngine = templateEngine;
2151
2261
  }
2262
+ if (customMapLib !== undefined) {
2263
+ this.config = { ...this.config, customMapLib };
2264
+ }
2152
2265
  this.logger.info({ rules: compiledRules.length }, 'Inference rules updated');
2153
2266
  }
2154
2267
  }
@@ -3028,12 +3141,17 @@ class JeevesWatcher {
3028
3141
  const compiledRules = this.factories.compileRules(this.config.inferenceRules ?? []);
3029
3142
  const configDir = this.configPath ? dirname(this.configPath) : '.';
3030
3143
  const templateEngine = await buildTemplateEngine(this.config.inferenceRules ?? [], this.config.templates, this.config.templateHelpers?.paths, configDir);
3144
+ // Load custom JsonMap lib functions
3145
+ const customMapLib = this.config.mapHelpers?.paths?.length && configDir
3146
+ ? await loadCustomMapHelpers(this.config.mapHelpers.paths, configDir)
3147
+ : undefined;
3031
3148
  const processor = this.factories.createDocumentProcessor({
3032
3149
  metadataDir: this.config.metadataDir ?? '.jeeves-metadata',
3033
3150
  chunkSize: this.config.embedding.chunkSize,
3034
3151
  chunkOverlap: this.config.embedding.chunkOverlap,
3035
3152
  maps: this.config.maps,
3036
3153
  configDir,
3154
+ customMapLib,
3037
3155
  }, embeddingProvider, vectorStore, compiledRules, logger, templateEngine);
3038
3156
  this.processor = processor;
3039
3157
  this.queue = this.factories.createEventQueue({
@@ -3152,7 +3270,10 @@ class JeevesWatcher {
3152
3270
  const compiledRules = this.factories.compileRules(newConfig.inferenceRules ?? []);
3153
3271
  const reloadConfigDir = dirname(this.configPath);
3154
3272
  const newTemplateEngine = await buildTemplateEngine(newConfig.inferenceRules ?? [], newConfig.templates, newConfig.templateHelpers?.paths, reloadConfigDir);
3155
- processor.updateRules(compiledRules, newTemplateEngine);
3273
+ const newCustomMapLib = newConfig.mapHelpers?.paths?.length && reloadConfigDir
3274
+ ? await loadCustomMapHelpers(newConfig.mapHelpers.paths, reloadConfigDir)
3275
+ : undefined;
3276
+ processor.updateRules(compiledRules, newTemplateEngine, newCustomMapLib);
3156
3277
  logger.info({ configPath: this.configPath, rules: compiledRules.length }, 'Config reloaded');
3157
3278
  }
3158
3279
  catch (error) {
package/package.json CHANGED
@@ -185,5 +185,5 @@
185
185
  },
186
186
  "type": "module",
187
187
  "types": "dist/index.d.ts",
188
- "version": "0.4.1"
188
+ "version": "0.4.3"
189
189
  }