[BETA] 제네릭 프롬프트 관리 API - PR 없이 통합하기

[BETA] 제네릭 프롬프트 관리 API - PR 없이 통합하기

문제

프롬프트 관리 프로바이더로서 LiteLLM에 통합하는 전통적인 방법은 다음을 요구했어요:

  • LiteLLM 저장소에 PR 제출
  • 리뷰와 병합 대기
  • LiteLLM 코드베이스에서 프로바이더별 코드 유지
  • API 변경에 맞춰 통합 업데이트

해결책

제네릭 프롬프트 관리 API(Generic Prompt Management API)를 사용하면 간단한 API 엔드포인트를 구현해 PR 없이 즉시 LiteLLM에 통합할 수 있어요.

주요 이점

  1. PR 불필요 - 즉시 배포하고 통합하세요
  2. 간단한 계약 - GET 엔드포인트 하나, 표준 JSON 응답
  3. 변수 치환 - {variable} 문법으로 프롬프트 변수 지원
  4. 커스텀 파라미터 - config를 통해 프로바이더별 query param 전달
  5. 완전한 제어 - 프롬프트 관리 API를 직접 소유하고 유지
  6. 모델·파라미터 오버라이드 - 프롬프트에서 모델과 파라미터 선택적 오버라이드

3단계로 시작하기

1단계: LiteLLM 설정

config.yaml 에 추가하세요:

prompts:
  - prompt_id: "simple_prompt"
    litellm_params:
      prompt_integration: "generic_prompt_management"
      api_base: http://localhost:8080
      api_key: os.environ/YOUR_API_KEY

2단계: API 엔드포인트 구현

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

@app.get("/beta/litellm_prompt_management")
async def get_prompt(prompt_id: str):
    return {
        "prompt_id": prompt_id,
        "prompt_template": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Help me with {task}"}
        ],
        "prompt_template_model": "gpt-5.6-terra",
        "prompt_template_optional_params": {"temperature": 0.7}
    }

3단계: 앱에서 사용

from litellm import completion

response = completion(
    model="gpt-5.6-terra",
    prompt_id="simple_prompt",
    prompt_variables={"task": "data analysis"},
    messages=[{"role": "user", "content": "I have sales data"}]
)

이것으로 끝이에요! LiteLLM이 프롬프트를 가져오고, 변수를 적용하고, 요청을 만듭니다.

출처: 문서

본문

API 계약

엔드포인트

GET /beta/litellm_prompt_management 을 구현하세요.

요청 형식

엔드포인트는 query parameter가 있는 GET 요청을 받아요:

GET /beta/litellm_prompt_management?prompt_id={prompt_id}&{custom_params}

Query 파라미터:

  • prompt_id (필수): 가져올 프롬프트의 ID
  • 커스텀 파라미터: provider_specific_query_params에서 설정한 추가 파라미터

예시:

GET /beta/litellm_prompt_management?prompt_id=hello-world-prompt-2bac&project_name=litellm&slug=hello-world-prompt-2bac

응답 형식

{
  "prompt_id": "hello-world-prompt-2bac",
  "prompt_template": [
    {
      "role": "system",
      "content": "You are a helpful assistant specialized in {domain}."
    },
    {
      "role": "user",
      "content": "Help me with {task}"
    }
  ],
  "prompt_template_model": "gpt-5.6-terra",
  "prompt_template_optional_params": {
    "temperature": 0.7,
    "max_tokens": 500,
    "top_p": 0.9
  }
}

응답 필드:

  • prompt_id (string, 필수): 프롬프트의 ID
  • prompt_template (array, 필수): 선택적 {variable} 플레이스홀더가 있는 OpenAI 형식 메시지 배열
  • prompt_template_model (string, 선택): 이 프롬프트에 사용할 모델 (ignore_prompt_manager_model: true가 아니면 클라이언트 모델을 오버라이드)
  • prompt_template_optional_params (object, 선택): temperature, max_tokens 등 추가 파라미터 (ignore_prompt_manager_optional_params: true가 아니면 클라이언트 params와 병합)

LiteLLM 설정

config.yaml 에 추가하세요:

model_list:
  - model_name: gpt-5.6-luna
    litellm_params:
      model: openai/gpt-5.6-luna
      api_key: os.environ/OPENAI_API_KEY

