OpenAI API Assistant does not find file in the uploaded files

Hi!

I am not able to get the Assistant API to find an uploaded file in the uploaded files. Could you please provide specific code to correct my code as I am not that good coder?

BR Markus

def initialize_openai_resources(file_path, model, analysis_type, user_prompt):
    """
    Initializes OpenAI client, uploads a file, creates an assistant, and initializes a thread based on predefined settings.

    Parameters:
    - file_path: Path to the file to be uploaded.
    - model: Model type for the assistant (e.g., 'gpt-3.5-turbo').

    Returns:
    - A dictionary containing the assistant and thread objects.
    """
    client = get_openai_client()
    print("OpenAI API key loaded successfully.\n")

    # Upload a file
    with open(file_path, 'rb') as file_data:
        my_file = client.files.create(
            file=file_data,
            purpose="assistants"
        )
    print(f"File uploaded successfully with ID: {my_file.id}\n")

    # Create Vector Store and upload a file there
    vector_store = client.beta.vector_stores.create(file_ids=[my_file.id])
    print(f"Vector store created successfully with ID: {vector_store.id}\n")
    print(f"File with ID {my_file.id} has been successfully attached to Vector store with ID {vector_store.id}\n")

    if analysis_type == 'thematic':
        instructions = ta_instruction.format(user_prompt=user_prompt)
    elif analysis_type == 'content':
        instructions = ca_instruction.format(user_prompt=user_prompt)
    elif analysis_type == 'grounded':
        instructions = gt_instruction.format(user_prompt=user_prompt)
    else:
        raise ValueError("Unsupported analysis type")

    # Create an Assistant
    my_assistant = client.beta.assistants.create(
        instructions=instructions,
        name="QDA-GPT",
        tools=[{"type": "file_search"}],
        model=model,
        tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}}
    )
    print(f"Assistant created successfully with ID: {my_assistant.id}\n")

    # Create a Thread
    my_thread = client.beta.threads.create()

    # Validate that everything has been initialized successfully.
    print(f"Thread created successfully with ID: {my_thread.id}\n")

    return {'assistant': my_assistant, 'file':my_file, 'thread': my_thread, 'vector_store': vector_store,}





def get_openai_response(content, assistant_id, thread_id):
    """
    Sends content to ChatGPT using an existing assistant and thread, and retrieves the response.

    Parameters:
    - content: The user's input to be sent to ChatGPT.
    - assistant_id: The ID of the initialized assistant.
    - thread_id: The ID of the initialized thread.

    Returns:
    - The response from ChatGPT as a string, or "No response." if no response is retrieved.
    """
    client = get_openai_client()
    print("OpenAI API key loaded successfully. Sending content to OpenAi Assistant.\n")


    try:

        # Send message to the thread
        my_thread_message = client.beta.threads.messages.create(
            thread_id=thread_id,
            role="user",
            content=content
        )
        if not my_thread_message or not my_thread_message.content:
            return "Message creation failed.", "Failure"
        print(f"Message sent to thread. Message ID: {my_thread_message.id}\n")


        # Run the assistant
        my_run = client.beta.threads.runs.create(
            thread_id=thread_id,
            assistant_id=assistant_id,
        )
        print(f"Assistant run initiated. Run ID: {my_run.id}\n")

        # Retrieve the Run status
        # Periodically retrieve the Run to check on its status to see if it has moved to completed
        print("Run status: in_progress", end="")
        sys.stdout.flush()  # Ensure "in_progress" is displayed immediately
        while my_run.status in ["queued", "in_progress"]:
            keep_retrieving_run = client.beta.threads.runs.retrieve(
                thread_id=thread_id,
                run_id=my_run.id
            )

            if keep_retrieving_run.status == "in_progress":
                print(".", end="")
                sys.stdout.flush()  # Print each dot immediately
                time.sleep(0.5)    # Increase/reduce this if necessary
            elif keep_retrieving_run.status == "completed":
                print("\nRun status: completed\n")

                # Retrieve the Messages added by the Assistant to the Thread
                all_messages = client.beta.threads.messages.list(
                    thread_id=thread_id
                )
                if not all_messages or not all_messages.data or not all_messages.data[0].content:
                    return "Response retrieval failed.", "Failure"

                response = all_messages.data[0].content[0].text.value

                print("------------------------------------------------------------\n")
                print("Response retrieved successfully.\n")
                print("Assistant response processed successfully.\n")
                return response
            else:
                print(f"\nRun status: {keep_retrieving_run.status}\n")
                break

        return "Failed to retrieve a valid response from OpenAI.\n"

    except Exception as e:
        print(f"An error occurred: {str(e)} \n")
        return "Failed to retrieve a valid response from OpenAI.\n"

