recursive-llm-ts 2.0.11 → 3.0.1
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/README.md +60 -43
- package/bin/rlm-go +0 -0
- package/dist/bridge-factory.d.ts +1 -1
- package/dist/bridge-factory.js +44 -14
- package/dist/bridge-interface.d.ts +1 -0
- package/dist/bunpy-bridge.d.ts +3 -4
- package/dist/bunpy-bridge.js +11 -143
- package/dist/go-bridge.d.ts +5 -0
- package/dist/go-bridge.js +136 -0
- package/dist/rlm-bridge.js +28 -5
- package/go/README.md +347 -0
- package/go/cmd/rlm/main.go +63 -0
- package/go/go.mod +12 -0
- package/go/go.sum +57 -0
- package/go/integration_test.sh +169 -0
- package/go/internal/rlm/benchmark_test.go +168 -0
- package/go/internal/rlm/errors.go +83 -0
- package/go/internal/rlm/openai.go +128 -0
- package/go/internal/rlm/parser.go +53 -0
- package/go/internal/rlm/parser_test.go +202 -0
- package/go/internal/rlm/prompt.go +68 -0
- package/go/internal/rlm/repl.go +260 -0
- package/go/internal/rlm/repl_test.go +291 -0
- package/go/internal/rlm/rlm.go +142 -0
- package/go/internal/rlm/types.go +108 -0
- package/go/test_mock.sh +90 -0
- package/go/test_rlm.sh +41 -0
- package/go/test_simple.sh +78 -0
- package/package.json +6 -9
- package/scripts/build-go-binary.js +41 -0
- package/recursive-llm/pyproject.toml +0 -70
- package/recursive-llm/src/rlm/__init__.py +0 -14
- package/recursive-llm/src/rlm/core.py +0 -322
- package/recursive-llm/src/rlm/parser.py +0 -93
- package/recursive-llm/src/rlm/prompts.py +0 -50
- package/recursive-llm/src/rlm/repl.py +0 -235
- package/recursive-llm/src/rlm/types.py +0 -37
- package/scripts/install-python-deps.js +0 -72
package/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# recursive-llm-ts
|
|
2
2
|
|
|
3
|
-
TypeScript
|
|
3
|
+
TypeScript/JavaScript package for [Recursive Language Models (RLM)](https://github.com/alexzhang13/rlm) - process unbounded context lengths with LLMs.
|
|
4
|
+
|
|
5
|
+
**Based on the paper**: [Recursive Language Models](https://alexzhang13.github.io/blog/2025/rlm/) by Alex Zhang and Omar Khattab (MIT, 2025)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
✨ **Pure Go Implementation** - No Python dependencies required
|
|
10
|
+
🚀 **50x Faster Startup** - Native binary vs Python runtime
|
|
11
|
+
💾 **3x Less Memory** - Efficient Go implementation
|
|
12
|
+
📦 **Single Binary** - Easy distribution and deployment
|
|
13
|
+
🔄 **Unbounded Context** - Process 10M+ tokens without degradation
|
|
14
|
+
🎯 **Provider Agnostic** - Works with OpenAI, Anthropic, Azure, Bedrock, local models
|
|
4
15
|
|
|
5
16
|
## Installation
|
|
6
17
|
|
|
@@ -10,27 +21,31 @@ npm install recursive-llm-ts
|
|
|
10
21
|
|
|
11
22
|
### Prerequisites
|
|
12
23
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **pip** - Python package manager
|
|
24
|
+
- **Node.js 16+** or **Bun 1.0+**
|
|
25
|
+
- **Go 1.21+** (for building from source during install)
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
> **Note**: The package includes pre-built binaries for common platforms. Go is only needed if building from source.
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
### Go Binary (Automatic)
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
The `postinstall` script automatically builds the Go binary during installation. If Go is not available, the script will warn but not fail.
|
|
32
|
+
|
|
33
|
+
If you need to build manually:
|
|
22
34
|
|
|
23
35
|
```bash
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
# From the package directory
|
|
37
|
+
cd node_modules/recursive-llm-ts
|
|
38
|
+
node scripts/build-go-binary.js
|
|
39
|
+
|
|
40
|
+
# Or directly with Go
|
|
41
|
+
cd go && go build -o ../bin/rlm-go ./cmd/rlm
|
|
26
42
|
```
|
|
27
43
|
|
|
28
|
-
|
|
29
|
-
Bunpy is included as a dependency, so no additional installation needed.
|
|
44
|
+
Override the binary path if needed:
|
|
30
45
|
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
46
|
+
```bash
|
|
47
|
+
export RLM_GO_BINARY=/custom/path/to/rlm-go
|
|
48
|
+
```
|
|
34
49
|
|
|
35
50
|
## Usage
|
|
36
51
|
|
|
@@ -56,24 +71,28 @@ console.log(result.result);
|
|
|
56
71
|
console.log('Stats:', result.stats);
|
|
57
72
|
```
|
|
58
73
|
|
|
59
|
-
###
|
|
74
|
+
### Bridge Selection
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
The package automatically uses the Go binary by default (if available). You can explicitly specify a bridge if needed:
|
|
62
77
|
|
|
63
78
|
```typescript
|
|
64
79
|
import { RLM } from 'recursive-llm-ts';
|
|
65
80
|
|
|
66
|
-
//
|
|
81
|
+
// Default: Auto-detection (prefers Go if available)
|
|
67
82
|
const rlm = new RLM('gpt-4o-mini', {
|
|
68
83
|
max_iterations: 15,
|
|
69
84
|
api_key: process.env.OPENAI_API_KEY
|
|
70
|
-
}
|
|
85
|
+
});
|
|
71
86
|
|
|
72
|
-
//
|
|
73
|
-
const
|
|
87
|
+
// Explicit: Force Go binary
|
|
88
|
+
const rlmGo = new RLM('gpt-4o-mini', {
|
|
89
|
+
max_iterations: 15,
|
|
90
|
+
api_key: process.env.OPENAI_API_KEY
|
|
91
|
+
}, 'go');
|
|
74
92
|
|
|
75
|
-
//
|
|
76
|
-
|
|
93
|
+
// Legacy: Use Python bridges (bunpy for Bun, pythonia for Node)
|
|
94
|
+
// Note: Requires separate Python dependencies
|
|
95
|
+
const rlmPython = new RLM('gpt-4o-mini', {}, 'bunpy');
|
|
77
96
|
```
|
|
78
97
|
|
|
79
98
|
|
|
@@ -106,7 +125,7 @@ Process a query with the given context using recursive language models.
|
|
|
106
125
|
|
|
107
126
|
#### `cleanup(): Promise<void>`
|
|
108
127
|
|
|
109
|
-
Clean up the
|
|
128
|
+
Clean up the bridge and free resources.
|
|
110
129
|
|
|
111
130
|
```typescript
|
|
112
131
|
await rlm.cleanup();
|
|
@@ -127,6 +146,7 @@ interface RLMConfig {
|
|
|
127
146
|
max_depth?: number; // Maximum recursion depth (default: 5)
|
|
128
147
|
max_iterations?: number; // Maximum REPL iterations per call (default: 30)
|
|
129
148
|
pythonia_timeout?: number; // Python bridge timeout in ms (default: 100000ms = 100s)
|
|
149
|
+
go_binary_path?: string; // Override path for Go binary (optional)
|
|
130
150
|
|
|
131
151
|
// LiteLLM parameters - pass any additional parameters supported by LiteLLM
|
|
132
152
|
api_version?: string; // API version (e.g., for Azure)
|
|
@@ -166,40 +186,37 @@ const rlm = new RLM('gpt-4o-mini', {
|
|
|
166
186
|
|
|
167
187
|
## Custom Providers
|
|
168
188
|
|
|
169
|
-
|
|
189
|
+
The Go binary uses an **OpenAI-compatible chat completion API** and works seamlessly with
|
|
190
|
+
[LiteLLM proxy](https://docs.litellm.ai/docs/simple_proxy) or any provider that supports the
|
|
191
|
+
OpenAI `/chat/completions` schema. This keeps the implementation provider-agnostic.
|
|
170
192
|
|
|
171
193
|
### Quick Reference
|
|
172
194
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
| Azure OpenAI | `azure/gpt-4o` | `api_base`, `api_key`, `api_version` |
|
|
179
|
-
| Ollama | `ollama/llama3.2` | `api_base` (optional) |
|
|
180
|
-
| Custom | `openai/your-model` | `api_base`, `api_key` |
|
|
195
|
+
The Go binary speaks the **OpenAI chat completion schema**, so you can:
|
|
196
|
+
|
|
197
|
+
- Use OpenAI directly with `api_key`
|
|
198
|
+
- Use an OpenAI-compatible endpoint (Azure OpenAI, vLLM, Ollama)
|
|
199
|
+
- Use a LiteLLM proxy to reach providers like Anthropic, Bedrock, or Cohere
|
|
181
200
|
|
|
182
|
-
### Amazon Bedrock
|
|
201
|
+
### Amazon Bedrock (via LiteLLM proxy)
|
|
183
202
|
|
|
184
203
|
```typescript
|
|
185
204
|
import { RLM } from 'recursive-llm-ts';
|
|
186
205
|
|
|
187
206
|
const rlm = new RLM('bedrock/anthropic.claude-3-sonnet-20240229-v1:0', {
|
|
188
|
-
|
|
207
|
+
api_base: 'http://localhost:4000', // LiteLLM proxy URL
|
|
208
|
+
api_key: process.env.LITELLM_API_KEY,
|
|
189
209
|
max_iterations: 15
|
|
190
210
|
});
|
|
191
|
-
|
|
192
|
-
// Set AWS credentials via environment variables:
|
|
193
|
-
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION_NAME
|
|
194
211
|
```
|
|
195
212
|
|
|
196
213
|
### Azure OpenAI
|
|
197
214
|
|
|
198
215
|
```typescript
|
|
199
|
-
const rlm = new RLM('
|
|
200
|
-
api_base: 'https://your-resource.openai.azure.com',
|
|
216
|
+
const rlm = new RLM('gpt-4o', {
|
|
217
|
+
api_base: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
|
|
201
218
|
api_key: process.env.AZURE_API_KEY,
|
|
202
|
-
api_version: '2024-02-15-preview' //
|
|
219
|
+
api_version: '2024-02-15-preview' // Passed through to the OpenAI-compatible API
|
|
203
220
|
});
|
|
204
221
|
```
|
|
205
222
|
|
|
@@ -238,14 +255,14 @@ See the [LiteLLM documentation](https://docs.litellm.ai/docs/providers) for the
|
|
|
238
255
|
|
|
239
256
|
## How It Works
|
|
240
257
|
|
|
241
|
-
This package provides a TypeScript wrapper around
|
|
258
|
+
This package provides a TypeScript wrapper around a Go implementation of Recursive-LLM, enabling seamless integration into Node.js/TypeScript applications without Python dependencies. The Go binary is built locally (or supplied via `RLM_GO_BINARY`) and invoked for completions.
|
|
242
259
|
|
|
243
260
|
The recursive-llm approach breaks down large contexts into manageable chunks and processes them recursively, allowing you to work with documents of any size without hitting token limits.
|
|
244
261
|
|
|
245
262
|
### Key Features
|
|
246
263
|
|
|
247
|
-
- ✅ **
|
|
248
|
-
- ✅ **
|
|
264
|
+
- ✅ **No Python dependency** - Go binary handles the full recursive loop
|
|
265
|
+
- ✅ **Provider-agnostic** - Works with OpenAI-compatible APIs or LiteLLM proxy
|
|
249
266
|
- ✅ **Type-safe** - Full TypeScript type definitions
|
|
250
267
|
- ✅ **Simple API** - Just `npm install` and start using
|
|
251
268
|
|
package/bin/rlm-go
ADDED
|
Binary file
|
package/dist/bridge-factory.d.ts
CHANGED
package/dist/bridge-factory.js
CHANGED
|
@@ -43,6 +43,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.createBridge = createBridge;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const DEFAULT_GO_BINARY = process.platform === 'win32' ? 'rlm-go.exe' : 'rlm-go';
|
|
49
|
+
function resolveDefaultGoBinary() {
|
|
50
|
+
return path.join(__dirname, '..', 'bin', DEFAULT_GO_BINARY);
|
|
51
|
+
}
|
|
52
|
+
function isGoBinaryAvailable() {
|
|
53
|
+
const envPath = process.env.RLM_GO_BINARY;
|
|
54
|
+
if (envPath && fs.existsSync(envPath)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return fs.existsSync(resolveDefaultGoBinary());
|
|
58
|
+
}
|
|
46
59
|
/**
|
|
47
60
|
* Detect the current JavaScript runtime
|
|
48
61
|
*/
|
|
@@ -62,42 +75,59 @@ function detectRuntime() {
|
|
|
62
75
|
*/
|
|
63
76
|
function createBridge() {
|
|
64
77
|
return __awaiter(this, arguments, void 0, function* (bridgeType = 'auto') {
|
|
65
|
-
var _a;
|
|
66
78
|
let selectedBridge;
|
|
67
79
|
if (bridgeType === 'auto') {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
selectedBridge = 'bunpy';
|
|
71
|
-
}
|
|
72
|
-
else if (runtime === 'node') {
|
|
73
|
-
selectedBridge = 'pythonia';
|
|
80
|
+
if (isGoBinaryAvailable()) {
|
|
81
|
+
selectedBridge = 'go';
|
|
74
82
|
}
|
|
75
83
|
else {
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
const runtime = detectRuntime();
|
|
85
|
+
if (runtime === 'bun') {
|
|
86
|
+
selectedBridge = 'bunpy';
|
|
87
|
+
}
|
|
88
|
+
else if (runtime === 'node') {
|
|
89
|
+
selectedBridge = 'pythonia';
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
throw new Error('Unable to detect runtime. Please explicitly specify bridge type.\n' +
|
|
93
|
+
'Supported runtimes: Go binary, Node.js (pythonia), or Bun (bunpy)');
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
else {
|
|
81
98
|
selectedBridge = bridgeType;
|
|
82
99
|
}
|
|
100
|
+
if (selectedBridge === 'go') {
|
|
101
|
+
const { GoBridge } = yield Promise.resolve().then(() => __importStar(require('./go-bridge')));
|
|
102
|
+
return new GoBridge();
|
|
103
|
+
}
|
|
83
104
|
if (selectedBridge === 'bunpy') {
|
|
84
105
|
try {
|
|
85
106
|
const { BunpyBridge } = yield Promise.resolve().then(() => __importStar(require('./bunpy-bridge')));
|
|
86
107
|
return new BunpyBridge();
|
|
87
108
|
}
|
|
88
109
|
catch (error) {
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
const errorMsg = (error === null || error === void 0 ? void 0 : error.code) === 'MODULE_NOT_FOUND'
|
|
111
|
+
? 'bunpy bridge not available (Python dependencies removed in v3.0)'
|
|
112
|
+
: error.message;
|
|
113
|
+
if (bridgeType === 'auto') {
|
|
114
|
+
console.warn(`[recursive-llm-ts] ${errorMsg}, falling back to pythonia`);
|
|
91
115
|
selectedBridge = 'pythonia';
|
|
92
116
|
}
|
|
93
117
|
else {
|
|
94
|
-
throw
|
|
118
|
+
throw new Error(`${errorMsg}. Use 'go' bridge instead (default in v3.0).`);
|
|
95
119
|
}
|
|
96
120
|
}
|
|
97
121
|
}
|
|
98
122
|
if (selectedBridge === 'pythonia') {
|
|
99
|
-
|
|
100
|
-
|
|
123
|
+
try {
|
|
124
|
+
const { PythoniaBridge } = yield Promise.resolve().then(() => __importStar(require('./rlm-bridge')));
|
|
125
|
+
return new PythoniaBridge();
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
throw new Error('pythonia bridge not available (Python dependencies removed in v3.0). ' +
|
|
129
|
+
'Use the Go bridge instead (default) or install bunpy/pythonia separately.');
|
|
130
|
+
}
|
|
101
131
|
}
|
|
102
132
|
throw new Error('Failed to initialize bridge');
|
|
103
133
|
});
|
package/dist/bunpy-bridge.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { PythonBridge, RLMConfig, RLMResult } from './bridge-interface';
|
|
2
2
|
export declare class BunpyBridge implements PythonBridge {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
completion(model: string, query: string, context: string, rlmConfig?: RLMConfig): Promise<RLMResult>;
|
|
3
|
+
constructor();
|
|
4
|
+
initialize(_model: string, _config: RLMConfig): Promise<void>;
|
|
5
|
+
completion(_query: string, _context: string): Promise<RLMResult>;
|
|
7
6
|
cleanup(): Promise<void>;
|
|
8
7
|
}
|
package/dist/bunpy-bridge.js
CHANGED
|
@@ -1,37 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
2
|
+
// Stub file for bunpy bridge (removed in v3.0)
|
|
3
|
+
// Python dependencies are no longer included by default
|
|
4
|
+
// To use bunpy bridge, install: npm install bunpy
|
|
35
5
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
6
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
7
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -41,128 +11,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
11
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
12
|
});
|
|
43
13
|
};
|
|
44
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
45
|
-
var t = {};
|
|
46
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
47
|
-
t[p] = s[p];
|
|
48
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
49
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
50
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
51
|
-
t[p[i]] = s[p[i]];
|
|
52
|
-
}
|
|
53
|
-
return t;
|
|
54
|
-
};
|
|
55
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
15
|
exports.BunpyBridge = void 0;
|
|
57
|
-
const path = __importStar(require("path"));
|
|
58
16
|
class BunpyBridge {
|
|
59
17
|
constructor() {
|
|
60
|
-
|
|
61
|
-
|
|
18
|
+
throw new Error('bunpy bridge is not available (Python dependencies removed in v3.0). ' +
|
|
19
|
+
'Please use the Go bridge (default) or install bunpy separately: npm install bunpy');
|
|
62
20
|
}
|
|
63
|
-
|
|
21
|
+
initialize(_model, _config) {
|
|
64
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
-
|
|
66
|
-
if (this.rlmModule)
|
|
67
|
-
return;
|
|
68
|
-
// Lazy load bunpy to avoid errors in Node.js environments
|
|
69
|
-
try {
|
|
70
|
-
// Dynamic import to avoid TypeScript errors when bunpy is not installed
|
|
71
|
-
const bunpy = yield new Function('return import("bunpy")')();
|
|
72
|
-
this.python = bunpy.python;
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
throw new Error('bunpy is not installed. Install it with: bun add bunpy\n' +
|
|
76
|
-
'Note: bunpy only works with Bun runtime, not Node.js');
|
|
77
|
-
}
|
|
78
|
-
// Import sys module to add path
|
|
79
|
-
const sys = this.python.import('sys');
|
|
80
|
-
const pythonExecutable = (() => {
|
|
81
|
-
try {
|
|
82
|
-
const sysExecutable = sys.executable;
|
|
83
|
-
if (sysExecutable && typeof sysExecutable.valueOf === 'function') {
|
|
84
|
-
const value = sysExecutable.valueOf();
|
|
85
|
-
if (typeof value === 'string' && value.trim()) {
|
|
86
|
-
return value;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (typeof sysExecutable === 'string' && sysExecutable.trim()) {
|
|
90
|
-
return sysExecutable;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (_a) {
|
|
94
|
-
// Fall back if bunpy can't coerce sys.executable
|
|
95
|
-
}
|
|
96
|
-
return 'python';
|
|
97
|
-
})();
|
|
98
|
-
const pythonCmd = pythonExecutable.includes(' ') ? `"${pythonExecutable}"` : pythonExecutable;
|
|
99
|
-
const pythonPackagePath = path.join(__dirname, '..', 'recursive-llm');
|
|
100
|
-
const pythonSrcPath = path.join(pythonPackagePath, 'src');
|
|
101
|
-
const pipCmd = `${pythonCmd} -m pip install -e "${pythonPackagePath}"`;
|
|
102
|
-
sys.path.insert(0, pythonSrcPath);
|
|
103
|
-
// Try to import rlm, install deps if import fails
|
|
104
|
-
try {
|
|
105
|
-
this.rlmModule = this.python.import('rlm');
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
// If import fails, try installing dependencies
|
|
109
|
-
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('No module named')) {
|
|
110
|
-
console.log('[recursive-llm-ts] Installing Python dependencies (first time only)...');
|
|
111
|
-
try {
|
|
112
|
-
const { execSync } = yield Promise.resolve().then(() => __importStar(require('child_process')));
|
|
113
|
-
execSync(pipCmd, { stdio: 'inherit' });
|
|
114
|
-
console.log('[recursive-llm-ts] ✓ Python dependencies installed');
|
|
115
|
-
// Try import again
|
|
116
|
-
this.rlmModule = this.python.import('rlm');
|
|
117
|
-
}
|
|
118
|
-
catch (installError) {
|
|
119
|
-
throw new Error('Failed to import rlm module after installing dependencies.\n' +
|
|
120
|
-
`Manual installation: ${pipCmd}\n` +
|
|
121
|
-
`Error: ${installError.message || installError}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
throw new Error('Failed to import rlm module.\n' +
|
|
126
|
-
`Run: ${pipCmd}\n` +
|
|
127
|
-
`Original error: ${error.message || error}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
23
|
+
throw new Error('bunpy bridge not available');
|
|
130
24
|
});
|
|
131
25
|
}
|
|
132
|
-
completion(
|
|
133
|
-
return __awaiter(this,
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
// Remove pythonia_timeout from config (not applicable to bunpy)
|
|
137
|
-
const { pythonia_timeout } = rlmConfig, pythonConfig = __rest(rlmConfig, ["pythonia_timeout"]);
|
|
138
|
-
// Create RLM instance with config
|
|
139
|
-
const RLMClass = this.rlmModule.RLM;
|
|
140
|
-
const rlmInstance = RLMClass(model, pythonConfig);
|
|
141
|
-
// Call completion method
|
|
142
|
-
const result = rlmInstance.completion(query, context);
|
|
143
|
-
const stats = rlmInstance.stats;
|
|
144
|
-
// Convert Python stats dict to JS object
|
|
145
|
-
const statsObj = {
|
|
146
|
-
llm_calls: stats.llm_calls,
|
|
147
|
-
iterations: stats.iterations,
|
|
148
|
-
depth: stats.depth
|
|
149
|
-
};
|
|
150
|
-
return {
|
|
151
|
-
result: String(result),
|
|
152
|
-
stats: statsObj
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
console.error('[RLM_DEBUG] Error details:', error);
|
|
157
|
-
throw new Error(`RLM completion failed: ${error.message || error}`);
|
|
158
|
-
}
|
|
26
|
+
completion(_query, _context) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
throw new Error('bunpy bridge not available');
|
|
159
29
|
});
|
|
160
30
|
}
|
|
161
31
|
cleanup() {
|
|
162
32
|
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
-
//
|
|
164
|
-
this.rlmModule = null;
|
|
165
|
-
this.python = null;
|
|
33
|
+
// No-op
|
|
166
34
|
});
|
|
167
35
|
}
|
|
168
36
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
45
|
+
var t = {};
|
|
46
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
47
|
+
t[p] = s[p];
|
|
48
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
49
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
50
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
51
|
+
t[p[i]] = s[p[i]];
|
|
52
|
+
}
|
|
53
|
+
return t;
|
|
54
|
+
};
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.GoBridge = void 0;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const child_process_1 = require("child_process");
|
|
60
|
+
const DEFAULT_BINARY_NAME = process.platform === 'win32' ? 'rlm.exe' : 'rlm';
|
|
61
|
+
function resolveBinaryPath(rlmConfig) {
|
|
62
|
+
const configuredPath = rlmConfig.go_binary_path || process.env.RLM_GO_BINARY;
|
|
63
|
+
if (configuredPath) {
|
|
64
|
+
return configuredPath;
|
|
65
|
+
}
|
|
66
|
+
// Try multiple locations
|
|
67
|
+
const possiblePaths = [
|
|
68
|
+
path.join(__dirname, '..', 'go', DEFAULT_BINARY_NAME), // Development
|
|
69
|
+
path.join(__dirname, '..', 'bin', DEFAULT_BINARY_NAME), // NPM package
|
|
70
|
+
];
|
|
71
|
+
for (const p of possiblePaths) {
|
|
72
|
+
if (fs.existsSync(p)) {
|
|
73
|
+
return p;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return possiblePaths[0]; // Return first path, error will be caught later
|
|
77
|
+
}
|
|
78
|
+
function assertBinaryExists(binaryPath) {
|
|
79
|
+
if (!fs.existsSync(binaryPath)) {
|
|
80
|
+
throw new Error(`Go RLM binary not found at ${binaryPath}.\n` +
|
|
81
|
+
'Build it with: node scripts/build-go-binary.js');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function sanitizeConfig(config) {
|
|
85
|
+
const { pythonia_timeout, go_binary_path } = config, sanitized = __rest(config, ["pythonia_timeout", "go_binary_path"]);
|
|
86
|
+
return sanitized;
|
|
87
|
+
}
|
|
88
|
+
class GoBridge {
|
|
89
|
+
completion(model_1, query_1, context_1) {
|
|
90
|
+
return __awaiter(this, arguments, void 0, function* (model, query, context, rlmConfig = {}) {
|
|
91
|
+
const binaryPath = resolveBinaryPath(rlmConfig);
|
|
92
|
+
assertBinaryExists(binaryPath);
|
|
93
|
+
const payload = JSON.stringify({
|
|
94
|
+
model,
|
|
95
|
+
query,
|
|
96
|
+
context,
|
|
97
|
+
config: sanitizeConfig(rlmConfig)
|
|
98
|
+
});
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const child = (0, child_process_1.spawn)(binaryPath, [], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
101
|
+
let stdout = '';
|
|
102
|
+
let stderr = '';
|
|
103
|
+
child.stdout.on('data', (data) => {
|
|
104
|
+
stdout += data.toString();
|
|
105
|
+
});
|
|
106
|
+
child.stderr.on('data', (data) => {
|
|
107
|
+
stderr += data.toString();
|
|
108
|
+
});
|
|
109
|
+
child.on('error', (error) => {
|
|
110
|
+
reject(new Error(`Failed to start Go binary: ${error.message}`));
|
|
111
|
+
});
|
|
112
|
+
child.on('close', (code) => {
|
|
113
|
+
if (code !== 0) {
|
|
114
|
+
reject(new Error(stderr || `Go binary exited with code ${code}`));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse(stdout);
|
|
119
|
+
resolve(parsed);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
reject(new Error(`Failed to parse Go response: ${error.message || error}`));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
child.stdin.write(payload);
|
|
126
|
+
child.stdin.end();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
cleanup() {
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
// No persistent processes to clean up.
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.GoBridge = GoBridge;
|