Spaces:
Running
Running
File size: 5,382 Bytes
a7e14dd 5dbccd2 cb79291 a7e14dd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# app.py
import os, asyncio, aiohttp, nest_asyncio
from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec
from llama_index.tools.weather import OpenWeatherMapToolSpec
from llama_index.tools.playwright import PlaywrightToolSpec
from llama_index.core.tools import FunctionTool
from llama_index.core.agent.workflow import ReActAgent, FunctionAgent, AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.llms.openai import OpenAI
from llama_index.core.memory import ChatMemoryBuffer
from llama_index.readers.web import RssReader
from llama_index.core.workflow import Context
import gradio as gr
import subprocess
subprocess.run(["playwright", "install"])
# allow nested loops in Spaces
nest_asyncio.apply()
# --- Secrets via env vars ---
HF_TOKEN = os.getenv("HF_TOKEN")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENWEATHERMAP_KEY = os.getenv("OPENWEATHERMAP_API_KEY")
SERPER_API_KEY = os.getenv("SERPER_API_KEY")
# --- LLMs ---
hf_llm = HuggingFaceInferenceAPI(
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
token=HF_TOKEN, task="conversational"
)
oa_llm = OpenAI(model="gpt-4o", api_key=OPENAI_API_KEY, temperature=0.0)
# --- Memory ---
memory = ChatMemoryBuffer.from_defaults(token_limit=4096)
# --- Tools Setup ---
# DuckDuckGo
duck_spec = DuckDuckGoSearchToolSpec()
search_tool = FunctionTool.from_defaults(duck_spec.duckduckgo_full_search)
# Weather
weather_spec = OpenWeatherMapToolSpec(key=OPENWEATHERMAP_KEY)
weather_cur = FunctionTool.from_defaults(
weather_spec.weather_at_location,
name="current_weather",
description="Get the current weather for a location."
)
weather_fc = FunctionTool.from_defaults(
weather_spec.forecast_tommorrow_at_location,
name="weather_forecast",
description="Get tomorrow’s forecast for a location."
)
# Playwright (synchronous start)
async def _start_browser():
return await PlaywrightToolSpec.create_async_playwright_browser(headless=True)
browser = asyncio.get_event_loop().run_until_complete(_start_browser())
pw_spec = PlaywrightToolSpec.from_async_browser(browser)
navigate_tool = FunctionTool.from_defaults(pw_spec.navigate_to, name="web_navigate", description="Go to URL")
extract_text = FunctionTool.from_defaults(pw_spec.extract_text, name="web_extract_text", description="Extract page text")
# …add extract_links if desired…
# Google News RSS
def fetch_google_news_rss():
docs = RssReader(html_to_text=True).load_data(["https://news.google.com/rss"])
return [{"title":d.metadata.get("title",""), "url":d.metadata.get("link","")} for d in docs]
google_rss_tool = FunctionTool.from_defaults(
fn=fetch_google_news_rss,
name="fetch_google_news_rss",
description="Get headlines & URLs from Google News RSS."
)
# Serper
async def fetch_serper(ctx, query):
if not SERPER_API_KEY:
raise ValueError("SERPER_API_KEY missing")
url = f"https://google.serper.dev/news?q={query}&tbs=qdr%3Ad"
hdr = {"X-API-KEY": SERPER_API_KEY, "Content-Type":"application/json"}
async with aiohttp.ClientSession() as s:
r = await s.get(url, headers=hdr)
r.raise_for_status()
return await r.json()
serper_tool = FunctionTool.from_defaults(
fetch_serper, name="fetch_news_from_serper",
description="Search today’s news via Serper."
)
# --- Agents ---
google_rss_agent = FunctionAgent(
name="google_rss_agent", llm=oa_llm, memory=memory,
tools=[google_rss_tool],
system_prompt="Fetch the latest headlines from Google News RSS."
)
web_agent = ReActAgent(
name="web_browsing_agent", llm=hf_llm, memory=memory,
tools=[serper_tool, navigate_tool, extract_text],
system_prompt=(
"When asked for details on a headline, "
"1) call fetch_news_from_serper(query); "
"2) navigate to each URL; 3) extract_text(); "
"4) summarize."
)
)
weather_agent = ReActAgent(
name="weather_agent", llm=oa_llm,
tools=[weather_cur, weather_fc],
system_prompt="You are a weather agent."
)
search_agent = ReActAgent(
name="search_agent", llm=oa_llm,
tools=[search_tool],
system_prompt="You are a search agent using DuckDuckGo."
)
router = ReActAgent(
name="router_agent", llm=hf_llm,
tools=[FunctionTool.from_defaults(lambda ctx, choice: choice,
name="choose_agent",
description="Return agent name.")],
system_prompt=(
"Route the user query to exactly one of: "
"['google_rss_agent','weather_agent','search_agent','web_browsing_agent']."
),
can_handoff_to=["google_rss_agent","weather_agent","search_agent","web_browsing_agent"]
)
workflow = AgentWorkflow(
agents=[router, google_rss_agent, web_agent, weather_agent, search_agent],
root_agent="router_agent"
)
ctx = Context(workflow)
# Sync wrapper
def respond(query: str) -> str:
out = asyncio.run(workflow.run(user_msg=query, ctx=ctx, memory=memory))
return out.response.blocks[0].text
# --- Gradio UI ---
with gr.Blocks() as demo:
gr.Markdown("## 🗞️ Multi‐Agent News & Weather Chatbot")
chatbot = gr.Chatbot()
txt = gr.Textbox(placeholder="Ask me about news, weather or anything…")
txt.submit(lambda q, chat: (chat + [[q, respond(q)]]), [txt, chatbot], chatbot)
demo.launch() |