Skip to content

Commit

Permalink
Add Web interface
Browse files Browse the repository at this point in the history
  • Loading branch information
n-yoshikawa committed Jul 18, 2023
1 parent 4e71e9e commit f047c70
Show file tree
Hide file tree
Showing 18 changed files with 2,651 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
This repository contains
- The source code for CLAIRify
- Dataset (Chem-RnD and Chem-EDU)
- CLAIRify web interface

### Requirement
- OpenAI Python Library
Expand Down
29 changes: 29 additions & 0 deletions web-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# CLAIRify Web Interface
This directory contains the source code for CLAIRify Web interface

## Dependencies
- [OpenAI Python Library](https://pypi.org/project/openai/) 0.27.8
- [Flask](https://flask.palletsprojects.com/) 2.2.2
- [Flask SocketIO](https://flask-socketio.readthedocs.io/) 5.3.4

You can install dependencies by

```bash
pip install -r requirements.txt
```

## Getting Started
1. Set `OPENAI_API_KEY` environment variable. (cf. [Best Practices for API Key Safety](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety))
2. Start the server
```bash
python main.py
```
3. Open `http://127.0.0.1:3000/` in your favorite browser.

## Usage
![](/images/screenshot.png)

1. Type your experiment in the left column.
2. Click "Translate" button.
3. You will see an execution log while translation
4. Final output XDL will be automatically shown after translation is done.
107 changes: 107 additions & 0 deletions web-interface/XDL_description.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
XDL files will follow XML syntax and consist of three mandatory sections: Hardware, where virtual vessels that the reaction mixture can reside in are declared. Reagents, where all reagents that are used in the procedure are declared, and Procedure, where the synthetic actions involved in the procedure are linearly declared.

XDL File Stub:
<XDL>
<Synthesis>
<Hardware>
<!-- ... -->
</Hardware>

<Reagents>
<!-- ... -->
</Reagents>

<Procedure>
<!-- ... -->
</Procedure>
</Synthesis>
</XDL>

Hardware:
Each individual reagent, unless otherwise stated should be contained within their own component.

(format is(Property, Type, Description))

id, str, Name of hardware

Reagents:
The Reagents section contains Reagent elements with the props below.
Any reagents which were combined before the experiment should be combined as one reagent before the procedure. (i.e. 'lime juice mixed with sugar' = <Reagent name='lime juice mixed with sugar')

Reagent:
Reagent used by procedure.

(format is(Property, Type, Description))

name, str, Name of reagent

Procedure:
All steps included in the Full Steps Specification may be given within the Procedure block of a XDL file. Additionally, the Procedure block may be, but does not have to be, divided up into Prep, Reaction, Workup and Purification blocks, each of which can contain any of the steps in the specification.


Here is a list of tags that can be used in this language:
Liquid Handling: Add, Separate, Transfer,
Stirring: StartStir, Stir, StopStir,
Temperature Control: HeatChill, HeatChillToTemp, StartHeatChill, StopHeatChill
Inert Gas: EvacuateAndRefill, Purge, StartPurge, StopPurge
Filtration: Filter, FilterThrough, WashSolid
Special: Wait, Repeat,
Other: CleanVessel, Crystallize, Dissolve, Dry, Evaporate, Irradiate, Precipitate, ResetHandling, RunColumn

Steps:
Liquid Handling:
Add liquid or solid reagent. Reagent identity (ie liquid or solid) is determined by the solid property of a reagent in the Reagent section.

The quantity of the reagent can be specified using either volume (liquid units) or amount (all accepted units e.g. ‘g’, ‘mL’, ‘eq’, ‘mmol’).

format(Property Type Description)
vessel vessel Vessel to add reagent to.
reagent reagent Reagent to add.

Separate:
Perform separation.
format(Property Type Description)
Property Type Description
purpose str 'wash' or 'extract'. 'wash' means that product phase will not be the added solvent phase, 'extract' means product phase will be the added solvent phase. If no solvent is added just use 'extract'.
product_phase str 'top' or 'bottom'. Phase that product will be in.
from_vessel vessel Contents of from_vessel are transferred to separation_vessel and separation is performed.
separation_vessel vessel Vessel in which separation of phases will be carried out.
to_vessel vessel Vessel to send product phase to.


Transfer:
Transfer liquid from one vessel to another.

The quantity to transfer can be specified using either volume (liquid units) or amount (all accepted units e.g. ‘g’, ‘mL’, ‘eq’, ‘mmol’).

format(Property Type Description)
from_vessel vessel Vessel to transfer liquid from.
to_vessel vessel Vessel to transfer liquid to.


Stirring:
StartStir:
Start stirring vessel.
format(Property Type Description)
vessel vessel Vessel to start stirring.


Stir:
Stir vessel for given time.
format(Property Type Description)
vessel vessel Vessel to stir.
time float Time to stir vessel for.


StopStir:
Stop stirring given vessel.
format(Property Type Description)
vessel vessel Vessel to stop stirring.

Temperature Control:
HeatChill:
Heat or chill vessel to given temp for given time.
format(Property Type Description)
vessel vessel Vessel to heat or chill.
temp float Temperature to heat or chill vessel to.
time float Time to heat or chill vessel for.
Binary file added web-interface/images/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 121 additions & 0 deletions web-interface/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import json
import os
from threading import Lock, Thread

import openai
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit

from verify import verify_xdl


def prompt(instructions, description, max_tokens):
"""Function that calls the OpenAI API"""
response = openai.Completion.create(
model="text-davinci-003",
prompt=description + "\nConvert to XDL:\n" + instructions,
temperature=0,
max_tokens=max_tokens,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
)
return response["choices"][0]["text"]


def translate(input_xdl):
"""Function that translates the input XDL"""

# Get API key
openai.api_key = os.environ["OPENAI_API_KEY"]

# Get XDL description
with open("XDL_description.txt", "r") as f:
XDL_description = f.read()

correct_syntax = False
errors = {}
prev_input_xdl = input_xdl

# Start 10 iteration for loop to limit token usage
for step in range(10):
socketio.emit("message", f"Convert to XDL: {input_xdl}")
try:
gpt3_output = prompt(input_xdl, XDL_description, 1000)
except:
socketio.emit(
"message", "Error. Too many tokens required or invalid API key.")
break

socketio.emit("message", "gpt3_output:::")
socketio.emit("message_xdl", f"{gpt3_output}")

if "<XDL>" in gpt3_output:
gpt3_output = gpt3_output[gpt3_output.index("<XDL>"):]
compile_correct = verify_xdl(gpt3_output)
errors[step] = {
"errors": compile_correct,
"input_xdl": input_xdl,
"gpt3_output": gpt3_output,
}
if not compile_correct:
correct_syntax = True
break
else:
error_list = set()
for item in compile_correct:
for error in item["errors"]:
error_list.add(error)
error_message = f"\n{gpt3_output}\nThis XDL was not correct. These were the errors\n{os.linesep.join(list(error_list))}\nPlease fix the errors."
input_xdl = f"{prev_input_xdl} {error_message}"

else:
error_message = f"\n{gpt3_output}\nThis XDL was not correct. XDL should start with <XDL>. Please fix the errors."
input_xdl = f"{prev_input_xdl} {error_message}"

try:
if correct_syntax:
xdl = gpt3_output
else:
xdl = "The correct XDL could not be generated."

except Exception as e:
socketio.emit("message", f"Error: {e}")

socketio.emit("message", f"XDL: {xdl}")
socketio.emit("correct_xdl", f"{xdl}")
socketio.emit("message", f"Final syntax valid: {correct_syntax}")


# Flask app
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(32)
app.config["SESSION_COOKIE_SECURE"] = True
socketio = SocketIO(app)

# Global variables
thread = None
thread_lock = Lock()
input_xdl = ""


def run_translation(input_xdl):
"""Function that runs the translation in a separate thread."""
global thread
with thread_lock:
thread = Thread(target=translate, args=(input_xdl,))
thread.start()


@app.route("/", methods=["GET", "POST"])
def index():
"""Function that renders the index page."""
global input_xdl
if request.method == "POST":
input_xdl = request.form["input_field"]
run_translation(input_xdl)

return render_template("index.html", input_xdl=input_xdl)


socketio.run(app, port=3000)
3 changes: 3 additions & 0 deletions web-interface/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Flask==2.2.2
Flask_SocketIO==5.3.4
openai==0.27.8
Binary file added web-interface/static/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions web-interface/static/img/microphone-342.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions web-interface/static/myscript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const button = document.getElementById("submit_button");
const tab2 = document.getElementById("secondaryOpen");
if (button.value == "Translate") {
button.addEventListener("click", (event) => {
button.value = "Running Translation...";
tab2.click();
output_xdl.innerHTML = "";
});

}


function copyClipboard() {
const copyText = document.getElementsByTagName("code")[0].innerText;
navigator.clipboard.writeText(copyText);
}


const formContainer = document.getElementById('input');
const tabContainer = document.querySelector('.tab-container');

if (window.innerWidth < 750) {
document.getElementById("tertiaryOpen").hidden = false;

tabContainer.insertBefore(formContainer, tabContainer.children[1]);
} else {
formContainer.className = "";
}
27 changes: 27 additions & 0 deletions web-interface/static/prism/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## LICENSE

This directory contains a part of [prism](https://github.com/PrismJS/prism) released under the following license.

```
MIT LICENSE
Copyright (c) 2012 Lea Verou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```
Loading

0 comments on commit f047c70

Please sign in to comment.