hanshan1988 commited on
Commit
1e9e483
·
1 Parent(s): 963d8bd

separated out agent file

Browse files
Files changed (2) hide show
  1. agent.py +169 -0
  2. app.py +3 -140
agent.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textwrap import dedent
2
+ from typing import TypedDict, List, Dict, Any, Optional, Annotated
3
+ import os
4
+
5
+ # from langchain_openai import ChatOpenAI
6
+ # from langchain_huggingface.llms import HuggingFaceEndpoint
7
+ from langgraph.graph import StateGraph, START, END
8
+ from langgraph.prebuilt import ToolNode, tools_condition
9
+ from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
10
+ from langgraph.graph.message import add_messages
11
+ from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint
12
+
13
+ from langfuse.langchain import CallbackHandler
14
+ from langfuse import get_client
15
+
16
+ from tools import fetch_website, get_wiki_full, youtube_transcript, python_repl_tool, duckduckgo_search_results
17
+
18
+ os.environ["LANGFUSE_PUBLIC_KEY"] = os.getenv("LANGFUSE_PUBLIC_KEY", "pk-lf-***") # Public key is safe to expose in client-side code
19
+ os.environ["LANGFUSE_SECRET_KEY"] = os.getenv("LANGFUSE_SECRET_KEY", "sk-lf-***")
20
+ os.environ["LANGFUSE_BASE_URL"] = os.getenv("LANGFUSE_BASE_URL", "https://us.cloud.langfuse.com") # 🇺🇸 US region
21
+
22
+ langfuse = get_client()
23
+ # Verify connection
24
+ if langfuse.auth_check():
25
+ print("Langfuse client is authenticated and ready!")
26
+ else:
27
+ print("Authentication failed. Please check your credentials and host.")
28
+ langfuse_handler = CallbackHandler()
29
+
30
+ # Initialize the Hugging Face model
31
+ hf_model_name = "openai/gpt-oss-120b" # "Qwen/Qwen2.5-72B-Instruct"
32
+ hf_model_provider = "nscale" # "hf-inference"
33
+
34
+ llm = HuggingFaceEndpoint(
35
+ repo_id=hf_model_name,
36
+ provider=hf_model_provider,
37
+ max_new_tokens=8192,
38
+ do_sample=False,
39
+ # temperature=0.,
40
+ )
41
+
42
+ chat_model = ChatHuggingFace(llm=llm)
43
+
44
+ # Equip llm with tools
45
+ tools_list = [
46
+ fetch_website,
47
+ get_wiki_full,
48
+ youtube_transcript,
49
+ python_repl_tool,
50
+ duckduckgo_search_results
51
+ ]
52
+
53
+ llm_with_tools = chat_model.bind_tools(
54
+ tools_list
55
+ )
56
+
57
+ # Define Agent Workflow
58
+
59
+ class AgentState(TypedDict):
60
+ messages: Annotated[list[AnyMessage], add_messages]
61
+
62
+
63
+ def assistant(state: AgentState):
64
+ # System message
65
+ textual_description_of_tool = dedent(
66
+ """
67
+ duckduckgo_search_results(query: str) -> list[dict]:
68
+ Perform a web search using DuckDuckGo and return the results.
69
+ Args:
70
+ query: The search query string.
71
+ Returns:
72
+ A list of search results, where each result is a dictionary that includes the snippet, title, and link.
73
+
74
+ fetch_website(url: str) -> str:
75
+ Fetch the content of a website.
76
+ Args:
77
+ url: The URL of the website to fetch.
78
+ Returns:
79
+ The title and content of the website.
80
+
81
+ get_wiki_full(query: str) -> str:
82
+ Scrape the content of a Wikipedia page based on the user query.
83
+ Args:
84
+ query: The user query to search for on Wikipedia.
85
+ Returns:
86
+ A single string containing the content of the Wikipedia page.
87
+
88
+ youtube_transcript(url: str) -> str:
89
+ Fetch the transcript of a youtube video.
90
+ Args:
91
+ url: input youtube url.
92
+ Returns:
93
+ A single string containing the transcript of the youtube videos.
94
+
95
+ python_repl_tool(code: str) -> str:
96
+ Execute Python code and return the output.
97
+ Args:
98
+ code: A string of Python code to execute.
99
+ Returns:
100
+ The output of the executed code or any error messages.
101
+ """
102
+ )
103
+
104
+ sys_msg = SystemMessage(
105
+ content=dedent(
106
+ f"""
107
+ You are a helpful assistant at answering user questions. \
108
+ Your final answer will be between <answer> and </answer> tags. \
109
+ You can access provided tools:\n{textual_description_of_tool}\n"""
110
+ )
111
+ )
112
+
113
+ return {
114
+ "messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
115
+ }
116
+
117
+ # Build the StateGraph for the agent
118
+ # The graph
119
+ builder = StateGraph(AgentState)
120
+
121
+ # Define nodes: these do the work
122
+ builder.add_node("assistant", assistant)
123
+ builder.add_node("tools", ToolNode(tools_list))
124
+
125
+ # Define edges: these determine how the control flow moves
126
+ builder.add_edge(START, "assistant")
127
+ builder.add_conditional_edges(
128
+ "assistant",
129
+ # If the latest message requires a tool, route to tools
130
+ # Otherwise, provide a direct response
131
+ tools_condition,
132
+ )
133
+ builder.add_edge("tools", "assistant")
134
+ agent_graph = builder.compile()
135
+
136
+ def extract_answer(text):
137
+ match = re.search(r'<answer>(.*?)</answer>', text, re.DOTALL)
138
+ if match:
139
+ return match.group(1).strip()
140
+ return 'None'
141
+
142
+ class BasicAgent:
143
+ def __init__(self):
144
+ print("BasicAgent initialized.")
145
+ async def __call__(self, question: str) -> str:
146
+ print(f"Agent received question (first 100 chars): {question[:100]}...")
147
+ # fixed_answer = "This is a default answer."
148
+ # print(f"Agent returning fixed answer: {fixed_answer}")
149
+ # Create agent with all the tools
150
+
151
+ # Example query agent might receive
152
+ # fixed_answer = await agent.run(question)
153
+ messages = [
154
+ HumanMessage(
155
+ # content="Who is Barack Obama?"
156
+ # content="Divide 6790 by 5"
157
+ content=question # + '/nothink'
158
+ )
159
+ ]
160
+ response = await agent_graph.ainvoke(
161
+ {"messages": messages},
162
+ config={
163
+ "recursion_limit": 10,
164
+ "callbacks": [langfuse_handler],
165
+ }
166
+ )
167
+ response_text = response['messages'][-1].content
168
+ # return response_text.split('</think>')[-1]
169
+ return extract_answer(response_text)
app.py CHANGED
@@ -6,7 +6,7 @@ import requests
6
  import inspect
