swroop1242 commited on
Commit
4735f2d
·
verified ·
1 Parent(s): 878daca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -87
app.py CHANGED
@@ -2,47 +2,46 @@ import os
2
  import asyncio
3
  import base64
4
  import io
5
- import json
6
  from typing import Optional, List, Dict, Any
7
  from PIL import Image
8
 
 
 
9
  import gradio as gr
10
  import spaces # Hugging Face ZeroGPU module
11
  import torch
12
  from transformers import pipeline
13
 
14
  from playwright.async_api import async_playwright
15
- from fastapi import FastAPI
16
  from mcp.server.fastmcp import FastMCP
17
 
18
  # ---------------------------------------------------------
19
  # 1. ZeroGPU Model Initialization & GPU-Decorated Functions
20
  # ---------------------------------------------------------
21
- print("Initializing ZeroGPU Vision & Text Research Pipeline...")
22
  try:
23
- # Model initialized at module level
24
  research_pipe = pipeline("text-generation", model="gpt2", device="cuda" if torch.cuda.is_available() else "cpu")
25
  except Exception as e:
26
- print(f"Model initialization note: {e}")
27
  research_pipe = None
28
 
29
  @spaces.GPU
30
  def run_zerogpu_analysis(prompt: str) -> str:
31
- """ZeroGPU function for accelerated text analysis, reasoning, and visual summaries."""
32
  if not research_pipe:
33
  return f"ZeroGPU offline. Input snippet: {prompt[:150]}..."
34
 
35
- formatted_input = f"Hybrid Research Query & Context:\n{prompt}\n\nSynthesis:"
36
  result = research_pipe(formatted_input, max_new_tokens=200, do_sample=True, temperature=0.7)
37
  return result[0]["generated_text"]
38
 
39
  # ---------------------------------------------------------
40
- # 2. Playwright Core Scraper Engine
41
  # ---------------------------------------------------------
42
  async def get_browser_page(p):
43
  browser = await p.chromium.launch(
44
  headless=True,
45
- args=["--no-sandbox", "--disable-setuid-sandbox"]
46
  )
