Mayank2027 commited on
Commit
155186b
·
verified ·
1 Parent(s): a867a5a

Update backend/agent.py

Browse files
Files changed (1) hide show
  1. backend/agent.py +47 -79
backend/agent.py CHANGED
@@ -1,83 +1,44 @@
1
- import asyncio, subprocess, json, os, re
2
  from pathlib import Path
3
- from typing import List, Dict, Any
4
  from model_router import route_chat
5
- import uuid
6
-
7
- # ---------- Tool definitions ----------
8
- def read_file(filepath: str, project_path: Path) -> str:
9
- full_path = project_path / filepath
10
- if full_path.exists():
11
- return full_path.read_text()
12
- return f"Error: File not found: {filepath}"
13
-
14
- def write_file(filepath: str, content: str, project_path: Path) -> str:
15
- full_path = project_path / filepath
16
- full_path.parent.mkdir(parents=True, exist_ok=True)
17
- full_path.write_text(content)
18
- return f"File written: {filepath}"
19
-
20
- def run_command(command: str, cwd: str) -> str:
21
- try:
22
- result = subprocess.run(command, shell=True, cwd=cwd, capture_output=True, text=True, timeout=30)
23
- return result.stdout + result.stderr
24
- except subprocess.TimeoutExpired:
25
- return "Command timed out."
26
-
27
- def list_dir(path: str, project_path: Path) -> List[str]:
28
- full_path = project_path / path if path else project_path
29
- try:
30
- return os.listdir(full_path)
31
- except:
32
- return []
33
-
34
- # ---------- Agent class ----------
35
  class CodingAgent:
36
  def __init__(self, project_path: str, model: str = "groq-llama-3.3-70b-versatile"):
37
  self.project_path = Path(project_path)
38
  self.model = model
39
  self.status = "idle"
40
  self.logs = []
41
- self.pending_changes = [] # list of {action, filepath, content/search/replace}
42
- self.max_steps = 10
43
 
44
- async def run_task(self, task: str):
45
- """Main agent loop with tool calls."""
46
  self.status = "running"
47
  self.logs = []
48
  self.pending_changes = []
49
 
50
- system = f"""You are an AI coding agent. Your goal is to complete the user's task step by step.
51
- You are working in the project folder: {self.project_path}.
52
- You have access to these tools (use exactly these function names):
53
- - read_file(filepath) – returns file contents
54
- - write_file(filepath, content) – creates or overwrites a file
55
- - edit_file(filepath, search, replace) – replaces the first occurrence of 'search' with 'replace' in the file
56
- - run_command(command) – executes a shell command, returns output
57
- - list_dir(path) – lists files in a directory (path can be empty for root)
58
- - task_complete() – call this when the task is fully finished
59
-
60
- For each step, you must output a JSON object with two keys:
61
- - "thought": a short description of what you are doing
62
- - "tool": the name of the tool to call
63
- - "arguments": the arguments for the tool (as a dictionary)
64
 
65
- Example:
66
- {{"thought": "I need to see the current code", "tool": "read_file", "arguments": {{"filepath": "main.py"}}}}
67
-
68
- DO NOT output anything except the JSON object. If you need to talk, use the "thought" field."""
69
 
70
  messages = [
71
  {"role": "system", "content": system},
72
  {"role": "user", "content": task}
73
  ]
74
 
75
- for step in range(self.max_steps):
76
- # Get agent's next action
77
  response = await self._generate(messages)
78
  self.logs.append(f"Step {step+1}: {response[:150]}...")
79
 
80
- # Parse the JSON action
81
  action = self._parse_action(response)
82
  if not action:
83
  messages.append({"role": "user", "content": "Invalid JSON. Please output only the JSON action."})
@@ -87,12 +48,10 @@ DO NOT output anything except the JSON object. If you need to talk, use the "tho
87
  tool = action.get("tool", "")
88
  args = action.get("arguments", {})
89
 
