ivoryos 0.1.9__py3-none-any.whl → 0.1.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +122 -99
- ivoryos/config.py +47 -47
- ivoryos/routes/auth/auth.py +100 -65
- ivoryos/routes/auth/templates/auth/login.html +25 -25
- ivoryos/routes/auth/templates/auth/signup.html +32 -32
- ivoryos/routes/control/control.py +400 -272
- ivoryos/routes/control/templates/control/controllers.html +75 -75
- ivoryos/routes/control/templates/control/controllers_home.html +50 -50
- ivoryos/routes/control/templates/control/controllers_new.html +89 -89
- ivoryos/routes/database/database.py +188 -114
- ivoryos/routes/database/templates/database/experiment_database.html +72 -72
- ivoryos/routes/design/design.py +543 -416
- ivoryos/routes/design/templates/design/experiment_builder.html +415 -415
- ivoryos/routes/design/templates/design/experiment_run.html +325 -325
- ivoryos/routes/main/main.py +42 -25
- ivoryos/routes/main/templates/main/help.html +141 -141
- ivoryos/routes/main/templates/main/home.html +68 -68
- ivoryos/static/.DS_Store +0 -0
- ivoryos/static/js/overlay.js +12 -12
- ivoryos/static/js/socket_handler.js +34 -34
- ivoryos/static/js/sortable_card.js +24 -24
- ivoryos/static/js/sortable_design.js +36 -36
- ivoryos/static/style.css +201 -201
- ivoryos/templates/base.html +143 -143
- ivoryos/utils/db_models.py +544 -518
- ivoryos/utils/form.py +328 -316
- ivoryos/utils/global_config.py +67 -67
- ivoryos/utils/llm_agent.py +183 -183
- ivoryos/utils/script_runner.py +166 -164
- ivoryos/utils/utils.py +437 -422
- ivoryos/version.py +1 -0
- {ivoryos-0.1.9.dist-info → ivoryos-0.1.12.dist-info}/LICENSE +21 -21
- {ivoryos-0.1.9.dist-info → ivoryos-0.1.12.dist-info}/METADATA +170 -169
- ivoryos-0.1.12.dist-info/RECORD +47 -0
- {ivoryos-0.1.9.dist-info → ivoryos-0.1.12.dist-info}/WHEEL +1 -1
- ivoryos-0.1.9.dist-info/RECORD +0 -45
- {ivoryos-0.1.9.dist-info → ivoryos-0.1.12.dist-info}/top_level.txt +0 -0
ivoryos/utils/global_config.py
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
# from ivoryos.utils.script_runner import ScriptRunner
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class GlobalConfig:
|
|
5
|
-
_instance = None
|
|
6
|
-
|
|
7
|
-
def __new__(cls, *args, **kwargs):
|
|
8
|
-
if cls._instance is None:
|
|
9
|
-
cls._instance = super(GlobalConfig, cls).__new__(cls, *args, **kwargs)
|
|
10
|
-
cls._instance._deck = None
|
|
11
|
-
cls._instance._agent = None
|
|
12
|
-
cls._instance._defined_variables = {}
|
|
13
|
-
cls._instance._api_variables = set()
|
|
14
|
-
cls._instance._deck_snapshot = {}
|
|
15
|
-
cls._instance._runner = None
|
|
16
|
-
return cls._instance
|
|
17
|
-
|
|
18
|
-
@property
|
|
19
|
-
def deck(self):
|
|
20
|
-
return self._deck
|
|
21
|
-
|
|
22
|
-
@deck.setter
|
|
23
|
-
def deck(self, value):
|
|
24
|
-
if self._deck is None:
|
|
25
|
-
self._deck = value
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def deck_snapshot(self):
|
|
30
|
-
return self._deck_snapshot
|
|
31
|
-
|
|
32
|
-
@deck_snapshot.setter
|
|
33
|
-
def deck_snapshot(self, value):
|
|
34
|
-
self._deck_snapshot = value
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def agent(self):
|
|
39
|
-
return self._agent
|
|
40
|
-
|
|
41
|
-
@agent.setter
|
|
42
|
-
def agent(self, value):
|
|
43
|
-
if self._agent is None:
|
|
44
|
-
self._agent = value
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def defined_variables(self):
|
|
48
|
-
return self._defined_variables
|
|
49
|
-
|
|
50
|
-
@defined_variables.setter
|
|
51
|
-
def defined_variables(self, value):
|
|
52
|
-
self._defined_variables = value
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def api_variables(self):
|
|
56
|
-
return self._api_variables
|
|
57
|
-
|
|
58
|
-
@api_variables.setter
|
|
59
|
-
def api_variables(self, value):
|
|
60
|
-
self._api_variables = value
|
|
61
|
-
|
|
62
|
-
@property
|
|
63
|
-
def runner(self):
|
|
64
|
-
return self._runner
|
|
65
|
-
|
|
66
|
-
@runner.setter
|
|
67
|
-
def runner(self, value):
|
|
1
|
+
# from ivoryos.utils.script_runner import ScriptRunner
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GlobalConfig:
|
|
5
|
+
_instance = None
|
|
6
|
+
|
|
7
|
+
def __new__(cls, *args, **kwargs):
|
|
8
|
+
if cls._instance is None:
|
|
9
|
+
cls._instance = super(GlobalConfig, cls).__new__(cls, *args, **kwargs)
|
|
10
|
+
cls._instance._deck = None
|
|
11
|
+
cls._instance._agent = None
|
|
12
|
+
cls._instance._defined_variables = {}
|
|
13
|
+
cls._instance._api_variables = set()
|
|
14
|
+
cls._instance._deck_snapshot = {}
|
|
15
|
+
cls._instance._runner = None
|
|
16
|
+
return cls._instance
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def deck(self):
|
|
20
|
+
return self._deck
|
|
21
|
+
|
|
22
|
+
@deck.setter
|
|
23
|
+
def deck(self, value):
|
|
24
|
+
if self._deck is None:
|
|
25
|
+
self._deck = value
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def deck_snapshot(self):
|
|
30
|
+
return self._deck_snapshot
|
|
31
|
+
|
|
32
|
+
@deck_snapshot.setter
|
|
33
|
+
def deck_snapshot(self, value):
|
|
34
|
+
self._deck_snapshot = value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def agent(self):
|
|
39
|
+
return self._agent
|
|
40
|
+
|
|
41
|
+
@agent.setter
|
|
42
|
+
def agent(self, value):
|
|
43
|
+
if self._agent is None:
|
|
44
|
+
self._agent = value
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def defined_variables(self):
|
|
48
|
+
return self._defined_variables
|
|
49
|
+
|
|
50
|
+
@defined_variables.setter
|
|
51
|
+
def defined_variables(self, value):
|
|
52
|
+
self._defined_variables = value
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def api_variables(self):
|
|
56
|
+
return self._api_variables
|
|
57
|
+
|
|
58
|
+
@api_variables.setter
|
|
59
|
+
def api_variables(self, value):
|
|
60
|
+
self._api_variables = value
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def runner(self):
|
|
64
|
+
return self._runner
|
|
65
|
+
|
|
66
|
+
@runner.setter
|
|
67
|
+
def runner(self, value):
|
|
68
68
|
self._runner = value
|
ivoryos/utils/llm_agent.py
CHANGED
|
@@ -1,183 +1,183 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
from openai import OpenAI
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# from dotenv import load_dotenv
|
|
10
|
-
# load_dotenv()
|
|
11
|
-
|
|
12
|
-
# host = "137.82.65.246"
|
|
13
|
-
# model = "llama3"
|
|
14
|
-
|
|
15
|
-
# structured output,
|
|
16
|
-
# class Action(BaseModel):
|
|
17
|
-
# action: str
|
|
18
|
-
# args: dict
|
|
19
|
-
# arg_types: dict
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# class ActionPlan(BaseModel):
|
|
23
|
-
# actions: list[Action]
|
|
24
|
-
# # final_answer: str
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class LlmAgent:
|
|
28
|
-
def __init__(self, model="llama3", output_path=os.curdir, host=None):
|
|
29
|
-
self.host = host
|
|
30
|
-
self.base_url = f"http://{self.host}:11434/v1/" if host is not None else ""
|
|
31
|
-
self.model = model
|
|
32
|
-
self.output_path = os.path.join(output_path, "llm_output") if output_path is not None else None
|
|
33
|
-
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) if host is None else OpenAI(api_key="ollama",
|
|
34
|
-
base_url=self.base_url)
|
|
35
|
-
if self.output_path is not None:
|
|
36
|
-
os.makedirs(self.output_path, exist_ok=True)
|
|
37
|
-
|
|
38
|
-
@staticmethod
|
|
39
|
-
def extract_annotations_docstrings(module_sigs):
|
|
40
|
-
class_str = ""
|
|
41
|
-
|
|
42
|
-
for name, value in module_sigs.items():
|
|
43
|
-
signature = value.get("signature")
|
|
44
|
-
docstring = value.get("docstring")
|
|
45
|
-
class_str += f'\tdef {name}{signature}:\n'
|
|
46
|
-
class_str += f'\t\t"""\n\t\t{docstring}\n\t\t"""' + '\n' if docstring else ''
|
|
47
|
-
class_str = class_str.replace('self, ', '')
|
|
48
|
-
class_str = class_str.replace('self', '')
|
|
49
|
-
name_list = list(module_sigs.keys())
|
|
50
|
-
# print(class_str)
|
|
51
|
-
# with open(os.path.join(self.output_path, "docstring_manual.txt"), "w") as f:
|
|
52
|
-
# f.write(class_str)
|
|
53
|
-
return class_str, name_list
|
|
54
|
-
|
|
55
|
-
@staticmethod
|
|
56
|
-
def parse_code_from_msg(msg):
|
|
57
|
-
msg = msg.strip()
|
|
58
|
-
# print(msg)
|
|
59
|
-
# code_blocks = re.findall(r'```(?:json\s)?(.*?)```', msg, re.DOTALL)
|
|
60
|
-
code_blocks = re.findall(r'\[\s*\{.*?\}\s*\]', msg, re.DOTALL)
|
|
61
|
-
|
|
62
|
-
json_blocks = []
|
|
63
|
-
for block in code_blocks:
|
|
64
|
-
if not block.startswith('['):
|
|
65
|
-
start_index = block.find('[')
|
|
66
|
-
block = block[start_index:]
|
|
67
|
-
block = re.sub(r'//.*', '', block)
|
|
68
|
-
block = block.replace('True', 'true').replace('False', 'false')
|
|
69
|
-
try:
|
|
70
|
-
# Try to parse the block as JSON
|
|
71
|
-
json_data = json.loads(block.strip())
|
|
72
|
-
if isinstance(json_data, list):
|
|
73
|
-
json_blocks = json_data
|
|
74
|
-
except json.JSONDecodeError:
|
|
75
|
-
continue
|
|
76
|
-
return json_blocks
|
|
77
|
-
|
|
78
|
-
def _generate(self, robot_sigs, prompt):
|
|
79
|
-
# deck_info, name_list = self.extract_annotations_docstrings(type(robot))
|
|
80
|
-
deck_info, name_list = self.extract_annotations_docstrings(robot_sigs)
|
|
81
|
-
full_prompt = '''I have some python functions, for example when calling them I want to write them using JSON,
|
|
82
|
-
it is necessary to include all args
|
|
83
|
-
for example
|
|
84
|
-
def dose_solid(amount_in_mg:float, bring_in:bool=True): def analyze():
|
|
85
|
-
dose_solid(3)
|
|
86
|
-
analyze()
|
|
87
|
-
I would want to write to
|
|
88
|
-
[
|
|
89
|
-
{
|
|
90
|
-
"action": "dose_solid",
|
|
91
|
-
"arg_types": {
|
|
92
|
-
"amount_in_mg": "float",
|
|
93
|
-
"bring_in": "bool"
|
|
94
|
-
},
|
|
95
|
-
"args": {
|
|
96
|
-
"amount_in_mg": 3,
|
|
97
|
-
"bring_in": true
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"action": "analyze",
|
|
102
|
-
"arg_types": {},
|
|
103
|
-
"args": {}
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
''' + f'''
|
|
107
|
-
Now these are my callable functions,
|
|
108
|
-
{deck_info}
|
|
109
|
-
and I want you to find the most appropriate function if I want to do these tasks
|
|
110
|
-
"""{prompt}"""
|
|
111
|
-
,and write a list of dictionary in json accordingly. Please only use these action names {name_list},
|
|
112
|
-
can you also help find the default value you can't find the info from my request.
|
|
113
|
-
'''
|
|
114
|
-
if self.output_path is not None:
|
|
115
|
-
with open(os.path.join(self.output_path, "prompt.txt"), "w") as f:
|
|
116
|
-
f.write(full_prompt)
|
|
117
|
-
messages = [{"role": "user",
|
|
118
|
-
"content": full_prompt}, ]
|
|
119
|
-
# if self.host == "openai":
|
|
120
|
-
output = self.client.chat.completions.create(
|
|
121
|
-
messages=messages,
|
|
122
|
-
model=self.model,
|
|
123
|
-
# response_format={"type": "json_object"},
|
|
124
|
-
)
|
|
125
|
-
msg = output.choices[0].message.content
|
|
126
|
-
# msg = output.choices[0].message.parsed
|
|
127
|
-
|
|
128
|
-
code = self.parse_code_from_msg(msg)
|
|
129
|
-
code = [action for action in code if action.get('action', '') in name_list]
|
|
130
|
-
# print('\033[91m', code, '\033[0m')
|
|
131
|
-
return code
|
|
132
|
-
|
|
133
|
-
def generate_code(self, robot_signature, prompt, attempt_allowance: int = 3):
|
|
134
|
-
attempt = 0
|
|
135
|
-
|
|
136
|
-
while attempt < attempt_allowance:
|
|
137
|
-
_code = self._generate(robot_signature, prompt)
|
|
138
|
-
attempt += 1
|
|
139
|
-
if _code:
|
|
140
|
-
break
|
|
141
|
-
|
|
142
|
-
return self.fill_blanks(_code, robot_signature)
|
|
143
|
-
# return code
|
|
144
|
-
|
|
145
|
-
@staticmethod
|
|
146
|
-
def fill_blanks(actions, robot_signature):
|
|
147
|
-
for action in actions:
|
|
148
|
-
action_name = action['action']
|
|
149
|
-
action_signature = robot_signature.get(action_name).get('signature', {})
|
|
150
|
-
args = action.get("args", {})
|
|
151
|
-
arg_types = action.get("arg_types", {})
|
|
152
|
-
for param in action_signature.parameters.values():
|
|
153
|
-
if param.name == 'self':
|
|
154
|
-
continue
|
|
155
|
-
if param.name not in args:
|
|
156
|
-
args[param.name] = param.default if param.default is not param.empty else ''
|
|
157
|
-
arg_types[param.name] = param.annotation.__name__
|
|
158
|
-
action['args'] = args
|
|
159
|
-
action['arg_types'] = arg_types
|
|
160
|
-
return actions
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if __name__ == "__main__":
|
|
164
|
-
from pprint import pprint
|
|
165
|
-
from example.sdl_example.abstract_sdl import deck
|
|
166
|
-
|
|
167
|
-
from utils import parse_functions
|
|
168
|
-
|
|
169
|
-
deck_sig = parse_functions(deck, doc_string=True)
|
|
170
|
-
# llm_agent = LlmAgent(host="openai", model="gpt-3.5-turbo")
|
|
171
|
-
llm_agent = LlmAgent(host="localhost", model="llama3.1")
|
|
172
|
-
# robot = IrohDeck()
|
|
173
|
-
# extract_annotations_docstrings(DummySDLDeck)
|
|
174
|
-
prompt = '''I want to start with dosing 10 mg of current sample, and add 1 mL of toluene
|
|
175
|
-
and equilibrate for 10 minute at 40 degrees, then sample 20 ul of sample to analyze with hplc, and save result'''
|
|
176
|
-
code = llm_agent.generate_code(deck_sig, prompt)
|
|
177
|
-
pprint(code)
|
|
178
|
-
|
|
179
|
-
"""
|
|
180
|
-
I want to dose 10mg, 6mg, 4mg, 3mg, 2mg, 1mg to 6 vials
|
|
181
|
-
I want to add 10 mg to vial a3, and 10 ml of liquid, then shake them for 3 minutes
|
|
182
|
-
|
|
183
|
-
"""
|
|
1
|
+
import inspect
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from openai import OpenAI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# from dotenv import load_dotenv
|
|
10
|
+
# load_dotenv()
|
|
11
|
+
|
|
12
|
+
# host = "137.82.65.246"
|
|
13
|
+
# model = "llama3"
|
|
14
|
+
|
|
15
|
+
# structured output,
|
|
16
|
+
# class Action(BaseModel):
|
|
17
|
+
# action: str
|
|
18
|
+
# args: dict
|
|
19
|
+
# arg_types: dict
|
|
20
|
+
#
|
|
21
|
+
#
|
|
22
|
+
# class ActionPlan(BaseModel):
|
|
23
|
+
# actions: list[Action]
|
|
24
|
+
# # final_answer: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LlmAgent:
|
|
28
|
+
def __init__(self, model="llama3", output_path=os.curdir, host=None):
|
|
29
|
+
self.host = host
|
|
30
|
+
self.base_url = f"http://{self.host}:11434/v1/" if host is not None else ""
|
|
31
|
+
self.model = model
|
|
32
|
+
self.output_path = os.path.join(output_path, "llm_output") if output_path is not None else None
|
|
33
|
+
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) if host is None else OpenAI(api_key="ollama",
|
|
34
|
+
base_url=self.base_url)
|
|
35
|
+
if self.output_path is not None:
|
|
36
|
+
os.makedirs(self.output_path, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def extract_annotations_docstrings(module_sigs):
|
|
40
|
+
class_str = ""
|
|
41
|
+
|
|
42
|
+
for name, value in module_sigs.items():
|
|
43
|
+
signature = value.get("signature")
|
|
44
|
+
docstring = value.get("docstring")
|
|
45
|
+
class_str += f'\tdef {name}{signature}:\n'
|
|
46
|
+
class_str += f'\t\t"""\n\t\t{docstring}\n\t\t"""' + '\n' if docstring else ''
|
|
47
|
+
class_str = class_str.replace('self, ', '')
|
|
48
|
+
class_str = class_str.replace('self', '')
|
|
49
|
+
name_list = list(module_sigs.keys())
|
|
50
|
+
# print(class_str)
|
|
51
|
+
# with open(os.path.join(self.output_path, "docstring_manual.txt"), "w") as f:
|
|
52
|
+
# f.write(class_str)
|
|
53
|
+
return class_str, name_list
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def parse_code_from_msg(msg):
|
|
57
|
+
msg = msg.strip()
|
|
58
|
+
# print(msg)
|
|
59
|
+
# code_blocks = re.findall(r'```(?:json\s)?(.*?)```', msg, re.DOTALL)
|
|
60
|
+
code_blocks = re.findall(r'\[\s*\{.*?\}\s*\]', msg, re.DOTALL)
|
|
61
|
+
|
|
62
|
+
json_blocks = []
|
|
63
|
+
for block in code_blocks:
|
|
64
|
+
if not block.startswith('['):
|
|
65
|
+
start_index = block.find('[')
|
|
66
|
+
block = block[start_index:]
|
|
67
|
+
block = re.sub(r'//.*', '', block)
|
|
68
|
+
block = block.replace('True', 'true').replace('False', 'false')
|
|
69
|
+
try:
|
|
70
|
+
# Try to parse the block as JSON
|
|
71
|
+
json_data = json.loads(block.strip())
|
|
72
|
+
if isinstance(json_data, list):
|
|
73
|
+
json_blocks = json_data
|
|
74
|
+
except json.JSONDecodeError:
|
|
75
|
+
continue
|
|
76
|
+
return json_blocks
|
|
77
|
+
|
|
78
|
+
def _generate(self, robot_sigs, prompt):
|
|
79
|
+
# deck_info, name_list = self.extract_annotations_docstrings(type(robot))
|
|
80
|
+
deck_info, name_list = self.extract_annotations_docstrings(robot_sigs)
|
|
81
|
+
full_prompt = '''I have some python functions, for example when calling them I want to write them using JSON,
|
|
82
|
+
it is necessary to include all args
|
|
83
|
+
for example
|
|
84
|
+
def dose_solid(amount_in_mg:float, bring_in:bool=True): def analyze():
|
|
85
|
+
dose_solid(3)
|
|
86
|
+
analyze()
|
|
87
|
+
I would want to write to
|
|
88
|
+
[
|
|
89
|
+
{
|
|
90
|
+
"action": "dose_solid",
|
|
91
|
+
"arg_types": {
|
|
92
|
+
"amount_in_mg": "float",
|
|
93
|
+
"bring_in": "bool"
|
|
94
|
+
},
|
|
95
|
+
"args": {
|
|
96
|
+
"amount_in_mg": 3,
|
|
97
|
+
"bring_in": true
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"action": "analyze",
|
|
102
|
+
"arg_types": {},
|
|
103
|
+
"args": {}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
''' + f'''
|
|
107
|
+
Now these are my callable functions,
|
|
108
|
+
{deck_info}
|
|
109
|
+
and I want you to find the most appropriate function if I want to do these tasks
|
|
110
|
+
"""{prompt}"""
|
|
111
|
+
,and write a list of dictionary in json accordingly. Please only use these action names {name_list},
|
|
112
|
+
can you also help find the default value you can't find the info from my request.
|
|
113
|
+
'''
|
|
114
|
+
if self.output_path is not None:
|
|
115
|
+
with open(os.path.join(self.output_path, "prompt.txt"), "w") as f:
|
|
116
|
+
f.write(full_prompt)
|
|
117
|
+
messages = [{"role": "user",
|
|
118
|
+
"content": full_prompt}, ]
|
|
119
|
+
# if self.host == "openai":
|
|
120
|
+
output = self.client.chat.completions.create(
|
|
121
|
+
messages=messages,
|
|
122
|
+
model=self.model,
|
|
123
|
+
# response_format={"type": "json_object"},
|
|
124
|
+
)
|
|
125
|
+
msg = output.choices[0].message.content
|
|
126
|
+
# msg = output.choices[0].message.parsed
|
|
127
|
+
|
|
128
|
+
code = self.parse_code_from_msg(msg)
|
|
129
|
+
code = [action for action in code if action.get('action', '') in name_list]
|
|
130
|
+
# print('\033[91m', code, '\033[0m')
|
|
131
|
+
return code
|
|
132
|
+
|
|
133
|
+
def generate_code(self, robot_signature, prompt, attempt_allowance: int = 3):
|
|
134
|
+
attempt = 0
|
|
135
|
+
|
|
136
|
+
while attempt < attempt_allowance:
|
|
137
|
+
_code = self._generate(robot_signature, prompt)
|
|
138
|
+
attempt += 1
|
|
139
|
+
if _code:
|
|
140
|
+
break
|
|
141
|
+
|
|
142
|
+
return self.fill_blanks(_code, robot_signature)
|
|
143
|
+
# return code
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def fill_blanks(actions, robot_signature):
|
|
147
|
+
for action in actions:
|
|
148
|
+
action_name = action['action']
|
|
149
|
+
action_signature = robot_signature.get(action_name).get('signature', {})
|
|
150
|
+
args = action.get("args", {})
|
|
151
|
+
arg_types = action.get("arg_types", {})
|
|
152
|
+
for param in action_signature.parameters.values():
|
|
153
|
+
if param.name == 'self':
|
|
154
|
+
continue
|
|
155
|
+
if param.name not in args:
|
|
156
|
+
args[param.name] = param.default if param.default is not param.empty else ''
|
|
157
|
+
arg_types[param.name] = param.annotation.__name__
|
|
158
|
+
action['args'] = args
|
|
159
|
+
action['arg_types'] = arg_types
|
|
160
|
+
return actions
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
from pprint import pprint
|
|
165
|
+
from example.sdl_example.abstract_sdl import deck
|
|
166
|
+
|
|
167
|
+
from utils import parse_functions
|
|
168
|
+
|
|
169
|
+
deck_sig = parse_functions(deck, doc_string=True)
|
|
170
|
+
# llm_agent = LlmAgent(host="openai", model="gpt-3.5-turbo")
|
|
171
|
+
llm_agent = LlmAgent(host="localhost", model="llama3.1")
|
|
172
|
+
# robot = IrohDeck()
|
|
173
|
+
# extract_annotations_docstrings(DummySDLDeck)
|
|
174
|
+
prompt = '''I want to start with dosing 10 mg of current sample, and add 1 mL of toluene
|
|
175
|
+
and equilibrate for 10 minute at 40 degrees, then sample 20 ul of sample to analyze with hplc, and save result'''
|
|
176
|
+
code = llm_agent.generate_code(deck_sig, prompt)
|
|
177
|
+
pprint(code)
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
I want to dose 10mg, 6mg, 4mg, 3mg, 2mg, 1mg to 6 vials
|
|
181
|
+
I want to add 10 mg to vial a3, and 10 ml of liquid, then shake them for 3 minutes
|
|
182
|
+
|
|
183
|
+
"""
|