7
  import pandas as pd
8
  import re
9
- from textwrap import dedent
10
 
11
  # (Keep Constants as is)
12
  # --- Constants ---
@@ -15,146 +15,9 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
15
  # --- Basic Agent Definition ---
16
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
17
 
18
- from typing import TypedDict, List, Dict, Any, Optional, Annotated
19
- from langgraph.graph import StateGraph, START, END
20
- # from langchain_openai import ChatOpenAI
21
- # from langchain_huggingface.llms import HuggingFaceEndpoint
22
- from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
23
- from langgraph.graph.message import add_messages
24
- from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint
25
- from langgraph.prebuilt import ToolNode, tools_condition
26
-
27
- from tools import fetch_website, get_wiki_full, youtube_transcript, python_repl_tool, duckduckgo_search_results
28
-
29
- # Initialize the Hugging Face model
30
- hf_model_name = "openai/gpt-oss-120b" # "Qwen/Qwen2.5-72B-Instruct"
31
- hf_model_provider = "nscale" # "hf-inference"
32
-
33
- llm = HuggingFaceEndpoint(
34
- repo_id=hf_model_name,
35
- provider=hf_model_provider,
36
- max_new_tokens=8192,
37
- do_sample=False,
38
- # temperature=0.,
39
- )
40
-
41
- chat_model = ChatHuggingFace(llm=llm)
42
-
43
- # Equip llm with tools
44
- tools_list = [
45
- fetch_website,
46
- get_wiki_full,
47
- youtube_transcript,
48
- python_repl_tool,
49
- duckduckgo_search_results
50
- ]
51
-
52
- llm_with_tools = chat_model.bind_tools(
53
- tools_list
54
- )
55
-
56
- # Define Agent Workflow
57
-
58
- class AgentState(TypedDict):
59
- messages: Annotated[list[AnyMessage], add_messages]
60
-
61
-
62
- def assistant(state: AgentState):
63
- # System message
64
- textual_description_of_tool = dedent(
65
- """
66
- duckduckgo_search_results(query: str) -> list[dict]:
67
- Perform a web search using DuckDuckGo and return the results.
68
- Args:
69
- query: The search query string.
70
- Returns:
71
- A list of search results, where each result is a dictionary that includes the snippet, title, and link.
72
-
73
- fetch_website(url: str) -> str:
74
- Fetch the content of a website.
75
- Args:
76
- url: The URL of the website to fetch.
77
- Returns:
78
- The title and content of the website.
79
-
80
- get_wiki_full(query: str) -> str:
81
- Scrape the content of a Wikipedia page based on the user query.
82
- Args:
83
- query: The user query to search for on Wikipedia.
84
- Returns:
85
- A single string containing the content of the Wikipedia page.
86
-
87
- youtube_transcript(url: str) -> str:
88
- Fetch the transcript of a youtube video.
89
- Args:
90
- url: input youtube url.
91
- Returns:
92
- A single string containing the transcript of the youtube videos.
93
-
94
- python_repl_tool(code: str) -> str:
95
- Execute Python code and return the output.
96
- Args:
97
- code: A string of Python code to execute.
98
- Returns:
99
- The output of the executed code or any error messages.
100
- """
101
- )
102
-
103
- sys_msg = SystemMessage(
104
- content=f"You are a helpful assistant at answering user questions. Your final answer will be between <answer> and </answer> tags. You can access provided tools:\n{textual_description_of_tool}\n"
105
- )
106
-
107
- return {
108
- "messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
109
- }
110
-
111
- # Build the StateGraph for the agent
112
- # The graph
113
- builder = StateGraph(AgentState)
114
-
115
- # Define nodes: these do the work
116
- builder.add_node("assistant", assistant)
117
- builder.add_node("tools", ToolNode(tools_list))
118
-
119
- # Define edges: these determine how the control flow moves
120
- builder.add_edge(START, "assistant")
121
- builder.add_conditional_edges(
122
- "assistant",
123
- # If the latest message requires a tool, route to tools
124
- # Otherwise, provide a direct response
125
- tools_condition,
126
- )
127
- builder.add_edge("tools", "assistant")
128
- agent_graph = builder.compile()
129
-
130
- def extract_answer(text):
131
- match = re.search(r'<answer>(.*?)</answer>', text, re.DOTALL)
132
- if match:
133
- return match.group(1).strip()
134
- return 'None'
135
 