Ok, let’s go in parts,

  1. Unless each wizard will only have one thread, I recommend that you separate the creation of the wizard from the creation of the thread, or create 3 wizards initially and then each one can have N number of threads, if you need to customize the instructions for each execution, you can modify the instructions in a single run.

Example:

try:
    run = client.beta.threads.runs.create_and_poll(
        thread_id=thread.id,
        assistant_id=assistant_id,
        instructions="instructions only for this run, this not change de main prompt for others runs running at same time"
    )
    print(f">>Asisstant run: {run.id}")
except Exception as e:
    print(f"Error creating run: {e}")
  1. I attach the corrected and improved algorithm:
def initialize_openai_resources(file_path, model, analysis_type, user_prompt):
    client = get_client()

    # Upload a file
    with open(file_path, 'rb') as file_data:
        my_file = client.files.create(
            file=file_data,
            purpose="assistants"
        )
    print(f"File uploaded successfully with ID: {my_file.id}\n")

    # Create Vector Store and upload a file there
    vector_store = client.beta.vector_stores.create(file_ids=[my_file.id])
    print(f"Vector store created successfully with ID: {vector_store.id}\n")
    print(f"File with ID {my_file.id} has been successfully attached to Vector store with ID {vector_store.id}\n")

    #I assume this has a specific proposal for you
    if analysis_type == 'thematic':
        instructions = ta_instruction.format(user_prompt=user_prompt)
    elif analysis_type == 'content':
        instructions = ca_instruction.format(user_prompt=user_prompt)
    elif analysis_type == 'grounded':
        instructions = gt_instruction.format(user_prompt=user_prompt)
    else:
        raise ValueError("Unsupported analysis type")

    # Create an Assistant
    my_assistant = client.beta.assistants.create(
        instructions=instructions,
        name="QDA-GPT",
        tools=[{"type": "file_search"}],
        model=model,
        tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}}
    )
    print(f"Assistant created successfully with ID: {my_assistant.id}\n")

    return my_assistant,my_file,vector_store

# Create one thread
def create_thread():
    try:
        thread = client.beta.threads.create()
        print(f"Thread ID: {thread.id}")
        return thread.id
    except Exception as e:
        print(f"Error creating thread: {e}")
        return None

