반응형
Claude 쓰다가 GPT로 바꾸면 SDK 갈아야 하고, Gemini 추가하면 또 다른 SDK. 프로바이더마다 인증 방식도 응답 포맷도 다릅니다. LiteLLM은 이 모든 걸 completion() 하나로 통일합니다. model 파라미터만 바꾸면 끝납니다.
[핵심 요약]
→ LiteLLM: BerriAI가 만든 오픈소스 LLM 통합 라이브러리 + AI 게이트웨이
→ GitHub: 40K 스타, 1,300+ 기여자, 2억 4천만+ Docker 풀
→ 지원 범위: 140개+ 프로바이더, 2,500개+ 모델
→ 두 가지 모드: Python SDK (라이브러리) / Proxy Server (팀 공용 게이트웨이)
→ OpenAI 완전 호환: base_url만 바꾸면 기존 코드 그대로
→ 지원 엔드포인트: /chat/completions, /embeddings, /images, /audio, /batches, /rerank, /a2a
→ 라이선스: MIT (오픈소스), Enterprise 기능은 상업 라이선스
→ OpenRouter와의 차이: 클라우드 서비스 vs 셀프호스팅 오픈소스
LiteLLM vs OpenRouter — 언제 뭘 쓰나
[OpenRouter]
→ 클라우드 서비스 (SaaS)
→ 가입하고 API 키 하나 받으면 끝
→ 인프라 운영 불필요
→ 5.5% 플랫폼 수수료
→ 프로바이더 목록 고정 (지원 안 하는 프로바이더 추가 불가)
→ 셀프호스팅 불가
[LiteLLM]
→ 오픈소스 (셀프호스팅)
→ 내 서버에 직접 배포 — 데이터가 외부 안 나감
→ 수수료 없음 (자체 API 키 사용)
→ 어떤 프로바이더든 추가 가능 (사내 모델, vLLM, Ollama 등)
→ Python SDK로 코드에 직접 임베딩 가능
→ 엔터프라이즈 거버넌스 완전 제어
[같이 쓰는 패턴]
→ OpenRouter: 빠른 프로토타이핑, 무료 모델 테스트
→ LiteLLM: 프로덕션 인프라, 팀 게이트웨이, 사내 보안 요구사항
실전 1 — 설치 및 첫 호출
# 기본 설치
pip install litellm
# 또는 특정 프로바이더 추가 의존성 포함
pip install litellm[extra] # 추가 프로바이더 지원
3가지 프로바이더를 같은 코드로
import litellm
import os
# API 키 설정
os.environ["OPENAI_API_KEY"] = "sk-..."
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..."
os.environ["GEMINI_API_KEY"] = "..."
# ── Claude ────────────────────────────────────────────
response = litellm.completion(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "안녕하세요!"}]
)
print(response.choices[0].message.content)
# ── GPT (model만 바꿈, 코드 동일) ────────────────────
response = litellm.completion(
model="openai/gpt-5.4",
messages=[{"role": "user", "content": "안녕하세요!"}]
)
print(response.choices[0].message.content)
# ── Gemini (코드 동일) ─────────────────────────────────
response = litellm.completion(
model="gemini/gemini-3-flash",
messages=[{"role": "user", "content": "안녕하세요!"}]
)
print(response.choices[0].message.content)
# ── Ollama 로컬 모델 (코드 동일) ──────────────────────
response = litellm.completion(
model="ollama/llama3.3",
messages=[{"role": "user", "content": "안녕하세요!"}],
api_base="http://localhost:11434"
)
print(response.choices[0].message.content)
# 모두 동일한 response 포맷 반환
# response.choices[0].message.content — 항상 이렇게 접근
실전 2 — 모델 ID 체계
# 프로바이더/모델명 형식
models = {
# Anthropic
"claude-opus": "anthropic/claude-opus-4-7",
"claude-sonnet": "anthropic/claude-sonnet-4-6",
"claude-haiku": "anthropic/claude-haiku-4-5",
# OpenAI
"gpt-5": "openai/gpt-5.4",
"gpt-4o-mini": "openai/gpt-4o-mini",
# Google
"gemini-flash": "gemini/gemini-3-flash",
"gemini-pro": "gemini/gemini-3.1-pro-preview",
"vertex-gemini": "vertex_ai/gemini-3-flash", # Vertex AI 경유
# AWS Bedrock
"claude-bedrock": "bedrock/anthropic.claude-sonnet-4-6",
"llama-bedrock": "bedrock/meta.llama3-70b-instruct-v1",
# Azure OpenAI
"azure-gpt": "azure/my-gpt-deployment", # 배포명
# 로컬
"llama-local": "ollama/llama3.3",
"qwen-local": "ollama/qwen3-coder:7b",
# OpenRouter 경유
"deepseek-via-or": "openrouter/deepseek/deepseek-chat",
# Hugging Face
"hf-model": "huggingface/mistralai/Mistral-7B-Instruct-v0.2",
}
# 모든 모델을 같은 코드로 호출
for name, model_id in models.items():
try:
response = litellm.completion(
model=model_id,
messages=[{"role": "user", "content": "한 줄로 자기소개"}],
max_tokens=50
)
print(f"✅ {name}: {response.choices[0].message.content[:50]}")
except Exception as e:
print(f"❌ {name}: {e}")
실전 3 — 주요 파라미터와 응답 포맷
import litellm
# ── 주요 파라미터 ──────────────────────────────────────
response = litellm.completion(
model="anthropic/claude-sonnet-4-6",
messages=[
{"role": "system", "content": "당신은 친절한 AI입니다."},
{"role": "user", "content": "파이썬 리스트 컴프리헨션이 뭐야?"}
],
# 생성 파라미터
temperature=0.7,
max_tokens=1024,
top_p=0.9,
stop=["###", "END"],
# 타임아웃·재시도
timeout=30,
num_retries=3,
# 메타데이터 (로깅용)
metadata={"user_id": "user-123", "session_id": "sess-456"},
)
# ── 응답 포맷 (모든 모델 동일) ────────────────────────
print(response.choices[0].message.content) # 응답 텍스트
print(response.model) # 실제 사용된 모델
print(response.usage.prompt_tokens) # 입력 토큰
print(response.usage.completion_tokens) # 출력 토큰
print(response.usage.total_tokens) # 전체 토큰
print(response._response_ms) # 응답 시간 (ms)
# ── 비동기 호출 ────────────────────────────────────────
import asyncio
import litellm
async def async_example():
response = await litellm.acompletion(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "안녕"}]
)
return response.choices[0].message.content
# 여러 모델 동시 호출
async def compare_models(prompt: str) -> dict:
models = [
"anthropic/claude-sonnet-4-6",
"openai/gpt-5.4",
"gemini/gemini-3-flash",
]
tasks = [
litellm.acompletion(
model=m,
messages=[{"role": "user", "content": prompt}]
)
for m in models
]
responses = await asyncio.gather(*tasks, return_exceptions=True)
return {
model: (r.choices[0].message.content if not isinstance(r, Exception) else str(r))
for model, r in zip(models, responses)
}
results = asyncio.run(compare_models("파이썬이란?"))
for model, text in results.items():
print(f"\n[{model}]\n{text[:100]}")
실전 4 — 스트리밍
# ── 동기 스트리밍 ──────────────────────────────────────
response = litellm.completion(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "긴 글을 써줘"}],
stream=True
)
full_text = ""
for chunk in response:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
full_text += delta
print()
# ── 비동기 스트리밍 ────────────────────────────────────
async def async_stream(prompt: str) -> str:
response = await litellm.acompletion(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": prompt}],
stream=True
)
full_text = ""
async for chunk in response:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
full_text += delta
print()
return full_text
실전 5 — 임베딩·이미지·오디오
# ── 임베딩 ───────────────────────────────────────────
response = litellm.embedding(
model="text-embedding-3-small", # OpenAI
# model="amazon.titan-embed-text-v1", # Bedrock
# model="vertex_ai/textembedding-gecko", # Vertex
input=["텍스트 1", "텍스트 2", "텍스트 3"]
)
embeddings = [r["embedding"] for r in response.data]
print(f"임베딩 차원: {len(embeddings[0])}")
# ── 이미지 분석 (멀티모달) ────────────────────────────
response = litellm.completion(
model="anthropic/claude-sonnet-4-6",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": "https://example.com/image.jpg"}
},
{"type": "text", "text": "이미지를 설명해줘"}
]
}
]
)
print(response.choices[0].message.content)
# ── 이미지 생성 ────────────────────────────────────────
response = litellm.image_generation(
model="dall-e-3",
prompt="한국 서울의 야경",
n=1,
size="1024x1024"
)
print(response.data[0].url) # 생성된 이미지 URL
실전 6 — 지원 프로바이더 정확한 목록
import litellm
# 사용 가능한 모든 프로바이더 조회
providers = litellm.provider_list
print(f"지원 프로바이더 수: {len(providers)}")
# 특정 프로바이더의 모델 목록
models = litellm.get_valid_models()
print(f"전체 지원 모델 수: {len(models)}")
# 모델 정보 조회
model_info = litellm.get_model_info("anthropic/claude-sonnet-4-6")
print(f"최대 토큰: {model_info.get('max_tokens')}")
print(f"입력 비용: ${model_info.get('input_cost_per_token', 0) * 1_000_000:.2f}/M")
print(f"출력 비용: ${model_info.get('output_cost_per_token', 0) * 1_000_000:.2f}/M")
[주요 지원 프로바이더 — 2026년 5월 기준]
클라우드:
→ openai: GPT-5.4, GPT-4o, GPT-4o-mini 등
→ anthropic: Claude Opus 4.7, Sonnet 4.6, Haiku 4.5
→ gemini: Gemini 3.1 Pro, Gemini 3 Flash 등
→ vertex_ai: Google Vertex AI 모든 모델
→ bedrock: AWS Bedrock (Claude, Llama, Mistral 등)
→ azure: Azure OpenAI (자체 배포명 사용)
→ openrouter: OpenRouter 경유 300개+ 모델
로컬·셀프호스팅:
→ ollama: Ollama 서버의 모든 모델
→ vllm: vLLM 서버
→ huggingface: HF Inference Endpoints
→ together_ai: Together AI
→ fireworks_ai: Fireworks AI
기타:
→ deepseek: DeepSeek V3, R1
→ groq: Groq (빠른 추론)
→ mistral: Mistral AI
→ cohere: Cohere Command R+
→ nvidia_nim: NVIDIA NIM
→ xai: Grok
실전 7 — OpenAI SDK 드롭인 교체
기존 OpenAI SDK 코드를 그대로 쓰면서 LiteLLM으로 전환합니다.
# ── 기존 OpenAI SDK 코드 ─────────────────────────────
# from openai import OpenAI
# client = OpenAI()
# response = client.chat.completions.create(
# model="gpt-4o",
# messages=[...]
# )
# ── LiteLLM으로 드롭인 교체 ──────────────────────────
from litellm import OpenAI # openai 대신 litellm에서 import
client = OpenAI() # 나머지 코드 완전히 동일
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "안녕"}]
)
print(response.choices[0].message.content)
# → 이제 model만 바꾸면 모든 프로바이더 사용 가능
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6", # model만 변경
messages=[{"role": "user", "content": "안녕"}]
)
# 환경변수로 전역 모델 지정
import litellm
import os
# 기본 모델 설정
litellm.model = "anthropic/claude-sonnet-4-6"
# 디버그 모드 (API 요청/응답 로그)
litellm.set_verbose = True
# 성공 콜백
def on_success(kwargs, completion_response, start_time, end_time):
cost = litellm.completion_cost(completion_response)
print(f"✅ 완료: ${cost:.6f} | {end_time - start_time:.2f}s")
litellm.success_callback = [on_success]
# 실패 콜백
def on_failure(kwargs, exception, start_time, end_time):
print(f"❌ 실패: {exception}")
litellm.failure_callback = [on_failure]
마무리
✅ 1편에서 한 것
→ LiteLLM 설치 + 첫 호출 (Claude·GPT·Gemini·Ollama 동일 코드)
→ 모델 ID 체계 이해 (provider/model-name)
→ 주요 파라미터 + 응답 포맷 (모든 모델 동일)
→ 스트리밍 (동기·비동기)
→ 임베딩·이미지·멀티모달
→ OpenAI SDK 드롭인 교체
→ 140개+ 프로바이더 목록
❌ 2편에서 다룰 것
→ 폴백·재시도 전략
→ Router: 로드밸런싱 + 여러 배포 관리
→ 비용 추적 (completion_cost)
→ 예산 제한 (max_budget)
→ 모델 별칭(alias) 설정
→ 캐싱
관련 글
반응형
'AI 개발' 카테고리의 다른 글
| LiteLLM 완전 가이드 3편 — Proxy 서버 모드: 팀 공용 LLM 게이트웨이 구축 실전 (0) | 2026.05.19 |
|---|---|
| LiteLLM 완전 가이드 2편 — 폴백·재시도, Router 로드밸런싱, 비용 추적, 예산·캐싱 실전 (0) | 2026.05.19 |
| OpenRouter 완전 가이드 4편 — 모니터링, 레이트 리밋 관리, OAuth PKCE, 팀 운영, ZDR (0) | 2026.05.19 |
| OpenRouter 완전 가이드 3편 — 스트리밍, 툴 콜링, 멀티모달, 구조화 출력, LangChain·LangGraph 통합 (0) | 2026.05.19 |
| OpenRouter 완전 가이드 2편 — 폴백 라우팅, 로드밸런싱, 프로바이더 제어, 비용 최적화 실전 (0) | 2026.05.19 |