ksj47 commited on
Commit
d383472
·
verified ·
1 Parent(s): fbede70

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -38
app.py CHANGED
@@ -11,42 +11,82 @@ from langchain_community.llms import HuggingFaceHub
11
  # load_dotenv()
12
 
13
  # --- Constants ---
14
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
 
 
15
 
16
  # --- Basic Agent Definition --
17
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
18
  class BasicAgent:
19
- def __init__(self, hf_api_token: str | None = None):
20
- print("BasicAgent initializing...")
21
- token_to_use = hf_api_token or os.getenv("HUGGINGFACEHUB_API_TOKEN") or os.getenv("HF_TOKEN")
 
 
 
 
22
 
23
  if not token_to_use:
24
  raise ValueError(
25
- "Hugging Face API token not found. Please set HUGGINGFACEHUB_API_TOKEN or HF_TOKEN "
26
- "as a secret in your Hugging Face Space. This token is required for the LLM."
27
  )
28
 
29
- self.llm_repo_id = "mistralai/Mistral-7B-Instruct-v0.1" # Or your preferred model
30
  try:
31
- self.llm = HuggingFaceHub(
32
- repo_id=self.llm_repo_id,
33
- # Increased max_new_tokens as the ReAct prompt is long and might generate a longer thought process
34
- # Temperature 0.0 for more deterministic ReAct output, 0.1 is also fine.
35
- model_kwargs={"temperature": 0.1, "max_new_tokens": 512},
36
- huggingfacehub_api_token=token_to_use
 
 
 
 
 
 
 
 
 
 
 
 
37
  )
