Hi @onurgulay !
I noticed that my guide had errors.
It should now be correct, extensive, and reference-quality, all verified against the API by sending, something the API Reference documents about “create message” is not.
Here is significant code from my own asyncio Python library, showing:
- upload
- create thread - with text message content
- add to thread - file image and text
- add to thread - url image and text
- list messages
- delete
The only requirement is a file: my_image.png, and httpx module (which openai also uses)
MEGA_DEMO
''' Assistants - vision file and URL demonstration '''
import os
import httpx
import asyncio
import aiofiles
import json
from typing import List, Optional, Dict
API_KEY = os.environ.get("OPENAI_API_KEY")
if not API_KEY:
raise ValueError("OPENAI_API_KEY environment variable not set")
HEADERS = {
"OpenAI-Beta": "assistants=v2",
"Authorization": f"Bearer {API_KEY}"
}
BASE_URL = "https://api.openai.com/v1"
async def upload_file(file_path: str, purpose="vision") -> dict:
"""
Uploads a file to the OpenAI API for use with assistants vision.
:param file_path: Path to the file to upload.
:return: JSON response containing file details.
"""
url = f"{BASE_URL}/files"
async with httpx.AsyncClient() as client:
async with aiofiles.open(file_path, 'rb') as f:
file_content = await f.read()
files = {
'file': (os.path.basename(file_path), file_content),
'purpose': (None, purpose),
}
response = await client.post(url, headers=HEADERS, files=files)
response.raise_for_status()
return response.json()
async def create_thread(
messages: Optional[List[Dict[str, str]]] = None
) -> dict:
"""
Creates a new thread.
:param messages: Optional list of messages to initialize the thread with.
:return: JSON response containing thread details.
"""
url = f"{BASE_URL}/threads"
body = {}
if messages:
body["messages"] = messages
async with httpx.AsyncClient() as client:
response = await client.post(url, headers=HEADERS, json=body)
response.raise_for_status()
print(response.json())
return response.json()
async def delete_thread(thread_id: str) -> dict:
"""
Deletes a specific thread.
:param thread_id: The ID of the thread to delete.
:return: JSON response confirming deletion.
"""
url = f"{BASE_URL}/threads/{thread_id}"
async with httpx.AsyncClient() as client:
response = await client.delete(url, headers=HEADERS)
response.raise_for_status()
return response.json()
async def create_message(
thread_id: str,
role: str,
content: str
) -> dict:
"""
Creates a new message within a thread.
:param thread_id: The ID of the thread.
:param role: The role of the message sender ('user' or 'assistant').
:param content: The content of the message.
:return: JSON response containing the created message.
"""
url = f"{BASE_URL}/threads/{thread_id}/messages"
body = {
"role": role,
"content": content
}
async with httpx.AsyncClient() as client:
response = await client.post(url, headers=HEADERS, json=body)
response.raise_for_status()
return response.json()
async def list_messages(
thread_id: str,
limit: int = 20,
order: str = "desc",
after: Optional[str] = None,
before: Optional[str] = None,
run_id: Optional[str] = None
) -> dict:
"""
Lists messages within a specific thread.
:param thread_id: The ID of the thread.
:param limit: Number of messages to retrieve (1-100).
:param order: Sort order, 'asc' or 'desc'.
:param after: Cursor for pagination.
:param before: Cursor for pagination.
:param run_id: Filter messages by run ID.
:return: JSON response containing a list of messages.
"""
url = f"{BASE_URL}/threads/{thread_id}/messages"
params = {
"limit": limit,
"order": order
}
if after:
params["after"] = after
if before:
params["before"] = before
if run_id:
params["run_id"] = run_id
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=HEADERS, params=params)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
async def main():
"""demonstrate a new thread with image messages"""
# Upload a file
print("uploading...")
file_path = "my_image.png" # Replace with your file path
file_response = await upload_file(file_path)
print("File uploaded:")
print(json.dumps(file_response, indent=2))
file_id = file_response["id"]
# file_id = "file-blahblah" # existing file instead
# Create a new thread with initial messages (no role parameter)
print("creating thread")
thread = await create_thread(
messages=[
{
"role": "user",
"content": "You will use your GPT-4 vision.",
},
]
)
print("Thread created:")
print(json.dumps(thread, indent=2))
thread_id = thread["id"]
# Create a message in the thread, using a image file
content = [
{
"type": "text",
"text": "What's in this image?"
},
{
"type": "image_file",
"image_file": {
"file_id": file_id,
"detail": "auto"
}
}
]
message = await create_message( # uses role parameter
thread_id,
role="user",
content=content
)
print("Message created:")
print(json.dumps(message, indent=2))
# Create another image message in the thread, using image URL
content = [
{
"type": "text",
"text": "Answer about that and this image too."
},
{
"type": "image_url",
"image_url": {
"url": "https://i.imgur.com/xGOspjW.jpeg",
"detail": "low"
}
}
]
message = await create_message(
thread_id,
role="user",
content=content
)
print("Message created:")
print(json.dumps(message, indent=2))
# List the thread messages
print("retrieving message list...")
messages = await list_messages(thread_id)
print("Thread messages list:")
print(json.dumps(messages, indent=2))
# Clean up after demo
delete_status = await delete_thread(thread_id)
print(delete_status)
asyncio.run(main())