Spaces:
Running
Running
Update app.py
Browse filesupdate chatbot
app.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
# app.py
|
2 |
import os
|
3 |
import logging
|
4 |
import asyncio
|
@@ -19,13 +18,10 @@ from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
|
|
19 |
from llama_index.core.memory import ChatMemoryBuffer
|
20 |
from llama_index.readers.web import RssReader
|
21 |
|
22 |
-
|
23 |
-
|
24 |
|
25 |
-
#
|
26 |
-
nest_asyncio.apply()
|
27 |
-
|
28 |
-
# --- Llangfuse ---
|
29 |
instrumentor = LlamaIndexInstrumentor(
|
30 |
public_key=os.environ.get("LANGFUSE_PUBLIC_KEY"),
|
31 |
secret_key=os.environ.get("LANGFUSE_SECRET_KEY"),
|
@@ -33,84 +29,80 @@ instrumentor = LlamaIndexInstrumentor(
|
|
33 |
)
|
34 |
instrumentor.start()
|
35 |
|
36 |
-
#
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
SERPER_API_KEY = os.getenv("SERPER_API_KEY")
|
41 |
|
42 |
-
#
|
43 |
llm = HuggingFaceInferenceAPI(
|
44 |
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
|
45 |
-
token=
|
46 |
task="conversational"
|
47 |
)
|
48 |
-
|
49 |
-
memory = ChatMemoryBuffer.from_defaults(token_limit=8192)
|
50 |
today_str = datetime.now().strftime("%B %d, %Y")
|
51 |
ANON_USER_ID = os.environ.get("ANON_USER_ID", uuid.uuid4().hex)
|
52 |
|
53 |
-
#
|
54 |
-
#
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
#
|
60 |
-
|
61 |
-
# --- Tools Setup ---
|
62 |
-
# DuckDuckGo
|
63 |
-
duck_spec = DuckDuckGoSearchToolSpec()
|
64 |
-
search_tool = FunctionTool.from_defaults(duck_spec.duckduckgo_full_search)
|
65 |
-
|
66 |
-
# Weather
|
67 |
-
openweather_api_key=OPENWEATHERMAP_KEY
|
68 |
-
weather_tool_spec = OpenWeatherMapToolSpec(key=openweather_api_key)
|
69 |
-
weather_tool_spec = OpenWeatherMapToolSpec(key=openweather_api_key)
|
70 |
weather_tool = FunctionTool.from_defaults(
|
71 |
-
|
72 |
name="current_weather",
|
73 |
-
description="Get the current weather
|
74 |
)
|
75 |
forecast_tool = FunctionTool.from_defaults(
|
76 |
-
|
77 |
name="weather_forecast",
|
78 |
-
description="Get tomorrow's weather forecast for a
|
79 |
)
|
80 |
|
81 |
-
# Playwright
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
85 |
playwright_tool_spec = PlaywrightToolSpec.from_async_browser(browser)
|
86 |
|
87 |
navigate_tool = FunctionTool.from_defaults(
|
88 |
playwright_tool_spec.navigate_to,
|
89 |
name="web_navigate",
|
90 |
-
description="Navigate to a
|
91 |
)
|
92 |
extract_text_tool = FunctionTool.from_defaults(
|
93 |
playwright_tool_spec.extract_text,
|
94 |
name="web_extract_text",
|
95 |
-
description="Extract
|
96 |
)
|
97 |
extract_links_tool = FunctionTool.from_defaults(
|
98 |
playwright_tool_spec.extract_hyperlinks,
|
99 |
name="web_extract_links",
|
100 |
-
description="Extract
|
101 |
)
|
102 |
|
103 |
-
# Google News RSS
|
104 |
def fetch_google_news_rss():
|
105 |
-
|
106 |
-
|
|
|
|
|
|
|
|
|
107 |
google_rss_tool = FunctionTool.from_defaults(
|
108 |
-
|
109 |
name="fetch_google_news_rss",
|
110 |
-
description="
|
111 |
)
|
112 |
|
113 |
-
# Serper
|
114 |
async def fetch_serper_news(query: str):
|
115 |
if not serper_api_key:
|
116 |
raise ValueError("Missing SERPER_API_KEY environment variable")
|
@@ -124,12 +116,12 @@ async def fetch_serper_news(query: str):
|
|
124 |
serper_news_tool = FunctionTool.from_defaults(
|
125 |
fetch_serper_news,
|
126 |
name="fetch_news_from_serper",
|
127 |
-
description="Fetch news articles on a
|
128 |
)
|
129 |
|
130 |
# Create the agent workflow
|
131 |
tools = [
|
132 |
-
|
133 |
navigate_tool,
|
134 |
extract_text_tool,
|
135 |
extract_links_tool,
|
@@ -162,26 +154,26 @@ async def run_query(query: str):
|
|
162 |
|
163 |
# Gradio interface function
|
164 |
async def gradio_query(user_input, chat_history=None):
|
165 |
-
|
166 |
result = await run_query(user_input)
|
167 |
-
|
168 |
-
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
# Build and launch Gradio app
|
172 |
grb = gr.Blocks()
|
173 |
with grb:
|
174 |
-
gr.Markdown("##
|
175 |
-
gr.
|
176 |
-
"This bot can check the news, tell you the weather, and even browse websites to answer follow-up questions — all powered by a team of tiny AI agents working behind the scenes.\n\n"
|
177 |
-
"🧪 Built for fun during the [AI Agents course](https://huggingface.co/learn/agents-course/unit0/introduction) — it's just a demo to show what agents can do. \n"
|
178 |
-
"🙌 Got ideas or improvements? PRs welcome! \n\n"
|
179 |
-
"👉 _Try asking “What’s the weather in Montreal?” or “What’s in the news today?”_"
|
180 |
-
)
|
181 |
-
chatbot = gr.Chatbot() # conversation UI
|
182 |
txt = gr.Textbox(placeholder="Ask me anything...", show_label=False)
|
183 |
txt.submit(gradio_query, [txt, chatbot], [chatbot, chatbot])
|
184 |
gr.Button("Send").click(gradio_query, [txt, chatbot], [chatbot, chatbot])
|
185 |
|
186 |
if __name__ == "__main__":
|
187 |
-
|
|
|
|
|
|
1 |
import os
|
2 |
import logging
|
3 |
import asyncio
|
|
|
18 |
from llama_index.core.memory import ChatMemoryBuffer
|
19 |
from llama_index.readers.web import RssReader
|
20 |
|
21 |
+
# Configure logging
|
22 |
+
logging.getLogger("langfuse").setLevel(logging.WARNING)
|
23 |
|
24 |
+
# Initialize Langfuse instrumentor
|
|
|
|
|
|
|
25 |
instrumentor = LlamaIndexInstrumentor(
|
26 |
public_key=os.environ.get("LANGFUSE_PUBLIC_KEY"),
|
27 |
secret_key=os.environ.get("LANGFUSE_SECRET_KEY"),
|
|
|
29 |
)
|
30 |
instrumentor.start()
|
31 |
|
32 |
+
# Environment variables
|
33 |
+
hf_token = os.environ.get("HF_TOKEN")
|
34 |
+
openweather_api_key = os.environ.get("OPENWEATHER_API_KEY")
|
35 |
+
serper_api_key = os.environ.get("SERPER_API_KEY")
|
|
|
36 |
|
37 |
+
# Initialize LLM and conversation memory
|
38 |
llm = HuggingFaceInferenceAPI(
|
39 |
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
|
40 |
+
token=hf_token,
|
41 |
task="conversational"
|
42 |
)
|
43 |
+
memory = ChatMemoryBuffer.from_defaults(token_limit=4096)
|
|
|
44 |
today_str = datetime.now().strftime("%B %d, %Y")
|
45 |
ANON_USER_ID = os.environ.get("ANON_USER_ID", uuid.uuid4().hex)
|
46 |
|
47 |
+
# Define tools
|
48 |
+
# DuckDuckGo web search
|
49 |
+
duckduckgo_tool = FunctionTool.from_defaults(
|
50 |
+
DuckDuckGoSearchToolSpec().duckduckgo_full_search
|
51 |
+
)
|
52 |
+
|
53 |
+
# Weather tools
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
weather_tool = FunctionTool.from_defaults(
|
55 |
+
OpenWeatherMapToolSpec(key=openweather_api_key).weather_at_location,
|
56 |
name="current_weather",
|
57 |
+
description="Get the current weather for a city/country."
|
58 |
)
|
59 |
forecast_tool = FunctionTool.from_defaults(
|
60 |
+
OpenWeatherMapToolSpec(key=openweather_api_key).forecast_tommorrow_at_location,
|
61 |
name="weather_forecast",
|
62 |
+
description="Get tomorrow's weather forecast for a city/country."
|
63 |
)
|
64 |
|
65 |
+
# Playwright tools setup
|
66 |
+
nest_asyncio.apply()
|
67 |
+
browser = asyncio.get_event_loop().run_until_complete(
|
68 |
+
PlaywrightToolSpec.create_async_playwright_browser(
|
69 |
+
headless=True,
|
70 |
+
args=["--no-sandbox", "--disable-setuid-sandbox"]
|
71 |
+
)
|
72 |
+
)
|
73 |
playwright_tool_spec = PlaywrightToolSpec.from_async_browser(browser)
|
74 |
|
75 |
navigate_tool = FunctionTool.from_defaults(
|
76 |
playwright_tool_spec.navigate_to,
|
77 |
name="web_navigate",
|
78 |
+
description="Navigate to a URL."
|
79 |
)
|
80 |
extract_text_tool = FunctionTool.from_defaults(
|
81 |
playwright_tool_spec.extract_text,
|
82 |
name="web_extract_text",
|
83 |
+
description="Extract text from the current page."
|
84 |
)
|
85 |
extract_links_tool = FunctionTool.from_defaults(
|
86 |
playwright_tool_spec.extract_hyperlinks,
|
87 |
name="web_extract_links",
|
88 |
+
description="Extract hyperlinks from the current page."
|
89 |
)
|
90 |
|
91 |
+
# Google News RSS tool
|
92 |
def fetch_google_news_rss():
|
93 |
+
reader = RssReader(html_to_text=True)
|
94 |
+
docs = reader.load_data(["https://news.google.com/rss"])
|
95 |
+
return [
|
96 |
+
{"title": doc.metadata.get("title", "").strip(), "url": doc.metadata.get("link", "")} for doc in docs
|
97 |
+
]
|
98 |
+
|
99 |
google_rss_tool = FunctionTool.from_defaults(
|
100 |
+
fetch_google_news_rss,
|
101 |
name="fetch_google_news_rss",
|
102 |
+
description="Get latest headlines and URLs from Google News RSS feed."
|
103 |
)
|
104 |
|
105 |
+
# Serper news API tool
|
106 |
async def fetch_serper_news(query: str):
|
107 |
if not serper_api_key:
|
108 |
raise ValueError("Missing SERPER_API_KEY environment variable")
|
|
|
116 |
serper_news_tool = FunctionTool.from_defaults(
|
117 |
fetch_serper_news,
|
118 |
name="fetch_news_from_serper",
|
119 |
+
description="Fetch news articles on a topic via Serper API."
|
120 |
)
|
121 |
|
122 |
# Create the agent workflow
|
123 |
tools = [
|
124 |
+
duckduckgo_tool,
|
125 |
navigate_tool,
|
126 |
extract_text_tool,
|
127 |
extract_links_tool,
|
|
|
154 |
|
155 |
# Gradio interface function
|
156 |
async def gradio_query(user_input, chat_history=None):
|
157 |
+
history = chat_history or []
|
158 |
result = await run_query(user_input)
|
159 |
+
# Ensure text-only content and strip any role prefix
|
160 |
+
resp_text = str(result.response)
|
161 |
+
if resp_text.lower().startswith("assistant:"):
|
162 |
+
resp_text = resp_text.split(":", 1)[1].strip()
|
163 |
+
# Append OpenAI-style message dicts
|
164 |
+
history.append({"role": "user", "content": user_input})
|
165 |
+
history.append({"role": "assistant", "content": resp_text})
|
166 |
+
return history, history
|
167 |
|
168 |
# Build and launch Gradio app
|
169 |
grb = gr.Blocks()
|
170 |
with grb:
|
171 |
+
gr.Markdown("## AI Web Agent")
|
172 |
+
chatbot = gr.Chatbot(type="messages") # use openai-style messages
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
txt = gr.Textbox(placeholder="Ask me anything...", show_label=False)
|
174 |
txt.submit(gradio_query, [txt, chatbot], [chatbot, chatbot])
|
175 |
gr.Button("Send").click(gradio_query, [txt, chatbot], [chatbot, chatbot])
|
176 |
|
177 |
if __name__ == "__main__":
|
178 |
+
# share=True if you want a public Space link
|
179 |
+
grb.launch()
|