|
import subprocess
|
|
from pathlib import Path
|
|
import os
|
|
import json
|
|
from dotenv import load_dotenv
|
|
from shutil import copyfile
|
|
from openai import OpenAI
|
|
|
|
load_dotenv()
|
|
|
|
|
|
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
|
|
MODEL = "qwen/qwen3-coder:free"
|
|
|
|
if not OPENROUTER_API_KEY:
|
|
raise ValueError("OPENROUTER_API_KEY must be set in .env file")
|
|
|
|
|
|
client = OpenAI(
|
|
base_url="https://openrouter.ai/api/v1",
|
|
api_key=OPENROUTER_API_KEY,
|
|
default_headers={"HTTP-Referer": "http://localhost:5000"}
|
|
)
|
|
|
|
|
|
TOOLS = [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "view",
|
|
"description": "View the contents of a file or directory",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"path": {
|
|
"type": "string",
|
|
"description": "Path to the file or directory"
|
|
}
|
|
},
|
|
"required": ["path"]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "create",
|
|
"description": "Create a new file with the given content",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"path": {
|
|
"type": "string",
|
|
"description": "Path to the file to create"
|
|
},
|
|
"file_text": {
|
|
"type": "string",
|
|
"description": "Content to write to the file"
|
|
}
|
|
},
|
|
"required": ["path", "file_text"]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "str_replace",
|
|
"description": "Replace a string in a file with another string",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"path": {
|
|
"type": "string",
|
|
"description": "Path to the file"
|
|
},
|
|
"old_str": {
|
|
"type": "string",
|
|
"description": "String to replace"
|
|
},
|
|
"new_str": {
|
|
"type": "string",
|
|
"description": "String to replace with"
|
|
}
|
|
},
|
|
"required": ["path", "old_str", "new_str"]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "bash",
|
|
"description": "Execute a bash command",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"command": {
|
|
"type": "string",
|
|
"description": "Command to execute"
|
|
}
|
|
},
|
|
"required": ["command"]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
|
|
def execute_tool(tool_name: str, tool_input: dict) -> dict:
|
|
"""Execute a tool and return structured result with error handling."""
|
|
try:
|
|
|
|
if tool_name == "view":
|
|
path = Path(str(tool_input.get("path")))
|
|
if path.is_file():
|
|
content = path.read_text()
|
|
return {"content": content, "is_error": False}
|
|
elif path.is_dir():
|
|
content = "\n".join(sorted([f.name for f in path.iterdir()]))
|
|
return {"content": content, "is_error": False}
|
|
else:
|
|
return {"content": f"Error: {path} does not exist", "is_error": True}
|
|
elif tool_name == "create":
|
|
path = Path(str(tool_input.get("path")))
|
|
content = str(tool_input.get("file_text"))
|
|
if not content:
|
|
return {
|
|
"content": "Error: No content provided in file_text",
|
|
"is_error": True,
|
|
}
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(content)
|
|
return {"content": f"File {path} written successfully", "is_error": False}
|
|
elif tool_name == "str_replace":
|
|
path = Path(str(tool_input.get("path")))
|
|
old_str = str(tool_input.get("old_str"))
|
|
new_str = str(tool_input.get("new_str"))
|
|
|
|
if not path.exists():
|
|
return {
|
|
"content": f"Error: File {path} does not exist",
|
|
"is_error": True,
|
|
}
|
|
|
|
content = path.read_text()
|
|
if old_str not in content:
|
|
return {
|
|
"content": f"Error: String '{old_str}' not found in {path}",
|
|
"is_error": True,
|
|
}
|
|
|
|
new_content = content.replace(old_str, new_str, 1)
|
|
path.write_text(new_content)
|
|
return {
|
|
"content": f"Replaced '{old_str}' with '{new_str}' in {path}",
|
|
"is_error": False,
|
|
}
|
|
|
|
elif tool_name == "bash":
|
|
command = tool_input.get("command")
|
|
print(command)
|
|
if not command:
|
|
return {
|
|
"content": "Error: No input in command",
|
|
"is_error": True,
|
|
}
|
|
result = subprocess.run(
|
|
command,
|
|
shell=True,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
)
|
|
|
|
|
|
output = f"stdout: {result.stdout}\nstderr: {result.stderr}"
|
|
return {"content": output, "is_error": result.returncode != 0}
|
|
else:
|
|
return {
|
|
"content": f"Error: Unknown tool '{tool_name}'",
|
|
"is_error": True,
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
"content": f"Error executing {tool_name}: {str(e)}",
|
|
"is_error": True,
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
prompt_content = Path("prompt.md").read_text()
|
|
|
|
system_prompt = prompt_content[
|
|
prompt_content.find("<role>") + 6 : prompt_content.find("</role>")
|
|
].strip()
|
|
|
|
instructions_content = prompt_content[
|
|
prompt_content.find("<thinking_process>") :
|
|
].strip()
|
|
|
|
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": instructions_content}
|
|
]
|
|
|
|
while True:
|
|
user_input = input("💬 User: ")
|
|
messages.append({"role": "user", "content": user_input})
|
|
|
|
while True:
|
|
try:
|
|
|
|
response = client.chat.completions.create(
|
|
model=MODEL,
|
|
messages=messages,
|
|
temperature=0.2,
|
|
max_tokens=4096,
|
|
tools=TOOLS,
|
|
tool_choice="auto"
|
|
)
|
|
except Exception as e:
|
|
print(f"Error calling OpenRouter API: {str(e)}")
|
|
break
|
|
|
|
assistant_message = response.choices[0].message
|
|
assistant_content = assistant_message.content or ""
|
|
|
|
|
|
if assistant_message.tool_calls:
|
|
tool_results = []
|
|
|
|
|
|
if assistant_content:
|
|
print(assistant_content)
|
|
|
|
print(f"Executing {len(assistant_message.tool_calls)} tool(s)...")
|
|
|
|
|
|
for tool_call in assistant_message.tool_calls:
|
|
function_name = tool_call.function.name
|
|
function_args = json.loads(tool_call.function.arguments)
|
|
|
|
print(f"Executing tool: {function_name}")
|
|
|
|
|
|
result = execute_tool(function_name, function_args)
|
|
print(result["content"])
|
|
|
|
|
|
tool_results.append({
|
|
"tool_call_id": tool_call.id,
|
|
"role": "tool",
|
|
"name": function_name,
|
|
"content": result["content"]
|
|
})
|
|
|
|
|
|
messages.append({
|
|
"role": "assistant",
|
|
"content": assistant_content,
|
|
"tool_calls": assistant_message.tool_calls
|
|
})
|
|
|
|
|
|
messages.extend(tool_results)
|
|
|
|
continue
|
|
else:
|
|
|
|
print(assistant_content)
|
|
|
|
|
|
messages.append({
|
|
"role": "assistant",
|
|
"content": assistant_content
|
|
})
|
|
|
|
break
|
|
|
|
|