David commited on
Commit
edf3100
·
1 Parent(s): 095d02f

Implementing agent tools and logic

Browse files
Files changed (3) hide show
  1. agent.py +35 -1
  2. requirements.txt +5 -1
  3. tools.py +124 -0
agent.py CHANGED
@@ -1,4 +1,13 @@
1
  from llama_index.llms.google_genai import GoogleGenAI
 
 
 
 
 
 
 
 
 
2
  import os
3
 
4
  GEMINI_API_KEY = os.getenv("GEMINI_TOKEN")
@@ -6,11 +15,36 @@ GEMINI_MODEL_NAME = "gemini-2.5-flash-preview-04-17"
6
 
7
  class FinalAgent:
8
  def __init__(self):
 
9
  self.llm = GoogleGenAI(model=GEMINI_MODEL_NAME, api_key=GEMINI_API_KEY)
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  print("FinalAgent initialized.")
12
  def __call__(self, question: str) -> str:
 
13
  print(f"Agent received question (first 50 chars): {question[:50]}...")
14
  fixed_answer = "This is a default answer."
15
  print(f"Agent returning fixed answer: {fixed_answer}")
16
- return fixed_answer
 
 
 
 
 
1
  from llama_index.llms.google_genai import GoogleGenAI
2
+ from llama_index.tools.arxiv import ArxivToolSpec
3
+ from llama_index.tools.wikipedia import WikipediaToolSpec
4
+ from llama_index.tools.duckduckgo import DuckDuckGoSearchResultsToolSpec
5
+ from llama_index.core.tools import FunctionTool
6
+ from llama_index.core.agent.workflow import AgentWorkflow
7
+
8
+ from tools import interpret_python_math_code
9
+ from gaia_system_prompt import GAIA_SYSTEM_PROMPT
10
+
11
  import os
12
 
13
  GEMINI_API_KEY = os.getenv("GEMINI_TOKEN")
 
15
 
16
  class FinalAgent:
17
  def __init__(self):
18
+ # LLM Initialization
19
  self.llm = GoogleGenAI(model=GEMINI_MODEL_NAME, api_key=GEMINI_API_KEY)
20
 
21
+ # Tool Initialization
22
+ self.tools = [
23
+ FunctionTool.from_defaults(
24
+ func=interpret_python_math_code,
25
+ name="InterpretPythonMathCode",
26
+ description="Interprets Python code for mathematical expressions."
27
+ ),
28
+ DuckDuckGoSearchResultsToolSpec(),
29
+ WikipediaToolSpec(),
30
+ ArxivToolSpec()
31
+ ]
32
+
33
+ # Agent Workflow Initialization
34
+ self.agent = AgentWorkflow(
35
+ llm=self.llm,
36
+ tools=self.tools,
37
+ system_prompt=GAIA_SYSTEM_PROMPT
38
+ )
39
+
40
  print("FinalAgent initialized.")
41
  def __call__(self, question: str) -> str:
42
+ # Example
43
  print(f"Agent received question (first 50 chars): {question[:50]}...")
44
  fixed_answer = "This is a default answer."
45
  print(f"Agent returning fixed answer: {fixed_answer}")
46
+
47
+ # Implement agent logic here
48
+ response = self.agent.run(question)
49
+
50
+ return response
requirements.txt CHANGED
@@ -2,7 +2,11 @@ gradio[oauth]
2
  requests
3
  numpy
4
  pandas
 
5
  llama-index
6
  llama-index-llms-gemini
7
  llama-index-llms-google-genai
8
- llama-index-utils-workflow
 
 
 
 
2
  requests
3
  numpy
4
  pandas
5
+ scipy
6
  llama-index
7
  llama-index-llms-gemini
8
  llama-index-llms-google-genai
