|
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_dotenv() |
|
|
|
|
|
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}") |
|
|
|
|
|
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 |
|
|
|
|
|
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: |
|
|
|
match = re.search(r'(\\documentclass.*?\\end{document})', latex_code, re.DOTALL) |
|
if match: |
|
content_to_render = match.group(1) |
|
else: |
|
|
|
match = re.search(r'(\\begin{tikzpicture}.*?\\end{tikzpicture})', latex_code, re.DOTALL) |
|
if match: |
|
|
|
content_to_render = f"""\\documentclass[border=5mm]{{standalone}} |
|
\\usepackage{{tikz}} |
|
\\usetikzlibrary{{shapes,arrows}} |
|
{match.group(0)} |
|
""" |
|
else: |
|
|
|
content_to_render = latex_code |
|
|
|
|
|
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}") |
|
|
|
|
|
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"): |
|
|
|
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> ') |
|
|
|
|
|
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. |
|
""" |
|
|
|
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 |
|
|
|
|
|
if consent: |
|
log_user_data(invention_disclosure) |
|
|
|
|
|
negotiator = AgenticNegotiator(invention_disclosure) |
|
|
|
final_state = None |
|
for state in negotiator.run_negotiation(): |
|
final_state = state |
|
|
|
|
|
negotiation_html = create_negotiation_transcript_display(state.negotiation_transcript) |
|
|
|
|
|
if state.negotiation_transcript: |
|
last_entry = state.negotiation_transcript[-1] |
|
status = f"Active Agent: {last_entry['agent']}" |
|
|
|
|
|
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" |
|
|
|
|
|
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}" |
|
|
|
|
|
if state.ideogram_image_b64 or state.figure_description: |
|
figures_section = "### Technical Figures\n\n" |
|
|
|
|
|
if state.ideogram_image_b64: |
|
figures_section += "### Conceptual Image (from Ideogram API)\n" |
|
image_md = f"" |
|
figures_section += image_md + "\n\n---\n\n" |
|
|
|
|
|
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"\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(""" |
|
<div style="background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%); |
|
border-bottom: 2px solid #00ff41; |
|
padding: 30px 20px; |
|
text-align: center; |
|
font-family: 'Courier New', monospace;"> |
|
<h1 style="font-size: 2.8em; |
|
margin: 0; |
|
text-shadow: 0 0 10px #00ff41; |
|
color: #00ff41; |
|
font-weight: 900; |
|
letter-spacing: 2px;"> |
|
PATENT ARCHITECT |
|
</h1> |
|
<p style="font-size: 1.2em; |
|
margin: 10px 0 0 0; |
|
color: #ffff00; |
|
text-shadow: 0 0 5px #ffff00; |
|
font-weight: 600;"> |
|
From Raw Idea to Strategized Patent Draft |
|
</p> |
|
</div> |
|
""") |
|
|
|
with gr.Row(variant="panel"): |
|
with gr.Column(scale=2, min_width=600): |
|
|
|
gr.Markdown(""" |
|
<div style="border: 1px solid #ffff00; padding: 20px; margin-top: 20px; box-shadow: 0 0 10px rgba(255,255,0,0.2);"> |
|
<h2 style="color: #ffff00; font-size: 1.5em; margin: 0 0 15px 0; text-transform: uppercase; letter-spacing: 1px;"> |
|
Step 1: Disclose Your Invention |
|
</h2> |
|
</div> |
|
""") |
|
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) |
|
|
|
|
|
gr.Markdown(""" |
|
<div style="border: 1px solid #ff8c00; padding: 20px; margin-top: 30px; box-shadow: 0 0 10px rgba(255,140,0,0.2);"> |
|
<h2 style="color: #ff8c00; font-size: 1.5em; margin: 0 0 15px 0; text-transform: uppercase; letter-spacing: 1px;"> |
|
Step 2: Observe the Agent Team |
|
</h2> |
|
</div> |
|
""") |
|
negotiation_display = gr.Markdown("The agent negotiation log will appear here once you begin.") |
|
|
|
with gr.Column(scale=1, min_width=300): |
|
|
|
gr.Markdown(""" |
|
<div style="border: 1px solid #00ff41; padding: 20px; margin-top: 20px; box-shadow: 0 0 10px rgba(0,255,65,0.2);"> |
|
<h2 style="color: #00ff41; font-size: 1.5em; margin: 0 0 15px 0; text-transform: uppercase; letter-spacing: 1px;"> |
|
System Status & Output |
|
</h2> |
|
</div> |
|
""") |
|
status_display = gr.Textbox( |
|
label="Current Status", |
|
interactive=False, |
|
value="Ready" |
|
) |
|
gr.Markdown(""" |
|
<h3 style="color: #00ff41; font-size: 1.3em; margin: 20px 0 15px 0; text-transform: uppercase;"> |
|
Step 3: Review Deliverables |
|
</h3> |
|
<p style="color: #ff8c00; font-size: 0.9em; margin-bottom: 15px;"> |
|
The final, strategy-aligned patent draft will appear below. |
|
</p> |
|
""") |
|
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("<hr style='border: 1px solid #00ff41; margin: 40px 0;'>") |
|
|
|
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(""" |
|
<style> |
|
.gradio-container { |
|
background: #0a0a0a !important; |
|
font-family: 'Courier New', monospace !important; |
|
max-width: 1400px !important; |
|
margin: auto !important; |
|
} |
|
.gradio-button.primary { |
|
background: linear-gradient(45deg, #00ff41, #ffff00) !important; |
|
border: 2px solid #00ff41 !important; |
|
color: #0a0a0a !important; |
|
font-weight: 900 !important; |
|
font-size: 1.2em !important; |
|
border-radius: 0 !important; |
|
text-transform: uppercase !important; |
|
letter-spacing: 2px !important; |
|
box-shadow: 0 0 15px #00ff41, inset 0 0 10px rgba(0,255,65,0.2) !important; |
|
font-family: 'Courier New', monospace !important; |
|
transition: all 0.3s ease !important; |
|
margin-top: 20px; |
|
} |
|
.gradio-button.primary:hover { |
|
background: linear-gradient(45deg, #ffff00, #ff8c00) !important; |
|
box-shadow: 0 0 25px #ffff00, inset 0 0 15px rgba(255,255,0,0.3) !important; |
|
border-color: #ffff00 !important; |
|
} |
|
.tab-nav button { |
|
background: linear-gradient(45deg, #1a1a1a, #0a0a0a) !important; |
|
border: 1px solid #00ff41 !important; |
|
color: #00ff41 !important; |
|
font-family: 'Courier New', monospace !important; |
|
text-transform: uppercase !important; |
|
letter-spacing: 1px !important; |
|
border-radius: 0 !important; |
|
} |
|
.tab-nav button.selected { |
|
background: linear-gradient(45deg, #00ff41, #ffff00) !important; |
|
color: #0a0a0a !important; |
|
box-shadow: 0 0 10px #00ff41 !important; |
|
} |
|
.gr-textbox, .gr-markdown { |
|
background: linear-gradient(45deg, #0a0a0a, #1a1a1a) !important; |
|
border: 1px solid #00ff41 !important; |
|
color: #00ff41 !important; |
|
font-family: 'Courier New', monospace !important; |
|
} |
|
.gr-textbox:focus, .gr-markdown:focus { |
|
border-color: #ffff00 !important; |
|
box-shadow: 0 0 10px #ffff00 !important; |
|
} |
|
.gr-info { |
|
color: #ff8c00 !important; |
|
} |
|
* { |
|
font-family: 'Courier New', monospace !important; |
|
} |
|
</style> |
|
""") |
|
|
|
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 |
|
) |