fdaudens HF Staff commited on
Commit
4c2fdec
·
verified ·
1 Parent(s): 71dd8bc

Update app.py

Browse files

update chatbot

Files changed (1) hide show
  1. app.py +57 -65
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
- import subprocess
23
- subprocess.run(["playwright", "install"])
24
 
25
- # allow nested loops in Spaces
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
- # --- Secrets via env vars ---
37
- HF_TOKEN = os.getenv("HF_TOKEN")
38
- # OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
39
- OPENWEATHERMAP_KEY = os.getenv("OPENWEATHERMAP_API_KEY")
40
- SERPER_API_KEY = os.getenv("SERPER_API_KEY")
41
 
42
- # --- LLMs ---
43
  llm = HuggingFaceInferenceAPI(
44
  model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
45
- token=HF_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
- # # OpenAI for pure function-calling
54
- # openai_llm = OpenAI(
55
- # model="gpt-4o",
56
- # api_key=OPENAI_API_KEY,
57
- # temperature=0.0,
58
- # streaming=False,
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
- weather_tool_spec.weather_at_location,
72
  name="current_weather",
73
- description="Get the current weather at a specific location (city, country)."
74
  )
75
  forecast_tool = FunctionTool.from_defaults(
76
- weather_tool_spec.forecast_tommorrow_at_location,
77
  name="weather_forecast",
78
- description="Get tomorrow's weather forecast for a specific location (city, country)."
79
  )
80
 
81
- # Playwright (synchronous start)
82
- async def _start_browser():
83
- return await PlaywrightToolSpec.create_async_playwright_browser(headless=True)
84
- browser = asyncio.get_event_loop().run_until_complete(_start_browser())
 
 
 
 
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 specific URL."
91
  )
92
  extract_text_tool = FunctionTool.from_defaults(
93
  playwright_tool_spec.extract_text,
94
  name="web_extract_text",
95
- description="Extract all text from the current page."
96
  )
97
  extract_links_tool = FunctionTool.from_defaults(
98
  playwright_tool_spec.extract_hyperlinks,
99
  name="web_extract_links",
100
- description="Extract all hyperlinks from the current page."
101
  )
102
 
103
- # Google News RSS
104
  def fetch_google_news_rss():
105
- docs = RssReader(html_to_text=True).load_data(["https://news.google.com/rss"])
106
- return [{"title":d.metadata.get("title",""), "url":d.metadata.get("link","")} for d in docs]
 
 
 
 
107
  google_rss_tool = FunctionTool.from_defaults(
108
- fn=fetch_google_news_rss,
109
  name="fetch_google_news_rss",
110
- description="Fetch latest headlines and URLs from Google News RSS."
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 given topic via the Serper API."
128
  )
129
 
130
  # Create the agent workflow
131
  tools = [
132
- search_tool,
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
- chat_history = chat_history or []
166
  result = await run_query(user_input)
167
- response = result.response
168
- chat_history.append((user_input, response))
169
- return chat_history, chat_history
 
 
 
 
 
170
 
171
  # Build and launch Gradio app
172
  grb = gr.Blocks()
173
  with grb:
174
- gr.Markdown("## Perspicacity")
175
- gr.Markdown(
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
- grb.launch()
 
 
 
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()