def get_openai_response(content, assistant_id, thread_id):
    try: 
        # Send message to the thread
        my_thread_message = client.beta.threads.messages.create(
            thread_id=thread_id,
            role="user",
            content=content
        )
        if not my_thread_message or not my_thread_message.content:
            return "Message creation failed.", "Failure"
        print(f"Message sent to thread. Message ID: {my_thread_message.id}\n")


        # Just create_and_poll for get a terminal state
        print(f">>Running: {assistant_id} <> Thread:", thread_id)
        try:
            run = client.beta.threads.runs.create_and_poll( # This method is a helper, so you dont need to use a While
                thread_id=thread.id,
                assistant_id=assistant_id,
                # instructions="instructions just for this run if you want"
            )
            print(f">>Run: {run.id}")
        except Exception as e:
            print(f"Error: {e}")
            
        try:
            run_status = client.beta.threads.runs.retrieve(
                thread_id=thread.id,
                run_id=run.id
            )
            print(">>Status: ", run_status.status)
        except Exception as e:
            print(f"Error with run: {e}")

        if run_status.status == 'completed':
            reply_data = client.beta.threads.messages.list(
                thread_id=thread.id,
                run_id=run.id
            )
            # The assistants can return multiple responses in each run, with this code you can obtain multiple responses, within each response there can be different types of content, with this same logic you can obtain files, annotations, images, etc. ..
            for reply in reversed(reply_data.data):
                if reply.role == 'assistant':
                    reply_content = reply.content
                                    
                    for reply_item in reply_content:
                        type = reply_item.type                    
                        if type == 'text':
                            
                            if isinstance(reply_item.text, dict):
                                texto = reply_item.text["value"]
                                print(">>>Answer: ",texto)                            
                            else:
                                texto = reply_item.text.value
                                print(">>>Answer: ",texto)
        elif run_status.status == 'failed':
            print(">>Failed")
        elif run_status.status == 'requires_action':
            print(">>Use tool submit actions..")

    except Exception as e:
        print(f"An error occurred: {str(e)} \n")
        return "Failed to retrieve a valid response from OpenAI.\n"


# Create a thread just one time
thread_id = create_thread()

# Run all
get_openai_response("What is the attached file about?","asst_YoYPNANRaWuDb868y1tajTTY",thread_id)

# Then just run get_openai_response again only changing the prompt
get_openai_response("Another question about the file","asst_YoYPNANRaWuDb868y1tajTTY",thread_id)

Remember that the retrieval is automatic, the Assistant Prompt must specify that the retrieval is always done to answer any query

Happy coding

Leave comments in the code, read them bro!

Hi!

@kenny.mendieta thank you very much for the reply. I was able to get the Assistant to retrieve information from the file that is uploaded.

The purpose is to create an OpenAI Assistant that helps to perform qualitative analysis. The idea is that a new assistant is created for each user based on individual instructions and data. Then different prompts will be assigned to the Assistant and thread to retrieve answers regarding the data.

Below is my revised code based on your help. The problem is that with the first question I get something like this: “The first 10 words in the attached document were not found through the search. If you have any specific document or text in mind, feel free to upload it again for a more targeted search.”

However, with the second and third prompt, I am able to get suitable answers from the Assistant.

Here’s my revised code for testing purposes:

# Adjust these constants
model  = "gpt-3.5-turbo"
file_path = r"C:\Users\MM\PycharmProjects\QDA-GPT_project\data\Interviews.txt"



def get_openai_client():
    """
    Initialize and return an OpenAI client using the API key from Django settings.
    """
    api_key = settings.OPENAI_API_KEY
    if not api_key:
        raise ValueError("Failed to load the OPENAI_API_KEY from settings.\n\n")
    openai.api_key = api_key
    return OpenAI(api_key=api_key)


def initialize_openai_resources(file_path, model):
    client = get_openai_client()

    # Upload a file
    with open(file_path, 'rb') as file_data:
        my_file = client.files.create(
            file=file_data,
            purpose="assistants"
        )
    print(f"File uploaded successfully with ID: {my_file.id}\n")

    # Create Vector Store and upload a file there
    vector_store = client.beta.vector_stores.create(file_ids=[my_file.id])
    print(f"Vector store created successfully with ID: {vector_store.id}\n")
    print(f"File with ID {my_file.id} has been successfully attached to Vector store with ID {vector_store.id}\n")


    # Create an Assistant
    my_assistant = client.beta.assistants.create(
        instructions="Answer question regarding attached data.",
        name="QDA-GPT",
        tools=[{"type": "file_search"}],
        model=model,
        tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}}
    )
    print(f"Assistant created successfully with ID: {my_assistant.id}\n")

    return my_assistant, my_file, vector_store


# Create one thread
def create_thread():
    try:
        client = get_openai_client()
        thread = client.beta.threads.create()
        print(f"Thread ID: {thread.id}")
        return thread.id
    except Exception as e:
        print(f"Error creating thread: {e}")
        return None


