동적 TPM/RPM 할당
동적 TPM/RPM 할당 (Dynamic TPM/RPM Allocation)
프로젝트가 tpm/rpm을 너무 많이 소비하지 못하게 해요.
See Also: Request Prioritization - 대용량 트래픽에서 LLM API 요청을 우선 순위 큐에 추가해 우선 처리.
모델의 TPM/RPM 용량을 키와 팀 전체에 공유해요. 제한기는 모델이 얼마나 포화되었는지 지켜봐요. 기록된 사용량이 구성 가능한 포화 임계값 아래에 있으면 어떤 키든 유휴 용량을 사용할 수 있어요. 사용량이 임계값을 넘으면 각 우선 순위 수준이 예약된 몫으로 제한돼요. 코드 보기 (See Code)
출처: 문서
본문
빠른 시작 사용법 (Quick Start Usage)
config.yaml 설정
model_list:
- model_name: my-fake-model
litellm_params:
model: gpt-5.6-luna
api_key: my-fake-key
mock_response: hello-world
tpm: 60
litellm_settings:
callbacks: ["dynamic_rate_limiter_v3"]
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY # OR set `LITELLM_MASTER_KEY=".."` in your .env
database_url: postgres://.. # OR set `DATABASE_URL=".."` in your .env
프록시 시작:
litellm --config /path/to/config.yaml
테스트!
test.py:
"""
- Run 2 keys calling the same model
- model has 60 TPM
- Mock response returns 30 total tokens / request
- The model serves 2 requests in the window (2 x 30 = 60 tokens),
then 429s every key until the 60s window rolls
"""
import requests
from openai import OpenAI, RateLimitError
def create_key(api_key: str, base_url: str):
response = requests.post(
url="{}/key/generate".format(base_url),
json={},
headers={
"Authorization": "Bearer {}".format(api_key)
}
)
_response = response.json()
return _response["key"]
key_1 = create_key(api_key="sk-<your-litellm-api-key>", base_url="http://0.0.0.0:4000")
key_2 = create_key(api_key="sk-<your-litellm-api-key>", base_url="http://0.0.0.0:4000")
# call proxy with key 1 - works
openai_client_1 = OpenAI(api_key=key_1, base_url="http://0.0.0.0:4000")
response = openai_client_1.chat.completions.with_raw_response.create(
model="my-fake-model", messages=[{"role": "user", "content": "Hello world!"}],
)
print("Headers for call 1 - {}".format(response.headers))
_response = response.parse()
print("Total tokens for call - {}".format(_response.usage.total_tokens))
# call proxy with key 2 - works
openai_client_2 = OpenAI(api_key=key_2, base_url="http://0.0.0.0:4000")
response = openai_client_2.chat.completions.with_raw_response.create(
model="my-fake-model", messages=[{"role": "user", "content": "Hello world!"}],
)
print("Headers for call 2 - {}".format(response.headers))
_response = response.parse()
print("Total tokens for call - {}".format(_response.usage.total_tokens))
# call proxy with key 2 - fails
try:
openai_client_2.chat.completions.with_raw_response.create(model="my-fake-model", messages=[{"role": "user", "content": "Hey, how's it going?"}])
raise Exception("This should have failed!")
except RateLimitError as e:
print("This was rate limited b/c - {}".format(str(e)))
예상 응답
This was rate limited b/c - Error code: 429 - {'error': {'message': 'Model capacity reached for my-fake-model. Priority: None, Rate limit type: tokens, Model TPM: 60, Model RPM: not configured, Remaining: 0', 'type': 'throttling_error', 'param': None, 'code': '429'}}
토큰은 각 응답이 완료된 후 제한기의 카운터에 기록되므로, 블록은 기록된 사용량이 모델의 TPM에 도달한 후의 첫 요청에서 발동해요. 예산이 소진될 때 이미 진행 중인 요청은 차단되지 않아요. How enforcement works 참고.
[BETA] 우선 순위 설정 / 할당량 예약 (Set Priority / Reserve Quota)
다른 환경이나 사용 사례에 TPM/RPM 용량을 예약해요. 이는 중요한 프로덕션 워크로드가 항상 보장된 용량을 갖도록 보장하며, 개발이나 낮은 우선 순위 작업은 남은 할당량을 사용해요.
사용 사례:
- 프로덕션 vs 개발 환경
- 실시간 애플리케이션 vs 배치 처리
- 중요 서비스 vs 실험 기능
Enterprise 기능
우선 순위에 따라 키에 TPM/RPM을 예약하려면 LiteLLM Enterprise 라이선스가 필요해요. 무료 30일 체험판을 시작하거나 데모를 예약하세요. Enterprise에 포함된 것 보기.
우선 순위 예약 동작 방식 (How Priority Reservation Works)
우선 순위 예약은 모델의 총 TPM/RPM의 백분율을 특정 우선 순위 수준에 할당해요. 높은 우선 순위의 키가 예약된 할당량에 먼저 보장된 액세스를 얻어요.
예시 시나리오:
- 모델이 총 10 RPM 용량
- 우선 순위 예약:
{"prod": 0.9, "dev": 0.1} - 결과: 프로덕션 키가 9 RPM 보장, 개발 키가 1 RPM 보장
구성 (Configuration)
1. config.yaml 설정
model_list:
- model_name: gpt-5.6-luna
litellm_params:
model: "gpt-5.6-luna"
api_key: os.environ/OPENAI_API_KEY
rpm: 10 # Total model capacity
litellm_settings:
callbacks: ["dynamic_rate_limiter_v3"]
priority_reservation:
"prod": 0.9 # 90% reserved for production (9 RPM)
"dev": 0.1 # 10% reserved for development (1 RPM)
# Alternative format:
# "prod":
# type: "rpm"
# Reserve based on requests per minute
# value: 9 # 9 RPM = 90% of 10 RPM capacity
# "dev":
# type: "tpm"
# Reserve based on tokens per minute
# value: 100 # 100 TPM
priority_reservation_settings:
default_priority: 0 # Weight (0%) assigned to keys without explicit priority metadata
saturation_threshold: 0.50 # A model is saturated if it has hit 50% of its RPM limit
saturation_check_cache_ttl: 60 # How long (seconds) saturation values are cached locally
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY # OR set `LITELLM_MASTER_KEY=".."` in your .env
database_url: postgres://.. # OR set `DATABASE_URL=".."` in your.env
구성 상세 (Configuration Details):
priority_reservation:Dict[str, Union[float, PriorityReservationDict]]- 키(str): 우선 순위 수준 이름 (
"prod","dev","critical"등 어떤 문자열이든) - 값: float(0.0-1.0) 또는 type/value 있는 dict
- Float:
0.9= 용량의 90% - Dict:
{"type": "rpm", "value": 9}= 분당 9 요청 - 지원 타입:
"percent","rpm","tpm"
- Float:
- 키(str): 우선 순위 수준 이름 (
priority_reservation_settings: 객체 (선택)default_priority(float): 우선 순위 메타데이터가 없는 API 키에 할당된 가중치/백분율(0.0~1.0, 기본 0.25). 명시적 우선 순위가 없는 모든 키는 이 크기의 하나의 풀을 공유해요. 키별 할당이 아니에요. 따라서 라벨이 없는 두 팀은 서로 사이에 바닥이 없이 같은 기본 풀 안에서 경쟁해요.saturation_threshold(float): 모델에 대한 엄격한 우선 순위 강제가 시작되는 포화 수준(0.0~1.0). 포화는max(current_rpm/max_rpm, current_tpm/max_tpm)로 계산돼요. 이 임계값 아래에서는 관대 모드가 미사용 용량에서 우선 순위가 빌릴 수 있게 해요. 위에서는 엄격 모드가 정규화된 우선 순위 한도를 강제해요.- 예시: 모델 사용량이 낮으면 키가 할당된 몫보다 더 사용할 수 있어요. 사용량이 높으면 키가 할당된 몫으로 엄격히 제한돼요.
saturation_check_cache_ttl(int): Redis에서 포화 값을 읽을 때 로컬 캐시의 TTL(초, 기본 60). 멀티 노드 배포에서 이는 노드가 같은 포화 상태로 수렴하는 속도를 제어해요. 낮은 값은 더 빠른 수렴을 의미하지만 더 많은 Redis 읽기를 의미해요.- 예시: 더 빠른 멀티 노드 일관성에 5, 또는 항상 Redis에서 직접 읽으려면 0.
프록시 시작:
litellm --config /path/to/config.yaml
팀이나 키에 우선 순위 설정
우선 순위는 팀 수준 또는 키 수준에서 설정할 수 있어요. 팀 수준 우선 순위가 키 수준 우선 순위보다 우선해요.
옵션 A: 팀에 우선 순위 설정 (권장)
팀 내의 모든 키가 팀의 우선 순위를 상속해요. 이는 특정 환경이나 프로젝트의 모든 키가 같은 우선 순위를 갖게 하려는 경우 유용해요.
curl -X POST 'http://0.0.0.0:4000/team/new' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{
"team_alias": "production-team",
"metadata": {"priority": "prod"}
}'
이 팀용 키 생성:
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{
"team_id": "team-id-from-previous-response"
}'
옵션 B: 개별 키에 우선 순위 설정
키에 직접 우선 순위를 설정해요. 키별 세밀한 제어가 필요할 때 유용해요.
프로덕션 키:
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{
"metadata": {"priority": "prod"}
}'
개발 키:
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{
"metadata": {"priority": "dev"}
}'
우선 순위 없는 키 (default_priority 가중치 사용):
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{}'
예상 응답:
{
"key": "sk-...",
"metadata": {"priority": "prod"}, // or "dev"
...
}
우선 순위 해석 순서 (Priority Resolution Order):
- 키가 metadata.priority가 설정된 팀에 속하면 → 팀 우선 순위 사용
- 그 외 키에 metadata.priority가 있으면 → 키 우선 순위 사용
- 그 외 → 콘피그에서 default_priority 사용
3. 우선 순위 할당 테스트
프로덕션 키 테스트 (9 RPM 받아야 함):
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ***' \
-d '{
"model": "gpt-5.6-luna",
"messages": [{"role": "user", "content": "Hello from prod"}]
}'
개발 키 테스트 (1 RPM 받아야 함):
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ***' \
-d '{
"model": "gpt-5.6-luna",
"messages": [{"role": "user", "content": "Hello from dev"}]
}'
예상 동작 (Expected Behavior)
우선 순위의 예약은 바닥이지 천장이 아니에요. 키가 실제로 사용할 수 있는 것은 모델이 얼마나 포화되었는지에 따라 달라져요:
- 포화 임계값 아래 (관대 모드): 모델 전체 용량만 강제돼요. 우선 순위와 무관하게 어떤 키든 자체 예약 너머의 유휴 용량을 사용할 수 있어요.
- 임계값 이상 (엄격 모드): 각 우선 순위는 창의 나머지 동안 예약된 몫으로 제한돼요. 우선 순위 풀이 소진된 키는 429를 받고, 예약 안에 있는 우선 순위는 계속 서빙돼요.
- 명시적 우선 순위가 없는 키는 default_priority 가중치로 하나의 풀을 공유해요. 위 구성(default_priority: 0)에서는 아무것도 얻지 못하고, 기본(0.25)에서는 라벨 없는 모든 키가 함께 25%를 얻어요.
- 관대 모드에서 빌리는 동안 키가 소비한 용량은 엄격 모드가 발동해도 되돌려지지 않아요. 바닥은 아직 소비되지 않은 용량을 보호하므로, 바닥은 보호된 우선 순위가 창 전체에 트래픽을 보낼 때만 완전히 보장돼요.
- (1)과 (2)의 중요한 결과: 포화는 기록된 사용량을 측정하지, 활성 키 수를 측정하지 않아요. 유휴 모델의 단독 키도 자체 트래픽으로 엄격 모드를 발동시키므로, 단일 우선 순위가 한 창에 사용할 수 있는 최대는
max(its reservation, saturation_threshold) x model capacity더하기 최대 하나의 진행 중 요청이에요. 외로운 키는 예약이나 임계값이 허용하지 않는 한 모델의 100%에 도달하지 않아요.
작동 예시 (Worked example)
tpm: 1000, priority_reservation: {"team_a": 0.5, "team_b": 0.5}, saturation_threshold: 0.5 모델. 팀 A가 매분 용량의 100%+를 요구하고, 팀 B는 행이 말하는 것만 요구한다고 합시다. 각 행은 하나의 새 60초 창이에요:
| 창 | A 요구 | B 요구 | A 서빙 | B 서빙 | 이유 |
|---|---|---|---|---|---|
| 1 | 100%+ | 유휴 | ~500 | 0 | A 단독이 모델을 50%로 포화시키고, 엄격 모드가 A를 자체 500 바닥으로 캡 |
| 2 | 100%+ | 500 | ~500 | ~500 | 엄격 모드가 용량을 50/50 바닥으로 분할 |
| 3 | 100%+ | 400 | ~500 | ~400 | B가 바닥 아래라 완전히 서빙, 429 0개; A가 나머지 차지 |
saturation_threshold를 0.8로 올리면 창 1이 ~800로 바뀌지만(A가 임계값까지 대출), 창 2가 약해져요. A가 엄격 모드 전에 관대 모드에서 ~600을 움켜쥐고, B는 A의 차용 용량이 되돌려지지 않고 모델 전체 캡이 나머지를 막으므로 ~420에서 멈춰요.
강제 동작 방식 (How enforcement works)
요청 수는 LLM 호출 전에 확인·증가되므로 RPM 한도는 정확해요. 토큰 수는 응답이 완료된 후에만 알 수 있으므로, TPM 강제는 기록된 사용량에 대한 입장 제어예요. 기록된 토큰이 한도 아래일 때 요청을 허용하고, 이후 자체 토큰이 카운터에 올라가요. 두 가지 결과:
- 창에서 서빙된 실제 토큰은 동시 전송 키당 대략 한 요청의 토큰만큼 구성된 TPM을 초과할 수 있어요. 함께 허용된 병렬 요청 버스트는 더 초과할 수 있어요. TPM은 분 내 하드 캡이 아니에요.
- 구성된 TPM이 일반적인 단일 응답보다 작으면, 단일 요청이 전체 예산을 날려버리고 강제가 대략 창당 한 요청으로 퇴화해요. 이 기능을 테스트할 때 TPM을 일반 요청당 토큰 수보다 훨씬 크게 잡으세요.
창은 캘린더 분이 아니라 모델의 첫 요청부터 60초 롤링이에요. 멀티 노드 배포에서는 포화 값이 추가로 saturation_check_cache_ttl초 동안 로컬로 캐시되므로, 트리거 트래픽을 서빙하지 않은 노드에서는 엄격 모드가 최대 그 초만큼 늦게 발동할 수 있어요.
요율 제한 오류 예시 (Rate Limit Error Examples):
엄격 모드에서 우선 순위 풀 소진:
{
"error": {
"message": "Priority-based rate limit exceeded. Model: gpt-5.6-luna, Priority: dev, Rate limit type: tokens, Model TPM: 1000, Model RPM: not configured, Remaining: 0, Model saturation: 52.8%",
"type": "throttling_error",
"code": "429"
}
}
모델 전체 용량 소진 (모든 우선 순위):
{
"error": {
"message": "Model capacity reached for gpt-5.6-luna. Priority: prod, Rate limit type: tokens, Model TPM: 1000, Model RPM: not configured, Remaining: 0",
"type": "throttling_error",
"code": "429"
}
}
데모 동영상 (Demo Video)
이 동영상은 우선 순위 예약으로 동적 요율 제한을 설정하고 locust 테스트로 동작을 검증하는 과정을 안내해요.