halfacupoftea commited on
Commit
1278b3f
·
1 Parent(s): b4b185f

Refactor directory structure

Browse files
agent/agent_config/prompts.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ system_message = {
2
+ "role": "system",
3
+ "content": (
4
+ "You are a senior developer assistant bot for GitHub issues.\n\n"
5
+
6
+ "Your job is to respond to GitHub issues **professionally** and **helpfully**, but never repeat the issue description verbatim.\n\n"
7
+ "First, classify the issue as one of the following:\n"
8
+ "- Bug report\n"
9
+ "- Implementation question\n"
10
+ "- Feature request\n"
11
+ "- Incomplete or unclear\n\n"
12
+
13
+ "Then, based on the classification, write a clear, concise, and friendly response.\n\n"
14
+ "The comment should be well formatted and readable, using Markdown for code blocks and lists where appropriate.\n\n"
15
+ "DO NOT paste or repeat the issue description. DO NOT quote it. Respond entirely in your own words.\n"
16
+ "You can only use the following tools: fetch_github_issue, get_issue_details, retrieve_context, post_comment.\n"
17
+ "Do not attempt to use any other tools such as web_search."
18
+ "DO NOT HALLUCINATE OR MAKE UP TOOLS."
19
+ )
20
+ }
21
+
22
+ user_message = {
23
+ "role": "user",
24
+ "content": "Please suggest a fix on this issue https://github.com/aditi-dsi/testing-cryptope/issues/4."
25
+ }
agent/{mistral.py → agent_config/tool_schema.py} RENAMED
@@ -1,9 +1,3 @@
1
- import json
2
- from mistralai import Mistral
3
- from agent.function_calling import fetch_github_issue, get_issue_details, post_comment
4
- from agent.code_index import retrieve_context
5
- from config import MISTRAL_API_KEY
6
-
7
  tools = [
8
  {
9
  "type": "function",
@@ -105,94 +99,4 @@ tools = [
105
  },
106
  },
107
  },
108
- ]
109
-
110
- names_to_functions = {
111
- "fetch_github_issue": fetch_github_issue,
112
- "get_issue_details": get_issue_details,
113
- "retrieve_context": retrieve_context,
114
- "post_comment": post_comment,
115
- }
116
-
117
- allowed_tools = set(names_to_functions.keys())
118
-
119
- system_message = {
120
- "role": "system",
121
- "content": (
122
- "You are a senior developer assistant bot for GitHub issues.\n\n"
123
-
124
- "Your job is to respond to GitHub issues **professionally** and **helpfully**, but never repeat the issue description verbatim.\n\n"
125
- "First, classify the issue as one of the following:\n"
126
- "- Bug report\n"
127
- "- Implementation question\n"
128
- "- Feature request\n"
129
- "- Incomplete or unclear\n\n"
130
-
131
- "Then, based on the classification, write a clear, concise, and friendly response.\n\n"
132
- "The comment should be well formatted and readable, using Markdown for code blocks and lists where appropriate.\n\n"
133
- "DO NOT paste or repeat the issue description. DO NOT quote it. Respond entirely in your own words.\n"
134
- "You can only use the following tools: fetch_github_issue, get_issue_details, retrieve_context, post_comment.\n"
135
- "Do not attempt to use any other tools such as web_search."
136
- "DO NOT HALLUCINATE OR MAKE UP TOOLS."
137
- )
138
- }
139
-
140
- user_message = {
141
- "role": "user",
142
- "content": "Please suggest a fix on this issue https://github.com/aditi-dsi/testing-cryptope/issues/4."
143
- }
144
-
145
- messages = [system_message, user_message]
146
-
147
- api_key = MISTRAL_API_KEY
148
- model = "devstral-small-latest"
149
- client = Mistral(api_key=api_key)
150
-
151
- MAX_STEPS = 5
152
- tool_calls = 0
153
-
154
- while True:
155
- response = client.chat.complete(
156
- model=model,
157
- messages=messages,
158
- tools=tools,
159
- tool_choice="any",
160
- )
161
- msg = response.choices[0].message
162
- messages.append(msg)
163
-
164
- if hasattr(msg, "tool_calls") and msg.tool_calls:
165
- for tool_call in msg.tool_calls:
166
- function_name = tool_call.function.name
167
- function_params = json.loads(tool_call.function.arguments)
168
- if function_name in allowed_tools:
169
- function_result = names_to_functions[function_name](**function_params)
170
- print(f"Agent is calling tool: {function_name}")
171
- tool_calls += 1
172
- messages.append({
173
- "role": "tool",
174
- "tool_call_id": tool_call.id,
175
- "content": str(function_result)
176
- })
177
-
178
- if function_name == "post_comment":
179
- print("OpenSorus (final): ✅ Comment posted successfully. No further action needed.")
180
- exit(0)
181
-
182
- else:
183
- print(f"LLM tried to call unknown tool: {function_name}")
184
- tool_error_msg = (
185
- f"Error: Tool '{function_name}' is not available. "
186
- "You can only use the following tools: fetch_github_issue, get_issue_details, post_comment."
187
- )
188
- messages.append({
189
- "role": "tool",
190
- "tool_call_id": tool_call.id,
191
- "content": tool_error_msg
192
- })
193
- if tool_calls >= MAX_STEPS:
194
- print(f"Agent stopped after {MAX_STEPS} tool calls to protect against rate limiting.")
195
- break
196
- else:
197
- print("OpenSorus (final):", msg.content)
198
- break
 
 
 
 
 
 
 
