Edits and improvements to Gradio UI
Browse files- assets/html.py +44 -4
- client/mcp_client.py +1 -1
- client/tool_workflows.py +5 -5
- rss_client.py +5 -4
assets/html.py
CHANGED
@@ -3,18 +3,58 @@
|
|
3 |
TITLE = (
|
4 |
'''
|
5 |
<center>
|
6 |
-
<h1>
|
|
|
7 |
</center>
|
8 |
'''
|
9 |
)
|
10 |
|
11 |
DESCRIPTION = (
|
12 |
'''
|
13 |
-
<p>
|
|
|
|
|
|
|
|
|
14 |
<a href='https://huggingface.co/spaces/Agents-MCP-Hackathon/rss-mcp-server'>
|
15 |
-
RSS feed reader</a> via MCP. Click 'Connect to MCP
|
16 |
-
|
|
|
|
|
17 |
main project repo on GitHub</a>. Both Spaces by
|
18 |
<a href=https://www.linkedin.com/in/gperdrizet/'>George Perdrizet</a>.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
'''
|
20 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
TITLE = (
|
4 |
'''
|
5 |
<center>
|
6 |
+
<h1>RASS (retrieval augmented simple syndication) Agent</h1>
|
7 |
+
<h2>Agentic RSS feed reader</h2>
|
8 |
</center>
|
9 |
'''
|
10 |
)
|
11 |
|
12 |
DESCRIPTION = (
|
13 |
'''
|
14 |
+
<p><b>Problem</b>: I love RSS feeds, but need help keeping up with all of the content from my subscriptions.
|
15 |
+
|
16 |
+
<b>Solution</b>: Build a tool to allow LLMs to find and interact with RSS feeds on behalf of the user.</p>
|
17 |
+
<h2>Introduction</h2>
|
18 |
+
<p>This demonstration uses sister space
|
19 |
<a href='https://huggingface.co/spaces/Agents-MCP-Hackathon/rss-mcp-server'>
|
20 |
+
RSS feed reader</a> via MCP to interact with RSS feeds. Click 'Connect to MCP
|
21 |
+
server' to get started. If it takes a minute or two to reply, don't worry the inference
|
22 |
+
container was probably cold and spinning up. Check out the
|
23 |
+
<a href='https://github.com/gperdrizet/MCP-hackathon/tree/main'>
|
24 |
main project repo on GitHub</a>. Both Spaces by
|
25 |
<a href=https://www.linkedin.com/in/gperdrizet/'>George Perdrizet</a>.</p>
|
26 |
+
|
27 |
+
I love RSS feeds - they remind me of a time when the internet was a weird and
|
28 |
+
wonderful place, filled with interesting content hiding behind every link. The tools
|
29 |
+
to produce and navigate that content have improved by leaps and bounds. However,
|
30 |
+
the improvement has not come without some losses. Content often feels homogeneous and
|
31 |
+
it is too often painfully apparent that your favorite platform has a large degree of
|
32 |
+
control over what content you see and what content you don't.
|
33 |
+
|
34 |
+
This tool give the user back some of that control. It let's them decide what content
|
35 |
+
and sources they are interested in. I built it because I want access to diverse,
|
36 |
+
unfiltered publishing by many sources, paired modern AI to help me navigate it.
|
37 |
+
I want the model to help me ingest my feed, not create it for me!
|
38 |
'''
|
39 |
)
|
40 |
+
|
41 |
+
FEATURES_TOOLS ='''
|
42 |
+
## Features
|
43 |
+
|
44 |
+
1. Inference with Anthropic's efficient claude-3-haiku model.
|
45 |
+
2. Custom MCP client with asynchronous server side events, retry and error handling based on the excellent repo by [Adel Zaalouk](https://github.com/zanetworker/mcp-playground/tree/main).
|
46 |
+
3. Multi-turn re-prompting to allow LLM workflows with multiple tool calls.
|
47 |
+
4. Queue and worker system to show user what's going on 'under the hood' while the model calls tools and generates replies.
|
48 |
+
|
49 |
+
## Tools
|
50 |
+
|
51 |
+
1. `get_feed()`: Given a website name or URL, find its RSS feed and
|
52 |
+
return recent article titles, links and a generated summary of content if
|
53 |
+
avalible. Caches results for fast retrieval by other tools. Embeds content
|
54 |
+
to vector database for subsequent RAG.
|
55 |
+
2. `context_search()`: Vector search on article content for RAG context.
|
56 |
+
3. `find_article()`: Uses vector search on article content to find title of article
|
57 |
+
that user is referring to.
|
58 |
+
4. `get_summary()`: Gets article summary from Redis cache using article title.
|
59 |
+
5. `get_link()`: Gets article link from Redis cache using article title.
|
60 |
+
'''
|
client/mcp_client.py
CHANGED
@@ -71,7 +71,7 @@ class MCPTimeoutError(Exception):
|
|
71 |
class MCPClientWrapper:
|
72 |
'''Main client wrapper class for interacting with Model Context Protocol (MCP) endpoints'''
|
73 |
|
74 |
-
def __init__(self, endpoint: str, timeout: float =
|
75 |
'''Initialize MCP client with endpoint URL
|
76 |
|
77 |
Args:
|
|
|
71 |
class MCPClientWrapper:
|
72 |
'''Main client wrapper class for interacting with Model Context Protocol (MCP) endpoints'''
|
73 |
|
74 |
+
def __init__(self, endpoint: str, timeout: float = 360.0, max_retries: int = 3):
|
75 |
'''Initialize MCP client with endpoint URL
|
76 |
|
77 |
Args:
|
client/tool_workflows.py
CHANGED
@@ -9,10 +9,10 @@ from client import prompts
|
|
9 |
from client.anthropic_bridge import AnthropicBridge
|
10 |
|
11 |
INTERMEDIATE_REPLY_HINTS = {
|
12 |
-
'
|
13 |
-
'
|
14 |
-
'
|
15 |
-
'
|
16 |
}
|
17 |
|
18 |
async def tool_loop(
|
@@ -37,7 +37,7 @@ async def tool_loop(
|
|
37 |
tool_call = result['tool_call']
|
38 |
tool_name = tool_call['name']
|
39 |
|
40 |
-
if tool_name == '
|
41 |
reply = await get_feed_call(
|
42 |
user_query,
|
43 |
result,
|
|
|
9 |
from client.anthropic_bridge import AnthropicBridge
|
10 |
|
11 |
INTERMEDIATE_REPLY_HINTS = {
|
12 |
+
'rss_mcp_server_context_search': 'Let me find some additional context before I generate a final answer.',
|
13 |
+
'rss_mcp_server_find_article': 'I will find the title of that article.',
|
14 |
+
'rss_mcp_server_get_summary': 'I will summarize that article',
|
15 |
+
'rss_mcp_server_get_link': 'I will get the link to that article'
|
16 |
}
|
17 |
|
18 |
async def tool_loop(
|
|
|
37 |
tool_call = result['tool_call']
|
38 |
tool_name = tool_call['name']
|
39 |
|
40 |
+
if tool_name == 'rss_mcp_server_get_feed':
|
41 |
reply = await get_feed_call(
|
42 |
user_query,
|
43 |
result,
|
rss_client.py
CHANGED
@@ -40,8 +40,7 @@ logger = logging.getLogger(__name__)
|
|
40 |
|
41 |
# Handle MCP server connection and interactions
|
42 |
RSS_CLIENT = MCPClientWrapper(
|
43 |
-
|
44 |
-
'http://127.0.0.1:7860/gradio_api/mcp/sse'
|
45 |
)
|
46 |
logger.info('Started MCP client')
|
47 |
|
@@ -57,6 +56,7 @@ logger.info('Started Anthropic API bridge')
|
|
57 |
OUTPUT_QUEUE = queue.Queue()
|
58 |
logger.info('Created response queue')
|
59 |
|
|
|
60 |
def user_message(message: str, history: list) -> Tuple[str, list]:
|
61 |
'''Adds user message to conversation and returns for immediate posting.
|
62 |
|
@@ -102,11 +102,12 @@ def send_message(chat_history: list):
|
|
102 |
yield chat_history
|
103 |
|
104 |
|
105 |
-
with gr.Blocks(title='
|
106 |
with gr.Row():
|
107 |
gr.HTML(html.TITLE)
|
108 |
|
109 |
gr.Markdown(html.DESCRIPTION)
|
|
|
110 |
|
111 |
# MCP connection/tool dump
|
112 |
connect_btn = gr.Button('Connect to MCP server')
|
@@ -128,7 +129,7 @@ with gr.Blocks(title='MCP RSS client') as demo:
|
|
128 |
# Chat interface
|
129 |
chatbot = gr.Chatbot(
|
130 |
value=[],
|
131 |
-
height=
|
132 |
type='messages',
|
133 |
show_copy_button=True
|
134 |
)
|
|
|
40 |
|
41 |
# Handle MCP server connection and interactions
|
42 |
RSS_CLIENT = MCPClientWrapper(
|
43 |
+
'https://agents-mcp-hackathon-rss-mcp-server.hf.space/gradio_api/mcp/sse'
|
|
|
44 |
)
|
45 |
logger.info('Started MCP client')
|
46 |
|
|
|
56 |
OUTPUT_QUEUE = queue.Queue()
|
57 |
logger.info('Created response queue')
|
58 |
|
59 |
+
|
60 |
def user_message(message: str, history: list) -> Tuple[str, list]:
|
61 |
'''Adds user message to conversation and returns for immediate posting.
|
62 |
|
|
|
102 |
yield chat_history
|
103 |
|
104 |
|
105 |
+
with gr.Blocks(title='RASS agent') as demo:
|
106 |
with gr.Row():
|
107 |
gr.HTML(html.TITLE)
|
108 |
|
109 |
gr.Markdown(html.DESCRIPTION)
|
110 |
+
gr.Markdown(html.FEATURES_TOOLS)
|
111 |
|
112 |
# MCP connection/tool dump
|
113 |
connect_btn = gr.Button('Connect to MCP server')
|
|
|
129 |
# Chat interface
|
130 |
chatbot = gr.Chatbot(
|
131 |
value=[],
|
132 |
+
height=500,
|
133 |
type='messages',
|
134 |
show_copy_button=True
|
135 |
)
|