(no commit message)

This commit is contained in:
2026-01-19 01:07:19 -08:00
parent dd15ce281b
commit eed65c4221
8 changed files with 221 additions and 282 deletions

View File

@@ -1,7 +1,7 @@
{ {
"model": null, "model": null,
"max_iters": 15, "max_iters": 15,
"lm": "openrouter/anthropic/claude-3.5-sonnet", "lm": "openai/gpt-5.2-codex",
"api_base": "https://openrouter.ai/api/v1", "api_base": "https://openrouter.ai/api/v1",
"max_tokens": 8192 "max_tokens": 8192
} }

View File

@@ -2,16 +2,218 @@
"""nanocode-dspy - minimal claude code alternative using DSPy ReAct""" """nanocode-dspy - minimal claude code alternative using DSPy ReAct"""
import os import os
import re
import glob as globlib
import subprocess
from modaic import PrecompiledProgram, PrecompiledConfig from modaic import PrecompiledProgram, PrecompiledConfig
import dspy import dspy
from dspy.utils.callback import BaseCallback from dspy.utils.callback import BaseCallback
from utils import (
RESET, BOLD, DIM, BLUE, CYAN, GREEN, RED, MAGENTA, # --- ANSI colors ---
separator, render_markdown,
read_file, write_file, edit_file, glob_files, grep_files, run_bash, RESET = "\033[0m"
AVAILABLE_MODELS, select_model, BOLD = "\033[1m"
) DIM = "\033[2m"
BLUE = "\033[34m"
CYAN = "\033[36m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
RED = "\033[31m"
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}"
def render_markdown(text):
"""Convert basic markdown bold syntax to ANSI bold."""
return re.sub(r"\*\*(.+?)\*\*", f"{BOLD}\\1{RESET}", text)
# --- File operations ---
def read_file(path: str, offset: int = 0, limit: int = None) -> str:
"""Read file contents with line numbers.
Args:
path: Path to the file to read
offset: Line number to start from (0-indexed)
limit: Maximum number of lines to read
Returns:
File contents with line numbers
"""
lines = open(path).readlines()
if limit is None:
limit = len(lines)
selected = lines[offset : offset + limit]
return "".join(f"{offset + idx + 1:4}| {line}" for idx, line in enumerate(selected))
def write_file(path: str, content: str) -> str:
"""Write content to a file.
Args:
path: Path to the file to write
content: Content to write to the file
Returns:
'ok' on success
"""
with open(path, "w") as f:
f.write(content)
return "ok"
def edit_file(path: str, old: str, new: str, replace_all: bool = False) -> str:
"""Replace text in a file.
Args:
path: Path to the file to edit
old: Text to find and replace
new: Replacement text
replace_all: If True, replace all occurrences; otherwise old must be unique
Returns:
'ok' on success, error message on failure
"""
text = open(path).read()
if old not in text:
return "error: old_string not found"
count = text.count(old)
if not replace_all and count > 1:
return f"error: old_string appears {count} times, must be unique (use replace_all=True)"
replacement = text.replace(old, new) if replace_all else text.replace(old, new, 1)
with open(path, "w") as f:
f.write(replacement)
return "ok"
def glob_files(pattern: str, path: str = ".") -> str:
"""Find files matching a glob pattern, sorted by modification time.
Args:
pattern: Glob pattern to match (e.g., '**/*.py')
path: Base directory to search in
Returns:
Newline-separated list of matching files
"""
full_pattern = (path + "/" + pattern).replace("//", "/")
files = globlib.glob(full_pattern, recursive=True)
files = sorted(
files,
key=lambda f: os.path.getmtime(f) if os.path.isfile(f) else 0,
reverse=True,
)
return "\n".join(files) or "no files found"
def grep_files(pattern: str, path: str = ".") -> str:
"""Search files for a regex pattern.
Args:
pattern: Regular expression pattern to search for
path: Base directory to search in
Returns:
Matching lines in format 'filepath:line_num:content'
"""
regex = re.compile(pattern)
hits = []
for filepath in globlib.glob(path + "/**", recursive=True):
try:
for line_num, line in enumerate(open(filepath), 1):
if regex.search(line):
hits.append(f"{filepath}:{line_num}:{line.rstrip()}")
except Exception:
pass
return "\n".join(hits[:50]) or "no matches found"
# --- Shell operations ---
def run_bash(cmd: str) -> str:
"""Run a shell command and return output.
Args:
cmd: Shell command to execute
Returns:
Command output (stdout and stderr combined)
"""
proc = subprocess.Popen(
cmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True
)
output_lines = []
try:
while True:
line = proc.stdout.readline()
if not line and proc.poll() is not None:
break
if line:
print(f" {DIM}{line.rstrip()}{RESET}", flush=True)
output_lines.append(line)
proc.wait(timeout=30)
except subprocess.TimeoutExpired:
proc.kill()
output_lines.append("\n(timed out after 30s)")
return "".join(output_lines).strip() or "(empty output)"
# --- 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"),
}
def select_model():
"""Interactive model selection or use environment variable."""
model_env = os.getenv("MODEL")
if model_env:
print(f"{GREEN}⏺ Using model from environment: {model_env}{RESET}")
return model_env
print(f"\n{BOLD}Select a 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)")
while True:
try:
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()
if custom_model:
print(f"{GREEN}⏺ Selected custom model: {custom_model}{RESET}")
return custom_model
else:
print(f"{RED}⏺ Invalid model ID{RESET}")
else:
print(f"{RED}⏺ Invalid choice. Please enter 1-8 or c{RESET}")
except (KeyboardInterrupt, EOFError):
print(f"\n{RED}⏺ Model selection cancelled{RESET}")
exit(1)
# --- DSPy Signature --- # --- DSPy Signature ---
@@ -148,6 +350,7 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
agent = AgentProgram(AgentConfig()) agent = AgentProgram(AgentConfig(lm="openai/gpt-5.2-codex"))
agent.push_to_hub("farouk1/nanocode", with_code=True) agent.push_to_hub("farouk1/nanocode")
#main() #main()