1
  tools = [
2
  {
3
  "type": "function",
 
99
  },
100
  },
101
  },
102
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent/core.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from mistralai import Mistral
3
+ from agent.agent_config import prompts
4
+ from agent.agent_config import tool_schema
5
+ from config import MISTRAL_API_KEY
6
+ from tools.code_index import retrieve_context
7
+ from tools.github_tools import fetch_github_issue, get_issue_details, post_comment
8
+
9
+ tools = tool_schema.tools
10
+ names_to_functions = {
11
+ "fetch_github_issue": fetch_github_issue,
12
+ "get_issue_details": get_issue_details,
13
+ "retrieve_context": retrieve_context,
14
+ "post_comment": post_comment,
15
+ }
16
+
17
+ allowed_tools = set(names_to_functions.keys())
18
+
19
+ system_message = prompts.system_message
20
+ user_message = {
21
+ "role": "user",
22
+ "content": "Please suggest a fix on this issue https://github.com/aditi-dsi/testing-cryptope/issues/4."
23
+ }
24
+
25
+ messages = [system_message, user_message]
26
+
27
+ api_key = MISTRAL_API_KEY
28
+ model = "devstral-small-latest"
29
+ client = Mistral(api_key=api_key)
30
+
31
+ MAX_STEPS = 5
32
+ tool_calls = 0
33
+
34
+ while True:
35
+ response = client.chat.complete(
36
+ model=model,
37
+ messages=messages,
38
+ tools=tools,
39
+ tool_choice="any",
40
+ )
41
+ msg = response.choices[0].message
42
+ messages.append(msg)
43
+
44
+ if hasattr(msg, "tool_calls") and msg.tool_calls:
45
+ for tool_call in msg.tool_calls:
46
+ function_name = tool_call.function.name
47
+ function_params = json.loads(tool_call.function.arguments)
48
+ if function_name in allowed_tools:
49
+ function_result = names_to_functions[function_name](**function_params)
50
+ print(f"Agent is calling tool: {function_name}")
51
+ tool_calls += 1
52
+ messages.append({
53
+ "role": "tool",
54
+ "tool_call_id": tool_call.id,
55
+ "content": str(function_result)
56
+ })
57
+
58
+ if function_name == "post_comment":
59
+ print("OpenSorus (final): ✅ Comment posted successfully. No further action needed.")
60
+ exit(0)
61
+
62
+ else:
63
+ print(f"LLM tried to call unknown tool: {function_name}")
64
+ tool_error_msg = (
65
+ f"Error: Tool '{function_name}' is not available. "
66
+ "You can only use the following tools: fetch_github_issue, get_issue_details, post_comment."
67
+ )
68
+ messages.append({
69
+ "role": "tool",
70
+ "tool_call_id": tool_call.id,
71
+ "content": tool_error_msg
72
+ })
73
+ if tool_calls >= MAX_STEPS:
74
+ print(f"Agent stopped after {MAX_STEPS} tool calls to protect against rate limiting.")
75
+ break
76
+ else:
77
+ print("OpenSorus (final):", msg.content)
78
+ break
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ llama_index==0.12.40
2
+ mistralai==1.8.1
3
+ PyJWT==2.10.1
4
+ python-dotenv==1.1.0
5
+ requests==2.32.3
tools/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ __all__ = [
2
+ "code_index",
3
+ "github_tools",
4
+ ]
{agent → tools}/code_index.py RENAMED
@@ -1,4 +1,3 @@
1
- import base64
2
  import os
