Fix config override bug by recreating LMs after load_state

This commit is contained in:
2026-01-24 02:48:58 -08:00
parent 2149e14573
commit e633f96338
2 changed files with 54 additions and 29 deletions

View File

@@ -1,6 +1,6 @@
# nanocode # nanocode
Minimal Claude Code alternative using DSPy RLM! Single Python file, ~390 lines. Minimal Claude Code alternative using DSPy RLM! Single Python file, ~305 lines.
Built using Claude Code, then used to build itself. Built using Claude Code, then used to build itself.
@@ -41,15 +41,14 @@ from modaic import AutoProgram
agent = AutoProgram.from_precompiled( agent = AutoProgram.from_precompiled(
"farouk1/nanocode", "farouk1/nanocode",
config={ config={
"lm": "openrouter/anthropic/claude-3.5-sonnet", "lm": "openrouter/openai/gpt-5.2-codex",
"max_iters": 20 "max_iters": 50
} }
) )
# Run a coding task # Run a coding task
result = agent(task="What Python files are in this directory?") result = agent(task="What Python files are in this directory?")
print(result.answer) print(result.answer)
print(result.affected_files)
``` ```
### Option 2: Run Locally (Interactive CLI) ### Option 2: Run Locally (Interactive CLI)
@@ -75,13 +74,14 @@ When using as a Modaic AutoProgram, you can configure these options:
| Parameter | Type | Default | Description | | Parameter | Type | Default | Description |
|-----------|------|---------|-------------| |-----------|------|---------|-------------|
| `lm` | str | `openrouter/anthropic/claude-3.5-sonnet` | Primary language model | | `lm` | str | `openrouter/openai/gpt-5.2-codex` | Primary language model |
| `sub_lm` | str | `openrouter/openai/gpt-4.1` | Sub-LM for reasoning steps | | `sub_lm` | str | `openrouter/openai/gpt-5-mini` | Sub-LM for reasoning steps |
| `max_iters` | int | `20` | Maximum agent iterations | | `max_iters` | int | `50` | Maximum agent iterations |
| `api_base` | str | `https://openrouter.ai/api/v1` | API base URL | | `api_base` | str | `https://openrouter.ai/api/v1` | API base URL |
| `max_tokens` | int | `16000` | Maximum tokens per request | | `max_tokens` | int | `50000` | Maximum tokens per request |
| `max_output_chars` | int | `100000` | Maximum output character limit | | `max_output_chars` | int | `100000` | Maximum output character limit |
| `verbose` | bool | `False` | Enable verbose logging | | `verbose` | bool | `False` | Enable verbose logging |
| `track_usage` | bool | `True` | Track token usage |
Example with custom configuration: Example with custom configuration:
@@ -91,11 +91,12 @@ from modaic import AutoProgram
agent = AutoProgram.from_precompiled( agent = AutoProgram.from_precompiled(
"farouk1/nanocode", "farouk1/nanocode",
config={ config={
"lm": "openrouter/openai/gpt-4", "lm": "openrouter/anthropic/claude-sonnet-4",
"sub_lm": "openrouter/openai/gpt-3.5-turbo", "sub_lm": "openrouter/openai/gpt-4.1-mini",
"max_iters": 30, "max_iters": 30,
"max_tokens": 8000, "max_tokens": 8000,
"verbose": True "verbose": True,
"track_usage": False
} }
) )
``` ```
@@ -115,14 +116,14 @@ agent = AutoProgram.from_precompiled(
The agent has access to the following tools: The agent has access to the following tools:
| Tool | Function | Description | | Tool | Description |
|------|----------|-------------| |------|-------------|
| `readfile` | `read_file(path, offset, limit)` | Read file contents with line numbers | | `read_file(path, offset, limit)` | Read file contents with line numbers |
| `writefile` | `write_file(path, content)` | Write content to a file | | `write_file(path, content)` | Write content to a file |
| `editfile` | `edit_file(path, old, new, replace_all)` | Replace text in a file (old must be unique unless `replace_all=True`) | | `edit_file(path, old, new, replace_all)` | Replace text in a file (old must be unique unless `replace_all=True`) |
| `globfiles` | `glob_files(pattern, path)` | Find files matching a glob pattern, sorted by modification time | | `glob_files(pattern, path)` | Find files matching a glob pattern, sorted by modification time |
| `grepfiles` | `grep_files(pattern, path)` | Search files for a regex pattern | | `grep_files(pattern, path)` | Search files for a regex pattern |
| `runbash` | `run_bash(cmd)` | Run a shell command and return output | | `run_bash(cmd)` | Run a shell command and return output |
--- ---
@@ -161,7 +162,7 @@ print(result.answer)
# Make edits # Make edits
result = agent(task="Add a comment at the top of README.md") result = agent(task="Add a comment at the top of README.md")
print(result.affected_files) # ['README.md'] print(result.answer)
``` ```
--- ---
@@ -183,7 +184,14 @@ nanocode.py
│ └── run_bash() - Execute commands │ └── run_bash() - Execute commands
├── DSPy Components ├── DSPy Components
│ ├── CodingAssistant (Signature) │ ├── CodingAssistant (Signature)
── RLMCodingProgram (PrecompiledProgram) ── RLMCodingProgram (PrecompiledProgram)
│ │ ├── forward() - Run agent on task
│ │ ├── get_tools() - Get available tools
│ │ ├── set_tool() - Add/replace a tool
│ │ ├── remove_tool() - Remove a tool
│ │ ├── reload_lms() - Recreate LMs from config
│ │ └── load_state() - Load state with LM fix
│ └── RLMReasoningCallback
└── Modaic Integration └── Modaic Integration
└── RLMCodingConfig (PrecompiledConfig) └── RLMCodingConfig (PrecompiledConfig)
``` ```
@@ -195,13 +203,14 @@ Configuration class extending `PrecompiledConfig` for experiment-specific parame
```python ```python
class RLMCodingConfig(PrecompiledConfig): class RLMCodingConfig(PrecompiledConfig):
max_iters: int = 20 max_iters: int = 50
lm: str = "openrouter/anthropic/claude-3.5-sonnet" lm: str = "openrouter/openai/gpt-5.2-codex"
sub_lm: str = "openrouter/openai/gpt-4.1" sub_lm: str = "openrouter/openai/gpt-5-mini"
api_base: str = "https://openrouter.ai/api/v1" api_base: str = "https://openrouter.ai/api/v1"
max_tokens: int = 16000 max_tokens: int = 50000
max_output_chars: int = 100000 max_output_chars: int = 100000
verbose: bool = False verbose: bool = False
track_usage: bool = True
``` ```
#### `RLMCodingProgram` #### `RLMCodingProgram`
@@ -212,8 +221,20 @@ class RLMCodingProgram(PrecompiledProgram):
config: RLMCodingConfig config: RLMCodingConfig
def forward(self, task: str) -> dspy.Prediction: def forward(self, task: str) -> dspy.Prediction:
# Returns prediction with .answer and .affected_files # Returns prediction with .answer
return self.agent(task=task) return self.agent(task=task)
def get_tools(self) -> dict:
# Returns dict of available tools
def set_tool(self, name: str, tool: callable):
# Add or replace a tool
def remove_tool(self, name: str):
# Remove a tool by name
def reload_lms(self):
# Recreate LM objects from current config
``` ```
#### `CodingAssistant` #### `CodingAssistant`
@@ -221,9 +242,10 @@ DSPy Signature defining the agent's input/output schema.
```python ```python
class CodingAssistant(dspy.Signature): class CodingAssistant(dspy.Signature):
task: str = dspy.InputField() """You are a concise coding assistant with access to sub agents."""
answer: str = dspy.OutputField()
affected_files: list[str] = dspy.OutputField() task: str = dspy.InputField(desc="The user's coding task or question")
answer: str = dspy.OutputField(desc="Your response to the user after completing the task")
``` ```
--- ---

View File

@@ -121,6 +121,7 @@ def grep_files(pattern: str, path: str = ".") -> str:
Matching lines in format 'filepath:line_num:content' Matching lines in format 'filepath:line_num:content'
""" """
print(f"{MAGENTA}⏺ Grep: {pattern}{RESET}") print(f"{MAGENTA}⏺ Grep: {pattern}{RESET}")
regex = re.compile(pattern) regex = re.compile(pattern)
hits = [] hits = []
for filepath in globlib.glob(path + "/**", recursive=True): for filepath in globlib.glob(path + "/**", recursive=True):
@@ -146,6 +147,7 @@ def run_bash(cmd: str) -> str:
Command output (stdout and stderr combined) Command output (stdout and stderr combined)
""" """
print(f"{MAGENTA}⏺ Bash: {cmd}{RESET}") print(f"{MAGENTA}⏺ Bash: {cmd}{RESET}")
proc = subprocess.Popen( proc = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
) )
@@ -302,4 +304,5 @@ if __name__ == "__main__":
MODAIC_REPO_PATH, MODAIC_REPO_PATH,
commit_message="Fix config override bug by recreating LMs after load_state", commit_message="Fix config override bug by recreating LMs after load_state",
branch="dev" branch="dev"
) )