matryoshka-rlm 0.1.0 → 0.2.0

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.
Files changed (312) hide show
  1. package/README.md +261 -140
  2. package/config.example.json +0 -5
  3. package/dist/adapters/base.d.ts +43 -0
  4. package/dist/adapters/base.d.ts.map +1 -0
  5. package/dist/adapters/base.js +181 -0
  6. package/dist/adapters/base.js.map +1 -0
  7. package/dist/adapters/deepseek.d.ts +14 -0
  8. package/dist/adapters/deepseek.d.ts.map +1 -0
  9. package/dist/adapters/deepseek.js +139 -0
  10. package/dist/adapters/deepseek.js.map +1 -0
  11. package/dist/adapters/index.d.ts +39 -0
  12. package/dist/adapters/index.d.ts.map +1 -0
  13. package/dist/adapters/index.js +90 -0
  14. package/dist/adapters/index.js.map +1 -0
  15. package/dist/adapters/nucleus.d.ts +18 -0
  16. package/dist/adapters/nucleus.d.ts.map +1 -0
  17. package/dist/adapters/nucleus.js +323 -0
  18. package/dist/adapters/nucleus.js.map +1 -0
  19. package/dist/adapters/qwen-barliman.d.ts +16 -0
  20. package/dist/adapters/qwen-barliman.d.ts.map +1 -0
  21. package/dist/adapters/qwen-barliman.js +165 -0
  22. package/dist/adapters/qwen-barliman.js.map +1 -0
  23. package/dist/adapters/qwen-synthesis.d.ts +13 -0
  24. package/dist/adapters/qwen-synthesis.d.ts.map +1 -0
  25. package/dist/adapters/qwen-synthesis.js +329 -0
  26. package/dist/adapters/qwen-synthesis.js.map +1 -0
  27. package/dist/adapters/qwen.d.ts +14 -0
  28. package/dist/adapters/qwen.d.ts.map +1 -0
  29. package/dist/adapters/qwen.js +216 -0
  30. package/dist/adapters/qwen.js.map +1 -0
  31. package/dist/adapters/types.d.ts +78 -0
  32. package/dist/adapters/types.d.ts.map +1 -0
  33. package/dist/adapters/types.js +9 -0
  34. package/dist/adapters/types.js.map +1 -0
  35. package/dist/config.d.ts +6 -6
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +0 -6
  38. package/dist/config.js.map +1 -1
  39. package/dist/constraints/index.d.ts +10 -0
  40. package/dist/constraints/index.d.ts.map +1 -0
  41. package/dist/constraints/index.js +9 -0
  42. package/dist/constraints/index.js.map +1 -0
  43. package/dist/constraints/types.d.ts +78 -0
  44. package/dist/constraints/types.d.ts.map +1 -0
  45. package/dist/constraints/types.js +45 -0
  46. package/dist/constraints/types.js.map +1 -0
  47. package/dist/constraints/verifier.d.ts +24 -0
  48. package/dist/constraints/verifier.d.ts.map +1 -0
  49. package/dist/constraints/verifier.js +228 -0
  50. package/dist/constraints/verifier.js.map +1 -0
  51. package/dist/engine/index.d.ts +7 -0
  52. package/dist/engine/index.d.ts.map +1 -0
  53. package/dist/engine/index.js +7 -0
  54. package/dist/engine/index.js.map +1 -0
  55. package/dist/engine/nucleus-engine.d.ts +115 -0
  56. package/dist/engine/nucleus-engine.d.ts.map +1 -0
  57. package/dist/engine/nucleus-engine.js +342 -0
  58. package/dist/engine/nucleus-engine.js.map +1 -0
  59. package/dist/feedback/error-analyzer.d.ts +35 -0
  60. package/dist/feedback/error-analyzer.d.ts.map +1 -0
  61. package/dist/feedback/error-analyzer.js +346 -0
  62. package/dist/feedback/error-analyzer.js.map +1 -0
  63. package/dist/feedback/execution-feedback.d.ts +31 -0
  64. package/dist/feedback/execution-feedback.d.ts.map +1 -0
  65. package/dist/feedback/execution-feedback.js +169 -0
  66. package/dist/feedback/execution-feedback.js.map +1 -0
  67. package/dist/index.js +56 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/lattice-mcp-server.d.ts +20 -0
  70. package/dist/lattice-mcp-server.d.ts.map +1 -0
  71. package/dist/lattice-mcp-server.js +363 -0
  72. package/dist/lattice-mcp-server.js.map +1 -0
  73. package/dist/lib.d.ts +17 -0
  74. package/dist/lib.d.ts.map +1 -0
  75. package/dist/lib.js +24 -0
  76. package/dist/lib.js.map +1 -0
  77. package/dist/llm/deepseek.d.ts.map +1 -1
  78. package/dist/llm/deepseek.js +11 -6
  79. package/dist/llm/deepseek.js.map +1 -1
  80. package/dist/llm/index.d.ts +41 -1
  81. package/dist/llm/index.d.ts.map +1 -1
  82. package/dist/llm/index.js +69 -4
  83. package/dist/llm/index.js.map +1 -1
  84. package/dist/llm/ollama.d.ts.map +1 -1
  85. package/dist/llm/ollama.js +14 -9
  86. package/dist/llm/ollama.js.map +1 -1
  87. package/dist/llm/types.d.ts +5 -1
  88. package/dist/llm/types.d.ts.map +1 -1
  89. package/dist/logic/constraint-resolver.d.ts +34 -0
  90. package/dist/logic/constraint-resolver.d.ts.map +1 -0
  91. package/dist/logic/constraint-resolver.js +214 -0
  92. package/dist/logic/constraint-resolver.js.map +1 -0
  93. package/dist/logic/index.d.ts +9 -0
  94. package/dist/logic/index.d.ts.map +1 -0
  95. package/dist/logic/index.js +9 -0
  96. package/dist/logic/index.js.map +1 -0
  97. package/dist/logic/lc-compiler.d.ts +25 -0
  98. package/dist/logic/lc-compiler.d.ts.map +1 -0
  99. package/dist/logic/lc-compiler.js +174 -0
  100. package/dist/logic/lc-compiler.js.map +1 -0
  101. package/dist/logic/lc-interpreter.d.ts +63 -0
  102. package/dist/logic/lc-interpreter.d.ts.map +1 -0
  103. package/dist/logic/lc-interpreter.js +276 -0
  104. package/dist/logic/lc-interpreter.js.map +1 -0
  105. package/dist/logic/lc-parser.d.ts +26 -0
  106. package/dist/logic/lc-parser.d.ts.map +1 -0
  107. package/dist/logic/lc-parser.js +757 -0
  108. package/dist/logic/lc-parser.js.map +1 -0
  109. package/dist/logic/lc-solver.d.ts +60 -0
  110. package/dist/logic/lc-solver.d.ts.map +1 -0
  111. package/dist/logic/lc-solver.js +1005 -0
  112. package/dist/logic/lc-solver.js.map +1 -0
  113. package/dist/logic/relational-solver.d.ts +45 -0
  114. package/dist/logic/relational-solver.d.ts.map +1 -0
  115. package/dist/logic/relational-solver.js +606 -0
  116. package/dist/logic/relational-solver.js.map +1 -0
  117. package/dist/logic/synthesis-integrator.d.ts +133 -0
  118. package/dist/logic/synthesis-integrator.d.ts.map +1 -0
  119. package/dist/logic/synthesis-integrator.js +798 -0
  120. package/dist/logic/synthesis-integrator.js.map +1 -0
  121. package/dist/logic/type-inference.d.ts +36 -0
  122. package/dist/logic/type-inference.d.ts.map +1 -0
  123. package/dist/logic/type-inference.js +287 -0
  124. package/dist/logic/type-inference.js.map +1 -0
  125. package/dist/logic/types.d.ts +343 -0
  126. package/dist/logic/types.d.ts.map +1 -0
  127. package/dist/logic/types.js +8 -0
  128. package/dist/logic/types.js.map +1 -0
  129. package/dist/mcp-server.d.ts +4 -0
  130. package/dist/mcp-server.d.ts.map +1 -1
  131. package/dist/mcp-server.js +151 -27
  132. package/dist/mcp-server.js.map +1 -1
  133. package/dist/minikanren/common.d.ts +17 -0
  134. package/dist/minikanren/common.d.ts.map +1 -0
  135. package/dist/minikanren/common.js +59 -0
  136. package/dist/minikanren/common.js.map +1 -0
  137. package/dist/minikanren/goals.d.ts +10 -0
  138. package/dist/minikanren/goals.d.ts.map +1 -0
  139. package/dist/minikanren/goals.js +49 -0
  140. package/dist/minikanren/goals.js.map +1 -0
  141. package/dist/minikanren/index.d.ts +12 -0
  142. package/dist/minikanren/index.d.ts.map +1 -0
  143. package/dist/minikanren/index.js +16 -0
  144. package/dist/minikanren/index.js.map +1 -0
  145. package/dist/minikanren/ramo.d.ts +9 -0
  146. package/dist/minikanren/ramo.d.ts.map +1 -0
  147. package/dist/minikanren/ramo.js +22 -0
  148. package/dist/minikanren/ramo.js.map +1 -0
  149. package/dist/minikanren/reify.d.ts +3 -0
  150. package/dist/minikanren/reify.d.ts.map +1 -0
  151. package/dist/minikanren/reify.js +27 -0
  152. package/dist/minikanren/reify.js.map +1 -0
  153. package/dist/minikanren/streams.d.ts +14 -0
  154. package/dist/minikanren/streams.d.ts.map +1 -0
  155. package/dist/minikanren/streams.js +44 -0
  156. package/dist/minikanren/streams.js.map +1 -0
  157. package/dist/minikanren/sugar.d.ts +16 -0
  158. package/dist/minikanren/sugar.d.ts.map +1 -0
  159. package/dist/minikanren/sugar.js +76 -0
  160. package/dist/minikanren/sugar.js.map +1 -0
  161. package/dist/minikanren/unify.d.ts +3 -0
  162. package/dist/minikanren/unify.d.ts.map +1 -0
  163. package/dist/minikanren/unify.js +31 -0
  164. package/dist/minikanren/unify.js.map +1 -0
  165. package/dist/rag/index.d.ts +12 -0
  166. package/dist/rag/index.d.ts.map +1 -0
  167. package/dist/rag/index.js +13 -0
  168. package/dist/rag/index.js.map +1 -0
  169. package/dist/rag/knowledge-base.d.ts +39 -0
  170. package/dist/rag/knowledge-base.d.ts.map +1 -0
  171. package/dist/rag/knowledge-base.js +227 -0
  172. package/dist/rag/knowledge-base.js.map +1 -0
  173. package/dist/rag/manager.d.ts +109 -0
  174. package/dist/rag/manager.d.ts.map +1 -0
  175. package/dist/rag/manager.js +236 -0
  176. package/dist/rag/manager.js.map +1 -0
  177. package/dist/rag/similarity.d.ts +63 -0
  178. package/dist/rag/similarity.d.ts.map +1 -0
  179. package/dist/rag/similarity.js +153 -0
  180. package/dist/rag/similarity.js.map +1 -0
  181. package/dist/repl/index.d.ts +8 -0
  182. package/dist/repl/index.d.ts.map +1 -0
  183. package/dist/repl/index.js +8 -0
  184. package/dist/repl/index.js.map +1 -0
  185. package/dist/repl/lattice-repl.d.ts +31 -0
  186. package/dist/repl/lattice-repl.d.ts.map +1 -0
  187. package/dist/repl/lattice-repl.js +334 -0
  188. package/dist/repl/lattice-repl.js.map +1 -0
  189. package/dist/repl/nucleus-repl.d.ts +31 -0
  190. package/dist/repl/nucleus-repl.d.ts.map +1 -0
  191. package/dist/repl/nucleus-repl.js +334 -0
  192. package/dist/repl/nucleus-repl.js.map +1 -0
  193. package/dist/rlm.d.ts +16 -4
  194. package/dist/rlm.d.ts.map +1 -1
  195. package/dist/rlm.js +781 -123
  196. package/dist/rlm.js.map +1 -1
  197. package/dist/sandbox/code-validator.d.ts +24 -0
  198. package/dist/sandbox/code-validator.d.ts.map +1 -0
  199. package/dist/sandbox/code-validator.js +195 -0
  200. package/dist/sandbox/code-validator.js.map +1 -0
  201. package/dist/sandbox.d.ts +4 -1
  202. package/dist/sandbox.d.ts.map +1 -1
  203. package/dist/sandbox.js +182 -12
  204. package/dist/sandbox.js.map +1 -1
  205. package/dist/session.d.ts +49 -0
  206. package/dist/session.d.ts.map +1 -0
  207. package/dist/session.js +78 -0
  208. package/dist/session.js.map +1 -0
  209. package/dist/synthesis/coordinator.d.ts +129 -0
  210. package/dist/synthesis/coordinator.d.ts.map +1 -0
  211. package/dist/synthesis/coordinator.js +456 -0
  212. package/dist/synthesis/coordinator.js.map +1 -0
  213. package/dist/synthesis/evalo/compile.d.ts +31 -0
  214. package/dist/synthesis/evalo/compile.d.ts.map +1 -0
  215. package/dist/synthesis/evalo/compile.js +135 -0
  216. package/dist/synthesis/evalo/compile.js.map +1 -0
  217. package/dist/synthesis/evalo/evalo.d.ts +45 -0
  218. package/dist/synthesis/evalo/evalo.d.ts.map +1 -0
  219. package/dist/synthesis/evalo/evalo.js +298 -0
  220. package/dist/synthesis/evalo/evalo.js.map +1 -0
  221. package/dist/synthesis/evalo/index.d.ts +18 -0
  222. package/dist/synthesis/evalo/index.d.ts.map +1 -0
  223. package/dist/synthesis/evalo/index.js +20 -0
  224. package/dist/synthesis/evalo/index.js.map +1 -0
  225. package/dist/synthesis/evalo/typeo.d.ts +47 -0
  226. package/dist/synthesis/evalo/typeo.d.ts.map +1 -0
  227. package/dist/synthesis/evalo/typeo.js +145 -0
  228. package/dist/synthesis/evalo/typeo.js.map +1 -0
  229. package/dist/synthesis/evalo/types.d.ts +84 -0
  230. package/dist/synthesis/evalo/types.d.ts.map +1 -0
  231. package/dist/synthesis/evalo/types.js +51 -0
  232. package/dist/synthesis/evalo/types.js.map +1 -0
  233. package/dist/synthesis/evolutionary.d.ts +88 -0
  234. package/dist/synthesis/evolutionary.d.ts.map +1 -0
  235. package/dist/synthesis/evolutionary.js +306 -0
  236. package/dist/synthesis/evolutionary.js.map +1 -0
  237. package/dist/synthesis/example-collector.d.ts +67 -0
  238. package/dist/synthesis/example-collector.d.ts.map +1 -0
  239. package/dist/synthesis/example-collector.js +159 -0
  240. package/dist/synthesis/example-collector.js.map +1 -0
  241. package/dist/synthesis/extractor/synthesis.d.ts +46 -0
  242. package/dist/synthesis/extractor/synthesis.d.ts.map +1 -0
  243. package/dist/synthesis/extractor/synthesis.js +441 -0
  244. package/dist/synthesis/extractor/synthesis.js.map +1 -0
  245. package/dist/synthesis/index.d.ts +13 -0
  246. package/dist/synthesis/index.d.ts.map +1 -0
  247. package/dist/synthesis/index.js +21 -0
  248. package/dist/synthesis/index.js.map +1 -0
  249. package/dist/synthesis/knowledge-base.d.ts +99 -0
  250. package/dist/synthesis/knowledge-base.d.ts.map +1 -0
  251. package/dist/synthesis/knowledge-base.js +229 -0
  252. package/dist/synthesis/knowledge-base.js.map +1 -0
  253. package/dist/synthesis/minikanren/core.d.ts +57 -0
  254. package/dist/synthesis/minikanren/core.d.ts.map +1 -0
  255. package/dist/synthesis/minikanren/core.js +203 -0
  256. package/dist/synthesis/minikanren/core.js.map +1 -0
  257. package/dist/synthesis/regex/synthesis.d.ts +48 -0
  258. package/dist/synthesis/regex/synthesis.d.ts.map +1 -0
  259. package/dist/synthesis/regex/synthesis.js +457 -0
  260. package/dist/synthesis/regex/synthesis.js.map +1 -0
  261. package/dist/synthesis/relational/coordinator.d.ts +114 -0
  262. package/dist/synthesis/relational/coordinator.d.ts.map +1 -0
  263. package/dist/synthesis/relational/coordinator.js +280 -0
  264. package/dist/synthesis/relational/coordinator.js.map +1 -0
  265. package/dist/synthesis/relational/engine.d.ts +123 -0
  266. package/dist/synthesis/relational/engine.d.ts.map +1 -0
  267. package/dist/synthesis/relational/engine.js +341 -0
  268. package/dist/synthesis/relational/engine.js.map +1 -0
  269. package/dist/synthesis/relational/interpreter.d.ts +95 -0
  270. package/dist/synthesis/relational/interpreter.d.ts.map +1 -0
  271. package/dist/synthesis/relational/interpreter.js +238 -0
  272. package/dist/synthesis/relational/interpreter.js.map +1 -0
  273. package/dist/synthesis/sandbox-tools.d.ts +28 -0
  274. package/dist/synthesis/sandbox-tools.d.ts.map +1 -0
  275. package/dist/synthesis/sandbox-tools.js +573 -0
  276. package/dist/synthesis/sandbox-tools.js.map +1 -0
  277. package/dist/tool/adapters/claude-code.d.ts +72 -0
  278. package/dist/tool/adapters/claude-code.d.ts.map +1 -0
  279. package/dist/tool/adapters/claude-code.js +210 -0
  280. package/dist/tool/adapters/claude-code.js.map +1 -0
  281. package/dist/tool/adapters/http.d.ts +114 -0
  282. package/dist/tool/adapters/http.d.ts.map +1 -0
  283. package/dist/tool/adapters/http.js +453 -0
  284. package/dist/tool/adapters/http.js.map +1 -0
  285. package/dist/tool/adapters/index.d.ts +12 -0
  286. package/dist/tool/adapters/index.d.ts.map +1 -0
  287. package/dist/tool/adapters/index.js +12 -0
  288. package/dist/tool/adapters/index.js.map +1 -0
  289. package/dist/tool/adapters/pipe.d.ts +67 -0
  290. package/dist/tool/adapters/pipe.d.ts.map +1 -0
  291. package/dist/tool/adapters/pipe.js +208 -0
  292. package/dist/tool/adapters/pipe.js.map +1 -0
  293. package/dist/tool/index.d.ts +17 -0
  294. package/dist/tool/index.d.ts.map +1 -0
  295. package/dist/tool/index.js +19 -0
  296. package/dist/tool/index.js.map +1 -0
  297. package/dist/tool/lattice-tool.d.ts +118 -0
  298. package/dist/tool/lattice-tool.d.ts.map +1 -0
  299. package/dist/tool/lattice-tool.js +304 -0
  300. package/dist/tool/lattice-tool.js.map +1 -0
  301. package/dist/tool/nucleus-tool.d.ts +118 -0
  302. package/dist/tool/nucleus-tool.d.ts.map +1 -0
  303. package/dist/tool/nucleus-tool.js +304 -0
  304. package/dist/tool/nucleus-tool.js.map +1 -0
  305. package/dist/tools.d.ts.map +1 -1
  306. package/dist/tools.js +101 -4
  307. package/dist/tools.js.map +1 -1
  308. package/dist/version.d.ts +8 -0
  309. package/dist/version.d.ts.map +1 -0
  310. package/dist/version.js +28 -0
  311. package/dist/version.js.map +1 -0
  312. package/package.json +26 -7
