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.
@@ -1,50 +0,0 @@
1
- """System prompt templates for RLM."""
2
-
3
-
4
- def build_system_prompt(context_size: int, depth: int = 0) -> str:
5
- """
6
- Build system prompt for RLM.
7
-
8
- Args:
9
- context_size: Size of context in characters
10
- depth: Current recursion depth
11
-
12
- Returns:
13
- System prompt string
14
- """
15
- # Minimal prompt (paper-style)
16
- prompt = f"""You are a Recursive Language Model. You interact with context through a Python REPL environment.
17
-
18
- The context is stored in variable `context` (not in this prompt). Size: {context_size:,} characters.
19
-
20
- Available in environment:
21
- - context: str (the document to analyze)
22
- - query: str (the question: "{"{"}query{"}"}")
23
- - recursive_llm(sub_query, sub_context) -> str (recursively process sub-context)
24
- - re: already imported regex module (use re.findall, re.search, etc.)
25
-
26
- Write Python code to answer the query. The last expression or print() output will be shown to you.
27
-
28
- Examples:
29
- - print(context[:100]) # See first 100 chars
30
- - errors = re.findall(r'ERROR', context) # Find all ERROR
31
- - count = len(errors); print(count) # Count and show
32
-
33
- When you have the answer, use FINAL("answer") - this is NOT a function, just write it as text.
34
-
35
- Depth: {depth}"""
36
-
37
- return prompt
38
-
39
-
40
- def build_user_prompt(query: str) -> str:
41
- """
42
- Build user prompt.
43
-
44
- Args:
45
- query: User's question
46
-
47
- Returns:
48
- User prompt string
49
- """
50
- return query
@@ -1,235 +0,0 @@
1
- """Safe REPL executor using RestrictedPython."""
2
-
3
- import io
4
- import sys
5
- from typing import Dict, Any, Optional
6
- from RestrictedPython import compile_restricted_exec, safe_globals, limited_builtins, utility_builtins
7
- from RestrictedPython.Guards import guarded_iter_unpack_sequence, safer_getattr
8
- from RestrictedPython.PrintCollector import PrintCollector
9
-
10
-
11
- class REPLError(Exception):
12
- """Error during REPL execution."""
13
- pass
14
-
15
-
16
- class REPLExecutor:
17
- """Safe Python code executor."""
18
-
19
- def __init__(self, timeout: int = 5, max_output_chars: int = 2000):
20
- """
21
- Initialize REPL executor.
22
-
23
- Args:
24
- timeout: Execution timeout in seconds (not currently enforced)
25
- max_output_chars: Maximum characters to return (truncate if longer)
26
- """
27
- self.timeout = timeout
28
- self.max_output_chars = max_output_chars
29
-
30
- def execute(self, code: str, env: Dict[str, Any]) -> str:
31
- """
32
- Execute Python code in restricted environment.
33
-
34
- Args:
35
- code: Python code to execute
36
- env: Environment with context, query, recursive_llm, etc.
37
-
38
- Returns:
39
- String result of execution (stdout or last expression)
40
-
41
- Raises:
42
- REPLError: If code execution fails
43
- """
44
- # Filter out code blocks if present (LLM might wrap code)
45
- code = self._extract_code(code)
46
-
47
- if not code.strip():
48
- return "No code to execute"
49
-
50
- # Build restricted globals
51
- restricted_globals = self._build_globals(env)
52
-
53
- # Capture stdout
54
- old_stdout = sys.stdout
55
- sys.stdout = captured_output = io.StringIO()
56
-
57
- try:
58
- # Compile with RestrictedPython
59
- byte_code = compile_restricted_exec(code)
60
-
61
- if byte_code.errors:
62
- raise REPLError(f"Compilation error: {', '.join(byte_code.errors)}")
63
-
64
- # Execute
65
- exec(byte_code.code, restricted_globals, env)
66
-
67
- # Get output from stdout
68
- output = captured_output.getvalue()
69
-
70
- # Get output from PrintCollector if available
71
- if '_print' in env and hasattr(env['_print'], '__call__'):
72
- # PrintCollector stores prints in its txt attribute
73
- print_collector = env['_print']
74
- if hasattr(print_collector, 'txt'):
75
- output += ''.join(print_collector.txt)
76
-
77
- # Check if last line was an expression (try to get its value)
78
- # This handles cases like: error_count (should return its value)
79
- lines = code.strip().split('\n')
80
- if lines:
81
- last_line = lines[-1].strip()
82
- # If last line is a simple expression (no assignment, no keyword)
83
- if last_line and not any(kw in last_line for kw in ['=', 'import', 'def', 'class', 'if', 'for', 'while', 'with']):
84
- try:
85
- # Try to evaluate the last line as expression
86
- result = eval(last_line, restricted_globals, env)
87
- if result is not None:
88
- output += str(result) + '\n'
89
- except:
90
- pass # Not an expression, ignore
91
-
92
- if not output:
93
- return "Code executed successfully (no output)"
94
-
95
- # Truncate output if too long (as per paper: "truncated version of output")
96
- if len(output) > self.max_output_chars:
97
- truncated = output[:self.max_output_chars]
98
- return f"{truncated}\n\n[Output truncated: {len(output)} chars total, showing first {self.max_output_chars}]"
99
-
100
- return output.strip()
101
-
102
- except Exception as e:
103
- raise REPLError(f"Execution error: {str(e)}")
104
-
105
- finally:
106
- sys.stdout = old_stdout
107
-
108
- def _extract_code(self, text: str) -> str:
109
- """
110
- Extract code from markdown code blocks if present.
111
-
112
- Args:
113
- text: Raw text that might contain code
114
-
115
- Returns:
116
- Extracted code
117
- """
118
- # Check for markdown code blocks
119
- if '```python' in text:
120
- start = text.find('```python') + len('```python')
121
- end = text.find('```', start)
122
- if end != -1:
123
- return text[start:end].strip()
124
-
125
- if '```' in text:
126
- start = text.find('```') + 3
127
- end = text.find('```', start)
128
- if end != -1:
129
- return text[start:end].strip()
130
-
131
- return text
132
-
133
- def _build_globals(self, env: Dict[str, Any]) -> Dict[str, Any]:
134
- """
135
- Build restricted globals for safe execution.
136
-
137
- Args:
138
- env: User environment
139
-
140
- Returns:
141
- Safe globals dict
142
- """
143
- restricted_globals = safe_globals.copy()
144
- restricted_globals.update(limited_builtins)
145
- restricted_globals.update(utility_builtins)
146
-
147
- # Add guards
148
- restricted_globals['_iter_unpack_sequence_'] = guarded_iter_unpack_sequence
149
- restricted_globals['_getattr_'] = safer_getattr
150
- restricted_globals['_getitem_'] = lambda obj, index: obj[index]
151
- restricted_globals['_getiter_'] = iter
152
- restricted_globals['_print_'] = PrintCollector
153
-
154
- # Add additional safe builtins
155
- restricted_globals.update({
156
- # Types
157
- 'len': len,
158
- 'str': str,
159
- 'int': int,
160
- 'float': float,
161
- 'bool': bool,
162
- 'list': list,
163
- 'dict': dict,
164
- 'tuple': tuple,
165
- 'set': set,
166
- 'frozenset': frozenset,
167
- 'bytes': bytes,
168
- 'bytearray': bytearray,
169
-
170
- # Iteration
171
- 'range': range,
172
- 'enumerate': enumerate,
173
- 'zip': zip,
174
- 'map': map,
175
- 'filter': filter,
176
- 'reversed': reversed,
177
- 'iter': iter,
178
- 'next': next,
179
-
180
- # Aggregation
181
- 'sorted': sorted,
182
- 'sum': sum,
183
- 'min': min,
184
- 'max': max,
185
- 'any': any,
186
- 'all': all,
187
-
188
- # Math
189
- 'abs': abs,
190
- 'round': round,
191
- 'pow': pow,
192
- 'divmod': divmod,
193
-
194
- # String/repr
195
- 'chr': chr,
196
- 'ord': ord,
197
- 'hex': hex,
198
- 'oct': oct,
199
- 'bin': bin,
200
- 'repr': repr,
201
- 'ascii': ascii,
202
- 'format': format,
203
-
204
- # Type checking
205
- 'isinstance': isinstance,
206
- 'issubclass': issubclass,
207
- 'callable': callable,
208
- 'type': type,
209
- 'hasattr': hasattr,
210
-
211
- # Constants
212
- 'True': True,
213
- 'False': False,
214
- 'None': None,
215
- })
216
-
217
- # Add safe standard library modules
218
- # These are read-only and don't allow file/network access
219
- import re
220
- import json
221
- import math
222
- from datetime import datetime, timedelta
223
- from collections import Counter, defaultdict
224
-
225
- restricted_globals.update({
226
- 're': re, # Regex (read-only)
227
- 'json': json, # JSON parsing (read-only)
228
- 'math': math, # Math functions
229
- 'datetime': datetime, # Date parsing
230
- 'timedelta': timedelta, # Time deltas
231
- 'Counter': Counter, # Counting helper
232
- 'defaultdict': defaultdict, # Dict with defaults
233
- })
234
-
235
- return restricted_globals
@@ -1,37 +0,0 @@
1
- """Type definitions for RLM."""
2
-
3
- from typing import TypedDict, Optional, Any, Callable, Awaitable
4
-
5
-
6
- class Message(TypedDict):
7
- """LLM message format."""
8
- role: str
9
- content: str
10
-
11
-
12
- class RLMConfig(TypedDict, total=False):
13
- """Configuration for RLM instance."""
14
- model: str
15
- recursive_model: Optional[str]
16
- api_base: Optional[str]
17
- api_key: Optional[str]
18
- max_depth: int
19
- max_iterations: int
20
- temperature: float
21
- timeout: int
22
-
23
-
24
- class REPLEnvironment(TypedDict, total=False):
25
- """REPL execution environment."""
26
- context: str
27
- query: str
28
- recursive_llm: Callable[[str, str], Awaitable[str]]
29
- re: Any # re module
30
-
31
-
32
- class CompletionResult(TypedDict):
33
- """Result from RLM completion."""
34
- answer: str
35
- iterations: int
36
- depth: int
37
- llm_calls: int
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env node
2
- const { execSync, execFileSync } = require('child_process');
3
- const path = require('path');
4
- const fs = require('fs');
5
-
6
- const pythonPackagePath = path.join(__dirname, '..', 'recursive-llm');
7
- const pyprojectPath = path.join(pythonPackagePath, 'pyproject.toml');
8
-
9
- // Check if pyproject.toml exists
10
- if (!fs.existsSync(pyprojectPath)) {
11
- console.warn('[recursive-llm-ts] Warning: pyproject.toml not found at', pyprojectPath);
12
- console.warn('[recursive-llm-ts] Python dependencies will need to be installed manually');
13
- process.exit(0); // Don't fail the install
14
- }
15
-
16
- console.log('[recursive-llm-ts] Installing Python dependencies...');
17
-
18
- try {
19
- const pythonCandidates = [];
20
- if (process.env.PYTHON) {
21
- pythonCandidates.push({ command: process.env.PYTHON, args: [] });
22
- }
23
- if (process.platform === 'win32') {
24
- pythonCandidates.push({ command: 'py', args: ['-3'] });
25
- pythonCandidates.push({ command: 'python', args: [] });
26
- pythonCandidates.push({ command: 'python3', args: [] });
27
- } else {
28
- pythonCandidates.push({ command: 'python3', args: [] });
29
- pythonCandidates.push({ command: 'python', args: [] });
30
- }
31
-
32
- let pythonCmd = null;
33
- for (const candidate of pythonCandidates) {
34
- try {
35
- execFileSync(candidate.command, [...candidate.args, '--version'], { stdio: 'pipe' });
36
- pythonCmd = candidate;
37
- break;
38
- } catch {
39
- // Try next candidate
40
- }
41
- }
42
-
43
- if (pythonCmd) {
44
- execFileSync(
45
- pythonCmd.command,
46
- [...pythonCmd.args, '-m', 'pip', 'install', '-e', pythonPackagePath],
47
- { stdio: 'inherit', cwd: pythonPackagePath }
48
- );
49
- } else {
50
- // Fall back to pip/pip3 if a Python executable wasn't found
51
- try {
52
- execSync('pip --version', { stdio: 'pipe' });
53
- } catch {
54
- execSync('pip3 --version', { stdio: 'pipe' });
55
- }
56
- const pipCommand = process.platform === 'win32'
57
- ? `pip install -e "${pythonPackagePath}"`
58
- : `pip install -e "${pythonPackagePath}" || pip3 install -e "${pythonPackagePath}"`;
59
- execSync(pipCommand, {
60
- stdio: 'inherit',
61
- cwd: pythonPackagePath
62
- });
63
- }
64
- console.log('[recursive-llm-ts] ✓ Python dependencies installed successfully');
65
- } catch (error) {
66
- console.warn('[recursive-llm-ts] Warning: Failed to auto-install Python dependencies');
67
- console.warn('[recursive-llm-ts] This is not critical - you can install them manually:');
68
- console.warn(`[recursive-llm-ts] cd node_modules/recursive-llm-ts/recursive-llm && python -m pip install -e .`);
69
- console.warn('[recursive-llm-ts] Or ensure Python 3.9+ and pip are in your PATH');
70
- // Don't fail the npm install - exit with 0
71
- process.exit(0);
72
- }