import gradio as gr import os import requests import json from typing import Dict, List, Generator, Optional from dotenv import load_dotenv import base64 import tempfile import re import urllib.parse from datetime import datetime # Load environment variables from .env file load_dotenv() # --- Data Logging (with consent) --- def log_user_data(invention_disclosure: str): """Logs the user's invention disclosure to a file if consent is given.""" try: with open("user_data_log.txt", "a") as f: log_entry = { "timestamp": datetime.now().isoformat(), "invention_disclosure": invention_disclosure } f.write(json.dumps(log_entry) + "\n") except Exception as e: print(f"Error logging user data: {e}") # --- New Agentic Backend Integration --- try: from real_ai_agents_implementation import AgenticNegotiator, NegotiationState AGENTIC_BACKEND_AVAILABLE = True except ImportError as e: print(f"Failed to import agentic backend: {e}") AGENTIC_BACKEND_AVAILABLE = False # Keep Gemini Image Generator for LaTeX compilation try: from gemini_image_generator import GeminiImageGenerator GEMINI_IMAGE_AVAILABLE = True except ImportError: GEMINI_IMAGE_AVAILABLE = False def compile_latex_to_image(latex_code: str) -> Optional[str]: """Try to compile LaTeX code to an image using a web service, with fallbacks.""" if not latex_code: return None content_to_render = None try: # A more robust regex to find the complete document, even with surrounding text. match = re.search(r'(\\documentclass.*?\\end{document})', latex_code, re.DOTALL) if match: content_to_render = match.group(1) else: # Fallback for tikzpicture only if full document not found match = re.search(r'(\\begin{tikzpicture}.*?\\end{tikzpicture})', latex_code, re.DOTALL) if match: # If only tikz is found, wrap it in a standard document class content_to_render = f"""\\documentclass[border=5mm]{{standalone}} \\usepackage{{tikz}} \\usetikzlibrary{{shapes,arrows}} {match.group(0)} """ else: # If no specific block is found, take the whole code and hope for the best content_to_render = latex_code # --- Attempt 1: codecogs API (fast but limited) --- try: encoded_latex = urllib.parse.quote(content_to_render) api_url = f"https://latex.codecogs.com/png.latex?{encoded_latex}" response = requests.get(api_url, timeout=15) if response.status_code == 200 and response.headers.get('content-type', '').startswith('image/'): image_b64 = base64.b64encode(response.content).decode('utf-8') return f"data:image/png;base64,{image_b64}" else: print(f"LaTeX compilation (codecogs) failed with status: {response.status_code}") except Exception as e: print(f"An error occurred during codecogs LaTeX compilation: {e}") # --- Attempt 2: A more robust secondary API (slower but more capable) --- print("Codecogs failed, trying secondary LaTeX service...") try: api_url_secondary = "https://latex.ytotech.com/api/v1" payload = { "latex": content_to_render, "format": "png", "quality": 85, "density": 300 } response = requests.post(api_url_secondary, json=payload, timeout=30) if response.status_code == 200: response_data = response.json() if response_data.get("status") == "success" and response_data.get("result"): # The result is already base64 encoded image_b64 = response_data["result"] return f"data:image/png;base64,{image_b64}" else: print(f"LaTeX compilation (secondary) failed with status: {response.status_code}") return None except Exception as e: print(f"An error occurred during secondary LaTeX compilation: {e}") return None except Exception as e: print(f"A critical error occurred during LaTeX preparation: {e}") return None def create_negotiation_transcript_display(transcript: List[Dict]) -> str: """Creates a markdown display for the agent negotiation transcript.""" if not transcript: return "### Agent Negotiation Log\n\n*Awaiting instructions...*" markdown = "### Agent Negotiation Log\n\n---\n" for entry in transcript: agent = entry.get('agent', 'System') message = entry.get('message', '').replace('\n', '\n> ') # Indent multiline messages # Using markdown emphasis and blockquotes instead of raw HTML markdown += f"\n\n**🤖 {agent}:**\n" markdown += f"> {message}\n\n" markdown += "---" return markdown def run_patent_architect_in_ui(invention_disclosure: str, consent: bool) -> Generator[List, None, None]: """ This is the new main UI function that runs the true agentic workflow. It no longer calls an external backend. """ # Initial state for all output fields negotiation_html = create_negotiation_transcript_display([]) prior_art_section = "### Prior Art Analysis\n\n*Awaiting agent analysis...*" summary_section = "### Invention Summary\n\n*Awaiting agent analysis...*" figures_section = "### Technical Figures\n\n*Awaiting agent analysis...*" claims_section = "### Patent Claims\n\n*Awaiting agent analysis...*" status = "System Initialized." yield [negotiation_html, prior_art_section, summary_section, figures_section, claims_section, status] if not invention_disclosure: status = "Please provide an invention disclosure." yield [negotiation_html, prior_art_section, summary_section, figures_section, claims_section, status] return if not AGENTIC_BACKEND_AVAILABLE: status = "ERROR: The agentic backend is not available. Check server logs." negotiation_html = create_negotiation_transcript_display([{"agent": "System", "message": "CRITICAL ERROR: Could not import `AgenticNegotiator`. The application cannot run."}]) yield [negotiation_html, prior_art_section, summary_section, figures_section, claims_section, status] return # --- Log data only if consent is given --- if consent: log_user_data(invention_disclosure) # --- Start the Real Agentic Workflow --- negotiator = AgenticNegotiator(invention_disclosure) final_state = None for state in negotiator.run_negotiation(): final_state = state # Update negotiation transcript negotiation_html = create_negotiation_transcript_display(state.negotiation_transcript) # Update status from the last message if state.negotiation_transcript: last_entry = state.negotiation_transcript[-1] status = f"Active Agent: {last_entry['agent']}" # As prior art becomes available, display it if state.prior_art_analysis: pa_data = state.prior_art_analysis prior_art_section = f"### Prior Art & Strategy\n\n" if state.strategic_mandate: prior_art_section += f"**Strategic Mandate:**\n> {state.strategic_mandate}\n\n---\n\n" prior_art_section += f"**Landscape Summary (from live web search):**\n> {pa_data.get('landscape_summary', 'N/A')}\n\n" prior_art_section += f"**Key Concepts Searched:**\n" for concept in pa_data.get('key_concepts', []): prior_art_section += f"- {concept}\n" prior_art_section += "\n**Representative Prior Art Found:**\n" for art in pa_data.get('real_prior_art', []): prior_art_section += f"- **[{art.get('title', 'Link')}]({art.get('link', '#')})**\n" # As sections get generated, display them if state.technical_summary: summary_section = f"### Invention Summary\n\n{state.technical_summary}" if state.patent_claims: claims_section = f"### Patent Claims\n\n{state.patent_claims}" # Handle both Ideogram image and LaTeX figure description if state.ideogram_image_b64 or state.figure_description: figures_section = "### Technical Figures\n\n" # Display the Ideogram image if it exists if state.ideogram_image_b64: figures_section += "### Conceptual Image (from Ideogram API)\n" image_md = f"![Generated Conceptual Image](data:image/jpeg;base64,{state.ideogram_image_b64})" figures_section += image_md + "\n\n---\n\n" # Display the LaTeX figure description and compiled image if it exists if state.figure_description: figures_section += "### Technical Figure (from LaTeX)\n" compiled_latex_image_uri = compile_latex_to_image(state.figure_description) if compiled_latex_image_uri: figures_section += f"![Compiled LaTeX Figure]({compiled_latex_image_uri})\n\n" else: figures_section += "*LaTeX compilation failed. Displaying raw code.*\n\n" figures_section += "#### Raw LaTeX/TikZ Code:\n" figures_section += state.figure_description yield [negotiation_html, prior_art_section, summary_section, figures_section, claims_section, status] status = "Agentic Negotiation Complete." yield [negotiation_html, prior_art_section, summary_section, figures_section, claims_section, status] with gr.Blocks(theme=gr.themes.Base(primary_hue="green", neutral_hue="slate"), title="Patent Architect") as demo: gr.HTML("""

