Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80629105ed |
@@ -251,7 +251,7 @@ program.push_to_hub(
|
|||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- [DSPy](https://dspy.ai/) - Framework for programming language models
|
- [DSPy](https://dspy.ai/) - Framework for programming language models
|
||||||
- [Modaic](https://modaic.ai/) - Hub for sharing and versioning DSPy programs
|
- [Modaic](https://modaic.dev/) - Hub for sharing and versioning DSPy programs
|
||||||
- OpenRouter API key (for accessing language models)
|
- OpenRouter API key (for accessing language models)
|
||||||
|
|
||||||
Install dependencies:
|
Install dependencies:
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
"lm": "openrouter/anthropic/claude-3.5-sonnet",
|
"lm": "openrouter/anthropic/claude-3.5-sonnet",
|
||||||
"sub_lm": "openrouter/openai/gpt-4.1",
|
"sub_lm": "openrouter/openai/gpt-4.1",
|
||||||
"api_base": "https://openrouter.ai/api/v1",
|
"api_base": "https://openrouter.ai/api/v1",
|
||||||
"max_tokens": 16000,
|
"max_tokens": 32000,
|
||||||
"max_output_chars": 100000,
|
"max_output_chars": 100000,
|
||||||
"verbose": false
|
"verbose": false,
|
||||||
|
"track_usage": true
|
||||||
}
|
}
|
||||||
74
nanocode.py
74
nanocode.py
@@ -234,16 +234,6 @@ class CodingAssistant(dspy.Signature):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
tools = {
|
|
||||||
"readfile": read_file,
|
|
||||||
"writefile": write_file,
|
|
||||||
"editfile": edit_file,
|
|
||||||
"globfiles": glob_files,
|
|
||||||
"grepfiles": grep_files,
|
|
||||||
"runbash": run_bash,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ToolLoggingCallback(BaseCallback):
|
class ToolLoggingCallback(BaseCallback):
|
||||||
"""Callback that logs tool calls as they happen."""
|
"""Callback that logs tool calls as they happen."""
|
||||||
|
|
||||||
@@ -278,9 +268,10 @@ class RLMCodingConfig(PrecompiledConfig):
|
|||||||
lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback
|
lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback
|
||||||
sub_lm: str = "openrouter/openai/gpt-4.1" # Default fallback
|
sub_lm: str = "openrouter/openai/gpt-4.1" # Default fallback
|
||||||
api_base: str = "https://openrouter.ai/api/v1"
|
api_base: str = "https://openrouter.ai/api/v1"
|
||||||
max_tokens: int = 16000
|
max_tokens: int = 32000
|
||||||
max_output_chars: int = 100000
|
max_output_chars: int = 100000
|
||||||
verbose: bool = False
|
verbose: bool = False
|
||||||
|
track_usage: bool = True
|
||||||
|
|
||||||
|
|
||||||
class RLMCodingProgram(PrecompiledProgram):
|
class RLMCodingProgram(PrecompiledProgram):
|
||||||
@@ -289,6 +280,14 @@ class RLMCodingProgram(PrecompiledProgram):
|
|||||||
def __init__(self, config: RLMCodingConfig, **kwargs):
|
def __init__(self, config: RLMCodingConfig, **kwargs):
|
||||||
self.config = config
|
self.config = config
|
||||||
super().__init__(config, **kwargs)
|
super().__init__(config, **kwargs)
|
||||||
|
self.tools = {
|
||||||
|
"read_file": read_file,
|
||||||
|
"write_file": write_file,
|
||||||
|
"edit_file": edit_file,
|
||||||
|
"glob_files": glob_files,
|
||||||
|
"grep_files": grep_files,
|
||||||
|
"run_bash": run_bash,
|
||||||
|
}
|
||||||
|
|
||||||
# tool logging for introspections on multi-turn conversations
|
# tool logging for introspections on multi-turn conversations
|
||||||
dspy.settings.configure(callbacks=[ToolLoggingCallback()])
|
dspy.settings.configure(callbacks=[ToolLoggingCallback()])
|
||||||
@@ -296,16 +295,18 @@ class RLMCodingProgram(PrecompiledProgram):
|
|||||||
self.config.lm,
|
self.config.lm,
|
||||||
api_base=self.config.api_base,
|
api_base=self.config.api_base,
|
||||||
max_tokens=self.config.max_tokens,
|
max_tokens=self.config.max_tokens,
|
||||||
|
track_usage=self.config.track_usage,
|
||||||
)
|
)
|
||||||
sub_lm = dspy.LM(
|
sub_lm = dspy.LM(
|
||||||
self.config.sub_lm,
|
self.config.sub_lm,
|
||||||
api_base=self.config.api_base,
|
api_base=self.config.api_base,
|
||||||
max_tokens=self.config.max_tokens,
|
max_tokens=self.config.max_tokens,
|
||||||
|
track_usage=self.config.track_usage,
|
||||||
)
|
)
|
||||||
agent = dspy.RLM(
|
agent = dspy.RLM(
|
||||||
CodingAssistant,
|
CodingAssistant,
|
||||||
sub_lm=sub_lm,
|
sub_lm=sub_lm,
|
||||||
tools=tools,
|
tools=self.tools,
|
||||||
max_output_chars=self.config.max_output_chars,
|
max_output_chars=self.config.max_output_chars,
|
||||||
max_iterations=self.config.max_iters,
|
max_iterations=self.config.max_iters,
|
||||||
verbose=self.config.verbose,
|
verbose=self.config.verbose,
|
||||||
@@ -318,6 +319,15 @@ class RLMCodingProgram(PrecompiledProgram):
|
|||||||
assert task, "Task cannot be empty"
|
assert task, "Task cannot be empty"
|
||||||
return self.agent(task=task)
|
return self.agent(task=task)
|
||||||
|
|
||||||
|
def get_tools(self):
|
||||||
|
return self.tools
|
||||||
|
|
||||||
|
def set_tool(self, name: str, tool: callable):
|
||||||
|
self.tools[name] = tool
|
||||||
|
|
||||||
|
def remove_tool(self, name: str):
|
||||||
|
del self.tools[name]
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
model = os.getenv("MODEL")
|
model = os.getenv("MODEL")
|
||||||
if model is None:
|
if model is None:
|
||||||
@@ -332,7 +342,7 @@ def main():
|
|||||||
|
|
||||||
agent = RLMCodingProgram(config)
|
agent = RLMCodingProgram(config)
|
||||||
print(
|
print(
|
||||||
f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n"
|
f"{BOLD}NANOCODE DSPY{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Conversation history for context
|
# Conversation history for context
|
||||||
@@ -352,6 +362,37 @@ def main():
|
|||||||
history = []
|
history = []
|
||||||
print(f"{GREEN}⏺ Cleared conversation{RESET}")
|
print(f"{GREEN}⏺ Cleared conversation{RESET}")
|
||||||
continue
|
continue
|
||||||
|
if user_input == "/model":
|
||||||
|
print(f"\n{BOLD}Current model: {agent.config.lm}{RESET}")
|
||||||
|
print(f"\n{BOLD}Select a new model:{RESET}")
|
||||||
|
for key, (name, model_id) in AVAILABLE_MODELS.items():
|
||||||
|
print(f" {BLUE}{key}{RESET}. {name} ({DIM}{model_id}{RESET})")
|
||||||
|
print(f" {BLUE}c{RESET}. Custom model (enter manually)")
|
||||||
|
print(f" {BLUE}k{RESET}. Keep current model")
|
||||||
|
|
||||||
|
choice = input(f"\n{BOLD}{BLUE}❯{RESET} Enter choice: ").strip().lower()
|
||||||
|
|
||||||
|
if choice == "k":
|
||||||
|
print(f"{GREEN}⏺ Keeping current model: {agent.config.lm}{RESET}")
|
||||||
|
continue
|
||||||
|
elif choice in AVAILABLE_MODELS:
|
||||||
|
name, model_id = AVAILABLE_MODELS[choice]
|
||||||
|
new_model = model_id if model_id.startswith("openrouter/") else f"openrouter/{model_id}"
|
||||||
|
config.lm = new_model
|
||||||
|
agent = RLMCodingProgram(config)
|
||||||
|
print(f"{GREEN}⏺ Switched to: {name} ({new_model}){RESET}")
|
||||||
|
elif choice == "c":
|
||||||
|
custom_model = input(f"{BOLD}{BLUE}❯{RESET} Enter model ID: ").strip()
|
||||||
|
if custom_model:
|
||||||
|
new_model = custom_model if custom_model.startswith("openrouter/") else f"openrouter/{custom_model}"
|
||||||
|
config.lm = new_model
|
||||||
|
agent = RLMCodingProgram(config)
|
||||||
|
print(f"{GREEN}⏺ Switched to custom model: {new_model}{RESET}")
|
||||||
|
else:
|
||||||
|
print(f"{RED}⏺ Invalid model ID, keeping current model{RESET}")
|
||||||
|
else:
|
||||||
|
print(f"{RED}⏺ Invalid choice, keeping current model{RESET}")
|
||||||
|
continue
|
||||||
|
|
||||||
# Build context from history
|
# Build context from history
|
||||||
context = f"Working directory: {os.getcwd()}\n"
|
context = f"Working directory: {os.getcwd()}\n"
|
||||||
@@ -364,11 +405,14 @@ def main():
|
|||||||
|
|
||||||
print(f"\n{CYAN}⏺{RESET} Thinking...", flush=True)
|
print(f"\n{CYAN}⏺{RESET} Thinking...", flush=True)
|
||||||
|
|
||||||
# Run the ReAct agent
|
# Run the RLM agent
|
||||||
result = agent(task=task)
|
result = agent(task=task)
|
||||||
|
|
||||||
# Display the answer
|
# Display the answer
|
||||||
print(f"\n{CYAN}⏺{RESET} {render_markdown(result.answer)}")
|
print(f"\n{CYAN}⏺{RESET} {render_markdown(result.answer)}")
|
||||||
|
|
||||||
|
# Display usage
|
||||||
|
print(f"\n{MAGENTA}⏺ Debug Prediction: {result}{RESET}")
|
||||||
|
|
||||||
# Save to history
|
# Save to history
|
||||||
history.append({"user": user_input, "assistant": result.answer})
|
history.append({"user": user_input, "assistant": result.answer})
|
||||||
@@ -386,5 +430,5 @@ def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
agent = RLMCodingProgram(RLMCodingConfig())
|
agent = RLMCodingProgram(RLMCodingConfig())
|
||||||
agent.push_to_hub(MODAIC_REPO_PATH, commit_message="Switch to RLM instead of ReAct", tag="v0.0.2")
|
agent.push_to_hub(MODAIC_REPO_PATH, commit_message="Switch to RLM instead of ReAct", tag="v0.0.3")
|
||||||
#main()
|
#main()
|
||||||
|
|||||||
12
program.json
12
program.json
@@ -4,7 +4,7 @@
|
|||||||
"train": [],
|
"train": [],
|
||||||
"demos": [],
|
"demos": [],
|
||||||
"signature": {
|
"signature": {
|
||||||
"instructions": "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.\n\nYou are tasked with producing the following outputs given the inputs `task`:\n- {answer}\n- {affected_files} # note: the value you produce must adhere to the JSON schema: {\"type\": \"array\", \"items\": {\"type\": \"string\"}}\n\nYou have access to a Python REPL environment. Write Python code and it will be executed. You will see the output, then write more code based on what you learned. This is an iterative process.\n\nAvailable:\n- Variables: `task` (your input data)\n- `llm_query(prompt)` - query a sub-LLM (~500K char capacity) for semantic analysis\n- `llm_query_batched(prompts)` - query multiple prompts concurrently (much faster for multiple queries)\n- `print()` - ALWAYS print to see results\n- `SUBMIT(answer, affected_files)` - submit final output when done\n- Standard libraries: re, json, collections, math, etc.\n\nIMPORTANT: This is ITERATIVE. Each code block you write will execute, you'll see the output, then you decide what to do next. Do NOT try to solve everything in one step.\n\n1. EXPLORE FIRST - Look at your data before processing it. Print samples, check types/lengths, understand the structure.\n2. ITERATE - Write small code snippets, observe outputs, then decide next steps. State persists between iterations.\n3. VERIFY BEFORE SUBMITTING - If results seem wrong (zeros, empty, unexpected), reconsider your approach.\n4. USE llm_query FOR SEMANTICS - String matching finds WHERE things are; llm_query understands WHAT things mean.\n5. MINIMIZE RETYPING (INPUTS & OUTPUTS) - When values are long, precise, or error-prone (IDs, numbers, code, quotes), re-access them via variables and parse/compute in code instead of retyping. Use small, targeted prints to sanity-check, but avoid manual copying when variables can carry the exact value.\n6. SUBMIT ONLY AFTER SEEING OUTPUTS - SUBMIT ends the current run immediately. If you need to inspect printed output, run it in one step, review the result, then call SUBMIT in a later step.\n\nYou have max 50 sub-LLM calls. When done, call SUBMIT() with your output.\nAdditional tools available (use these instead of standard library equivalents):\n- `readfile(path: str, offset: int, limit: int) -> str` - Read file contents with line numbers.\n- `writefile(path: str, content: str) -> str` - Write content to a file.\n- `editfile(path: str, old: str, new: str, replace_all: bool) -> str` - Replace text in a file.\n- `globfiles(pattern: str, path: str) -> str` - Find files matching a glob pattern, sorted by modification time.\n- `grepfiles(pattern: str, path: str) -> str` - Search files for a regex pattern.\n- `runbash(cmd: str) -> str` - Run a shell command and return output.",
|
"instructions": "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.\n\nYou are tasked with producing the following outputs given the inputs `task`:\n- {answer}\n- {affected_files} # note: the value you produce must adhere to the JSON schema: {\"type\": \"array\", \"items\": {\"type\": \"string\"}}\n\nYou have access to a Python REPL environment. Write Python code and it will be executed. You will see the output, then write more code based on what you learned. This is an iterative process.\n\nAvailable:\n- Variables: `task` (your input data)\n- `llm_query(prompt)` - query a sub-LLM (~500K char capacity) for semantic analysis\n- `llm_query_batched(prompts)` - query multiple prompts concurrently (much faster for multiple queries)\n- `print()` - ALWAYS print to see results\n- `SUBMIT(answer, affected_files)` - submit final output when done\n- Standard libraries: re, json, collections, math, etc.\n\nIMPORTANT: This is ITERATIVE. Each code block you write will execute, you'll see the output, then you decide what to do next. Do NOT try to solve everything in one step.\n\n1. EXPLORE FIRST - Look at your data before processing it. Print samples, check types/lengths, understand the structure.\n2. ITERATE - Write small code snippets, observe outputs, then decide next steps. State persists between iterations.\n3. VERIFY BEFORE SUBMITTING - If results seem wrong (zeros, empty, unexpected), reconsider your approach.\n4. USE llm_query FOR SEMANTICS - String matching finds WHERE things are; llm_query understands WHAT things mean.\n5. MINIMIZE RETYPING (INPUTS & OUTPUTS) - When values are long, precise, or error-prone (IDs, numbers, code, quotes), re-access them via variables and parse/compute in code instead of retyping. Use small, targeted prints to sanity-check, but avoid manual copying when variables can carry the exact value.\n6. SUBMIT ONLY AFTER SEEING OUTPUTS - SUBMIT ends the current run immediately. If you need to inspect printed output, run it in one step, review the result, then call SUBMIT in a later step.\n\nYou have max 50 sub-LLM calls. When done, call SUBMIT() with your output.\nAdditional tools available (use these instead of standard library equivalents):\n- `read_file(path: str, offset: int, limit: int) -> str` - Read file contents with line numbers.\n- `write_file(path: str, content: str) -> str` - Write content to a file.\n- `edit_file(path: str, old: str, new: str, replace_all: bool) -> str` - Replace text in a file.\n- `glob_files(pattern: str, path: str) -> str` - Find files matching a glob pattern, sorted by modification time.\n- `grep_files(pattern: str, path: str) -> str` - Search files for a regex pattern.\n- `run_bash(cmd: str) -> str` - Run a shell command and return output.",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"prefix": "Variables Info:",
|
"prefix": "Variables Info:",
|
||||||
@@ -37,8 +37,9 @@
|
|||||||
"launch_kwargs": {},
|
"launch_kwargs": {},
|
||||||
"train_kwargs": {},
|
"train_kwargs": {},
|
||||||
"temperature": null,
|
"temperature": null,
|
||||||
"max_tokens": 16000,
|
"max_tokens": 32000,
|
||||||
"api_base": "https://openrouter.ai/api/v1"
|
"api_base": "https://openrouter.ai/api/v1",
|
||||||
|
"track_usage": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"agent.extract": {
|
"agent.extract": {
|
||||||
@@ -75,8 +76,9 @@
|
|||||||
"launch_kwargs": {},
|
"launch_kwargs": {},
|
||||||
"train_kwargs": {},
|
"train_kwargs": {},
|
||||||
"temperature": null,
|
"temperature": null,
|
||||||
"max_tokens": 16000,
|
"max_tokens": 32000,
|
||||||
"api_base": "https://openrouter.ai/api/v1"
|
"api_base": "https://openrouter.ai/api/v1",
|
||||||
|
"track_usage": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
|||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = ["dspy>=3.1.2", "modaic>=0.10.3"]
|
dependencies = ["dspy>=3.1.2", "modaic>=0.10.4"]
|
||||||
|
|||||||
Reference in New Issue
Block a user