-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4e71e9e
commit f047c70
Showing
18 changed files
with
2,651 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = ""; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
``` |
Oops, something went wrong.