Upload image to assistant via API

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())
1 Like