loteriof commited on
Commit
c607294
·
verified ·
1 Parent(s): 81917a3

Create app.py

Browse files

Create the agent

Files changed (1) hide show
  1. app.py +140 -194
app.py CHANGED
@@ -1,196 +1,142 @@
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
+ from transformers import pipeline
2
+ import operator
3
+ from langgraph.graph import StateGraph, START, END
4
+ from typing import Annotated
5
+ from typing_extensions import TypedDict, Optional
6
+ from langgraph.types import Send
7
+ from pydantic import BaseModel
8
+
9
+ from langchain_community.utilities import WikipediaAPIWrapper
10
+ from langchain_community.tools import WikipediaQueryRun
11
+ from langchain_experimental.utilities import PythonREPL
12
+
13
+ llm = pipeline("text-generation", model="distilgpt2")
14
+
15
+ class SmallerQuestions(BaseModel):
16
+ questions = list[str]
17
+
18
+ class Question(BaseModel):
19
+ question: str
20
+ nature: str
21
+ context: str
22
+ middle_answer: str
23
+
24
+ class Nature(BaseModel):
25
+ nature: Literal["research", "computation", "interpretation"]
26
+
27
+ class SearchableQuestion(BaseModel):
28
+ question: str
29
+ key_words: list[str]
30
+ context: str
31
+
32
+ class Code(BaseModel):
33
+ code: str
34
+
35
+ class OverallState(TypedDict):
36
+ first_question: str
37
+ smaller_questions: list[str]
38
+ middle_answer: Annotated[list, operator.add]
39
+ answer: str
40
+
41
+ #Nodes
42
+
43
+ def BreakQuestion(state: OverallState):
44
+ prompt = """Task:
45
+ Break the following question down into a maximum of 7 smaller questions.
46
+ Your response must be a list in which each item corresponds to one smaller question.
47
+ Question:
48
+ """ + state["first_question"]
49
+ response = llm.with_structured_output(SmallerQuestions).invoke(prompt)
50
+ return {smaller_questions: response.questions}
51
+
52
+ def DecideNature(state: Question):
53
+ prompt = """Task:
54
+ The following question is answearable via one of these three approaches: "computation", "reasearch" or "interpretation".
55
+ Computation is a question that requires a python interpreter; research is a question that requires a web search in sites like wikipedia; interpretation is a question that needs only reasoning to be answered.
56
+ Decide which of those is the best approach and return only the string corresponding to the one you choose, and nothing else.
57
+ Question:
58
+ """ + state["question"]
59
+ response = llm.with_structured_output(Nature).invoke(prompt)
60
+ return {nature: response.nature}
61
+
62
+ def KeyWords(state: SearchableQuestion):
63
+ prompt = """Task:
64
+ Gather the key words from the following question to perform a search.
65
+ Question:
66
+ """ + state["question"]
67
+ response = llm.with_structured_output(SearchableQuestion).invoke(prompt)
68
+ return {key_words: response.key_words}
69
+
70
+ def WikiSearch(state: SearchableQuestion):
71
+ wikipedia_api = WikipediaAPIWrapper(top_k_results=1,doc_content_chars_max=1000)
72
+ wikipedia = WikipediaQueryRun(api_wrapper=wikipedia_api)
73
+ result = wikipedia.run({query: state["key_words"]})
74
+ return {context: result}
75
+
76
+ def ContextAnswer(state: SearchableQuestion):
77
+ prompt = """Task:
78
+ I will give you a context and a question, and I wnat you to answer the question based on the given context and nothing else.
79
+ Context:
80
+ """ + state["context"] + "Question: " + state["question"]
81
+ response = llm.invoke(prompt)
82
+ return {middle_answer: [response]}
83
+
84
+ def Computation(state: Question):
85
+ python_repl = PythonREPL()
86
+ prompt = """Task:
87
+ The following question requires a computation to be answered. Create a code that solves the problem.
88
+ Your response must be only the code that solves the problem and nothing else.
89
+ Question:
90
+ """ + state["question"]
91
+ response = llm.with_structured_output(Code).invoke(prompt)
92
+ code_result = python_repl.run(response.code)
93
+ structured_answer = "The result of the following problem is " + code_result + "Problem: " + state["question"]
94
+ return {middle_answer: [structured_answer]}
95
+
96
+ def Reasoning(state: Question):
97
+ prompt = """Task:
98
+ Answer the following question using your reasoning. Your response must be contain only the answer to the question and nothing else.
99
+ Question:
100
+ """ + state["question"])
101
+ response = llm.invoke(prompt)
102
+ return {middle_answer: [response]}
103
+
104
+ def FinalAnswer(state: OverallState):
105
+ middle_answers = "\n\n".join(state["middle_answer"])
106
+ prompt = """Task:
107
+ You will receive a question and some information from which you must be able to solve the question completely.
108
+ Do your best to keep your response as close to the given information as possible. Your response should contain only the final answer to the question and nothing else.
109
+ Question:
110
+ """ + state["question"] + "Context: " + middle_answers
111
+ response = llm.invoke(prompt)
112
+ return {answer: response}
113
+
114
+ #Edges
115
+
116
+ def Nature(state: Question):
117
+ if state["nature"] == "computation":
118
+ return Send("computation", state)
119
+ elif state["nature"] == "research":
120
+ return Send("key_words", state)
121
  else:
122
+ return Send("interpretation", state)
123
+
124
+ builder = StateGraph(State)
125
+ builder.add_node("break_question", BreakQuestion)
126
+ builder.add_node("key_words", KeyWords)
127
+ builder.add_node("search", WikiSearch)
128
+ builder.add_node("answer", ContextAnswer)
129
+ builder.add_node("computation", Computation)
130
+ builder.add_node("interpretation", Reasoning)
131
+ builder.add_node("final_answer", FinalAnswer)
132
+ builder.add_edge(START, "break_question")
133
+ builder.add_conditional_edges("define_nature", Nature)
134
+ builder.add_edge("key_words", "search")
135
+ builder.add_edge("search", "answer")
136
+ builder.add_edge("answer", "final_answer")
137
+ builder.add_edge("computation", "final_answer")
138
+ builder.add_edge("interpretation", "final_answer")
139
+ builder.add_edge("final_answer", END)
140
+
141
+ graph = builder.compile()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142