OpenAI API

OpenAI API

이 페이지는 SGLang 확산 HTTP 서버의 OpenAI 호환 API를 설명해요. 이미지 및 비디오 생성을 위한 엔드포인트와 LoRA 어댑터 관리를 다뤄요.

출처: 문서

본문

SGLang 확산 HTTP 서버는 이미지 및 비디오 생성과 LoRA 어댑터 관리를 위한 OpenAI 호환 API를 구현해요.

전제 조건 (Prerequisites)

  • OpenAI Python SDK를 사용할 계획이라면 Python 3.11 이상.

서빙 (Serve)

sglang serve 명령으로 서버를 실행해요.

서버 시작 (Start the server)

SERVER_ARGS=(
  --model-path Wan-AI/Wan2.1-T2V-1.3B-Diffusers
  --served-model-name wan-t2v
  --text-encoder-cpu-offload
  --pin-cpu-memory
  --num-gpus 4
  --ulysses-degree=2
  --ring-degree=2
  --port 30010
)

sglang serve "${SERVER_ARGS[@]}"
  • --model-path: 모델 경로 또는 모델 ID.
  • --served-model-name: 서빙 API가 노출하는 안정적인 모델 이름. 기본값은 설정 시 --model-id, 그렇지 않으면 --model-path.
  • --port: 수신할 HTTP 포트 (기본값: 30000).

서빙 모델 이름 (Served model name)

--served-model-name은 공개 API 정체성과 체크포인트 위치를 분리해요. 복제본이 다른 로컬 마운트 경로를 사용하거나 게이트웨이가 하나의 안정적인 모델 이름을 필요로 할 때 유용해요:

sglang serve \
  --model-path /models/Wan2.1-T2V-1.3B-Diffusers \
  --served-model-name wan-t2v \
  --port 30010

--model-id는 자유 형식 배포 별칭이 아니에요. 로컬 경로로 식별할 수 없는 체크포인트에 대해 등록된 모델 구성을 선택해요. --served-model-name은 서빙 API가 노출하는 이름만 제어해요. 둘 다 설정되면 API 응답에서는 서빙된 이름이 우선해요.

서빙 모델 발견 (Discover the served model)

엔드포인트: GET /v1/models

공개 모델 이름과 확산별 런타임 정보를 반환해요.

Curl 예시:

curl -sS "http://localhost:30010/v1/models"

응답 예시:

{
  "object": "list",
  "data": [
    {
      "id": "wan-t2v",
      "object": "model",
      "created": 1786348800,
      "owned_by": "sglang",
      "root": "wan-t2v",
      "parent": null,
      "max_model_len": null,
      "num_gpus": 4,
      "task_type": "T2V",
      "dit_precision": "bf16",
      "vae_precision": "fp16",
      "pipeline_name": "WanPipeline",
      "pipeline_class": "WanPipeline"
    }
  ]
}

서빙된 이름으로 동일한 모델 검색:

curl -sS "http://localhost:30010/v1/models/wan-t2v"

GET /server_info도 게이트웨이 발견을 위해 served_model_name을 보고해요. 비디오와 액션 응답은 요청이 모델을 명시적으로 제공하지 않을 때 이 이름을 사용해요.


엔드포인트 (Endpoints)

모델별 요청 필드 (Model-specific request fields)

이미지 및 비디오 요청 스키마는 OpenAI 필드와 모델 패밀리 간에 공유되는 안정적인 SGLang 확장만 포함해요. 모델별 컨트롤은 OpenAI Python 클라이언트를 사용할 때 extra_body에 넣어요:

from openai import OpenAI

client = OpenAI(api_key="EMPTY", base_url="http://localhost:30010/v1")
response = client.images.generate(
    model="ideogram-ai/ideogram-4-nf4",
    prompt="A typeset poster",
    extra_body={"preset": "V4_QUALITY_48"},
)

원시 JSON 요청은 이 필드를 최상위로 보낼 수 있어요. Multipart 비디오 요청은 개별 선언된 폼 필드 또는 extra_params의 하나의 JSON 객체를 보낼 수 있어요. SGLang은 활성 모델이 필드를 선언할 때만 모델별 확장을 전달하므로, 한 모델 패밀리를 위한 필드가 다른 모델을 조용히 바꾸지 않아요.

이미지 생성 (Image Generation)

서버는 /v1/images 네임스페이스 아래에 OpenAI 호환 Images API를 구현해요.

이미지 생성

엔드포인트: POST /v1/images/generations

요청 품질 (Request quality)