47
  context = await browser.new_context(
48
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
@@ -53,11 +52,11 @@ async def get_browser_page(p):
53
  # ---------------------------------------------------------
54
  # 3. FastMCP Server Tools
55
  # ---------------------------------------------------------
56
- mcp = FastMCP("Hybrid Research Playwright Server", host="0.0.0.0", port=7860)
57
 
58
  @mcp.tool()
59
  async def search_web(query: str, max_results: int = 5) -> str:
60
- """Search the web via DuckDuckGo to discover relevant URLs for research."""
61
  async with async_playwright() as p:
62
  browser, page = await get_browser_page(p)
63
  try:
@@ -85,7 +84,7 @@ async def search_web(query: str, max_results: int = 5) -> str:
85
 
86
  @mcp.tool()
87
  async def fetch_webpage_text(url: str, selector: Optional[str] = None) -> str:
88
- """Fetch readable text content and title from a webpage."""
89
  async with async_playwright() as p:
90
  browser, page = await get_browser_page(p)
91
  try:
@@ -104,36 +103,9 @@ async def fetch_webpage_text(url: str, selector: Optional[str] = None) -> str:
104
  await browser.close()
105
  return f"Error fetching {url}: {str(e)}"
106
 
107
- @mcp.tool()
108
- async def extract_links_and_metadata(url: str, keyword_filter: Optional[str] = None) -> str:
109
- """Extract metadata (OpenGraph/title) and links from a webpage, optionally filtered by keyword."""
110
- async with async_playwright() as p:
111
- browser, page = await get_browser_page(p)
112
- try:
113
- await page.goto(url, wait_until="domcontentloaded", timeout=20000)
114
- title = await page.title()
115
-
116
- # Extract links
117
- anchors = await page.query_selector_all("a[href]")
118
- links = []
119
- for a in anchors:
120
- href = await a.get_attribute("href")
121
- text = (await a.inner_text()).strip()
122
- if href and text:
123
- if keyword_filter and keyword_filter.lower() not in text.lower() and keyword_filter.lower() not in href.lower():
124
- continue
125
- links.append(f"[{text}]({href})")
126
-
127
- await browser.close()
128
- links_str = "\n".join(links[:20]) if links else "No matching links found."
129
- return f"### Metadata & Links for {title} ({url}):\n\n**Extracted Links:**\n{links_str}"
130
- except Exception as e:
131
- await browser.close()
132
- return f"Link extraction error: {str(e)}"
133
-
134
  @mcp.tool()
135
  async def extract_tables_and_data(url: str) -> str:
136
- """Extract HTML tables and structured data from a webpage as Markdown formatted tables."""
137
  async with async_playwright() as p:
138
  browser, page = await get_browser_page(p)
139
  try:
@@ -141,7 +113,7 @@ async def extract_tables_and_data(url: str) -> str:
141
  tables = await page.query_selector_all("table")
142
 
143
  extracted_tables = []
144
- for i, table in enumerate(tables[:3]): # Parse up to 3 tables
145
  rows = await table.query_selector_all("tr")
146
  table_md = []
147
  for row in rows:
@@ -151,7 +123,6 @@ async def extract_tables_and_data(url: str) -> str:
151
  table_md.append("|" + "|".join(col_texts) + "|")
152
 
153
  if table_md:
154
- # Add Markdown header separator line if present
155
  if len(table_md) > 1:
156
  num_cols = table_md[0].count("|") - 1
157
  sep = "|" + "|".join(["---"] * num_cols) + "|"
@@ -166,28 +137,9 @@ async def extract_tables_and_data(url: str) -> str:
166
  await browser.close()
167
  return f"Table extraction error: {str(e)}"
168
 
169
- @mcp.tool()
170
- async def batch_scrape_pages(urls: List[str]) -> str:
171
- """Scrape text content from multiple URLs concurrently for comparative research."""
172
- async def fetch_single(url: str):
173
- async with async_playwright() as p:
174
- browser, page = await get_browser_page(p)
175
- try:
176
- await page.goto(url, wait_until="domcontentloaded", timeout=15000)
177
- title = await page.title()
178
- text = await page.inner_text("body")
179
- await browser.close()
180
- return f"### Source: {title} ({url})\n{text[:1200]}\n"
181
- except Exception as e:
182
- await browser.close()
183
- return f"### Source: {url}\nFailed to load: {str(e)}\n"
184
-
185
- results = await asyncio.gather(*[fetch_single(u) for u in urls[:4]])
186
- return "\n---\n".join(results)
187
-
188
  @mcp.tool()
189
  async def visual_chart_analysis_gpu(url: str, question: str) -> str:
190
- """Capture page screenshot and run ZeroGPU visual/content reasoning on layout, charts, or text."""
191
  async with async_playwright() as p:
192
  browser, page = await get_browser_page(p)
193
  try:
@@ -196,54 +148,58 @@ async def visual_chart_analysis_gpu(url: str, question: str) -> str:
196
  text_content = await page.inner_text("body")
197
  await browser.close()
198
 
199
- prompt = f"Research Query: {question}\nPage Title: {title}\nURL: {url}\n\nContent Snippet:\n{text_content[:1500]}"
200
  analysis = run_zerogpu_analysis(prompt)
201
- return f"### ZeroGPU Visual & Layout Analysis ({url}):\n\n{analysis}"
202
  except Exception as e:
203
  await browser.close()
204
  return f"Visual analysis error: {str(e)}"
205
 
206
  # ---------------------------------------------------------
207
- # 4. Multi-Tab Gradio Dashboard
208
  # ---------------------------------------------------------
209
  def build_gradio_ui():
210
- with gr.Blocks(title="🎭 Hybrid Research Playwright MCP Server") as demo:
211
  gr.Markdown(
212
  """
213
- # 🎭 Hybrid Research MCP Server (ZeroGPU + Playwright)
214
 
215
- This server provides **hybrid research tools** (web search, tabular data extraction, link discovery, multi-page batch scraping, and ZeroGPU analysis).
216
-
217
- ### 📡 MCP SSE Endpoint
218
- Connect external agents to: `https://<YOUR-HF-SPACE>.hf.space/sse`
219
  """
220
  )
221
 
222
- with gr.Tab("1. Web Search"):
223
- sq = gr.Textbox(label="Search Query", value="ZeroGPU Hugging Face Spaces")
224
  s_btn = gr.Button("Search Web")
225
  s_out = gr.Markdown()
226
- s_btn.click(fn=lambda q: asyncio.run(search_web(q)), inputs=sq, outputs=s_out)
227
-
228
- with gr.Tab("2. Table Extraction"):
229
- tu = gr.Textbox(label="URL with Tables", value="https://en.wikipedia.org/wiki/Comparison_of_web_browsers")
230
- t_btn = gr.Button("Extract Tables")
231
- t_out = gr.Markdown()
232
- t_btn.click(fn=lambda u: asyncio.run(extract_tables_and_data(u)), inputs=tu, outputs=t_out)
233
 
234
- with gr.Tab("3. ZeroGPU Research"):
235
- ru = gr.Textbox(label="Target URL", value="https://news.ycombinator.com")
236
- rq = gr.Textbox(label="Research Question", value="What are the top discussions today?")
237
  r_btn = gr.Button("Run ZeroGPU Analysis", variant="primary")
238
  r_out = gr.Markdown()
239
- r_btn.click(fn=lambda u, q: asyncio.run(visual_chart_analysis_gpu(u, q)), inputs=[ru, rq], outputs=r_out)
240
 
241
  return demo
242
 
243
  # ---------------------------------------------------------
244
- # 5. Launch Application
245
  # ---------------------------------------------------------
246
- demo = build_gradio_ui()
 
 
 
 
 
 
 
 
 
 
 
247
 
248
  if __name__ == "__main__":
249
- demo.queue().launch(server_name="0.0.0.0", server_port=7860)
 
 
2
  import asyncio
3
  import base64
4
  import io
 
5
  from typing import Optional, List, Dict, Any
6
  from PIL import Image
7
 
8
+ import uvicorn
9
+ from fastapi import FastAPI
10
  import gradio as gr
11
  import spaces # Hugging Face ZeroGPU module
12
  import torch
13
  from transformers import pipeline
14
 
15
  from playwright.async_api import async_playwright
 
16
  from mcp.server.fastmcp import FastMCP
17
 
18
  # ---------------------------------------------------------
19
  # 1. ZeroGPU Model Initialization & GPU-Decorated Functions
20
  # ---------------------------------------------------------
21
+ print("Initializing ZeroGPU Pipeline...")
22
  try:
 
23
  research_pipe = pipeline("text-generation", model="gpt2", device="cuda" if torch.cuda.is_available() else "cpu")
24
  except Exception as e:
25
+ print(f"Pipeline loading note: {e}")
26
  research_pipe = None
27
 
28
  @spaces.GPU
29
  def run_zerogpu_analysis(prompt: str) -> str:
30
+ """ZeroGPU function decorated with @spaces.GPU."""
31
  if not research_pipe:
32
  return f"ZeroGPU offline. Input snippet: {prompt[:150]}..."
33
 
34
+ formatted_input = f"Hybrid Research Context:\n{prompt}\n\nSynthesis:"
35
  result = research_pipe(formatted_input, max_new_tokens=200, do_sample=True, temperature=0.7)
36
  return result[0]["generated_text"]
37
 
38
  # ---------------------------------------------------------
39
+ # 2. Playwright Core Engine
40
  # ---------------------------------------------------------
41
  async def get_browser_page(p):
42
  browser = await p.chromium.launch(
43
  headless=True,
44
+ args=["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
45
  )
46
  context = await browser.new_context(
47
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
 
52
  # ---------------------------------------------------------
53
  # 3. FastMCP Server Tools
54
  # ---------------------------------------------------------
55
+ mcp = FastMCP("Hybrid Research Playwright Server")
56
 
57
  @mcp.tool()
58
  async def search_web(query: str, max_results: int = 5) -> str:
59
+ """Search the web via DuckDuckGo to discover candidate research URLs."""
60
  async with async_playwright() as p:
61
  browser, page = await get_browser_page(p)
62
  try:
 
84
 
85
  @mcp.tool()
86
  async def fetch_webpage_text(url: str, selector: Optional[str] = None) -> str:
87
+ """Fetch readable text content from a webpage."""
88
  async with async_playwright() as p:
89
  browser, page = await get_browser_page(p)
90
  try:
 
103
  await browser.close()
104
  return f"Error fetching {url}: {str(e)}"
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  @mcp.tool()
107
  async def extract_tables_and_data(url: str) -> str:
108
+ """Extract HTML tables as Markdown formatted tables."""
109
  async with async_playwright() as p:
110
  browser, page = await get_browser_page(p)
111
  try:
 
113
  tables = await page.query_selector_all("table")
114
 
115
  extracted_tables = []
116
+ for i, table in enumerate(tables[:3]):
117
  rows = await table.query_selector_all("tr")
118
  table_md = []
119
  for row in rows:
 
123
  table_md.append("|" + "|".join(col_texts) + "|")
124
 
125
  if table_md:
 
126
  if len(table_md) > 1:
127
  num_cols = table_md[0].count("|") - 1
128
  sep = "|" + "|".join(["---"] * num_cols) + "|"
 
137
  await browser.close()
138
  return f"Table extraction error: {str(e)}"
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  @mcp.tool()
141
  async def visual_chart_analysis_gpu(url: str, question: str) -> str:
142
+ """Run ZeroGPU visual/content reasoning on page layout or text."""
143
  async with async_playwright() as p:
144
  browser, page = await get_browser_page(p)
145
  try:
 
148
  text_content = await page.inner_text("body")
149
  await browser.close()
150
 
151
+ prompt = f"Query: {question}\nTitle: {title}\nURL: {url}\n\nContent:\n{text_content[:1500]}"
152
  analysis = run_zerogpu_analysis(prompt)
153
+ return f"### ZeroGPU Analysis ({url}):\n\n{analysis}"
154
  except Exception as e:
155
  await browser.close()
156
  return f"Visual analysis error: {str(e)}"
157
 
158
  # ---------------------------------------------------------
159
+ # 4. Gradio Interface
160
  # ---------------------------------------------------------
161
  def build_gradio_ui():
162
+ with gr.Blocks(title="🎭 Playwright MCP Server on ZeroGPU") as demo:
163
  gr.Markdown(
164
  """
165
+ # 🎭 Hybrid Research MCP Server (ZeroGPU + Docker)
166
 
167
+ ### 📡 External MCP SSE Connection
168
+ Point MCP clients to: `https://swroop1242-pywright-mcp-server.hf.space/sse`
 
 
169
  """
170
  )
171
 
172
+ with gr.Tab("Web Search"):
173
+ q_in = gr.Textbox(label="Search Query", value="ZeroGPU Hugging Face")
174
  s_btn = gr.Button("Search Web")
175
  s_out = gr.Markdown()
176
+ s_btn.click(fn=lambda q: asyncio.run(search_web(q)), inputs=q_in, outputs=s_out)
 
 
 
 
 
 
177
 
178
+ with gr.Tab("ZeroGPU Research"):
179
+ url_in = gr.Textbox(label="Target URL", value="https://news.ycombinator.com")
180
+ query_in = gr.Textbox(label="Research Question", value="Summarize top headlines")
181
  r_btn = gr.Button("Run ZeroGPU Analysis", variant="primary")
182
  r_out = gr.Markdown()
183
+ r_btn.click(fn=lambda u, q: asyncio.run(visual_chart_analysis_gpu(u, q)), inputs=[url_in, query_in], outputs=r_out)
184
 
185
  return demo
186
 
187
  # ---------------------------------------------------------
188
+ # 5. Unified FastAPI + FastMCP + Gradio Application
189
  # ---------------------------------------------------------
190
+ main_app = FastAPI()
191
+
192
+ # Get FastMCP SSE sub-application
193
+ mcp_sse_subapp = mcp.sse_app()
194
+
195
+ # Mount SSE and Message routes for MCP
196
+ main_app.mount("/sse", mcp_sse_subapp)
197
+ main_app.mount("/messages", mcp_sse_subapp)
198
+
199
+ # Mount Gradio UI at root path
200
+ demo_ui = build_gradio_ui()
201
+ main_app = gr.mount_gradio_app(main_app, demo_ui, path="/")
202
 
203
  if __name__ == "__main__":
204
+ # Run Unified FastAPI App on port 7860
205
+ uvicorn.run(main_app, host="0.0.0.0", port=7860)