9
+ llama-index-utils-workflow
10
+ llama-index-tools-duckduckgo
11
+ llama-index-tools-arxiv
12
+ llama-index-tools-wikipedia
tools.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import ast
3
+ import io
4
+ import sys
5
+ import numpy as np
6
+ import pandas as pd
7
+ import scipy
8
+
9
+ ALLOWED_MODULES = {"numpy", "pandas", "scipy"}
10
+
11
+ def interpret_python_math_code(python_code: str) -> str:
12
+ """
13
+ Interprets a string of Python code to perform math calculations.
14
+
15
+ Security Note: This function uses exec(). While it attempts to restrict
16
+ imports to numpy, pandas, and scipy, and runs with a restricted
17
+ global scope, executing arbitrary code always carries risks. Ensure
18
+ that input code is from a trusted source or properly sanitized.
19
+
20
+ The code must only import modules from the allowed list: numpy, pandas, scipy.
21
+ Submodules of these (e.g., numpy.linalg, scipy.stats) are permitted.
22
+ For example:
23
+ 'import numpy as np' is allowed.
24
+ 'from scipy.stats import norm' is allowed.
25
+ 'import os' is NOT allowed.
26
+
27
+ To return a result, the code should either:
28
+ 1. End with an expression (e.g., '1 + 1' or 'np.array([1,2,3]).sum()').
29
+ 2. Assign the result to a variable named '_result' (e.g., '_result = my_calculation').
30
+
31
+ Print statements will also be captured and returned along with the result.
32
+ """
33
+ # 1. Validate imports using AST
34
+ try:
35
+ tree = ast.parse(python_code)
36
+ for node in tree.body:
37
+ if isinstance(node, ast.Import):
38
+ for alias in node.names:
39
+ root_module = alias.name.split('.')[0]
40
+ if root_module not in ALLOWED_MODULES:
41
+ return (f"Error: Import of '{alias.name}' is not allowed. "
42
+ f"Only modules from {list(ALLOWED_MODULES)} are permitted.")
43
+ elif isinstance(node, ast.ImportFrom):
44
+ if node.module: # Handles cases like 'from . import something' where module is None
45
+ root_module = node.module.split('.')[0]
46
+ if root_module not in ALLOWED_MODULES:
47
+ return (f"Error: Import from '{node.module}' is not allowed. "
48
+ f"Only modules from {list(ALLOWED_MODULES)} are permitted.")
49
+ except SyntaxError as e:
50
+ return f"Syntax Error in input code: {e}"
51
+
52
+ # 2. Prepare execution environment
53
+ restricted_globals = {
54
+ "__builtins__": {
55
+ "print": print,
56
+ "abs": abs, "round": round, "min": min, "max": max, "sum": sum, "len": len,
57
+ "range": range, "zip": zip, "enumerate": enumerate,
58
+ "int": int, "float": float, "str": str, "list": list, "dict": dict, "tuple": tuple, "set": set,
59
+ "True": True, "False": False, "None": None,
60
+ "__import__": __import__, # Add this line
61
+ }
62
+ # numpy, pandas, scipy are NOT pre-loaded here.
63
+ # The user's code `import numpy` will use Python's import mechanism.
64
+ # The AST check above is the primary guard.
65
+ }
66
+ local_vars = {}
67
+
68
+ # 3. Capture stdout
69
+ old_stdout = sys.stdout
70
+ redirected_output = io.StringIO()
71
+ sys.stdout = redirected_output
72
+
73
+ # 4. Execute code and retrieve result
74
+ calculated_value = None
75
+ result_source = ""
76
+ output_str = ""
77
+
78
+ try:
79
+ compiled_code = compile(python_code, '<string>', 'exec')
80
+ exec(compiled_code, restricted_globals, local_vars)
81
+
82
+ # Priority 1: Check for '_result' variable
83
+ if "_result" in local_vars:
84
+ calculated_value = local_vars["_result"]
85
+ result_source = "variable '_result'"
86
+ # Priority 2: If no _result, and the last AST node was an expression, evaluate it.
87
+ elif tree.body and isinstance(tree.body[-1], ast.Expr):
88
+ # Ensure the expression node's value is a valid AST object for ast.Expression
89
+ if isinstance(tree.body[-1].value, ast.AST):
90
+ last_expr_ast = ast.Expression(body=tree.body[-1].value)
91
+ # Compile the expression in 'eval' mode
92
+ compiled_expr = compile(last_expr_ast, '<string>', 'eval')
93
+ # Evaluate in the context of restricted_globals and local_vars (which holds state from exec)
94
+ calculated_value = eval(compiled_expr, restricted_globals, local_vars)
95
+ result_source = "last expression"
96
+
97
+ sys.stdout = old_stdout # Restore stdout before getting its value
98
+ output_str = redirected_output.getvalue()
99
+
100
+ if calculated_value is not None:
101
+ return f"Result (from {result_source}):\n{calculated_value}\n\nCaptured Output:\n{output_str}".strip()
102
+ else:
103
+ return f"Executed successfully.\n\nCaptured Output:\n{output_str}\n(No specific result value found via '_result' variable or last expression evaluation.)".strip()
104
+
105
+ except Exception as e:
106
+ if sys.stdout == redirected_output: # Ensure stdout is restored on error too
107
+ sys.stdout = old_stdout
108
+ output_str = redirected_output.getvalue() # Get any output captured before the error
109
+ return f"Execution Error: {type(e).__name__}: {e}\n\nCaptured Output:\n{output_str}".strip()
110
+ finally:
111
+ # Ensure stdout is always restored
112
+ if sys.stdout == redirected_output:
113
+ sys.stdout = old_stdout
114
+
115
+
116
+ # Example usage:
117
+ if __name__ == "__main__":
118
+ code = """
119
+ import numpy as np
120
+ # import os # This should trigger an error since 'os' is not allowed
121
+ arr = np.array([1, 2, 3, 4, 5])
122
+ _result = arr.mean()
123
+ """
124
+ print(interpret_python_math_code(code))