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:
|