quality는 누적 요청 수준 최적화 등급을 선택해요: lossless는 선택된 배포의 참조 경로와 모든 무조건적 비트 정확 대체를 유지하고, extra-high는 요청 게이팅된 DiT/VAE 커널 융합만 추가로 활성화하며, high는 전체 extra-high 집합을 포함하고 모델 소유의 sparse, caching, 저정밀 또는 기타 근사 경로도 활성화할 수 있어요. 이 등급은 별도로 구성된 양자화, 어텐션 또는 캐싱 옵션을 덮어쓰지 않아요. lossless 런타임 기본값을 유지하려면 생략하거나(또는 OpenAI 기본 auto 전송) 하세요. 이는 출력 파일 압축만 제어하는 output_quality와 구별돼요. 동일한 확장이 이미지 편집과 비디오 요청에도 수용돼요.

Python 예시 (b64_json 응답):

import base64
from openai import OpenAI

client = OpenAI(api_key="«redacted:sk-…»", base_url="http://localhost:30010/v1")

img = client.images.generate(
    prompt="A calico cat playing a piano on stage",
    size="1024x1024",
    n=1,
    response_format="b64_json",
)

image_bytes = base64.b64decode(img.data[0].b64_json)
with open("output.png", "wb") as f:
    f.write(image_bytes)

Curl 예시:

curl -sS -X POST "http://localhost:30010/v1/images/generations" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
        "prompt": "A calico cat playing a piano on stage",
        "size": "1024x1024",
        "n": 1,
        "response_format": "b64_json"
      }'

Note response_format=url을 사용하고 클라우드 저장소가 구성되지 않으면 API는 /v1/images/<IMAGE_ID>/content 같은 상대 URL을 반환해요.

이미지 편집

엔드포인트: POST /v1/images/edits

이 엔드포인트는 입력 이미지와 텍스트 프롬프트가 있는 multipart 폼 업로드를 받아요. 서버는 base64 인코딩 이미지 또는 이미지를 내려받을 URL을 반환할 수 있어요.

Curl 예시 (b64_json 응답):

curl -sS -X POST "http://localhost:30010/v1/images/edits" \
  -H "Authorization: Bearer ***" \
  -F "image=@local_input_image.png" \
  -F "url=image_url.jpg" \
  -F "prompt=A calico cat playing a piano on stage" \
  -F "size=1024x1024" \
  -F "response_format=b64_json"

Curl 예시 (URL 응답):

curl -sS -X POST "http://localhost:30010/v1/images/edits" \
  -H "Authorization: Bearer ***" \
  -F "image=@local_input_image.png" \
  -F "url=image_url.jpg" \
  -F "prompt=A calico cat playing a piano on stage" \
  -F "size=1024x1024" \
  -F "response_format=url"

이미지 콘텐츠 내려받기

POST /v1/images/generations 또는 POST /v1/images/edits에서 response_format=url을 사용하면 API는 /v1/images/<IMAGE_ID>/content 같은 상대 URL을 반환해요.

엔드포인트: GET /v1/images/&#123;image_id&#125;/content

Curl 예시:

curl -sS -L "http://localhost:30010/v1/images/<IMAGE_ID>/content" \
  -H "Authorization: Bearer ***" \
  -o output.png

비디오 생성 (Video Generation)

서버는 /v1/videos 네임스페이스 아래에 OpenAI Videos API의 일부를 구현해요.

비디오 생성 (text-to-video)

엔드포인트: POST /v1/videos

Python 예시:

from openai import OpenAI

client = OpenAI(api_key="«redacted:sk-…»", base_url="http://localhost:30010/v1")

video = client.videos.create(
    prompt="A calico cat playing a piano on stage",
    size="1280x720"
)
print(f"Video ID: {video.id}, Status: {video.status}")

Curl 예시:

curl -sS -X POST "http://localhost:30010/v1/videos" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
        "prompt": "A calico cat playing a piano on stage",
        "size": "1280x720"
      }'

비디오 생성 (image-to-video)

I2V 또는 TI2V 모델(예: Wan2.1 I2V, LTX-2.3 two-stage)의 경우 multipart 폼 업로드 또는 참조 URL로 입력 이미지를 전달해요.

Curl 예시 (multipart 폼 업로드):

curl -sS -X POST "http://localhost:30010/v1/videos" \
  -H "Authorization: Bearer ***" \
  -F "prompt=A cat playing a piano" \
  -F "input_reference=@input_image.png" \
  -F "size=1280x720"

Curl 예시 (참조 URL):