PATENT ARCHITECT

From Raw Idea to Strategized Patent Draft

""") with gr.Row(variant="panel"): with gr.Column(scale=2, min_width=600): # --- STEP 1: INPUT --- gr.Markdown("""

Step 1: Disclose Your Invention

""") invention_input = gr.Textbox( lines=10, label="Invention Disclosure", placeholder="Describe the core of your invention. Focus on what it does and what makes it novel. The AI agent team will analyze the technical fundamentals.", info="Provide a clear, concise description. Details are good, but the core concept is essential." ) with gr.Row(elem_classes="consent-row"): consent_checkbox = gr.Checkbox( label="I agree to let the developer collect my anonymized invention data to improve this tool.", value=False, interactive=True, info="We will only store the invention text and a timestamp. No personal data is collected. See our Privacy Policy." ) generate_btn = gr.Button("Develop Patent Strategy", variant="primary", size="lg", interactive=False) # --- STEP 2: LIVE ANALYSIS --- gr.Markdown("""

Step 2: Observe the Agent Team

""") negotiation_display = gr.Markdown("The agent negotiation log will appear here once you begin.") with gr.Column(scale=1, min_width=300): # --- SIDEBAR: STATUS & DELIVERABLES --- gr.Markdown("""

System Status & Output

""") status_display = gr.Textbox( label="Current Status", interactive=False, value="Ready" ) gr.Markdown("""

