Spaces:
Runtime error
Runtime error
Vaishnav14220
commited on
Commit
Β·
d474c53
1
Parent(s):
d90ea25
Add DeepSeek AI integration for reaction completion: API key input, automatic reaction completion using DeepSeek-V3.2-Exp, enhanced UI with AI options, and OpenAI dependency
Browse files- app.py +245 -57
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -6,8 +6,9 @@ import re
|
|
| 6 |
from functools import partial
|
| 7 |
from io import StringIO
|
| 8 |
from textwrap import dedent
|
| 9 |
-
from typing import List, Sequence, Tuple
|
| 10 |
from urllib.parse import quote_plus
|
|
|
|
| 11 |
|
| 12 |
import gradio as gr
|
| 13 |
import pandas as pd
|
|
@@ -788,19 +789,147 @@ def _render_smiles_to_svg(smiles_text: str) -> str | None:
|
|
| 788 |
return svg
|
| 789 |
|
| 790 |
|
| 791 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
reaction_text = (reaction_text or "").strip()
|
| 793 |
if not reaction_text:
|
| 794 |
return "", "β οΈ Enter a reaction SMILES/SMARTS string (e.g. CH4.O>>CO2)."
|
| 795 |
-
if ">>" not in reaction_text:
|
| 796 |
-
return "", "β οΈ Reaction input must include '>>' separating reactants and products."
|
| 797 |
|
| 798 |
-
|
| 799 |
-
if
|
| 800 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
|
| 802 |
-
|
| 803 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 804 |
|
| 805 |
|
| 806 |
def _extract_compounds_from_reaction(reaction_text: str) -> List[str]:
|
|
@@ -1218,14 +1347,35 @@ def build_interface() -> gr.Blocks:
|
|
| 1218 |
with thermo_accordion:
|
| 1219 |
thermo_data_display = gr.JSON(label="Raw Thermodynamic Data")
|
| 1220 |
|
| 1221 |
-
# Tab 3: Reaction SVG (Enhanced with NIST reactions)
|
| 1222 |
with gr.TabItem("Reaction SVG"):
|
| 1223 |
gr.Markdown(
|
| 1224 |
-
"π¨ **Render chemical reactions as SVG using RDKit**\n\n"
|
| 1225 |
-
"Choose from NIST database reactions
|
| 1226 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1227 |
)
|
| 1228 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1229 |
# NIST reactions dropdown
|
| 1230 |
nist_reactions = _fetch_all_nist_reactions(limit=200)
|
| 1231 |
nist_reaction_options = [("", "")] + nist_reactions if nist_reactions else []
|
|
@@ -1241,31 +1391,32 @@ def build_interface() -> gr.Blocks:
|
|
| 1241 |
)
|
| 1242 |
|
| 1243 |
reaction_input = gr.Textbox(
|
| 1244 |
-
label="Custom Reaction
|
| 1245 |
-
placeholder="
|
| 1246 |
-
lines=
|
| 1247 |
-
info="
|
| 1248 |
)
|
| 1249 |
|
| 1250 |
with gr.Column():
|
| 1251 |
render_mode = gr.Radio(
|
| 1252 |
label="Render Mode",
|
| 1253 |
-
choices=["Auto (
|
| 1254 |
-
value="Auto (
|
| 1255 |
-
info="Auto mode
|
| 1256 |
)
|
| 1257 |
|
| 1258 |
-
|
| 1259 |
-
label="
|
| 1260 |
-
choices=["
|
| 1261 |
-
value=["
|
| 1262 |
-
info="
|
| 1263 |
)
|
| 1264 |
|
| 1265 |
# Buttons
|
| 1266 |
with gr.Row():
|
| 1267 |
-
|
| 1268 |
-
|
|
|
|
| 1269 |
clear_btn = gr.Button("ποΈ Clear", variant="stop")
|
| 1270 |
|
| 1271 |
# Output
|
|
@@ -1285,54 +1436,91 @@ def build_interface() -> gr.Blocks:
|
|
| 1285 |
outputs=reaction_input,
|
| 1286 |
)
|
| 1287 |
|
| 1288 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1289 |
def render_nist_reaction(reaction_text, options):
|
| 1290 |
if not reaction_text:
|
| 1291 |
return "", "β οΈ Please select a reaction from the dropdown or enter a custom reaction."
|
| 1292 |
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
-
|
| 1300 |
-
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
return "", f"π¨ Error rendering reaction: {exc}"
|
| 1304 |
-
|
| 1305 |
-
# Render custom SMILES reaction
|
| 1306 |
-
def render_custom_reaction(reaction_text, options):
|
| 1307 |
if not reaction_text:
|
| 1308 |
return "", "β οΈ Please enter a reaction in SMILES/SMARTS format."
|
| 1309 |
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
|
| 1316 |
-
|
| 1317 |
-
|
| 1318 |
-
return "", f"β Could not parse reaction. Please check your SMILES/SMARTS format: {reaction_text[:100]}..."
|
| 1319 |
-
except Exception as exc:
|
| 1320 |
-
return "", f"π¨ Error rendering reaction: {exc}"
|
| 1321 |
|
| 1322 |
# Clear function
|
| 1323 |
def clear_outputs():
|
| 1324 |
return "", "", ""
|
| 1325 |
|
| 1326 |
# Button handlers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1327 |
render_nist_btn.click(
|
| 1328 |
fn=render_nist_reaction,
|
| 1329 |
-
inputs=[reaction_input,
|
| 1330 |
outputs=[reaction_svg_output, render_status],
|
| 1331 |
)
|
| 1332 |
|
| 1333 |
-
|
| 1334 |
-
fn=
|
| 1335 |
-
inputs=[reaction_input,
|
| 1336 |
outputs=[reaction_svg_output, render_status],
|
| 1337 |
)
|
| 1338 |
|
|
|
|
| 6 |
from functools import partial
|
| 7 |
from io import StringIO
|
| 8 |
from textwrap import dedent
|
| 9 |
+
from typing import List, Sequence, Tuple, Optional, Dict, Any
|
| 10 |
from urllib.parse import quote_plus
|
| 11 |
+
import json
|
| 12 |
|
| 13 |
import gradio as gr
|
| 14 |
import pandas as pd
|
|
|
|
| 789 |
return svg
|
| 790 |
|
| 791 |
|
| 792 |
+
def _complete_reaction_with_deepseek(partial_reaction: str, api_key: str) -> Optional[str]:
|
| 793 |
+
"""Use DeepSeek API to complete missing parts of a chemical reaction."""
|
| 794 |
+
if not api_key or not partial_reaction.strip():
|
| 795 |
+
return None
|
| 796 |
+
|
| 797 |
+
try:
|
| 798 |
+
from openai import OpenAI
|
| 799 |
+
|
| 800 |
+
client = OpenAI(
|
| 801 |
+
api_key=api_key,
|
| 802 |
+
base_url="https://api.deepseek.com",
|
| 803 |
+
)
|
| 804 |
+
|
| 805 |
+
system_prompt = """
|
| 806 |
+
You are a chemistry expert. The user will provide a partial chemical reaction (missing reactants or products).
|
| 807 |
+
Please complete the reaction by inferring the missing components based on chemical knowledge and reaction patterns.
|
| 808 |
+
|
| 809 |
+
Analyze the given reaction and determine what might be missing. Consider:
|
| 810 |
+
- Conservation of mass and atoms
|
| 811 |
+
- Common reaction types (combustion, substitution, addition, etc.)
|
| 812 |
+
- Chemical plausibility
|
| 813 |
+
- Radical reactions, ionic reactions, etc.
|
| 814 |
+
|
| 815 |
+
Output in JSON format with the completed reaction.
|
| 816 |
+
|
| 817 |
+
EXAMPLE INPUT:
|
| 818 |
+
CH4 + O2 β CO2
|
| 819 |
+
|
| 820 |
+
EXAMPLE OUTPUT:
|
| 821 |
+
{"completed_reaction": "CH4 + 2O2 β CO2 + 2H2O", "reasoning": "This is a combustion reaction requiring balanced oxygen and water as product"}
|
| 822 |
+
|
| 823 |
+
EXAMPLE INPUT:
|
| 824 |
+
C2H5β’ + H2 β
|
| 825 |
+
|
| 826 |
+
EXAMPLE OUTPUT:
|
| 827 |
+
{"completed_reaction": "C2H5β’ + H2 β C2H6 + Hβ’", "reasoning": "Hydrogen abstraction reaction where ethyl radical abstracts H from H2"}
|
| 828 |
+
"""
|
| 829 |
+
|
| 830 |
+
user_prompt = f"Complete this partial chemical reaction: {partial_reaction}"
|
| 831 |
+
|
| 832 |
+
messages = [
|
| 833 |
+
{"role": "system", "content": system_prompt},
|
| 834 |
+
{"role": "user", "content": user_prompt}
|
| 835 |
+
]
|
| 836 |
+
|
| 837 |
+
response = client.chat.completions.create(
|
| 838 |
+
model="deepseek-chat",
|
| 839 |
+
messages=messages,
|
| 840 |
+
response_format={'type': 'json_object'},
|
| 841 |
+
max_tokens=500,
|
| 842 |
+
temperature=0.1
|
| 843 |
+
)
|
| 844 |
+
|
| 845 |
+
result = json.loads(response.choices[0].message.content)
|
| 846 |
+
|
| 847 |
+
if "completed_reaction" in result:
|
| 848 |
+
return result["completed_reaction"]
|
| 849 |
+
|
| 850 |
+
except Exception as exc:
|
| 851 |
+
print(f"DeepSeek API error: {exc}")
|
| 852 |
+
return None
|
| 853 |
+
|
| 854 |
+
return None
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
def _analyze_reaction_completeness(reaction_text: str) -> Dict[str, Any]:
|
| 858 |
+
"""Analyze if a reaction is complete or needs completion."""
|
| 859 |
+
reaction_text = reaction_text.strip()
|
| 860 |
+
|
| 861 |
+
# Check for reaction arrow
|
| 862 |
+
has_arrow = any(arrow in reaction_text for arrow in ["β", "->", "β", "β"])
|
| 863 |
+
|
| 864 |
+
if not has_arrow:
|
| 865 |
+
return {"complete": False, "missing": "reaction arrow", "reason": "No reaction arrow found"}
|
| 866 |
+
|
| 867 |
+
# Split reaction
|
| 868 |
+
parts = None
|
| 869 |
+
for sep in [" β ", " -> ", " β ", " β "]:
|
| 870 |
+
if sep in reaction_text:
|
| 871 |
+
parts = reaction_text.split(sep, 1)
|
| 872 |
+
break
|
| 873 |
+
|
| 874 |
+
if not parts or len(parts) != 2:
|
| 875 |
+
return {"complete": False, "missing": "proper format", "reason": "Cannot parse reaction format"}
|
| 876 |
+
|
| 877 |
+
reactants_text, products_text = parts
|
| 878 |
+
|
| 879 |
+
# Check if reactants/products exist
|
| 880 |
+
reactants = [r.strip() for r in reactants_text.split("+") if r.strip()]
|
| 881 |
+
products = [p.strip() for p in products_text.split("+") if p.strip()]
|
| 882 |
+
|
| 883 |
+
if not reactants:
|
| 884 |
+
return {"complete": False, "missing": "reactants", "reason": "No reactants found"}
|
| 885 |
+
|
| 886 |
+
if not products:
|
| 887 |
+
return {"complete": False, "missing": "products", "reason": "No products found"}
|
| 888 |
+
|
| 889 |
+
# Basic completeness check
|
| 890 |
+
if len(reactants) >= 1 and len(products) >= 1:
|
| 891 |
+
return {"complete": True, "reactants": reactants, "products": products}
|
| 892 |
+
|
| 893 |
+
return {"complete": False, "missing": "components", "reason": "Insufficient reaction components"}
|
| 894 |
+
|
| 895 |
+
|
| 896 |
+
def render_reaction_svg(reaction_text: str, api_key: str = "", auto_complete: bool = False):
|
| 897 |
reaction_text = (reaction_text or "").strip()
|
| 898 |
if not reaction_text:
|
| 899 |
return "", "β οΈ Enter a reaction SMILES/SMARTS string (e.g. CH4.O>>CO2)."
|
|
|
|
|
|
|
| 900 |
|
| 901 |
+
# Check if it's already SMILES format (contains >>)
|
| 902 |
+
if ">>" in reaction_text:
|
| 903 |
+
svg = _render_smiles_to_svg(reaction_text)
|
| 904 |
+
if svg:
|
| 905 |
+
status = "β
Reaction rendered successfully from SMILES."
|
| 906 |
+
return svg, status
|
| 907 |
+
else:
|
| 908 |
+
return "", "π¨ Could not parse SMILES reaction format."
|
| 909 |
+
|
| 910 |
+
# If not SMILES and auto_complete is enabled, try to complete with DeepSeek
|
| 911 |
+
if auto_complete and api_key:
|
| 912 |
+
analysis = _analyze_reaction_completeness(reaction_text)
|
| 913 |
+
if not analysis["complete"]:
|
| 914 |
+
completed_reaction = _complete_reaction_with_deepseek(reaction_text, api_key)
|
| 915 |
+
if completed_reaction:
|
| 916 |
+
# Try to render the completed reaction
|
| 917 |
+
svg = _render_reaction_from_nist(completed_reaction)
|
| 918 |
+
if svg:
|
| 919 |
+
status = f"β
Reaction completed and rendered using DeepSeek AI.\nOriginal: {reaction_text}\nCompleted: {completed_reaction}"
|
| 920 |
+
return svg, status
|
| 921 |
+
else:
|
| 922 |
+
return "", f"π¨ DeepSeek completed reaction but rendering failed: {completed_reaction}"
|
| 923 |
+
else:
|
| 924 |
+
return "", f"π¨ Could not complete reaction with AI. Missing: {analysis.get('missing', 'unknown')}"
|
| 925 |
|
| 926 |
+
# Fallback: try NIST format rendering
|
| 927 |
+
svg = _render_reaction_from_nist(reaction_text)
|
| 928 |
+
if svg:
|
| 929 |
+
status = "β
Reaction rendered from NIST format."
|
| 930 |
+
return svg, status
|
| 931 |
+
|
| 932 |
+
return "", "π¨ Could not parse or render the reaction. Try SMILES format (reactants>>products) or enable AI completion."
|
| 933 |
|
| 934 |
|
| 935 |
def _extract_compounds_from_reaction(reaction_text: str) -> List[str]:
|
|
|
|
| 1347 |
with thermo_accordion:
|
| 1348 |
thermo_data_display = gr.JSON(label="Raw Thermodynamic Data")
|
| 1349 |
|
| 1350 |
+
# Tab 3: Reaction SVG (Enhanced with NIST reactions and AI completion)
|
| 1351 |
with gr.TabItem("Reaction SVG"):
|
| 1352 |
gr.Markdown(
|
| 1353 |
+
"π¨ **Render chemical reactions as SVG using RDKit + AI Completion**\n\n"
|
| 1354 |
+
"Choose from NIST database reactions, enter custom reactions, or let AI complete partial reactions!\n\n"
|
| 1355 |
+
"**Features:**\n"
|
| 1356 |
+
"- π§ͺ 200+ NIST database reactions\n"
|
| 1357 |
+
"- π€ AI-powered reaction completion (DeepSeek)\n"
|
| 1358 |
+
"- π¬ Multiple input formats (NIST, SMILES, SMARTS)\n"
|
| 1359 |
+
"- β‘ Automatic format detection and conversion"
|
| 1360 |
)
|
| 1361 |
|
| 1362 |
+
# API Key Configuration
|
| 1363 |
+
with gr.Accordion("π DeepSeek API Configuration", open=False):
|
| 1364 |
+
deepseek_api_key = gr.Textbox(
|
| 1365 |
+
label="DeepSeek API Key",
|
| 1366 |
+
placeholder="sk-...",
|
| 1367 |
+
type="password",
|
| 1368 |
+
info="Get your API key from https://platform.deepseek.com/"
|
| 1369 |
+
)
|
| 1370 |
+
gr.Markdown(
|
| 1371 |
+
"**How to get API key:**\n"
|
| 1372 |
+
"1. Visit https://platform.deepseek.com/\n"
|
| 1373 |
+
"2. Sign up/Login to your account\n"
|
| 1374 |
+
"3. Go to API Keys section\n"
|
| 1375 |
+
"4. Create a new API key\n"
|
| 1376 |
+
"5. Copy and paste it here"
|
| 1377 |
+
)
|
| 1378 |
+
|
| 1379 |
# NIST reactions dropdown
|
| 1380 |
nist_reactions = _fetch_all_nist_reactions(limit=200)
|
| 1381 |
nist_reaction_options = [("", "")] + nist_reactions if nist_reactions else []
|
|
|
|
| 1391 |
)
|
| 1392 |
|
| 1393 |
reaction_input = gr.Textbox(
|
| 1394 |
+
label="Custom Reaction Input",
|
| 1395 |
+
placeholder="Enter reaction in any format:\nNIST: CH4 + O2 β CO2 + H2O\nSMILES: CH4.O2>>CO2.H2O\nPartial: CH4 + O2 β (AI will complete)",
|
| 1396 |
+
lines=4,
|
| 1397 |
+
info="Supports NIST format, SMILES/SMARTS, or partial reactions"
|
| 1398 |
)
|
| 1399 |
|
| 1400 |
with gr.Column():
|
| 1401 |
render_mode = gr.Radio(
|
| 1402 |
label="Render Mode",
|
| 1403 |
+
choices=["Auto (detect format)", "Force NIST format", "Force SMILES/SMARTS"],
|
| 1404 |
+
value="Auto (detect format)",
|
| 1405 |
+
info="Auto mode intelligently detects and converts formats"
|
| 1406 |
)
|
| 1407 |
|
| 1408 |
+
ai_options = gr.CheckboxGroup(
|
| 1409 |
+
label="π€ AI Enhancement",
|
| 1410 |
+
choices=["Enable AI completion", "High quality rendering", "Show completion reasoning"],
|
| 1411 |
+
value=["Enable AI completion"],
|
| 1412 |
+
info="Use DeepSeek AI to complete partial reactions"
|
| 1413 |
)
|
| 1414 |
|
| 1415 |
# Buttons
|
| 1416 |
with gr.Row():
|
| 1417 |
+
render_auto_btn = gr.Button("π Smart Render (Auto-detect)", variant="primary")
|
| 1418 |
+
render_nist_btn = gr.Button("π§ͺ Render NIST Format", variant="secondary")
|
| 1419 |
+
render_smiles_btn = gr.Button("π¬ Render SMILES/SMARTS", variant="secondary")
|
| 1420 |
clear_btn = gr.Button("ποΈ Clear", variant="stop")
|
| 1421 |
|
| 1422 |
# Output
|
|
|
|
| 1436 |
outputs=reaction_input,
|
| 1437 |
)
|
| 1438 |
|
| 1439 |
+
# Smart auto-render function
|
| 1440 |
+
def render_auto_reaction(reaction_text, api_key, ai_options, render_mode):
|
| 1441 |
+
if not reaction_text:
|
| 1442 |
+
return "", "β οΈ Please enter a reaction or select from the NIST dropdown."
|
| 1443 |
+
|
| 1444 |
+
status_prefix = ""
|
| 1445 |
+
final_reaction = reaction_text
|
| 1446 |
+
|
| 1447 |
+
# Check if AI completion is enabled and reaction might be incomplete
|
| 1448 |
+
if "Enable AI completion" in (ai_options or []) and api_key:
|
| 1449 |
+
analysis = _analyze_reaction_completeness(reaction_text)
|
| 1450 |
+
if not analysis["complete"]:
|
| 1451 |
+
completed_reaction = _complete_reaction_with_deepseek(reaction_text, api_key)
|
| 1452 |
+
if completed_reaction:
|
| 1453 |
+
final_reaction = completed_reaction
|
| 1454 |
+
status_prefix = f"π€ **AI Completed Reaction**\nOriginal: {reaction_text}\nCompleted: {final_reaction}\n\n"
|
| 1455 |
+
|
| 1456 |
+
# Render based on detected format or forced mode
|
| 1457 |
+
if render_mode == "Force SMILES/SMARTS" or (render_mode == "Auto (detect format)" and ">>" in final_reaction):
|
| 1458 |
+
svg = _render_smiles_to_svg(final_reaction)
|
| 1459 |
+
render_type = "SMILES/SMARTS"
|
| 1460 |
+
elif render_mode == "Force NIST format" or render_mode == "Auto (detect format)":
|
| 1461 |
+
svg = _render_reaction_from_nist(final_reaction)
|
| 1462 |
+
render_type = "NIST format"
|
| 1463 |
+
else:
|
| 1464 |
+
# Try both formats
|
| 1465 |
+
svg = _render_reaction_from_nist(final_reaction)
|
| 1466 |
+
if not svg:
|
| 1467 |
+
svg = _render_smiles_to_svg(final_reaction)
|
| 1468 |
+
render_type = "auto-detected"
|
| 1469 |
+
|
| 1470 |
+
if svg:
|
| 1471 |
+
quality_note = " (High quality)" if "High quality rendering" in (ai_options or []) else ""
|
| 1472 |
+
status = f"{status_prefix}β
Successfully rendered as {render_type}{quality_note}"
|
| 1473 |
+
return svg, status
|
| 1474 |
+
else:
|
| 1475 |
+
return "", f"{status_prefix}β Could not render reaction. Try a different format or check syntax: {final_reaction[:100]}..."
|
| 1476 |
+
|
| 1477 |
+
# Legacy render functions (kept for compatibility)
|
| 1478 |
def render_nist_reaction(reaction_text, options):
|
| 1479 |
if not reaction_text:
|
| 1480 |
return "", "β οΈ Please select a reaction from the dropdown or enter a custom reaction."
|
| 1481 |
|
| 1482 |
+
svg = _render_reaction_from_nist(reaction_text)
|
| 1483 |
+
if svg:
|
| 1484 |
+
status = f"β
Successfully rendered NIST reaction: {reaction_text[:100]}..."
|
| 1485 |
+
if "High quality" in (options or []):
|
| 1486 |
+
status += " (High quality mode)"
|
| 1487 |
+
return svg, status
|
| 1488 |
+
else:
|
| 1489 |
+
return "", f"β Could not render reaction. The reaction format may not be supported by RDKit: {reaction_text[:100]}..."
|
| 1490 |
+
|
| 1491 |
+
def render_smiles_reaction(reaction_text, options):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1492 |
if not reaction_text:
|
| 1493 |
return "", "β οΈ Please enter a reaction in SMILES/SMARTS format."
|
| 1494 |
|
| 1495 |
+
svg = _render_smiles_to_svg(reaction_text)
|
| 1496 |
+
if svg:
|
| 1497 |
+
status = f"β
Successfully rendered SMILES reaction: {reaction_text[:100]}..."
|
| 1498 |
+
if "High quality" in (options or []):
|
| 1499 |
+
status += " (High quality mode)"
|
| 1500 |
+
return svg, status
|
| 1501 |
+
else:
|
| 1502 |
+
return "", f"β Could not parse reaction. Please check your SMILES/SMARTS format: {reaction_text[:100]}..."
|
|
|
|
|
|
|
|
|
|
| 1503 |
|
| 1504 |
# Clear function
|
| 1505 |
def clear_outputs():
|
| 1506 |
return "", "", ""
|
| 1507 |
|
| 1508 |
# Button handlers
|
| 1509 |
+
render_auto_btn.click(
|
| 1510 |
+
fn=render_auto_reaction,
|
| 1511 |
+
inputs=[reaction_input, deepseek_api_key, ai_options, render_mode],
|
| 1512 |
+
outputs=[reaction_svg_output, render_status],
|
| 1513 |
+
)
|
| 1514 |
+
|
| 1515 |
render_nist_btn.click(
|
| 1516 |
fn=render_nist_reaction,
|
| 1517 |
+
inputs=[reaction_input, ai_options],
|
| 1518 |
outputs=[reaction_svg_output, render_status],
|
| 1519 |
)
|
| 1520 |
|
| 1521 |
+
render_smiles_btn.click(
|
| 1522 |
+
fn=render_smiles_reaction,
|
| 1523 |
+
inputs=[reaction_input, ai_options],
|
| 1524 |
outputs=[reaction_svg_output, render_status],
|
| 1525 |
)
|
| 1526 |
|
requirements.txt
CHANGED
|
@@ -10,3 +10,4 @@ fastapi
|
|
| 10 |
uvicorn
|
| 11 |
plotly
|
| 12 |
pandas
|
|
|
|
|
|
| 10 |
uvicorn
|
| 11 |
plotly
|
| 12 |
pandas
|
| 13 |
+
openai
|