curl -sS -X POST "http://localhost:30010/v1/videos" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
        "prompt": "A cat playing a piano",
        "reference_url": "https://example.com/input_image.png",
        "size": "1280x720"
      }'

비디오 목록

엔드포인트: GET /v1/videos

Python 예시:

videos = client.videos.list()
for item in videos.data:
    print(item.id, item.status)

Curl 예시:

curl -sS -X GET "http://localhost:30010/v1/videos" \
  -H "Authorization: Bearer ***"

비디오 콘텐츠 내려받기

엔드포인트: GET /v1/videos/&#123;video_id&#125;/content

Python 예시:

import time

# Poll for completion
while True:
    page = client.videos.list()
    item = next((v for v in page.data if v.id == video_id), None)
    if item and item.status == "completed":
        break
    time.sleep(5)

# Download content
resp = client.videos.download_content(video_id=video_id)
with open("output.mp4", "wb") as f:
    f.write(resp.read())

Curl 예시:

curl -sS -L "http://localhost:30010/v1/videos/<VIDEO_ID>/content" \
  -H "Authorization: Bearer ***" \
  -o output.mp4

LoRA 관리 (LoRA Management)

서버는 LoRA 어댑터의 동적 로딩, 병합, 병합 해제를 지원해요.

중요한 참고 사항:

  • 상호 배제: 한 번에 대상당 하나의 LoRA 구성만 활성화 가능
  • 전환: unmerge_lora_weights로 현재 LoRA를 비활성화한 다음 새 것으로 set
  • 캐싱: 서버는 로드된 LoRA 가중치를 메모리에 캐시해요. 이전에 로드된 LoRA(동일 경로)로의 전환은 비용이 거의 없어요.

LoRA 어댑터 설정

하나 이상의 LoRA 어댑터를 로드하고 모델에 적용해요. 기본적으로 일반 가중치는 정적으로 병합되고, FSDP-샤딩 가중치는 전체 gather 메모리 피크를 피하기 위해 동적 LoRA를 사용해요.

엔드포인트: POST /v1/set_lora

파라미터:

  • lora_nickname (string 또는 string 목록, 필수): LoRA 어댑터의 고유 식별자. 단일 문자열 또는 여러 LoRA의 문자열 목록일 수 있음
  • lora_path (string 또는 string/None 목록, 선택): .safetensors 파일 경로 또는 Hugging Face repo ID. 첫 로드에 필요하며, 캐시된 nickname을 재활성화할 때는 선택. 목록이면 lora_nickname과 길이 일치해야 함
  • target (string 또는 string 목록, 선택): LoRA를 적용할 트랜스포머. 목록이면 lora_nickname과 길이 일치해야 함. 유효한 값:
    • "all" (기본값): 모든 트랜스포머에 적용
    • "transformer": 기본 트랜스포머에만 적용 (Wan2.2의 고노이즈)
    • "transformer_2": transformer_2에만 적용 (Wan2.2의 저노이즈)
    • "critic": critic 모델에만 적용
  • strength (float 또는 float 목록, 선택): 병합용 LoRA 강도, 기본 1.0. 목록이면 lora_nickname과 길이 일치해야 함. < 1.0 값은 효과를 줄이고 > 1.0 값은 효과를 증폭
  • merge_mode (string, 선택): "auto"(기본 서버 정책), "merge"(정적 병합 강제) 또는 "dynamic"(forward 시 LoRA 적용)

단일 LoRA 예시:

curl -X POST http://localhost:30010/v1/set_lora \
  -H "Content-Type: application/json" \
  -d '{
        "lora_nickname": "lora_name",
        "lora_path": "/path/to/lora.safetensors",
        "target": "all",
        "strength": 0.8
      }'

여러 LoRA 예시:

curl -X POST http://localhost:30010/v1/set_lora \
  -H "Content-Type: application/json" \
  -d '{
        "lora_nickname": ["lora_1", "lora_2"],
        "lora_path": ["/path/to/lora1.safetensors", "/path/to/lora2.safetensors"],
        "target": ["transformer", "transformer_2"],
        "strength": [0.8, 1.0]
      }'

동일 대상의 여러 LoRA:

curl -X POST http://localhost:30010/v1/set_lora \
  -H "Content-Type: application/json" \
  -d '{
        "lora_nickname": ["style_lora", "character_lora"],
        "lora_path": ["/path/to/style.safetensors", "/path/to/character.safetensors"],
        "target": "all",
        "strength": [0.7, 0.9]
      }'