def get_openai_response(content, assistant_id, thread_id):
    client = get_openai_client()

    try:
        # Send message to the thread
        my_thread_message = client.beta.threads.messages.create(
            thread_id=thread_id,
            role="user",
            content=content
        )
        print(f"Message sent successfully. Message ID: {my_thread_message.id}\n")

        if not my_thread_message or not my_thread_message.content:
            return "Message creation failed.", "Failure"
        print(f"Message sent to thread. Message ID: {my_thread_message.id}\n")

        # Just create_and_poll for get a terminal state
        print(f">>Running: {assistant_id} <> Thread:", thread_id)
        try:
            run = client.beta.threads.runs.create_and_poll(  # This method is a helper, so you dont need to use a While
                thread_id=thread_id,
                assistant_id=assistant_id,
                # instructions="instructions just for this run if you want"
            )
            print(f">>Run: {run.id}")
        except Exception as e:
            print(f"Error: {e}")

        try:
            run_status = client.beta.threads.runs.retrieve(
                thread_id=thread_id,
                run_id=run.id
            )
            print(">>Status: ", run_status.status)
        except Exception as e:
            print(f"Error with run: {e}")

        if run_status.status == 'completed':
            print("Run status: completed\n")
            reply_data = client.beta.threads.messages.list(
                thread_id=thread_id,
                run_id=run.id
            )
            # The assistants can return multiple responses in each run, with this code you can obtain multiple responses, within each response there can be different types of content, with this same logic you can obtain files, annotations, images, etc. ..
            for reply in reversed(reply_data.data):
                if reply.role == 'assistant':
                    reply_content = reply.content

                    for reply_item in reply_content:
                        type = reply_item.type
                        if type == 'text':

                            if isinstance(reply_item.text, dict):
                                texto = reply_item.text["value"]
                                print(">>>Answer: ",texto)
                            else:
                                texto = reply_item.text.value
                                print(">>>Answer: ",texto)
        elif run_status.status == 'failed':
            print(">>Failed")
        elif run_status.status == 'requires_action':
            print(">>Use tool submit actions..")

    except Exception as e:
        print(f"An error occurred: {str(e)} \n")
        return "Failed to retrieve a valid response from OpenAI.\n"


def delete_openai_resources(assistant_id, file_id, thread_id, vector_store_id):

    client = get_openai_client()
    print("OpenAI API key loaded successfully. \n")

    results = {}
    try:
        results['file'] = client.files.delete(file_id)
        print("File deleted. \n")
    except Exception as e:
        print(f"Failed to delete file: {e} \n")

    try:
        results['vector_store'] = client.beta.vector_stores.delete(vector_store_id)
        print("Vector store deleted. \n")
    except Exception as e:
        print(f"Failed to delete vector store: {e} \n")

    try:
        results['assistant'] = client.beta.assistants.delete(assistant_id)
        print("Assistant deleted. \n")
    except Exception as e:
        print(f"Failed to delete assistant: {e} \n")

    try:
        results['thread'] = client.beta.threads.delete(thread_id)
        print("Thread deleted. \n")
    except Exception as e:
        print(f"Failed to delete thread: {e} \n")

    return results



# Create a thread just one time
thread_id = create_thread()

# Initialize OpenAI resources
assistant, uploaded_file, vector_store = initialize_openai_resources(file_path, model)

# First question
get_openai_response("What are the first 10 words in the attached document?", assistant.id, thread_id)

# Second question
get_openai_response("What are the words from the 10th to the 20th in the attached document?", assistant.id, thread_id)

# Third question
get_openai_response("Summarize the content of this attachment in 5 sentences.", assistant.id, thread_id)

# Delete OpenAI resources
delete_openai_resources(assistant.id, uploaded_file.id, thread_id, vector_store.id)

I figured this out, so basically the problem was that at the time of the first prompt, some indexation etc. regarding files, vector store, etc. was not ready, and by adding time.sleep(1), I was able to get relevant response for the first prompt.