3
  import re
4
  import time
@@ -9,55 +8,13 @@ from llama_index.core.postprocessor import SimilarityPostprocessor
9
  from llama_index.embeddings.mistralai import MistralAIEmbedding
10
  from llama_index.llms.mistralai import MistralAI
11
  from mistralai import Mistral
12
- from agent.function_calling import github_request, get_installation_id, get_installation_token
13
  from config import MISTRAL_API_KEY
 
 
14
 
15
  repo_indices_cache: Dict[str, VectorStoreIndex] = {}
16
  INCLUDE_FILE_EXTENSIONS = {".py", ".js", ".ts", ".json", ".md", ".txt"}
17
 
18
- def fetch_repo_files(owner: str, repo: str, ref: str = "main") -> List[str]:
19
- """
20
- Lists all files in the repository by recursively fetching the Git tree from GitHub API.
21
- Returns a list of file paths.
22
- """
23
- installation_id = get_installation_id(owner, repo)
24
- token = get_installation_token(installation_id)
25
- url = f"https://api.github.com/repos/{owner}/{repo}/git/trees/{ref}?recursive=1"
26
- headers = {
27
- "Authorization": f"Bearer {token}",
28
- "Accept": "application/vnd.github.v3+json"
29
- }
30
- response = github_request("GET", url, headers=headers)
31
- if response.status_code != 200:
32
- raise Exception(f"Failed to list repository files: {response.status_code} {response.text}")
33
-
34
- tree = response.json().get("tree", [])
35
- file_paths = [item["path"] for item in tree if item["type"] == "blob"]
36
- return file_paths
37
-
38
- # print(fetch_repo_files("aditi-dsi", "EvalAI-Starters", "master"))
39
-
40
- def fetch_file_content(owner: str, repo: str, path: str, ref: str = "main") -> str:
41
- """
42
- Fetches the content of a file from the GitHub repository.
43
- """
44
- installation_id = get_installation_id(owner, repo)
45
- token = get_installation_token(installation_id)
46
- url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}"
47
- headers = {
48
- "Authorization": f"Bearer {token}",
49
- "Accept": "application/vnd.github.v3+json"
50
- }
51
- response = github_request("GET", url, headers=headers)
52
- if response.status_code != 200:
53
- raise Exception(f"Failed to fetch file content {path}: {response.status_code} {response.text}")
54
-
55
- content_json = response.json()
56
- content = base64.b64decode(content_json["content"]).decode("utf-8", errors="ignore")
57
- return content
58
-
59
- # print(fetch_file_content("aditi-dsi", "testing-cryptope", "frontend/src/lib/buildSwap.ts", "main"))
60
-
61
  def clean_line(line: str) -> str:
62
  line = re.sub(r'^\s*\d+[\.\)]\s*', '', line)
63
  line = line.strip(' `"\'')
 
 
1
  import os
2
  import re
3
  import time
 
8
  from llama_index.embeddings.mistralai import MistralAIEmbedding
9
  from llama_index.llms.mistralai import MistralAI
10
  from mistralai import Mistral
 
11
  from config import MISTRAL_API_KEY
12
+ from tools.utils import fetch_repo_files, fetch_file_content
13
+
14
 
15
  repo_indices_cache: Dict[str, VectorStoreIndex] = {}
16
  INCLUDE_FILE_EXTENSIONS = {".py", ".js", ".ts", ".json", ".md", ".txt"}
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  def clean_line(line: str) -> str:
19
  line = re.sub(r'^\s*\d+[\.\)]\s*', '', line)
20
  line = line.strip(' `"\'')
