forked from daveshap/OpenAI_Agent_Swarm
-
Notifications
You must be signed in to change notification settings - Fork 0
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
1cd76a7
commit ce57fe9
Showing
9 changed files
with
394 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
Empty file.
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,79 @@ | ||
import time | ||
import json | ||
|
||
def chat(client, thread, assistant, functions): | ||
while True: | ||
user_message = input("You: ") | ||
|
||
# add user message to thread | ||
thread_message = client.beta.threads.messages.create( | ||
thread.id, | ||
role="user", | ||
content=user_message, | ||
) | ||
|
||
# get assistant response in thread | ||
run = client.beta.threads.runs.create( | ||
thread_id=thread.id, | ||
assistant_id=assistant.id, | ||
) | ||
|
||
# wait for run to complete | ||
wait_time = 0 | ||
while True: | ||
if wait_time % 5 == 0: | ||
print(f"waiting for run to complete...", flush=True) | ||
wait_time += 1 | ||
time.sleep(1) | ||
|
||
run = client.beta.threads.runs.retrieve( | ||
thread_id=thread.id, | ||
run_id=run.id, | ||
) | ||
|
||
if run.status == "completed": | ||
break | ||
elif run.status == "in_progress": | ||
continue | ||
elif run.status == "queued": | ||
continue | ||
elif run.status == "requires_action": | ||
if run.required_action.type == 'submit_tool_outputs': | ||
tool_calls = run.required_action.submit_tool_outputs.tool_calls | ||
|
||
tool_outputs = [] | ||
for tc in tool_calls: | ||
function_to_call = functions.get(tc.function.name, None) | ||
if not function_to_call: | ||
raise ValueError(f"Function {tc.function.name} not found in execution environment") | ||
function_args = json.loads(tc.function.arguments) | ||
function_response = function_to_call(**function_args) | ||
|
||
tool_outputs.append({ | ||
"tool_call_id": tc.id, | ||
"output": json.dumps(function_response), | ||
}) | ||
|
||
print(f"Submitting tool outputs...", flush=True) | ||
run = client.beta.threads.runs.submit_tool_outputs( | ||
thread_id=thread.id, | ||
run_id=run.id, | ||
tool_outputs=tool_outputs | ||
) | ||
else: | ||
input(f'Run status: {run.status}. press enter to continue, or ctrl+c to quit') | ||
|
||
# get most recent message from thread | ||
thread_messages = client.beta.threads.messages.list(thread.id, limit=10, order='desc') | ||
|
||
# get assistant response from message | ||
assistant_response = thread_messages.data[0].content[0].text.value | ||
|
||
print(f"\n\nBot: {assistant_response}\n\n", flush=True) | ||
|
||
# continue? | ||
try: | ||
input("Press enter to continue chatting, or ctrl+c to stop chat\n") | ||
except KeyboardInterrupt: | ||
print(f"Stopping chat\n" + 90*"-" + "\n\n", flush=True) | ||
break |
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,16 @@ | ||
# tool_creator assistant | ||
import tool_maker.tool_creator as creator | ||
from tool_maker.creator_config import AssistantConfig as CreatorConfig | ||
|
||
# tool_user assistant | ||
import tool_maker.tool_user as user | ||
from tool_maker.user_config import AssistantConfig as UserConfig | ||
|
||
if __name__ == '__main__': | ||
# create the tool creator assistant and chat to create your tools | ||
creator_details = CreatorConfig().assistant_details | ||
creator.talk_to_tool_creator(creator_details) | ||
|
||
# create the tool user assistant and chat to test your tools | ||
user_details = UserConfig().assistant_details | ||
user.talk_to_tool_user(user_details) |
Empty file.
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,95 @@ | ||
class AssistantConfig: | ||
def __init__(self): | ||
self.create_tool_function = """ | ||
def create_tool(tool_name=None, tool_description=None, tool_parameters=None, tool_code=None, required_action_by_user=None): | ||
\"\"\" | ||
returns a tool that can be used by other assistants | ||
\"\"\" | ||
# create the tool file | ||
os.makedirs('tools', exist_ok=True) | ||
with open(f'tools/{tool_name}.py', 'w') as f: | ||
f.write(tool_code) | ||
# create the tool details file | ||
tool_details = { | ||
'name': tool_name, | ||
'description': tool_description, | ||
'parameters': tool_parameters, | ||
} | ||
with open(f'tools/{tool_name}.json', 'w') as f: | ||
json.dump(tool_details, f, indent=4) | ||
return_value = f'created tool at tools/{tool_name}.py with details tools/{tool_name}.json\\n\\n' | ||
return_value += f'There is a required action by the user before the tool can be used: {required_action_by_user}' | ||
return return_value | ||
""" | ||
self.files_for_assistant = [] | ||
self.instructions_for_assistant = "You create tools to accomplish arbitrary tasks. Write and run code to implement the interface for these tools using the OpenAI API format. You do not have access to the tools you create. Instruct the user that to use the tool, they will have to create an assistant equipped with that tool, or consult with the AssistantCreationAssistant about the use of that tool in a new assistant." | ||
self.example_tool = """ | ||
def new_tool_name(param1=None, param2='default_value'): | ||
if not param1: | ||
return None | ||
# does something with the parameters to get the result | ||
intermediate_output = ... | ||
# get the tool output | ||
tool_output = ... | ||
return tool_output | ||
""" | ||
self.assistant_details = self._build_assistant_details() | ||
|
||
def _build_assistant_details(self): | ||
return { | ||
'build_params' : { | ||
'model': "gpt-4-1106-preview", | ||
'name': "Tool Creator", | ||
'description': "Assistant to create tools for use in the OpenAI platform by other Assistants.", | ||
'instructions': self.instructions_for_assistant, | ||
'tools': [ | ||
{ | ||
"type": "function", | ||
"function": { | ||
"name": "create_tool", | ||
"description": "returns a tool that can be used by other assistants. specify the tool_name, tool_description, tool_parameters, and tool_code. all of those are required. use the JSON schema for all tool_parameters.", | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"tool_name": { | ||
"type": "string", | ||
"description": "The name of the tool, using snake_case e.g. new_tool_name", | ||
}, | ||
"tool_description": { | ||
"type": "string", | ||
"description": "The description of the tool, e.g. This tool does a computation using param1 and param2 to return a result that ...", | ||
}, | ||
"tool_parameters": { | ||
"type": "string", | ||
"description": 'The parameters of the tool, using JSON schema to specify the type and properties for each parameter.\n\ne.g.\n\n{"type": "object", "properties": {"location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["location"]}', | ||
}, | ||
"tool_code": { | ||
"type": "string", | ||
"description": f"The code for the tool, e.g. \n{self.example_tool}", | ||
}, | ||
"required_action_by_user": { | ||
"type": "string", | ||
"description": "Optional. The action required by the user before the tool can be used, e.g. 'set up API keys for service X and add them as environment variables'. It's important to be as detailed as possible so that these tools can be used for arbitrary tasks. If there is nothing required, do not include this parameter.", | ||
}, | ||
}, | ||
"required": ["tool_name", "tool_description", "tool_parameters", "tool_code"], | ||
}, | ||
}, | ||
}, | ||
], | ||
'file_ids': [], | ||
'metadata': {}, | ||
}, | ||
'file_paths': self.files_for_assistant, | ||
'functions': { | ||
'create_tool': self.create_tool_function, | ||
}, | ||
} |
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,63 @@ | ||
""" | ||
create a tool-creator assistant using the assistant creation API | ||
""" | ||
|
||
import json | ||
import os | ||
|
||
from shared.utils import chat as chat_loop | ||
|
||
from openai import OpenAI | ||
client = OpenAI() # be sure to set your OPENAI_API_KEY environment variable | ||
|
||
def create_tool_creator(assistant_details): | ||
# create the assistant | ||
tool_creator = client.beta.assistants.create(**assistant_details["build_params"]) | ||
|
||
print(f"Created assistant to create tools: {tool_creator.id}\n\n" + 90*"-" + "\n\n", flush=True) | ||
|
||
# save the assistant info to a json file | ||
info_to_export = { | ||
"assistant_id": tool_creator.id, | ||
"assistant_details": assistant_details, | ||
} | ||
|
||
os.makedirs('assistants', exist_ok=True) | ||
with open('assistants/tool_creator.json', 'w') as f: | ||
json.dump(info_to_export, f, indent=4) | ||
|
||
return tool_creator | ||
|
||
def talk_to_tool_creator(assistant_details): | ||
""" | ||
talk to the assistant to create tools | ||
""" | ||
|
||
# check if json file exists | ||
try: | ||
os.makedirs('assistants', exist_ok=True) | ||
with open('assistants/tool_creator.json') as f: | ||
create_new = input(f'Assistant details found in tool_creator.json. Create a new assistant? [y/N]') | ||
if create_new == 'y': | ||
raise Exception("User wants a new assistant") | ||
assistant_from_json = json.load(f) | ||
tool_creator = client.beta.assistants.retrieve(assistant_from_json['assistant_id']) | ||
print(f"Loaded assistant details from tool_creator.json\n\n" + 90*"-" + "\n\n", flush=True) | ||
print(f'Assistant {tool_creator.id}:\n') | ||
assistant_details = assistant_from_json["assistant_details"] | ||
except: | ||
tool_creator = create_tool_creator(assistant_details) | ||
|
||
# load the functions into the execution environment | ||
functions = assistant_details["functions"] | ||
for func in functions: | ||
# define the function in this execution environment | ||
exec(functions[func], globals()) | ||
|
||
# add the function to the assistant details | ||
functions.update({func: eval(func)}) | ||
|
||
# Create thread | ||
thread = client.beta.threads.create() | ||
|
||
chat_loop(client, thread, tool_creator, functions) |
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,65 @@ | ||
""" | ||
Create an assistant using the tools from tool_creator using the assistant creation API | ||
""" | ||
|
||
import os | ||
import json | ||
|
||
from shared.utils import chat as chat_loop | ||
|
||
from openai import OpenAI | ||
client = OpenAI() # be sure to set your OPENAI_API_KEY environment variable | ||
|
||
def create_tool_user(assistant_details): | ||
# create the assistant | ||
tool_user = client.beta.assistants.create(**assistant_details["build_params"]) | ||
|
||
print(f"Created assistant {tool_user.id} to use tools\n\n" + 90*"-" + "\n\n", flush=True) | ||
|
||
# save the assistant info to a json file | ||
info_to_export = { | ||
"assistant_id": tool_user.id, | ||
"assistant_details": assistant_details, | ||
} | ||
os.makedirs('assistants', exist_ok=True) | ||
with open('assistants/tool_user.json', 'w') as f: | ||
json.dump(info_to_export, f, indent=4) | ||
|
||
return tool_user | ||
|
||
def talk_to_tool_user(assistant_details): | ||
""" | ||
talk to the assistant to use the tools | ||
""" | ||
|
||
# check if json file exists | ||
try: | ||
os.makedirs('assistants', exist_ok=True) | ||
with open('assistants/tool_user.json') as f: | ||
create_new = input(f'Assistant details found in tool_user.json. Create a new assistant? [y/N]') | ||
if create_new == 'y': | ||
raise Exception("User wants a new assistant") | ||
assistant_from_json = json.load(f) | ||
tool_user = client.beta.assistants.retrieve(assistant_from_json['assistant_id']) | ||
print(f"Loaded assistant details from tool_user.json\n\n" + 90*"-" + "\n\n", flush=True) | ||
print(f'Assistant {tool_user.id}:\n') | ||
assistant_details = assistant_from_json["assistant_details"] | ||
except: | ||
# create the assistant first | ||
tool_user = create_tool_user(assistant_details) | ||
|
||
# exec the functions from the py files | ||
os.makedirs('tools', exist_ok=True) | ||
functions = assistant_details["functions"] | ||
for func in functions: | ||
print(f"Loading function {func} into execution environment", flush=True) | ||
with open('tools/' + func + '.py') as f: | ||
exec(f.read(), globals()) | ||
|
||
functions.update({func: eval(func)}) | ||
|
||
# Create thread | ||
thread = client.beta.threads.create() | ||
|
||
# chat with the assistant | ||
chat_loop(client, thread, tool_user, functions) |
Oops, something went wrong.