Update openai_agent_example.py
This commit is contained in:
parent
536438e874
commit
beb50489d3
@ -3,20 +3,8 @@ Written in 2024 by retoor@molodetz.nl.
|
|||||||
|
|
||||||
MIT license. Enjoy!
|
MIT license. Enjoy!
|
||||||
|
|
||||||
The purpose of this file is to be a native part of your application
|
You'll need a paid OpenAI account, named a project in it, requested an api key and created an assistant.
|
||||||
instead of yet another library. It's just not worth making a library,
|
URL's to all these pages are described in the class for convenience.
|
||||||
especially not another one. Just modify and use it!
|
|
||||||
|
|
||||||
The docstrings of all methods contain tips and important facts.
|
|
||||||
This document contains all URLs for all services that you need.
|
|
||||||
|
|
||||||
You'll need:
|
|
||||||
- An OpenAI account.
|
|
||||||
- A named project in the OpenAI dashboard.
|
|
||||||
- A requested API key and an assistant created.
|
|
||||||
|
|
||||||
URLs to all these services are described in the class for convenience.
|
|
||||||
They can be hard to find initially.
|
|
||||||
|
|
||||||
The API keys described in this document are fake but are in the correct format for educational purposes.
|
The API keys described in this document are fake but are in the correct format for educational purposes.
|
||||||
|
|
||||||
@ -24,15 +12,23 @@ How to start:
|
|||||||
- sudo apt install python3.12-venv python3-pip -y
|
- sudo apt install python3.12-venv python3-pip -y
|
||||||
- python3 -m venv .venv
|
- python3 -m venv .venv
|
||||||
- . .venv/bin/activate
|
- . .venv/bin/activate
|
||||||
- pip install openai
|
- pip install openapi
|
||||||
|
|
||||||
|
This file is to be used as part of your project or a standalone after doing
|
||||||
|
some modifications at the end of the file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# AGAIN, NOT REAL DATA, ONLY LOOKS LIKE IT FOR EDUCATIONAL PURPOSES.
|
try:
|
||||||
# Not required to use the Agent class. The Agent class accepts api_key as a parameter.
|
import os
|
||||||
API_KEY = "sk-proj-V1Jc3my22xSvtfZ3dxXNHgLWZIhEopmJVIMlcNrft_q-7p8dDT_-AQCE8wo9cKpO3v05egDm7CT3BlbkFjN21maiSZqS4oz8FSGiblOeKMH2i6BzIGdQWMcVbKHnRqWy0KiSwKQywJ7XEf792UgGFtwLtxkA"
|
import sys
|
||||||
# Not required to use the Agent class. The Agent class accepts assistant_id as a parameter.
|
|
||||||
ASSISTANT_ID = "asst_NgncvKEN8CTf642RE8a4PgAp"
|
sys.path.append(os.getcwd())
|
||||||
|
import env
|
||||||
|
|
||||||
|
API_KEY = env.API_KEY
|
||||||
|
ASSISTANT_ID = env.ASSISTANT_ID
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -45,13 +41,13 @@ from openai import OpenAI
|
|||||||
|
|
||||||
class Agent:
|
class Agent:
|
||||||
"""
|
"""
|
||||||
This class represents a single user session with its own memory.
|
This class translates into an instance a single user session with its own memory.
|
||||||
|
|
||||||
The messages property of this class is a list containing the full chat history about
|
The messages property of this class is a list containing the full chat history about
|
||||||
what the user said and what the assistant (agent) said. This can be used in the future to continue
|
what the user said and what the assistant (agent) said. This can be used in future to continue
|
||||||
where you left off. The format is described in the docs of the __init__ function below.
|
where you left off. Format is described in the docs of __init__ function below.
|
||||||
|
|
||||||
Introduction to API usage if you want to extend this class:
|
Introduction API usage for if you want to extend this class:
|
||||||
https://platform.openai.com/docs/api-reference/introduction
|
https://platform.openai.com/docs/api-reference/introduction
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -62,10 +58,10 @@ class Agent:
|
|||||||
You can find and create API keys here:
|
You can find and create API keys here:
|
||||||
https://platform.openai.com/api-keys
|
https://platform.openai.com/api-keys
|
||||||
|
|
||||||
You can find the assistant_id (agent_id) here. It is the ID that starts with 'asst_', not your custom name:
|
You can find assistant_id (agent_id) here. It is the id that starts with 'asst_', not your custom name:
|
||||||
https://platform.openai.com/assistants/
|
https://platform.openai.com/assistants/
|
||||||
|
|
||||||
Messages are optional and should be in this format to keep a message history that you can later use again:
|
Messages are optional in this format, this is to keep a message history that you can later use again:
|
||||||
[
|
[
|
||||||
{"role": "user", "message": "What is choking the chicken?"},
|
{"role": "user", "message": "What is choking the chicken?"},
|
||||||
{"role": "assistant", "message": "Lucky for the cock."}
|
{"role": "assistant", "message": "Lucky for the cock."}
|
||||||
@ -82,8 +78,8 @@ class Agent:
|
|||||||
self, prompt: str, width: Optional[int] = 512, height: Optional[int] = 512
|
self, prompt: str, width: Optional[int] = 512, height: Optional[int] = 512
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
In my opinion, DALL·E 2 produces unusual results.
|
In my opinion dall-e-2 produces unusual results.
|
||||||
Sizes: 256x256, 512x512, or 1024x1024.
|
Sizes: 256x256, 512x512 or 1024x1024.
|
||||||
"""
|
"""
|
||||||
result = self.client.images.generate(
|
result = self.client.images.generate(
|
||||||
model="dall-e-2", prompt=prompt, n=1, size=f"{width}x{height}"
|
model="dall-e-2", prompt=prompt, n=1, size=f"{width}x{height}"
|
||||||
@ -94,8 +90,8 @@ class Agent:
|
|||||||
async def models(self):
|
async def models(self):
|
||||||
"""
|
"""
|
||||||
List models in dict format. That's more convenient than the original
|
List models in dict format. That's more convenient than the original
|
||||||
list method because this can be directly converted to JSON to be used
|
list method because this can be directly converted to json to be used
|
||||||
in your frontend or API. This is not the original result, which is a
|
in your front end or api. That's not the original result which is a
|
||||||
custom list with unserializable models.
|
custom list with unserializable models.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
@ -112,7 +108,7 @@ class Agent:
|
|||||||
self, prompt: str, height: Optional[int] = 1024, width: Optional[int] = 1024
|
self, prompt: str, height: Optional[int] = 1024, width: Optional[int] = 1024
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Sadly, only large sizes are allowed. It's more expensive.
|
Sadly only big sizes allowed. Is more pricy.
|
||||||
Sizes: 1024x1024, 1792x1024, or 1024x1792.
|
Sizes: 1024x1024, 1792x1024, or 1024x1792.
|
||||||
"""
|
"""
|
||||||
result = self.client.images.generate(
|
result = self.client.images.generate(
|
||||||
@ -121,12 +117,17 @@ class Agent:
|
|||||||
print(result)
|
print(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def upload_file(file_name: str, purpose: str) -> str:
|
||||||
|
with open(file_name, "rb") as file_fd:
|
||||||
|
response = self.client.files.create(file=file_fd, purpose=purpose)
|
||||||
|
return response.id
|
||||||
|
|
||||||
async def chat(
|
async def chat(
|
||||||
self, message: str, interval: Optional[float] = 0.2
|
self, message: str, interval: Optional[float] = 0.2
|
||||||
) -> Generator[None, None, str]:
|
) -> Generator[None, None, str]:
|
||||||
"""
|
"""
|
||||||
Chat with the agent. It yields at the given interval to inform the caller it's still busy, so you can
|
Chat with the agent. It yields on given interval to inform the caller it' still busy so you can
|
||||||
update the user with a live status. It doesn't hang. You can use this fully asynchronously with other
|
update the user with live status. It doesn't hang. You can use this fully async with other
|
||||||
instances of this class.
|
instances of this class.
|
||||||
|
|
||||||
This function also updates the self.messages list with chat history for later use.
|
This function also updates the self.messages list with chat history for later use.
|
||||||
@ -152,13 +153,14 @@ class Agent:
|
|||||||
response_messages = self.client.beta.threads.messages.list(
|
response_messages = self.client.beta.threads.messages.list(
|
||||||
thread_id=self.thread.id
|
thread_id=self.thread.id
|
||||||
).data
|
).data
|
||||||
last_message = response_messages[0]
|
last_message = response_messages[0].content[0].text.value
|
||||||
self.messages.append({"role": "assistant", "content": last_message})
|
self.messages.append({"role": "assistant", "content": last_message})
|
||||||
yield last_message
|
print(last_message)
|
||||||
|
yield str(last_message)
|
||||||
|
|
||||||
async def chatp(self, message: str) -> str:
|
async def chatp(self, message: str) -> str:
|
||||||
"""
|
"""
|
||||||
Just like the regular chat function but with progress indication and returns a string directly.
|
Just like regular chat function but with progress indication and returns string directly.
|
||||||
This is handy for interactive usage or for a process log.
|
This is handy for interactive usage or for a process log.
|
||||||
"""
|
"""
|
||||||
asyncio.get_event_loop()
|
asyncio.get_event_loop()
|
||||||
@ -173,8 +175,8 @@ class Agent:
|
|||||||
|
|
||||||
async def read_line(self, ps: Optional[str] = "> "):
|
async def read_line(self, ps: Optional[str] = "> "):
|
||||||
"""
|
"""
|
||||||
Non-blocking read_line.
|
Non blocking read_line.
|
||||||
Blocking read_line can break WebSocket connections.
|
Blocking read line can break web socket connections.
|
||||||
That's why.
|
That's why.
|
||||||
"""
|
"""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
@ -183,9 +185,9 @@ class Agent:
|
|||||||
|
|
||||||
async def cli(self):
|
async def cli(self):
|
||||||
"""
|
"""
|
||||||
Interactive client. Can be used in a terminal by the user or a different process.
|
Interactive client. Can be used on terminal by user or a different process.
|
||||||
The bottom newline is so that a process can check for '\n\n' to determine if the response has ended
|
The bottom new line is so that a process can check for \n\n to check if it's end response
|
||||||
and there's nothing left to wait for, allowing the process to send the next prompt if the '>' shows.
|
and there's nothing left to wait for and thus can send next prompt if the '>' shows.
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -196,24 +198,22 @@ class Agent:
|
|||||||
print(response.content[0].text.value)
|
print(response.content[0].text.value)
|
||||||
print("")
|
print("")
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Exiting...")
|
print("Exiting..")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
"""
|
"""
|
||||||
Example main function. The keys here are not real but look exactly like
|
Example main function. The keys here are not real but look exactly like
|
||||||
the real ones for example purposes so you can verify your key is in the
|
the real ones for example purposes and that you're sure your key is in the
|
||||||
correct format.
|
right format.
|
||||||
"""
|
"""
|
||||||
agent = Agent(api_key=API_KEY, assistant_id=ASSISTANT_ID)
|
agent = Agent(api_key=API_KEY, assistant_id=ASSISTANT_ID)
|
||||||
|
|
||||||
# Generate an image. Use DALL·E 3, as DALL·E 2 is almost unusable. For image sizes, look at the class method docstring.
|
|
||||||
list_containing_dicts_with_url_to_images = await agent.dalle3("Make a photo-realistic image of a Rust developer")
|
|
||||||
|
|
||||||
# Run interactive chat
|
# Run interactive chat
|
||||||
await agent.cli()
|
await agent.cli()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Only executed by direct execution of the script, not when imported.
|
# Only gets executed by direct execution of script. Not when important.
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
Loading…
Reference in New Issue
Block a user