tools/github_tools.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from urllib.parse import urlparse
2
+ from tools.utils import get_installation_id, get_installation_token, github_request
3
+
4
+ def fetch_github_issue(issue_url):
5
+ parsed = urlparse(issue_url)
6
+ path_parts = parsed.path.strip('/').split('/')
7
+ if len(path_parts) >= 4 and path_parts[2] == 'issues':
8
+ owner = path_parts[0]
9
+ repo = path_parts[1]
10
+ issue_num = path_parts[3]
11
+ return owner, repo, issue_num
12
+ else:
13
+ raise ValueError("Invalid GitHub Issue URL")
14
+
15
+
16
+ def get_issue_details(owner, repo, issue_num):
17
+ installation_id = get_installation_id(owner, repo)
18
+ token = get_installation_token(installation_id)
19
+ url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}"
20
+ headers = {
21
+ "Authorization": f"Bearer {token}",
22
+ "Accept": "application/vnd.github.v3+json"
23
+ }
24
+ response = github_request("GET", url, headers=headers)
25
+ if response.status_code == 200:
26
+ return response.json()
27
+ else:
28
+ raise Exception(f"Failed to fetch issue: {response.status_code} {response.text}")
29
+
30
+ # print(get_issue_details("aditi-dsi", "testing-cryptope", "3"))
31
+
32
+ def post_comment(owner, repo, issue_num, comment_body):
33
+ installation_id = get_installation_id(owner, repo)
34
+ token = get_installation_token(installation_id)
35
+ url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}/comments"
36
+ headers = {
37
+ "Authorization": f"Bearer {token}",
38
+ "Accept": "application/vnd.github.v3+json"
39
+ }
40
+ data = {"body": comment_body}
41
+ response = github_request("POST", url, headers=headers, json=data)
42
+ if response.status_code == 201:
43
+ return response.json()
44
+ else:
45
+ raise Exception(f"Failed to post comment: {response.status_code} {response.text}")
46
+
47
+ # print(post_comment("aditi-dsi", "testing-cryptope", "3", "This is a test comment from OpenSorus."))
agent/function_calling.py → tools/utils.py RENAMED
@@ -1,14 +1,17 @@
1
- import requests
2
- from urllib.parse import urlparse
3
- from config import APP_ID, APP_PRIVATE_KEY
4
- import time
5
- import jwt
6
  from datetime import datetime, timezone, timedelta
 
7
  import threading
 
 
 
 
 
8
 
9
  installation_tokens = {}
10
  token_lock = threading.Lock()
11
 
 
12
  def generate_jwt():
13
  """Generate a JWT signed with GitHub App private key."""
14
  now = int(time.time())
@@ -90,121 +93,46 @@ def get_installation_token(installation_id):
90
 
91
  # print(get_installation_token(69452220))
92
 
93
- def fetch_github_issue(issue_url):
94
- parsed = urlparse(issue_url)
95
- path_parts = parsed.path.strip('/').split('/')
96
- if len(path_parts) >= 4 and path_parts[2] == 'issues':
97
- owner = path_parts[0]
98
- repo = path_parts[1]
99
- issue_num = path_parts[3]
100
- return owner, repo, issue_num
101
- else:
102
- raise ValueError("Invalid GitHub Issue URL")
103
-
104
-
105
- def get_issue_details(owner, repo, issue_num):
106
  installation_id = get_installation_id(owner, repo)
107
  token = get_installation_token(installation_id)
108
- url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}"
109
  headers = {
110
  "Authorization": f"Bearer {token}",
111
  "Accept": "application/vnd.github.v3+json"
112
  }
113
  response = github_request("GET", url, headers=headers)
114
- if response.status_code == 200:
115
- return response.json()
116
- else:
117
- raise Exception(f"Failed to fetch issue: {response.status_code} {response.text}")
 
 
118
 
119
- # print(get_issue_details("aditi-dsi", "testing-cryptope", "3"))
120
 
121
- def post_comment(owner, repo, issue_num, comment_body):
 
 
 
122
  installation_id = get_installation_id(owner, repo)
123
  token = get_installation_token(installation_id)
124
- url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}/comments"
125
  headers = {
126
  "Authorization": f"Bearer {token}",
127
  "Accept": "application/vnd.github.v3+json"
128
  }