38
- print(f"BasicAgent initialized with LLM: {self.llm_repo_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  except Exception as e:
40
- print(f"Error initializing HuggingFaceHub: {e}")
41
- raise ValueError(f"Failed to initialize LLM: {e}. Check token and model repo_id.")
42
 
43
  # Modified signature to accept task_id (though not used in this simple version yet)
44
  def __call__(self, question: str, task_id: str | None = None) -> str:
45
- print(f"Agent received question (Task ID: {task_id}, first 80 chars): {question[:80]}...")
46
 
47
- # Prompt engineering is crucial.
48
- # The `question` variable (method argument) is now correctly inserted here.
49
- # This is a single-shot prompt. A true ReAct agent would have a loop.
50
  current_prompt = f"""You are a diligent and highly intelligent AI assistant. Your goal is to answer the given `Question` accurately and concisely.
51
  If the question requires multiple steps or information from tools, think step-by-step.
52
 
@@ -69,44 +109,181 @@ Example: If asked "What is 2+2?", your output should be "4".
69
  Now, please answer the following question:
70
  Question: {question}
71
 
72
- Answer:""" # Modified to guide the LLM towards a direct answer for this simplified agent
73
 
74
  try:
75
- print(f"Sending to LLM (first 200 chars of prompt): {current_prompt[:200]}...")
76
- response_text = self.llm.invoke(current_prompt)
77
- answer = response_text.strip()
78
 
79
- # Further cleaning if the model still adds prefixes or explanations
80
- # This is important because we are not doing a full ReAct loop to extract "Final Answer:"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
- # Try to find "Answer:" if the LLM adds it despite instructions
83
  if "Answer:" in answer:
84
- # Take text after the last occurrence of "Answer:"
85
  answer = answer.split("Answer:")[-1].strip()
86
 
87
- # Remove common conversational prefixes that might slip through
88
  common_prefixes_to_remove = [
89
  "The answer is", "My answer is", "Based on the information", "The final answer is",
90
  "Here is the answer", "I found that", "It seems that"
91
- ] # Case-insensitive removal
92
  for prefix in common_prefixes_to_remove:
93
  if answer.lower().startswith(prefix.lower()):
94
  answer = answer[len(prefix):].strip()
95
- # If the first character is now a colon or period, remove it
96
  if answer.startswith(":") or answer.startswith("."):
97
  answer = answer[1:].strip()
98
- break # Only remove one such prefix
99
-
100
- # If the LLM generated a ReAct-style "Final Answer:", extract from it.
101
  if "Final Answer:" in answer:
102
  answer = answer.split("Final Answer:")[-1].strip()
103
 
104
- print(f"Agent LLM raw response (first 80 chars): {response_text[:80]}...")
105
- print(f"Agent cleaned answer (first 80 chars): {answer[:80]}...")
106
 
107
  if not answer:
108
- print("Warning: Agent produced an empty answer after cleaning.")
109
- return "Unable to generate a valid answer."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  return answer
112
  except Exception as e:
 
11
  # load_dotenv()
12
 
13
  # --- Constants ---
14
+
15
+ # TO:
16
+ import google.generativeai as genai # For Gemini
17
+
18
+ # ... (rest of your existing imports and constants)
19
+
20
+ # --- Constants ---
21
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" # This remains the same
22
 
23
  # --- Basic Agent Definition --
24
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
25
  class BasicAgent:
26
+ def __init__(self, google_api_key: str | None = None): # Changed parameter name for clarity
27
+ print("BasicAgent initializing with Google Gemini...")
28
+
29
+ # Determine the Google API token
30
+ token_to_use = google_api_key
31
+ if not token_to_use:
32
+ token_to_use = os.getenv("GOOGLE_API_KEY") # Standard environment variable for Google API keys
33
 
34
  if not token_to_use:
35
  raise ValueError(
36
+ "Google API key not found. Please set GOOGLE_API_KEY "
37
+ "as a secret in your Hugging Face Space. This token is required for Gemini."
38
  )
39
 
 
40
  try:
41
+ # Configure the Gemini client
42
+ genai.configure(api_key=token_to_use)
43
+
44
+ # Specify the Gemini model you want to use.
45
+ # As of my last update, "gemini-1.5-pro-latest" is a good choice for high capability.
46
+ # "gemini-pro" is also a valid and generally available option.
47
+ # "gemini-2.5-pro" is not a known generally available model name as of now.
48
+ # Please use a valid model name from: https://ai.google.dev/models/gemini
49
+ self.model_name = "gemini-1.5-pro-latest" # Or "gemini-pro"
50
+
51
+ # Create the GenerativeModel instance
52
+ self.llm = genai.GenerativeModel(self.model_name)
53
+
54
+ # Define generation configuration (optional, but good for consistency)
55
+ self.generation_config = genai.types.GenerationConfig(
56
+ temperature=0.1, # For more deterministic output, good for agents
57
+ # max_output_tokens=512 # Equivalent to max_new_tokens
58
+ # You can set other parameters here like top_p, top_k
59
  )
60
+ # Define safety settings (important for Gemini to avoid overly restrictive blocking)
61
+ # Adjust these based on your needs and if you encounter content blocking.
62
+ self.safety_settings = [
63
+ {
64
+ "category": "HARM_CATEGORY_HARASSMENT",
65
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE" # Or BLOCK_ONLY_HIGH, BLOCK_NONE
66
+ },
67
+ {
68
+ "category": "HARM_CATEGORY_HATE_SPEECH",
69
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
70
+ },
71
+ {
72
+ "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
73
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
74
+ },
75
+ {
76
+ "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
77
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
78
+ }
79
+ ]
80
+
81
+ print(f"BasicAgent initialized with Google Gemini model: {self.model_name}")
82
  except Exception as e:
83
+ print(f"Error initializing Google Gemini client: {e}")
84
+ raise ValueError(f"Failed to initialize Gemini: {e}. Check API key and model name.")
85
 
86
  # Modified signature to accept task_id (though not used in this simple version yet)
87
  def __call__(self, question: str, task_id: str | None = None) -> str:
88
+ print(f"Agent (Gemini) received question (Task ID: {task_id}, first 80 chars): {question[:80]}...")
89
 
 
 
 
90
  current_prompt = f"""You are a diligent and highly intelligent AI assistant. Your goal is to answer the given `Question` accurately and concisely.
91
  If the question requires multiple steps or information from tools, think step-by-step.
92
 
 
109
  Now, please answer the following question:
110
  Question: {question}
111
 
112
+ Answer:"""
113
 
114
  try:
115
+ print(f"Sending to Gemini (first 200 chars of prompt): {current_prompt[:200]}...")
 
 
116
 
117
+ # FROM: response_text = self.llm.invoke(current_prompt)
118
+ # TO: Use Gemini's generate_content method
119
+ response = self.llm.generate_content(
120
+ current_prompt,
121
+ generation_config=self.generation_config,
122
+ safety_settings=self.safety_settings
123
+ )
124
+
125
+ # Extract the text from the Gemini response object
126
+ # Need to handle potential errors or blocked content
127
+ if response.candidates:
128
+ if response.candidates[0].content.parts:
129
+ response_text = response.candidates[0].content.parts[0].text
130
+ else: # Content might be empty if blocked or other issues
131
+ response_text = ""
132
+ print("Warning: Gemini response has no content parts.")
133
+ if response.prompt_feedback and response.prompt_feedback.block_reason:
134
+ print(f"Prompt blocked by Gemini. Reason: {response.prompt_feedback.block_reason_message or response.prompt_feedback.block_reason}")
135
+ return f"AGENT_ERROR: Prompt blocked by Gemini ({response.prompt_feedback.block_reason})."
136
+ else: # No candidates means something went wrong, possibly blocking
137
+ response_text = ""
138
+ print("Warning: Gemini response has no candidates.")
139
+ if response.prompt_feedback and response.prompt_feedback.block_reason:
140
+ print(f"Prompt blocked by Gemini. Reason: {response.prompt_feedback.block_reason_message or response.prompt_feedback.block_reason}")
141
+ return f"AGENT_ERROR: Prompt blocked by Gemini ({response.prompt_feedback.block_reason})."
142
+ return "AGENT_ERROR: Gemini returned no candidates in response."
143
+
144
+
145
+ answer = response_text.strip()
146
 
147
+ # (Your existing cleaning logic can largely remain the same)
148
  if "Answer:" in answer:
 
149
  answer = answer.split("Answer:")[-1].strip()
150
 
 
151
  common_prefixes_to_remove = [
152
  "The answer is", "My answer is", "Based on the information", "The final answer is",
153
  "Here is the answer", "I found that", "It seems that"
154
+ ]
155
  for prefix in common_prefixes_to_remove:
156
  if answer.lower().startswith(prefix.lower()):
157
  answer = answer[len(prefix):].strip()
 
158
  if answer.startswith(":") or answer.startswith("."):
159
  answer = answer[1:].strip()
160
+ break
 
 
161
  if "Final Answer:" in answer:
162
  answer = answer.split("Final Answer:")[-1].strip()
163
 
164
+ print(f"Agent (Gemini) LLM raw response (first 80 chars): {response_text[:80]}...")
165
+ print(f"Agent (Gemini) cleaned answer (first 80 chars): {answer[:80]}...")
166
 
167
  if not answer:
168
+ print("Warning: Agent (Gemini) produced an empty answer after cleaning.")
169
+ return "Unable to generate a valid answer from Gemini."
170
+
171
+ return answer
172
+ except Exception as e:
173
+ # Check if it's a specific Google API error for more details
174
+ if hasattr(e, 'message'): # google.api_core.exceptions often have a message attribute
175
+ error_message = e.message
176
+ else:
177
+ error_message = str(e)
178
+ print(f"Error during Gemini LLM call for question '{question[:50]}...': {error_message}")
179
+ return f"AGENT_ERROR: Gemini LLM call failed. ({type(e).__name__}: {error_message})"
180
+
181
+ # ... (The rest of your `run_and_submit_all` function and Gradio UI code remains unchanged) ...
182
+
183
+ # At the end of the file, in the __main__ block, update the warning message:
184
+ # if __name__ == "__main__":
185
+ # ... (existing startup prints) ...
186
+
187
+ # TO: Check for GOOGLE_API_KEY
188
+ if not os.getenv("GOOGLE_API_KEY"):
189
+ print("⚠️ WARNING: GOOGLE_API_KEY environment variable not found.")
190
+ print(" The Gemini agent will likely fail to initialize. Please set this token in your Space secrets.")
191
+
192
+ # ... (rest of existing startup prints and demo.launch()) ...
193
+ # --- Basic Agent Definition --
194
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
195
+ # class BasicAgent:
196
+ # def __init__(self, hf_api_token: str | None = None):
197
+ # print("BasicAgent initializing...")
198
+ # token_to_use = hf_api_token or os.getenv("HUGGINGFACEHUB_API_TOKEN") or os.getenv("HF_TOKEN")
199
+
200
+ # if not token_to_use:
201
+ # raise ValueError(
202
+ # "Hugging Face API token not found. Please set HUGGINGFACEHUB_API_TOKEN or HF_TOKEN "
203
+ # "as a secret in your Hugging Face Space. This token is required for the LLM."
204
+ # )
205
+
206
+ # self.llm_repo_id = "mistralai/Mistral-7B-Instruct-v0.1" # Or your preferred model
207
+ # try:
208
+ # self.llm = HuggingFaceHub(
209
+ # repo_id=self.llm_repo_id,
210
+ # # Increased max_new_tokens as the ReAct prompt is long and might generate a longer thought process
211
+ # # Temperature 0.0 for more deterministic ReAct output, 0.1 is also fine.
212
+ # model_kwargs={"temperature": 0.1, "max_new_tokens": 512},
213
+ # huggingfacehub_api_token=token_to_use
214
+ # )
215
+ # print(f"BasicAgent initialized with LLM: {self.llm_repo_id}")
216
+ # except Exception as e:
217
+ # print(f"Error initializing HuggingFaceHub: {e}")
218
+ # raise ValueError(f"Failed to initialize LLM: {e}. Check token and model repo_id.")
219
+
220
+ # # Modified signature to accept task_id (though not used in this simple version yet)
221
+ # def __call__(self, question: str, task_id: str | None = None) -> str:
222
+ # print(f"Agent received question (Task ID: {task_id}, first 80 chars): {question[:80]}...")
223
+
224
+ # # Prompt engineering is crucial.
225
+ # # The `question` variable (method argument) is now correctly inserted here.
226
+ # # This is a single-shot prompt. A true ReAct agent would have a loop.
227
+ # current_prompt = f"""You are a diligent and highly intelligent AI assistant. Your goal is to answer the given `Question` accurately and concisely.
228
+ # If the question requires multiple steps or information from tools, think step-by-step.
229
+
230
+ # **Available Tools (Conceptual - for your reasoning process, actual tool calls are not implemented in this version):**
231
+
232
+ # 1. **`GAIAFileLookup(filename: str) -> str`**: Retrieves file content.
233
+ # 2. **`Calculator(expression: str) -> str`**: Performs calculations.
234
+ # 3. **`LLM_Query(sub_question: str) -> str`**: For general knowledge.
235
+
236
+ # **Output Format Expectation:**
237
+ # While you might reason using a "Thought:", "Action:", "Observation:" cycle internally, for this specific task, your final output should be ONLY the direct answer to the question.
238
+ # Example: If asked "What is 2+2?", your output should be "4".
239
+
240
+ # **Key Guidelines for GAIA Submission:**
241
+ # 1. **Conciseness:** The final answer must be precise and directly address the question.
242
+ # 2. **No "FINAL ANSWER" Prefix in Submission:** Do NOT include "FINAL ANSWER:" or "The answer is:" in your actual response. Just the answer value.
243
+
244
+ # ---
245
+
246
+ # Now, please answer the following question:
247
+ # Question: {question}
248
+
249
+ # Answer:""" # Modified to guide the LLM towards a direct answer for this simplified agent
250
+
251
+ # try:
252
+ # print(f"Sending to LLM (first 200 chars of prompt): {current_prompt[:200]}...")
253
+ # response_text = self.llm.invoke(current_prompt)
254
+ # answer = response_text.strip()
255
+
256
+ # # Further cleaning if the model still adds prefixes or explanations
257
+ # # This is important because we are not doing a full ReAct loop to extract "Final Answer:"
258
+
259
+ # # Try to find "Answer:" if the LLM adds it despite instructions
260
+ # if "Answer:" in answer:
261
+ # # Take text after the last occurrence of "Answer:"
262
+ # answer = answer.split("Answer:")[-1].strip()
263
+
264
+ # # Remove common conversational prefixes that might slip through
265
+ # common_prefixes_to_remove = [
266
+ # "The answer is", "My answer is", "Based on the information", "The final answer is",
267
+ # "Here is the answer", "I found that", "It seems that"
268
+ # ] # Case-insensitive removal
269
+ # for prefix in common_prefixes_to_remove:
270
+ # if answer.lower().startswith(prefix.lower()):
271
+ # answer = answer[len(prefix):].strip()
272
+ # # If the first character is now a colon or period, remove it
273
+ # if answer.startswith(":") or answer.startswith("."):
274
+ # answer = answer[1:].strip()
275
+ # break # Only remove one such prefix
276
+
277
+ # # If the LLM generated a ReAct-style "Final Answer:", extract from it.
278
+ # if "Final Answer:" in answer:
279
+ # answer = answer.split("Final Answer:")[-1].strip()
280
+
281
+ # print(f"Agent LLM raw response (first 80 chars): {response_text[:80]}...")
282
+ # print(f"Agent cleaned answer (first 80 chars): {answer[:80]}...")
283
+
284
+ # if not answer:
285
+ # print("Warning: Agent produced an empty answer after cleaning.")
286
+ # return "Unable to generate a valid answer."
287
 
288
  return answer
289
  except Exception as e: