Lasdw commited on
Commit
71621f7
·
1 Parent(s): b699411

fix python code tool error

Browse files
Files changed (2) hide show
  1. agent.py +73 -12
  2. tools.py +196 -35
agent.py CHANGED
@@ -29,7 +29,8 @@ from tools import (
29
  excel_to_text,
30
  save_attachment_to_tempfile,
31
  process_youtube_video,
32
- transcribe_audio
 
33
  )
34
 
35
  load_dotenv()
@@ -69,6 +70,7 @@ excel_to_text: Convert Excel to Markdown table with attachment, args: {"excel_pa
69
  process_youtube_video: Extract and analyze YouTube video content by providing the video URL. Returns video metadata and transcript, args: {"url": {"type": "string"}, "summarize": {"type": "boolean", "optional": true}}
70
  transcribe_audio: Transcribe audio files using OpenAI Whisper, args: {"audio_path": {"type": "string"}, "file_content": {"type": "string", "optional": true}, "language": {"type": "string", "optional": true}}
71
 
 
72
  IMPORTANT: Make sure your JSON is properly formatted with double quotes around keys and string values.
73
 
74
  Example use for tools:
@@ -474,15 +476,64 @@ def python_code_node(state: AgentState) -> Dict[str, Any]:
474
  print(f"Python code action_input: {action_input}")
475
  print(f"Action input type: {type(action_input)}")
476
 
477
- # Try different ways to extract the code
478
- code = ""
479
- if isinstance(action_input, dict):
480
- code = action_input.get("code", "")
481
- print(f"Extracted code from dict: {repr(code[:100])}")
482
- elif isinstance(action_input, str):
483
- # If action_input is a string, it might be the code directly
484
- code = action_input
485
- print(f"Using string as code: {repr(code[:100])}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
 
487
  # Additional validation: check for unmatched braces
488
  open_braces = code.count('{')
@@ -490,7 +541,7 @@ def python_code_node(state: AgentState) -> Dict[str, Any]:
490
  if open_braces != close_braces:
491
  result = f"Error: Code contains unmatched braces. Found {open_braces} '{{' and {close_braces} '}}'. Please check your code syntax."
492
  else:
493
- # Call the code execution function
494
  result = run_python_code(code)
495
 
496
  print(f"Code execution result: {result[:100]}...") # Print first 100 chars
@@ -1109,7 +1160,17 @@ class TurboNerd:
1109
  # Example usage:
1110
  if __name__ == "__main__":
1111
  agent = TurboNerd(max_iterations=25)
1112
- response = agent("""Who did the actor who played Ray in the Polish-language version of Everybody Loves Raymond play in Magda M.? Give only the first name.""")
 
 
 
 
 
 
 
 
 
 
1113
  print("\nFinal Response:")
1114
  print(response)
1115
 
 
29
  excel_to_text,
30
  save_attachment_to_tempfile,
31
  process_youtube_video,
32
+ transcribe_audio,
33
+ extract_python_code_from_complex_input
34
  )
35
 
36
  load_dotenv()
 
70
  process_youtube_video: Extract and analyze YouTube video content by providing the video URL. Returns video metadata and transcript, args: {"url": {"type": "string"}, "summarize": {"type": "boolean", "optional": true}}
71
  transcribe_audio: Transcribe audio files using OpenAI Whisper, args: {"audio_path": {"type": "string"}, "file_content": {"type": "string", "optional": true}, "language": {"type": "string", "optional": true}}
72
 
73
+ If you get stuck, try using another tool. For example if you are unable to find relevant information from the tavily_search tool, try using the wikipedia_search tool and vice versa.
74
  IMPORTANT: Make sure your JSON is properly formatted with double quotes around keys and string values.
75
 
76
  Example use for tools:
 
476
  print(f"Python code action_input: {action_input}")
477
  print(f"Action input type: {type(action_input)}")
478
 
479
+ # First try our specialized extraction function that handles nested structures
480
+ code = extract_python_code_from_complex_input(action_input)
481
+
482
+ # If extraction failed or returned the same complex structure, fallback to regex
483
+ if code == action_input or (isinstance(code, str) and code.strip().startswith('{') and '"code"' in code):
484
+ # Convert the action_input to string for regex processing if it's a dictionary
485
+ if isinstance(action_input, dict):
486
+ action_input_str = json.dumps(action_input)
487
+ else:
488
+ action_input_str = str(action_input)
489
+
490
+ # First, attempt direct regex extraction which is most robust for nested structures
491
+ import re
492
+
493
+ # Try to extract code using regex patterns for different nesting levels
494
+ # Pattern for deeply nested code
495
+ deep_pattern = re.search(r'"code"\s*:\s*"(.*?)(?<!\\)"\s*}\s*}\s*}', action_input_str, re.DOTALL)
496
+ if deep_pattern:
497
+ extracted_code = deep_pattern.group(1)
498
+ # Unescape the extracted code
499
+ extracted_code = extracted_code.replace('\\n', '\n').replace('\\"', '"').replace("\\'", "'")
500
+ code = extracted_code
501
+ print(f"Extracted deeply nested code using regex: {repr(code[:100])}")
502
+
503
+ # Pattern for single level nesting
504
+ elif '"code"' in action_input_str:
505
+ pattern = re.search(r'"code"\s*:\s*"(.*?)(?<!\\)"', action_input_str, re.DOTALL)
506
+ if pattern:
507
+ extracted_code = pattern.group(1)
508
+ # Unescape the extracted code
509
+ extracted_code = extracted_code.replace('\\n', '\n').replace('\\"', '"').replace("\\'", "'")
510
+ code = extracted_code
511
+ print(f"Extracted code using regex: {repr(code[:100])}")
512
+
513
+ # If regex extraction failed, try dictionary approaches
514
+ if code == action_input and isinstance(action_input, dict):
515
+ # Direct code access
516
+ if "code" in action_input:
517
+ code = action_input["code"]
518
+ print(f"Extracted code directly from dict: {repr(code[:100])}")
519
+
520
+ # Nested JSON structure handling
521
+ elif isinstance(action_input.get("code", ""), str) and action_input.get("code", "").strip().startswith('{'):
522
+ try:
523
+ nested_json = json.loads(action_input["code"])
524
+ if "action_input" in nested_json and isinstance(nested_json["action_input"], dict) and "code" in nested_json["action_input"]:
525
+ code = nested_json["action_input"]["code"]
526
+ print(f"Extracted code from nested JSON: {repr(code[:100])}")
527
+ except:
528
+ # If parsing fails, use the code field as-is
529
+ pass
530
+
531
+ # If still no code, use the action_input directly (string case)
532
+ if code == action_input and isinstance(action_input, str):
533
+ code = action_input
534
+ print(f"Using action_input as code: {repr(code[:100])}")
535
+
536
+ print(f"Final code to execute: {repr(code[:100])}...")
537
 
538
  # Additional validation: check for unmatched braces
539
  open_braces = code.count('{')
 
541
  if open_braces != close_braces:
542
  result = f"Error: Code contains unmatched braces. Found {open_braces} '{{' and {close_braces} '}}'. Please check your code syntax."
543
  else:
544
+ # Call the code execution function, which now also has improved extraction logic
545
  result = run_python_code(code)
546
 
547
  print(f"Code execution result: {result[:100]}...") # Print first 100 chars
 
1160
  # Example usage:
1161
  if __name__ == "__main__":
1162
  agent = TurboNerd(max_iterations=25)
1163
+ response = agent("""Given this table defining * on the set S = {a, b, c, d, e}
1164
+
1165
+ |*|a|b|c|d|e|
1166
+ |---|---|---|---|---|---|
1167
+ |a|a|b|c|b|d|
1168
+ |b|b|c|a|e|c|
1169
+ |c|c|a|b|b|a|
1170
+ |d|b|e|b|e|d|
1171
+ |e|d|b|a|d|c|
1172
+
1173
+ provide the subset of S involved in any possible counter-examples that prove * is not commutative. Provide your answer as a comma separated list of the elements in the set in alphabetical order.""")
1174
  print("\nFinal Response:")
1175
  print(response)
1176
 
tools.py CHANGED
@@ -24,46 +24,207 @@ from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, No
24
 
25
  load_dotenv()
26
 
27
- def run_python_code(code: str):
28
- """Execute Python code safely using exec() instead of subprocess."""
29
- # Check for potentially dangerous operations
30
- dangerous_operations = [
31
- "os.system", "os.popen", "os.unlink", "os.remove",
32
- "subprocess.run", "subprocess.call", "subprocess.Popen",
33
- "shutil.rmtree", "shutil.move", "shutil.copy",
34
- "open(", "file(", "eval(", "exec(",
35
- "__import__", "input(", "raw_input(",
36
- "__builtins__", "globals(", "locals(",
37
- "compile(", "execfile(", "reload("
38
- ]
39
 
40
- # Safe imports that should be allowed
41
- safe_imports = {
42
- "import datetime", "import math", "import random",
43
- "import statistics", "import collections", "import itertools",
44
- "import re", "import json", "import csv", "import numpy",
45
- "import pandas", "from math import", "from datetime import",
46
- "from statistics import", "from collections import",
47
- "from itertools import"
48
- }
49
 
50
- # Check for dangerous operations
51
- for dangerous_op in dangerous_operations:
52
- if dangerous_op in code:
53
- return f"Error: Code contains potentially unsafe operations: {dangerous_op}"
54
 
55
- # Check each line for imports
56
- for line in code.splitlines():
57
- line = line.strip()
58
- if line.startswith("import ") or line.startswith("from "):
59
- # Check if it's in our safe list
60
- is_safe = any(line.startswith(safe_import) for safe_import in safe_imports)
61
- # Also allow basic numpy/pandas imports
62
- is_safe = is_safe or line.startswith("import numpy") or line.startswith("import pandas")
63
- if not is_safe:
64
- return f"Error: Code contains potentially unsafe import: {line}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
 
 
 
 
 
66
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  # Capture stdout to get print output
68
  import io
69
  import sys
 
24
 
25
  load_dotenv()
26
 
27
+ def extract_python_code_from_complex_input(input_text):
28
+ """
29
+ Dedicated function to extract Python code from deeply nested JSON structures.
30
+ This function handles the specific case of Python code embedded in nested JSON.
31
+ """
32
+ import re
33
+ import json
 
 
 
 
 
34
 
35
+ # Convert to string if it's not already
36
+ if not isinstance(input_text, str):
37
+ try:
38
+ input_text = json.dumps(input_text)
39
+ except:
40
+ input_text = str(input_text)
 
 
 
41
 
42
+ # Check if this looks like a JSON structure containing code
43
+ if not (input_text.strip().startswith('{') and '"code"' in input_text):
44
+ return input_text # Not a JSON structure, return as is
 
45
 
46
+ # First attempt: Try to extract using a direct regex for the nested case
47
+ # This pattern looks for "code": "..." with proper escaping
48
+ pattern = re.compile(r'"code"\s*:\s*"(.*?)(?<!\\)"\s*}', re.DOTALL)
49
+ matches = pattern.findall(input_text)
50
+
51
+ if matches:
52
+ # Get the longest match (most likely the complete code)
53
+ extracted_code = max(matches, key=len)
54
+
55
+ # Unescape common escape sequences
56
+ extracted_code = extracted_code.replace('\\n', '\n')
57
+ extracted_code = extracted_code.replace('\\"', '"')
58
+ extracted_code = extracted_code.replace("\\'", "'")
59
+ extracted_code = extracted_code.replace("\\\\", "\\")
60
+
61
+ print(f"Extracted code using direct regex approach: {extracted_code[:50]}...")
62
+ return extracted_code
63
+
64
+ # Second attempt: Try JSON parsing and navigate the structure
65
+ try:
66
+ parsed = json.loads(input_text)
67
+
68
+ # Navigate through possible structures
69
+ if isinstance(parsed, dict):
70
+ # Direct code field
71
+ if 'code' in parsed:
72
+ extracted = parsed['code']
73
+ if isinstance(extracted, str):
74
+ return extracted
75
+
76
+ # Action with action_input structure
77
+ if 'action' in parsed and 'action_input' in parsed:
78
+ action_input = parsed['action_input']
79
+
80
+ # Case 1: action_input is a dict with code
81
+ if isinstance(action_input, dict) and 'code' in action_input:
82
+ return action_input['code']
83
+
84
+ # Case 2: action_input is a string that might be JSON
85
+ if isinstance(action_input, str):
86
+ try:
87
+ nested = json.loads(action_input)
88
+ if isinstance(nested, dict) and 'code' in nested:
89
+ return nested['code']
90
+ except:
91
+ # If it's not valid JSON, might be the code itself
92
+ return action_input
93
+ except:
94
+ # If JSON parsing fails, try one more regex approach
95
+ # This looks for any content between balanced braces
96
+ try:
97
+ # Find the innermost code field
98
+ code_start = input_text.rfind('"code"')
99
+ if code_start != -1:
100
+ # Find the start of the value (after the colon and quote)
101
+ value_start = input_text.find(':', code_start)
102
+ if value_start != -1:
103
+ value_start = input_text.find('"', value_start)
104
+ if value_start != -1:
105
+ value_start += 1 # Move past the quote
106
+ # Now find the end quote that's not escaped
107
+ value_end = value_start
108
+ while True:
109
+ next_quote = input_text.find('"', value_end + 1)
110
+ if next_quote == -1:
111
+ break
112
+ # Check if this quote is escaped
113
+ if input_text[next_quote - 1] != '\\':
114
+ value_end = next_quote
115
+ break
116
+ value_end = next_quote
117
+
118
+ if value_end > value_start:
119
+ extracted = input_text[value_start:value_end]
120
+ # Unescape
121
+ extracted = extracted.replace('\\n', '\n')
122
+ extracted = extracted.replace('\\"', '"')
123
+ extracted = extracted.replace("\\'", "'")
124
+ extracted = extracted.replace("\\\\", "\\")
125
+ return extracted
126
+ except:
127
+ pass
128
 
129
+ # If all else fails, return the original input
130
+ return input_text
131
+
132
+ def run_python_code(code: str):
133
+ """Execute Python code safely using exec() instead of subprocess."""
134
  try:
135
+ # Pre-process code to handle complex nested structures
136
+ # This is our most aggressive approach to extract the actual code
137
+ code = extract_python_code_from_complex_input(code)
138
+
139
+ # First, check if the input is a nested JSON structure
140
+ if code.strip().startswith('{') and ('"action"' in code or "'action'" in code):
141
+ try:
142
+ # Common issue: escaped quotes causing JSON parse errors
143
+ # Pre-process to handle common escaping problems
144
+ preprocessed_code = code
145
+
146
+ # Handle the specific case we're seeing with nested escaped quotes
147
+ import re
148
+
149
+ # Search for nested code pattern - this is a more direct approach
150
+ code_pattern = re.search(r'"code"\s*:\s*"(.*?)"\s*\}\s*\}\s*\}', code, re.DOTALL)
151
+ if code_pattern:
152
+ extracted_code = code_pattern.group(1)
153
+ # Unescape the extracted code
154
+ extracted_code = extracted_code.replace('\\n', '\n').replace('\\"', '"').replace("\\'", "'")
155
+ code = extracted_code
156
+ print(f"Extracted code using regex pattern: {code[:100]}")
157
+ else:
158
+ # Try JSON parsing approach if regex fails
159
+ import json
160
+ try:
161
+ # First try direct parsing
162
+ parsed_json = json.loads(code)
163
+
164
+ # Check if this is an action structure with embedded code
165
+ if 'action' in parsed_json and 'action_input' in parsed_json:
166
+ if isinstance(parsed_json['action_input'], dict) and 'code' in parsed_json['action_input']:
167
+ # Extract the actual code from the nested structure
168
+ code = parsed_json['action_input']['code']
169
+ print(f"Extracted code using JSON parsing: {code[:100]}")
170
+ elif isinstance(parsed_json['action_input'], str):
171
+ # Try to parse the action_input as JSON if it's a string
172
+ try:
173
+ inner_input = json.loads(parsed_json['action_input'])
174
+ if isinstance(inner_input, dict) and 'code' in inner_input:
175
+ code = inner_input['code']
176
+ print(f"Extracted nested code: {code[:100]}")
177
+ except:
178
+ # If parsing fails, assume the action_input itself is the code
179
+ code = parsed_json['action_input']
180
+ print(f"Using action_input as code: {code[:100]}")
181
+ except json.JSONDecodeError:
182
+ # Direct parsing failed, try alternative approaches
183
+ print("JSON parsing failed, trying alternative approaches")
184
+ except Exception as e:
185
+ print(f"Error during code extraction: {str(e)}")
186
+ # If JSON parsing fails, proceed with the original code
187
+ pass
188
+
189
+ print(f"Final code to execute: {code[:100]}...")
190
+
191
+ # Check for potentially dangerous operations
192
+ dangerous_operations = [
193
+ "os.system", "os.popen", "os.unlink", "os.remove",
194
+ "subprocess.run", "subprocess.call", "subprocess.Popen",
195
+ "shutil.rmtree", "shutil.move", "shutil.copy",
196
+ "open(", "file(", "eval(", "exec(",
197
+ "__import__", "input(", "raw_input(",
198
+ "__builtins__", "globals(", "locals(",
199
+ "compile(", "execfile(", "reload("
200
+ ]
201
+
202
+ # Safe imports that should be allowed
203
+ safe_imports = {
204
+ "import datetime", "import math", "import random",
205
+ "import statistics", "import collections", "import itertools",
206
+ "import re", "import json", "import csv", "import numpy",
207
+ "import pandas", "from math import", "from datetime import",
208
+ "from statistics import", "from collections import",
209
+ "from itertools import"
210
+ }
211
+
212
+ # Check for dangerous operations
213
+ for dangerous_op in dangerous_operations:
214
+ if dangerous_op in code:
215
+ return f"Error: Code contains potentially unsafe operations: {dangerous_op}"
216
+
217
+ # Check each line for imports
218
+ for line in code.splitlines():
219
+ line = line.strip()
220
+ if line.startswith("import ") or line.startswith("from "):
221
+ # Check if it's in our safe list
222
+ is_safe = any(line.startswith(safe_import) for safe_import in safe_imports)
223
+ # Also allow basic numpy/pandas imports
224
+ is_safe = is_safe or line.startswith("import numpy") or line.startswith("import pandas")
225
+ if not is_safe:
226
+ return f"Error: Code contains potentially unsafe import: {line}"
227
+
228
  # Capture stdout to get print output
229
  import io
230
  import sys