90
- # Execute tool
91
  result = ""
92
  if tool == "read_file":
93
- result = read_file(args.get("filepath", ""), self.project_path)
94
  elif tool == "write_file":
95
- # Queue the change instead of writing immediately
96
  filepath = args.get("filepath", "")
97
  content = args.get("content", "")
98
  self.pending_changes.append({"action": "create", "filepath": filepath, "content": content})
@@ -101,8 +60,7 @@ DO NOT output anything except the JSON object. If you need to talk, use the "tho
101
  filepath = args.get("filepath", "")
102
  search = args.get("search", "")
103
  replace = args.get("replace", "")
104
- # Read current file, apply change, write back (temporary, will be approved later)
105
- current = read_file(filepath, self.project_path)
106
  if search in current:
107
  modified = current.replace(search, replace)
108
  self.pending_changes.append({"action": "edit", "filepath": filepath, "original": current, "modified": modified})
@@ -110,49 +68,59 @@ DO NOT output anything except the JSON object. If you need to talk, use the "tho
110
  else:
111
  result = f"Search string not found in {filepath}"
112
  elif tool == "run_command":
113
- result = run_command(args.get("command", ""), str(self.project_path))
114
  elif tool == "list_dir":
115
- result = "\n".join(list_dir(args.get("path", ""), self.project_path))
116
  elif tool == "task_complete":
117
  self.logs.append("Agent marked task complete.")
118
  break
119
  else:
120
  result = f"Unknown tool: {tool}"
121
 
122
- # Feed result back to model
123
  messages.append({"role": "assistant", "content": response})
124
  messages.append({"role": "user", "content": f"Tool result: {result}"})
125
 
126
  self.status = "awaiting_approval"
127
- # Return a summary and the pending changes
128
- return {
129
- "logs": self.logs,
130
- "pending_changes": self.pending_changes
131
- }
132
 
133
  async def approve_and_apply(self):
134
- """Apply all pending changes."""
135
  self.status = "applying"
136
  for change in self.pending_changes:
137
  try:
138
  if change["action"] == "create":
139
- write_file(change["filepath"], change["content"], self.project_path)
 
140
  self.logs.append(f"Created {change['filepath']}")
141
  elif change["action"] == "edit":
142
- write_file(change["filepath"], change["modified"], self.project_path)
143
  self.logs.append(f"Edited {change['filepath']}")
144
  except Exception as e:
145
  self.logs.append(f"Error: {e}")
146
  self.pending_changes = []
147
  self.status = "completed"
148
 
149
- def _parse_action(self, text: str) -> Dict | None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  text = text.strip()
151
- # Remove any markdown fences
152
- if text.startswith("```"):
153
- text = text[3:]
154
- if text.endswith("```"):
155
- text = text[:-3]
156
  try:
157
  return json.loads(text)
158
  except:
 
1
+ import asyncio, subprocess, re, json
2
  from pathlib import Path
3
+ from typing import List, Dict
4
  from model_router import route_chat
5
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  class CodingAgent:
7
  def __init__(self, project_path: str, model: str = "groq-llama-3.3-70b-versatile"):
8
  self.project_path = Path(project_path)
9
  self.model = model
10
  self.status = "idle"
11
  self.logs = []
12
+ self.pending_changes = []
 
13
 
14
+ async def run_task(self, task: str) -> dict:
15
+ """New agent loop – returns {logs, pending_changes}."""
16
  self.status = "running"
17
  self.logs = []
18
  self.pending_changes = []
19
 
20
+ system = f"""You are an AI coding agent. Complete the user's task step by step.
21
+ You work in the project folder: {self.project_path}.
22
+ Available tools (respond with a JSON object containing "thought", "tool", and "arguments"):
23
+ - read_file(filepath)
24
+ - write_file(filepath, content)
25
+ - edit_file(filepath, search, replace)
26
+ - run_command(command)
27
+ - list_dir(path)
28
+ - task_complete()
 
 
 
 
 
29
 
30
+ Example: {{"thought": "I need to see the code", "tool": "read_file", "arguments": {{"filepath": "main.py"}}}}
31
+ Only output the JSON object."""
 
 
32
 
