(no commit message)
This commit is contained in:
@@ -4,7 +4,7 @@ Minimal Claude Code alternative. Single Python file, zero dependencies, ~250 lin
|
||||
|
||||
Built using Claude Code, then used to build itself.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"max_iters": 15,
|
||||
"lm": "openai/gpt-5.2-codex",
|
||||
"api_base": "https://openrouter.ai/api/v1",
|
||||
"max_tokens": 8192
|
||||
"max_tokens": 16000
|
||||
}
|
||||
61
nanocode.py
61
nanocode.py
@@ -25,6 +25,7 @@ MAGENTA = "\033[35m"
|
||||
|
||||
# --- Display utilities ---
|
||||
|
||||
|
||||
def separator():
|
||||
"""Return a horizontal separator line that fits the terminal width."""
|
||||
return f"{DIM}{'─' * min(os.get_terminal_size().columns, 80)}{RESET}"
|
||||
@@ -37,6 +38,7 @@ def render_markdown(text):
|
||||
|
||||
# --- File operations ---
|
||||
|
||||
|
||||
def read_file(path: str, offset: int = 0, limit: int = None) -> str:
|
||||
"""Read file contents with line numbers.
|
||||
|
||||
@@ -138,6 +140,7 @@ def grep_files(pattern: str, path: str = ".") -> str:
|
||||
|
||||
# --- Shell operations ---
|
||||
|
||||
|
||||
def run_bash(cmd: str) -> str:
|
||||
"""Run a shell command and return output.
|
||||
|
||||
@@ -148,9 +151,7 @@ def run_bash(cmd: str) -> str:
|
||||
Command output (stdout and stderr combined)
|
||||
"""
|
||||
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
|
||||
)
|
||||
output_lines = []
|
||||
try:
|
||||
@@ -196,14 +197,20 @@ def select_model():
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"\n{BOLD}{BLUE}❯{RESET} Enter choice (1-8 or c): ").strip().lower()
|
||||
choice = (
|
||||
input(f"\n{BOLD}{BLUE}❯{RESET} Enter choice (1-8 or c): ")
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
|
||||
if choice in AVAILABLE_MODELS:
|
||||
name, model_id = AVAILABLE_MODELS[choice]
|
||||
print(f"{GREEN}⏺ Selected: {name}{RESET}")
|
||||
return model_id
|
||||
elif choice == "c":
|
||||
custom_model = input(f"{BOLD}{BLUE}❯{RESET} Enter model ID (e.g., openai/gpt-4): ").strip()
|
||||
custom_model = input(
|
||||
f"{BOLD}{BLUE}❯{RESET} Enter model ID (e.g., openai/gpt-4): "
|
||||
).strip()
|
||||
if custom_model:
|
||||
print(f"{GREEN}⏺ Selected custom model: {custom_model}{RESET}")
|
||||
return custom_model
|
||||
@@ -218,12 +225,18 @@ def select_model():
|
||||
|
||||
# --- 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."""
|
||||
|
||||
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")
|
||||
affected_files: list[str] = dspy.OutputField(desc="List of files that were written or modified during the task")
|
||||
answer: str = dspy.OutputField(
|
||||
desc="Your response to the user after completing the task"
|
||||
)
|
||||
affected_files: list[str] = dspy.OutputField(
|
||||
desc="List of files that were written or modified during the task"
|
||||
)
|
||||
|
||||
|
||||
# ReAct agent with tools
|
||||
|
||||
@@ -235,7 +248,7 @@ class ToolLoggingCallback(BaseCallback):
|
||||
|
||||
def on_tool_start(self, call_id, instance, inputs):
|
||||
"""Log when a tool starts executing."""
|
||||
tool_name = instance.name if hasattr(instance, 'name') else str(instance)
|
||||
tool_name = instance.name if hasattr(instance, "name") else str(instance)
|
||||
# Format args nicely
|
||||
args_str = ", ".join(f"{k}={repr(v)[:50]}" for k, v in inputs.items())
|
||||
print(f" {MAGENTA}⏺ {tool_name}({args_str}){RESET}", flush=True)
|
||||
@@ -248,10 +261,12 @@ class ToolLoggingCallback(BaseCallback):
|
||||
def on_module_end(self, call_id, outputs, exception):
|
||||
"""Log when the finish tool is called (ReAct completion)."""
|
||||
# Check if this is a ReAct prediction with tool_calls
|
||||
if outputs and 'tool_calls' in outputs:
|
||||
for call in outputs['tool_calls']:
|
||||
args_str = ", ".join(f"{k}={repr(v)[:50]}" for k, v in call.args.items())
|
||||
if call.name == 'finish':
|
||||
if outputs and "tool_calls" in outputs:
|
||||
for call in outputs["tool_calls"]:
|
||||
args_str = ", ".join(
|
||||
f"{k}={repr(v)[:50]}" for k, v in call.args.items()
|
||||
)
|
||||
if call.name == "finish":
|
||||
print(f" {GREEN}⏺ finish{RESET}", flush=True)
|
||||
else:
|
||||
print(f" {MAGENTA}⏺ {call.name}({args_str}){RESET}", flush=True)
|
||||
@@ -261,7 +276,8 @@ class AgentConfig(PrecompiledConfig):
|
||||
max_iters: int = 15
|
||||
lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback
|
||||
api_base: str = "https://openrouter.ai/api/v1"
|
||||
max_tokens: int = 8192
|
||||
max_tokens: int = 16000
|
||||
|
||||
|
||||
class AgentProgram(PrecompiledProgram):
|
||||
config: AgentConfig
|
||||
@@ -273,8 +289,14 @@ class AgentProgram(PrecompiledProgram):
|
||||
# Configure logging callback globally
|
||||
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)
|
||||
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,
|
||||
)
|
||||
agent.set_lm(lm)
|
||||
self.agent = agent
|
||||
|
||||
@@ -282,6 +304,7 @@ class AgentProgram(PrecompiledProgram):
|
||||
assert task, "Task cannot be empty"
|
||||
return self.agent(task=task)
|
||||
|
||||
|
||||
# --- Main ---
|
||||
|
||||
|
||||
@@ -299,7 +322,9 @@ def main():
|
||||
config.lm = model
|
||||
|
||||
agent = AgentProgram(config)
|
||||
print(f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n")
|
||||
print(
|
||||
f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n"
|
||||
)
|
||||
|
||||
# Conversation history for context
|
||||
history = []
|
||||
@@ -345,6 +370,7 @@ def main():
|
||||
break
|
||||
except Exception as err:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
print(f"{RED}⏺ Error: {err}{RESET}")
|
||||
|
||||
@@ -352,5 +378,4 @@ def main():
|
||||
if __name__ == "__main__":
|
||||
agent = AgentProgram(AgentConfig(lm="openai/gpt-5.2-codex"))
|
||||
agent.push_to_hub("farouk1/nanocode")
|
||||
#main()
|
||||
|
||||
# main()
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"launch_kwargs": {},
|
||||
"train_kwargs": {},
|
||||
"temperature": null,
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": 16000,
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
}
|
||||
},
|
||||
@@ -79,7 +79,7 @@
|
||||
"launch_kwargs": {},
|
||||
"train_kwargs": {},
|
||||
"temperature": null,
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": 16000,
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user