
Der frustrierende Fehler RateLimitError: Error code: 429
tritt häufig bei der Nutzung der (Azure) OpenAI-API auf, selbst wenn das Limit nicht überschritten wird. Es wird gezeigt, wie eine Batch-Logik dabei helfen kann, die Herausforderungen bei der Arbeit mit LLMs zu meistern.
Der Fehler liegt hier häufig in der Größe der Anfrage. Dabei ist es nicht entscheidend, ob der Zugriff direkt über die von OpenAI bereitgestellte API erfolgt, oder über einen Wrapper wie von Langchain zur Verfügung gestellt wird. Eine Lösung lässt sich meist erst nach langer Internetrecherche finden. Der Grund, ein zu großer Text-Input für einen Request führt zu der irritierenden Fehlermeldung.
Nehmen wir an es soll ein RAG-System aufgebaut werden. Dazu müssen die Informationen, welche als Text vorliegen, in einzelne Chunk zerlegt werden. Anschließend müssen die Chunks mithilfe der Embedding-API in Vektoren überführt werden. Als Input dient dabei eine Liste von Texten oder ein einzelner Text. Wie ein möglicher Anwendungsfall aussehen könnte, kann an dieser Stelle nachgelesen werden.
In Python kann die Embedding-API über den Azure OpenAI Client mit der Funktion embeddings.create() erzeugt werden.
client = AzureOpenAI(
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-09-01-preview",
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
)
embedding = client.embeddings.create(
input=text,
model="EMBEDDING_MODEL",
)
Pro Request an die API darf eine maximale Anzahl an Input-Token nicht überschritten werden. Diese ist von der Länge des Eingabetextes oder der Eingabetexte abhängig. Ein Token ist dabei die kleinste Einheit, in der ein Text zerlegt wird. Ein Token kann ein Wort sein oder ein Teil eines Wortes oder ein Satzzeichen siehe Tokenizer OpenAi.
Tipp: Nicht nur die Größe des gesamten Inputs ist relevant, sondern auch eines einzelnen Textes. Dabei ist zu beachten, dass ein einzelner Text oder ein Text aus einem Array die maximal 8192 Token nicht überschreiten darf (openai-python/src/openai/resources/embeddings.py at main · openai/openai-python).
Bei den von OpenAI bereitgestellten Model zum Embedding “text-embedding-3-large"
, kommt es bei der Überschreitung des Tokenlimits pro Request zu der Fehlermeldung:
Error code: 429 - {'error': {'code': '429', 'message': 'Requests to the Embeddings_Create Operation under Azure OpenAI API version 2024-08-01-preview have exceeded call rate limit of your current OpenAI S0 pricing tier. Please retry after 86400 seconds. Please go here:
Dynamics 365 Customer Voice if you would like to further increase the default rate limit.
Der Fehler hat allerdings nichts mit dem “OpenAI S0 Pricing Tier"
zu tun und eine Erhöhung mithilfe des referenzierten Links hilft nicht zur Lösung des Problems. Der Hinweis, es nach 86400 Sekunden oder auch einem Tag erneut zu versuchen, kann somit nicht weiterhelfen. Der Fehler entsteht dadurch, dass ein einzelner Request auf einmal nur eine bestimmte Menge an Tokens verarbeiten kann. Die genaue Anzahl an maximalen Tokens pro Request, scheint dabei zu variieren und liegt nicht bei einem absoluten Wert.
Use Case for Error 429
Imports and Definitions
import os
import random
import requests
import tiktoken
import numpy as np
from dotenv import load_dotenv
from openai.lib.azure import AzureOpenAI
random.seed(42)
load_dotenv("secrets.env")
API_VERSION = "2024-08-01-preview"
EMBEDDING_MODEL = "text-embedding-3-large"
API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
AZURE_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
WORDLIST_URL = "https://www.mit.edu/~ecprice/wordlist.10000"
WORDLIST_PATH = "../wordlist.10000.txt"
Define Functions
def download_wordlist(url: str, file_path: str):
response = requests.get(url)
response.raise_for_status() # Ensure we notice bad responses
with open(file_path, "w") as file:
file.write(response.text)
def load_text_file(file_path: str) -> list[str]:
with open(file_path, "r") as file:
words = file.read().splitlines()
return words
# create a stringarray with the size of n with random words
def generate_random_strings(n, words):
random_strings = []
for _ in range(n):
sentence = " ".join(random.choices(words, k=130))
random_strings.append(sentence)
return random_strings
def calculate_tokens(texts: list[str], model: str):
encoding = tiktoken.encoding_for_model(model)
# Tokenize the input text
tokens = [encoding.encode(text) for text in texts]
# Calculate the total number of tokens
total_tokens = sum(len(token) for token in tokens)
# Return the total number of tokens
return total_tokens
Create text for embedding
if not os.path.exists(WORDLIST_PATH):
download_wordlist(WORDLIST_URL, WORDLIST_PATH)
client = AzureOpenAI(
api_key=API_KEY,
api_version=API_VERSION,
azure_endpoint=AZURE_ENDPOINT,
)
words = load_text_file(WORDLIST_PATH)
# Generate random sentence to have around 256.000+ tokens
random_strings = generate_random_strings(1700, words)
# example of random strings
print("Example of the first 10 random strings:\n")
for string in random_strings[5:10]:
print(" ".join(string.split()[10:20]).join(["...", "..."]))
# Calculate the total number of tokens
tokens = calculate_tokens(random_strings, "text-embedding-3-large")
print(f"\n Total number of tokens for this request: {tokens}")
Example of the first 10 random strings:
...michael enquiries yahoo bryan ringtone notebook se cut llp infant...
...trek legal tales sufficiently drawing scripting helmet under lee ski...
...pharmaceutical spend organize timeline patch equivalent impact moses recording benefits...
...savage superb lived institutions wagner assurance jewelry gzip pray keys...
...sox share b substance antiques adware treated substitute moms modem...
Total number of tokens for this request: 256035
Create a vector with the embedding-API
# The Error 429 Error happens when the Input Token is more than 256.000 tokens per request
embeddings = client.embeddings.create(
input=random_strings,
model=EMBEDDING_MODEL,
)
json = embeddings.model_dump()
print(json["usage"])
---------------------------------------------------------------------------
RateLimitError Traceback (most recent call last)
Cell In[55], line 2
1 # The Error 429 Error happens when the Input Token is more than 256.000 tokens per request
----> 2 embeddings = client.embeddings.create(
3 input=random_strings,
4 model=EMBEDDING_MODEL,
5 )
6 json = embeddings.model_dump()
8 print(json["usage"])
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/resources/embeddings.py:124, in Embeddings.create(self, input, model, dimensions, encoding_format, user, extra_headers, extra_query, extra_body, timeout)
118 embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call]
119 base64.b64decode(data), dtype="float32"
120 ).tolist()
122 return obj
--> 124 return self._post(
125 "/embeddings",
126 body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams),
127 options=make_request_options(
128 extra_headers=extra_headers,
129 extra_query=extra_query,
130 extra_body=extra_body,
131 timeout=timeout,
132 post_parser=parser,
133 ),
134 cast_to=CreateEmbeddingResponse,
135 )
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1283, in SyncAPIClient.post(self, path, cast_to, body, options, files, stream, stream_cls)
1269 def post(
1270 self,
1271 path: str,
(...)
1278 stream_cls: type[_StreamT] | None = None,
1279 ) -> ResponseT | _StreamT:
1280 opts = FinalRequestOptions.construct(
1281 method="post", url=path, json_data=body, files=to_httpx_files(files), **options
1282 )
-> 1283 return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:960, in SyncAPIClient.request(self, cast_to, options, remaining_retries, stream, stream_cls)
957 else:
958 retries_taken = 0
--> 960 return self._request(
961 cast_to=cast_to,
962 options=options,
963 stream=stream,
964 stream_cls=stream_cls,
965 retries_taken=retries_taken,
966 )
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1049, in SyncAPIClient._request(self, cast_to, options, retries_taken, stream, stream_cls)
1047 if remaining_retries > 0 and self._should_retry(err.response):
1048 err.response.close()
-> 1049 return self._retry_request(
1050 input_options,
1051 cast_to,
1052 retries_taken=retries_taken,
1053 response_headers=err.response.headers,
1054 stream=stream,
1055 stream_cls=stream_cls,
1056 )
1058 # If the response is streamed then we need to explicitly read the response
1059 # to completion before attempting to access the response text.
1060 if not err.response.is_closed:
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1098, in SyncAPIClient._retry_request(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)
1094 # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
1095 # different thread if necessary.
1096 time.sleep(timeout)
-> 1098 return self._request(
1099 options=options,
1100 cast_to=cast_to,
1101 retries_taken=retries_taken + 1,
1102 stream=stream,
1103 stream_cls=stream_cls,
1104 )
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1049, in SyncAPIClient._request(self, cast_to, options, retries_taken, stream, stream_cls)
1047 if remaining_retries > 0 and self._should_retry(err.response):
1048 err.response.close()
-> 1049 return self._retry_request(
1050 input_options,
1051 cast_to,
1052 retries_taken=retries_taken,
1053 response_headers=err.response.headers,
1054 stream=stream,
1055 stream_cls=stream_cls,
1056 )
1058 # If the response is streamed then we need to explicitly read the response
1059 # to completion before attempting to access the response text.
1060 if not err.response.is_closed:
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1098, in SyncAPIClient._retry_request(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)
1094 # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
1095 # different thread if necessary.
1096 time.sleep(timeout)
-> 1098 return self._request(
1099 options=options,
1100 cast_to=cast_to,
1101 retries_taken=retries_taken + 1,
1102 stream=stream,
1103 stream_cls=stream_cls,
1104 )
File ~/Library/Caches/pypoetry/virtualenvs/demo-429-ratelimit-eo_0Q7ut-py3.12/lib/python3.12/site-packages/openai/_base_client.py:1064, in SyncAPIClient._request(self, cast_to, options, retries_taken, stream, stream_cls)
1061 err.response.read()
1063 log.debug("Re-raising status error")
-> 1064 raise self._make_status_error_from_response(err.response) from None
1066 return self._process_response(
1067 cast_to=cast_to,
1068 options=options,
(...)
1072 retries_taken=retries_taken,
1073 )
RateLimitError: Error code: 429 - {'error': {'code': '429', 'message': 'Requests to the Embeddings_Create Operation under Azure OpenAI API version 2024-08-01-preview have exceeded call rate limit of your current OpenAI S0 pricing tier. Please retry after 86400 seconds. Please go here: https://aka.ms/oai/quotaincrease if you would like to further increase the default rate limit.'}}
Um das Problem mit den zu großen Input-Token zu lösen, sollte die Anzahl der Input-Token für einen Request klein genug sein, um den Fehler zu umgehen. Beispielsweise kann so die Anzahl der Token für einen Request aus dem Notebook oben mithilfe eines Batch-Jobs beschränkt werden. Der einzelne Request kann nun von dem Embedding Modell verarbeitet werden.
Solution to fix the issue for the the misleading error message
# Important: Split the array into parts that do not overwhelm the API in one request
split_random_strings = np.array_split(random_strings, 10)
embeddings = []
for i, split in enumerate(split_random_strings):
embedding = client.embeddings.create(
input=split,
model=EMBEDDING_MODEL,
)
embeddings.append(embedding)
print(f"Request {i+1} of {len(split_random_strings)}")
print(embedding.model_dump()["usage"])
print("\n")
split_random_strings = np.array_split(random_strings, 10)
Request 1 of 10
{'prompt_tokens': 25751, 'total_tokens': 25751}
Request 2 of 10
{'prompt_tokens': 25686, 'total_tokens': 25686}
Request 3 of 10
{'prompt_tokens': 25528, 'total_tokens': 25528}
Request 4 of 10
{'prompt_tokens': 25531, 'total_tokens': 25531}
Request 5 of 10
{'prompt_tokens': 25523, 'total_tokens': 25523}
Request 6 of 10
{'prompt_tokens': 25655, 'total_tokens': 25655}
Request 7 of 10
{'prompt_tokens': 25553, 'total_tokens': 25553}
Request 8 of 10
{'prompt_tokens': 25528, 'total_tokens': 25528}
Request 9 of 10
{'prompt_tokens': 25579, 'total_tokens': 25579}
Request 10 of 10
{'prompt_tokens': 25701, 'total_tokens': 25701}
Fazit
Abschließend lässt sich festhalten, dass die ausgegebene Fehlermeldung häufig auf eine Überschreitung des Tokenlimits hinweist. Dies kann frustrierend sein, insbesondere wenn sie fälschlicherweise, wie im Fehlercode erklärt, mit dem “Pricing Tier” in Verbindung gebracht wird.
Um diese Probleme zu vermeiden, ist es ratsam, die Token-Anzahl im Voraus zu überprüfen und gegebenenfalls Texte zu kürzen oder aufzuteilen. So kann sichergestellt werden, dass Anfragen effizient bearbeitet werden und die volle Funktionalität der API genutzt werden kann.
Letztlich ist ein proaktiver Umgang mit den Token-Beschränkungen der Schlüssel zu einer erfolgreichen Integration der OpenAI API in Projekte.
Bei Fragen stehen wir Ihnen gerne zur Verfügung. Weitere Informationen zu unseren Angeboten finden Sie außerdem hier.