본문 바로가기

AI 개발

LiteLLM 완전 가이드 1편 — 100개+ LLM을 코드 한 줄로 갈아타는 오픈소스 AI 게이트웨이

반응형

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) 설정
→ 캐싱

관련 글

반응형