Spaces:
Sleeping
Sleeping
code_tool
Browse files
app.py
CHANGED
@@ -25,11 +25,12 @@ from tools import (
|
|
25 |
wikipedia_search_tool,
|
26 |
ocr_image_tool,
|
27 |
audio_transcriber_tool,
|
28 |
-
parse_excel_tool
|
|
|
29 |
)
|
30 |
|
31 |
# βββββββββββββββββββββββββββ Configuration βββββββββββββββββββββββββββββββ
|
32 |
-
LLM = ChatOpenAI(model_name="gpt-4.1", temperature=0.
|
33 |
MAX_TOOL_CALLS = 5
|
34 |
|
35 |
# βββββββββββββββββββββββββββ Helper utilities ββββββββββββββββββββββββββββ
|
@@ -92,21 +93,25 @@ def tool_selector(state: AgentState) -> AgentState:
|
|
92 |
|
93 |
prompt = SystemMessage(
|
94 |
content=(
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
"Reply with ONE JSON only (no markdown). Choices:\n"
|
96 |
" {'action':'wiki','query':'β¦'}\n"
|
97 |
" {'action':'ocr'}\n"
|
98 |
" {'action':'audio'}\n"
|
99 |
" {'action':'excel'}\n"
|
|
|
|
|
100 |
" {'action':'final'}\n"
|
101 |
-
|
102 |
-
"Use wiki if you need to search online for information. Keep the query short and concise and accurate. The query should not be a prompt but instad you should search for the relevant information rather than asking for the answer directly.\n"
|
103 |
-
"If the question is about any image, you have to use ocr tool. It will tell you about the image also\n"
|
104 |
-
"Use audio if the question is about an audio file\n"
|
105 |
-
"Use excel if the question is about an excel file\n"
|
106 |
|
107 |
)
|
108 |
)
|
109 |
-
raw = LLM(state.messages + [prompt]).content.strip()
|
110 |
print(f"Tool selector response: {raw}")
|
111 |
state.add(AIMessage(content=raw))
|
112 |
parsed = safe_json(raw)
|
@@ -157,6 +162,16 @@ def excel_tool(state: AgentState) -> AgentState:
|
|
157 |
state.next_action = None
|
158 |
return state
|
159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
161 |
# ------------- final answer -------------
|
162 |
|
@@ -167,7 +182,7 @@ def final_node(state: AgentState) -> AgentState:
|
|
167 |
"reply **only** with "
|
168 |
"{\"final_answer\":\"β¦\"} (no markdown, no commentary)."
|
169 |
)
|
170 |
-
raw = LLM(state.messages + [wrap]).content.strip()
|
171 |
# print("raw : ", raw)
|
172 |
state.add(AIMessage(content=raw))
|
173 |
parsed = safe_json(raw)
|
@@ -187,6 +202,7 @@ for name, fn in [
|
|
187 |
("ocr_tool", ocr_tool),
|
188 |
("audio_tool", audio_tool),
|
189 |
("excel_tool", excel_tool),
|
|
|
190 |
("final_node", final_node),
|
191 |
]:
|
192 |
graph.add_node(name, fn)
|
@@ -200,6 +216,7 @@ def dispatch(state: AgentState) -> str:
|
|
200 |
"ocr": "ocr_tool",
|
201 |
"audio": "audio_tool",
|
202 |
"excel": "excel_tool",
|
|
|
203 |
"final": "final_node",
|
204 |
}.get(state.next_action, "final_node")
|
205 |
|
@@ -211,12 +228,13 @@ graph.add_conditional_edges(
|
|
211 |
"ocr_tool": "ocr_tool",
|
212 |
"audio_tool": "audio_tool",
|
213 |
"excel_tool": "excel_tool",
|
|
|
214 |
"final_node": "final_node",
|
215 |
},
|
216 |
)
|
217 |
|
218 |
# tools loop back to selector
|
219 |
-
for tool_name in ("wiki_tool", "ocr_tool", "audio_tool", "excel_tool"):
|
220 |
graph.add_edge(tool_name, "tool_selector")
|
221 |
|
222 |
# final_answer β END
|
|
|
25 |
wikipedia_search_tool,
|
26 |
ocr_image_tool,
|
27 |
audio_transcriber_tool,
|
28 |
+
parse_excel_tool,
|
29 |
+
analyze_code_tool
|
30 |
)
|
31 |
|
32 |
# βββββββββββββββββββββββββββ Configuration βββββββββββββββββββββββββββββββ
|
33 |
+
LLM = ChatOpenAI(model_name="gpt-4.1-mini", temperature=0.3)
|
34 |
MAX_TOOL_CALLS = 5
|
35 |
|
36 |
# βββββββββββββββββββββββββββ Helper utilities ββββββββββββββββββββββββββββ
|
|
|
93 |
|
94 |
prompt = SystemMessage(
|
95 |
content=(
|
96 |
+
"if the tool you want isnt listed below, return {'action':'final'} \n"
|
97 |
+
"Use wiki if you need to search online for information. Keep the query short and concise and accurate. The query should not be a prompt but instad you should search for the relevant information rather than asking for the answer directly.\n"
|
98 |
+
"If the question is about any image, you have to use ocr tool. It will tell you about the image also\n"
|
99 |
+
"Use audio if the question is about an audio file\n"
|
100 |
+
"Use excel if the question is about an excel file\n"
|
101 |
+
"Use code if the question is about a code file, or if you want to run your own code\n"
|
102 |
"Reply with ONE JSON only (no markdown). Choices:\n"
|
103 |
" {'action':'wiki','query':'β¦'}\n"
|
104 |
" {'action':'ocr'}\n"
|
105 |
" {'action':'audio'}\n"
|
106 |
" {'action':'excel'}\n"
|
107 |
+
" {'action':'code', 'snippet':'<python code>'}\n"
|
108 |
+
" {'action':'code', 'file':'<name>.py'}\n"
|
109 |
" {'action':'final'}\n"
|
110 |
+
|
|
|
|
|
|
|
|
|
111 |
|
112 |
)
|
113 |
)
|
114 |
+
raw = LLM.invoke(state.messages + [prompt]).content.strip()
|
115 |
print(f"Tool selector response: {raw}")
|
116 |
state.add(AIMessage(content=raw))
|
117 |
parsed = safe_json(raw)
|
|
|
162 |
state.next_action = None
|
163 |
return state
|
164 |
|
165 |
+
def code_tool(state: AgentState) -> AgentState:
|
166 |
+
out = {"analysis": analyze_code_tool({
|
167 |
+
"task_id": state.task_id,
|
168 |
+
"snippet": state.snippet,
|
169 |
+
"file": state.file
|
170 |
+
})}
|
171 |
+
state.tool_calls += 1
|
172 |
+
state.add(SystemMessage(content=f"CODE_TOOL_OUT: {brief(out)}"))
|
173 |
+
state.next_action = None
|
174 |
+
return state
|
175 |
|
176 |
# ------------- final answer -------------
|
177 |
|
|
|
182 |
"reply **only** with "
|
183 |
"{\"final_answer\":\"β¦\"} (no markdown, no commentary)."
|
184 |
)
|
185 |
+
raw = LLM.invoke(state.messages + [wrap]).content.strip()
|
186 |
# print("raw : ", raw)
|
187 |
state.add(AIMessage(content=raw))
|
188 |
parsed = safe_json(raw)
|
|
|
202 |
("ocr_tool", ocr_tool),
|
203 |
("audio_tool", audio_tool),
|
204 |
("excel_tool", excel_tool),
|
205 |
+
("code_tool", code_tool),
|
206 |
("final_node", final_node),
|
207 |
]:
|
208 |
graph.add_node(name, fn)
|
|
|
216 |
"ocr": "ocr_tool",
|
217 |
"audio": "audio_tool",
|
218 |
"excel": "excel_tool",
|
219 |
+
"code": "code_tool",
|
220 |
"final": "final_node",
|
221 |
}.get(state.next_action, "final_node")
|
222 |
|
|
|
228 |
"ocr_tool": "ocr_tool",
|
229 |
"audio_tool": "audio_tool",
|
230 |
"excel_tool": "excel_tool",
|
231 |
+
"code_tool": "code_tool",
|
232 |
"final_node": "final_node",
|
233 |
},
|
234 |
)
|
235 |
|
236 |
# tools loop back to selector
|
237 |
+
for tool_name in ("wiki_tool", "ocr_tool", "audio_tool", "excel_tool", "code_tool"):
|
238 |
graph.add_edge(tool_name, "tool_selector")
|
239 |
|
240 |
# final_answer β END
|
tools.py
CHANGED
@@ -146,6 +146,7 @@ def parse_excel_tool(args: dict) -> str:
|
|
146 |
xls,
|
147 |
sheet_name=sheet if sheet and sheet in xls.sheet_names else xls.sheet_names[0]
|
148 |
)
|
|
|
149 |
return str(df.to_dict(orient="records"))
|
150 |
except Exception as e:
|
151 |
return f"Error reading Excel file: {e}"
|
@@ -263,7 +264,44 @@ def wikipedia_search_tool(args: dict) -> str:
|
|
263 |
return f"Unexpected error in wikipedia_search_tool: {e}"
|
264 |
|
265 |
|
|
|
|
|
|
|
266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
267 |
|
268 |
|
269 |
# def web_search_tool(state: AgentState) -> AgentState:
|
|
|
146 |
xls,
|
147 |
sheet_name=sheet if sheet and sheet in xls.sheet_names else xls.sheet_names[0]
|
148 |
)
|
149 |
+
print(f"Excel file read successfully: {str(df.to_dict(orient='records'))}")
|
150 |
return str(df.to_dict(orient="records"))
|
151 |
except Exception as e:
|
152 |
return f"Error reading Excel file: {e}"
|
|
|
264 |
return f"Unexpected error in wikipedia_search_tool: {e}"
|
265 |
|
266 |
|
267 |
+
from langchain_openai import ChatOpenAI
|
268 |
+
from langchain.schema import SystemMessage, HumanMessage
|
269 |
+
LLM = ChatOpenAI(model_name="gpt-4.1-mini", temperature=0.2)
|
270 |
|
271 |
+
def analyze_code_tool(args: dict) -> str:
|
272 |
+
"""
|
273 |
+
Either args['snippet'] OR (args['file'] + args['task_id'])
|
274 |
+
Reads the code (max 400 lines / 10 kB) and asks the LLM for:
|
275 |
+
β’ plain-language summary
|
276 |
+
β’ list of key functions/classes
|
277 |
+
β’ obvious bugs or style smells
|
278 |
+
Returns that analysis as a string.
|
279 |
+
"""
|
280 |
+
code_txt = ""
|
281 |
+
if args.get("snippet"):
|
282 |
+
code_txt = args["snippet"]
|
283 |
+
elif args.get("file") and args.get("task_id"):
|
284 |
+
path = _download_file_for_task(args["task_id"], "py")
|
285 |
+
if not path:
|
286 |
+
return "Error: .py file not found for this task."
|
287 |
+
code_txt = Path(path).read_text(encoding="utf-8", errors="ignore")
|
288 |
+
else:
|
289 |
+
return "Error: neither snippet nor file provided."
|
290 |
+
|
291 |
+
# Truncate for safety
|
292 |
+
lines = code_txt.splitlines()[:400]
|
293 |
+
code_sample = "\n".join(lines)[:10_000]
|
294 |
+
|
295 |
+
prompt = [
|
296 |
+
SystemMessage(content="You are a senior Python code reviewer."),
|
297 |
+
HumanMessage(content=(
|
298 |
+
"Please analyse the following code. "
|
299 |
+
"Summarise what it does, list key functions/classes, "
|
300 |
+
"and point out any obvious bugs, performance issues or style problems.\n\n"
|
301 |
+
f"```python\n{code_sample}\n```"
|
302 |
+
))
|
303 |
+
]
|
304 |
+
return LLM.invoke(prompt).content.strip()
|
305 |
|
306 |
|
307 |
# def web_search_tool(state: AgentState) -> AgentState:
|