ashishja commited on
Commit
95f4e51
·
verified ·
1 Parent(s): f4eeecb

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +144 -240
agent.py CHANGED
@@ -1,243 +1,147 @@
1
- from zoneinfo import ZoneInfo
2
- from google.adk.agents import Agent,BaseAgent,LlmAgent
3
- from google.adk.tools import google_search
4
- from google.adk.runners import Runner
5
- from google.adk.sessions import InMemorySessionService
6
- from google.genai import types
7
- import google.genai.types as types
8
- import requests
9
- from google.adk.events import Event, EventActions
10
- from google.adk.agents.invocation_context import InvocationContext
11
- from typing import AsyncGenerator
12
- from google.genai import types as genai_types
13
- from google.adk.tools import ToolContext, FunctionTool
14
- import logging
15
- #from google.adk.tools import built_in_code_execution
16
- from google.adk.tools import agent_tool
17
-
18
- logging.basicConfig(level=logging.ERROR)
19
- #from google.adk.tools import agent_tool
20
- url = 'https://agents-course-unit4-scoring.hf.space/questions'
21
- headers = {'accept': 'application/json'}
22
- response = requests.get(url, headers=headers)
23
-
24
- # class responses_api(BaseAgent):
25
- # async def _run_async_impl(self, ctx: InvocationContext)-> AsyncGenerator[Event, None]:
26
- # # This method is called when the agent is run
27
- # # You can implement your logic here
28
- # # For example, you can call an external API or perform some calculations
29
- # # and return the result
30
- # url = 'https://agents-course-unit4-scoring.hf.space/questions'
31
- # headers = {'accept': 'application/json'}
32
- # response = requests.get(url, headers=headers)
33
- # for i in response.json():
34
- # if i['file_name'] != '':
35
- # url_file = f"https://agents-course-unit4-scoring.hf.space/files/{i['task_id']}"
36
- # question = i['question']
37
- # prompt = f"{question} and the file is {url_file}, give the final answer only"
38
- # else:
39
- # question = i['question']
40
- # prompt = f"{question} give the final answer only"
41
- # existing_responses = ctx.session.state.get("user:responses", [])
42
- # existing_responses.append(prompt)
43
- # ctx.session_state["user:responses"] = existing_responses
44
-
45
- # # Optionally, yield a single event to indicate completion or provide some output
46
- # yield Event(author=self.name, content=types.Content(parts=[types.Part(text=f"Fetched {len(questions_data)} questions."))])
47
-
48
-
49
-
50
- def answer_questions():
51
- """Fetch questions from the GAIA API and return them in a structured format"""
52
- url = 'https://agents-course-unit4-scoring.hf.space/questions'
53
- headers = {'accept': 'application/json'}
54
- response = requests.get(url, headers=headers)
55
-
56
- if response.status_code != 200:
57
- return f"Error fetching questions: {response.status_code}"
58
-
59
- questions_data = response.json()
60
- return questions_data
61
- #responses_api = responses_api(name= 'responses_api_1')
62
- from typing import Dict, Any
63
- def submit_questions(answers: list[str]) -> Dict[str, Any]:
64
- url = 'https://agents-course-unit4-scoring.hf.space/submit'
65
- payload = {
66
- "username": "ashishja",
67
- "agent_code": "https://huggingface.co/spaces/ashishja/Agents_Course_Final_Assignment_Ashish/tree/main",
68
- "answers": answers}
69
- headers = {'accept': 'application/json', "Content-Type": "application/json"}
70
- response = requests.post(url, headers=headers, json =payload)
71
- import json
72
- print(json.dumps(payload, indent=2))
73
- if response.status_code == 200:
74
- return response.json()
75
- else:
76
- response.raise_for_status()
77
-
78
-
79
-
80
-
81
- responses_api = FunctionTool(func= answer_questions)
82
- submit_api = FunctionTool(func=submit_questions)
83
-
84
- # class QuestionAnswerer(LlmAgent):
85
- # async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
86
- # questions_to_answer = ctx.session_service.get('fetched_questions', [])
87
- # for q in questions_to_answer:
88
- # answer = await self._llm(messages=[types.ChatMessage(role="user", parts=[types.Part(text=q)])])
89
- # yield Event(author=self.name, content=answer.content)
90
-
91
- # qa = QuestionAnswerer(name = 'qa_1', model="gemini-2.0-flash", description="Question Answerer")
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
- APP_NAME="weather_sentiment_agent"
101
- USER_ID="user1234"
102
- SESSION_ID="1234"
103
-
104
-
105
- code_agent = LlmAgent(
106
- name='codegaiaAgent',
107
- model="gemini-2.5-pro-preview-05-06",
108
- description=(
109
- "You are a smart agent that can write and execute code and answer any questions provided access the given files and answer"
110
- ),
111
- instruction = (
112
- "if the question contains a file with .py ,Get the code file and depending on the question and the file provided, execute the code and provide the final answer. "
113
- "If the question contains a spreadsheet file like .xlsx and .csv among others, get the file and depending on the question and the file provided, execute the code and provide the final answer. "
114
- "use code like import pandas as pd , file = pd.read_csv('file.csv') and then use the file to answer the question. "
115
- "if the question contains a file with .txt ,Get the code file and depending on the question and the file provided, execute the code and provide the final answer. "
116
- "if the question contains a file with .json ,Get the code file and depending on the question and the file provided, execute the code and provide the final answer. "
117
- "If you are writing code or if you get a code file, use the code execution tool to run the code and provide the final answer. "
118
- )
119
-
120
- ,
121
- # tools=[built_in_code_execution],
122
- # Add the responses_api agent as a tool
123
- #sub_agents=[responses_api]
124
- )
125
-
126
-
127
- search_agent = LlmAgent(
128
- name='searchgaiaAgent',
129
- model="gemini-2.5-pro-preview-05-06",
130
- description=(
131
- "You are a smart agent that can search the web and answer any questions provided access the given files and answer"
132
- ),
133
- instruction = (
134
- "Get the url associated perform a search and consolidate the information provided and answer the provided question "
135
- )
136
-
137
- ,
138
- tools=[google_search],
139
- # Add the responses_api agent as a tool
140
- #sub_agents=[responses_api]
141
- )
142
-
143
- image_agent = LlmAgent(
144
- name='imagegaiaAgent',
145
- model="gemini-2.5-pro-preview-05-06",
146
- description=(
147
- "You are a smart agent that can when given a image file and answer any questions related to it"
148
- ),
149
- instruction = (
150
- "Get the image file from the link associated in the prompt use Gemini to watch the video and answer the provided question ")
151
-
152
- ,
153
- # tools=[google_search],
154
- # Add the responses_api agent as a tool
155
- #sub_agents=[responses_api]
156
- )
157
-
158
-
159
- youtube_agent = LlmAgent(
160
- name='youtubegaiaAgent',
161
- model="gemini-2.5-pro-preview-05-06",
162
- description=(
163
- "You are a smart agent that can when given a youtube link watch it and answer any questions related to it"
164
- ),
165
- instruction = (
166
- "Get the youtube link associated use Gemini to watch the video and answer the provided question ")
167
-
168
- ,
169
- # tools=[google_search],
170
- # Add the responses_api agent as a tool
171
- #sub_agents=[responses_api]
172
- )
173
-
174
- root_agent = LlmAgent(
175
- name='basegaiaAgent',
176
- model="gemini-2.5-pro-preview-05-06",
177
- description=(
178
- "You are a smart agent that can answer any questions provided access the given files and answer"
179
- ),
180
- instruction = (
181
- "You are a helpful agent. When the user asks to get the questions or makes a similar request, "
182
- "invoke your tool 'responses_api' to retrieve the questions data. "
183
- "The questions data will be a list of dictionaries, each containing 'task_id', 'question', and 'file_name' fields. "
184
- "For each question in the data: "
185
- "1. If file_name is not empty, the file can be accessed at https://agents-course-unit4-scoring.hf.space/files/[TASK_ID] "
186
- "2. Use appropriate sub-agents based on question type (code_agent for coding, search_agent for web search, etc.) "
187
- "3. Provide a concise, direct answer for each question "
188
- "4. Return a dictionary with keys 'task_id' and 'submitted_answer' for each answer "
189
- "5. Collect all dictionaries in a list and pass to 'submit_api' tool to submit the answers. "
190
- "Always provide direct, factual answers without prefixes like 'The answer is:' or 'Final answer:'"
191
- )
192
-
193
- ,
194
- tools=[responses_api,submit_api,agent_tool.AgentTool(agent = code_agent),\
195
- agent_tool.AgentTool(agent = search_agent), agent_tool.AgentTool(youtube_agent), agent_tool.AgentTool(image_agent)],
196
- # Add the responses_api agent as a tool
197
- #sub_agents=[responses_api]
198
- )
199
-
200
- # root_agent = LlmAgent(
201
- # name='gaiaAgent',
202
- # model="gemini-2.5-pro-preview-05-06",
203
- # description=(
204
- # "You are a smart agent that can answer any questions provided access the given files and answer"
205
- # ),
206
- # instruction = (
207
- # "You are a helpful agent. When the user asks to get the questions or makes a similar request, "
208
- # "invoke base agent. "
209
- # "Once you the answers check if are in correct format. "
210
- # #"Collect all such dictionaries in a list (do not include any backslashes), and pass this list to the 'submit_api' tool to submit the answers."
211
- # )
212
-
213
- # ,
214
- # #tools=[submit_api],
215
- # # Add the responses_api agent as a tool
216
- # sub_agents=[base_agent]
217
- # )
218
-
219
- session_service = InMemorySessionService()
220
-
221
- # Create the default session synchronously (create_session is not async)
222
- try:
223
- session = session_service.create_session(
224
- app_name=APP_NAME,
225
- user_id=USER_ID,
226
- session_id=SESSION_ID
227
  )