Note 여러 LoRA를 사용할 때:

  • 모든 목록 파라미터(lora_nickname, lora_path, target, strength)는 길이가 같아야 함
  • target 또는 strength가 단일 값이면 모든 LoRA에 적용됨
  • 같은 대상에 적용되는 여러 LoRA는 순서대로 적용됨

LoRA 가중치 병합

현재 설정된 LoRA 가중치를 기본 모델에 수동 병합해요.

Note FSDP-샤딩 가중치에서 수동 병합은 전체 gather를 요구하고 OOM할 수 있어요. 낮은 피크 경로에는 merge_mode="auto" 또는 "dynamic"으로 set_lora를 사용해요.

엔드포인트: POST /v1/merge_lora_weights

파라미터:

  • target (string, 선택): 병합할 트랜스포머. "all"(기본), "transformer", "transformer_2", "critic" 중 하나
  • strength (float, 선택): 병합용 LoRA 강도, 기본 1.0. < 1.0 값은 효과를 줄이고 > 1.0 값은 효과를 증폭

Curl 예시:

curl -X POST http://localhost:30010/v1/merge_lora_weights \
  -H "Content-Type: application/json" \
  -d '{"strength": 0.8}'

LoRA 가중치 병합 해제

현재 활성 LoRA 가중치를 기본 모델에서 병합 해제하여 원래 상태로 복원해요. 다른 LoRA를 설정하기 전에 반드시 호출해야 해요.

엔드포인트: POST /v1/unmerge_lora_weights

Curl 예시:

curl -X POST http://localhost:30010/v1/unmerge_lora_weights \
  -H "Content-Type: application/json"

LoRA 어댑터 나열

로드된 LoRA 어댑터와 모듈별 현재 적용 상태를 반환해요.

엔드포인트: GET /v1/list_loras

Curl 예시:

curl -sS -X GET "http://localhost:30010/v1/list_loras"

응답 예시:

{
  "loaded_adapters": [
    { "nickname": "lora_a", "path": "/weights/lora_a.safetensors" },
    { "nickname": "lora_b", "path": "/weights/lora_b.safetensors" }
  ],
  "active": {
    "transformer": [
      {
        "nickname": "lora2",
        "path": "tarn59/pixel_art_style_lora_z_image_turbo",
        "merged": true,
        "mode": "merged",
        "strength": 1.0
      }
    ]
  }
}

참고 사항:

  • 현재 파이프라인에서 LoRA가 활성화되지 않으면 서버가 오류를 반환해요.
  • num_lora_layers_with_weights는 활성 어댑터에 대해 LoRA 가중치가 적용된 레이어만 셈.

LoRA 전환 예시 (Example: Switching LoRAs)

  1. LoRA A 설정:
    curl -X POST http://localhost:30010/v1/set_lora -d '{"lora_nickname": "lora_a", "lora_path": "path/to/A"}'
    
  2. LoRA A로 생성...
  3. LoRA A 병합 해제:
    curl -X POST http://localhost:30010/v1/unmerge_lora_weights
    
  4. LoRA B 설정:
    curl -X POST http://localhost:30010/v1/set_lora -d '{"lora_nickname": "lora_b", "lora_path": "path/to/B"}'
    
  5. LoRA B로 생성...

출력 품질 조정 (Adjust Output Quality)

서버는 output-qualityoutput-compression 파라미터를 통해 이미지 및 비디오 생성의 출력 품질과 압축 수준을 조정하는 것을 지원해요.

파라미터

  • output-quality (string, 선택): 압축을 자동 설정하는 사전 품질 수준. 기본값은 "default". 유효한 값:

    • "maximum": 최고 품질 (100)
    • "high": 고품질 (90)
    • "medium": 중간 품질 (55)
    • "low": 저품질 (35)
    • "default": 미디어 유형에 따라 자동 조정 (비디오 50, 이미지 75)
  • output-compression (integer, 선택): 직접 압축 수준 오버라이드 (0-100). 기본값은 None. 제공되면(None이 아니면) output-quality보다 우선함.

    • 0: 최저 품질, 최소 파일 크기
    • 100: 최고 품질, 최대 파일 크기

참고 사항

  • 우선순위: output-qualityoutput-compression이 모두 제공되면 output-compression이 우선해요.
  • 형식 지원: 품질 설정은 JPEG와 비디오 형식에 적용돼요. PNG는 무손실 압축을 사용하고 이 설정을 무시해요.
  • 파일 크기 vs 품질: 더 낮은 압축 값(또는 "low" 품질 프리셋)은 더 작은 파일을 만들지만 보이는 아티팩트가 생길 수 있어요.

더 알아보기