24Arys11 commited on
Commit
d26c7f3
·
1 Parent(s): 81917a3

setup project structure: getting ready to build my agents

Browse files
.gitignore ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .coverage
39
+ .coverage.*
40
+ .cache
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ .hypothesis/
45
+
46
+ # Jupyter Notebook
47
+ .ipynb_checkpoints
48
+
49
+ # Virtual environments
50
+ .venv
51
+ venv/
52
+ ENV/
53
+ env/
54
+ env.bak/
55
+ venv.bak/
56
+
57
+ # IDE related
58
+ .idea/
59
+ .vscode/
60
+ *.swp
61
+ *.swo
62
+
63
+ # Local configuration file
64
+ .env
65
+
66
+ # Other files
67
+ _to_ignore/
68
+ .github/
app.py CHANGED
@@ -1,196 +1,292 @@
1
  import os
2
  import gradio as gr
3
  import requests
4
- import inspect
5
  import pandas as pd
 
 
6
 
7
- # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
10
 
11
- # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
- class BasicAgent:
14
  def __init__(self):
15
- print("BasicAgent initialized.")
16
- def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
21
-
22
- def run_and_submit_all( profile: gr.OAuthProfile | None):
23
- """
24
- Fetches all questions, runs the BasicAgent on them, submits all answers,
25
- and displays the results.
26
- """
27
- # --- Determine HF Space Runtime URL and Repo URL ---
28
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
-
30
- if profile:
31
- username= f"{profile.username}"
32
- print(f"User logged in: {username}")
33
- else:
34
- print("User not logged in.")
35
- return "Please Login to Hugging Face with the button.", None
36
-
37
- api_url = DEFAULT_API_URL
38
- questions_url = f"{api_url}/questions"
39
- submit_url = f"{api_url}/submit"
40
-
41
- # 1. Instantiate Agent ( modify this part to create your agent)
42
- try:
43
- agent = BasicAgent()
44
- except Exception as e:
45
- print(f"Error instantiating agent: {e}")
46
- return f"Error initializing agent: {e}", None
47
- # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
48
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
49
- print(agent_code)
50
-
51
- # 2. Fetch Questions
52
- print(f"Fetching questions from: {questions_url}")
53
- try:
54
- response = requests.get(questions_url, timeout=15)
55
- response.raise_for_status()
56
- questions_data = response.json()
57
- if not questions_data:
58
- print("Fetched questions list is empty.")
59
- return "Fetched questions list is empty or invalid format.", None
60
- print(f"Fetched {len(questions_data)} questions.")
61
- except requests.exceptions.RequestException as e:
62
- print(f"Error fetching questions: {e}")
63
- return f"Error fetching questions: {e}", None
64
- except requests.exceptions.JSONDecodeError as e:
65
- print(f"Error decoding JSON response from questions endpoint: {e}")
66
- print(f"Response text: {response.text[:500]}")
67
- return f"Error decoding server response for questions: {e}", None
68
- except Exception as e:
69
- print(f"An unexpected error occurred fetching questions: {e}")
70
- return f"An unexpected error occurred fetching questions: {e}", None
71
-
72
- # 3. Run your Agent
73
- results_log = []
74
- answers_payload = []
75
- print(f"Running agent on {len(questions_data)} questions...")
76
- for item in questions_data:
77
- task_id = item.get("task_id")
78
- question_text = item.get("question")
79
- if not task_id or question_text is None:
80
- print(f"Skipping item with missing task_id or question: {item}")
81
- continue
82
  try:
