import gradio as gr import modal import json # --- Configuration --- MODAL_APP_NAME = "sitegeist-ai-app" def analyze_web_content(urls_json: str, deep_analysis: bool = False, analysis_prompt: str = "Summarize the content and identify key themes."): """ MCP Tool: Analyzes web content from one or more URLs. Performs deep analysis with marketing metrics if a single URL is provided and deep_analysis is True. Otherwise, performs a swarm analysis for multiple URLs or a single URL without deep_analysis. Args: urls_json (str): A JSON string representing a list of URLs. e.g., '["http://example.com", "http://another.com"]' deep_analysis (bool): If True and only one URL is provided, performs an in-depth analysis. analysis_prompt (str): The specific analysis to perform on the content. """ print(f"Received request: deep_analysis={deep_analysis}, 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: if len(urls) == 1 and deep_analysis: print(f"Calling Modal: deep_analyze_url for {urls[0]}") # Lookup the Modal function modal_deep_analyze = modal.Function.lookup(MODAL_APP_NAME, "deep_analyze_url") if modal_deep_analyze is None: return json.dumps({"status": "error", "message": f"Could not find Modal function 'deep_analyze_url' in app '{MODAL_APP_NAME}'."}) # Call the Modal function remotely result = modal_deep_analyze.remote(url=urls[0]) else: print(f"Calling Modal: swarm_analyze_urls for {len(urls)} URLs") # Lookup the Modal function 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}'."}) # Call the Modal function remotely result = modal_swarm_analyze.remote(urls=urls, analysis_prompt=analysis_prompt) return json.dumps(result, indent=2) # Return the result from Modal as a JSON string 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: {e}") return json.dumps({"status": "error", "message": f"An unexpected error occurred: {str(e)}"}) # --- Gradio Interface for Testing (if not using MCP directly) --- # This allows you to test the analyze_web_content function via a web UI. # You would typically expose `analyze_web_content` directly as an MCP tool. with gr.Blocks() as demo: gr.Markdown("# Sitegeist AI: Marketing & Content Intelligence Engine") gr.Markdown( "Enter URLs as a JSON list (e.g., `[\"http://url1.com\", \"http://url2.com\"]`). " "The Modal backend calls are mocked and will return predefined data." ) with gr.Row(): urls_input = gr.Textbox(label="URLs (JSON list)", placeholder='["https://example.com"]') deep_analysis_checkbox = gr.Checkbox(label="Perform Deep Analysis (for single URL)", value=False) analysis_prompt_input = gr.Textbox(label="Analysis Prompt", value="Summarize the content.") submit_button = gr.Button("Analyze Content") output_json = gr.JSON(label="Analysis Result") submit_button.click( analyze_web_content, inputs=[urls_input, deep_analysis_checkbox, analysis_prompt_input], outputs=output_json ) if __name__ == "__main__": # To run this Gradio app: python app.py # Ensure your Modal token is configured (`modal token set`) # And the Modal app (`modal_app.py`) is deployed (`modal deploy modal_app.py`) # or runnable locally if you are testing `modal run modal_app.py` in another terminal. # For the Gradio app to successfully call `modal.Function.lookup()`, # it generally expects the Modal app to be deployed, or you need to be # running within a `modal.stub.run()` context if calling local stubs # from another Modal process, which is more advanced. # The simplest way for this sketch is to deploy `modal_app.py` first. print("Attempting to launch Gradio demo...") print("REMINDER: For Gradio to connect to Modal functions,") print(f"1. Deploy 'modal_app.py' using 'modal deploy modal_app.py'.") print(f"2. Ensure your Modal token is set up.") print(f"3. The MODAL_APP_NAME ('{MODAL_APP_NAME}') in app.py must match the app name in modal_app.py.") demo.launch(mcp_server=True)