[BETA] LiteLLM 관리형 파일 - Batches 함께 쓰기
[BETA] LiteLLM 관리형 파일 - Batches 함께 쓰기
LiteLLM 관리형 파일(Managed Files)을 Batches API와 함께 사용해, 배치 모델을 여러 Azure Batch 디플로이먼트에 걸쳐 로드 밸런싱하고, 키/사용자/팀 단위로 배치 모델 접근을 제어할 수 있어요. 하나의 배치 입력 파일을 여러 디플로이먼트에 동시에 써서 부하를 분산해요.
이 기능은 프록시 전용이며 별도 Enterprise 라이선스가 필요 없는 Free Enterprise 기능이에요. 파일 id 저장을 위해 Postgres DB가 필요해요.
출처: 문서
본문
무료 Enterprise 기능이에요.
litellm[proxy] 패키지나 아무 litellm 도커 이미지에서 사용할 수 있고, Enterprise 라이선스는 필요 없어요.
| 특성 | 지원 | 설명 |
|---|---|---|
| Proxy | ✅ | |
| SDK | ❌ | 파일 id 저장에 Postgres DB 필요 |
| Batch 공급자 전체에서 사용 | ✅ |
개요 (Overview)
이 기능은 다음과 같이 사용해요:
- 여러 Azure Batch 디플로이먼트에 걸쳐 로드 밸런싱하기
- 키/사용자/팀 단위로 배치 모델 접근 제어하기 (채팅 완성 모델과 동일)
(프록시 관리자) 사용법
개발자에게 Batch 모델 접근을 주는 방법이에요.
1. config.yaml 설정하기
- 각 모델에
mode: batch를 지정해 개발자가 이게 배치 모델임을 알 수 있게 해요. - 특정 batch 공급자나 모델에 대해 배치 입력 파일의 사전 읽기(pre-read)를 선택적으로 건너뛸 수 있어요 (커스텀 vLLM 배치 디플로이먼트의 대용량 파일에 유용).
model_list:
- model_name: "gpt-4o-batch"
litellm_params:
model: azure/gpt-4o-mini-general-deployment
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
model_info:
mode: batch # tells developers this is a batch model
- model_name: "gpt-4o-batch"
litellm_params:
model: azure/gpt-4o-mini-special-deployment
api_base: os.environ/AZURE_API_BASE_2
api_key: os.environ/AZURE_API_KEY_2
model_info:
mode: batch # tells developers this is a batch model
general_settings:
# Optional: do not charge batch input files against TPM/RPM
# disable_batch_input_file_rate_limiting: true
# Optional: apply this behavior only to selected providers
skip_batch_input_file_rate_limiting_for_providers:
- hosted_vllm
litellm_settings:
# Optional: require target_model_names on POST /v1/files (blocks classic file uploads)
# require_managed_files: true
기본적으로 LiteLLM은 각 배치 입력 파일을 제출 전에 읽고, 그 토큰과 레코드 수를 호출자의 TPM·RPM 한도에 반영해요. 대용량 파일에서는 이로 인해 지연이 늘 수 있어요. 배치 제출을 TPM·RPM 계산에 넣지 않아도 될 때만 위 설정을 쓰세요. 분당 창 대신 대기 중인 배치 작업으로 배치 제출을 제어하려면 Enqueued-token 제한을 참고하세요. 자세한 내용과 한계는 Batches API의 레이트 리밋 동작 방식을 보세요.
2. 가상 키 만들기
curl -L -X POST 'https://${PROXY_BASE_URL}/key/generate' \
-H 'Authorization: Bearer ***' \
-H 'Content-Type: application/json' \
-d '{"models": ["gpt-4o-batch"]}'
이제 가상 키로 배치 모델에 접근할 수 있어요 (개발자 사용법 참고).
(개발자) 사용법
LiteLLM 관리형 파일을 만들고 그 파일로 Batch CRUD 작업을 실행하는 방법이에요.
1. request.jsonl 만들기
/model_group/info에서 사용 가능한 모델 확인하기mode: batch인 모든 모델 보기- .jsonl의
model을/model_group/info의 모델로 설정하기
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an unhelpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}
LiteLLM이 모델 이름을 Azure 디플로이먼트별 값(예: gpt-4o-mini-general-deployment)으로 변환해요.
2. 파일 업로드하기
target_model_names: "<model-name>"을 지정해 LiteLLM 관리형 파일과 요청 검증을 활성화해요. 모델 이름은 request.jsonl의 model과 일치해야 해요.
from openai import OpenAI
client = OpenAI(
base_url="http://0.0.0.0:4000",
api_key="sk-<your-litellm-api-key>",
)
# Upload file
batch_input_file = client.files.create(
file=open("./request.jsonl", "rb"), # {"model": "gpt-4o-batch"} <-> {"model": "gpt-4o-mini-special-deployment"}
purpose="batch",
extra_body={"target_model_names": "gpt-4o-batch"}
)
print(batch_input_file)
파일은 어디에 쓰이나요?
모든 gpt-4o-batch 디플로이먼트(gpt-4o-mini-general-deployment, gpt-4o-mini-special-deployment)에 쓰여요. 이 덕분에 3단계에서 모든 gpt-4o-batch 디플로이먼트에 걸쳐 로드 밸런싱할 수 있어요.
3. 배치 만들기 + 조회하기
...
# Create batch
batch = client.batches.create(
input_file_id=batch_input_file.id,
endpoint="/v1/chat/completions",
completion_window="24h",
metadata={"description": "Test batch job"},
)
print(batch)
batch_id = batch.id
# Retrieve batch
batch_response = client.batches.retrieve(batch_id)
status = batch_response.status
4. 배치 콘텐츠 조회하기
...
file_id = batch_response.output_file_id
file_response = client.files.content(file_id)
print(file_response.text)
5. 배치 목록 조회하기
...
client.batches.list(limit=10, extra_query={"target_model_names": "gpt-4o-batch"})
6. 배치 취소하기
...
client.batches.cancel(batch_id)
E2E 예제
import json
from openai import OpenAI
"""
litellm yaml:
model_list:
- model_name: gpt-4o-batch
litellm_params:
model: azure/gpt-4o-my-special-deployment
api_key: ..
api_base: ..
---
request.jsonl:
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-batch", ...}}
"""
client = OpenAI(
base_url="http://0.0.0.0:4000",
api_key="sk-<your-litellm-api-key>",
)
# Upload file
batch_input_file = client.files.create(
file=open("./request.jsonl", "rb"),
purpose="batch",
extra_body={"target_model_names": "gpt-4o-batch"}
)
print(batch_input_file)
# Create batch
batch = client.batches.create(
input_file_id=batch_input_file.id,
endpoint="/v1/chat/completions",
completion_window="24h",
metadata={"description": "Test batch job"},
)
print(batch)
batch_id = batch.id
# Retrieve batch
batch_response = client.batches.retrieve(batch_id)
status = batch_response.status
print(f"status: {status}, output_file_id: {batch_response.output_file_id}")
# Download file
output_file_id = batch_response.output_file_id
print(f"output_file_id: {output_file_id}")
if not output_file_id:
output_file_id = batch_response.error_file_id
if output_file_id:
file_response = client.files.content(output_file_id)
raw_responses = file_response.text.strip().split("\n")
with open("unified_batch_output.jsonl", "w") as output_file:
for raw_response in raw_responses:
json.dump(json.loads(raw_response), output_file)
output_file.write("\n")
# List batches
list_batch_response = client.batches.list(
extra_query={"target_model_names": "gpt-4o-batch"}
)
# Cancel batch
batch_response = client.batches.cancel(batch_id)
status = batch_response.status
print(f"status: {status}")
관측성 (Observability)
관리형 배치가 completed에 도달하면 프록시의 배치 비용 폴러(poller)가 출력 파일을 내려받아 각 줄에 가격을 매기고, 배치 전체에 대해 spend log 한 줄을 써요. 그 줄이 /spend/logs와 Logs 페이지가 읽는 데이터이며, 요청별 결과 횟수, reasoning 토큰 합계, 배치 비용이 여기에 담겨요.
폴러는 타이머로 동작하므로 배치가 끝난 직후가 아니라 얼마 뒤 그 줄이 나타나요. general_settings의 proxy_batch_polling_interval(또는 PROXY_BATCH_POLLING_INTERVAL 환경 변수)이 기본 간격(초)을 정하고 기본값은 3600이에요. 폴러는 그 위에 최대 30초 지터를 더해요. 테스트 중에는 30처럼 작은 값으로 두는 게 좋아요.
Spend log 필드
배치의 비용 줄은 call_type: "aretrieve_batch"이고, request_id는 <batch id>_batch_cost예요. 여기서 <batch id>는 POST /v1/batches가 반환한 id예요.
curl -s "http://0.0.0.0:4000/spend/logs?request_id=${BATCH_ID}_batch_cost" \
-H "Authorization: Bearer ***"
{
"request_id": "bGl0ZWxsbV9wcm94eTttb2RlbF9pZDo3YjJl..._batch_cost",
"session_id": "bGl0ZWxsbV9wcm94eTttb2RlbF9pZDo3YjJl...",
"call_type": "aretrieve_batch",
"model": "gemini-2.5-flash",
"model_group": "gemini-batch",
"spend": 0.0003221,
"prompt_tokens": 14,
"completion_tokens": 256,
"total_tokens": 270,
"status": "success",
"metadata": {
"batch_models": ["gemini-2.5-flash"],
"batch_successful_requests": 2,
"batch_failed_requests": 1,
"cost_breakdown": {
"input_cost": 0.0000021,
"output_cost": 0.00032,
"total_cost": 0.0003221,
"tool_usage_cost": 0.0
},
"usage_object": {
"prompt_tokens": 14,
"completion_tokens": 256,
"total_tokens": 270,
"completion_tokens_details": {"text_tokens": 32, "reasoning_tokens": 224}
}
}
}
| 필드 | 담는 내용 |
|---|---|
request_id |
_batch_cost가 붙은 배치 id |
session_id |
배치 id. create 줄과 공유되어 둘이 하나의 trace로 묶임 |
spend |
배치 전체 비용. 성공한 줄만 집계 |
prompt_tokens, completion_tokens, total_tokens |
모든 성공 줄의 합 |
metadata.batch_successful_requests |
공급자가 성공적으로 응답한 요청 수 |
metadata.batch_failed_requests |
출력 파일과 오류 파일에서 읽은 거부된 요청 수 |
metadata.batch_models |
배치가 실행된 모델 |
metadata.usage_object.completion_tokens_details.reasoning_tokens |
성공 줄의 reasoning 토큰 합계 |
metadata.cost_breakdown |
spend 뒤의 입력·출력 비용 분해 |
/spend/logs/v2는 페이징과 함께 같은 필드를 반환하며 단일 조회를 넘어설 때 쓰는 엔드포인트예요. request_id를 넘겨도 start_date와 end_date가 필요하므로, 배치 한 줄을 빨리 뽑으려면 위처럼 단순한 /spend/logs?request_id= 호출이 더 짧아요.
Logs 페이지에서
폴러가 실행된 뒤 http://localhost:4000/ui/?page=logs를 열어보세요. 배치의 create 줄과 비용 줄이 하나의 세션을 공유하므로, 페이지는 둘을 하나의 그룹 줄로 보여주면서 배치의 총 비용과 토큰을 싣고, 평소 LLM 배지 대신 Type 컬럼에 Batch 배지를 달아요. 결과는 Status 컬럼에 나타나요: 모든 요청이 성공하면 초록 Success 배지, 일부가 실패하면 주황 N/M succeeded 배지가 보여요. N은 성공 수, M은 총 수라서 2/3 succeeded는 요청 3개 중 1개가 실패했음을 뜻해요. Request ID 컬럼은 원시 <batch id>_batch_cost 문자열 대신 POST /v1/batches에서 받은 id와 일치하도록 작은 batch cost 라벨 아래에 배치 id 자체를 보여줘요.
줄을 클릭하면 서랍(drawer)이 열려요. Batch Results 카드에는 배치 id, 성공·실패 요청 수(실패 수가 0이 아니면 빨간색으로 강조), 배치가 실행된 모델이 나열돼요. Metrics는 배치가 reasoning 토큰을 집계했을 때 Reasoning Tokens 줄을 추가하고, Cost Breakdown은 줄 비용 뒤의 입력·출력 분해를 보여줘요.
부분 실패한 배치 읽기
배치는 모든 요청이 성공했는지와 무관하게 실행을 끝내는 즉시 공급자에서 completed가 되므로, 상태만으로는 실패를 알 수 없어요. 비용 줄의 횟수가 실패를 알려주며, 이는 배치 자체의 request_counts와 일치해요:
# what the provider reports
curl -s "http://0.0.0.0:4000/v1/batches/${BATCH_ID}" \
-H "Authorization: Bearer ***" | jq '.status, .request_counts'
# "completed"
# {"completed": 2, "failed": 1, "total": 3}
# what the spend log recorded
curl -s "http://0.0.0.0:4000/spend/logs?request_id=${BATCH_ID}_batch_cost" \
-H "Authorization: Bearer ***" \
| jq '.[0].metadata | {batch_successful_requests, batch_failed_requests}'
# {"batch_successful_requests": 2, "batch_failed_requests": 1}
실패한 요청이 왜 실패했는지 보려면 배치의 오류 파일을 내려받으세요. 거부된 요청마다 한 줄씩, 입력 파일에서 지정한 custom_id로 키가 매겨져 있어요:
ERROR_FILE_ID=$(curl -s "http://0.0.0.0:4000/v1/batches/${BATCH_ID}" \
-H "Authorization: Bearer ***" | jq -r '.error_file_id')
curl -s "http://0.0.0.0:4000/v1/files/${ERROR_FILE_ID}/content" \
-H "Authorization: Bearer ***"
실패한 요청은 비용이 들지 않으므로 spend는 성공한 줄만 포함하고, 절반이 실패한 배치는 예산의 절반쯤만 비용이 들어요. 공급자가 수락했지만 LiteLLM이 가격을 매기지 못한 요청도 성공으로 치며 $0로 청구되는데, 이 덕분에 횟수가 공급자의 숫자와 조정돼요. 모든 요청이 실패한 배치는 출력 파일 자체가 없고, 비용 줄은 $0, 성공 요청 0회, 오류 파일에서 읽은 실패 횟수를 기록해요. Anthropic과 Bedrock extended thinking 배치는 줄 단위로 reasoning 토큰을 보고하지 않으므로, 모델이 thinking하고 있었어도 reasoning_tokens가 빠져 있어요. 두 횟수가 공급자 총계와 맞지 않는 유일한 경우는 유효하지 않은 JSON인 출력 줄인데, 이는 경고와 함께 건너뛰고 어느 쪽 횟수에도 들어가지 않아요.
FAQ
내 파일은 어디에 쓰이나요?
target_model_names를 지정하면 파일은 그것과 일치하는 모든 디플로이먼트에 쓰여요. 추가 인프라는 필요 없어요.
배치가 한 디플로이먼트(예: eastus-01)에서 만들어졌는데, 이후 조회가 다른 디플로이먼트(예: eastus2-01)로 라우팅될 수 있나요?
아니요. LiteLLM은 초기 배치 생성 시에만 디플로이먼트 간 로드 밸런싱을 해요. 반환된 배치 id는 사용된 디플로이먼트를 인코딩하므로, retrieve·cancel·파일 콘텐츠 호출은 그 디플로이먼트에 고정(sticky)돼요.
더 알아보기 (Learn more)
- Batches: 배치 공급자, enqueued-token 제한, 레이트 리밋 상세
- Managed Files w/ Finetuning APIs
- LiteLLM 관리형 파일