prompts:
  - prompt_id: "simple_prompt"
    litellm_params:
      prompt_integration: "generic_prompt_management"
      provider_specific_query_params:
        project_name: litellm
        slug: hello-world-prompt-2bac
      api_base: http://localhost:8080
      api_key: os.environ/YOUR_PROMPT_API_KEY  # optional
      ignore_prompt_manager_model: true  # optional, keep client's model
      ignore_prompt_manager_optional_params: true  # optional, don't merge prompt manager's params (e.g. temperature, max_tokens, etc.)

설정 파라미터

  • prompt_integration : 반드시 "generic_prompt_management" 여야 합니다
  • provider_specific_query_params : API로 보내는 커스텀 query 파라미터 (선택)
  • api_base : 프롬프트 관리 API의 기본 URL
  • api_key : 인증용 선택적 API 키 (Bearer 토큰으로 전송)
  • ignore_prompt_manager_model : true이면 프롬프트의 모델 대신 클라이언트가 지정한 모델 사용 (기본 false)
  • ignore_prompt_manager_optional_params : true이면 프롬프트의 선택 파라미터를 클라이언트 params와 병합하지 않음 (기본 false)

사용법

LiteLLM SDK로 사용

프롬프트 ID를 사용한 기본 사용법:

from litellm import completion

response = completion(
    model="gpt-5.6-terra",
    prompt_id="simple_prompt",
    messages=[{"role": "user", "content": "Additional message"}]
)

프롬프트 변수 사용:

response = completion(
    model="gpt-5.6-terra",
    prompt_id="simple_prompt",
    prompt_variables={
        "domain": "data science",
        "task": "analyzing customer churn"
    },
    messages=[{"role": "user", "content": "Please provide a detailed analysis"}]
)

프롬프트 템플릿에서 {domain}은 "data science"로, {task}는 "analyzing customer churn"으로 치환됩니다.

LiteLLM Proxy로 사용

  1. config로 프록시 시작:
litellm --config /path/to/config.yaml
  1. prompt_id로 요청:
curl http://0.0.0.0:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "gpt-5.6-terra",
    "prompt_id": "simple_prompt",
    "prompt_variables": {
      "domain": "healthcare",
      "task": "patient risk assessment"
    },
    "messages": [
      {"role": "user", "content": "Analyze the following data..."}
    ]
  }'
  1. OpenAI SDK로 사용:
from openai import OpenAI

client = OpenAI(
    base_url="http://0.0.0.0:4000",
    api_key="sk-<your-litellm-api-key>"
)

response = client.chat.completions.create(
    model="gpt-5.6-terra",
    messages=[
        {"role": "user", "content": "Analyze the data"}
    ],
    extra_body={
        "prompt_id": "simple_prompt",
        "prompt_variables": {
            "domain": "finance",
            "task": "fraud detection"
        }
    }
)

구현 예시

완전한 참조 구현(여러 예시 프롬프트, 인증, 편의 엔드포인트 포함)은 mock_prompt_management_server.py를 참고하세요.

최소 FastAPI 예시:

from fastapi import FastAPI, HTTPException, Header
from typing import Optional, Dict, Any, List
from pydantic import BaseModel

app = FastAPI()

# In-memory prompt storage (replace with your database)
PROMPTS = {
    "hello-world-prompt": {
        "prompt_id": "hello-world-prompt",
        "prompt_template": [
            {
                "role": "system",
                "content": "You are a helpful assistant specialized in {domain}."
            },
            {
                "role": "user", 
                "content": "Help me with: {task}"
            }
        ],
        "prompt_template_model": "gpt-5.6-terra",
        "prompt_template_optional_params": {
            "temperature": 0.7,
            "max_tokens": 500
        }
    },
    "code-review-prompt": {
        "prompt_id": "code-review-prompt",
        "prompt_template": [
            {
                "role": "system",
                "content": "You are an expert code reviewer. Review code for {language}."
            },
            {
                "role": "user",
                "content": "Review the following code:\n\n{code}"
            }
        ],
        "prompt_template_model": "gpt-5.6-terra",
        "prompt_template_optional_params": {
            "temperature": 0.3,
            "max_tokens": 1000
        }
    }
}

class PromptResponse(BaseModel):
    prompt_id: str
    prompt_template: List[Dict[str, str]]
    prompt_template_model: Optional[str] = None
    prompt_template_optional_params: Optional[Dict[str, Any]] = None

