ZeroTimo commited on
Commit
909bf64
·
verified ·
1 Parent(s): 23caa29

Create logic.py

Browse files
Files changed (1) hide show
  1. logic.py +196 -0
logic.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Tuple
2
+ import re
3
+ import tempfile
4
+ from pathlib import Path
5
+ import pandas as pd
6
+ import requests
7
+ from agent import GaiaAgent
8
+ from pandas import DataFrame
9
+
10
+ # --- Constants ---
11
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
+ QUESTIONS_URL = f"{DEFAULT_API_URL}/questions"
13
+ SUBMIT_URL = f"{DEFAULT_API_URL}/submit"
14
+ FILE_PATH = f"{DEFAULT_API_URL}/files/"
15
+
16
+
17
+ # --- Helper Methods ---
18
+ def fetch_all_questions() -> Dict:
19
+ """Fetches all questions from the specified API endpoint.
20
+ This function retrieves a list of questions from the API, handles potential errors
21
+ such as network issues, invalid responses, or empty question lists, and returns
22
+ the questions as a dictionary.
23
+ Returns:
24
+ Dict: A dictionary containing the questions data retrieved from the API.
25
+ Raises:
26
+ UserWarning: If there is an error fetching the questions, such as network issues,
27
+ invalid JSON response, or an empty question list. The exception message
28
+ provides details about the specific error encountered.
29
+ """
30
+ print(f"Fetching questions from: {QUESTIONS_URL}")
31
+ response = requests.get(QUESTIONS_URL, timeout=15)
32
+ try:
33
+ response.raise_for_status()
34
+ questions_data = response.json()
35
+ if not questions_data:
36
+ print("Fetched questions list is empty.")
37
+ raise UserWarning("Fetched questions list is empty or invalid format.")
38
+ print(f"Fetched {len(questions_data)} questions.")
39
+ return questions_data
40
+ except requests.exceptions.RequestException as e:
41
+ print(f"Error fetching questions: {e}")
42
+ raise UserWarning(f"Error fetching questions: {e}")
43
+ except requests.exceptions.JSONDecodeError as e:
44
+ print(f"Error decoding JSON response from questions endpoint: {e}")
45
+ print(f"Response text: {response.text[:500]}")
46
+ raise UserWarning(f"Error decoding server response for questions: {e}")
47
+ except Exception as e:
48
+ print(f"An unexpected error occurred fetching questions: {e}")
49
+ raise UserWarning(f"An unexpected error occurred fetching questions: {e}")
50
+
51
+
52
+ def submit_answers(submission_data: dict, results_log: list) -> Tuple[str, DataFrame]:
53
+ """Submits answers to the scoring API and returns the submission status and results.
54
+ This function sends the provided answers to the scoring API, handles potential errors
55
+ such as network issues, server errors, or invalid responses, and returns a status
56
+ message indicating the success or failure of the submission, along with a DataFrame
57
+ containing the results log.
58
+ Args:
59
+ submission_data (dict): A dictionary containing the answers to be submitted.
60
+ Expected to have a structure compatible with the scoring API.
61
+ results_log (list): A list of dictionaries containing the results log.
62
+ This log is converted to a Pandas DataFrame and returned.
63
+ Returns:
64
+ Tuple[str, DataFrame]: A tuple containing:
65
+ - A status message (str) indicating the submission status and any relevant
66
+ information or error messages.
67
+ - A Pandas DataFrame containing the results log.
68
+ """
69
+ try:
70
+ response = requests.post(SUBMIT_URL, json=submission_data, timeout=60)
71
+ response.raise_for_status()
72
+ result_data = response.json()
73
+ final_status = (
74
+ f"Submission Successful!\n"
75
+ f"User: {result_data.get('username')}\n"
76
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
77
+ f"({result_data.get('correct_count', '?')}/"
78
+ f"{result_data.get('total_attempted', '?')} correct)\n"
79
+ f"Message: {result_data.get('message', 'No message received.')}"
80
+ )
81
+ print("Submission successful.")
82
+ results_df = pd.DataFrame(results_log)
83
+ return final_status, results_df
84
+ except requests.exceptions.HTTPError as e:
85
+ error_detail = f"Server responded with status {e.response.status_code}."
86
+ try:
87
+ error_json = e.response.json()
88
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
89
+ except requests.exceptions.JSONDecodeError:
90
+ error_detail += f" Response: {e.response.text[:500]}"
91
+ status_message = f"Submission Failed: {error_detail}"
92
+ print(status_message)
93
+ results_df = pd.DataFrame(results_log)
94
+ return status_message, results_df
95
+ except requests.exceptions.Timeout:
96
+ status_message = "Submission Failed: The request timed out."
97
+ print(status_message)
98
+ results_df = pd.DataFrame(results_log)
99
+ return status_message, results_df
100
+ except requests.exceptions.RequestException as e:
101
+ status_message = f"Submission Failed: Network error - {e}"
102
+ print(status_message)
103
+ results_df = pd.DataFrame(results_log)
104
+ return status_message, results_df
105
+ except Exception as e:
106
+ status_message = f"An unexpected error occurred during submission: {e}"
107
+ print(status_message)
108
+ results_df = pd.DataFrame(results_log)
109
+ return status_message, results_df
110
+
111
+
112
+ def run_agent(
113
+ gaia_agent: GaiaAgent, questions_data: List[Dict]
114
+ ) -> Tuple[List[Dict], List[Dict]]:
115
+ """Runs the agent on a list of questions and returns the results and answers.
116
+ This function iterates through a list of questions, runs the provided agent on each
117
+ question, and collects the results and answers. It handles potential errors during
118
+ agent execution and returns the results log and the answers payload.
119
+ Args:
120
+ gaia_agent (GaiaAgent): An instance of the GaiaAgent class, which is responsible for
121
+ generating answers to the questions.
122
+ questions_data (List[Dict]): A list of dictionaries, where each dictionary
123
+ represents a question and contains at least the 'task_id' and 'question' keys.
124
+ Returns:
125
+ Tuple[List[Dict], List[Dict]]: A tuple containing:
126
+ - A list of dictionaries representing the results log, where each dictionary
127
+ contains the 'Task ID', 'Question', and 'Submitted Answer'.
128
+ - A list of dictionaries representing the answers payload, where each dictionary
129
+ contains the 'task_id' and 'submitted_answer'.
130
+ """
131
+ results_log = []
132
+ answers_payload = []
133
+
134
+ print(f"🚀 Running agent on {len(questions_data)} questions...")
135
+ for item in questions_data:
136
+ task_id = item.get("task_id")
137
+ question_text = item.get("question")
138
+ question_text = process_file(task_id, question_text)
139
+ if not task_id or question_text is None:
140
+ print(f"⚠️ Skipping invalid item (missing task_id or question): {item}")
141
+ continue
142
+ try:
143
+ submitted_answer = gaia_agent(task_id, question_text)
144
+ answers_payload.append(
145
+ {"task_id": task_id, "submitted_answer": submitted_answer}
146
+ )
147
+ except Exception as e:
148
+ print(f"❌ Error running agent on task {task_id}: {e}")
149
+ submitted_answer = f"AGENT ERROR: {e}"
150
+
151
+ results_log.append(
152
+ {
153
+ "Task ID": task_id,
154
+ "Question": question_text,
155
+ "Submitted Answer": submitted_answer,
156
+ }
157
+ )
158
+ return results_log, answers_payload
159
+
160
+
161
+ def process_file(task_id: str, question_text: str) -> str:
162
+ """
163
+ Attempt to download a file associated with a task from the API.
164
+ - If the file exists (HTTP 200), it is saved to a temp directory and the local file path is returned.
165
+ - If no file is found (HTTP 404), returns None.
166
+ - For all other HTTP errors, the exception is propagated to the caller.
167
+ """
168
+ file_url = f"{FILE_PATH}{task_id}"
169
+
170
+ try:
171
+ response = requests.get(file_url, timeout=30)
172
+ response.raise_for_status()
173
+ except requests.exceptions.RequestException as exc:
174
+ print(f"Exception in download_file>> {str(exc)}")
175
+ return question_text # Unable to get the file
176
+
177
+ # Determine filename from 'Content-Disposition' header, fallback to task_id
178
+ content_disposition = response.headers.get("content-disposition", "")
179
+ filename = task_id
180
+ match = re.search(r'filename="([^"]+)"', content_disposition)
181
+ if match:
182
+ filename = match.group(1)
183
+
184
+ # Save file in a temp directory
185
+ temp_storage_dir = Path(tempfile.gettempdir()) / "gaia_cached_files"
186
+ temp_storage_dir.mkdir(parents=True, exist_ok=True)
187
+
188
+ file_path = temp_storage_dir / filename
189
+ file_path.write_bytes(response.content)
190
+ return (
191
+ f"{question_text}\n\n"
192
+ f"---\n"
193
+ f"A file was downloaded for this task and saved locally at:\n"
194
+ f"{str(file_path)}\n"
195
+ f"---\n\n"
196
+ )