Files changed (1) hide show
  1. app.py +0 -402
app.py DELETED
@@ -1,402 +0,0 @@
1
- import os
2
- from dotenv import load_dotenv
3
- load_dotenv()
4
- import gradio as gr
5
- import requests
6
- import inspect
7
- import pandas as pd
8
- #model requirement
9
- from smolagents import DuckDuckGoSearchTool, load_tool, tool, CodeAgent,InferenceClientModel
10
- from typing import TypedDict, List, Dict, Any, Optional,Callable
11
- from langgraph.graph import StateGraph, END
12
- from langchain_openai import ChatOpenAI
13
- from langchain_core.messages import HumanMessage
14
- from langchain_community.tools.tavily_search import TavilySearchResults
15
- from langchain_community.document_loaders import WikipediaLoader
16
- from langchain_community.document_loaders import ArxivLoader
17
- from youtube_transcript_api import YouTubeTranscriptApi
18
-
19
- # (Keep Constants as is)
20
- # --- Constants ---
21
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
22
-
23
- def openrouter_inference(prompt, model="deepseek/deepseek-r1:free"):
24
- api_key = os.environ["OPENROUTER_API_KEY"]
25
- url = "https://openrouter.ai/api/v1/chat/completions"
26
- headers = {
27
- "Authorization": f"Bearer {api_key}",
28
- "Content-Type": "application/json"
29
- }
30
- payload = {
31
- "model": model,
32
- "messages": [
33
- {"role": "user", "content": prompt}
34
- ]
35
- }
36
- response = requests.post(url, headers=headers, json=payload)
37
- response.raise_for_status()
38
- data = response.json()
39
- # Extract the answer from the response
40
- return data["choices"][0]["message"]["content"]
41
-
42
- @tool
43
- def add(a:int,b:int)->int:
44
- """
45
- Adds two integers.
46
- Args:
47
- a (int): The first integer.
48
- b (int): The second integer.
49
- Returns:
50
- int: The sum of the two integers.
51
- """
52
- return a + b
53
-
54
- @tool
55
- def subtract(a:int,b:int)->int:
56
- """
57
- Subtracts two integers.
58
- Args:
59
- a (int): The first integer.
60
- b (int): The second integer.
61
- Returns:
62
- int: The difference of the two integers.
63
- """
64
- return a - b
65
-
66
- @tool
67
- def multiply(a:int,b:int)->int:
68
- """
69
- Multiplies two integers.
70
- Args:
71
- a (int): The first integer.
72
- b (int): The second integer.
73
- Returns:
74
- int: The product of the two integers.
75
- """
76
- return a * b
77
-
78
- @tool
79
- def divide(a:int,b:int)->float:
80
- """
81
- Divides two integers.
82
- Args:
83
- a (int): The numerator.
84
- b (int): The denominator.
85
- Returns:
86
- float: The quotient of the two integers.
87
- """
88
- if b == 0:
89
- raise ValueError("Division by zero is not allowed.")
90
- return a / b
91
-
92
- @tool
93
- def modulus(a: int, b: int) -> int:
94
- """Get the modulus of two numbers.
95
-
96
- Args:
97
- a: first int
98
- b: second int
99
- """
100
- return a % b
101
-
102
- search_tool = DuckDuckGoSearchTool()
103
-
104
- @tool
105
- def web_search(query: str) -> str:
106
- """Search Tavily for a query and return maximum 3 results.
107
-
108
- Args:
109
- query: The search query."""
110
- search_docs = TavilySearchResults(max_results=3).invoke(query=query)
111
- formatted_search_docs = "\n\n---\n\n".join(
112
- [
113
- f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
114
- for doc in search_docs
115
- ])
116
- return {"web_results": formatted_search_docs}
117
-
118
- @tool
119
- def arvix_search(query: str) -> str:
120
- """Search Arxiv for a query and return maximum 3 result.
121
-
122
- Args:
123
- query: The search query."""
124
- search_docs = ArxivLoader(query=query, load_max_docs=3).load()
125
- formatted_search_docs = "\n\n---\n\n".join(
126
- [
127
- f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
128
- for doc in search_docs
129
- ])
130
- return {"arvix_results": formatted_search_docs}
131
-
132
- @tool
133
- def wikipedia_tool(query: str) -> str:
134
- """
135
- Searches Wikipedia for the given query and returns a summary.
136
-
137
- Args:
138
- query (str): The search term or question to look up on Wikipedia.
139
-
140
- Returns:
141
- str: A summary or error message.
142
- """
143
- try:
144
- search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
145
- formatted_search_docs = "\n\n---\n\n".join(
146
- [
147
- f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
148
- for doc in search_docs
149
- ]
150
- )
151
- return formatted_search_docs
152
- except Exception as e:
153
- return f"Wikipedia search error: {e}"
154
-
155
- @tool
156
- def youtube_transcript_tool(video_id: str,query:str) -> str:
157
- """
158
- Fetches the transcript of a YouTube video.
159
-
160
- Args:
161
- video_id (str): The YouTube video ID.
162
- query (str): The question to be answered based on the transcript.
163
-
164
- Returns:
165
- str: The transcript text or an error message.
166
- """
167
- try:
168
- transcript = YouTubeTranscriptApi.get_transcript(video_id)
169
- question = f"Answer the question based on the transcript: {query}"
170
- prompt = (
171
- f"Given the following YouTube transcript, answer the question as directly as possible:\n"
172
- f"Question: {question}\n"
173
- f"Transcript: {transcript}\n"
174
- f"Answer:"
175
- )
176
- answer = openrouter_inference(prompt)
177
- except Exception as e:
178
- return f"Transcript error: {e}"
179
-
180
-
181
-
182
-
183
- image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
184
-
185
-
186
- # --- Basic Agent Definition ---
187
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
188
- class BasicAgent:
189
- def __init__(self):
190
- print("BasicAgent initialized.")
191
- token=os.environ["OPENROUTER_API_KEY"]
192
- self.system_prompt = """
193
- You are a helpful assistant. Answer each question as directly and briefly as possible.
194
- Return only the answer, with no extra text, no punctuation, and no justification.
195
- If the answer is a list, return it as a comma-separated list with no brackets or bullets.
196
- If the answer is a number, write it in digits with no units.
197
- If the answer is a string, use lowercase and no articles or abbreviations.
198
- """
199
-
200
- model = InferenceClientModel(
201
- model_id="deepseek/deepseek-r1:free", # Correct OpenRouter model ID
202
- token=os.environ["OPENROUTER_API_KEY"], # Your OpenRouter API key
203
- provider="openrouter" # Explicitly set to openrouter
204
- )
205
- self.agent= CodeAgent(
206
- tools = [add, subtract, multiply, divide,modulus,arvix_search, web_search, image_generation_tool,youtube_transcript_tool, wikipedia_tool],
207
- model=model,
208
- )
209
- def __call__(self, question: str, context: str = "") -> str:
210
- print(f"Agent received question (first 50 chars): {question[:50]}...")
211
- # Inject system prompt + question
212
- question_with_prompt = f"{self.system_prompt}\n\nContext: {context}\n\nQuestion: {question.strip()}"
213
- try:
214
- answer = openrouter_inference(question_with_prompt)
215
- except Exception as e:
216
- print(f"Error calling OpenRouter: {e}")
217
- answer = f"Sorry, I couldn't get an answer from the model {e}."
218
- print(f"Agent returning answer: {answer.strip()}")
219
- return answer.strip()
220
- # # Fix: handle dict or string
221
- # if isinstance(answer, dict) and "content" in answer:
222
- # result = answer["content"]
223
- # else:
224
- # result = str(answer)
225
- # print(f"Agent returning answer: {result.strip()}")
226
- # return result.strip()
227
-
228
- def run_and_submit_all( profile: gr.OAuthProfile | None):
229
- """
230
- Fetches all questions, runs the BasicAgent on them, submits all answers,
231
- and displays the results.
232
- """
233
- # --- Determine HF Space Runtime URL and Repo URL ---
234
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
235
-
236
- if profile:
237
- username= f"{profile.username}"
238
- print(f"User logged in: {username}")
239
- else:
240
- print("User not logged in.")
241
- return "Please Login to Hugging Face with the button.", None
242
-
243
- api_url = DEFAULT_API_URL
244
- questions_url = f"{api_url}/questions"
245
- submit_url = f"{api_url}/submit"
246
-
247
- # 1. Instantiate Agent ( modify this part to create your agent)
248
- try:
249
- agent = BasicAgent()
250
- except Exception as e:
251
- print(f"Error instantiating agent: {e}")
252
- return f"Error initializing agent: {e}", None
253
- # 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)
254
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
255
- print(agent_code)
256
-
257
- # 2. Fetch Questions
258
- print(f"Fetching questions from: {questions_url}")
259
- try:
260
- response = requests.get(questions_url, timeout=15)
261
- response.raise_for_status()
262
- questions_data = response.json()
263
- if not questions_data:
264
- print("Fetched questions list is empty.")
265
- return "Fetched questions list is empty or invalid format.", None
266
- print(f"Fetched {len(questions_data)} questions.")
267
- except requests.exceptions.RequestException as e:
268
- print(f"Error fetching questions: {e}")
269
- return f"Error fetching questions: {e}", None
270
- except requests.exceptions.JSONDecodeError as e:
271
- print(f"Error decoding JSON response from questions endpoint: {e}")
272
- print(f"Response text: {response.text[:500]}")
273
- return f"Error decoding server response for questions: {e}", None
274
- except Exception as e:
275
- print(f"An unexpected error occurred fetching questions: {e}")
276
- return f"An unexpected error occurred fetching questions: {e}", None
277
-
278
- # 3. Run your Agent
279
- results_log = []
280
- answers_payload = []
281
- print(f"Running agent on {len(questions_data)} questions...")
282
- for item in questions_data:
283
- task_id = item.get("task_id")
284
- question_text = item.get("question")
285
- if not task_id or question_text is None:
286
- print(f"Skipping item with missing task_id or question: {item}")
287
- continue
288
- try:
289
- submitted_answer = agent(question_text)
290
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
291
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
292
- except Exception as e:
293
- print(f"Error running agent on task {task_id}: {e}")
294
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
295
-
296
- if not answers_payload:
297
- print("Agent did not produce any answers to submit.")
298
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
299
-
300
- # 4. Prepare Submission
301
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
302
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
303
- print(status_update)
304
-
305
- # 5. Submit
306
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
307
- try:
308
- response = requests.post(submit_url, json=submission_data, timeout=60)
309
- response.raise_for_status()
310
- result_data = response.json()
311
- final_status = (
312
- f"Submission Successful!\n"
313
- f"User: {result_data.get('username')}\n"
314
- f"Overall Score: {result_data.get('score', 'N/A')}% "
315
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
316
- f"Message: {result_data.get('message', 'No message received.')}"
317
- )
318
- print("Submission successful.")
319
- results_df = pd.DataFrame(results_log)
320
- return final_status, results_df
321
- except requests.exceptions.HTTPError as e:
322
- error_detail = f"Server responded with status {e.response.status_code}."
323
- try:
324
- error_json = e.response.json()
325
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
326
- except requests.exceptions.JSONDecodeError:
327
- error_detail += f" Response: {e.response.text[:500]}"
328
- status_message = f"Submission Failed: {error_detail}"
329
- print(status_message)
330
- results_df = pd.DataFrame(results_log)
331
- return status_message, results_df
332
- except requests.exceptions.Timeout:
333
- status_message = "Submission Failed: The request timed out."
334
- print(status_message)
335
- results_df = pd.DataFrame(results_log)
336
- return status_message, results_df
337
- except requests.exceptions.RequestException as e:
338
- status_message = f"Submission Failed: Network error - {e}"
339
- print(status_message)
340
- results_df = pd.DataFrame(results_log)
341
- return status_message, results_df
342
- except Exception as e:
343
- status_message = f"An unexpected error occurred during submission: {e}"
344
- print(status_message)
345
- results_df = pd.DataFrame(results_log)
346
- return status_message, results_df
347
-
348
-
349
- # --- Build Gradio Interface using Blocks ---
350
- with gr.Blocks() as demo:
351
- gr.Markdown("# Basic Agent Evaluation Runner")
352
- gr.Markdown(
353
- """
354
- **Instructions:**
355
-
356
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
357
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
358
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
359
-
360
- ---
361
- **Disclaimers:**
362
- 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).
363
- 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.
364
- """
365
- )
366
-
367
- gr.LoginButton()
368
-
369
- run_button = gr.Button("Run Evaluation & Submit All Answers")
370
-
371
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
372
- # Removed max_rows=10 from DataFrame constructor
373
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
374
-
375
- run_button.click(
376
- fn=run_and_submit_all,
377
- outputs=[status_output, results_table]
378
- )
379
-
380
- if __name__ == "__main__":
381
- print("\n" + "-"*30 + " App Starting " + "-"*30)
382
- # Check for SPACE_HOST and SPACE_ID at startup for information
383
- space_host_startup = os.getenv("SPACE_HOST")
384
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
385
-
386
- if space_host_startup:
387
- print(f"✅ SPACE_HOST found: {space_host_startup}")
388
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
389
- else:
390
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
391
-
392
- if space_id_startup: # Print repo URLs if SPACE_ID is found
393
- print(f"✅ SPACE_ID found: {space_id_startup}")
394
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
395
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
396
- else:
397
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
398
-
399
- print("-"*(60 + len(" App Starting ")) + "\n")
400
-
401
- print("Launching Gradio Interface for Basic Agent Evaluation...")
402
- demo.launch(debug=True, share=False)