228
- print(f"✅ Default session created: {SESSION_ID}")
229
- except Exception as e:
230
- print(f"⚠️ Error creating default session: {e}")
231
- session = None
232
-
233
- runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)
234
- # # def send_query_to_agent(root_agent, query, session):
235
- # # session = session
236
- # # content = types.Content(role='user', parts=[types.Part(text=query)])
237
 
238
- # # async def main():
239
- # # await process_questions_and_answer()
240
 
241
- # # if __name__ == "__main__":
242
- # # import asyncio
243
- # # asyncio.run(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TypedDict, Annotated, Optional
2
+ from langgraph.graph.message import add_messages
3
+ from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage, ToolMessage
4
+ from langgraph.prebuilt import ToolNode, tools_condition
5
+ from langgraph.graph import START, StateGraph, END
6
+ from langchain_openai import ChatOpenAI
7
+ from pydantic import SecretStr
8
+ import os
9
+ from dotenv import load_dotenv
10
+ from tools import download_file_from_url, basic_web_search, extract_url_content, wikipedia_reader, transcribe_audio_file, question_youtube_video
11
+
12
+ # Load environment variables from .env file
13
+ load_dotenv()
14
+
15
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
16
+ MAIN_LLM_MODEL = os.getenv("MAIN_LLM_MODEL", "google/gemini-2.0-flash-lite-001")
17
+
18
+ # Generate the chat interface, including the tools
19
+ if not OPENROUTER_API_KEY:
20
+ raise ValueError("OPENROUTER_API_KEY is not set. Please ensure it is defined in your .env file or environment variables.")
21
+
22
+
23
+ def create_agent_graph():
24
+
25
+ main_llm = ChatOpenAI(
26
+ model=MAIN_LLM_MODEL, # e.g., "mistralai/mistral-7b-instruct"
27
+ api_key=SecretStr(OPENROUTER_API_KEY), # Your OpenRouter API key
28
+ base_url="https://openrouter.ai/api/v1", # Standard OpenRouter API base
29
+ verbose=True # Optional: for debugging
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  )
 
 
 
 
 
 
 
 
 