@app.get("/beta/litellm_prompt_management", response_model=PromptResponse)
async def get_prompt(
    prompt_id: str,
    authorization: *** = Header(None),
    project_name: Optional[str] = None,
    slug: Optional[str] = None,
):
    """
    Get a prompt by ID with optional filtering by project_name and slug.
    
    Args:
        prompt_id: The ID of the prompt to fetch
        authorization: Optional *** token for authentication
        project_name: Optional project name filter
        slug: Optional slug filter
    """
    
    # Optional: Validate authorization
    if authorization:
        token = authorization.replace("Bearer ", "")
        # Validate your token here
        if not is_valid_token(token):
            raise HTTPException(status_code=401, detail="Invalid API key")
    
    # Optional: Apply additional filtering based on custom params
    if project_name or slug:
        # You can use these parameters to filter or validate access
        # For example, check if the user has access to this project
        pass
    
    # Fetch the prompt from your storage
    if prompt_id not in PROMPTS:
        raise HTTPException(
            status_code=404,
            detail=f"Prompt '{prompt_id}' not found"
        )
    
    prompt_data = PROMPTS[prompt_id]
    
    return PromptResponse(**prompt_data)

def is_valid_token(token: str) -> bool:
    """Validate API token - implement your logic here"""
    # Example: Check against your database or secret store
    valid_tokens = ["your-secret-token", "another-valid-token"]
    return token in valid_tokens

# Optional: Health check endpoint
@app.get("/health")
async def health_check():
    return {"status": "healthy"}

# Optional: List all prompts endpoint
@app.get("/prompts")
async def list_prompts(authorization: *** = Header(None)):
    """List all available prompts"""
    if authorization:
        token = authorization.replace("Bearer ", "")
        if not is_valid_token(token):
            raise HTTPException(status_code=401, detail="Invalid API key")
    
    return {
        "prompts": [
            {"prompt_id": pid, "model": p.get("prompt_template_model")}
            for pid, p in PROMPTS.items()
        ]
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)

예시 서버 실행

  1. 의존성 설치:
uv add fastapi uvicorn
  1. 위 코드를 prompt_server.py로 저장
  2. 서버 실행:
python prompt_server.py
  1. 엔드포인트 테스트:
curl "http://localhost:8080/beta/litellm_prompt_management?prompt_id=hello-world-prompt&project_name=litellm&slug=hello-world-prompt-2bac"

기대 응답:

{
  "prompt_id": "hello-world-prompt",
  "prompt_template": [
    {
      "role": "system",
      "content": "You are a helpful assistant specialized in {domain}."
    },
    {
      "role": "user",
      "content": "Help me with: {task}"
    }
  ],
  "prompt_template_model": "gpt-5.6-terra",
  "prompt_template_optional_params": {
    "temperature": 0.7,
    "max_tokens": 500
  }
}

고급 기능

변수 치환

LiteLLM은 {variable} 문법으로 프롬프트 템플릿의 변수를 자동 치환해요. {variable}{{variable}} 형식 모두 지원됩니다.

예시 프롬프트 템플릿:

{
  "prompt_template": [
    {
      "role": "system",
      "content": "You are an expert in {domain} with {years} years of experience."
    }
  ]
}

클라이언트 요청:

completion(
    model="gpt-5.6-terra",
    prompt_id="expert_prompt",
    prompt_variables={
        "domain": "machine learning",
        "years": "10"
    }
)

결과:

"You are an expert in machine learning with 10 years of experience."

캐싱

LiteLLM은 가져온 프롬프트를 메모리에 자동 캐시해요. 캐시 키에는 다음이 포함됩니다:

  • prompt_id
  • prompt_label (제공 시)
  • prompt_version (제공 시)

즉, API 엔드포인트는 고유한 프롬프트 구성당 한 번만 호출됩니다.

모델 오버라이드 동작

기본 동작 (ignore_prompt_manager_model 없음):

prompts:
  - prompt_id: "my_prompt"
    litellm_params:
      prompt_integration: "generic_prompt_management"
      api_base: http://localhost:8080

API가 "prompt_template_model": "gpt-5.6-terra"를 반환하면, LiteLLM은 클라이언트가 무엇을 지정했든 gpt-5.6-terra를 사용합니다.