package/README.md CHANGED
@@ -4,10 +4,110 @@ Process documents 100x larger than your LLM's context window—without vector da
4
4
 
5
5
  ## The Problem
6
6
 
7
- LLMs have fixed context windows. Traditional solutions (RAG, chunking) lose information or miss connections across chunks. RLM takes a different approach: the model writes code to explore documents programmatically, deciding at runtime how to decompose and analyze the data.
7
+ LLMs have fixed context windows. Traditional solutions (RAG, chunking) lose information or miss connections across chunks. RLM takes a different approach: the model reasons about your query and outputs symbolic commands that a logic engine executes against the document.
8
8
 
9
9
  Based on the [Recursive Language Models paper](https://arxiv.org/abs/2512.24601).
10
10
 
11
+ ## How It Works
12
+
13
+ Unlike traditional approaches where an LLM writes arbitrary code, RLM uses **[Nucleus](https://github.com/michaelwhitford/nucleus)**—a constrained symbolic language based on S-expressions. The LLM outputs Nucleus commands, which are parsed, type-checked, and executed by **Lattice**, our logic engine.
14
+
15
+ ```
16
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
17
+ │ User Query │────▶│ LLM Reasons │────▶│ Nucleus Command │
18
+ │ "total sales?" │ │ about intent │ │ (sum RESULTS) │
19
+ └─────────────────┘ └─────────────────┘ └────────┬────────┘
20
+
21
+ ┌─────────────────┐ ┌─────────────────┐ ┌────────▼────────┐
22
+ │ Final Answer │◀────│ Lattice Engine │◀────│ Parser │
23
+ │ 13,000,000 │ │ Executes │ │ Validates │
24
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
25
+ ```
26
+
27
+ **Why this works better than code generation:**
28
+
29
+ 1. **Reduced entropy** - Nucleus has a rigid grammar with fewer valid outputs than JavaScript
30
+ 2. **Fail-fast validation** - Parser rejects malformed commands before execution
31
+ 3. **Safe execution** - Lattice only executes known operations, no arbitrary code
32
+ 4. **Small model friendly** - 7B models handle symbolic grammars better than freeform code
33
+
34
+ ## Architecture
35
+
36
+ ### The Nucleus DSL
37
+
38
+ The LLM outputs commands in the Nucleus DSL—an S-expression language designed for document analysis:
39
+
40
+ ```scheme
41
+ ; Search for patterns
42
+ (grep "SALES_DATA")
43
+
44
+ ; Filter results
45
+ (filter RESULTS (lambda x (match x "NORTH" 0)))
46
+
47
+ ; Aggregate
48
+ (sum RESULTS) ; Auto-extracts numbers like "$2,340,000" from lines
49
+ (count RESULTS) ; Count matching items
50
+
51
+ ; Final answer
52
+ <<<FINAL>>>13000000<<<END>>>
53
+ ```
54
+
55
+ ### The Lattice Engine
56
+
57
+ The Lattice engine (`src/logic/`) processes Nucleus commands:
58
+
59
+ 1. **Parser** (`lc-parser.ts`) - Parses S-expressions into an AST
60
+ 2. **Type Inference** (`type-inference.ts`) - Validates types before execution
61
+ 3. **Constraint Resolver** (`constraint-resolver.ts`) - Handles symbolic constraints like `[Σ⚡μ]`
62
+ 4. **Solver** (`lc-solver.ts`) - Executes commands against the document
63
+
64
+ Lattice uses **miniKanren** (a relational programming engine) for pattern classification and filtering operations.
65
+
66
+ ### Pre-Search Optimization
67
+
68
+ Before calling the LLM, the system extracts keywords from your query and pre-runs grep:
69
+
70
+ ```
71
+ Query: "What is the total of all north sales data values?"
72
+
73
+
74
+ ┌─────────────────────────────────────────────────────┐
75
+ │ Pre-search extracts: "north", "sales", "data" │
76
+ │ Tries compound patterns: SALES.*NORTH, NORTH.*SALES │
77
+ │ Pre-populates RESULTS before LLM is called │
78
+ └─────────────────────────────────────────────────────┘
79
+
80
+
81
+ ┌─────────────────────────────────────────────────────┐
82
+ │ LLM receives: "RESULTS has 1 match" │
83
+ │ LLM outputs: (sum RESULTS) ← skips search step! │
84
+ └─────────────────────────────────────────────────────┘
85
+ ```
86
+
87
+ This saves turns by pre-populating `RESULTS` so the model can immediately aggregate.
88
+
89
+ ### The Role of the LLM
90
+
91
+ The LLM does **reasoning**, not code generation:
92
+
93
+ 1. **Understands intent** - Interprets "total of north sales" as needing grep + filter + sum
94
+ 2. **Chooses operations** - Decides which Nucleus commands achieve the goal
95
+ 3. **Verifies results** - Checks if the current results answer the query
96
+ 4. **Iterates** - Refines search if results are too broad or narrow
97
+
98
+ The LLM never writes JavaScript. It outputs Nucleus commands that Lattice executes safely.
99
+
100
+ ### Components Summary
101
+
102
+ | Component | Purpose |
103
+ |-----------|---------|
104
+ | **Nucleus Adapter** | Prompts LLM to output Nucleus commands |
105
+ | **Lattice Parser** | Parses S-expressions to AST |
106
+ | **Lattice Solver** | Executes commands against document |
107
+ | **miniKanren** | Relational engine for classification |
108
+ | **Pre-Search** | Extracts keywords and pre-runs grep |
109
+ | **RAG Hints** | Few-shot examples from past successes |
110
+
11
111
  ## Installation
12
112
 
13
113
  ### npm (recommended)
@@ -19,7 +119,7 @@ npm install -g matryoshka-rlm
19
119
  ### npx (no install)
20
120
 
21
121
  ```bash
22
- npx matryoshka-rlm "Summarize this document" ./document.txt
122
+ npx matryoshka-rlm "What is the total of all sales values?" ./report.txt
23
123
  ```
24
124
 
25
125
  ### From source
@@ -43,7 +143,7 @@ Copy `config.example.json` to `config.json` and configure your LLM provider:
43
143
  "providers": {
44
144
  "ollama": {
45
145
  "baseUrl": "http://localhost:11434",
46
- "model": "qwen3-coder:30b",
146
+ "model": "qwen2.5-coder:7b",
47
147
  "options": { "temperature": 0.2, "num_ctx": 8192 }
48
148
  },
49
149
  "deepseek": {
@@ -62,10 +162,10 @@ Copy `config.example.json` to `config.json` and configure your LLM provider:
62
162
 
63
163
  ```bash
64
164
  # Basic usage
65
- rlm "Summarize this document" ./path/to/document.txt
165
+ rlm "What is the total of all sales values?" ./report.txt
66
166
 
67
167
  # With options
68
- rlm "Find all error codes" ./logs.txt --max-turns 15 --verbose
168
+ rlm "Count all ERROR entries" ./logs.txt --max-turns 15 --verbose
69
169
 
70
170
  # See all options
71
171
  rlm --help
@@ -73,7 +173,7 @@ rlm --help
73
173
 
74
174
  ### MCP Integration
75
175
 
76
- RLM includes an MCP (Model Context Protocol) server that exposes the `analyze_document` tool. This allows Claude to analyze documents that exceed its context window.
176
+ RLM includes an MCP (Model Context Protocol) server that exposes the `analyze_document` tool. This allows coding agents to analyze documents that exceed their context window.
77
177
 
78
178
  #### MCP Tool: `analyze_document`
79
179
 
@@ -84,63 +184,22 @@ RLM includes an MCP (Model Context Protocol) server that exposes the `analyze_do
84
184
  | `maxTurns` | number | No | Maximum exploration turns (default: 10) |
85
185
  | `timeoutMs` | number | No | Timeout per turn in milliseconds (default: 30000) |
86
186
 
87
- #### Claude Code
88
-
89
- Add the MCP server to Claude Code:
90
-
91
- ```bash
92
- # Add to user config (available in all projects)
93
- claude mcp add --transport stdio --scope user rlm -- rlm-mcp
94
-
95
- # Or add to current project only
96
- claude mcp add --transport stdio rlm -- rlm-mcp
97
-
98
- # Verify it's connected
99
- claude mcp list
100
- ```
101
-
102
- Then ask Claude to analyze documents:
103
-
104
- > Use the analyze_document tool to find all sales figures in /path/to/report.txt and calculate the total
105
-
106
- #### Claude Desktop
107
-
108
- Add to your Claude Desktop config file:
109
-
110
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
111
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
112
-
113
- Using npx (no global install needed):
114
-
115
- ```json
116
- {
117
- "mcpServers": {
118
- "rlm": {
119
- "command": "npx",
120
- "args": ["-y", "-p", "matryoshka-rlm", "rlm-mcp"]
121
- }
122
- }
123
- }
124
- ```
125
-
126
- Or if installed globally:
187
+ #### Example MCP config
127
188
 
128
189
  ```json
129
190
  {
130
- "mcpServers": {
191
+ "mcp": {
131
192
  "rlm": {
193
+ "type": "stdio",
132
194
  "command": "rlm-mcp"
133
195
  }
134
196
  }
135
197
  }
136
198
  ```
137
199
 
138
- Restart Claude Desktop after updating the config. Look for the hammer icon to confirm the server loaded.
139
-
140
200
  #### Testing the MCP Server
141
201
 
142
202
  ```bash
143
- # Verify the server starts correctly
144
203
  rlm-mcp --test
145
204
  # Output: MCP server ready
146
205
  # Output: Available tools: analyze_document
@@ -154,143 +213,204 @@ import { createLLMClient } from "matryoshka-rlm";
154
213
 
155
214
  const llmClient = createLLMClient("ollama", {
156
215
  baseUrl: "http://localhost:11434",
157
- model: "qwen3-coder:30b",
216
+ model: "qwen2.5-coder:7b",
158
217
  options: { temperature: 0.2 }
159
218
  });
160
219
 
161
- const result = await runRLM("What are the main themes?", "./book.txt", {
220
+ const result = await runRLM("What is the total of all sales values?", "./report.txt", {
162
221
  llmClient,
163
222
  maxTurns: 10,
164
223
  turnTimeoutMs: 30000,
165
224
  });
166
225
  ```
167
226
 
168
- ## Architecture
227
+ ## Example Session
169
228
 
170
- ```mermaid
171
- sequenceDiagram
172
- participant User
173
- participant RLM as RLM Engine
174
- participant Sandbox as JavaScript Sandbox
175
- participant LLM as LLM Provider
176
-
177
- User->>RLM: query + document path
178
- RLM->>Sandbox: Create sandbox with document as `context`
179
-
180
- loop Until FINAL or maxTurns
181
- RLM->>LLM: System prompt + history
182
- LLM-->>RLM: JavaScript code block
183
- RLM->>Sandbox: Execute code (with timeout)
184
- Sandbox-->>RLM: { result, logs, error }
185
- Note over RLM: Append output to history
186
- end
187
-
188
- RLM-->>User: Final answer
189
229
  ```
230
+ $ rlm "What is the total of all north sales data values?" ./report.txt --verbose
190
231
 
191
- ### Components
232
+ [Pre-search] Found 1 data matches for "SALES.*NORTH"
233
+ [Pre-search] RESULTS pre-populated with 1 matches
192
234
 
193
- | Component | Purpose |
194
- |-----------|---------|
195
- | **RLM Engine** | Orchestrates the turn loop, builds prompts, extracts answers |
196
- | **Sandbox** | Isolated VM executing LLM-generated JavaScript with timeout protection |
197
- | **Tools** | `text_stats()`, `fuzzy_search()`, `llm_query()` available in sandbox |
198
- | **Memory** | Persistent array for accumulating findings across turns |
235
+ ──────────────────────────────────────────────────
236
+ [Turn 1/10] Querying LLM...
237
+ [Turn 1] Term: (sum RESULTS)
238
+ [Turn 1] Console output:
239
+ [Lattice] Summing 1 values
240
+ [Lattice] Sum = 2340000
241
+ [Turn 1] Result: 2340000
199
242
 
200
- ### How It Works
243
+ ──────────────────────────────────────────────────
244
+ [Turn 2/10] Querying LLM...
245
+ [Turn 2] Final answer received
201
246
 
202
- 1. Document loads into sandbox as read-only `context` variable
203
- 2. LLM receives system prompt with available tools and writes JavaScript
204
- 3. Code executes in sandbox, results feed back to LLM
205
- 4. LLM iterates until it outputs `<<<FINAL>>>answer<<<END>>>`
206
- 5. Sub-queries via `llm_query()` enable recursive decomposition
247
+ 2340000
248
+ ```
207
249
 
208
- ### Sandbox Tools
250
+ The model:
251
+ 1. Received pre-populated RESULTS (pre-search found the data)
252
+ 2. Immediately summed the results (no grep needed)
253
+ 3. Output the final answer
209
254
 
210
- The LLM has access to these tools when exploring documents:
255
+ ## Nucleus DSL Reference
211
256
 
212
- | Tool | Description |
213
- |------|-------------|
214
- | `text_stats()` | Returns document metadata: length, line count, samples from start/middle/end |
215
- | `fuzzy_search(query, limit?)` | Finds approximate matches, returns lines with scores |
216
- | `llm_query(prompt)` | Spawns a sub-LLM call for complex analysis (limited by `maxSubCalls`) |
217
- | `context` | The full document text (read-only string) |
218
- | `memory` | Persistent array to accumulate findings across turns |
257
+ ### Search Commands
219
258
 
220
- ### Safety
259
+ ```scheme
260
+ (grep "pattern") ; Regex search, returns matches with line numbers
261
+ (fuzzy_search "query" 10) ; Fuzzy search, returns top N matches with scores
262
+ (text_stats) ; Document metadata (length, line count, samples)
263
+ ```
221
264
 
222
- - Sandbox isolates code execution (no filesystem, network, or process access)
223
- - Configurable timeout per turn
224
- - `maxSubCalls` limit prevents infinite recursion
225
- - Sub-LLM calls receive only the prompt, never parent context
226
- - Auto-fixes common syntax errors in LLM-generated code
265
+ ### Collection Operations
227
266
 
228
- ## Troubleshooting
267
+ ```scheme
268
+ (filter RESULTS (lambda x (match x "pattern" 0))) ; Filter by regex
269
+ (map RESULTS (lambda x (match x "(\\d+)" 1))) ; Extract from each
270
+ (sum RESULTS) ; Sum numbers in results
271
+ (count RESULTS) ; Count items
272
+ ```
229
273
 
230
- ### Model Answers Immediately Without Exploring
274
+ ### String Operations
231
275
 
232
- **Symptom**: The model provides an answer on the first turn without running any code, often with hallucinated data.
276
+ ```scheme
277
+ (match str "pattern" 0) ; Regex match, return group N
278
+ (replace str "from" "to") ; String replacement
279
+ (split str "," 0) ; Split and get index
280
+ (parseInt str) ; Parse integer
281
+ (parseFloat str) ; Parse float
282
+ ```
233
283
 
234
- **Cause**: Smaller or less capable models may not follow the instruction to explore via code before answering.
284
+ ### Type Coercion
235
285
 
236
- **Solutions**:
286
+ When the model sees data that needs parsing, it can use declarative type coercion:
237
287
 
238
- 1. **Use a more capable model** - Models like `deepseek-chat` or larger Ollama models follow instructions better
239
- 2. **Make your query more specific** - Instead of vague queries, be explicit:
240
- ```bash
241
- # Vague (may cause hallucination)
242
- rlm "What are the sales figures?" ./report.txt
288
+ ```scheme
289
+ ; Date parsing (returns ISO format YYYY-MM-DD)
290
+ (parseDate "Jan 15, 2024") ; -> "2024-01-15"
291
+ (parseDate "01/15/2024" "US") ; -> "2024-01-15" (MM/DD/YYYY)
292
+ (parseDate "15/01/2024" "EU") ; -> "2024-01-15" (DD/MM/YYYY)
243
293
 
244
- # Specific (guides exploration)
245
- rlm "Search for SALES_DATA entries and sum the dollar amounts" ./report.txt
246
- ```
247
- 3. **Include data patterns in your query** - If you know how data is formatted, mention it:
248
- ```bash
249
- rlm "Find lines matching 'Total:' and extract the numbers" ./data.txt
250
- ```
294
+ ; Currency parsing (handles $, €, commas, etc.)
295
+ (parseCurrency "$1,234.56") ; -> 1234.56
296
+ (parseCurrency "€1.234,56") ; -> 1234.56 (EU format)
251
297
 
252
- ### Max Turns Reached Without Answer
298
+ ; Number parsing
299
+ (parseNumber "1,234,567") ; -> 1234567
300
+ (parseNumber "50%") ; -> 0.5
253
301
 
254
- **Symptom**: Output shows "Max turns (N) reached without final answer"
302
+ ; General coercion
303
+ (coerce value "date") ; Coerce to date
304
+ (coerce value "currency") ; Coerce to currency
305
+ (coerce value "number") ; Coerce to number
306
+
307
+ ; Extract and coerce in one step
308
+ (extract str "\\$[\\d,]+" 0 "currency") ; Extract and parse as currency
309
+ ```
310
+
311
+ Use in map for batch transformations:
312
+
313
+ ```scheme
314
+ ; Parse all dates in results
315
+ (map RESULTS (lambda x (parseDate (match x "[A-Za-z]+ \\d+, \\d+" 0))))
316
+
317
+ ; Extract and sum currencies
318
+ (map RESULTS (lambda x (parseCurrency (match x "\\$[\\d,]+" 0))))
319
+ ```
255
320
 
256
- **Cause**: The model keeps exploring but never terminates properly.
321
+ ### Program Synthesis
322
+
323
+ For complex transformations, the model can synthesize functions from examples:
324
+
325
+ ```scheme
326
+ ; Synthesize from input/output pairs
327
+ (synthesize
328
+ ("$100" 100)
329
+ ("$1,234" 1234)
330
+ ("$50,000" 50000))
331
+ ; -> Returns a function that extracts numbers from currency strings
332
+ ```
333
+
334
+ This uses Barliman-style relational synthesis with miniKanren to automatically build extraction functions.
335
+
336
+ ### Cross-Turn State
337
+
338
+ Results from previous turns are available:
339
+ - `RESULTS` - Latest array result (updated by grep, filter)
340
+ - `_0`, `_1`, `_2`, ... - Results from specific turns
341
+
342
+ ### Final Answer
343
+
344
+ ```scheme
345
+ <<<FINAL>>>your answer here<<<END>>>
346
+ ```
347
+
348
+ ## Troubleshooting
349
+
350
+ ### Model Answers Without Exploring
351
+
352
+ **Symptom**: The model provides an answer immediately with hallucinated data.
257
353
 
258
354
  **Solutions**:
355
+ 1. Use a more capable model (7B+ recommended)
356
+ 2. Be specific in your query: "Find lines containing SALES_DATA and sum the dollar amounts"
357
+
358
+ ### Max Turns Reached
359
+
360
+ **Symptom**: "Max turns (N) reached without final answer"
259
361
 
362
+ **Solutions**:
260
363
  1. Increase `--max-turns` for complex documents
261
- 2. Check if the model is stuck in a loop (`--verbose` shows repeated patterns)
262
- 3. Simplify the query to require less exploration
364
+ 2. Check `--verbose` output for repeated patterns (model stuck in loop)
365
+ 3. Simplify the query
263
366
 
264
- ### Sandbox Execution Errors
367
+ ### Parse Errors
265
368
 
266
- **Symptom**: Repeated "Error: Unexpected token" or similar JavaScript errors
369
+ **Symptom**: "Parse error: no valid command"
267
370
 
268
- **Cause**: The model is generating invalid JavaScript code.
371
+ **Cause**: Model output malformed S-expression.
269
372
 
270
373
  **Solutions**:
271
-
272
- 1. The system auto-fixes common issues (missing semicolons, TypeScript syntax)
273
- 2. If errors persist, try a different model - some are better at generating valid code
274
- 3. Use `--verbose` to see what code the model is generating
374
+ 1. The system auto-converts JSON to S-expressions as fallback
375
+ 2. Use `--verbose` to see what the model is generating
376
+ 3. Try a different model tuned for code/symbolic output
275
377
 
276
378
  ## Development
277
379
 
278
380
  ```bash
279
- # Run tests
280
- npm test
381
+ npm test # Run tests
382
+ npm test -- --coverage # With coverage
383
+ RUN_E2E=1 npm test -- tests/e2e.test.ts # E2E tests (requires Ollama)
384
+ npm run build # Build
385
+ npm run typecheck # Type check
386
+ ```
281
387
 
282
- # Run with coverage
283
- npm test -- --coverage
388
+ ## Project Structure
284
389
 
285
- # E2E tests (requires Ollama running locally)
286
- RUN_E2E=1 npm test -- --run tests/e2e.test.ts
390
+ ```
391
+ src/
392
+ ├── adapters/ # Model-specific prompting
393
+ │ ├── nucleus.ts # Nucleus DSL adapter
394
+ │ └── types.ts # Adapter interface
395
+ ├── logic/ # Lattice engine
396
+ │ ├── lc-parser.ts # Nucleus parser
397
+ │ ├── lc-solver.ts # Command executor (uses miniKanren)
398
+ │ ├── type-inference.ts
399
+ │ └── constraint-resolver.ts
400
+ ├── minikanren/ # Relational programming engine
401
+ ├── synthesis/ # Program synthesis (Barliman-style)
402
+ │ └── evalo/ # Extractor DSL
403
+ ├── rag/ # Few-shot hint retrieval
404
+ └── rlm.ts # Main execution loop
405
+ ```
287
406
 
288
- # Build
289
- npm run build
407
+ ## Acknowledgements
290
408
 
291
- # Type check
292
- npm run typecheck
293
- ```
409
+ This project incorporates ideas and code from:
410
+
411
+ - **[Nucleus](https://github.com/michaelwhitford/nucleus)** - A symbolic S-expression language by Michael Whitford. RLM uses Nucleus syntax for the constrained DSL that the LLM outputs, providing a rigid grammar that reduces model errors.
412
+ - **[ramo](https://github.com/wjlewis/ramo)** - A miniKanren implementation in TypeScript by Will Lewis. Used for constraint-based program synthesis.
413
+ - **[Barliman](https://github.com/webyrd/Barliman)** - A prototype smart editor by William Byrd and Greg Rosenblatt that uses program synthesis to assist programmers. The Barliman-style approach of providing input/output constraints instead of code inspired the synthesis workflow.
294
414
 
295
415
  ## License
296
416
 
@@ -301,3 +421,4 @@ MIT
301
421
  - [RLM Paper](https://arxiv.org/abs/2512.24601)
302
422
  - [Original Implementation](https://github.com/alexzhang13/rlm)
303
423
  - [Model Context Protocol](https://modelcontextprotocol.io/)
424
+ - [miniKanren](http://minikanren.org/)
@@ -20,11 +20,6 @@
20
20
  }
21
21
  }
22
22
  },
23
- "sandbox": {
24
- "maxSubCalls": 10,
25
- "turnTimeoutMs": 30000,
26
- "memoryLimitMb": 128
27
- },
28
23
  "rlm": {
29
24
  "maxTurns": 10
30
25
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Base Model Adapter
3
+ *
4
+ * Default adapter implementation that works with most models.
5
+ * Other adapters can spread this and override specific methods.
6
+ */
7
+ import type { ModelAdapter, FinalVarMarker, RAGHints } from "./types.js";
8
+ /**
9
+ * Build the default system prompt for the RLM
10
+ */
11
+ declare function buildSystemPrompt(contextLength: number, toolInterfaces: string, hints?: RAGHints): string;
12
+ /**
13
+ * Extract code from LLM response
14
+ */
15
+ declare function extractCode(response: string): string | null;
16
+ /**
17
+ * Extract final answer from LLM response
18
+ */
19
+ declare function extractFinalAnswer(response: string | undefined | null): string | FinalVarMarker | null;
20
+ /**
21
+ * Get feedback message when model provides no code block
22
+ */
23
+ declare function getNoCodeFeedback(): string;
24
+ /**
25
+ * Get feedback message when code execution fails
26
+ */
27
+ declare function getErrorFeedback(error: string): string;
28
+ /**
29
+ * Get feedback message after successful code execution
30
+ * Generic reminder about continuing exploration or providing final answer
31
+ */
32
+ declare function getSuccessFeedback(): string;
33
+ /**
34
+ * Get feedback message when model repeats the same code
35
+ * Encourages a different approach
36
+ */
37
+ declare function getRepeatedCodeFeedback(): string;
38
+ /**
39
+ * Create the base adapter instance
40
+ */
41
+ export declare function createBaseAdapter(): ModelAdapter;
42
+ export { buildSystemPrompt as baseBuildSystemPrompt, extractCode as baseExtractCode, extractFinalAnswer as baseExtractFinalAnswer, getNoCodeFeedback as baseGetNoCodeFeedback, getErrorFeedback as baseGetErrorFeedback, getSuccessFeedback as baseGetSuccessFeedback, getRepeatedCodeFeedback as baseGetRepeatedCodeFeedback, };
43
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEzE;;GAEG;AACH,iBAAS,iBAAiB,CACxB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,QAAQ,GACf,MAAM,CAyDR;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUpD;AAED;;GAEG;AACH,iBAAS,kBAAkB,CACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,MAAM,GAAG,cAAc,GAAG,IAAI,CA6ChC;AAED;;GAEG;AACH,iBAAS,iBAAiB,IAAI,MAAM,CAMnC;AAED;;GAEG;AACH,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;GAGG;AACH,iBAAS,kBAAkB,IAAI,MAAM,CAEpC;AAED;;;GAGG;AACH,iBAAS,uBAAuB,IAAI,MAAM,CASzC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,CAWhD;AAGD,OAAO,EACL,iBAAiB,IAAI,qBAAqB,EAC1C,WAAW,IAAI,eAAe,EAC9B,kBAAkB,IAAI,sBAAsB,EAC5C,iBAAiB,IAAI,qBAAqB,EAC1C,gBAAgB,IAAI,oBAAoB,EACxC,kBAAkB,IAAI,sBAAsB,EAC5C,uBAAuB,IAAI,2BAA2B,GACvD,CAAC"}