31
 
 
 
32
 
33
+ tools = [download_file_from_url, basic_web_search, extract_url_content, wikipedia_reader, transcribe_audio_file, question_youtube_video] # Ensure these tools are defined
34
+ chat_with_tools = main_llm.bind_tools(tools)
35
+
36
+ class AgentState(TypedDict):
37
+ messages: Annotated[list[AnyMessage], add_messages]
38
+ file_url: Optional[str | None]
39
+ file_ext: Optional[str | None]
40
+ local_file_path: Optional[str | None]
41
+ final_answer: Optional[str | None]
42
+
43
+ def assistant(state: AgentState):
44
+ return {
45
+ "messages": [chat_with_tools.invoke(state["messages"])],
46
+ "file_url": state.get("file_url", None),
47
+ "file_ext": state.get("file_ext", None),
48
+ "local_file_path": state.get("local_file_path", None),
49
+ "final_answer": state.get("final_answer", None)
50
+ }
51
+
52
+ def file_path_updater_node(state: AgentState):
53
+ download_tool_response = state["messages"][-1].content
54
+ file_path = download_tool_response.split("Local File Path: ")[-1].strip()
55
+ return {
56
+ "local_file_path": file_path
57
+ }
58
+
59
+ def file_path_condition(state: AgentState) -> str:
60
+ if state["messages"] and isinstance(state["messages"][-1], ToolMessage):
61
+ tool_response = state["messages"][-1]
62
+ if tool_response.name == "download_file_from_url":
63
+ return "update_file_path" # Route to file path updater if a file was downloaded
64
+ return "assistant" # Otherwise, continue with the assistant node
65
+
66
+ def format_final_answer_node(state: AgentState) -> AgentState:
67
+ """
68
+ Formats the final answer based on the state.
69
+ This node is reached when the assistant has completed its task.
70
+ """
71
+ final_answer = state["messages"][-1].content if state["messages"] else None
72
+ if final_answer:
73
+ state["final_answer"] = final_answer.split("FINAL ANSWER:")[-1].strip() #if FINAL_ANSWER isn't present we grab the whole string
74
+ return state
75
+
76
+
77
+ # The graph
78
+ builder = StateGraph(AgentState)
79
+
80
+ builder.add_node("assistant", assistant)
81
+ builder.add_edge(START, "assistant")
82
+ builder.add_node("tools", ToolNode(tools))
83
+ builder.add_node("file_path_updater_node", file_path_updater_node)
84
+ builder.add_node("format_final_answer_node", format_final_answer_node)
85
+
86
+ builder.add_conditional_edges(
87
+ "assistant",
88
+ tools_condition,
89
+ {
90
+ "tools": "tools",
91
+ "__end__": "format_final_answer_node" # This is the end node for the assistant
92
+ }
93
+ )
94
+ builder.add_conditional_edges(
95
+ "tools",
96
+ file_path_condition,
97
+ {
98
+ "update_file_path": "file_path_updater_node",
99
+ "assistant": "assistant"
100
+ }
101
+ )
102
+
103
+ builder.add_edge("file_path_updater_node", "assistant")
104
+ builder.add_edge("format_final_answer_node", END)
105
+ graph = builder.compile()
106
+ return graph
107
+
108
+ class BasicAgent:
109
+ """
110
+ A basic agent that can answer questions and download files.
111
+ Requires a system message be defined in 'system_prompt.txt'.
112
+ """
113
+ def __init__(self, graph=None):
114
+
115
+ with open("system_prompt.txt", "r", encoding="utf-8") as f:
116
+ self.system_message = SystemMessage(content=f.read())
117
+
118
+ if graph is None:
119
+ self.graph = create_agent_graph()
120
+ else:
121
+ self.graph = graph
122
+
123
+ def __call__(self, question: str, file_url: Optional[str] = None, file_ext: Optional[str] = None) -> str:
124
+ """
125
+ Call the agent with a question and optional file URL and extension.
126
+
127
+ Args:
128
+ question (str): The user's question.
129
+ file_url (Optional[str]): The URL of the file to download.
130
+ file_ext (Optional[str]): The file extension for the downloaded file.
131
+
132
+ Returns:
133
+ str: The agent's response.
134
+ """
135
+ if file_url and file_ext:
136
+ question += f"\nREFERENCE FILE MUST BE RETRIEVED\nFile URL: {file_url}, File Extension: {file_ext}\nUSE A TOOL TO DOWNLOAD THIS FILE."
137
+ state = {
138
+ "messages": [self.system_message, HumanMessage(content=question)],
139
+ "file_url": file_url,
140
+ "file_ext": file_ext,
141
+ "local_file_path": None,
142
+ "final_answer": None
143
+ }
144
+ response = self.graph.invoke(state)
145
+ for m in response["messages"]:
146
+ m.pretty_print()
147
+ return response["final_answer"] if response["final_answer"] else "No final answer generated."