33
  messages = [
34
  {"role": "system", "content": system},
35
  {"role": "user", "content": task}
36
  ]
37
 
38
+ for step in range(8):
 
39
  response = await self._generate(messages)
40
  self.logs.append(f"Step {step+1}: {response[:150]}...")
41
 
 
42
  action = self._parse_action(response)
43
  if not action:
44
  messages.append({"role": "user", "content": "Invalid JSON. Please output only the JSON action."})
 
48
  tool = action.get("tool", "")
49
  args = action.get("arguments", {})
50
 
 
51
  result = ""
52
  if tool == "read_file":
53
+ result = self._read_file(args.get("filepath", ""))
54
  elif tool == "write_file":
 
55
  filepath = args.get("filepath", "")
56
  content = args.get("content", "")
57
  self.pending_changes.append({"action": "create", "filepath": filepath, "content": content})
 
60
  filepath = args.get("filepath", "")
61
  search = args.get("search", "")
62
  replace = args.get("replace", "")
63
+ current = self._read_file(filepath)
 
64
  if search in current:
65
  modified = current.replace(search, replace)
66
  self.pending_changes.append({"action": "edit", "filepath": filepath, "original": current, "modified": modified})
 
68
  else:
69
  result = f"Search string not found in {filepath}"
70
  elif tool == "run_command":
71
+ result = self._run_command(args.get("command", ""))
72
  elif tool == "list_dir":
73
+ result = "\n".join(self._list_dir(args.get("path", "")))
74
  elif tool == "task_complete":
75
  self.logs.append("Agent marked task complete.")
76
  break
77
  else:
78
  result = f"Unknown tool: {tool}"
79
 
 
80
  messages.append({"role": "assistant", "content": response})
81
  messages.append({"role": "user", "content": f"Tool result: {result}"})
82
 
83
  self.status = "awaiting_approval"
84
+ return {"logs": self.logs, "pending_changes": self.pending_changes}
 
 
 
 
85
 
86
  async def approve_and_apply(self):
 
87
  self.status = "applying"
88
  for change in self.pending_changes:
89
  try:
90
  if change["action"] == "create":
91
+ (self.project_path / change["filepath"]).parent.mkdir(parents=True, exist_ok=True)
92
+ (self.project_path / change["filepath"]).write_text(change["content"])
93
  self.logs.append(f"Created {change['filepath']}")
94
  elif change["action"] == "edit":
95
+ (self.project_path / change["filepath"]).write_text(change["modified"])
96
  self.logs.append(f"Edited {change['filepath']}")
97
  except Exception as e:
98
  self.logs.append(f"Error: {e}")
99
  self.pending_changes = []
100
  self.status = "completed"
101
 
102
+ def _read_file(self, filepath: str) -> str:
103
+ p = self.project_path / filepath
104
+ return p.read_text() if p.exists() else "File not found."
105
+
106
+ def _run_command(self, cmd: str) -> str:
107
+ try:
108
+ r = subprocess.run(cmd, shell=True, cwd=self.project_path, capture_output=True, text=True, timeout=30)
109
+ return r.stdout + r.stderr
110
+ except Exception as e:
111
+ return str(e)
112
+
113
+ def _list_dir(self, path: str) -> list:
114
+ p = self.project_path / path if path else self.project_path
115
+ try:
116
+ return [f.name for f in p.iterdir()]
117
+ except:
118
+ return []
119
+
120
+ def _parse_action(self, text: str) -> dict | None:
121
  text = text.strip()
122
+ if text.startswith("```"): text = text[3:]
123
+ if text.endswith("```"): text = text[:-3]
 
 
 
124
  try:
125
  return json.loads(text)
126
  except: