Python SDK: Different results when passing API_Key as param or as environment variable

I have an assistant in a project.
I need to pass the api_key as a parameter ( this process access different projects with different api_keys )

and it doesn’t work - it seems that something below is always trying to access the env variable
Here is a script that reproduces the issue:

import os
import openai
from retrying import retry


def should_reject(run_status):
    return run_status.status != "completed"


@retry(retry_on_result=should_reject, stop_max_attempt_number=20, wait_fixed=3000)
def retrieve_run_status(thread_id, run_id):
    return openai.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run_id)


def monitor_thread_status(thread_id, run_id):
    run_status = retrieve_run_status(thread_id, run_id)
    return run_status

def access_the_assistant(api_key):

    question = "What can you help with?"
    project_id = 'proj_..........'
    assistant_id = 'asst_...........'

    client = openai.OpenAI(project=project_id, api_key=api_key)

    the_thread = client.beta.threads.create()
    thread_id = the_thread.id

    _ = client.beta.threads.messages.create(
        thread_id, role="user", content=question
    )
    run = client.beta.threads.runs.create(
        thread_id=thread_id, assistant_id=assistant_id
    )

    run_status = retrieve_run_status(thread_id, run.id)

    thread_messages = client.beta.threads.messages.list(
        thread_id,
    )
    return ( str (thread_messages.data) )

def main():
    #load the api key
    api_key = os.getenv("OPENAI_API_KEY")

    # if uncommented fix the issue - caches the api_key
    # ret = access_the_assistant(api_key)
    # print(ret)

    del os.environ['OPENAI_API_KEY']

    ret = access_the_assistant(api_key)
    print(ret)


if __name__ == "__main__":
    main()

specifically: Im failing here:

in some module level client initialization.

To iterate what i think i need

  • in the same process
    two different OpenAI clients
    client = openai.OpenAI(project=project_id, api_key=api_key)

each initialized with its own api_key

The client has it’s own default method for getting the API key from the environment variable, it does not need to be passed.

In main, you set api_key, and never change it.

You then have examples of calling a function twice with the same unchanged variable as a positional param. Messing with run env variables isn’t affecting that.

If you want to rotate keys within code, for example to use keys scoped to a different project, you might use multiple alternate environment variables of your own creation:

def main():
    # Default key is used by client init
    return = access_the_assistant()
    print(return)

    # Load the first API key
    api_key1 = os.getenv("OPENAI_API_KEY_1")
    return1 = access_the_assistant(api_key=api_key1)
    print(return1)

    # Change to a second API key
    api_key2 = os.getenv("OPENAI_API_KEY_2")
    return2 = access_the_assistant(api_key=api_key2)
    print(return2)

The hard-coded project_id instead can also be an numbered environment variable to accompany the keys. There is no application I know where project_id needs to be passed, except as a mechanism to create disagreement and block using the wrong API key.

the code you’ve pasted looks good.
at least for me it doesn’t work => why?

as you’ve said - “The client has it’s own default method for getting the API key”
the problem is there is no the
sometimes i want it to be key1 ( to access project1) and sometimes key2 ( to access project2)

if the client key is not the same as the initialized object key - the code fails
( either in thread not found or assistant not reachable etc… )

Assistants are now scoped to projects and the keys.

They are expected to be inaccessible from other project keys.

There’s no grand announcement of this, you just have to read docs to see what OpenAI is up to that breaks applications (like calling moderations or continuing fine-tuning as examples) in an attempt to manage your data when it all could have been on your side.

You can create the openai client instance globally, and then directly act on it with client.api_key at every turn if you have other unexpected behaviors in procedural code.

yes
that’s the main point.

projects need separate api_key
=>
we need to have separate OpenAI objects initialized with different keys
=>
any code in the OpenAI package that relies on directly accessing the env variable
is problematic
( unless the user doesn’t pass the key - which is a nice convenience thingy)

Another possibility if you are getting “unintended consequences” or “I want my own behavior” with your understanding of how things are actually working and the scoping of client in your code, is go into _client.py of your own openai site-packages install and take out all of this environment variable default operation:

        This automatically infers the following arguments from their corresponding environment variables if they are not provided:
        - `api_key` from `OPENAI_API_KEY`
        - `organization` from `OPENAI_ORG_ID`
        - `project` from `OPENAI_PROJECT_ID`
        """
        if api_key is None:  ### COMMENT OUT
            api_key = os.environ.get("OPENAI_API_KEY")  ### COMMENT OUT, etc
        if api_key is None:
            raise OpenAIError(
                "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable"
            )
        self.api_key = api_key
...

At the moment i don’t managae to work around even in the most ugly way:


    os.environ['OPENAI_API_KEY'] = data2['api_key']
    openai._reset_client()
    ret = access_the_assistant(data2)
    print(ret)

    os.environ['OPENAI_API_KEY'] = data1['api_key']
    openai._reset_client()
    ret = access_the_assistant(data1)
    print(ret)

still getting:
{‘error’: {‘message’: “No thread found with id ‘thread_bekANeeA8YAsu1Mazj5bHAdS’.”, ‘type’: ‘invalid_request_error’, ‘param’: None, ‘code’: None}}

on the method:
openai.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run_id)

It looks like a bug here(the code in first post):
retrieve_run_status use openai.beta…, not client.beta… so when change client it will not compatible.
You may try pass the client instance to retrieve_run_status and see if bug resolved.
Two different apikeys with two clients should not be so problematic?

2 Likes

you’re totally right.
and that was my problem
tnx!

1 Like