129
- data = {"body": comment_body}
130
- response = github_request("POST", url, headers=headers, json=data)
131
- if response.status_code == 201:
132
- return response.json()
133
- else:
134
- raise Exception(f"Failed to post comment: {response.status_code} {response.text}")
135
-
136
- # print(post_comment("aditi-dsi", "testing-cryptope", "3", "This is a test comment from OpenSorus."))
137
-
138
- # tools = [
139
- # {
140
- # "type": "function",
141
- # "function": {
142
- # "name": "fetch_github_issue",
143
- # "description": "Fetch GitHub issue details",
144
- # "parameters": {
145
- # "type": "object",
146
- # "properties": {
147
- # "issue_url": {
148
- # "type": "string",
149
- # "description": "The full URL of the GitHub issue"
150
- # }
151
- # },
152
- # "required": ["issue_url"]
153
- # },
154
- # },
155
- # },
156
- # {
157
- # "type": "function",
158
- # "function": {
159
- # "name": "get_issue_details",
160
- # "description": "Get details of a GitHub issue",
161
- # "parameters": {
162
- # "type": "object",
163
- # "properties": {
164
- # "owner": {
165
- # "type": "string",
166
- # "description": "The owner of the repository."
167
- # },
168
- # "repo": {
169
- # "type": "string",
170
- # "description": "The name of the repository."
171
- # },
172
- # "issue_num": {
173
- # "type": "string",
174
- # "description": "The issue number."
175
- # }
176
- # },
177
- # "required": ["owner", "repo", "issue_num"],
178
- # },
179
- # },
180
- # },
181
- # {
182
- # "type": "function",
183
- # "function": {
184
- # "name": "post_comment",
185
- # "description": "Post a comment on a GitHub issue",
186
- # "parameters": {
187
- # "type": "object",
188
- # "properties": {
189
- # "owner": {
190
- # "type": "string",
191
- # "description": "The owner of the repository."
192
- # },
193
- # "repo": {
194
- # "type": "string",
195
- # "description": "The name of the repository."
196
- # },
197
- # "issue_num": {
198
- # "type": "string",
199
- # "description": "The issue number."
200
- # },
201
- # "comment_body": {
202
- # "type": "string",
203
- # "description": "The body of the comment."
204
- # }
205
- # },
206
- # "required": ["owner", "repo", "issue_num", "comment_body"],
207
- # },
208
- # },
209
- # },
210
- # ]
 
1
+ import base64
 
 
 
 
2
  from datetime import datetime, timezone, timedelta
3
+ import jwt
4
  import threading
5
+ import time
6
+ from typing import List
7
+ import requests
8
+ from config import APP_ID, APP_PRIVATE_KEY
9
+
10
 
11
  installation_tokens = {}
12
  token_lock = threading.Lock()
13
 
14
+
15
  def generate_jwt():
16
  """Generate a JWT signed with GitHub App private key."""
17
  now = int(time.time())
 
93
 
94
  # print(get_installation_token(69452220))
95
 
96
+ def fetch_repo_files(owner: str, repo: str, ref: str = "main") -> List[str]:
97
+ """
98
+ Lists all files in the repository by recursively fetching the Git tree from GitHub API.
99
+ Returns a list of file paths.
100
+ """
 
 
 
 
 
 
 
 
101
  installation_id = get_installation_id(owner, repo)
102
  token = get_installation_token(installation_id)
103
+ url = f"https://api.github.com/repos/{owner}/{repo}/git/trees/{ref}?recursive=1"
104
  headers = {
105
  "Authorization": f"Bearer {token}",
106
  "Accept": "application/vnd.github.v3+json"
107
  }
108
  response = github_request("GET", url, headers=headers)
109
+ if response.status_code != 200:
110
+ raise Exception(f"Failed to list repository files: {response.status_code} {response.text}")
111
+
112
+ tree = response.json().get("tree", [])
113
+ file_paths = [item["path"] for item in tree if item["type"] == "blob"]
114
+ return file_paths
115
 
116
+ # print(fetch_repo_files("aditi-dsi", "EvalAI-Starters", "master"))
117
 
118
+ def fetch_file_content(owner: str, repo: str, path: str, ref: str = "main") -> str:
119
+ """
120
+ Fetches the content of a file from the GitHub repository.
121
+ """
122
  installation_id = get_installation_id(owner, repo)
123
  token = get_installation_token(installation_id)
124
+ url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}"
125
  headers = {
126
  "Authorization": f"Bearer {token}",
127
  "Accept": "application/vnd.github.v3+json"
128
  }
129
+ response = github_request("GET", url, headers=headers)
130
+ if response.status_code != 200:
131
+ raise Exception(f"Failed to fetch file content {path}: {response.status_code} {response.text}")
132
+
133
+ content_json = response.json()
134
+ content = base64.b64decode(content_json["content"]).decode("utf-8", errors="ignore")
135
+ return content
136
+
137
+ # print(fetch_file_content("aditi-dsi", "testing-cryptope", "frontend/src/lib/buildSwap.ts", "main"))
138
+