View File

@@ -29,7 +29,7 @@
] ]
}, },
"lm": { "lm": {
"model": "openrouter/anthropic/claude-3.5-sonnet", "model": "openai/gpt-5.2-codex",
"model_type": "chat", "model_type": "chat",
"cache": true, "cache": true,
"num_retries": 3, "num_retries": 3,
@@ -71,7 +71,7 @@
] ]
}, },
"lm": { "lm": {
"model": "openrouter/anthropic/claude-3.5-sonnet", "model": "openai/gpt-5.2-codex",
"model_type": "chat", "model_type": "chat",
"cache": true, "cache": true,
"num_retries": 3, "num_retries": 3,

View File

@@ -1,50 +0,0 @@
"""Utility modules for nanocode."""
from .display import (
RESET,
BOLD,
DIM,
BLUE,
CYAN,
GREEN,
YELLOW,
RED,
MAGENTA,
separator,
render_markdown,
)
from .file_ops import (
read_file,
write_file,
edit_file,
glob_files,
grep_files,
)
from .shell_ops import run_bash
from .model_selection import AVAILABLE_MODELS, select_model
__all__ = [
# Display
"RESET",
"BOLD",
"DIM",
"BLUE",
"CYAN",
"GREEN",
"YELLOW",
"RED",
"MAGENTA",
"separator",
"render_markdown",
# File operations
"read_file",
"write_file",
"edit_file",
"glob_files",
"grep_files",
# Shell operations
"run_bash",
# Model selection
"AVAILABLE_MODELS",
"select_model",
]

View File

@@ -1,25 +0,0 @@
"""Display and UI utilities for nanocode."""
import os
import re
# ANSI colors
RESET = "\033[0m"
BOLD = "\033[1m"
DIM = "\033[2m"
BLUE = "\033[34m"
CYAN = "\033[36m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
RED = "\033[31m"
MAGENTA = "\033[35m"
def separator():
"""Return a horizontal separator line that fits the terminal width."""
return f"{DIM}{'' * min(os.get_terminal_size().columns, 80)}{RESET}"
def render_markdown(text):
"""Convert basic markdown bold syntax to ANSI bold."""
return re.sub(r"\*\*(.+?)\*\*", f"{BOLD}\\1{RESET}", text)

View File

@@ -1,104 +0,0 @@
"""File operation utilities for nanocode."""
import glob as globlib
import os
import re
def read_file(path: str, offset: int = 0, limit: int = None) -> str:
"""Read file contents with line numbers.
Args:
path: Path to the file to read
offset: Line number to start from (0-indexed)
limit: Maximum number of lines to read
Returns:
File contents with line numbers
"""
lines = open(path).readlines()
if limit is None:
limit = len(lines)
selected = lines[offset : offset + limit]
return "".join(f"{offset + idx + 1:4}| {line}" for idx, line in enumerate(selected))
def write_file(path: str, content: str) -> str:
"""Write content to a file.
Args:
path: Path to the file to write
content: Content to write to the file
Returns:
'ok' on success
"""
with open(path, "w") as f:
f.write(content)
return "ok"
def edit_file(path: str, old: str, new: str, replace_all: bool = False) -> str:
"""Replace text in a file.
Args:
path: Path to the file to edit
old: Text to find and replace
new: Replacement text
replace_all: If True, replace all occurrences; otherwise old must be unique
Returns:
'ok' on success, error message on failure
"""
text = open(path).read()
if old not in text:
return "error: old_string not found"
count = text.count(old)
if not replace_all and count > 1:
return f"error: old_string appears {count} times, must be unique (use replace_all=True)"
replacement = text.replace(old, new) if replace_all else text.replace(old, new, 1)
with open(path, "w") as f:
f.write(replacement)
return "ok"
def glob_files(pattern: str, path: str = ".") -> str:
"""Find files matching a glob pattern, sorted by modification time.
Args:
pattern: Glob pattern to match (e.g., '**/*.py')
path: Base directory to search in
Returns:
Newline-separated list of matching files
"""
full_pattern = (path + "/" + pattern).replace("//", "/")
files = globlib.glob(full_pattern, recursive=True)
files = sorted(
files,
key=lambda f: os.path.getmtime(f) if os.path.isfile(f) else 0,
reverse=True,
)
return "\n".join(files) or "no files found"
def grep_files(pattern: str, path: str = ".") -> str:
"""Search files for a regex pattern.
Args:
pattern: Regular expression pattern to search for
path: Base directory to search in
Returns:
Matching lines in format 'filepath:line_num:content'
"""
regex = re.compile(pattern)
hits = []
for filepath in globlib.glob(path + "/**", recursive=True):
try:
for line_num, line in enumerate(open(filepath), 1):
if regex.search(line):
hits.append(f"{filepath}:{line_num}:{line.rstrip()}")
except Exception:
pass
return "\n".join(hits[:50]) or "no matches found"

View File

@@ -1,51 +0,0 @@
"""Model selection utilities for nanocode."""
import os
from .display import RESET, BOLD, DIM, BLUE, GREEN, RED
# Available OpenRouter models
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"),
}
def select_model():
"""Interactive model selection or use environment variable."""
# Check environment variable first
model_env = os.getenv("MODEL")
if model_env:
print(f"{GREEN}⏺ Using model from environment: {model_env}{RESET}")
return model_env
print(f"\n{BOLD}Select a 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)")
while True:
try:
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()
if custom_model:
print(f"{GREEN}⏺ Selected custom model: {custom_model}{RESET}")
return custom_model
else:
print(f"{RED}⏺ Invalid model ID{RESET}")
else:
print(f"{RED}⏺ Invalid choice. Please enter 1-8 or c{RESET}")
except (KeyboardInterrupt, EOFError):
print(f"\n{RED}⏺ Model selection cancelled{RESET}")
exit(1)

View File

@@ -1,34 +0,0 @@
"""Shell operation utilities for nanocode."""
import subprocess
from .display import DIM, RESET
def run_bash(cmd: str) -> str:
"""Run a shell command and return output.
Args:
cmd: Shell command to execute
Returns:
Command output (stdout and stderr combined)
"""
proc = subprocess.Popen(
cmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True
)
output_lines = []
try:
while True:
line = proc.stdout.readline()
if not line and proc.poll() is not None:
break
if line:
print(f" {DIM}{line.rstrip()}{RESET}", flush=True)
output_lines.append(line)
proc.wait(timeout=30)
except subprocess.TimeoutExpired:
proc.kill()
output_lines.append("\n(timed out after 30s)")
return "".join(output_lines).strip() or "(empty output)"