Switch to RLM instead of ReAct

This commit is contained in:
2026-01-21 18:29:54 -08:00
parent 415e711fa7
commit 939538291c
5 changed files with 73 additions and 72 deletions

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python3
"""nanocode-dspy - minimal claude code alternative using DSPy ReAct"""
import os
import re
import glob as globlib
@@ -9,6 +6,9 @@ from modaic import PrecompiledProgram, PrecompiledConfig
import dspy
from dspy.utils.callback import BaseCallback
# --- Modaic ---
MODAIC_REPO_PATH = "farouk1/nanocode"
# --- ANSI colors ---
@@ -22,7 +22,6 @@ YELLOW = "\033[33m"
RED = "\033[31m"
MAGENTA = "\033[35m"
# --- Display utilities ---
@@ -172,14 +171,14 @@ def run_bash(cmd: str) -> str:
# --- Model selection ---
AVAILABLE_MODELS = {
"1": ("Claude 3.5 Sonnet", "anthropic/claude-3.5-sonnet"),
"2": ("Claude 3.5 Haiku", "anthropic/claude-3.5-haiku"),
"3": ("GPT-4o", "openai/gpt-4o"),
"4": ("GPT-4o mini", "openai/gpt-4o-mini"),
"5": ("Gemini Pro 1.5", "google/gemini-pro-1.5"),
"6": ("Llama 3.1 405B", "meta-llama/llama-3.1-405b-instruct"),
"7": ("DeepSeek V3", "deepseek/deepseek-chat"),
"8": ("Qwen 2.5 72B", "qwen/qwen-2.5-72b-instruct"),
"1": ("GPT-5.2 Codex", "openai/gpt-5.2-codex"),
"2": ("GPT-5.2", "openai/gpt-5.2"),
"3": ("Claude Opus 4.5", "anthropic/claude-opus-4.5"),
"4": ("Claude Opus 4", "anthropic/claude-opus-4"),
"5": ("Qwen 3 Coder", "qwen/qwen3-coder"),
"6": ("Gemini 3 Flash Preview", "google/gemini-3-flash-preview"),
"7": ("Kimi K2 0905", "moonshotai/kimi-k2-0905"),
"8": ("Minimax M2.1", "minimax/minimax-m2.1"),
}
@@ -223,9 +222,6 @@ def select_model():
exit(1)
# --- DSPy Signature ---
class CodingAssistant(dspy.Signature):
"""You are a concise coding assistant. Help the user with their coding task by using the available tools to read, write, edit files, search the codebase, and run commands."""
@@ -238,9 +234,14 @@ class CodingAssistant(dspy.Signature):
)
# ReAct agent with tools
tools = [read_file, write_file, edit_file, glob_files, grep_files, run_bash]
tools = {
"readfile": read_file,
"writefile": write_file,
"editfile": edit_file,
"globfiles": glob_files,
"grepfiles": grep_files,
"runbash": run_bash,
}
class ToolLoggingCallback(BaseCallback):
@@ -272,31 +273,44 @@ class ToolLoggingCallback(BaseCallback):
print(f" {MAGENTA}{call.name}({args_str}){RESET}", flush=True)
class AgentConfig(PrecompiledConfig):
max_iters: int = 15
class RLMCodingConfig(PrecompiledConfig):
max_iters: int = 20
lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback
sub_lm: str = "openrouter/openai/gpt-4.1" # Default fallback
api_base: str = "https://openrouter.ai/api/v1"
max_tokens: int = 16000
max_output_chars: int = 100000
verbose: bool = False
class AgentProgram(PrecompiledProgram):
config: AgentConfig
class RLMCodingProgram(PrecompiledProgram):
config: RLMCodingConfig
def __init__(self, config: AgentConfig, **kwargs):
def __init__(self, config: RLMCodingConfig, **kwargs):
self.config = config
super().__init__(config, **kwargs)
# Configure logging callback globally
# tool logging for introspections on multi-turn conversations
dspy.settings.configure(callbacks=[ToolLoggingCallback()])
agent = dspy.ReAct(
CodingAssistant, tools=tools, max_iters=self.config.max_iters
)
lm = dspy.LM(
self.config.lm,
api_base=self.config.api_base,
max_tokens=self.config.max_tokens,
)
sub_lm = dspy.LM(
self.config.sub_lm,
api_base=self.config.api_base,
max_tokens=self.config.max_tokens,
)
agent = dspy.RLM(
CodingAssistant,
sub_lm=sub_lm,
tools=tools,
max_output_chars=self.config.max_output_chars,
max_iterations=self.config.max_iters,
verbose=self.config.verbose,
)
agent.set_lm(lm)
self.agent = agent
@@ -304,12 +318,7 @@ class AgentProgram(PrecompiledProgram):
assert task, "Task cannot be empty"
return self.agent(task=task)
# --- Main ---
def main():
"""Create AgentConfig with selected model."""
model = os.getenv("MODEL")
if model is None:
model = select_model()
@@ -318,10 +327,10 @@ def main():
if not model.startswith("openrouter/"):
model = f"openrouter/{model}"
config = AgentConfig()
config = RLMCodingConfig()
config.lm = model
agent = AgentProgram(config)
agent = RLMCodingProgram(config)
print(
f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n"
)
@@ -376,6 +385,6 @@ def main():
if __name__ == "__main__":
agent = AgentProgram(AgentConfig(lm="openai/gpt-5.2-codex"))
agent.push_to_hub("farouk1/nanocode")
agent = RLMCodingProgram(RLMCodingConfig(lm="openai/gpt-5.2-codex"))
agent.push_to_hub(MODAIC_REPO_PATH, commit_message="Switch to RLM instead of ReAct", tag="v0.0.1")
#main()