groknroll 2.0.0__py3-none-any.whl
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.
- groknroll/__init__.py +36 -0
- groknroll/__main__.py +9 -0
- groknroll/agents/__init__.py +18 -0
- groknroll/agents/agent_manager.py +187 -0
- groknroll/agents/base_agent.py +118 -0
- groknroll/agents/build_agent.py +231 -0
- groknroll/agents/plan_agent.py +215 -0
- groknroll/cli/__init__.py +7 -0
- groknroll/cli/enhanced_cli.py +372 -0
- groknroll/cli/large_codebase_cli.py +413 -0
- groknroll/cli/main.py +331 -0
- groknroll/cli/rlm_commands.py +258 -0
- groknroll/clients/__init__.py +63 -0
- groknroll/clients/anthropic.py +112 -0
- groknroll/clients/azure_openai.py +142 -0
- groknroll/clients/base_lm.py +33 -0
- groknroll/clients/gemini.py +162 -0
- groknroll/clients/litellm.py +105 -0
- groknroll/clients/openai.py +129 -0
- groknroll/clients/portkey.py +94 -0
- groknroll/core/__init__.py +9 -0
- groknroll/core/agent.py +339 -0
- groknroll/core/comms_utils.py +264 -0
- groknroll/core/context.py +251 -0
- groknroll/core/exceptions.py +181 -0
- groknroll/core/large_codebase.py +564 -0
- groknroll/core/lm_handler.py +206 -0
- groknroll/core/rlm.py +446 -0
- groknroll/core/rlm_codebase.py +448 -0
- groknroll/core/rlm_integration.py +256 -0
- groknroll/core/types.py +276 -0
- groknroll/environments/__init__.py +34 -0
- groknroll/environments/base_env.py +182 -0
- groknroll/environments/constants.py +32 -0
- groknroll/environments/docker_repl.py +336 -0
- groknroll/environments/local_repl.py +388 -0
- groknroll/environments/modal_repl.py +502 -0
- groknroll/environments/prime_repl.py +588 -0
- groknroll/logger/__init__.py +4 -0
- groknroll/logger/rlm_logger.py +63 -0
- groknroll/logger/verbose.py +393 -0
- groknroll/operations/__init__.py +15 -0
- groknroll/operations/bash_ops.py +447 -0
- groknroll/operations/file_ops.py +473 -0
- groknroll/operations/git_ops.py +620 -0
- groknroll/oracle/__init__.py +11 -0
- groknroll/oracle/codebase_indexer.py +238 -0
- groknroll/oracle/oracle_agent.py +278 -0
- groknroll/setup.py +34 -0
- groknroll/storage/__init__.py +14 -0
- groknroll/storage/database.py +272 -0
- groknroll/storage/models.py +128 -0
- groknroll/utils/__init__.py +0 -0
- groknroll/utils/parsing.py +168 -0
- groknroll/utils/prompts.py +146 -0
- groknroll/utils/rlm_utils.py +19 -0
- groknroll-2.0.0.dist-info/METADATA +246 -0
- groknroll-2.0.0.dist-info/RECORD +62 -0
- groknroll-2.0.0.dist-info/WHEEL +5 -0
- groknroll-2.0.0.dist-info/entry_points.txt +3 -0
- groknroll-2.0.0.dist-info/licenses/LICENSE +21 -0
- groknroll-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
|
|
3
|
+
from groknroll.core.types import QueryMetadata
|
|
4
|
+
|
|
5
|
+
# System prompt for the REPL environment with explicit final answer checking
|
|
6
|
+
RLM_SYSTEM_PROMPT = textwrap.dedent(
|
|
7
|
+
"""You are tasked with answering a query with associated context. You can access, transform, and analyze this context interactively in a REPL environment that can recursively query sub-LLMs, which you are strongly encouraged to use as much as possible. You will be queried iteratively until you provide a final answer.
|
|
8
|
+
|
|
9
|
+
The REPL environment is initialized with:
|
|
10
|
+
1. A `context` variable that contains extremely important information about your query. You should check the content of the `context` variable to understand what you are working with. Make sure you look through it sufficiently as you answer your query.
|
|
11
|
+
2. A `llm_query` function that allows you to query an LLM (that can handle around 500K chars) inside your REPL environment.
|
|
12
|
+
3. A `llm_query_batched` function that allows you to query multiple prompts concurrently: `llm_query_batched(prompts: List[str]) -> List[str]`. This is much faster than sequential `llm_query` calls when you have multiple independent queries. Results are returned in the same order as the input prompts.
|
|
13
|
+
4. The ability to use `print()` statements to view the output of your REPL code and continue your reasoning.
|
|
14
|
+
|
|
15
|
+
You will only be able to see truncated outputs from the REPL environment, so you should use the query LLM function on variables you want to analyze. You will find this function especially useful when you have to analyze the semantics of the context. Use these variables as buffers to build up your final answer.
|
|
16
|
+
Make sure to explicitly look through the entire context in REPL before answering your query. An example strategy is to first look at the context and figure out a chunking strategy, then break up the context into smart chunks, and query an LLM per chunk with a particular question and save the answers to a buffer, then query an LLM with all the buffers to produce your final answer.
|
|
17
|
+
|
|
18
|
+
You can use the REPL environment to help you understand your context, especially if it is huge. Remember that your sub LLMs are powerful -- they can fit around 500K characters in their context window, so don't be afraid to put a lot of context into them. For example, a viable strategy is to feed 10 documents per sub-LLM query. Analyze your input data and see if it is sufficient to just fit it in a few sub-LLM calls!
|
|
19
|
+
|
|
20
|
+
When you want to execute Python code in the REPL environment, wrap it in triple backticks with 'repl' language identifier. For example, say we want our recursive model to search for the magic number in the context (assuming the context is a string), and the context is very long, so we want to chunk it:
|
|
21
|
+
```repl
|
|
22
|
+
chunk = context[:10000]
|
|
23
|
+
answer = llm_query(f"What is the magic number in the context? Here is the chunk: {{chunk}}")
|
|
24
|
+
print(answer)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
As an example, suppose you're trying to answer a question about a book. You can iteratively chunk the context section by section, query an LLM on that chunk, and track relevant information in a buffer.
|
|
28
|
+
```repl
|
|
29
|
+
query = "In Harry Potter and the Sorcerer's Stone, did Gryffindor win the House Cup because they led?"
|
|
30
|
+
for i, section in enumerate(context):
|
|
31
|
+
if i == len(context) - 1:
|
|
32
|
+
buffer = llm_query(f"You are on the last section of the book. So far you know that: {{buffers}}. Gather from this last section to answer {{query}}. Here is the section: {{section}}")
|
|
33
|
+
print(f"Based on reading iteratively through the book, the answer is: {{buffer}}")
|
|
34
|
+
else:
|
|
35
|
+
buffer = llm_query(f"You are iteratively looking through a book, and are on section {{i}} of {{len(context)}}. Gather information to help answer {{query}}. Here is the section: {{section}}")
|
|
36
|
+
print(f"After section {{i}} of {{len(context)}}, you have tracked: {{buffer}}")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
As another example, when the context isn't that long (e.g. >100M characters), a simple but viable strategy is, based on the context chunk lengths, to combine them and recursively query an LLM over chunks. For example, if the context is a List[str], we ask the same query over each chunk using `llm_query_batched` for concurrent processing:
|
|
40
|
+
```repl
|
|
41
|
+
query = "A man became famous for his book "The Great Gatsby". How many jobs did he have?"
|
|
42
|
+
# Suppose our context is ~1M chars, and we want each sub-LLM query to be ~0.1M chars so we split it into 10 chunks
|
|
43
|
+
chunk_size = len(context) // 10
|
|
44
|
+
chunks = []
|
|
45
|
+
for i in range(10):
|
|
46
|
+
if i < 9:
|
|
47
|
+
chunk_str = "\n".join(context[i*chunk_size:(i+1)*chunk_size])
|
|
48
|
+
else:
|
|
49
|
+
chunk_str = "\n".join(context[i*chunk_size:])
|
|
50
|
+
chunks.append(chunk_str)
|
|
51
|
+
|
|
52
|
+
# Use batched query for concurrent processing - much faster than sequential calls!
|
|
53
|
+
prompts = [f"Try to answer the following query: {{query}}. Here are the documents:\n{{chunk}}. Only answer if you are confident in your answer based on the evidence." for chunk in chunks]
|
|
54
|
+
answers = llm_query_batched(prompts)
|
|
55
|
+
for i, answer in enumerate(answers):
|
|
56
|
+
print(f"I got the answer from chunk {{i}}: {{answer}}")
|
|
57
|
+
final_answer = llm_query(f"Aggregating all the answers per chunk, answer the original query about total number of jobs: {{query}}\\n\\nAnswers:\\n" + "\\n".join(answers))
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
As a final example, after analyzing the context and realizing its separated by Markdown headers, we can maintain state through buffers by chunking the context by headers, and iteratively querying an LLM over it:
|
|
61
|
+
```repl
|
|
62
|
+
# After finding out the context is separated by Markdown headers, we can chunk, summarize, and answer
|
|
63
|
+
import re
|
|
64
|
+
sections = re.split(r'### (.+)', context["content"])
|
|
65
|
+
buffers = []
|
|
66
|
+
for i in range(1, len(sections), 2):
|
|
67
|
+
header = sections[i]
|
|
68
|
+
info = sections[i+1]
|
|
69
|
+
summary = llm_query(f"Summarize this {{header}} section: {{info}}")
|
|
70
|
+
buffers.append(f"{{header}}: {{summary}}")
|
|
71
|
+
final_answer = llm_query(f"Based on these summaries, answer the original query: {{query}}\\n\\nSummaries:\\n" + "\\n".join(buffers))
|
|
72
|
+
```
|
|
73
|
+
In the next step, we can return FINAL_VAR(final_answer).
|
|
74
|
+
|
|
75
|
+
IMPORTANT: When you are done with the iterative process, you MUST provide a final answer inside a FINAL function when you have completed your task, NOT in code. Do not use these tags unless you have completed your task. You have two options:
|
|
76
|
+
1. Use FINAL(your final answer here) to provide the answer directly
|
|
77
|
+
2. Use FINAL_VAR(variable_name) to return a variable you have created in the REPL environment as your final output
|
|
78
|
+
|
|
79
|
+
Think step by step carefully, plan, and execute this plan immediately in your response -- do not just say "I will do this" or "I will do that". Output to the REPL environment and recursive LLMs as much as possible. Remember to explicitly answer the original query in your final answer.
|
|
80
|
+
"""
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def build_rlm_system_prompt(
|
|
85
|
+
system_prompt: str,
|
|
86
|
+
query_metadata: QueryMetadata,
|
|
87
|
+
) -> list[dict[str, str]]:
|
|
88
|
+
"""
|
|
89
|
+
Build the initial system prompt for the REPL environment based on extra prompt metadata.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
query_metadata: QueryMetadata object containing context metadata
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
List of message dictionaries
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
context_lengths = query_metadata.context_lengths
|
|
99
|
+
context_total_length = query_metadata.context_total_length
|
|
100
|
+
context_type = query_metadata.context_type
|
|
101
|
+
|
|
102
|
+
# If there are more than 100 chunks, truncate to the first 100 chunks.
|
|
103
|
+
if len(context_lengths) > 100:
|
|
104
|
+
others = len(context_lengths) - 100
|
|
105
|
+
context_lengths = str(context_lengths[:100]) + "... [" + str(others) + " others]"
|
|
106
|
+
|
|
107
|
+
metadata_prompt = f"Your context is a {context_type} with {context_total_length} total characters, and is broken up into chunks of char lengths: {context_lengths}."
|
|
108
|
+
|
|
109
|
+
return [
|
|
110
|
+
{"role": "system", "content": system_prompt},
|
|
111
|
+
{"role": "assistant", "content": metadata_prompt},
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
USER_PROMPT = """Think step-by-step on what to do using the REPL environment (which contains the context) to answer the prompt.\n\nContinue using the REPL environment, which has the `context` variable, and querying sub-LLMs by writing to ```repl``` tags, and determine your answer. Your next action:"""
|
|
116
|
+
USER_PROMPT_WITH_ROOT = """Think step-by-step on what to do using the REPL environment (which contains the context) to answer the original prompt: \"{root_prompt}\".\n\nContinue using the REPL environment, which has the `context` variable, and querying sub-LLMs by writing to ```repl``` tags, and determine your answer. Your next action:"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def build_user_prompt(
|
|
120
|
+
root_prompt: str | None = None,
|
|
121
|
+
iteration: int = 0,
|
|
122
|
+
context_count: int = 1,
|
|
123
|
+
history_count: int = 0,
|
|
124
|
+
) -> dict[str, str]:
|
|
125
|
+
if iteration == 0:
|
|
126
|
+
safeguard = "You have not interacted with the REPL environment or seen your prompt / context yet. Your next action should be to look through and figure out how to answer the prompt, so don't just provide a final answer yet.\n\n"
|
|
127
|
+
prompt = safeguard + (
|
|
128
|
+
USER_PROMPT_WITH_ROOT.format(root_prompt=root_prompt) if root_prompt else USER_PROMPT
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
prompt = "The history before is your previous interactions with the REPL environment. " + (
|
|
132
|
+
USER_PROMPT_WITH_ROOT.format(root_prompt=root_prompt) if root_prompt else USER_PROMPT
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Inform model about multiple contexts if present
|
|
136
|
+
if context_count > 1:
|
|
137
|
+
prompt += f"\n\nNote: You have {context_count} contexts available (context_0 through context_{context_count - 1})."
|
|
138
|
+
|
|
139
|
+
# Inform model about prior conversation histories if present
|
|
140
|
+
if history_count > 0:
|
|
141
|
+
if history_count == 1:
|
|
142
|
+
prompt += "\n\nNote: You have 1 prior conversation history available in the `history` variable."
|
|
143
|
+
else:
|
|
144
|
+
prompt += f"\n\nNote: You have {history_count} prior conversation histories available (history_0 through history_{history_count - 1})."
|
|
145
|
+
|
|
146
|
+
return {"role": "user", "content": prompt}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def filter_sensitive_keys(kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
5
|
+
"""Filter out sensitive keys like API keys from kwargs.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
kwargs: Dictionary of keyword arguments that may contain sensitive keys
|
|
9
|
+
|
|
10
|
+
Returns:
|
|
11
|
+
Filtered dictionary with sensitive keys removed
|
|
12
|
+
"""
|
|
13
|
+
filtered: dict[str, Any] = {}
|
|
14
|
+
for key, value in kwargs.items():
|
|
15
|
+
key_lower: str = key.lower()
|
|
16
|
+
if "api" in key_lower and "key" in key_lower:
|
|
17
|
+
continue
|
|
18
|
+
filtered[key] = value
|
|
19
|
+
return filtered
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: groknroll
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: groknroll - CLI coding agent with unlimited context via Recursive Language Models (RLM). Better than Claude Code.
|
|
5
|
+
Author-email: Michael Thornton <tekcin@yahoo.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tekcin/groknroll
|
|
8
|
+
Project-URL: Documentation, https://github.com/tekcin/groknroll/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/tekcin/groknroll
|
|
10
|
+
Project-URL: Issues, https://github.com/tekcin/groknroll/issues
|
|
11
|
+
Keywords: groknroll,rlm,llm,language-models,ai,ml,context,infinite-context,recursive,coding-agent,cli,assistant
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: anthropic>=0.40.0
|
|
24
|
+
Requires-Dist: google-genai>=1.0.0
|
|
25
|
+
Requires-Dist: openai>=1.0.0
|
|
26
|
+
Requires-Dist: portkey-ai>=1.0.0
|
|
27
|
+
Requires-Dist: click>=8.1.0
|
|
28
|
+
Requires-Dist: rich>=13.0.0
|
|
29
|
+
Requires-Dist: blessed>=1.20.0
|
|
30
|
+
Requires-Dist: inquirer>=3.1.0
|
|
31
|
+
Requires-Dist: radon>=6.0.0
|
|
32
|
+
Requires-Dist: bandit>=1.7.0
|
|
33
|
+
Requires-Dist: gitpython>=3.1.0
|
|
34
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
35
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
36
|
+
Requires-Dist: requests>=2.28.0
|
|
37
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
38
|
+
Requires-Dist: jinja2>=3.1.0
|
|
39
|
+
Provides-Extra: modal
|
|
40
|
+
Requires-Dist: modal>=0.73.0; extra == "modal"
|
|
41
|
+
Requires-Dist: dill>=0.3.7; extra == "modal"
|
|
42
|
+
Provides-Extra: prime
|
|
43
|
+
Requires-Dist: prime-sandboxes>=0.2.0; extra == "prime"
|
|
44
|
+
Requires-Dist: dill>=0.3.7; extra == "prime"
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
48
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
49
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
50
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
51
|
+
Requires-Dist: ruff>=0.0.290; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
<h1 align="center" style="font-size:2.8em">
|
|
58
|
+
<span>Recursive Language Models (<span style="color:orange">RLM</span>s)</span>
|
|
59
|
+
</h1>
|
|
60
|
+
|
|
61
|
+
<p align="center" style="font-size:1.3em">
|
|
62
|
+
<a href="https://arxiv.org/abs/2512.24601">Full Paper</a> •
|
|
63
|
+
<a href="https://alexzhang13.github.io/blog/2025/rlm/">Blogpost</a> •
|
|
64
|
+
<a href="https://alexzhang13.github.io/rlm/">Documentation</a> •
|
|
65
|
+
<a href="https://github.com/alexzhang13/rlm-minimal">RLM Minimal</a>
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<a href="https://github.com/alexzhang13/rlm/actions/workflows/style.yml">
|
|
70
|
+
<img src="https://github.com/alexzhang13/rlm/actions/workflows/style.yml/badge.svg" alt="Style" />
|
|
71
|
+
</a>
|
|
72
|
+
<a href="https://github.com/alexzhang13/rlm/actions/workflows/test.yml">
|
|
73
|
+
<img src="https://github.com/alexzhang13/rlm/actions/workflows/test.yml/badge.svg" alt="Test" />
|
|
74
|
+
</a>
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<p align="center">
|
|
78
|
+
<a href="https://arxiv.org/abs/2512.24601">
|
|
79
|
+
<img src="media/paper_preview.png" alt="Paper Preview" width="300"/>
|
|
80
|
+
</a>
|
|
81
|
+
</p>
|
|
82
|
+
|
|
83
|
+
## Overview
|
|
84
|
+
Recursive Language Models (RLMs) are a task-agnostic inference paradigm for language models (LMs) to handle near-infinite length contexts by enabling the LM to *programmatically* examine, decompose, and recursively call itself over its input. RLMs replace the canonical `llm.completion(prompt, model)` call with a `rlm.completion(prompt, model)` call. RLMs offload the context as a variable in a REPL environment that the LM can interact with and launch sub-LM calls inside of.
|
|
85
|
+
|
|
86
|
+
This repository provides an extensible inference engine for using RLMs around standard API-based and local LLMs. The initial experiments and idea were proposed in a [blogpost](https://alexzhang13.github.io/blog/2025/rlm/) in 2025, with expanded results in an [arXiv preprint](https://arxiv.org/abs/2512.24601).
|
|
87
|
+
|
|
88
|
+
> [!NOTE]
|
|
89
|
+
> This repository contains inference code for RLMs with support for various sandbox environments. Open-source contributions are welcome. This repository is maintained by the authors of the paper from the MIT OASYS lab.
|
|
90
|
+
|
|
91
|
+
<!-- ## Installation
|
|
92
|
+
```
|
|
93
|
+
pip install groknroll
|
|
94
|
+
```
|
|
95
|
+
To install the latest from `main`:
|
|
96
|
+
```
|
|
97
|
+
pip install git+https://github.com/alexzhang13/rlm.git
|
|
98
|
+
```
|
|
99
|
+
``` -->
|
|
100
|
+
|
|
101
|
+
## Quick Setup
|
|
102
|
+
Set up the dependencies with `uv` (or your virtual environment of choice):
|
|
103
|
+
```bash
|
|
104
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
105
|
+
uv init && uv venv --python 3.12 # change version as needed
|
|
106
|
+
uv pip install -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This project includes a `Makefile` to simplify common tasks.
|
|
110
|
+
|
|
111
|
+
- `make install`: Install base dependencies.
|
|
112
|
+
- `make check`: Run linter, formatter, and tests.
|
|
113
|
+
|
|
114
|
+
To run a quick test, the following will run an RLM query with the OpenAI client using your environment variable `OPENAI_API_KEY` (feel free to change this). This will generate console output as well as a log which you can use with the visualizer to explore the trajectories.
|
|
115
|
+
```bash
|
|
116
|
+
make quickstart
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The default RLM client uses a REPL environment that runs on the host process through Python `exec` calls. It uses the same virtual environment as the host process (i.e. it will have access to the same dependencies), but with some limitations in its available global modules. As an example, we can call RLM completions using GPT-5-nano:
|
|
120
|
+
```python
|
|
121
|
+
from groknroll import RLM
|
|
122
|
+
|
|
123
|
+
rlm = RLM(
|
|
124
|
+
backend="openai",
|
|
125
|
+
backend_kwargs={"model_name": "gpt-5-nano"},
|
|
126
|
+
verbose=True, # For printing to console with rich, disabled by default.
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
print(rlm.completion("Print me the first 100 powers of two, each on a newline.").response)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Oracle Agent - Codebase Knowledge System 🔮
|
|
133
|
+
|
|
134
|
+
The Oracle Agent is an RLM-powered tool that has **unlimited context** and knows **everything** about your codebase. It can answer any question about your code by leveraging RLM's infinite context capabilities.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from groknroll import OracleAgent
|
|
138
|
+
|
|
139
|
+
# Initialize Oracle for your project
|
|
140
|
+
oracle = OracleAgent(
|
|
141
|
+
project_path=".",
|
|
142
|
+
backend="openai",
|
|
143
|
+
model="gpt-4o-mini"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Ask any question about your codebase
|
|
147
|
+
response = oracle.ask("Where is the RLM class defined?")
|
|
148
|
+
print(response.answer)
|
|
149
|
+
|
|
150
|
+
# Use convenience methods
|
|
151
|
+
oracle.find_class("RLM")
|
|
152
|
+
oracle.find_function("completion")
|
|
153
|
+
oracle.get_architecture_overview()
|
|
154
|
+
oracle.how_to_add_feature("support for Claude AI backend")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Features:**
|
|
158
|
+
- **Unlimited Context**: Handles arbitrarily large codebases via RLM
|
|
159
|
+
- **Automatic Indexing**: Parses files, functions, classes, imports using AST
|
|
160
|
+
- **Semantic Understanding**: Understands what your code does, not just keywords
|
|
161
|
+
- **Comprehensive Answers**: Detailed explanations with code examples and sources
|
|
162
|
+
|
|
163
|
+
**See [ORACLE_AGENT.md](ORACLE_AGENT.md) for full documentation.**
|
|
164
|
+
|
|
165
|
+
## REPL Environments
|
|
166
|
+
We support two types of REPL environments -- isolated, and non-isolated. Non-isolated environments (default) run code execution on the same machine as the RLM (e.g. through `exec`), which is pretty reasonable for some local low-risk tasks, like simple benchmarking, but can be problematic if the prompts or tool calls can interact with malicious users. Fully isolated environments used Cloud-based sandboxes (e.g. Prime Sandboxes, [Modal Sandboxes](https://modal.com/docs/guide/sandboxes)) to run code generated by the RLM, ensuring completely isolation from the host process. Environments can be added, but we natively support the following: `local` (default), `modal`, `prime`.
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
rlm = RLM(
|
|
170
|
+
environment="...", # "local", "docker", "modal", "prime"
|
|
171
|
+
environment_kwargs={...},
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Local Environments
|
|
176
|
+
The default `local` environment `LocalREPL` runs in the same process as the RLM itself, with specified global and local namespaces for minimal security. Using this REPL is generally safe, but should not be used for production settings. It also shares the same virtual environment (e.g. Conda or uv) as the host process.
|
|
177
|
+
|
|
178
|
+
#### Docker <img src="https://github.com/docker.png" alt="Docker" height="20" style="vertical-align: middle;"/> (*requires [Docker installed](https://docs.docker.com/desktop/setup/install/)*)
|
|
179
|
+
We also support a Docker-based environment called `DockerREPL` that launches the REPL environment as a Docker image. By default, we use the `python:3.11-slim` image, but the user can specify custom images as well.
|
|
180
|
+
|
|
181
|
+
### Isolated Environments
|
|
182
|
+
We support several different REPL environments that run on separate, cloud-based machines. Whenever a recursive sub-call is made in these instances, it is requested from the host process.
|
|
183
|
+
|
|
184
|
+
#### Modal Sandboxes <img src="https://github.com/modal-labs.png" alt="Modal" height="20" style="vertical-align: middle;"/>
|
|
185
|
+
To use [Modal Sandboxes](https://modal.com/docs/guide/sandboxes) as the REPL environment, you need to install and authenticate your Modal account.
|
|
186
|
+
```bash
|
|
187
|
+
uv add modal # add modal library
|
|
188
|
+
modal setup # authenticate account
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Prime Intellect Sandboxes <img src="https://github.com/PrimeIntellect-ai.png" alt="Prime Intellect" height="20" style="vertical-align: middle;"/>
|
|
192
|
+
> [!NOTE]
|
|
193
|
+
> **Prime Intellect Sandboxes** are currently a beta feature. See the [documentation](https://docs.primeintellect.ai/sandboxes/overview) for more information. We noticed slow runtimes when using these sandboxes, which is currently an open issue.
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
To use [Prime Sandboxes](https://docs.primeintellect.ai/sandboxes/sdk), install the SDK and set your API key:
|
|
197
|
+
```bash
|
|
198
|
+
uv pip install -e ".[prime]"
|
|
199
|
+
export PRIME_API_KEY=...
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
### Model Providers
|
|
204
|
+
We currently support most major clients (OpenAI, Anthropic), as well as the router platforms (OpenRouter, Portkey, LiteLLM). For local models, we recommend using vLLM (which interfaces with the [OpenAI client](https://github.com/alexzhang13/rlm/blob/main/rlm/clients/openai.py)). To view or add support for more clients, start by looking at [`rlm/clients/`](https://github.com/alexzhang13/rlm/tree/main/rlm/clients).
|
|
205
|
+
|
|
206
|
+
## Relevant Reading
|
|
207
|
+
* **[Dec '25]** [Recursive Language Models arXiv](https://arxiv.org/abs/2512.24601)
|
|
208
|
+
* **[Oct '25]** [Recursive Language Models Blogpost](https://alexzhang13.github.io/blog/2025/rlm/)
|
|
209
|
+
|
|
210
|
+
If you use this code or repository in your research, please cite:
|
|
211
|
+
|
|
212
|
+
```bibtex
|
|
213
|
+
@misc{zhang2025recursivelanguagemodels,
|
|
214
|
+
title={Recursive Language Models},
|
|
215
|
+
author={Alex L. Zhang and Tim Kraska and Omar Khattab},
|
|
216
|
+
year={2025},
|
|
217
|
+
eprint={2512.24601},
|
|
218
|
+
archivePrefix={arXiv},
|
|
219
|
+
primaryClass={cs.AI},
|
|
220
|
+
url={https://arxiv.org/abs/2512.24601},
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Optional Debugging: Visualizing RLM Trajectories
|
|
225
|
+
We additionally provide a simple visualizer tool to examine and view the code, sub-LM, and root-LM calls of an RLM trajectory. To save log files (`.jsonl`) on every completion call that can be viewed in the visualizer, initialize the `RLMLogger` object and pass it into the `RLM` on initialization:
|
|
226
|
+
```python
|
|
227
|
+
from groknroll.logger import RLMLogger
|
|
228
|
+
from groknroll import RLM
|
|
229
|
+
|
|
230
|
+
logger = RLMLogger(log_dir="./logs")
|
|
231
|
+
rlm = RLM(
|
|
232
|
+
...
|
|
233
|
+
logger=logger
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
To run the visualizer locally, we use Node.js and shadcn/ui:
|
|
238
|
+
```
|
|
239
|
+
cd visualizer/
|
|
240
|
+
npm run dev # default localhost:3001
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
You'll have the option to select saved `.jsonl` files
|
|
244
|
+
<p align="center">
|
|
245
|
+
<img src="media/visualizer.png" alt="RLM Visualizer Example" width="800"/>
|
|
246
|
+
</p>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
groknroll/__init__.py,sha256=cSgEIY238em4bslHfCUyTB43xluwF2Dsa1iuajnTQAA,914
|
|
2
|
+
groknroll/__main__.py,sha256=8Lsdq3QYPeEj2nyHemr6uxAd2nszPhKv6fqyblNgvRc,131
|
|
3
|
+
groknroll/setup.py,sha256=p87YAtmRanqmh7cEGsYTJ5jERxDj62mY4OlI18fNo88,750
|
|
4
|
+
groknroll/agents/__init__.py,sha256=013zCuiLsq_27iEF170Ec_f8aHouvsJVwiArhNpOWmE,443
|
|
5
|
+
groknroll/agents/agent_manager.py,sha256=lQ6ZjAlMtU01NIMEPuQBM5dLcAT2N1Pj725uEef_rHk,4901
|
|
6
|
+
groknroll/agents/base_agent.py,sha256=eLSTnJna2KPeahnaGOZ4cwZy3A8CT-f3MVjUOjSqk2M,3325
|
|
7
|
+
groknroll/agents/build_agent.py,sha256=kM1lvbDPMddNhlAwYXbErtjgNQXODFCxifX0w_3-Kls,6979
|
|
8
|
+
groknroll/agents/plan_agent.py,sha256=IgM6Ckn7FlrY2J645KAZk4J061Tn0QRC2Ak9DKzJ9ZM,6232
|
|
9
|
+
groknroll/cli/__init__.py,sha256=0LqYqFNmcfwTNL2iQu0XbqJRh_YCuVq8LuIRIrk2BSA,110
|
|
10
|
+
groknroll/cli/enhanced_cli.py,sha256=9TnCBWsVwEjB4QmAZyG-jeKr1PWenNUr4ek7tapitHs,12707
|
|
11
|
+
groknroll/cli/large_codebase_cli.py,sha256=q5hV_JjmK9agVMSBpWf4dTeBtQoNKPKkJ9_VHtGP4kA,14438
|
|
12
|
+
groknroll/cli/main.py,sha256=qNEPclmmIGRdgNFRv81Vs2R_ABmo4_tsD0QZCqV21ug,11240
|
|
13
|
+
groknroll/cli/rlm_commands.py,sha256=YGLsxkxzOidmlRw7bNii73wnAh3AF7OOoleDzo941Z0,8424
|
|
14
|
+
groknroll/clients/__init__.py,sha256=JB-7qsm6tDqfyGoys0VvhH3BTFeNHS5YAdSqq5rDfo0,2183
|
|
15
|
+
groknroll/clients/anthropic.py,sha256=N3ghhvfSrbOexnSguUzbGs4tLAa3DnNOTikBrwmBw1Q,4268
|
|
16
|
+
groknroll/clients/azure_openai.py,sha256=CrLIyj60mKNjXFcUxDCx_VdEKyVmefn7Op6aI3gCX7k,5102
|
|
17
|
+
groknroll/clients/base_lm.py,sha256=dQKrpxN-jURd1xos18WoOsAN8n80MRYixTx7hV2LWWY,1017
|
|
18
|
+
groknroll/clients/gemini.py,sha256=_wrOS_CHEZQ58WfFliQB_YFdOsTD9Y0k6VxLY0AKw9o,5746
|
|
19
|
+
groknroll/clients/litellm.py,sha256=FoJWpGbdL00arVa8anre9JAaAdGGRosXB2elWfiHJV0,3946
|
|
20
|
+
groknroll/clients/openai.py,sha256=A6SzE5HntROzQpvnp2tjwXrN6nbbi5932KjLcjZLJMw,4994
|
|
21
|
+
groknroll/clients/portkey.py,sha256=QfpSOD5oyVSCBVOdITbnIMBgTo6CU2mTGiXJ3k6zqM0,3801
|
|
22
|
+
groknroll/core/__init__.py,sha256=LduS8Nfw4eInpr8-O6-018l2WSwe3MlXtNye676e0AI,280
|
|
23
|
+
groknroll/core/agent.py,sha256=ypv37xZMV9p7ZmlLWl8_dkT_dk7hbg0pm__vqzFHORs,10401
|
|
24
|
+
groknroll/core/comms_utils.py,sha256=GLoac6Dgdl9MSCHPNZmMGhsjKwoZQiuTd-ZdNs3-H9s,8591
|
|
25
|
+
groknroll/core/context.py,sha256=TJL2XW_jcdEBMrGwfXrzjBc310llBLi5NMCanwS7xMs,7515
|
|
26
|
+
groknroll/core/exceptions.py,sha256=vXeERLyv8z7uh_cO4kKTW_xpXNDLaNs4PK7ChEUQw0Y,5057
|
|
27
|
+
groknroll/core/large_codebase.py,sha256=iP51okb-dsRXQYPywW2ty2R5WySYG6twjtylmUvLXLo,18461
|
|
28
|
+
groknroll/core/lm_handler.py,sha256=siVZFUihb4RjkcyfEaY3y9Riy1swHTCdtlDYlau95nI,7514
|
|
29
|
+
groknroll/core/rlm.py,sha256=prFc72K911_sWIrqdYUoxuDYYmmxEbb-ZP2a9Iw24SM,19157
|
|
30
|
+
groknroll/core/rlm_codebase.py,sha256=UVC1OxHldpn3b52DmdNcoHgP6K6FNg3N3jDAyU8dl58,12907
|
|
31
|
+
groknroll/core/rlm_integration.py,sha256=mCX4qCpTGK2InRGno_jATm_gsEYOyabE8dSQOsG5wHA,7627
|
|
32
|
+
groknroll/core/types.py,sha256=-hL4DxuNy9E3gV9yuFbE5goEHkQZe5TUSOTyU0iYwVQ,8694
|
|
33
|
+
groknroll/environments/__init__.py,sha256=_xCSw3jKfpvAGRhKI7UYY3ImiTFbtZ314ZaesRqXWK0,1239
|
|
34
|
+
groknroll/environments/base_env.py,sha256=c4wg34DthvJmAIYNe7mdDsWOV0J7wvtFvVAekTQQXl0,5792
|
|
35
|
+
groknroll/environments/constants.py,sha256=Z2niqZjQ7gXu7VMkJy5kswwoX3_1R59YvCrq8KNy9mg,638
|
|
36
|
+
groknroll/environments/docker_repl.py,sha256=5wnJiS_YvmLd3PTLZW8UjreyHH9oWoS8D4bP0M2upUA,11231
|
|
37
|
+
groknroll/environments/local_repl.py,sha256=mngKStwAYDZXdbORjwBtrbRRlEYFVQsoyAONS8Z2UJ4,12424
|
|
38
|
+
groknroll/environments/modal_repl.py,sha256=IkJ3Cn-Ak4RDuSDeAYL5f-164Gznkm_ojSy9a5QQUvU,15482
|
|
39
|
+
groknroll/environments/prime_repl.py,sha256=yqNZmTpaATONk58rW-zT-rjw81agoVCjq0jptQ7Cwm4,19178
|
|
40
|
+
groknroll/logger/__init__.py,sha256=6rVRsCyzD574bOfhbtMpivQCkWjxJkpAfExqiIGCzkY,145
|
|
41
|
+
groknroll/logger/rlm_logger.py,sha256=IO2CyO5x_vVT35eshpwMX1f52YwpIiDehkvhnk35SS4,1717
|
|
42
|
+
groknroll/logger/verbose.py,sha256=559_HHiEcxLmnowbjZ_ctUmOahowhMsniuxgQrxpZD4,13008
|
|
43
|
+
groknroll/operations/__init__.py,sha256=vq0mWnnIej_0I7RIsf7DwUr4bHQn6STM5UxkiNifhFU,345
|
|
44
|
+
groknroll/operations/bash_ops.py,sha256=HYKplHwI2oQ5IpK9_oniAiwqx7AlNp6aWFtBAeb-cUE,13044
|
|
45
|
+
groknroll/operations/file_ops.py,sha256=bkhV8GLhTSa7p7D79jmoFUQK3Z9rLsVlqQWQFM9bQx8,14616
|
|
46
|
+
groknroll/operations/git_ops.py,sha256=uoHnVq6C62hGbBJLdz90O5m-_Jx7nOw4D6Es46Jj3rY,16772
|
|
47
|
+
groknroll/oracle/__init__.py,sha256=hCfOpvGd1WDQvxGb0YUOwIOTB4i8MuIos6omcJ6HS-A,339
|
|
48
|
+
groknroll/oracle/codebase_indexer.py,sha256=hQ0uS7VbgNiVdsq8fv6aKZScyzUyJHBAs6wLaFPW2TY,8368
|
|
49
|
+
groknroll/oracle/oracle_agent.py,sha256=i1Sl92ffb4adfJLjuctxYXrWvJqk58Tv0h1eVmNg4Sk,9792
|
|
50
|
+
groknroll/storage/__init__.py,sha256=DeYCbqD7GY-T_xTQlrS7kbwrkRVTaXEZ9AiZEyCFheo,319
|
|
51
|
+
groknroll/storage/database.py,sha256=hr16B7ye5y9RrzucobxOh-nM_L-pXeYOFr5gqEC45w0,8728
|
|
52
|
+
groknroll/storage/models.py,sha256=E_YxCK0ykJnjP75cxupCDr6Sv6ZqenZM3S2HvU9ZXwU,4539
|
|
53
|
+
groknroll/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
+
groknroll/utils/parsing.py,sha256=BrSEZMyp54GUqrQccL9bvsjYXtsF_pigySDrmiYCfdY,5355
|
|
55
|
+
groknroll/utils/prompts.py,sha256=dDBNPEoqrz53PcW0r8-vfdaFP3QReWKnIygfEipoDDw,9903
|
|
56
|
+
groknroll/utils/rlm_utils.py,sha256=2KaVsbA9HMKr7WWnHZ92tR5wpKa-CV0pasQp5zct2B0,557
|
|
57
|
+
groknroll-2.0.0.dist-info/licenses/LICENSE,sha256=mJQhMqyQ8qn_SQ_--hKclqYgbLccd_bL2TtjP3Ke7m8,1067
|
|
58
|
+
groknroll-2.0.0.dist-info/METADATA,sha256=hPu89eeLv5IPeeIDiEQfpXqs_XnelMgcSd6E2et5FKA,11401
|
|
59
|
+
groknroll-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
60
|
+
groknroll-2.0.0.dist-info/entry_points.txt,sha256=bjCOBIduyQ-9iHJT0HtUJNsTYI0MCGkOmIua4vL_S14,85
|
|
61
|
+
groknroll-2.0.0.dist-info/top_level.txt,sha256=YvCXzBP7-dvE1t1kCBpDTzPfNPql_amSS4S502Z3rWE,10
|
|
62
|
+
groknroll-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Alex Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
groknroll
|