ignore_prompt_manager_model: true 설정 시:

prompts:
  - prompt_id: "my_prompt"
    litellm_params:
      prompt_integration: "generic_prompt_management"
      api_base: http://localhost:8080
      ignore_prompt_manager_model: true

LiteLLM은 프롬프트의 모델을 무시하고 클라이언트가 지정한 모델을 사용해요.

파라미터 병합 동작

기본 동작 (ignore_prompt_manager_optional_params 없음):

클라이언트 params는 프롬프트 params와 병합되며, 프롬프트 params가 우선합니다:

# Prompt returns: {"temperature": 0.7, "max_tokens": 500}
# Client sends: {"temperature": 0.9, "top_p": 0.95}
# Final params: {"temperature": 0.7, "max_tokens": 500, "top_p": 0.95}

ignore_prompt_manager_optional_params: true 설정 시:

클라이언트 params만 사용됩니다:

# Prompt returns: {"temperature": 0.7, "max_tokens": 500}
# Client sends: {"temperature": 0.9, "top_p": 0.95}
# Final params: {"temperature": 0.9, "top_p": 0.95}

보안 고려 사항

  1. 인증: api_key 파라미터로 프롬프트 관리 API를 보호하세요
  2. 권한 부여: 커스텀 query 파라미터로 팀/사용자 기반 접근 제어 구현
  3. 속도 제한: API 남용을 막기 위한 rate limiting 추가
  4. 입력 검증: 처리 전 모든 query 파라미터 검증
  5. HTTPS: 프로덕션에서는 암호화 통신을 위해 항상 HTTPS 사용
  6. 시크릿: API 키는 config 파일이 아닌 환경 변수에 저장

사용 사례

제네릭 프롬프트 관리 API 사용:

  • PR을 기다리지 않고 즉시 통합하고 싶을 때
  • 프롬프트 관리 서비스를 직접 유지할 때
  • 프롬프트 버전 관리와 업데이트를 완전히 제어해야 할 때
  • 커스텀 프롬프트 관리 기능을 만들고 싶을 때
  • 내부 시스템과 통합해야 할 때

일반적인 시나리오:

  • 조직을 위한 내부 프롬프트 관리 시스템
  • 팀 기반 접근 제어가 있는 멀티 테넌트 프롬프트 관리
  • 서로 다른 프롬프트 버전의 A/B 테스트
  • 프롬프트 실험과 분석
  • 기존 프롬프트 엔지니어링 워크플로와 통합

언제 이것을 써야 하나

제네릭 프롬프트 관리 API 사용:

  • PR을 기다리지 않고 즉시 통합하고 싶을 때
  • 프롬프트 관리 서비스를 직접 유지할 때
  • 업데이트와 기능을 완전히 제어해야 할 때
  • 커스텀 프롬프트 저장 및 버전 관리 로직을 원할 때

PR을 만들기:

  • LiteLLM 내부와 더 깊이 통합하고 싶을 때
  • 통합에 복잡한 LiteLLM 전용 로직이 필요할 때
  • 빌트인 프로바이더로 소개되고 싶을 때
  • 커뮤니티를 위한 재사용 가능한 통합을 만들 때

문제 해결

프롬프트를 찾지 못함

  • prompt_id가 정확히 일치하는지 확인 (대소문자 구분)
  • API 엔드포인트가 LiteLLM에서 접근 가능한지 확인
  • api_key를 사용한다면 인증 확인

변수가 치환되지 않음

  • 변수가 {variable} 또는 {{variable}} 문법을 사용하는지 확인
  • prompt_variables의 변수 이름이 템플릿과 정확히 일치하는지 확인
  • 변수는 대소문자를 구분해요

모델이 오버라이드되지 않음

  • config에 ignore_prompt_manager_model: true가 있는지 확인
  • API가 응답에서 prompt_template_model을 반환하는지 확인

파라미터가 적용되지 않음

  • ignore_prompt_manager_optional_params: true가 설정됐는지 확인
  • API가 prompt_template_optional_params를 반환하는지 확인
  • 파라미터 이름이 OpenAI의 파라미터 이름과 일치하는지 확인

질문?

이것은 베타 API예요. 피드백에 따라 적극적으로 개선 중입니다. 추가 기능이 필요하면 이슈나 PR을 열어 주세요.

더 알아보기 (Learn more)