Update openai_agent_example.py

This commit is contained in:
retoor 2024-12-20 19:00:20 +00:00
parent e20f228d6c
commit 2fe55f297d

View File

@ -3,20 +3,20 @@ Written in 2024 by retoor@molodetz.nl.
MIT license. Enjoy! MIT license. Enjoy!
Purpose of this file is to be a native part of your application The purpose of this file is to be a native part of your application
instead of the nth-library. It's just not worth a library. instead of yet another library. It's just not worth making a library,
Especially not another one. Just modify and use it! especially not another one. Just modify and use it!
The docstrings of all methods contain tips and important facts. The docstrings of all methods contain tips and important facts.
This document contains all URL's for all services that you need. This document contains all URLs for all services that you need.
You'll need: You'll need:
- OpenAI account. - An OpenAI account.
- Named a project in OpenAI dashboard. - A named project in the OpenAI dashboard.
- Requested an api key and created an assistant. - A requested API key and an assistant created.
URL's to all these services are described in the class for convenience. URLs to all these services are described in the class for convenience.
It can be hard to find initially. 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,14 +24,14 @@ 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 openapi - pip install openai
""" """
# AGAIN. NOT REAL DATA, ONLY LOOKS THAT WAY FOR EDUCATIVE PURPOSES. # AGAIN, NOT REAL DATA, ONLY LOOKS LIKE IT FOR EDUCATIONAL PURPOSES.
# Not required to use the Agent class. Agent class accepts api_key as parameter. # Not required to use the Agent class. The Agent class accepts api_key as a parameter.
API_KEY = "sk-proj-V1Jc3my22xSvtfZ3dxXNHgLWZIhEopmJVIMlcNrft_q-7p8dDT_-AQCE8wo9cKpO3v05egDm7CT3BlbkFjN21maiSZqS4oz8FSGiblOeKMH2i6BzIGdQWMcVbKHnRqWy0KiSwKQywJ7XEf792UgGFtwLtxkA" API_KEY = "sk-proj-V1Jc3my22xSvtfZ3dxXNHgLWZIhEopmJVIMlcNrft_q-7p8dDT_-AQCE8wo9cKpO3v05egDm7CT3BlbkFjN21maiSZqS4oz8FSGiblOeKMH2i6BzIGdQWMcVbKHnRqWy0KiSwKQywJ7XEf792UgGFtwLtxkA"
# Not required to use the Agent class. Agent class accepts assistant_id as parameter. # Not required to use the Agent class. The Agent class accepts assistant_id as a parameter.
ASSISTANT_ID = "asst_NgncvKEN8CTf642RE8a4PgAp" ASSISTANT_ID = "asst_NgncvKEN8CTf642RE8a4PgAp"
@ -45,13 +45,13 @@ from openai import OpenAI
class Agent: class Agent:
""" """
This class translates into an instance a single user session with its own memory. This class represents 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 future to continue what the user said and what the assistant (agent) said. This can be used in the future to continue
where you left off. Format is described in the docs of __init__ function below. where you left off. The format is described in the docs of the __init__ function below.
Introduction API usage for if you want to extend this class: Introduction to API usage 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 +62,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 assistant_id (agent_id) here. It is the id that starts with 'asst_', not your custom name: You can find the 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 in this format, this is to keep a message history that you can later use again: Messages are optional and should be in this format 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 +82,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 +94,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 front end or api. That's not the original result which is a in your frontend or API. This is not the original result, which is a
custom list with unserializable models. custom list with unserializable models.
""" """
return [ return [
@ -112,7 +112,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 big sizes allowed. Is more pricy. Sadly, only large sizes are allowed. It's more expensive.
Sizes: 1024x1024, 1792x1024, or 1024x1792. Sizes: 1024x1024, 1792x1024, or 1024x1792.
""" """
result = self.client.images.generate( result = self.client.images.generate(
@ -125,8 +125,8 @@ class Agent:
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 on given interval to inform the caller it' still busy so you can Chat with the agent. It yields at the given interval to inform the caller it's still busy, so you can
update the user with live status. It doesn't hang. You can use this fully async with other update the user with a live status. It doesn't hang. You can use this fully asynchronously 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.
@ -158,7 +158,7 @@ class Agent:
async def chatp(self, message: str) -> str: async def chatp(self, message: str) -> str:
""" """
Just like regular chat function but with progress indication and returns string directly. Just like the regular chat function but with progress indication and returns a 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 +173,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 web socket connections. Blocking read_line can break WebSocket connections.
That's why. That's why.
""" """
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
@ -183,9 +183,9 @@ class Agent:
async def cli(self): async def cli(self):
""" """
Interactive client. Can be used on terminal by user or a different process. Interactive client. Can be used in a terminal by the user or a different process.
The bottom new line is so that a process can check for \n\n to check if it's end response The bottom newline is so that a process can check for '\n\n' to determine if the response has ended
and there's nothing left to wait for and thus can send next prompt if the '>' shows. and there's nothing left to wait for, allowing the process to send the next prompt if the '>' shows.
""" """
while True: while True:
try: try:
@ -196,24 +196,24 @@ 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 and that you're sure your key is in the the real ones for example purposes so you can verify your key is in the
right format. correct 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 dalle3, dalle2 is almost unusable. For image sizes look at the class method docstring. # 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 photo realistic image of a rust dev") 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 gets executed by direct execution of script. Not when imported. # Only executed by direct execution of the script, not when imported.
asyncio.run(main()) asyncio.run(main())