Step 3: Review Deliverables

The final, strategy-aligned patent draft will appear below.

""") with gr.Tabs(): with gr.TabItem("Prior Art & Strategy"): prior_art_output = gr.Markdown( value="*The Prior Art Detective's findings and the Chief Strategist's resulting mandate will be shown here.*" ) with gr.TabItem("Technical Summary"): summary_output = gr.Markdown( value="*The Technical Writer's summary, aligned with the final strategy, will appear here.*" ) with gr.TabItem("Technical Figures"): figures_output = gr.Markdown( value="*A conceptual image from the Ideogram API and a technical figure from the Figure Drafter will appear here.*" ) with gr.TabItem("Patent Claims"): claims_output = gr.Markdown( value="*The Claims Drafter's claims, focused on the strategic mandate, will appear here.*" ) gr.HTML("
") gr.Examples( [ ["A smart coffee mug using phase-change materials and a machine learning algorithm that learns user habits to optimize temperature and energy use."], ["A modular vertical farming system with AI-controlled, adaptive full-spectrum LED lighting based on real-time plant reflectance spectra analysis."], ["Non-invasive glucose monitoring using Raman spectroscopy."], ], inputs=[invention_input], label="Invention Examples (click one to get started)" ) consent_checkbox.change(lambda x: gr.update(interactive=x), consent_checkbox, generate_btn) generate_btn.click( fn=run_patent_architect_in_ui, inputs=[invention_input, consent_checkbox], outputs=[negotiation_display, prior_art_output, summary_output, figures_output, claims_output, status_display] ) gr.HTML(""" """) if __name__ == "__main__": print("PATENT ARCHITECT - True Agentic Negotiation Workflow") print(f"Agentic Backend: {'ONLINE' if AGENTIC_BACKEND_AVAILABLE else 'OFFLINE'}") if not os.getenv("GEMINI_API_KEY"): print("⚠️ WARNING: GEMINI_API_KEY environment variable not set. Gemini-powered agents will not function.") if not os.getenv("SEGMIND_API_KEY"): print("⚠️ WARNING: SEGMIND_API_KEY environment variable not set. Ideogram image generation will not function.") demo.queue().launch( share=False, show_error=True )