83
- submitted_answer = agent(question_text)
84
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
 
 
 
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
- print(f"Error running agent on task {task_id}: {e}")
88
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
89
-
90
- if not answers_payload:
91
- print("Agent did not produce any answers to submit.")
92
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
-
94
- # 4. Prepare Submission
95
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
- print(status_update)
98
-
99
- # 5. Submit
100
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
101
- try:
102
- response = requests.post(submit_url, json=submission_data, timeout=60)
103
- response.raise_for_status()
104
- result_data = response.json()
105
- final_status = (
106
- f"Submission Successful!\n"
107
- f"User: {result_data.get('username')}\n"
108
- f"Overall Score: {result_data.get('score', 'N/A')}% "
109
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
110
- f"Message: {result_data.get('message', 'No message received.')}"
111
- )
112
- print("Submission successful.")
113
- results_df = pd.DataFrame(results_log)
114
- return final_status, results_df
115
- except requests.exceptions.HTTPError as e:
116
- error_detail = f"Server responded with status {e.response.status_code}."
117
- try:
118
- error_json = e.response.json()
119
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
120
- except requests.exceptions.JSONDecodeError:
121
- error_detail += f" Response: {e.response.text[:500]}"
122
- status_message = f"Submission Failed: {error_detail}"
123
- print(status_message)
124
- results_df = pd.DataFrame(results_log)
125
- return status_message, results_df
126
- except requests.exceptions.Timeout:
127
- status_message = "Submission Failed: The request timed out."
128
- print(status_message)
129
- results_df = pd.DataFrame(results_log)
130
- return status_message, results_df
131
- except requests.exceptions.RequestException as e:
132
- status_message = f"Submission Failed: Network error - {e}"
133
- print(status_message)
134
- results_df = pd.DataFrame(results_log)
135
- return status_message, results_df
136
- except Exception as e:
137
- status_message = f"An unexpected error occurred during submission: {e}"
138
- print(status_message)
139
- results_df = pd.DataFrame(results_log)
140
- return status_message, results_df
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- # --- Build Gradio Interface using Blocks ---
144
- with gr.Blocks() as demo:
145
- gr.Markdown("# Basic Agent Evaluation Runner")
146
- gr.Markdown(
147
  """
148
- **Instructions:**
 
 
 
 
 
 
 
 
149
 
150
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- ---
155
- **Disclaimers:**
156
- Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
157
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
158
  """
159
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- gr.LoginButton()
162
 
163
- run_button = gr.Button("Run Evaluation & Submit All Answers")
164
 
165
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
- # Removed max_rows=10 from DataFrame constructor
167
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
- run_button.click(
170
- fn=run_and_submit_all,
171
- outputs=[status_output, results_table]
172
- )
 
173
 
174
- if __name__ == "__main__":
175
- print("\n" + "-"*30 + " App Starting " + "-"*30)
176
- # Check for SPACE_HOST and SPACE_ID at startup for information
177
- space_host_startup = os.getenv("SPACE_HOST")
178
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
 
180
- if space_host_startup:
181
- print(f"✅ SPACE_HOST found: {space_host_startup}")
182
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
- else:
184
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
 
186
- if space_id_startup: # Print repo URLs if SPACE_ID is found
187
- print(f"✅ SPACE_ID found: {space_id_startup}")
188
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
- else:
191
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
 
193
- print("-"*(60 + len(" App Starting ")) + "\n")
194
 
195
- print("Launching Gradio Interface for Basic Agent Evaluation...")
196
- demo.launch(debug=True, share=False)
 
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
+ from manager import Manager
6
+
7
 
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+ MOCK_SUBMISSION = True
11
+ QUESTIONS_LIMIT = 3
12
+
13
+
14
+ class Application:
15
 
 
 
 
16
  def __init__(self):
17
+ self.space_id = os.getenv("SPACE_ID")
18
+ self.username = None
19
+ self.questions_url, self.submit_url = self._get_runtime_and_repo_urls()
20
+
21
+ @staticmethod
22
+ def _get_username(profile: gr.OAuthProfile | None):
23
+ """Get profile username"""
24
+ if profile:
25
+ username= f"{profile.username}"
26
+ print(f"User logged in: {username}")
27
+ return username
28
+ else:
29
+ print("User not logged in.")
30
+ return None
31
+
32
+ @staticmethod
33
+ def _get_runtime_and_repo_urls():
34
+ """Determine HF Space Runtime URL and Repo URL"""
35
+ api_url = DEFAULT_API_URL
36
+ questions_url = f"{api_url}/questions"
37
+ submit_url = f"{api_url}/submit"
38
+ return questions_url, submit_url
39
+
40
+ def _fetch_questions(self):
41
+ """
42
+ Fetches questions from `questions_url`.
43
+
44
+ Sends a GET request to retrieve and parse questions as JSON. Handles network,
45
+ JSON decoding, and unexpected errors.
46
+
47
+ Returns:
48
+ tuple: (error_message: str or None, questions: list or None)
49
+
50
+ Logs:
51
+ - Progress, success, empty data, or errors.
52
+ """
53
+
54
+ print(f"Fetching questions from: {self.questions_url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  try:
56
+ response = requests.get(self.questions_url, timeout=15)
57
+ response.raise_for_status()
58
+ questions_data = response.json()
59
+ if not questions_data:
60
+ print("Fetched questions list is empty.")
61
+ return "Fetched questions list is empty or invalid format.", None
62
+ print(f"Fetched {len(questions_data)} questions.")
63
+ except requests.exceptions.RequestException as e:
64
+ print(f"Error fetching questions: {e}")
65
+ return f"Error fetching questions: {e}", None
66
+ except requests.exceptions.JSONDecodeError as e:
67
+ print(f"Error decoding JSON response from questions endpoint: {e}")
68
+ print(f"Response text: {response.text[:500]}")
69
+ return f"Error decoding server response for questions: {e}", None
70
  except Exception as e:
71
+ print(f"An unexpected error occurred fetching questions: {e}")
72
+ return f"An unexpected error occurred fetching questions: {e}", None
73
+ return questions_data
74
+
75
+ @staticmethod
76
+ async def _run_agent(questions_data, agent):
77
+ """
78
+ Runs the agent on a list of questions and collects results.
79
+
80
+ Args:
81
+ questions_data (list): List of question dictionaries with "task_id" and "question".
82
+ agent (callable): Callable that processes a question and returns an answer.
83
+
84
+ Returns:
85
+ tuple:
86
+ - results_log (list): Logs with "Task ID", "Question", and "Submitted Answer".
87
+ - answers_payload (list): Payload with "task_id" and "submitted_answer".
88
+ """
89
+ if QUESTIONS_LIMIT > 0:
90
+ questions_data = questions_data[:QUESTIONS_LIMIT]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ results_log = []
93
+ answers_payload = []
94
+ print(f"Running agent on {len(questions_data)} questions...")
95
+ for item in questions_data:
96
+ task_id = item.get("task_id")
97
+ question_text = item.get("question")
98
+ if not task_id or question_text is None:
99
+ print(f"Skipping item with missing task_id or question: {item}")
100
+ continue
101
+ try:
102
+ submitted_answer = await agent(question_text)
103
+ print(f"SUBMITED_ANSWER: {submitted_answer}")
104
+ answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
105
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
106
+ except Exception as e:
107
+ print(f"Error running agent on task {task_id}: {e}")
108
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
109
+ return results_log, answers_payload
110
 
111
+ def _submit(self, answers_payload, submission_data, results_log):
 
 
 
112
  """
113
+ Submits answers to the specified URL and processes the response.
114
+
115
+ Args:
116
+ answers_payload (list): List of answers to submit.
117
+ submission_data (dict): Payload for the POST request.
118
+ results_log (list): Log of results to convert into a DataFrame.
119
+
120
+ Returns:
121
+ tuple: (status_message (str), results_df (pd.DataFrame)).
122
 
123
+ Notes:
124
+ - Sends a POST request to `self.submit_url` with `submission_data`.
125
+ - Handles exceptions and provides error messages.
126
+ """
127
+ print(f"Submitting {len(answers_payload)} answers to: {self.submit_url}")
128
+ try:
129
+ if MOCK_SUBMISSION:
130
+ mock_response = type('MockResponse', (), {
131
+ 'status_code': 200,
132
+ 'json': lambda *args: {
133
+ "username": self.username,
134
+ "score": 100,
135
+ "correct_count": len(answers_payload),
136
+ "total_attempted": len(answers_payload),
137
+ "message": "Mock submission successful."
138
+ }
139
+ })
140
+ response = mock_response()
141
+ else:
142
+ response = requests.post(self.submit_url, json=submission_data, timeout=60)
143
+ response.raise_for_status()
144
+ result_data = response.json()
145
+ final_status = (
146
+ f"Submission Successful!\n"
147
+ f"User: {result_data.get('username')}\n"
148
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
149
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
150
+ f"Message: {result_data.get('message', 'No message received.')}"
151
+ )
152
+ print("Submission successful.")
153
+ results_df = pd.DataFrame(results_log)
154
+ return final_status, results_df
155
+ except requests.exceptions.HTTPError as e:
156
+ error_detail = f"Server responded with status {e.response.status_code}."
157
+ try:
158
+ error_json = e.response.json()
159
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
160
+ except requests.exceptions.JSONDecodeError:
161
+ error_detail += f" Response: {e.response.text[:500]}"
162
+ status_message = f"Submission Failed: {error_detail}"
163
+ print(status_message)
164
+ results_df = pd.DataFrame(results_log)
165
+ return status_message, results_df
166
+ except requests.exceptions.Timeout:
167
+ status_message = "Submission Failed: The request timed out."
168
+ print(status_message)
169
+ results_df = pd.DataFrame(results_log)
170
+ return status_message, results_df
171
+ except requests.exceptions.RequestException as e:
172
+ status_message = f"Submission Failed: Network error - {e}"
173
+ print(status_message)
174
+ results_df = pd.DataFrame(results_log)
175
+ return status_message, results_df
176
+ except Exception as e:
177
+ status_message = f"An unexpected error occurred during submission: {e}"
178
+ print(status_message)
179
+ results_df = pd.DataFrame(results_log)
180
+ return status_message, results_df
181
 
182
+ async def eval_and_submit_all(self, profile: gr.OAuthProfile | None):
183
+ """
184
+ Fetches all questions, runs the agent on them, submits all answers,
185
+ and displays the results.
186
  """
187
+ self.username = self._get_username(profile)
188
+ if self.username is None:
189
+ return "Please Login to Hugging Face with the button.", None
190
+
191
+ # 1. Instantiate the Main Agent
192
+ try:
193
+ agent = Manager()
194
+ except Exception as e:
195
+ print(f"Error instantiating agent: {e}")
196
+ return f"Error initializing agent: {e}", None
197
+ # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
198
+ agent_code = f"https://huggingface.co/spaces/{self.space_id}/tree/main"
199
+ print(agent_code)
200
+
201
+ # 2. Fetch Questions
202
+ questions_data = self._fetch_questions()
203
+
204
+ # 3. Run your Agent
205
+ results_log, answers_payload = await self._run_agent(questions_data, agent)
206
+
207
+ if not answers_payload:
208
+ print("Agent did not produce any answers to submit.")
209
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
210
+
211
+ # 4. Prepare Submission
212
+ submission_data = {"username": self.username.strip(), "agent_code": agent_code, "answers": answers_payload}
213
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{self.username}'..."
214
+ print(status_update)
215
+
216
+ # 5. Submit
217
+ status_message, results_df = self._submit(answers_payload, submission_data, results_log)
218
+ return status_message, results_df
219
 
 
220
 
221
+ class UI:
222
 
223
+ app = Application()
224
+
225
+ @classmethod
226
+ def _check_space_host_and_id(cls):
227
+ """Check for SPACE_HOST and SPACE_ID at startup for information"""
228
+ space_host_startup = os.getenv("SPACE_HOST")
229
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
230
+
231
+ if space_host_startup:
232
+ print(f"✅ SPACE_HOST found: {space_host_startup}")
233
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
234
+ else:
235
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
236
+
237
+ # Print repo URLs if SPACE_ID is found
238
+ if space_id_startup:
239
+ print(f"✅ SPACE_ID found: {space_id_startup}")
240
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
241
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
242
+ else:
243
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
244
+
245
+ @classmethod
246
+ def _build(cls):
247
+ gr.Markdown("# Main Agent Evaluation Runner")
248
+ gr.Markdown(
249
+ """
250
+ **Instructions:**
251
+
252
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
253
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
254
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
255
+
256
+ ---
257
+ **Disclaimers:**
258
+ Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
259
+ This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
260
+ """
261
+ )
262
+
263
+ gr.LoginButton()
264
+
265
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
266
+
267
+ status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
268
+ # Removed max_rows=10 from DataFrame constructor
269
+ results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
270
+
271
+ run_button.click(
272
+ fn=cls.app.eval_and_submit_all,
273
+ outputs=[status_output, results_table]
274
+ )
275
 
276
+ @classmethod
277
+ def launch(cls):
278
+ """Build Gradio Interface using Blocks"""
279
+ with gr.Blocks() as demo:
280
+ cls._build()
281
 
282
+ if __name__ == "__main__":
283
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
284
+ cls._check_space_host_and_id()
 
 
285
 
286
+ print("-"*(60 + len(" App Starting ")) + "\n")
 
 
 
 
287
 
288
+ print("Launching Gradio Interface for Main Agent Evaluation...")
289
+ demo.launch(debug=True, share=False)
 
 
 
 
290
 
 
291
 
292
+ UI.launch()
 
args.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from enum import Enum
3
+
4
+
5
+ class InterfaceChoice(Enum):
6
+ HUGGINGFACE = "HuggingFace"
7
+ OPENAILIKE = "OpenAILike"
8
+ OPENAI = "OpenAI"
9
+ # Add your own if you like (then adjust the LLMFactory)
10
+
11
+
12
+ class Args:
13
+ INTERFACE = InterfaceChoice.OPENAILIKE
14
+ model_name="Qwen/Qwen2.5-Coder-32B-Instruct"
15
+ api_base="http://127.0.0.1:1234/v1" # LM Studio local endpoint
16
+ api_key="api_key"
17
+ token = "" # Not needed when using OpenAILike API
diagrams/diagram.png ADDED
diagrams/diagram.puml ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @startuml
2
+
3
+ !include style.puml
4
+
5
+ ' left to right direction
6
+
7
+ ' Hide the class indicators (C)
8
+ hide circle
9
+ hide empty members
10
+
11
+ ' Agent Definitions (Use class notation for agents with tools as fields)
12
+
13
+
14
+ class "Ambassador" as Ambassador <<M>> {
15
+ }
16
+
17
+ class "Manager" as Manager <<M>> {
18
+ - Solver() {trivial task}
19
+ - Manager() {complex task}
20
+ }
21
+
22
+ class "Solver" as Solver {
23
+ - Researcher()
24
+ - EncryptionExpert()
25
+ - MathExpert()
26
+ - Reasoner()
27
+ - ImageHandler()
28
+ - VideoHandler()
29
+ }
30
+
31
+ class "Researcher" as Researcher {
32
+ + DuckDuckGoSearchToolSpec
33
+ }
34
+
35
+ class "EncryptionExpert" as EncryptionExpert {
36
+ + ASCII Encode
37
+ + ASCII Decode
38
+ + ChrToInt Encode
39
+ + ChrToInt Decode
40
+ + Base64 Encode
41
+ + Base64 Decode
42
+ + Caesar Cipher Encode
43
+ + Caesar Cipher Decode
44
+ + Caesar Cipher Brute Force
45
+ + Reverse String
46
+ - MathExpert()
47
+ - Reasoner()
48
+ }
49
+
50
+ class "MathExpert" as MathExpert {
51
+ + Symbolic Math Calculator
52
+ + Unit Converter
53
+ - Reasoner()
54
+ }
55
+
56
+ class "Reasoner" as Reasoner {
57
+ }
58
+
59
+ class "ImageHandler" as ImageHandler {
60
+ }
61
+
62
+ class "VideoHandler" as VideoHandler {
63
+ }
64
+
65
+ ' Agent-to-Agent Connections
66
+ Query --> Ambassador
67
+ Ambassador --> Manager : request
68
+ Manager --> Ambassador : solution
69
+ Ambassador --> Final_Answer : problem solved
70
+
71
+ Manager ..> Manager : complex task
72
+ Manager ..> Solver : trivial task
73
+ Solver ..> Manager : solution
74
+
75
+ Solver .. Researcher : query
76
+ Solver .. EncryptionExpert : query
77
+ Solver .. MathExpert : query
78
+ Solver .. Reasoner : query
79
+ Solver .. ImageHandler : query
80
+ Solver .. VideoHandler : query
81
+
82
+ EncryptionExpert .. MathExpert : query
83
+ EncryptionExpert .. Reasoner : query
84
+ MathExpert .. Reasoner : query
85
+
86
+
87
+ @enduml
diagrams/style.puml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ skinparam class {
2
+ BackgroundColor #E0F7FA
3
+ BorderColor #D84315
4
+ FontColor #E65100
5
+ FontStyle bold
6
+ HeaderBackgroundColor #FFE0B2
7
+ }
llm_factory.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from llama_index.llms.openai_like import OpenAILike
2
+ from llama_index.llms.openai import OpenAI
3
+ from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
4
+ from args import InterfaceChoice, Args
5
+
6
+
7
+ class LLMFactory():
8
+
9
+ @classmethod
10
+ def create(cls, system_prompt, temperature = None, max_tokens = None):
11
+ if Args.INTERFACE == InterfaceChoice.OPENAILIKE:
12
+ return cls._openailike_create(system_prompt, temperature, max_tokens)
13
+ elif Args.INTERFACE == InterfaceChoice.OPENAI:
14
+ return cls._openai_create(system_prompt, temperature, max_tokens)
15
+ elif Args.INTERFACE == InterfaceChoice.HUGGINGFACE:
16
+ return cls._hf_create(system_prompt, temperature, max_tokens)
17
+ else:
18
+ raise ValueError(f"Invalid interface choice: {Args.INTERFACE}")
19
+
20
+ @staticmethod
21
+ def _openailike_create(system_prompt, temperature=None, max_tokens=None):
22
+ kwargs = {
23
+ "model": Args.model_name,
24
+ "api_base": Args.api_base,
25
+ "api_key": Args.api_key,
26
+ "system_prompt": system_prompt,
27
+ }
28
+
29
+ if temperature is not None:
30
+ kwargs["temperature"] = temperature
31
+
32
+ if max_tokens is not None:
33
+ kwargs["max_tokens"] = max_tokens
34
+
35
+ llm = OpenAILike(**kwargs)
36
+ return llm
37
+
38
+ @staticmethod
39
+ def _openai_create(system_prompt, temperature = None, max_tokens = None):
40
+ kwargs = {
41
+ "model": Args.model_name,
42
+ "api_key": Args.api_key,
43
+ "system_prompt": system_prompt,
44
+ }
45
+
46
+ if temperature is not None:
47
+ kwargs["temperature"] = temperature
48
+
49
+ if max_tokens is not None:
50
+ kwargs["max_tokens"] = max_tokens
51
+
52
+ llm = OpenAI(**kwargs)
53
+ return llm
54
+
55
+ @staticmethod
56
+ def _hf_create(system_prompt, temperature = None, max_tokens = None):
57
+ kwargs = {
58
+ "model_name": Args.model_name,
59
+ "system_prompt": system_prompt,
60
+ "token": Args.token,
61
+ }
62
+
63
+ if temperature is not None:
64
+ kwargs["temperature"] = temperature
65
+
66
+ if max_tokens is not None:
67
+ kwargs["max_tokens"] = max_tokens
68
+
69
+ llm = HuggingFaceInferenceAPI(**kwargs)
70
+ return llm
manager.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+ from llm_factory import LLMFactory
4
+
5
+
6
+ # TODO: Langgraph graph that process the following:
7
+ # 1. Thakes the Query Node (Start Node) and sends it to the Assistant Node (has memory - access to the conversation hystory stored in the State)
8
+ # 2. The Assistant Node decides whether the querry is ready to be delivered (the solution is available in the conversation hystory, or the `MAX_DEPTH` has been reached)
9
+ # - if yes: formulates a concise final answer and sends it to the Final_Answer Node (End Node)
10
+ # - if no: formulates a querry (could be the first one or a follow-up) and sends it to the Manager Node (also has access to the conversation hystory)
11
+ # (!) This communication happens back and forth until the querry gets solved (or up to a maximum depth defined by a `MAX_DEPTH` variable)
12
+
13
+
14
+ class Manager:
15
+
16
+ def __init__(self):
17
+ print("Agent initialized.")
18
+
19
+ async def __call__(self, question: str) -> str:
20
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
21
+ self.final_answer = ""
22
+ response = await self.query(question)
23
+ print(f"Agent processed the response: {response}")
24
+ return response
25
+
26
+ async def query(self, question: str) -> str:
27
+ # TODO
28
+ pass
29
+
30
+
31
+ if __name__ == "__main__":
32
+ print("---__main__---")
requirements.txt CHANGED
@@ -1,2 +1,13 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ gradio[oauth]
3
+ requests
4
+ openai
5
+ IPython
6
+ langgraph
7
+ llama_index
8
+ llama-index-llms-huggingface-api
9
+ llama-index-embeddings-huggingface
10
+ llama-index-llms-openai
11
+ llama-index-llms-openai-like
12
+ llama-index-tools-duckduckgo
13
+ pint
solver.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from llama_index.core.agent.workflow import AgentWorkflow
2
+ from llama_index.core.tools import FunctionTool
3
+ from llama_index.core.workflow import Context
4
+ import asyncio
5
+ import os
6
+ from llm_factory import LLMFactory
7
+ from toolbox import Toolbox
8
+
9
+
10
+ class MathExpert:
11
+
12
+ def __init__(self, temperature, max_tokens):
13
+ system_prompt_path = os.path.join(os.getcwd(), "system_prompts", "06_math_expert.txt")
14
+ self.system_prompt = ""
15
+ with open(system_prompt_path, "r") as file:
16
+ self.system_prompt = file.read().strip()
17
+ llm = LLMFactory.create(self.system_prompt, temperature, max_tokens)
18
+ self.agent = AgentWorkflow.from_tools_or_functions(
19
+ [
20
+ Toolbox.math.symbolic_calc,
21
+ Toolbox.math.unit_converter,
22
+ ],
23
+ llm=llm
24
+ )
25
+ self.ctx = Context(self.agent)
26
+
27
+ def get_system_prompt(self):
28
+ return self.system_prompt
29
+
30
+ async def query(self, question: str) -> str:
31
+ response = await self.agent.run(question, ctx=self.ctx)
32
+ response = str(response)
33
+ return response
34
+
35
+
36
+ class Researcher:
37
+ def __init__(self, temperature, max_tokens):
38
+ system_prompt_path = os.path.join(os.getcwd(), "system_prompts", "04_researcher.txt")
39
+ self.system_prompt = ""
40
+ with open(system_prompt_path, "r") as file:
41
+ self.system_prompt = file.read().strip()
42
+ llm = LLMFactory.create(self.system_prompt, temperature, max_tokens)
43
+
44
+ self.agent = AgentWorkflow.from_tools_or_functions(
45
+ Toolbox.web_search.duck_duck_go_tools,
46
+ llm=llm
47
+ )
48
+ self.ctx = Context(self.agent)
49
+
50
+ def get_system_prompt(self):
51
+ return self.system_prompt
52
+
53
+ async def query(self, question: str) -> str:
54
+ response = await self.agent.run(question, ctx=self.ctx)
55
+ response = str(response)
56
+ return response
57
+
58
+
59
+ class EncryptionExpert:
60
+ def __init__(self, temperature, max_tokens):
61
+ system_prompt_path = os.path.join(os.getcwd(), "system_prompts", "05_encryption_expert.txt")
62
+ self.system_prompt = ""
63
+ with open(system_prompt_path, "r") as file:
64
+ self.system_prompt = file.read().strip()
65
+ llm = LLMFactory.create(self.system_prompt, temperature, max_tokens)
66
+
67
+ self.agent = AgentWorkflow.from_tools_or_functions(
68
+ [
69
+ Toolbox.encryption.base64_encode,
70
+ Toolbox.encryption.base64_decode,
71
+ Toolbox.encryption.caesar_cipher_encode,
72
+ Toolbox.encryption.caesar_cipher_decode,
73
+ Toolbox.encryption.reverse_string
74
+ ],
75
+ llm=llm
76
+ )
77
+ self.ctx = Context(self.agent)
78
+
79
+ def get_system_prompt(self):
80
+ return self.system_prompt
81
+
82
+ async def query(self, question: str) -> str:
83
+ response = await self.agent.run(question, ctx=self.ctx)
84
+ response = str(response)
85
+ return response
86
+
87
+
88
+ class ImageHandler:
89
+ pass
90
+
91
+ class VideoHandler:
92
+ pass
93
+
94
+ class RecursiveSolverAgent:
95
+ pass
96
+
97
+
98
+ class Solver:
99
+
100
+ def __init__(self, temperature, max_tokens):
101
+ print("Agent initialized.")
102
+ system_prompt_path = os.path.join(os.getcwd(), "system_prompts", "01_assistant.txt")
103
+ self.system_prompt = ""
104
+ with open(system_prompt_path, "r") as file:
105
+ self.system_prompt = file.read().strip()
106
+ llm = LLMFactory.create(self.system_prompt, temperature, max_tokens)
107
+ self.agent = AgentWorkflow.from_tools_or_functions(
108
+ [
109
+ FunctionTool.from_defaults(self.delegate_to_math_expert),
110
+ FunctionTool.from_defaults(self.set_final_answer)
111
+ ],
112
+ llm=llm
113
+ )
114
+ self.ctx = Context(self.agent)
115
+ self.final_answer = ""
116
+
117
+ async def __call__(self, question: str) -> str:
118
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
119
+ self.final_answer = ""
120
+ response = await self.query(question)
121
+ print(f"Agent processed the response: {response}")
122
+ if self.final_answer == "":
123
+ response = await self.query("I noticed the final_answer is an empty string. Have you forgot to set the final_answer ?")
124
+ return self.final_answer
125
+
126
+ def get_system_prompt(self):
127
+ return self.system_prompt
128
+
129
+ async def query(self, question: str) -> str:
130
+ response = await self.agent.run(question, ctx=self.ctx)
131
+ response = str(response)
132
+
133
+ final_answer = response
134
+
135
+ self.set_final_answer(final_answer)
136
+ return response
137
+
138
+ def set_final_answer(self, final_answer: str) -> str:
139
+ """
140
+ Sets the final answer for the current querry.
141
+
142
+ Args:
143
+ final_answer (str): The final answer to be set for the agent.
144
+
145
+ Returns:
146
+ str: The final answer that was set.
147
+ """
148
+ print("-> set_final_answer !")
149
+ self.final_answer = final_answer
150
+
151
+ def delegate_to_math_expert(self, question: str) -> str:
152
+ print("-> delegated to math agent !")
153
+ math_agent = MathExpert(temperature=0.7, max_tokens=100)
154
+ return math_agent.query(question)
155
+
156
+
157
+ if __name__ == "__main__":
158
+ encryption_agent = EncryptionExpert(temperature=0.7, max_tokens=2000)
159
+ # encryption_query = "Descifer this: 'Bmfy bfx ymj wjxzqy gjybjjs z-hqzo fsi zsnajwxnyfyjf-hwfntaf ns fuwnq 2025 ?'"
160
+ encryption_query = ".rewsna eht sa ""tfel"" drow eht fo etisoppo eht etirw ,ecnetnes siht dnatsrednu uoy fI"
161
+ # print(encryption_agent.get_system_prompt())
162
+ # encoding = encryption_agent.caesar_cipher_encode(encryption_query, 5)
163
+ # print(encoding)
164
+ # print(encryption_agent.caesar_cipher_decode(encoding, 5))
165
+ print(asyncio.run(encryption_agent.query(encryption_query)))
system_prompts/01_assistant.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are a powerful manager assistant. Please respond concisely and accurately.
system_prompts/02_manager.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are a powerful manager assistant. Please respond concisely and accurately.
system_prompts/03_solver.txt ADDED
File without changes
system_prompts/04_researcher.txt ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are a specialized search agent with the capability to search the web for current information.
2
+
3
+ Your goal is to provide accurate, up-to-date information from the web in response to user queries.
4
+
5
+ You have access to the DuckDuckGo search tool that allows you to perform web searches to find information.
6
+
7
+ When responding to questions:
8
+ - Determine if the query requires searching the web for current information
9
+ - Use the DuckDuckGo search tool to find relevant information
10
+ - Summarize and synthesize the information you find from search results
11
+ - Always cite your sources by mentioning where the information came from
12
+ - If the search results don't provide useful information, acknowledge this and explain why
13
+ - Provide a comprehensive answer that directly addresses the query
14
+
15
+ If your initial search doesn't yield satisfactory results:
16
+ - Reformulate your search query using different keywords or phrasings
17
+ - Try more specific queries if initial results are too general
18
+ - Try broader queries if specific searches don't return results
19
+ - Consider breaking complex queries into simpler sub-queries
20
+ - Attempt at least 2-3 different search formulations before concluding information isn't available
21
+ - Explain your search strategy and how you're refining your approach
22
+
23
+ When reformulating searches:
24
+ - Remove or replace jargon with more common terms
25
+ - Include alternative terminology or synonyms
26
+ - Add qualifiers like "latest," "recent," "explained," or "overview" as appropriate
27
+ - Consider searching for specific time periods if date-sensitive information is needed
28
+
29
+ Only use the search tool when appropriate. For simple questions or queries not requiring web information, respond directly without using the tool.
30
+
31
+ Be persistent in your search efforts. Your goal is to provide the most accurate and helpful information possible to the user.
system_prompts/05_encryption_expert.txt ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are an encryption and decryption specialist assistant. Your goal is to help users encode or decode messages using various encryption techniques.
2
+
3
+ Your capabilities include:
4
+ 1. Base64 encoding and decoding
5
+ 2. Caesar cipher encryption and decryption (with customizable shift values)
6
+ 3. String reversal
7
+
8
+ DECRYPTION STRATEGY GUIDE:
9
+ When asked to decrypt or decipher an unknown message:
10
+
11
+ 1. PATTERN RECOGNITION & REASONING APPROACH:
12
+ - First, analyze the encrypted text to identify patterns
13
+ - For potential Caesar ciphers:
14
+ * Look for preserved patterns (punctuation, numbers, spaces)
15
+ * Identify preserved word structure (short words may be "a", "an", "the", "and", etc.)
16
+ * Use frequency analysis - in English, 'e', 't', 'a', 'o', 'i', 'n' are most common letters
17
+ - Map several possible words to determine the rule/shift being applied
18
+ - Once you identify a potential rule, TEST it on several words
19
+ - Develop a hypothesis about what encryption was used and test it systematically
20
+
21
+ 2. For Caesar cipher (most common scenario):
22
+ - When no shift is specified, PERFORM SYSTEMATIC TESTING of shifts:
23
+ * Try common shifts first: 13, 3, 5, 7, 1, 25
24
+ * If those fail, methodically try every shift from 1 to 25
25
+ * For each shift, evaluate if the output contains recognizable English words
26
+ * Test at least 5-10 different shifts before concluding
27
+ - FREQUENCY ANALYSIS: Look for recurring letters and match to common English frequencies
28
+ - WORD PATTERN ANALYSIS: Common 2-3 letter words (is, in, at, the, and) can indicate correct decryption
29
+
30
+ 3. For encoded messages:
31
+ - First check for base64 indicators (character set A-Z, a-z, 0-9, +, /, =)
32
+ - Check for padding characters (=) at the end which often indicate base64
33
+
34
+ 4. For reversed text:
35
+ - Check if reversing produces readable text
36
+
37
+ 5. For combined encryption:
38
+ - Try decrypting using one method, then apply another
39
+
40
+ DEBUGGING AND REASONING PROCESS:
41
+ - Show your work by explaining what you're trying
42
+ - For each Caesar shift attempt, show a sample of the output
43
+ - Compare partial results against known English words
44
+ - Consider if you're seeing partial success (some words readable but others not)
45
+ - If you find readable segments, expand from there
46
+
47
+ EXAMPLES WITH REASONING:
48
+
49
+ Example 1: "Ifmmp xpsme"
50
+ Reasoning: Looking at the pattern, it appears to be a short phrase. Testing Caesar shift 1:
51
+ I → H, f → e, m → l, m → l, p → o...
52
+ Result: "Hello world" - This makes sense, so shift 1 is correct.
53
+
54
+ Example 2: "Xlmw mw e wivmicw tlvewi"
55
+ Reasoning: Testing shift 4:
56
+ X → T, l → h, m → i, w → s...
57
+ Result: "This is a serious phrase" - Correctly decoded with shift 4.
58
+
59
+ Example 3: "What was the result between u-cluj and universitatea-craiova in april 2025?"
60
+ If encrypted with shift 5:
61
+ "Bmfy bfx ymj wjxzqy gjybjjs z-hqzo fsi zsnajwxnyfyjf-hwfntaf ns fuwnq 2025?"
62
+ Reasoning to decrypt:
63
+ - Notice numbers and punctuation are preserved (common in Caesar cipher)
64
+ - Try different shifts:
65
+ With shift 5: "What was the result between u-cluj and universitatea-craiova in april 2025?"
66
+ This produces readable English with proper grammar and preserved patterns.
67
+
68
+ Never give up after a single attempt. If one approach doesn't work, try another systematically.
69
+ For ANY cipher, show your reasoning and demonstrate multiple decryption attempts.
system_prompts/06_math_expert.txt ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are a mathematical problem solver with access to two specialized tools:
2
+
3
+ 1. SYMBOLIC_MATH_CALCULATOR: For all mathematical computations
4
+ 2. UNIT_CONVERTER: ONLY for unit conversions between measurement systems
5
+
6
+ MANDATORY PROTOCOL:
7
+ - You have NO calculation abilities of your own
8
+ - ALWAYS use symbolic_math_calculator for ANY mathematical operation
9
+ - ONLY use unit_converter for converting between physical units (e.g., meters to feet)
10
+ - NEVER state mathematical results unless directly produced by a tool
11
+
12
+ CRITICAL THINKING FRAMEWORK:
13
+
14
+ STEP-BY-STEP REASONING (MANDATORY):
15
+ 1. ANALYZE: Define what is known and unknown precisely
16
+ 2. PLAN: Outline a logical solution strategy before making tool calls
17
+ 3. EXECUTE: Implement each step with a specific tool call
18
+ 4. VERIFY: Confirm results through independent calculations
19
+ 5. INTERPRET: Explain the mathematical meaning of the results
20
+
21
+ AGGRESSIVE ERROR RECOVERY (CRITICAL):
22
+ - If a tool call returns an error, IMMEDIATELY try alternative syntax
23
+ - NEVER give up after a single failed attempt
24
+ - Try at least 3 different syntax variations before considering an approach failed
25
+ - For each error, diagnose the likely cause and adjust accordingly
26
+ - PERSIST with different approaches until you get a result or exhaust all reasonable options
27
+
28
+ ERROR HANDLING STRATEGIES:
29
+ 1. Fix syntax: Correct parentheses, function names, argument order
30
+ 2. Try alternative function: replace "integrate" with "Integral", "simplify" with "expand"
31
+ 3. Break expression into parts: Solve simpler components first
32
+ 4. Use different representation: Convert to different form (polar, exponential)
33
+ 5. Apply mathematical identities: Transform using known equivalences
34
+
35
+ SYMBOLIC MATH CALCULATOR STRATEGIES:
36
+
37
+ FOR CHALLENGING INTEGRALS:
38
+ 1. Try direct computation:
39
+ symbolic_math_calculator("integrate(log(sin(x)), (x, 0, pi/2))")
40
+
41
+ 2. If that fails, try AGGRESSIVELY:
42
+ - Alternative syntax: symbolic_math_calculator("Integral(log(sin(x)), (x, 0, pi/2)).doit()")
43
+ - Known result: symbolic_math_calculator("-pi*log(2)/2")
44
+ - Numerical approach: symbolic_math_calculator("N(integrate(log(sin(x)), (x, 0, pi/2)), 10)")
45
+ - Series expansion: symbolic_math_calculator("series(log(sin(x)), x, 0, 10).integrate(x).subs(x, pi/2)")
46
+ - Integration by parts: Break into multiple steps
47
+
48
+ FOR EQUATIONS:
49
+ 1. Direct solving: symbolic_math_calculator("solve(x**2 - 5*x + 6, x)")
50
+ 2. If that fails, try AGGRESSIVELY:
51
+ - symbolic_math_calculator("solveset(x**2 - 5*x + 6, x)")
52
+ - symbolic_math_calculator("roots(x**2 - 5*x + 6, x)")
53
+ - symbolic_math_calculator("factor(x**2 - 5*x + 6)")
54
+ - symbolic_math_calculator("solve(x**2 - 5*x + 6 == 0, x)")
55
+
56
+ UNIT CONVERTER EXAMPLES:
57
+ 1. Length: unit_converter(value=100, from_unit="cm", to_unit="inch")
58
+ 2. Mass: unit_converter(value=5, from_unit="kg", to_unit="pound")
59
+ 3. Temperature: unit_converter(value=32, from_unit="fahrenheit", to_unit="celsius")
60
+ 4. Speed: unit_converter(value=60, from_unit="mph", to_unit="km/h")
61
+ 5. Volume: unit_converter(value=1, from_unit="gallon", to_unit="liter")
62
+
63
+ DO NOT use unit_converter for mathematical expressions or calculations.
64
+
65
+ LOGICAL VALIDATION FRAMEWORK (MANDATORY):
66
+ 1. CHECK ASSUMPTIONS: Explicitly state all assumptions made
67
+ 2. IDENTIFY CONSTRAINTS: List all constraints and boundary conditions
68
+ 3. VERIFY DOMAIN: Ensure solutions exist within required domain
69
+ 4. TEST EDGE CASES: Verify solution at boundaries and special cases
70
+ 5. CONSISTENCY CHECK: Ensure all results align with mathematical principles
71
+
72
+ VERIFICATION METHODS (USE AT LEAST TWO):
73
+ - Substitute solutions back into original equations
74
+ - Check derivatives of antiderivatives
75
+ - Calculate using alternative methods
76
+ - Test with specific numerical values
77
+ - Apply mathematical identities to verify equivalence
78
+
79
+ RESPONSE STRUCTURE:
80
+ 1. PROBLEM ANALYSIS: Define problem, identify knowns/unknowns, constraints
81
+ 2. SOLUTION STRATEGY: Outline logical approach before executing
82
+ 3. STEP-BY-STEP EXECUTION: Show each tool call with clear purpose
83
+ 4. VERIFICATION: Demonstrate at least two verification methods
84
+ 5. INTERPRETATION: Explain mathematical meaning of results
85
+ 6. CONCLUSION: Present final answer with appropriate precision/units
86
+
87
+ Only present conclusions directly supported by tool outputs. Use sound mathematical logic at each step, and NEVER give up after initial failed attempts.
system_prompts/07_reasoner.txt ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are a mathematical problem solver with access to two specialized tools:
2
+
3
+ 1. SYMBOLIC_MATH_CALCULATOR: For all mathematical computations
4
+ 2. UNIT_CONVERTER: ONLY for unit conversions between measurement systems
5
+
6
+ MANDATORY PROTOCOL:
7
+ - You have NO calculation abilities of your own
8
+ - ALWAYS use symbolic_math_calculator for ANY mathematical operation
9
+ - ONLY use unit_converter for converting between physical units (e.g., meters to feet)
10
+ - NEVER state mathematical results unless directly produced by a tool
11
+
12
+ CRITICAL THINKING FRAMEWORK:
13
+
14
+ STEP-BY-STEP REASONING (MANDATORY):
15
+ 1. ANALYZE: Define what is known and unknown precisely
16
+ 2. PLAN: Outline a logical solution strategy before making tool calls
17
+ 3. EXECUTE: Implement each step with a specific tool call
18
+ 4. VERIFY: Confirm results through independent calculations
19
+ 5. INTERPRET: Explain the mathematical meaning of the results
20
+
21
+ AGGRESSIVE ERROR RECOVERY (CRITICAL):
22
+ - If a tool call returns an error, IMMEDIATELY try alternative syntax
23
+ - NEVER give up after a single failed attempt
24
+ - Try at least 3 different syntax variations before considering an approach failed
25
+ - For each error, diagnose the likely cause and adjust accordingly
26
+ - PERSIST with different approaches until you get a result or exhaust all reasonable options
27
+
28
+ ERROR HANDLING STRATEGIES:
29
+ 1. Fix syntax: Correct parentheses, function names, argument order
30
+ 2. Try alternative function: replace "integrate" with "Integral", "simplify" with "expand"
31
+ 3. Break expression into parts: Solve simpler components first
32
+ 4. Use different representation: Convert to different form (polar, exponential)
33
+ 5. Apply mathematical identities: Transform using known equivalences
34
+
35
+ SYMBOLIC MATH CALCULATOR STRATEGIES:
36
+
37
+ FOR CHALLENGING INTEGRALS:
38
+ 1. Try direct computation:
39
+ symbolic_math_calculator("integrate(log(sin(x)), (x, 0, pi/2))")
40
+
41
+ 2. If that fails, try AGGRESSIVELY:
42
+ - Alternative syntax: symbolic_math_calculator("Integral(log(sin(x)), (x, 0, pi/2)).doit()")
43
+ - Known result: symbolic_math_calculator("-pi*log(2)/2")
44
+ - Numerical approach: symbolic_math_calculator("N(integrate(log(sin(x)), (x, 0, pi/2)), 10)")
45
+ - Series expansion: symbolic_math_calculator("series(log(sin(x)), x, 0, 10).integrate(x).subs(x, pi/2)")
46
+ - Integration by parts: Break into multiple steps
47
+
48
+ FOR EQUATIONS:
49
+ 1. Direct solving: symbolic_math_calculator("solve(x**2 - 5*x + 6, x)")
50
+ 2. If that fails, try AGGRESSIVELY:
51
+ - symbolic_math_calculator("solveset(x**2 - 5*x + 6, x)")
52
+ - symbolic_math_calculator("roots(x**2 - 5*x + 6, x)")
53
+ - symbolic_math_calculator("factor(x**2 - 5*x + 6)")
54
+ - symbolic_math_calculator("solve(x**2 - 5*x + 6 == 0, x)")
55
+
56
+ UNIT CONVERTER EXAMPLES:
57
+ 1. Length: unit_converter(value=100, from_unit="cm", to_unit="inch")
58
+ 2. Mass: unit_converter(value=5, from_unit="kg", to_unit="pound")
59
+ 3. Temperature: unit_converter(value=32, from_unit="fahrenheit", to_unit="celsius")
60
+ 4. Speed: unit_converter(value=60, from_unit="mph", to_unit="km/h")
61
+ 5. Volume: unit_converter(value=1, from_unit="gallon", to_unit="liter")
62
+
63
+ DO NOT use unit_converter for mathematical expressions or calculations.
64
+
65
+ LOGICAL VALIDATION FRAMEWORK (MANDATORY):
66
+ 1. CHECK ASSUMPTIONS: Explicitly state all assumptions made
67
+ 2. IDENTIFY CONSTRAINTS: List all constraints and boundary conditions
68
+ 3. VERIFY DOMAIN: Ensure solutions exist within required domain
69
+ 4. TEST EDGE CASES: Verify solution at boundaries and special cases
70
+ 5. CONSISTENCY CHECK: Ensure all results align with mathematical principles
71
+
72
+ VERIFICATION METHODS (USE AT LEAST TWO):
73
+ - Substitute solutions back into original equations
74
+ - Check derivatives of antiderivatives
75
+ - Calculate using alternative methods
76
+ - Test with specific numerical values
77
+ - Apply mathematical identities to verify equivalence
78
+
79
+ RESPONSE STRUCTURE:
80
+ 1. PROBLEM ANALYSIS: Define problem, identify knowns/unknowns, constraints
81
+ 2. SOLUTION STRATEGY: Outline logical approach before executing
82
+ 3. STEP-BY-STEP EXECUTION: Show each tool call with clear purpose
83
+ 4. VERIFICATION: Demonstrate at least two verification methods
84
+ 5. INTERPRETATION: Explain mathematical meaning of results
85
+ 6. CONCLUSION: Present final answer with appropriate precision/units
86
+
87
+ Only present conclusions directly supported by tool outputs. Use sound mathematical logic at each step, and NEVER give up after initial failed attempts.
system_prompts/08_image_handler.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are a powerful manager assistant. Please respond concisely and accurately.
system_prompts/09_video_handler.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are a powerful manager assistant. Please respond concisely and accurately.
test_agents.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OUTDATED !
2
+ from agents_stage_2 import MathAgent, MainAgent
3
+ import asyncio
4
+
5
+ def test_math_agent():
6
+ math_agent = MathAgent(temperature=0.7, max_tokens=100)
7
+ print(math_agent.get_system_prompt())
8
+ asyncio.run(math_agent.query("What is 345 times 281?"))
9
+
10
+ def test_main_agent():
11
+ main_agent = MainAgent(temperature=0.7, max_tokens=100)
12
+ print(main_agent.get_system_prompt())
13
+ asyncio.run(main_agent.query("What is 345 times 281?"))
14
+
15
+ if __name__ == "__main__":
16
+ # test_math_agent()
17
+ test_main_agent()
toolbox.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pint
2
+ import sympy as sp
3
+
4
+ from llama_index.core.tools import FunctionTool
5
+ from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec
6
+
7
+
8
+ class _Math:
9
+
10
+ @staticmethod
11
+ def symbolic_calc(expression: str) -> str:
12
+ """
13
+ Evaluates complex mathematical expressions using SymPy.
14
+
15
+ Args:
16
+ expression: Mathematical expression as string
17
+
18
+ Returns:
19
+ Result of the calculation
20
+ """
21
+ print(f"-> symbolic_math_calculator tool used (input: {expression}) !")
22
+ try:
23
+ result = sp.sympify(expression)
24
+ print(f"-> (output: {result}) !")
25
+ return str(result)
26
+ except Exception as e:
27
+ return f"Error in calculation: {str(e)}"
28
+
29
+ @staticmethod
30
+ def unit_converter(value: float, from_unit: str, to_unit: str) -> str:
31
+ """
32
+ Converts values between different units of measurement.
33
+
34
+ Args:
35
+ value: The numerical value to convert
36
+ from_unit: The source unit (e.g., 'meter', 'kg', 'celsius')
37
+ to_unit: The target unit (e.g., 'feet', 'pound', 'fahrenheit')
38
+
39
+ Returns:
40
+ The converted value with appropriate units
41
+ """
42
+ print(f"-> unit_converter tool used (inputs: [value: {value}, from_unit: {from_unit}, to_unit: {to_unit}]) !")
43
+ try:
44
+ # Create unit registry
45
+ ureg = pint.UnitRegistry()
46
+
47
+ # Create quantity with source unit
48
+ quantity = value * ureg(from_unit)
49
+
50
+ # Convert to target unit
51
+ result = quantity.to(to_unit)
52
+
53
+ answer = f"{value} {from_unit} = {result.magnitude} {to_unit}"
54
+ print(f"-> (output: {result}) !")
55
+ return answer
56
+ except Exception as e:
57
+ return f"Error in unit conversion: {str(e)}"
58
+
59
+
60
+ class _MathToolbox:
61
+ symbolic_calc = FunctionTool.from_defaults(
62
+ name="symbolic_calc",
63
+ description="Evaluates complex mathematical expressions using SymPy",
64
+ fn=_Math.symbolic_calc
65
+ )
66
+ unit_converter = FunctionTool.from_defaults(
67
+ name="unit_converter",
68
+ description="Converts values between different units of measurement",
69
+ fn=_Math.unit_converter
70
+ )
71
+
72
+
73
+ class _WebSearchToolbox:
74
+ duck_duck_go_tools = DuckDuckGoSearchToolSpec().to_tool_list()
75
+
76
+
77
+ class _Encryption:
78
+
79
+ @staticmethod
80
+ def base64_encode(text: str) -> str:
81
+ """
82
+ Encodes a string to base64.
83
+
84
+ Args:
85
+ text: The text to encode
86
+
87
+ Returns:
88
+ Base64 encoded string
89
+ """
90
+ print(f"-> base64_encode tool used (input: {text[:30]}...) !")
91
+ import base64
92
+ try:
93
+ encoded_bytes = base64.b64encode(text.encode('utf-8'))
94
+ encoded_text = encoded_bytes.decode('utf-8')
95
+ print(f"-> (output: {encoded_text[:30]}...) !")
96
+ return encoded_text
97
+ except Exception as e:
98
+ return f"Error in base64 encoding: {str(e)}"
99
+
100
+ @staticmethod
101
+ def base64_decode(encoded_text: str) -> str:
102
+ """
103
+ Decodes a base64 string to plain text.
104
+
105
+ Args:
106
+ encoded_text: The base64 encoded text
107
+
108
+ Returns:
109
+ Decoded string
110
+ """
111
+ print(f"-> base64_decode tool used (input: {encoded_text[:30]}...) !")
112
+ import base64
113
+ try:
114
+ decoded_bytes = base64.b64decode(encoded_text)
115
+ decoded_text = decoded_bytes.decode('utf-8')
116
+ print(f"-> (output: {decoded_text[:30]}...) !")
117
+ return decoded_text
118
+ except Exception as e:
119
+ return f"Error in base64 decoding: {str(e)}"
120
+
121
+ @staticmethod
122
+ def caesar_cipher_encode(text: str, shift: int) -> str:
123
+ """
124
+ Encodes text using Caesar cipher with specified shift.
125
+
126
+ Args:
127
+ text: The text to encode
128
+ shift: Number of positions to shift each character
129
+
130
+ Returns:
131
+ Caesar cipher encoded string
132
+ """
133
+ print(f"-> caesar_cipher_encode tool used (input: {text[:30]}..., shift: {shift}) !")
134
+ result = ""
135
+ try:
136
+ for char in text:
137
+ if char.isalpha():
138
+ ascii_offset = ord('a') if char.islower() else ord('A')
139
+ encoded_char = chr((ord(char) - ascii_offset + shift) % 26 + ascii_offset)
140
+ result += encoded_char
141
+ else:
142
+ result += char
143
+ print(f"-> (output: {result[:30]}...) !")
144
+ return result
145
+ except Exception as e:
146
+ return f"Error in Caesar cipher encoding: {str(e)}"
147
+
148
+ @classmethod
149
+ def caesar_cipher_decode(cls, encoded_text: str, shift: int) -> str:
150
+ """
151
+ Decodes Caesar cipher text with specified shift.
152
+
153
+ Args:
154
+ encoded_text: The encoded text
155
+ shift: Number of positions the text was shifted
156
+
157
+ Returns:
158
+ Decoded string
159
+ """
160
+ print(f"-> caesar_cipher_decode tool used (input: {encoded_text[:30]}..., shift: {shift}) !")
161
+ # To decode, we shift in the opposite direction
162
+ return cls.caesar_cipher_encode(encoded_text, -shift)
163
+
164
+ @staticmethod
165
+ def reverse_string(text: str) -> str:
166
+ """
167
+ Reverses a string.
168
+
169
+ Args:
170
+ text: The text to reverse
171
+
172
+ Returns:
173
+ Reversed string
174
+ """
175
+ print(f"-> reverse_string tool used (input: {text[:30]}...) !")
176
+ reversed_text = text[::-1]
177
+ print(f"-> (output: {reversed_text[:30]}...) !")
178
+ return reversed_text
179
+
180
+
181
+ class _EncryptionToolbox:
182
+ base64_encode = FunctionTool.from_defaults(
183
+ name="base64_encode",
184
+ description="Encode a string to base64",
185
+ fn=_Encryption.base64_encode
186
+ )
187
+ base64_decode = FunctionTool.from_defaults(
188
+ name="base64_decode",
189
+ description="Decode a base64 string to plain text",
190
+ fn=_Encryption.base64_decode
191
+ )
192
+ caesar_cipher_encode = FunctionTool.from_defaults(
193
+ name="caesar_cipher_encode",
194
+ description="Encode a string using Caesar cipher with specified shift",
195
+ fn=_Encryption.caesar_cipher_encode
196
+ )
197
+ caesar_cipher_decode = FunctionTool.from_defaults(
198
+ name="caesar_cipher_decode",
199
+ description="Decode a Caesar cipher string with specified shift",
200
+ fn=_Encryption.caesar_cipher_decode
201
+ )
202
+ reverse_string = FunctionTool.from_defaults(
203
+ name="reverse_string",
204
+ description="Reverse a string",
205
+ fn=_Encryption.reverse_string
206
+ )
207
+
208
+
209
+ class Toolbox:
210
+ math = _MathToolbox()
211
+ web_search = _WebSearchToolbox()
212
+ encryption = _EncryptionToolbox()