136
- class BasicAgent:
137
- def __init__(self):
138
- print("BasicAgent initialized.")
139
- async def __call__(self, question: str) -> str:
140
- print(f"Agent received question (first 100 chars): {question[:100]}...")
141
- # fixed_answer = "This is a default answer."
142
- # print(f"Agent returning fixed answer: {fixed_answer}")
143
- # Create agent with all the tools
144
-
145
- # Example query agent might receive
146
- # fixed_answer = await agent.run(question)
147
- messages = [
148
- HumanMessage(
149
- # content="Who is Barack Obama?"
150
- # content="Divide 6790 by 5"
151
- content=question # + '/nothink'
152
- )
153
- ]
154
- response = await agent_graph.ainvoke({"messages": messages}, config={"recursion_limit": 10})
155
- response_text = response['messages'][-1].content
156
- # return response_text.split('</think>')[-1]
157
- return extract_answer(response_text)
158
 
159
  async def run_and_submit_all( profile: gr.OAuthProfile | None):
160
  """
 
6
  import inspect
7
  import pandas as pd
8
  import re
9
+
10
 
11
  # (Keep Constants as is)
12
  # --- Constants ---
 
15
  # --- Basic Agent Definition ---
16
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
17
 
18
+ # offloaded to agent.py for better modularity and readability
19
+ from agent import BasicAgent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  async def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """