Mehedi2 commited on
Commit
a55fd4b
·
verified ·
1 Parent(s): cdbaf96

Upload logic.py

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