SitegeistAI / app.py
Alejandro Ardila
Added more compelling text to the Gradio MCP Server UI
1d5c5a0
Raw
History Blame Contribute Delete
9.29 kB
import gradio as gr
import modal
import os
import json
# --- Configuration ---
MODAL_APP_NAME = "sitegeist-ai-app"
# Import the Modal app from the package
try:
from sitegeist_core import app as modal_app
except ImportError:
# Fallback for when package is not available locally
modal_app = None
print("Warning: sitegeist_core package not found. Modal functions may not be available locally.")
# --- Gradio MCP Tool Definitions ---
def analyze_specific_urls(urls_json: str, analysis_prompt: str = "Summarize the content and identify key themes."):
"""
MCP Tool: Analyzes web content from a specific list of URLs using swarm analysis. Is useful for finding content related to a specific topic and compare the content with other URLs.
Args:
urls_json (str): A JSON string representing a list of URLs. e.g., '["http://example.com", "http://another.com"]'
analysis_prompt (str): The specific analysis to perform on the content.
"""
print(f"Tool 'analyze_specific_urls' received: prompt='{analysis_prompt}', urls_json='{urls_json}'")
try:
urls = json.loads(urls_json)
if not isinstance(urls, list) or not all(isinstance(url, str) for url in urls):
raise ValueError("Input must be a JSON string of a list of URLs.")
if not urls:
return json.dumps({"status": "error", "message": "URL list cannot be empty."})
except json.JSONDecodeError:
return json.dumps({"status": "error", "message": "Invalid JSON format for URLs."})
except ValueError as ve:
return json.dumps({"status": "error", "message": str(ve)})
result = None
try:
print(f"Calling Modal: swarm_analyze_urls for {len(urls)} URLs")
modal_swarm_analyze = modal.Function.lookup(MODAL_APP_NAME, "swarm_analyze_urls")
if modal_swarm_analyze is None:
return json.dumps({"status": "error", "message": f"Could not find Modal function 'swarm_analyze_urls' in app '{MODAL_APP_NAME}'."})
result = modal_swarm_analyze.remote(urls=urls, analysis_prompt=analysis_prompt)
return json.dumps(result, indent=2)
except modal.exception.NotFoundError as e:
print(f"Modal function not found: {e}")
return json.dumps({"status": "error", "message": f"Modal function lookup failed. Ensure '{MODAL_APP_NAME}' is deployed and functions are correctly named. Details: {e}"})
except Exception as e:
print(f"An unexpected error occurred in 'analyze_specific_urls': {e}")
return json.dumps({"status": "error", "message": f"An unexpected error occurred: {str(e)}"})
def discover_and_analyze_web(search_prompt: str, num_urls: int = 100, analysis_prompt_override: str = None):
"""
MCP Tool: Discovers relevant URLs based on a search prompt using web search search and then analyzes as Marketing & Content Intelligence Engine assistant.
The analysis uses the swarm mode and is useful for finding content related to a specific topic including SEO keywords.
Args:
search_prompt (str): The query to search for.
num_urls (int): The number of URLs to discover (max 200, uses pagination for >50 results).
analysis_prompt_override (str, optional): Specific prompt for the analysis phase.
If None, a default based on search_prompt is used.
"""
print(f"Tool 'discover_and_analyze_web' received: search_prompt='{search_prompt}', num_urls={num_urls}, analysis_prompt_override='{analysis_prompt_override}'")
if not search_prompt:
return json.dumps({"status": "error", "message": "Search prompt cannot be empty."})
# Ensure num_urls is within a reasonable range for Tavily search
num_urls = max(1, min(int(num_urls), 200)) # Cap at 200, uses pagination for >50 results
# 1. Discover URLs (Using Tavily via Modal)
try:
print(f"Calling Modal: tavily_search_engine for query '{search_prompt}'")
modal_tavily_search = modal.Function.lookup(MODAL_APP_NAME, "tavily_search_engine")
if modal_tavily_search is None:
return json.dumps({"status": "error", "message": f"Could not find Modal function 'tavily_search_engine' in app '{MODAL_APP_NAME}'."})
discovered_urls = modal_tavily_search.remote(query=search_prompt, num_results=num_urls)
if not discovered_urls:
return json.dumps({"status": "error", "message": "Search returned no URLs."})
except modal.exception.NotFoundError as e:
print(f"Modal function not found: {e}")
return json.dumps({"status": "error", "message": f"Modal function lookup failed. Ensure '{MODAL_APP_NAME}' is deployed and tavily_search_engine function exists. Details: {e}"})
except Exception as e:
print(f"An unexpected error occurred in tavily search: {e}")
return json.dumps({"status": "error", "message": f"An unexpected error occurred during search: {str(e)}"})
# 2. Determine the analysis prompt for the swarm analysis
current_analysis_prompt = analysis_prompt_override if analysis_prompt_override else f"Analyze content related to the search query: '{search_prompt}'"
print(f"Using analysis prompt for swarm: '{current_analysis_prompt}'")
# 3. Call swarm_analyze_urls Modal function
try:
print(f"Calling Modal: swarm_analyze_urls for {len(discovered_urls)} discovered URLs")
modal_swarm_analyze = modal.Function.lookup(MODAL_APP_NAME, "swarm_analyze_urls")
if modal_swarm_analyze is None:
return json.dumps({"status": "error", "message": f"Could not find Modal function 'swarm_analyze_urls' in app '{MODAL_APP_NAME}'."})
result = modal_swarm_analyze.remote(urls=discovered_urls, analysis_prompt=current_analysis_prompt)
return json.dumps(result, indent=2)
except modal.exception.NotFoundError as e:
print(f"Modal function not found: {e}")
return json.dumps({"status": "error", "message": f"Modal function lookup failed. Ensure '{MODAL_APP_NAME}' is deployed. Details: {e}"})
except Exception as e:
print(f"An unexpected error occurred in 'discover_and_analyze_web': {e}")
return json.dumps({"status": "error", "message": f"An unexpected error occurred during discovery/analysis: {str(e)}"})
# --- Gradio Interface ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 👻 SitegeistAI - Your On-Demand Marketing Strategist")
gr.Markdown("Stop drowning in data. SitegeistAI transforms the web into your competitive advantage. "
"Instantly analyze competitor pages to uncover their strategy, or unleash our AI to discover and distill key insights from top-ranking content on any topic. "
"Go from question to actionable intelligence in seconds.")
with gr.Tabs():
with gr.TabItem("Deep-Dive Analysis"):
gr.Markdown("## Analyze Your Competition")
gr.Markdown(
"Paste in competitor URLs, product pages, or key articles to instantly extract their core strategies, themes, and talking points."
)
specific_urls_input = gr.Textbox(label="Target URLs", placeholder='["https://competitor.com/pricing", "https://competitor.com/blog/big-announcement"]')
specific_analysis_prompt_input = gr.Textbox(label="What insights do you need?", value="Identify the primary calls-to-action and target audience.")
specific_submit_button = gr.Button("Run Deep-Dive Analysis")
specific_output_json = gr.JSON(label="Your Intelligence Report")
specific_submit_button.click(
analyze_specific_urls,
inputs=[specific_urls_input, specific_analysis_prompt_input],
outputs=specific_output_json
)
with gr.TabItem("Market & Trend Discovery"):
gr.Markdown("## Master Any Topic")
gr.Markdown(
"Want to dominate a new market or understand the latest trends? "
"Enter any topic, and SitegeistAI will find the most relevant online content and deliver a complete strategic summary."
)
discover_search_prompt_input = gr.Textbox(label="Topic to Research", placeholder="e.g., emerging strategies for B2B SaaS marketing")
discover_num_urls_input = gr.Number(label="Number of Sources to Analyze", value=10, minimum=1, maximum=200, step=1, info="We'll find and analyze the top results for your topic.")
discover_analysis_prompt_input = gr.Textbox(
label="Refine Your Analysis (Optional)",
placeholder="Focus on summarizing the common advice for beginners."
)
discover_submit_button = gr.Button("Discover & Generate Insights")
discover_output_json = gr.JSON(label="Your Market Overview")
discover_submit_button.click(
discover_and_analyze_web,
inputs=[discover_search_prompt_input, discover_num_urls_input, discover_analysis_prompt_input],
outputs=discover_output_json
)
if __name__ == "__main__":
print("Attempting to launch Gradio hackaton demo ...")
demo.launch(mcp_server=True)