설정 가이드¶
이 가이드는 Continuum Router 설정에 대한 종합적인 문서를 제공합니다. 라우터는 다양한 배포 시나리오에 최대한의 유연성을 제공하기 위해 명확한 우선순위 시스템과 함께 여러 설정 방법을 지원합니다.
설정 방법¶
Continuum Router는 세 가지 설정 방법을 지원합니다:
- 설정 파일 (YAML) - 프로덕션 환경에 권장
- 환경 변수 - 컨테이너화된 배포에 적합
- 명령줄 인수 - 테스트 및 오버라이드에 유용
설정 파일 검색¶
라우터는 다음 위치에서 설정 파일을 자동으로 검색합니다 (순서대로):
--config플래그로 지정된 경로./config.yaml(현재 디렉토리)./config.yml/etc/continuum-router/config.yaml/etc/continuum-router/config.yml~/.config/continuum-router/config.yaml~/.config/continuum-router/config.yml
설정 우선순위¶
설정은 다음 우선순위로 적용됩니다 (높은 것부터 낮은 순):
- 명령줄 인수 (최고 우선순위)
- 환경 변수
- 설정 파일
- 기본값 (최저 우선순위)
이를 통해 다음이 가능합니다:
- 파일에 기본 설정 지정
- 컨테이너에서 환경 변수로 특정 설정 오버라이드
- 명령줄 인수로 임시 조정
설정 파일 형식¶
전체 설정 예제¶
# Continuum Router 설정
# 이 예제는 모든 사용 가능한 설정 옵션과 기본값을 보여줍니다
# 서버 설정
server:
# bind_address는 단일 문자열 또는 주소 배열을 허용합니다
# TCP 형식: "host:port", Unix 소켓 형식: "unix:/path/to/socket"
bind_address: "0.0.0.0:8080" # 단일 주소 (하위 호환)
# bind_address: # 또는 다중 주소:
# - "0.0.0.0:8080" # 모든 인터페이스의 TCP
# - "unix:/var/run/router.sock" # Unix 소켓 (Unix/Linux/macOS만 해당)
# socket_mode: 0o660 # 선택 사항: Unix 소켓 파일 권한
workers: 4 # 워커 스레드 수 (0 = 자동 감지)
connection_pool_size: 100 # 백엔드당 최대 유휴 연결 수
# 모델 메타데이터 설정 (선택 사항)
model_metadata_file: "model-metadata.yaml" # 외부 모델 메타데이터 파일 경로
# 백엔드 설정
backends:
# 내장 설정이 있는 네이티브 OpenAI API
- name: "openai"
type: openai # 네이티브 OpenAI 백엔드 사용
api_key: "${CONTINUUM_OPENAI_API_KEY}" # 환경 변수에서 로드
org_id: "${CONTINUUM_OPENAI_ORG_ID}" # 선택적 조직 ID
weight: 3
models: # 사용할 모델 지정
- gpt-4o
- gpt-4o-mini
- o3-mini
- text-embedding-3-large
retry_override: # 백엔드별 재시도 설정 (선택 사항)
max_attempts: 5
base_delay: "200ms"
max_delay: "30s"
exponential_backoff: true
jitter: true
# 사용자 정의 메타데이터가 있는 일반 OpenAI 호환 백엔드
- name: "openai-compatible"
url: "https://custom-llm.example.com"
weight: 1
models:
- "gpt-4"
- "gpt-3.5-turbo"
model_configs: # 메타데이터가 포함된 향상된 모델 설정
- id: "gpt-4"
aliases: # 이 메타데이터를 공유하는 대체 ID (선택 사항)
- "gpt-4-0125-preview"
- "gpt-4-turbo-preview"
metadata:
display_name: "GPT-4"
summary: "복잡한 작업을 위한 가장 유능한 GPT-4 모델"
capabilities: ["text", "image", "function_calling"]
knowledge_cutoff: "2024-04"
pricing:
input_tokens: 0.03
output_tokens: 0.06
limits:
context_window: 128000
max_output: 4096
# 자동 URL 감지가 있는 Ollama 로컬 서버
- name: "local-ollama"
type: ollama # 기본값 http://localhost:11434
weight: 2
models:
- "llama2"
- "mistral"
- "codellama"
# vLLM 서버
- name: "vllm-server"
type: vllm
url: "http://localhost:8000"
weight: 1
# 지정하지 않으면 모델이 자동으로 검색됩니다
# 네임스페이스 접두사가 있는 모델 (예: "custom/gpt-4")은
# 자동으로 기본 이름의 메타데이터와 일치합니다 (예: "gpt-4")
# Google Gemini API (네이티브 백엔드)
- name: "gemini"
type: gemini # 네이티브 Gemini 백엔드 사용
api_key: "${CONTINUUM_GEMINI_API_KEY}" # 환경 변수에서 로드
weight: 2
models:
- gemini-2.5-pro
- gemini-2.5-flash
- gemini-2.0-flash
# 헬스 모니터링 설정
health_checks:
enabled: true # 헬스 체크 활성화/비활성화
interval: "30s" # 백엔드 상태 확인 주기
timeout: "10s" # 헬스 체크 요청 타임아웃
unhealthy_threshold: 3 # 비정상으로 표시되기 전 실패 횟수
healthy_threshold: 2 # 정상으로 표시되기 전 성공 횟수
endpoint: "/v1/models" # 헬스 체크에 사용되는 엔드포인트
# 요청 처리 및 타임아웃 설정
timeouts:
connection: "10s" # TCP 연결 설정 타임아웃
request:
standard: # 비스트리밍 요청
first_byte: "30s" # 첫 바이트 수신까지의 시간
total: "180s" # 전체 요청 타임아웃 (3분)
streaming: # 스트리밍 (SSE) 요청
first_byte: "60s" # 첫 SSE 청크까지의 시간
chunk_interval: "30s" # 청크 간 최대 시간
total: "600s" # 전체 스트리밍 타임아웃 (10분)
image_generation: # 이미지 생성 요청 (DALL-E 등)
first_byte: "60s" # 첫 바이트 수신까지의 시간
total: "180s" # 전체 타임아웃 (기본 3분)
model_overrides: # 모델별 타임아웃 오버라이드
gpt-5-latest:
streaming:
total: "1200s" # GPT-5용 20분
gpt-4o:
streaming:
total: "900s" # GPT-4o용 15분
health_check:
timeout: "5s" # 헬스 체크 타임아웃
interval: "30s" # 헬스 체크 간격
request:
max_retries: 3 # 요청 최대 재시도 횟수
retry_delay: "1s" # 재시도 간 초기 지연
# 전역 재시도 및 복원력 설정
retry:
max_attempts: 3 # 최대 재시도 횟수
base_delay: "100ms" # 재시도 간 기본 지연
max_delay: "30s" # 재시도 간 최대 지연
exponential_backoff: true # 지수 백오프 사용
jitter: true # 지연에 무작위 지터 추가
# 캐싱 및 최적화 설정
cache:
model_cache_ttl: "300s" # 모델 목록 캐시 5분
deduplication_ttl: "60s" # 요청 중복 제거 1분
enable_deduplication: true # 요청 중복 제거 활성화
# 로깅 설정
logging:
level: "info" # 로그 레벨: trace, debug, info, warn, error
format: "json" # 로그 형식: json, pretty
enable_colors: false # 컬러 출력 활성화 (pretty 형식용)
# Files API 설정
files:
enabled: true # Files API 엔드포인트 활성화/비활성화
max_file_size: 536870912 # 최대 파일 크기 (바이트, 기본: 512MB)
storage_path: "./data/files" # 업로드된 파일 저장 경로 (~지원)
retention_days: 0 # 파일 보존 기간 (일, 0 = 영구 보존)
metadata_storage: persistent # 메타데이터 백엔드: "memory" 또는 "persistent" (기본)
cleanup_orphans_on_startup: false # 시작 시 고아 파일 자동 정리
# 인증 및 권한
auth:
method: api_key # "none" 또는 "api_key" (기본)
required_scope: files # 접근에 필요한 API 키 스코프
enforce_ownership: true # 사용자가 자신의 파일만 접근 가능
admin_can_access_all: true # admin 스코프는 모든 파일 접근 가능
# 로드 밸런싱 설정
load_balancer:
strategy: "round_robin" # 전략: round_robin, weighted, random
health_aware: true # 정상 백엔드로만 라우팅
# 분산 추적 설정
tracing:
enabled: true # 분산 추적 활성화/비활성화
w3c_trace_context: true # W3C Trace Context 지원 (traceparent 헤더)
headers:
trace_id: "X-Trace-ID" # 추적 ID 헤더 이름
request_id: "X-Request-ID" # 요청 ID 헤더 이름
correlation_id: "X-Correlation-ID" # 상관 ID 헤더 이름
# 서킷 브레이커 설정
circuit_breaker:
enabled: false # 서킷 브레이커 활성화
failure_threshold: 5 # 회로 개방까지의 실패 횟수
recovery_timeout: "60s" # 복구 시도까지의 시간
half_open_retries: 3 # 반개방 상태에서의 재시도 횟수
# 속도 제한 설정
rate_limiting:
enabled: false # 속도 제한 활성화
requests_per_second: 100 # 전역 초당 요청 수
burst_size: 200 # 버스트 용량
# 메트릭 및 모니터링 설정
metrics:
enabled: false # 메트릭 수집 활성화
endpoint: "/metrics" # 메트릭 엔드포인트 경로
include_labels: true # 상세 레이블 포함
최소 설정¶
# 최소 설정 - 나머지 설정은 기본값 사용
server:
bind_address: "0.0.0.0:8080"
backends:
- name: "ollama"
url: "http://localhost:11434"
- name: "lm-studio"
url: "http://localhost:1234"
환경 변수¶
모든 설정 옵션은 CONTINUUM_ 접두사가 붙은 환경 변수로 오버라이드할 수 있습니다:
서버 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_BIND_ADDRESS | string | "0.0.0.0:8080" | 서버 바인드 주소 |
CONTINUUM_WORKERS | integer | 4 | 워커 스레드 수 |
CONTINUUM_CONNECTION_POOL_SIZE | integer | 100 | HTTP 연결 풀 크기 |
백엔드 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_BACKEND_URLS | string | 쉼표로 구분된 백엔드 URL | |
CONTINUUM_BACKEND_WEIGHTS | string | 쉼표로 구분된 가중치 (URL과 일치해야 함) |
헬스 체크 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_HEALTH_CHECKS_ENABLED | boolean | true | 헬스 체크 활성화 |
CONTINUUM_HEALTH_CHECK_INTERVAL | string | "30s" | 헬스 체크 간격 |
CONTINUUM_HEALTH_CHECK_TIMEOUT | string | "10s" | 헬스 체크 타임아웃 |
CONTINUUM_UNHEALTHY_THRESHOLD | integer | 3 | 비정상 판정까지의 실패 횟수 |
CONTINUUM_HEALTHY_THRESHOLD | integer | 2 | 정상 판정까지의 성공 횟수 |
요청 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_REQUEST_TIMEOUT | string | "300s" | 최대 요청 타임아웃 |
CONTINUUM_MAX_RETRIES | integer | 3 | 최대 재시도 횟수 |
CONTINUUM_RETRY_DELAY | string | "1s" | 초기 재시도 지연 |
로깅 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_LOG_LEVEL | string | "info" | 로그 레벨 |
CONTINUUM_LOG_FORMAT | string | "json" | 로그 형식 |
CONTINUUM_LOG_COLORS | boolean | false | 컬러 출력 활성화 |
RUST_LOG | string | Rust 전용 로깅 설정 |
캐시 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_MODEL_CACHE_TTL | string | "300s" | 모델 캐시 TTL |
CONTINUUM_DEDUPLICATION_TTL | string | "60s" | 중복 제거 TTL |
CONTINUUM_ENABLE_DEDUPLICATION | boolean | true | 중복 제거 활성화 |
Files API 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_FILES_ENABLED | boolean | true | Files API 활성화/비활성화 |
CONTINUUM_FILES_MAX_SIZE | integer | 536870912 | 최대 파일 크기 (바이트, 512MB) |
CONTINUUM_FILES_STORAGE_PATH | string | "./data/files" | 업로드 파일 저장 경로 |
CONTINUUM_FILES_RETENTION_DAYS | integer | 0 | 파일 보존 기간 (일, 0 = 영구) |
CONTINUUM_FILES_METADATA_STORAGE | string | "persistent" | 메타데이터 백엔드: "memory" 또는 "persistent" |
CONTINUUM_FILES_CLEANUP_ORPHANS | boolean | false | 시작 시 고아 파일 자동 정리 |
CONTINUUM_FILES_AUTH_METHOD | string | "api_key" | 인증 방법: "none" 또는 "api_key" |
CONTINUUM_FILES_AUTH_SCOPE | string | "files" | Files API 접근에 필요한 API 키 스코프 |
CONTINUUM_FILES_ENFORCE_OWNERSHIP | boolean | true | 사용자가 자신의 파일만 접근 가능 |
CONTINUUM_FILES_ADMIN_ACCESS_ALL | boolean | true | admin 스코프는 모든 파일 접근 가능 |
CONTINUUM_DEV_MODE | boolean | false | 개발용 API 키 활성화 (프로덕션에서 사용 금지) |
API 키 관리 설정¶
| 변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
CONTINUUM_API_KEY | string | - | 간단한 배포용 단일 API 키 |
CONTINUUM_API_KEY_SCOPES | string | "read,write" | API 키의 쉼표로 구분된 스코프 |
CONTINUUM_API_KEY_USER_ID | string | "admin" | API 키와 연결된 사용자 ID |
CONTINUUM_API_KEY_ORG_ID | string | "default" | API 키와 연결된 조직 ID |
CONTINUUM_DEV_MODE | boolean | false | 개발용 API 키 활성화 (프로덕션에서 사용 금지) |
환경 설정 예제¶
# 기본 설정
export CONTINUUM_BIND_ADDRESS="0.0.0.0:9000"
export CONTINUUM_BACKEND_URLS="http://localhost:11434,http://localhost:1234"
export CONTINUUM_LOG_LEVEL="debug"
# 고급 설정
export CONTINUUM_CONNECTION_POOL_SIZE="200"
export CONTINUUM_HEALTH_CHECK_INTERVAL="60s"
export CONTINUUM_MODEL_CACHE_TTL="600s"
export CONTINUUM_ENABLE_DEDUPLICATION="true"
# 라우터 시작
continuum-router
명령줄 인수¶
명령줄 인수는 최고 우선순위의 설정 방법으로 테스트와 임시 오버라이드에 유용합니다.
핵심 옵션¶
| 인수 | 타입 | 설명 |
|---|---|---|
-c, --config <FILE> | path | 설정 파일 경로 |
--generate-config | flag | 샘플 설정을 생성하고 종료 |
백엔드 설정¶
| 인수 | 타입 | 설명 |
|---|---|---|
--backends <URLs> | string | 쉼표로 구분된 백엔드 URL |
--backend-url <URL> | string | 단일 백엔드 URL (지원 중단) |
서버 설정¶
| 인수 | 타입 | 설명 |
|---|---|---|
--bind <ADDRESS> | string | 서버 바인드 주소 |
--connection-pool-size <SIZE> | integer | HTTP 연결 풀 크기 |
헬스 체크 설정¶
| 인수 | 타입 | 설명 |
|---|---|---|
--disable-health-checks | flag | 헬스 모니터링 비활성화 |
--health-check-interval <SECONDS> | integer | 헬스 체크 간격 |
--health-check-timeout <SECONDS> | integer | 헬스 체크 타임아웃 |
--unhealthy-threshold <COUNT> | integer | 비정상 판정까지의 실패 횟수 |
--healthy-threshold <COUNT> | integer | 정상 판정까지의 성공 횟수 |
CLI 사용 예제¶
# 설정 파일과 오버라이드 사용
continuum-router --config config.yaml --bind "0.0.0.0:9000"
# 백엔드 임시 오버라이드
continuum-router --config config.yaml --backends "http://localhost:11434"
# 테스트용 헬스 체크 설정 조정
continuum-router --config config.yaml --health-check-interval 10
# 샘플 설정 생성
continuum-router --generate-config > my-config.yaml
설정 섹션¶
서버 섹션¶
HTTP 서버 동작을 제어합니다:
server:
bind_address: "0.0.0.0:8080" # 바인드할 호스트와 포트
workers: 4 # 워커 스레드 (0 = 자동)
connection_pool_size: 100 # HTTP 연결 풀 크기
다중 바인드 주소 및 Unix 소켓¶
서버는 Unix 도메인 소켓(Unix 계열 시스템)을 포함하여 여러 주소에 동시에 바인딩하는 것을 지원합니다. 이를 통해 다음과 같은 유연한 배포 시나리오가 가능합니다:
- IPv4와 IPv6 주소 모두에서 수신
- 외부 클라이언트용 TCP 포트 노출과 로컬 서비스용 Unix 소켓 동시 사용
- 보안 강화를 위한 Unix 소켓을 통한 리버스 프록시 운영
단일 주소 (하위 호환):
다중 주소:
server:
bind_address:
- "127.0.0.1:8080" # IPv4 localhost
- "[::1]:8080" # IPv6 localhost
- "0.0.0.0:9090" # 포트 9090의 모든 인터페이스
Unix 소켓 바인딩 (Unix/Linux/macOS만 해당):
server:
bind_address:
- "0.0.0.0:8080" # 외부 액세스용 TCP
- "unix:/var/run/continuum-router.sock" # 로컬 서비스용 Unix 소켓
socket_mode: 0o660 # 선택 사항: Unix 소켓의 파일 권한 (8진수)
설정 옵션:
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
bind_address | 문자열 또는 배열 | "0.0.0.0:8080" | 바인드할 주소. TCP 형식: host:port. Unix 소켓 형식: unix:/path/to/socket |
socket_mode | 정수 (8진수) | null | Unix 소켓의 파일 권한 (예: 0o660은 소유자/그룹 읽기-쓰기) |
Unix 소켓 참고사항:
- Unix 소켓 주소는
unix:접두사로 시작해야 합니다 - 기존 소켓 파일은 바인딩 전에 자동으로 삭제됩니다
- 소켓 파일은 정상적인 종료 시 정리됩니다
- Unix가 아닌 플랫폼에서는
unix:주소가 경고를 기록하고 건너뜁니다 - Unix 소켓 연결은 IP 기반 인증 검사를 우회합니다 (클라이언트 IP는 "unix"로 보고됨)
Nginx 리버스 프록시 예제:
upstream continuum {
server unix:/var/run/continuum-router.sock;
}
server {
listen 443 ssl;
location /v1/ {
proxy_pass http://continuum;
}
}
성능 튜닝:
workers: 자동 감지를 위해 0으로 설정하거나 CPU 코어 수와 일치connection_pool_size: 고부하 시나리오에서는 증가 (200-500)
CORS 설정¶
CORS (Cross-Origin Resource Sharing)는 라우터가 다른 출처에서 실행되는 웹 브라우저의 요청을 수락할 수 있게 합니다. 다음 환경에 continuum-router를 임베딩할 때 필수적입니다:
- Tauri 앱:
tauri://localhost와 같은 출처를 사용하는 WebView - Electron 앱: 사용자 정의 프로토콜
- 별도의 웹 프론트엔드: 다른 포트의 개발 서버
server:
bind_address: "0.0.0.0:8080"
cors:
enabled: true
allow_origins:
- "tauri://localhost"
- "http://localhost:*" # 와일드카드 포트 매칭
- "https://example.com"
allow_methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
- "PATCH"
allow_headers:
- "Content-Type"
- "Authorization"
- "X-Request-ID"
- "X-Trace-ID"
expose_headers:
- "X-Request-ID"
- "X-Fallback-Used"
allow_credentials: false
max_age: 3600 # 프리플라이트 캐시 기간 (초)
CORS 설정 옵션:
| 옵션 | 유형 | 기본값 | 설명 |
|---|---|---|---|
enabled | boolean | false | CORS 미들웨어 활성화/비활성화 |
allow_origins | array | [] | 허용된 출처 (*로 모든 출처 허용, http://localhost:*와 같은 포트 와일드카드 지원) |
allow_methods | array | ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"] | 허용된 HTTP 메서드 |
allow_headers | array | ["Content-Type", "Authorization", "X-Request-ID", "X-Trace-ID"] | 허용된 요청 헤더 |
expose_headers | array | [] | 클라이언트 JavaScript에 노출되는 헤더 |
allow_credentials | boolean | false | 쿠키와 인증 헤더 허용 |
max_age | integer | 3600 | 프리플라이트 응답 캐시 기간 (초) |
출처 패턴 매칭:
| 패턴 | 예시 | 설명 |
|---|---|---|
* | * | 모든 출처 매칭 (allow_credentials: true와 호환되지 않음) |
| 정확한 URL | https://example.com | 정확히 일치 |
| 사용자 정의 스킴 | tauri://localhost | 사용자 정의 프로토콜 (Tauri, Electron) |
| 포트 와일드카드 | http://localhost:* | localhost의 모든 포트 매칭 |
보안 고려사항:
- 출처에
*를 사용하면 모든 웹사이트에서 요청할 수 있습니다 - 공개 API에만 사용하세요 allow_credentials가true일 때 출처에*를 사용할 수 없습니다 - 정확한 출처를 지정하세요- 개발 시에는 유연성을 위해
http://localhost:*와 같은 포트 와일드카드를 사용하세요 - 프로덕션에서는 보안을 위해 항상 정확한 출처를 지정하세요
핫 리로드: CORS 설정은 즉시 핫 리로드를 지원합니다 - 서버 재시작 없이 새 요청에 변경 사항이 즉시 적용됩니다.
백엔드 섹션¶
요청을 라우팅할 LLM 백엔드를 정의합니다:
backends:
- name: "unique-identifier" # 모든 백엔드에서 고유해야 함
type: "generic" # 백엔드 유형 (선택 사항, 기본값 "generic")
url: "http://backend:port" # 백엔드 기본 URL
weight: 1 # 로드 밸런싱 가중치 (1-100)
api_key: "${API_KEY}" # API 키 (선택 사항, 환경 변수 참조 지원)
org_id: "${ORG_ID}" # 조직 ID (선택 사항, OpenAI용)
models: ["model1", "model2"] # 선택 사항: 명시적 모델 목록
retry_override: # 선택 사항: 백엔드별 재시도 설정
max_attempts: 5
base_delay: "200ms"
백엔드 없이 시작하기¶
라우터는 빈 백엔드 목록(backends: [])으로 시작할 수 있으며, 다음과 같은 경우에 유용합니다:
- 인프라 부트스트래핑: 라우터를 먼저 시작한 다음 Admin API를 통해 동적으로 백엔드 추가
- 컨테이너 오케스트레이션: 백엔드 서비스보다 라우터 컨테이너가 먼저 준비될 수 있음
- 개발 워크플로우: 백엔드가 프로비저닝되기 전에 관리자 엔드포인트 테스트
- 점진적 롤아웃: 백엔드 없이 시작하여 점진적으로 추가
백엔드 없이 실행할 때:
/v1/models는{"object": "list", "data": []}를 반환/v1/chat/completions및 기타 라우팅 엔드포인트는 503 "No backends available" 반환/health는 정상 상태 반환 (라우터 자체는 작동 중)POST /admin/backends를 통해 백엔드 추가 가능
동적 백엔드 관리를 위한 최소 구성 예제:
server:
bind_address: "0.0.0.0:8080"
backends: [] # 백엔드 없이 시작 - 나중에 Admin API로 추가
admin:
auth:
method: bearer
token: "${ADMIN_TOKEN}"
지원되는 백엔드 유형:
| 유형 | 설명 | 기본 URL |
|---|---|---|
generic | OpenAI 호환 API (기본) | 지정 필수 |
openai | 내장 설정이 있는 네이티브 OpenAI API | https://api.openai.com/v1 |
gemini | Google Gemini API (OpenAI 호환 엔드포인트) | https://generativelanguage.googleapis.com/v1beta/openai |
azure | Azure OpenAI Service | 지정 필수 |
vllm | vLLM 서버 | 지정 필수 |
ollama | Ollama 로컬 서버 | http://localhost:11434 |
llamacpp | llama.cpp llama-server (GGUF 모델) | http://localhost:8080 |
anthropic | Anthropic Claude API (요청/응답 변환 포함 네이티브) | https://api.anthropic.com |
네이티브 OpenAI 백엔드¶
type: openai를 사용하면 라우터는 다음을 제공합니다:
- 기본 URL:
https://api.openai.com/v1(프록시용으로 오버라이드 가능) - 내장 모델 메타데이터: 자동 가격, 컨텍스트 윈도우, 기능
- 환경 변수 지원:
CONTINUUM_OPENAI_API_KEY및CONTINUUM_OPENAI_ORG_ID에서 자동 로드
최소 OpenAI 설정:
명시적 API 키가 있는 전체 OpenAI 설정:
backends:
- name: "openai-primary"
type: openai
api_key: "${CONTINUUM_OPENAI_API_KEY}"
org_id: "${CONTINUUM_OPENAI_ORG_ID}" # 선택 사항
models:
- gpt-4o
- gpt-4o-mini
- o1
- o1-mini
- o3-mini
- text-embedding-3-large
프록시와 함께 OpenAI 사용:
backends:
- name: "openai-proxy"
type: openai
url: "https://my-proxy.example.com/v1" # 기본 URL 오버라이드
api_key: "${PROXY_API_KEY}"
models:
- gpt-4o
OpenAI 환경 변수¶
| 변수 | 설명 |
|---|---|
CONTINUUM_OPENAI_API_KEY | OpenAI API 키 (type: openai 백엔드용으로 자동 로드) |
CONTINUUM_OPENAI_ORG_ID | OpenAI 조직 ID (선택 사항) |
모델 자동 검색:
models가 지정되지 않았거나 비어 있으면, 백엔드는 초기화 중에 /v1/models API 엔드포인트에서 사용 가능한 모델을 자동으로 검색합니다. 이 기능은 설정 유지보수를 줄이고 백엔드에서 보고된 모든 모델이 라우팅 가능하도록 보장합니다.
| 백엔드 유형 | 자동 검색 지원 | 폴백 모델 |
|---|---|---|
openai | ✅ 예 | gpt-4o, gpt-4o-mini, o3-mini |
gemini | ✅ 예 | gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash |
vllm | ✅ 예 | vicuna-7b-v1.5, llama-2-7b-chat, mistral-7b-instruct |
ollama | ✅ 예 | vLLM 검색 메커니즘 사용 |
llamacpp | ✅ 예 | /v1/models 엔드포인트에서 자동 검색 |
anthropic | ❌ 아니오 (API 없음) | 하드코딩된 Claude 모델 |
generic | ❌ 아니오 | 모든 모델 지원 (supports_model()이 true 반환) |
검색 동작:
- 타임아웃: 10초 타임아웃으로 시작 차단 방지
- 폴백: 검색 실패 시 (타임아웃, 네트워크 오류, 잘못된 응답) 폴백 모델 사용
- 로깅: 검색된 모델은 INFO 레벨로 로깅; 폴백 사용은 WARN 레벨로 로깅
모델 해석 우선순위:
- 설정의 명시적
models목록 (최고 우선순위) model_configs필드의 모델- 백엔드 API에서 자동 검색된 모델
-
하드코딩된 폴백 모델 (최저 우선순위)
-
명시적 모델 목록은 시작 시간을 개선하고 백엔드 쿼리를 줄입니다
네이티브 Gemini 백엔드¶
type: gemini를 사용하면 라우터는 다음을 제공합니다:
- 기본 URL:
https://generativelanguage.googleapis.com/v1beta/openai(OpenAI 호환 엔드포인트) - 내장 모델 메타데이터: Gemini 모델에 대한 자동 컨텍스트 윈도우 및 기능
- 환경 변수 지원:
CONTINUUM_GEMINI_API_KEY에서 자동 로드 - 확장 스트리밍 타임아웃: 사고 모델용 300초 타임아웃 (gemini-2.5-pro, gemini-3-pro)
- 자동 max_tokens 조정: 사고 모델용, 아래 참조
최소 Gemini 설정:
backends:
- name: "gemini"
type: gemini
models:
- gemini-2.5-pro
- gemini-2.5-flash
- gemini-2.0-flash
API 키를 사용한 전체 Gemini 설정:
backends:
- name: "gemini"
type: gemini
api_key: "${CONTINUUM_GEMINI_API_KEY}"
weight: 2
models:
- gemini-2.5-pro
- gemini-2.5-flash
- gemini-2.0-flash
Gemini 인증 방법¶
Gemini 백엔드는 두 가지 인증 방법을 지원합니다:
API 키 인증 (기본)¶
Google AI Studio API 키를 사용하는 가장 간단한 인증 방법:
backends:
- name: "gemini"
type: gemini
api_key: "${CONTINUUM_GEMINI_API_KEY}"
models:
- gemini-2.5-pro
서비스 계정 인증¶
엔터프라이즈 환경 및 Google Cloud Platform (GCP) 배포의 경우, 자동 OAuth2 토큰 관리와 함께 서비스 계정 인증을 사용할 수 있습니다:
backends:
- name: "gemini"
type: gemini
auth:
type: service_account
key_file: "/path/to/service-account.json"
models:
- gemini-2.5-pro
- gemini-2.5-flash
키 파일 경로에 환경 변수 사용:
backends:
- name: "gemini"
type: gemini
auth:
type: service_account
key_file: "${GOOGLE_APPLICATION_CREDENTIALS}"
models:
- gemini-2.5-pro
서비스 계정 인증 기능:
| 기능 | 설명 |
|---|---|
| 자동 토큰 갱신 | OAuth2 토큰이 만료 5분 전에 자동으로 갱신됩니다 |
| 토큰 캐싱 | 인증 오버헤드를 최소화하기 위해 토큰이 메모리에 캐시됩니다 |
| 스레드 안전 | 동시 요청이 토큰 갱신 작업을 안전하게 공유합니다 |
| 환경 변수 확장 | 키 파일 경로가 ${VAR} 및 ~ 확장을 지원합니다 |
서비스 계정 키 생성:
- Google Cloud Console로 이동
- IAM & Admin > Service Accounts로 이동
- 새 서비스 계정을 만들거나 기존 계정 선택
- Keys > Add Key > Create new key 클릭
- JSON 형식을 선택하고 키 파일 다운로드
- 키 파일을 안전하게 저장하고 설정에서 참조
필요한 권한:
Gemini API 접근을 위해 서비스 계정에 다음 역할이 필요합니다:
roles/aiplatform.user- Vertex AI Gemini 엔드포인트용- 또는 generativelanguage.googleapis.com용 적절한 Google AI Studio 권한
인증 우선순위¶
여러 인증 방법이 설정된 경우:
| 우선순위 | 방법 | 조건 |
|---|---|---|
| 1 (최고) | auth 블록 | auth.type이 지정된 경우 |
| 2 | api_key 필드 | auth 블록이 없는 경우 |
| 3 | 환경 변수 | CONTINUUM_GEMINI_API_KEY로 폴백 |
api_key와 auth가 모두 지정된 경우, auth 블록이 우선되며 경고가 기록됩니다.
Gemini 사고 모델: 자동 max_tokens 조정¶
Gemini "사고" 모델 (gemini-2.5-pro, gemini-3-pro, -pro-preview 접미사가 있는 모델)은 응답 생성 전에 확장된 추론을 수행합니다. 응답 잘림을 방지하기 위해 라우터가 자동으로 max_tokens를 조정합니다:
| 조건 | 동작 |
|---|---|
max_tokens 미지정 | 자동으로 16384로 설정 |
max_tokens < 4096 | 자동으로 16384로 증가 |
max_tokens >= 4096 | 클라이언트 값 유지 |
이렇게 하면 클라이언트 라이브러리의 낮은 기본값으로 인한 잘림 없이 사고 모델이 완전한 응답을 생성할 수 있습니다.
Gemini 환경 변수¶
| 변수 | 설명 |
|---|---|
CONTINUUM_GEMINI_API_KEY | Google Gemini API 키 (type: gemini 백엔드용으로 자동 로드) |
GOOGLE_APPLICATION_CREDENTIALS | 서비스 계정 JSON 키 파일 경로 (표준 GCP 환경 변수) |
네이티브 Anthropic 백엔드¶
type: anthropic을 사용하면 라우터는 다음을 제공합니다:
- 기본 URL:
https://api.anthropic.com(프록시용으로 오버라이드 가능) - 네이티브 API 변환: OpenAI 형식 요청을 Anthropic Messages API 형식으로 자동 변환 및 역변환
- Anthropic 전용 헤더:
x-api-key및anthropic-version헤더 자동 추가 - 환경 변수 지원:
CONTINUUM_ANTHROPIC_API_KEY에서 자동 로드 - 확장 스트리밍 타임아웃: 확장 사고 모델용 600초 타임아웃 (Claude Opus, Sonnet 4)
최소 Anthropic 설정:
backends:
- name: "anthropic"
type: anthropic
models:
- claude-sonnet-4-20250514
- claude-haiku-3-5-20241022
전체 Anthropic 설정:
backends:
- name: "anthropic"
type: anthropic
api_key: "${CONTINUUM_ANTHROPIC_API_KEY}"
weight: 2
models:
- claude-opus-4-5-20250514
- claude-sonnet-4-20250514
- claude-haiku-3-5-20241022
Anthropic API 변환¶
라우터는 OpenAI와 Anthropic API 형식 간의 변환을 자동으로 처리합니다:
| OpenAI 형식 | Anthropic 형식 |
|---|---|
role: "system"이 있는 messages 배열 | 별도의 system 매개변수 |
Authorization: Bearer <key> | x-api-key: <key> 헤더 |
선택적 max_tokens | 필수 max_tokens (누락 시 자동 채움) |
choices[0].message.content | content[0].text |
finish_reason: "stop" | stop_reason: "end_turn" |
usage.prompt_tokens | usage.input_tokens |
요청 변환 예제:
OpenAI 형식 (클라이언트에서 들어오는):
{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "system", "content": "당신은 도움이 됩니다."},
{"role": "user", "content": "안녕하세요"}
],
"max_tokens": 1024
}
Anthropic 형식 (API로 전송되는):
{
"model": "claude-sonnet-4-20250514",
"system": "당신은 도움이 됩니다.",
"messages": [
{"role": "user", "content": "안녕하세요"}
],
"max_tokens": 1024
}
Anthropic 네이티브 API 엔드포인트¶
OpenAI 형식 요청을 Anthropic 백엔드로 라우팅하는 것 외에도, 라우터는 네이티브 Anthropic API 엔드포인트도 제공합니다:
| 엔드포인트 | 설명 |
|---|---|
POST /anthropic/v1/messages | 네이티브 Anthropic Messages API |
POST /anthropic/v1/messages/count_tokens | 계층적 백엔드 지원을 통한 토큰 카운팅 |
GET /anthropic/v1/models | Anthropic 형식의 모델 목록 |
이 엔드포인트를 통해 Anthropic의 네이티브 API 형식을 사용하는 클라이언트(예: Claude Code)가 요청/응답 변환 오버헤드 없이 직접 연결할 수 있습니다.
Claude Code 호환성¶
Anthropic 네이티브 API 엔드포인트는 Claude Code 및 기타 고급 Anthropic API 클라이언트와의 완전한 호환성을 포함합니다:
프롬프트 캐싱 지원:
라우터는 요청/응답 파이프라인 전체에서 cache_control 필드를 유지합니다:
- 시스템 프롬프트 텍스트 블록
- 사용자 메시지 콘텐츠 블록 (텍스트, 이미지, 문서)
- 도구 정의
- 도구 사용 및 도구 결과 블록
헤더 전달:
| 헤더 | 동작 |
|---|---|
anthropic-version | 네이티브 Anthropic 백엔드로 전달됨 |
anthropic-beta | 베타 기능 활성화를 위해 전달됨 (예: prompt-caching-2024-07-31, interleaved-thinking-2025-05-14) |
x-request-id | 요청 추적을 위해 전달됨 |
캐시 사용량 보고:
네이티브 Anthropic 백엔드의 스트리밍 응답에는 캐시 사용량 정보가 포함됩니다:
{
"usage": {
"input_tokens": 2159,
"cache_creation_input_tokens": 2048,
"cache_read_input_tokens": 0
}
}
Anthropic 확장 사고 모델¶
확장 사고를 지원하는 모델 (Claude Opus, Sonnet 4)은 더 긴 응답 시간이 필요할 수 있습니다. 라우터는 자동으로: - 사고 모델에 대해 더 높은 기본 max_tokens (16384) 설정 - 이 모델에 대해 확장 스트리밍 타임아웃 (600초) 사용
OpenAI ↔ Claude 추론 매개변수 변환¶
라우터는 OpenAI의 추론 매개변수와 Claude의 thinking 매개변수 간을 자동으로 변환하여 원활한 크로스 프로바이더 추론 요청을 가능하게 합니다.
지원되는 OpenAI 형식:
| 형식 | API | 예제 |
|---|---|---|
reasoning_effort (플랫) | Chat Completions API | "reasoning_effort": "high" |
reasoning.effort (중첩) | Responses API | "reasoning": {"effort": "high"} |
두 형식이 모두 있으면 reasoning_effort (플랫)가 우선합니다.
Effort 레벨에서 Budget 토큰 매핑:
| Effort 레벨 | Claude thinking.budget_tokens |
|---|---|
none | (사고 비활성화) |
minimal | 1,024 |
low | 4,096 |
medium | 10,240 |
high | 32,768 |
요청 예제 - Chat Completions API (플랫 형식):
// 클라이언트가 OpenAI Chat Completions API 요청을 보냄
{
"model": "claude-sonnet-4-5-20250929",
"reasoning_effort": "high",
"messages": [{"role": "user", "content": "이 복잡한 문제를 풀어주세요"}]
}
// 라우터가 Claude 형식으로 변환
{
"model": "claude-sonnet-4-5-20250929",
"thinking": {"type": "enabled", "budget_tokens": 32768},
"messages": [{"role": "user", "content": "이 복잡한 문제를 풀어주세요"}]
}
요청 예제 - Responses API (중첩 형식):
// 클라이언트가 OpenAI Responses API 요청을 보냄
{
"model": "claude-sonnet-4-5-20250929",
"reasoning": {"effort": "medium"},
"messages": [{"role": "user", "content": "이 데이터를 분석해주세요"}]
}
// 라우터가 Claude 형식으로 변환
{
"model": "claude-sonnet-4-5-20250929",
"thinking": {"type": "enabled", "budget_tokens": 10240},
"messages": [{"role": "user", "content": "이 데이터를 분석해주세요"}]
}
추론 콘텐츠가 있는 응답:
{
"choices": [{
"message": {
"role": "assistant",
"content": "최종 답변은...",
"reasoning_content": "단계별로 분석해 보겠습니다..."
}
}]
}
중요 사항: - thinking 매개변수가 명시적으로 제공되면 reasoning_effort 및 reasoning.effort보다 우선합니다 - reasoning_effort (플랫)가 두 형식이 모두 있을 때 reasoning.effort (중첩)보다 우선합니다 - 확장 사고를 지원하는 모델 (Opus 4.x, Sonnet 4.x)만 추론이 활성화됩니다 - 추론이 활성화되면 temperature 매개변수가 자동으로 제거됩니다 (Claude API 요구 사항) - 스트리밍 응답의 경우 사고 콘텐츠가 reasoning_content delta 이벤트로 반환됩니다
Anthropic 환경 변수¶
| 변수 | 설명 |
|---|---|
CONTINUUM_ANTHROPIC_API_KEY | Anthropic API 키 (type: anthropic 백엔드용으로 자동 로드) |
네이티브 llama.cpp 백엔드¶
type: llamacpp를 사용하면 라우터는 llama.cpp llama-server에 대한 네이티브 지원을 제공합니다:
- 기본 URL:
http://localhost:8080(llama-server 기본 포트) - 헬스 체크:
/health엔드포인트 사용 (/v1/models로 폴백) - 모델 검색: llama-server의 하이브리드
/v1/models응답 형식 파싱 - 풍부한 메타데이터: 응답에서 컨텍스트 윈도우, 파라미터 수, 모델 크기 추출
최소 llama.cpp 설정:
backends:
- name: "local-llama"
type: llamacpp
# 기본 http://localhost:8080 사용 시 URL 불필요
# 로컬 서버에는 API 키 불필요
전체 llama.cpp 설정:
backends:
- name: "local-llama"
type: llamacpp
url: "http://192.168.1.100:8080" # 필요시 커스텀 URL
weight: 2
# 모델은 /v1/models 엔드포인트에서 자동 검색
llama.cpp 기능¶
| 기능 | 설명 |
|---|---|
| GGUF 모델 | GGUF 양자화 모델 네이티브 지원 |
| 로컬 추론 | 클라우드 API 의존성 없음 |
| 하드웨어 지원 | CPU, NVIDIA, AMD, Apple Silicon |
| 스트리밍 | 완전한 SSE 스트리밍 지원 |
| 임베딩 | /v1/embeddings 엔드포인트 지원 |
| 도구 호출 감지 | /props 엔드포인트를 통한 도구 호출 지원 자동 감지 |
도구 호출 자동 감지¶
라우터는 모델 검색 중 /props 엔드포인트를 쿼리하여 llama.cpp 백엔드의 도구 호출 기능을 자동으로 감지합니다. 이를 통해 수동 설정 없이 자동으로 함수 호출 지원이 가능해집니다.
작동 방식:
- llama.cpp 백엔드가 검색되면 라우터가
/props엔드포인트를 조회합니다 chat_template필드를 정밀한 Jinja2 패턴 매칭으로 분석하여 도구 관련 구문을 감지합니다- 도구 호출 패턴이 감지되면 모델의
function_calling기능이 자동으로 활성화됩니다 - 감지 결과가 참조용으로 저장됩니다 (채팅 템플릿의 해시 포함)
감지 패턴:
라우터는 오탐을 줄이기 위해 정밀한 패턴 매칭을 사용합니다:
- 역할 기반 패턴:
message['role'] == 'tool',message.role == "tool" - 도구 반복:
for tool in tools,for function in functions - 도구 호출 접근:
.tool_calls,['tool_calls'],message.tool_call - 도구 키워드가 포함된 Jinja2 블록:
{% if tools %},{% for tool_call in ... %}
분석되는 /props 응답 예제:
{
"chat_template": "{% for message in messages %}{% if message['role'] == 'tool' %}...",
"default_generation_settings": { ... },
"total_slots": 1
}
폴백 동작:
/props를 사용할 수 없는 경우: 도구 호출이 지원되는 것으로 가정 (최신 llama.cpp 버전에 대한 낙관적 폴백)/props가 오류를 반환하는 경우: 도구 호출이 지원되는 것으로 가정 (새로운 모델과의 호환성 보장)- 채팅 템플릿이 64KB를 초과하는 경우: 감지가 건너뛰어지고 지원되는 것으로 기본 설정
- 최대 호환성을 위해 대소문자 구분 없음
- 결과는
model-metadata.yaml의 기존 모델 메타데이터와 병합됩니다 - 감지된 기능은
/v1/models/{model_id}응답의features필드에 나타납니다
모델 메타데이터 추출¶
라우터는 llama-server 응답에서 풍부한 메타데이터를 추출합니다:
| 필드 | 소스 | 설명 |
|---|---|---|
| 컨텍스트 윈도우 | meta.n_ctx_train | 학습 컨텍스트 윈도우 크기 |
| 파라미터 수 | meta.n_params | 모델 파라미터 (예: "4B") |
| 모델 크기 | meta.size | 파일 크기 (바이트) |
| 기능 | models[].capabilities | 모델 기능 배열 |
llama-server 시작하기¶
# 기본 시작
./llama-server -m model.gguf --port 8080
# GPU 레이어 사용
./llama-server -m model.gguf --port 8080 -ngl 35
# 커스텀 컨텍스트 크기 사용
./llama-server -m model.gguf --port 8080 --ctx-size 8192
llama.cpp 백엔드 자동 감지¶
타입이 지정되지 않은 백엔드가 추가되면 (기본값 generic), 라우터는 자동으로 /v1/models 엔드포인트를 탐색하여 백엔드 타입을 감지합니다. llama.cpp 백엔드는 다음으로 식별됩니다:
- 응답의
owned_by: "llamacpp" - llama.cpp 전용 메타데이터 필드 존재 (
n_ctx_train,n_params,vocab_type) models[]와data[]배열이 모두 있는 하이브리드 응답 형식
이 자동 감지는 다음에 적용됩니다:
- 핫 리로드 설정 변경
- 명시적 타입 없이 Admin API를 통해 추가된 백엔드
type: generic또는 타입이 지정되지 않은 설정 파일
예제: Admin API를 통한 자동 감지 백엔드:
# 타입 지정 없이 백엔드 추가 - llama.cpp 자동 감지
curl -X POST http://localhost:8080/admin/backends \
-H "Content-Type: application/json" \
-d '{
"name": "local-llm",
"url": "http://localhost:8080"
}'
Unix Domain Socket 백엔드¶
Continuum Router는 로컬 LLM 백엔드를 위해 TCP 대신 Unix Domain Socket(UDS)을 전송 수단으로 지원합니다. Unix 소켓은 다음을 제공합니다:
- 향상된 보안: TCP 포트 노출 없음 - 파일 시스템을 통한 통신
- 낮은 레이턴시: 로컬 통신에서 네트워크 스택 오버헤드 없음
- 더 나은 성능: 컨텍스트 스위칭 및 메모리 복사 감소
- 간단한 접근 제어: 표준 Unix 파일 권한 사용
URL 형식:
플랫폼 지원:
| 플랫폼 | 지원 |
|---|---|
| Linux | AF_UNIX를 통한 완전 지원 |
| macOS | AF_UNIX를 통한 완전 지원 |
| Windows | 현재 미지원 (향후 릴리스에서 지원 예정) |
설정 예제:
backends:
# Unix 소켓을 사용하는 llama-server
- name: "llama-socket"
type: llamacpp
url: "unix:///var/run/llama-server.sock"
weight: 2
models:
- llama-3.2-3b
- qwen3-4b
# Unix 소켓을 사용하는 Ollama
- name: "ollama-socket"
type: ollama
url: "unix:///var/run/ollama.sock"
weight: 1
models:
- llama3.2
- mistral
# Unix 소켓을 사용하는 vLLM
- name: "vllm-socket"
type: vllm
url: "unix:///tmp/vllm.sock"
weight: 3
models:
- meta-llama/Llama-3.1-8B-Instruct
Unix 소켓으로 백엔드 시작하기:
# llama-server
./llama-server -m model.gguf --unix /var/run/llama.sock
# Ollama
OLLAMA_HOST="unix:///var/run/ollama.sock" ollama serve
# vLLM
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B \
--unix-socket /tmp/vllm.sock
소켓 경로 규칙:
| 경로 | 사용 사례 |
|---|---|
/var/run/*.sock | 시스템 서비스 (root 권한 필요) |
/tmp/*.sock | 임시, 사용자 접근 가능 |
~/.local/share/continuum/*.sock | 사용자별 영구 소켓 |
헬스 체크: 라우터는 TCP 백엔드와 동일한 엔드포인트(/health, /v1/models)를 사용하여 Unix 소켓 백엔드에 대해 자동으로 헬스 체크를 수행합니다.
현재 제한 사항:
- 스트리밍(SSE) 미지원: Unix 소켓 백엔드는 현재 Server-Sent Events(SSE) 스트리밍을 지원하지 않습니다. 스트리밍 채팅 완성에는 TCP 백엔드를 사용하세요.
- Windows 플랫폼: Unix 소켓은 현재 Windows에서 지원되지 않습니다 (향후 릴리스에서 지원 예정).
- 최대 응답 크기: 메모리 고갈을 방지하기 위해 응답 본문은 기본적으로 100MB로 제한됩니다.
문제 해결:
| 오류 | 원인 | 해결 방법 |
|---|---|---|
| "Socket file not found" | 서버가 실행되지 않음 | 백엔드 서버 시작 |
| "Permission denied" | 파일 권한 | chmod 660 socket.sock |
| "Connection timeout" | 서버가 연결을 수락하지 않음 | 서버가 리스닝 중인지 확인 |
| "Response body exceeds maximum size" | 응답이 너무 큼 | maxresponsesize 증가 또는 TCP 백엔드로 스트리밍 사용 |
헬스 체크 섹션¶
백엔드 헬스 모니터링을 설정합니다:
health_checks:
enabled: true # 헬스 모니터링 활성화/비활성화
interval: "30s" # 확인 주기
timeout: "10s" # 요청 타임아웃
unhealthy_threshold: 3 # 비정상 표시까지의 실패 횟수
healthy_threshold: 2 # 정상 표시까지의 성공 횟수
endpoint: "/v1/models" # 확인할 엔드포인트
warmup_check_interval: "1s" # 워밍업 중 가속화된 확인 주기
max_warmup_duration: "300s" # 최대 워밍업 감지 시간
헬스 체크 프로세스:
- 라우터가 각 백엔드의 헬스 엔드포인트를 쿼리
- 성공적인 응답은 성공 카운터 증가
- 실패한 응답은 실패 카운터 증가
- 실패 임계값에 도달하면 백엔드를 비정상으로 표시
- 성공 임계값에 도달하면 백엔드를 정상으로 표시
- 정상 백엔드만 트래픽 수신
가속화된 워밍업 헬스 체크¶
라우터는 백엔드 워밍업 중 가속화된 헬스 체크를 지원합니다. 이는 모델 로딩 중 HTTP 503을 반환하는 llama.cpp와 같은 백엔드에 특히 유용합니다.
백엔드 상태:
| 상태 | HTTP 응답 | 동작 |
|---|---|---|
ready | 200 OK | 일반 간격으로 체크 |
warming_up | 503 Service Unavailable | 가속화된 간격으로 체크 |
down | 연결 실패 | 일반 간격으로 체크 |
unknown | 초기 상태 | 첫 번째 체크로 상태 결정 |
워밍업 설정:
| 옵션 | 기본값 | 설명 |
|---|---|---|
warmup_check_interval | 1s | 워밍업 중 가속화된 체크 간격 |
max_warmup_duration | 300s | 가속화 모드 유지 최대 시간 |
동작 방식:
- 백엔드가 HTTP 503을 반환하면
warming_up상태로 진입 - 헬스 체크가 가속화된 간격(기본값: 1초)으로 전환
- 백엔드가 HTTP 200을 반환하면
ready상태가 되어 일반 간격으로 복귀 - 워밍업이
max_warmup_duration을 초과하면 백엔드를 비정상으로 표시
이 기능으로 모델 가용성 감지 지연 시간이 최대 30초(최악의 경우)에서 약 1초로 단축됩니다.
백엔드별 헬스 체크 설정¶
각 백엔드 타입에는 합리적인 기본 헬스 체크 엔드포인트가 있습니다. 백엔드별로 커스텀 health_check 설정으로 이러한 기본값을 재정의할 수 있습니다.
백엔드 타입별 기본 헬스 체크 엔드포인트:
| 백엔드 타입 | 기본 엔드포인트 | 폴백 엔드포인트 | 메서드 | 참고 |
|---|---|---|---|---|
openai | /v1/models | - | GET | 표준 OpenAI 엔드포인트 |
vllm | /health | /v1/models | GET | /health는 모델 로드 후 사용 가능 |
ollama | /api/tags | / | GET | Ollama 전용 엔드포인트 |
llamacpp | /health | /v1/models | GET | llama-server 엔드포인트 |
anthropic | /v1/messages | - | POST | 200, 400, 401, 429를 정상으로 허용 |
gemini | /models | /v1beta/models | GET | 네이티브 Gemini 엔드포인트 |
azure | /health | /v1/models | GET | Azure OpenAI 엔드포인트 |
generic | /health | /v1/models | GET | 범용 폴백 |
폴백 동작:
기본 헬스 체크 엔드포인트가 HTTP 404를 반환하면 라우터는 자동으로 폴백 엔드포인트를 순서대로 시도합니다. 이를 통해 모든 표준 엔드포인트를 구현하지 않는 백엔드와의 호환성을 보장합니다.
커스텀 헬스 체크 설정:
backends:
- name: vllm-custom
type: vllm
url: http://localhost:8000
models:
- my-model
health_check:
endpoint: /custom-health # 기본 엔드포인트
fallback_endpoints: # 기본 엔드포인트가 404 반환 시 시도
- /health
- /v1/models
method: GET # HTTP 메서드: GET, POST 또는 HEAD
timeout: 10s # 전역 헬스 체크 타임아웃 재정의
accept_status: # 정상을 나타내는 상태 코드
- 200
- 204
warmup_status: # 모델 로딩 중을 나타내는 상태 코드
- 503
헬스 체크 설정 옵션:
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
endpoint | string | 백엔드 타입별 상이 | 기본 헬스 체크 엔드포인트 경로 |
fallback_endpoints | array | 백엔드 타입별 상이 | 기본 엔드포인트가 404 반환 시 시도할 엔드포인트 |
method | string | GET | HTTP 메서드: GET, POST 또는 HEAD |
body | object | null | POST 요청용 JSON 본문 |
accept_status | array | [200] | 백엔드가 정상임을 나타내는 상태 코드 |
warmup_status | array | [503] | 백엔드가 워밍업 중임을 나타내는 상태 코드 |
timeout | string | 전역 타임아웃 | 전역 헬스 체크 타임아웃 재정의 |
예시: Anthropic 스타일 헬스 체크:
POST 요청을 사용하거나 오류 코드를 정상 지표로 허용하는 백엔드의 경우:
backends:
- name: custom-api
type: generic
url: http://localhost:9000
models:
- custom-model
health_check:
endpoint: /api/v1/health
method: POST
body:
check: true
accept_status:
- 200
- 400 # Bad request는 서버가 동작 중임을 의미
- 401 # Unauthorized는 서버가 동작 중임을 의미
- 429 # Rate limited는 서버가 동작 중임을 의미
요청 섹션¶
요청 처리 동작을 제어합니다:
request:
timeout: "300s" # 최대 요청 지속 시간
max_retries: 3 # 실패한 요청의 재시도 횟수
retry_delay: "1s" # 재시도 간 초기 지연
타임아웃 고려 사항:
- 긴 타임아웃 (300s)은 느린 모델 추론 수용
- 스트리밍 요청은 비스트리밍보다 오래 걸릴 수 있음
- 사용자 경험과 리소스 사용 간 균형
재시도 섹션¶
복원력을 위한 전역 재시도 설정:
retry:
max_attempts: 3 # 최대 재시도 횟수
base_delay: "100ms" # 재시도 간 기본 지연
max_delay: "30s" # 재시도 지연 상한
exponential_backoff: true # 지수 백오프 사용
jitter: true # 무작위 지터 추가
재시도 전략:
- 지수 백오프: 지연이 지수적으로 증가 (100ms, 200ms, 400ms...)
- 지터: 썬더링 허드 방지를 위한 무작위성 추가
- 최대 지연: 극도로 긴 대기 방지
캐시 섹션¶
캐싱 및 최적화를 제어합니다:
cache:
model_cache_ttl: "300s" # 모델 목록 캐시 기간
deduplication_ttl: "60s" # 동일 요청 캐시 기간
enable_deduplication: true # 요청 중복 제거 활성화
캐시 스탬피드 방지¶
라우터는 캐시 스탬피드(썬더링 허드 문제)를 방지하기 위해 세 가지 전략을 구현합니다:
- 싱글플라이트 패턴: 한 번에 하나의 집계 요청만 실행
- Stale-While-Revalidate: 백그라운드에서 갱신하면서 오래된 데이터 반환
- 백그라운드 갱신: 만료 전 사전 캐시 업데이트
고급 캐시 설정:
model_aggregation:
cache_ttl: 60 # 캐시 TTL (초, 기본값: 60)
soft_ttl_ratio: 0.8 # 백그라운드 갱신 트리거 시점 (기본값: 0.8 = 80%)
empty_response_base_ttl_seconds: 5 # 빈 응답의 기본 TTL
empty_response_max_ttl_seconds: 60 # 지수 백오프 최대 TTL
max_cache_entries: 100 # 최대 캐시 항목 수
background_refresh:
enabled: true # 백그라운드 갱신 활성화
check_interval: 10s # 검사 간격
| 옵션 | 기본값 | 설명 |
|---|---|---|
cache_ttl | 60초 | 하드 TTL - 이 시간 후 캐시 만료 |
soft_ttl_ratio | 0.8 | 소프트 TTL = cachettl * softttl_ratio. 소프트와 하드 TTL 사이에서 캐시가 오래되었지만 사용 가능 |
empty_response_base_ttl_seconds | 5 | 빈 응답의 기본 TTL (DoS 방지) |
empty_response_max_ttl_seconds | 60 | 지수 백오프 최대 TTL (base * 2^n) |
max_cache_entries | 100 | 최대 캐시 항목 수 |
background_refresh.enabled | true | 사전 캐시 갱신 활성화 |
background_refresh.check_interval | 10초 | 캐시 신선도 검사 주기 |
캐시 이점:
- 모델 캐싱은 백엔드 쿼리 감소
- 중복 제거는 중복 처리 방지
- TTL은 오래된 데이터 문제 방지
- 스탬피드 방지로 썬더링 허드 회피
- 백그라운드 갱신으로 캐시가 항상 신선하게 유지
로깅 섹션¶
로깅 출력을 설정합니다:
logging:
level: "info" # trace, debug, info, warn, error
format: "json" # json, pretty
enable_colors: false # 컬러 출력 (pretty 형식만)
로그 레벨:
trace: 매우 상세, 모든 세부 정보 포함debug: 상세한 디버깅 정보info: 일반적인 운영 정보warn: 경고 메시지 및 잠재적 문제error: 오류 조건만
로그 형식:
json: 구조화된 JSON 로깅 (프로덕션 권장)pretty: 사람이 읽기 쉬운 형식 (개발에 적합)
API 키 섹션¶
API 키는 라우터 엔드포인트에 대한 클라이언트 접근을 제어합니다. 키는 여러 소스를 통해 설정할 수 있습니다.
인증 모드¶
mode 설정은 API 엔드포인트에 API 인증이 필요한지 제어합니다:
| 모드 | 동작 |
|---|---|
permissive (기본) | API 키 없이 요청 허용. 유효한 API 키가 있는 요청은 인증됨. |
blocking | API 키 인증을 통과한 요청만 처리. 인증되지 않은 요청은 401 수신. |
대상 엔드포인트 (mode가 blocking일 때):
/v1/chat/completions/v1/completions/v1/responses/v1/images/generations/v1/images/edits/v1/images/variations/v1/models
참고: Admin, Files, Metrics 엔드포인트는 별도의 인증 메커니즘이 있으며 이 설정의 영향을 받지 않습니다.
섹션 설정 속성:
| 속성 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
mode | string | 아니오 | permissive | 인증 모드: permissive 또는 blocking |
api_keys | array | 아니오 | [] | 인라인 API 키 정의 |
api_keys_file | string | 아니오 | - | 외부 API 키 파일 경로 |
api_keys:
# 인증 모드: "permissive" (기본) 또는 "blocking"
mode: permissive
# 인라인 API 키 정의
api_keys:
- key: "${API_KEY_1}" # 환경 변수 치환
id: "key-production-1" # 고유 식별자
user_id: "user-admin" # 연결된 사용자
organization_id: "org-main" # 연결된 조직
name: "Production Admin Key" # 사람이 읽을 수 있는 이름
scopes: # 권한
- read
- write
- files
- admin
rate_limit: 1000 # 분당 요청 수 (선택 사항)
enabled: true # 활성 상태
expires_at: "2025-12-31T23:59:59Z" # 선택적 만료 (ISO 8601)
- key: "${API_KEY_2}"
id: "key-service-1"
user_id: "service-bot"
organization_id: "org-main"
name: "Service Account"
scopes: [read, write, files]
rate_limit: 500
enabled: true
# 보안 향상을 위한 외부 키 파일
api_keys_file: "/etc/continuum-router/api-keys.yaml"
키 속성:
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
key | string | 예 | API 키 값 (${ENV_VAR} 치환 지원) |
id | string | 예 | admin 작업을 위한 고유 식별자 |
user_id | string | 예 | 이 키와 연결된 사용자 |
organization_id | string | 예 | 사용자가 속한 조직 |
name | string | 아니오 | 사람이 읽을 수 있는 이름 |
description | string | 아니오 | 키에 대한 메모 |
scopes | array | 예 | 권한: read, write, files, admin |
rate_limit | integer | 아니오 | 분당 최대 요청 수 |
enabled | boolean | 아니오 | 활성 상태 (기본값: true) |
expires_at | string | 아니오 | ISO 8601 만료 타임스탬프 |
외부 키 파일 형식:
# /etc/continuum-router/api-keys.yaml
keys:
- key: "sk-prod-xxxxxxxxxxxxxxxxxxxxx"
id: "key-external-1"
user_id: "external-user"
organization_id: "external-org"
scopes: [read, write, files]
enabled: true
보안 기능:
- 키 마스킹: 전체 키는 로깅되지 않음 (
sk-***last4로 표시) - 만료 적용: 만료된 키는 자동으로 거부
- 핫 리로드: 서버 재시작 없이 키 업데이트
- 감사 로깅: 모든 키 관리 작업 로깅
- 상수 시간 검증: 타이밍 공격 방지
- 최대 키 제한: DoS 방지를 위해 최대 10,000개 키
Admin API 엔드포인트 (admin 인증 필요):
| 엔드포인트 | 메서드 | 설명 |
|---|---|---|
/admin/api-keys | GET | 모든 키 목록 (마스킹됨) |
/admin/api-keys/:id | GET | 키 세부 정보 가져오기 |
/admin/api-keys | POST | 새 키 생성 |
/admin/api-keys/:id | PUT | 키 속성 업데이트 |
/admin/api-keys/:id | DELETE | 키 삭제 |
/admin/api-keys/:id/rotate | POST | 새 키 값 생성 |
/admin/api-keys/:id/enable | POST | 키 활성화 |
/admin/api-keys/:id/disable | POST | 키 비활성화 |
고급 설정¶
전역 프롬프트¶
전역 프롬프트를 사용하면 모든 요청에 시스템 프롬프트를 주입하여 보안, 규정 준수 및 동작 가이드라인에 대한 중앙 집중식 정책 관리를 제공할 수 있습니다. 프롬프트는 인라인으로 정의하거나 외부 Markdown 파일에서 로드할 수 있습니다.
기본 설정¶
global_prompts:
# 인라인 기본 프롬프트
default: |
회사 보안 정책을 따라야 합니다.
내부 시스템 세부 정보를 공개하지 마십시오.
도움이 되고 전문적이어야 합니다.
# 병합 전략: prepend (기본), append, 또는 replace
merge_strategy: prepend
# 전역 프롬프트와 사용자 프롬프트 사이의 사용자 정의 구분자
separator: "\n\n---\n\n"
외부 프롬프트 파일¶
복잡한 프롬프트의 경우 외부 Markdown 파일에서 콘텐츠를 로드할 수 있습니다. 이를 통해:
- 구문 강조가 있는 더 나은 편집 경험
- 설정 파일 노이즈 없는 버전 관리
- 프롬프트 업데이트에 대한 핫 리로드 지원
global_prompts:
# 프롬프트 파일이 있는 디렉토리 (설정 디렉토리 기준 상대 경로)
prompts_dir: "./prompts"
# 파일에서 기본 프롬프트 로드
default_file: "system.md"
# 파일에서 백엔드별 프롬프트
backends:
anthropic:
prompt_file: "anthropic-system.md"
openai:
prompt_file: "openai-system.md"
# 파일에서 모델별 프롬프트
models:
gpt-4o:
prompt_file: "gpt4o-system.md"
claude-3-opus:
prompt_file: "claude-opus-system.md"
merge_strategy: prepend
프롬프트 해석 우선순위¶
요청에 사용할 프롬프트 결정 시:
- 모델별 프롬프트 (최고 우선순위) -
global_prompts.models.<model-id> - 백엔드별 프롬프트 -
global_prompts.backends.<backend-name> - 기본 프롬프트 -
global_prompts.default또는global_prompts.default_file
각 레벨에서 prompt (인라인)와 prompt_file이 모두 지정되면 prompt_file이 우선합니다.
병합 전략¶
| 전략 | 동작 |
|---|---|
prepend | 전역 프롬프트가 사용자 시스템 프롬프트 앞에 추가 (기본) |
append | 전역 프롬프트가 사용자 시스템 프롬프트 뒤에 추가 |
replace | 전역 프롬프트가 사용자 시스템 프롬프트를 완전히 대체 |
REST API 관리¶
프롬프트 파일은 Admin API를 통해 런타임에 관리할 수 있습니다:
# 모든 프롬프트 목록
curl http://localhost:8080/admin/config/prompts
# 특정 프롬프트 파일 가져오기
curl http://localhost:8080/admin/config/prompts/prompts/system.md
# 프롬프트 파일 업데이트
curl -X PUT http://localhost:8080/admin/config/prompts/prompts/system.md \
-H "Content-Type: application/json" \
-d '{"content": "# 업데이트된 시스템 프롬프트\n\n새 콘텐츠."}'
# 디스크에서 모든 프롬프트 파일 리로드
curl -X POST http://localhost:8080/admin/config/prompts/reload
전체 API 문서는 Admin REST API 참조를 참조하세요.
보안 고려 사항¶
- 경로 탐색 보호: 디렉토리 탐색 공격 방지를 위한 모든 파일 경로 검증
- 파일 크기 제한: 개별 파일 1MB, 전체 캐시 50MB 제한
- 상대 경로만: 프롬프트 파일은 설정된
prompts_dir또는 설정 디렉토리 내에 있어야 함 - 샌드박스 접근: 허용된 디렉토리 외부 파일은 거부
핫 리로드¶
전역 프롬프트는 즉시 핫 리로드를 지원합니다. 프롬프트 설정 또는 파일 변경 사항은 서버 재시작 없이 다음 요청에 적용됩니다.
모델 메타데이터¶
Continuum Router는 모델 기능, 가격, 한도에 대한 상세 정보를 제공하는 풍부한 모델 메타데이터를 지원합니다. 이 메타데이터는 /v1/models API 응답에 반환되며 클라이언트가 정보에 입각한 모델 선택 결정을 내리는 데 사용할 수 있습니다.
메타데이터 소스¶
모델 메타데이터는 세 가지 방법으로 설정할 수 있습니다 (우선순위 순):
- 백엔드별 model_configs (최고 우선순위)
- 외부 메타데이터 파일 (model-metadata.yaml)
- 메타데이터 없음 (모델은 메타데이터 없이도 작동)
외부 메타데이터 파일¶
model-metadata.yaml 파일을 만드세요:
models:
- id: "gpt-4"
aliases: # 이 메타데이터를 공유하는 대체 ID
- "gpt-4-0125-preview"
- "gpt-4-turbo-preview"
- "gpt-4-vision-preview"
metadata:
display_name: "GPT-4"
summary: "복잡한 작업을 위한 가장 유능한 GPT-4 모델"
capabilities: ["text", "image", "function_calling"]
knowledge_cutoff: "2024-04"
pricing:
input_tokens: 0.03 # 1000 토큰당
output_tokens: 0.06 # 1000 토큰당
limits:
context_window: 128000
max_output: 4096
- id: "llama-3-70b"
aliases: # 동일 모델의 다른 양자화
- "llama-3-70b-instruct"
- "llama-3-70b-chat"
- "llama-3-70b-q4"
- "llama-3-70b-q8"
metadata:
display_name: "Llama 3 70B"
summary: "강력한 성능의 오픈 소스 모델"
capabilities: ["text", "code"]
knowledge_cutoff: "2023-12"
pricing:
input_tokens: 0.001
output_tokens: 0.002
limits:
context_window: 8192
max_output: 2048
설정에서 참조하세요:
Thinking 패턴 설정¶
일부 모델은 추론/사고 콘텐츠를 비표준 방식으로 출력합니다. 라우터는 스트리밍 응답을 적절히 변환하기 위해 모델별 thinking 패턴 설정을 지원합니다.
패턴 유형:
| 패턴 | 설명 | 예시 모델 |
|---|---|---|
none | thinking 패턴 없음 (기본값) | 대부분의 모델 |
standard | 명시적 시작/종료 태그 (<think>...</think>) | 커스텀 추론 모델 |
unterminated_start | 시작 태그 없이 종료 태그만 있음 | nemotron-3-nano |
설정 예시:
models:
- id: nemotron-3-nano
metadata:
display_name: "Nemotron 3 Nano"
capabilities: ["chat", "reasoning"]
# Thinking 패턴 설정
thinking:
pattern: unterminated_start
end_marker: "</think>"
assume_reasoning_first: true
Thinking 패턴 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
pattern | string | 패턴 유형: none, standard, 또는 unterminated_start |
start_marker | string | standard 패턴용 시작 마커 (예: <think>) |
end_marker | string | 종료 마커 (예: </think>) |
assume_reasoning_first | boolean | true인 경우, 종료 마커까지 첫 토큰들을 추론으로 처리 |
작동 방식:
모델에 thinking 패턴이 설정되면:
- 스트리밍 응답이 가로채져 변환됨
end_marker이전 콘텐츠는reasoning_content필드로 전송end_marker이후 콘텐츠는content필드로 전송- 출력은 호환성을 위해 OpenAI의
reasoning_content형식을 따름
출력 예시:
// 추론 콘텐츠 (종료 마커 이전)
{"choices": [{"delta": {"reasoning_content": "분석해 보겠습니다..."}}]}
// 일반 콘텐츠 (종료 마커 이후)
{"choices": [{"delta": {"content": "답은 42입니다."}}]}
네임스페이스 인식 매칭¶
라우터는 네임스페이스 접두사가 있는 모델 ID를 지능적으로 처리합니다. 예:
- 백엔드 반환:
"custom/gpt-4","openai/gpt-4","optimized/gpt-4" - 메타데이터 정의:
"gpt-4" - 결과: 모든 변형이 일치하고 동일한 메타데이터 수신
이를 통해 다른 백엔드가 공통 메타데이터 정의를 공유하면서 자체 명명 규칙을 사용할 수 있습니다.
메타데이터 우선순위 및 별칭 해석¶
모델의 메타데이터를 조회할 때, 라우터는 다음 우선순위 체인을 사용합니다:
- 정확한 모델 ID 매칭
- 정확한 별칭 매칭
- 날짜 접미사 정규화 (자동, 설정 불필요)
- 와일드카드 패턴 별칭 매칭
- 기본 모델 이름 폴백 (네임스페이스 제거)
각 소스 (백엔드 설정, 메타데이터 파일, 내장) 내에서 동일한 우선순위가 적용됩니다:
-
백엔드별
model_configs(최고 우선순위) -
외부 메타데이터 파일 (두 번째 우선순위)
-
내장 메타데이터 (OpenAI 및 Gemini 백엔드용)
자동 날짜 접미사 처리¶
LLM 프로바이더는 날짜 접미사가 있는 모델 버전을 자주 릴리스합니다. 라우터는 설정 없이 자동으로 날짜 접미사를 감지하고 정규화합니다:
지원되는 날짜 패턴:
-YYYYMMDD(예:claude-opus-4-5-20251130)-YYYY-MM-DD(예:gpt-4o-2024-08-06)-YYMM(예:o1-mini-2409)@YYYYMMDD(예:model@20251130)
작동 방식:
요청: claude-opus-4-5-20251215
↓ (날짜 접미사 감지됨)
조회: claude-opus-4-5-20251101 (기존 메타데이터 항목)
↓ (기본 이름 일치)
결과: claude-opus-4-5-20251101 메타데이터 사용
이는 모델 패밀리당 메타데이터를 한 번만 설정하면 되고, 새로운 날짜 버전이 자동으로 메타데이터를 상속한다는 것을 의미합니다.
와일드카드 패턴 매칭¶
별칭은 * 문자를 사용한 glob 스타일 와일드카드 패턴을 지원합니다:
- 접두사 매칭:
claude-*가claude-opus,claude-sonnet등과 매칭 - 접미사 매칭:
*-preview가gpt-4o-preview,o1-preview등과 매칭 - 중위 매칭:
gpt-*-turbo가gpt-4-turbo,gpt-3.5-turbo등과 매칭
와일드카드 패턴이 있는 설정 예:
models:
- id: "claude-opus-4-5-20251101"
aliases:
- "claude-opus-4-5" # 기본 이름의 정확한 매칭
- "claude-opus-*" # 모든 claude-opus 변형에 대한 와일드카드
metadata:
display_name: "Claude Opus 4.5"
# 자동 매칭: claude-opus-4-5-20251130, claude-opus-test 등
- id: "gpt-4o"
aliases:
- "gpt-4o-*-preview" # 프리뷰 버전 매칭
- "*-4o-turbo" # 접미사 매칭
metadata:
display_name: "GPT-4o"
우선순위 참고: 정확한 별칭은 항상 와일드카드 패턴보다 먼저 매칭되어 둘 다 매칭될 수 있는 경우에도 예측 가능한 동작을 보장합니다.
모델 변형에 별칭 사용¶
별칭은 특히 다음에 유용합니다:
- 다른 양자화:
qwen3-32b-i1,qwen3-23b-i4→ 모두qwen3메타데이터 사용 - 버전 변형:
gpt-4-0125-preview,gpt-4-turbo→gpt-4메타데이터 공유 - 배포 변형:
llama-3-70b-instruct,llama-3-70b-chat→ 동일 기본 모델 - 날짜 버전:
claude-3-5-sonnet-20241022,claude-3-5-sonnet-20241201→ 메타데이터 공유 (날짜 접미사 처리로 자동)
별칭이 있는 설정 예:
model_configs:
- id: "qwen3"
aliases:
- "qwen3-32b-i1" # 1비트 양자화된 32B
- "qwen3-23b-i4" # 4비트 양자화된 23B
- "qwen3-16b-q8" # 8비트 양자화된 16B
- "qwen3-*" # 다른 모든 qwen3 변형에 대한 와일드카드
metadata:
display_name: "Qwen 3"
summary: "Alibaba의 Qwen 모델 계열"
# ... 나머지 메타데이터
API 응답¶
/v1/models 엔드포인트는 풍부한 모델 정보를 반환합니다:
{
"object": "list",
"data": [
{
"id": "gpt-4",
"object": "model",
"created": 1234567890,
"owned_by": "openai",
"backends": ["openai-proxy"],
"metadata": {
"display_name": "GPT-4",
"summary": "복잡한 작업을 위한 가장 유능한 GPT-4 모델",
"capabilities": ["text", "image", "function_calling"],
"knowledge_cutoff": "2024-04",
"pricing": {
"input_tokens": 0.03,
"output_tokens": 0.06
},
"limits": {
"context_window": 128000,
"max_output": 4096
}
}
}
]
}
핫 리로드¶
Continuum Router는 서버 재시작 없이 런타임 설정 업데이트를 위한 핫 리로드를 지원합니다. 설정 변경은 자동으로 감지되어 분류에 따라 적용됩니다.
설정 항목 분류¶
설정 항목은 핫 리로드 기능에 따라 세 가지 범주로 분류됩니다:
즉시 업데이트 (서비스 중단 없음)¶
이 설정들은 서비스 중단 없이 즉시 업데이트됩니다:
# 로깅 설정
logging:
level: "info" # ✅ 즉시: 로그 레벨 변경이 즉시 적용
format: "json" # ✅ 즉시: 로그 형식 변경이 즉시 적용
# 속도 제한 설정
rate_limiting:
enabled: true # ✅ 즉시: 속도 제한 활성화/비활성화
limits:
per_client:
requests_per_second: 10 # ✅ 즉시: 새 제한이 즉시 적용
burst_capacity: 20 # ✅ 즉시: 버스트 설정이 즉시 업데이트
# 서킷 브레이커 설정
circuit_breaker:
enabled: true # ✅ 즉시: 서킷 브레이커 활성화/비활성화
failure_threshold: 5 # ✅ 즉시: 임계값 업데이트가 즉시 적용
timeout_seconds: 60 # ✅ 즉시: 타임아웃 변경이 즉시
# 재시도 설정
retry:
max_attempts: 3 # ✅ 즉시: 재시도 정책이 즉시 업데이트
base_delay: "100ms" # ✅ 즉시: 백오프 설정이 즉시 적용
exponential_backoff: true # ✅ 즉시: 전략 변경이 즉시
# 전역 프롬프트
global_prompts:
default: "당신은 도움이 됩니다" # ✅ 즉시: 프롬프트 변경이 새 요청에 적용
default_file: "prompts/system.md" # ✅ 즉시: 파일 기반 프롬프트도 핫 리로드
점진적 업데이트 (기존 연결 유지)¶
이 설정들은 기존 연결을 유지하면서 새 연결에 적용됩니다:
# 백엔드 설정
backends:
- name: "ollama" # ✅ 점진적: 새 요청이 업데이트된 백엔드 풀 사용
url: "http://localhost:11434"
weight: 2 # ✅ 점진적: 새 요청에 로드 밸런싱 업데이트
models: ["llama3.2"] # ✅ 점진적: 모델 라우팅이 점진적으로 업데이트
# 헬스 체크 설정
health_checks:
interval: "30s" # ✅ 점진적: 다음 헬스 체크 주기가 새 간격 사용
timeout: "10s" # ✅ 점진적: 새 체크가 업데이트된 타임아웃 사용
unhealthy_threshold: 3 # ✅ 점진적: 임계값이 새 평가에 적용
healthy_threshold: 2 # ✅ 점진적: 복구 임계값이 점진적으로 업데이트
# 타임아웃 설정
timeouts:
connection: "10s" # ✅ 점진적: 새 요청이 업데이트된 타임아웃 사용
request:
standard:
first_byte: "30s" # ✅ 점진적: 새 요청에 적용
total: "180s" # ✅ 점진적: 새 요청이 새 타임아웃 사용
streaming:
chunk_interval: "30s" # ✅ 점진적: 새 스트림이 업데이트된 설정 사용
재시작 필요 (핫 리로드 불가)¶
이 설정들은 적용을 위해 서버 재시작이 필요합니다. 변경 사항은 경고로 기록됩니다:
server:
bind_address: "0.0.0.0:8080" # ❌ 재시작 필요: TCP/Unix 소켓 바인딩
# - "0.0.0.0:8080"
# - "unix:/var/run/router.sock"
socket_mode: 0o660 # ❌ 재시작 필요: 소켓 권한
workers: 4 # ❌ 재시작 필요: 워커 스레드 풀 크기
이 설정들이 변경되면 라우터는 다음과 같은 경고를 기록합니다:
핫 리로드 프로세스¶
- 파일 시스템 감시자 - 설정 파일 변경 자동 감지
- 설정 로딩 - 새 설정 로드 및 파싱
- 유효성 검사 - 스키마에 대해 새 설정 검증
- 변경 감지 - ConfigDiff 계산으로 변경 사항 식별
- 분류 - 변경 사항 분류 (즉시/점진적/재시작)
- 원자적 업데이트 - 유효한 설정이 원자적으로 적용
- 컴포넌트 전파 - 영향받는 컴포넌트에 업데이트 전파:
- HealthChecker가 체크 간격 및 임계값 업데이트
- RateLimitStore가 속도 제한 규칙 업데이트
- CircuitBreaker가 실패 임계값 및 타임아웃 업데이트
- BackendPool이 백엔드 설정 업데이트
- 즉시 헬스 체크 - 백엔드가 추가되면 즉시 헬스 체크가 실행되어 새 백엔드가 다음 주기적 체크를 기다리지 않고 1-2초 내에 사용 가능해짐
- 오류 처리 - 잘못된 경우 오류 기록 및 이전 설정 유지
핫 리로드 상태 확인¶
admin API를 사용하여 핫 리로드 상태 및 기능을 확인하세요:
# 핫 리로드가 활성화되었는지 확인
curl http://localhost:8080/admin/config/hot-reload-status
# 현재 설정 보기
curl http://localhost:8080/admin/config
핫 리로드 동작 예제¶
예제 1: 로그 레벨 변경 (즉시)
결과: 로그 레벨이 즉시 변경됩니다. 재시작 불필요. 진행 중인 요청은 계속되고 새 로그는 debug 레벨 사용.예제 2: 백엔드 추가 (즉시 헬스 체크와 함께 점진적)
# 이전
backends:
- name: "ollama"
url: "http://localhost:11434"
# 이후
backends:
- name: "ollama"
url: "http://localhost:11434"
- name: "lmstudio"
url: "http://localhost:1234"
예제 2b: 백엔드 제거 (우아한 드레이닝)
# 이전
backends:
- name: "ollama"
url: "http://localhost:11434"
- name: "lmstudio"
url: "http://localhost:1234"
# 이후
backends:
- name: "ollama"
url: "http://localhost:11434"
백엔드 상태 생명주기¶
백엔드가 설정에서 제거되면 우아한 종료 프로세스를 거칩니다:
- Active → Draining: 백엔드가 드레이닝으로 표시됩니다. 새 요청은 이 백엔드를 건너뜁니다.
- 진행 중인 요청 완료: 기존 요청/스트림은 중단 없이 계속됩니다.
- 정리: 모든 참조가 해제되거나 5분 타임아웃 후에 백엔드가 제거됩니다.
이를 통해 설정 변경 중에도 진행 중인 연결에 영향을 주지 않습니다.
예제 3: 바인드 주소 변경 (재시작 필요)
결과: 경고 기록됨. 변경이 적용되지 않음. 새 포트에 바인드하려면 재시작 필요.분산 추적¶
Continuum Router는 백엔드 서비스 간 요청 상관관계를 위한 분산 추적을 지원합니다. 이 기능은 여러 서비스를 통과하는 요청의 디버깅 및 모니터링에 도움을 줍니다.
설정¶
tracing:
enabled: true # 분산 추적 활성화/비활성화 (기본값: true)
w3c_trace_context: true # W3C Trace Context 헤더 지원 (기본값: true)
headers:
trace_id: "X-Trace-ID" # 추적 ID 헤더 이름 (기본값)
request_id: "X-Request-ID" # 요청 ID 헤더 이름 (기본값)
correlation_id: "X-Correlation-ID" # 상관 ID 헤더 이름 (기본값)
동작 방식¶
-
추적 ID 추출: 요청이 도착하면 라우터는 다음 우선순위로 헤더에서 추적 ID를 추출합니다:
- W3C
traceparent헤더 (W3C 지원 활성화 시) - 설정된
trace_id헤더 (X-Trace-ID) - 설정된
request_id헤더 (X-Request-ID) - 설정된
correlation_id헤더 (X-Correlation-ID)
- W3C
-
추적 ID 생성: 헤더에서 추적 ID가 발견되지 않으면 새 UUID가 생성됩니다.
-
헤더 전파: 추적 ID는 여러 헤더를 통해 백엔드 서비스로 전파됩니다:
X-Request-ID: 광범위한 호환성을 위함X-Trace-ID: 주요 추적 식별자X-Correlation-ID: 상관관계 추적용traceparent: W3C Trace Context (활성화 시)tracestate: W3C Trace State (원본 요청에 존재 시)
-
재시도 시 보존: 동일한 추적 ID가 모든 재시도 시도에서 보존되어, 단일 클라이언트 요청에 대한 여러 백엔드 요청의 상관관계를 쉽게 파악할 수 있습니다.
구조화된 로깅¶
추적이 활성화되면 모든 로그 메시지에 trace_id 필드가 포함됩니다:
{
"timestamp": "2024-01-15T10:30:00Z",
"level": "info",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"message": "Processing chat completions request",
"backend": "openai",
"model": "gpt-4o"
}
W3C Trace Context¶
w3c_trace_context가 활성화되면 라우터는 W3C Trace Context 표준을 지원합니다:
- 수신:
traceparent헤더 파싱 (형식:00-{trace_id}-{span_id}-{flags}) - 송신: 보존된 추적 ID와 새 span ID로
traceparent헤더 생성 - 상태: 원본 요청에 있는 경우
tracestate헤더 전달
traceparent 예시: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
추적 비활성화¶
분산 추적을 비활성화하려면:
로드 밸런싱 전략¶
load_balancer:
strategy: "round_robin" # round_robin, weighted, random
health_aware: true # 정상 백엔드만 사용
전략:
round_robin: 백엔드 간 균등 분배weighted: 백엔드 가중치에 기반한 분배random: 무작위 선택 (패턴 방지에 좋음)
백엔드별 재시도 설정¶
backends:
- name: "slow-backend"
url: "http://slow.example.com"
retry_override: # 전역 재시도 설정 오버라이드
max_attempts: 5 # 느린 백엔드에 더 많은 시도
base_delay: "500ms" # 더 긴 지연
max_delay: "60s"
모델 폴백¶
Continuum Router는 기본 모델을 사용할 수 없을 때 자동 모델 폴백을 지원합니다. 이 기능은 계층화된 장애 조치 보호를 위해 서킷 브레이커와 통합됩니다.
설정¶
fallback:
enabled: true
# 각 기본 모델에 대한 폴백 체인 정의
fallback_chains:
# 동일 프로바이더 폴백
"gpt-4o":
- "gpt-4-turbo"
- "gpt-3.5-turbo"
"claude-opus-4-5-20251101":
- "claude-sonnet-4-5"
- "claude-haiku-4-5"
# 크로스 프로바이더 폴백
"gemini-2.5-pro":
- "gemini-2.5-flash"
- "gpt-4o" # Gemini를 사용할 수 없으면 OpenAI로 폴백
fallback_policy:
trigger_conditions:
error_codes: [429, 500, 502, 503, 504]
timeout: true
connection_error: true
model_not_found: true
circuit_breaker_open: true
max_fallback_attempts: 3
fallback_timeout_multiplier: 1.5
preserve_parameters: true
model_settings:
"gpt-4o":
fallback_enabled: true
notify_on_fallback: true
트리거 조건¶
| 조건 | 설명 |
|---|---|
error_codes | 폴백을 트리거하는 HTTP 상태 코드 (예: 429, 500, 502, 503, 504) |
timeout | 요청 타임아웃 |
connection_error | TCP 연결 실패 |
model_not_found | 백엔드에서 모델을 사용할 수 없음 |
circuit_breaker_open | 백엔드 서킷 브레이커가 열림 |
응답 헤더¶
폴백이 사용되면 다음 헤더가 응답에 추가됩니다:
| 헤더 | 설명 | 예제 |
|---|---|---|
X-Fallback-Used | 폴백이 사용되었음을 나타냄 | true |
X-Original-Model | 원래 요청된 모델 | gpt-4o |
X-Fallback-Model | 요청을 처리한 모델 | gpt-4-turbo |
X-Fallback-Reason | 폴백이 트리거된 이유 | error_code_429 |
X-Fallback-Attempts | 폴백 시도 횟수 | 2 |
크로스 프로바이더 매개변수 변환¶
프로바이더 간 폴백 시 (예: OpenAI → Anthropic) 라우터가 요청 매개변수를 자동으로 변환합니다:
| OpenAI 매개변수 | Anthropic 매개변수 | 비고 |
|---|---|---|
max_tokens | max_tokens | 누락 시 자동 채움 (Anthropic 필수) |
temperature | temperature | 직접 매핑 |
top_p | top_p | 직접 매핑 |
stop | stop_sequences | 배열 변환 |
프로바이더별 매개변수는 크로스 프로바이더 폴백 중에 자동으로 제거되거나 변환됩니다.
서킷 브레이커와의 통합¶
폴백 시스템은 서킷 브레이커와 함께 작동합니다:
- 서킷 브레이커가 실패 감지하고 임계값 초과 시 열림
- 폴백 체인 활성화 서킷 브레이커가 열렸을 때
- 요청이 폴백 모델로 라우팅 설정된 체인에 따라
- 서킷 브레이커가 복구 테스트하고 백엔드 복구 시 닫힘
# 예제: 서킷 브레이커와 폴백 결합 설정
circuit_breaker:
enabled: true
failure_threshold: 5
timeout: 60s
fallback:
enabled: true
fallback_policy:
trigger_conditions:
circuit_breaker_open: true # 서킷 브레이커에 연결
속도 제한¶
Continuum Router는 남용을 방지하고 공정한 리소스 할당을 보장하기 위해 /v1/models 엔드포인트에 대한 내장 속도 제한을 포함합니다.
현재 설정¶
속도 제한은 현재 다음 기본값으로 설정되어 있습니다:
# 참고: 이 값들은 현재 하드코딩되어 있지만 향후 버전에서 설정 가능해질 수 있습니다
rate_limiting:
models_endpoint:
# 클라이언트별 제한 (API 키 또는 IP 주소로 식별)
sustained_limit: 100 # 분당 최대 요청 수
burst_limit: 20 # 5초 윈도우에서 최대 요청 수
# 시간 윈도우
window_duration: 60s # 지속 제한을 위한 슬라이딩 윈도우
burst_window: 5s # 버스트 감지를 위한 윈도우
# 클라이언트 식별 우선순위
identification:
- api_key # Bearer 토큰 (처음 16자가 ID로 사용)
- x_forwarded_for # 프록시/로드 밸런서 헤더
- x_real_ip # 대체 IP 헤더
- fallback: "unknown" # 식별자가 없을 때
작동 방식¶
- 클라이언트 식별: 각 요청은 다음을 사용하여 클라이언트와 연결:
Authorization: Bearer <token>헤더의 API 키 (선호)-
프록시 헤더의 IP 주소 (폴백)
-
이중 윈도우 접근 방식:
- 지속 제한: 시간 경과에 따른 과도한 사용 방지
-
버스트 보호: 연속적인 빠른 요청 포착
-
독립적인 할당량: 각 클라이언트는 별도의 속도 제한:
- API 키
abc123...을 가진 클라이언트 A: 100 req/min - API 키
def456...을 가진 클라이언트 B: 100 req/min - IP
192.168.1.1의 클라이언트 C: 100 req/min
응답 헤더¶
속도 제한 시 응답 포함:
- 상태 코드:
429 Too Many Requests - 오류 메시지: 버스트 또는 지속 제한 초과 여부 표시
캐시 TTL 최적화¶
캐시 오염 공격 방지를 위해:
- 빈 모델 목록: 5초만 캐시
- 일반 응답: 60초 캐시
이를 통해 공격자가 백엔드 장애 중에 빈 응답을 캐시하도록 강제하는 것을 방지합니다.
모니터링¶
속도 제한 위반은 메트릭에서 추적:
rate_limit_violations: 거부된 총 요청empty_responses_returned: 제공된 빈 모델 목록- 문제가 있는 클라이언트 식별을 위한 클라이언트별 위반 추적
향후 개선 사항¶
향후 버전에서 지원될 수 있는 기능:
- YAML/환경 변수를 통한 설정 가능한 속도 제한
- 엔드포인트별 속도 제한
- API 키별 사용자 정의 속도 제한
- Redis 기반 분산 속도 제한
환경별 설정¶
개발 설정¶
# config/development.yaml
server:
bind_address: "127.0.0.1:8080"
backends:
- name: "local-ollama"
url: "http://localhost:11434"
health_checks:
interval: "10s" # 더 빈번한 확인
timeout: "5s"
logging:
level: "debug" # 상세 로깅
format: "pretty" # 사람이 읽기 쉬운
enable_colors: true
프로덕션 설정¶
# config/production.yaml
server:
bind_address: "0.0.0.0:8080"
workers: 8 # 프로덕션용 더 많은 워커
connection_pool_size: 300 # 더 큰 연결 풀
backends:
- name: "primary-openai"
url: "https://api.openai.com"
weight: 3
- name: "secondary-azure"
url: "https://azure-openai.example.com"
weight: 2
- name: "fallback-local"
url: "http://internal-llm:11434"
weight: 1
health_checks:
interval: "60s" # 덜 빈번한 확인
timeout: "15s" # 네트워크 지연을 위한 더 긴 타임아웃
unhealthy_threshold: 5 # 더 많은 허용
healthy_threshold: 3
request:
timeout: "120s" # 프로덕션용 짧은 타임아웃
max_retries: 5 # 더 많은 재시도
logging:
level: "warn" # 덜 상세한 로깅
format: "json" # 구조화된 로깅
컨테이너 설정¶
# config/container.yaml - 컨테이너에 최적화
server:
bind_address: "0.0.0.0:8080"
workers: 0 # 컨테이너 제한에 따라 자동 감지
backends:
- name: "backend-1"
url: "${BACKEND_1_URL}" # 환경 변수 치환
- name: "backend-2"
url: "${BACKEND_2_URL}"
logging:
level: "${LOG_LEVEL}" # 환경을 통해 설정 가능
format: "json" # 컨테이너에서 항상 JSON
예제¶
다중 백엔드 설정¶
# 엔터프라이즈 다중 백엔드 설정
server:
bind_address: "0.0.0.0:8080"
workers: 8
connection_pool_size: 400
backends:
# 기본 OpenAI GPT 모델
- name: "openai-primary"
url: "https://api.openai.com"
weight: 5
models: ["gpt-4", "gpt-3.5-turbo"]
retry_override:
max_attempts: 3
base_delay: "500ms"
# 보조 Azure OpenAI
- name: "azure-openai"
url: "https://your-resource.openai.azure.com"
weight: 3
models: ["gpt-4", "gpt-35-turbo"]
# 오픈 모델을 위한 로컬 Ollama
- name: "local-ollama"
url: "http://ollama:11434"
weight: 2
models: ["llama2", "mistral", "codellama"]
# vLLM 배포
- name: "vllm-cluster"
url: "http://vllm-service:8000"
weight: 4
models: ["meta-llama/Llama-2-7b-chat-hf"]
health_checks:
enabled: true
interval: "45s"
timeout: "15s"
unhealthy_threshold: 3
healthy_threshold: 2
request:
timeout: "180s"
max_retries: 4
cache:
model_cache_ttl: "600s" # 10분 캐시
deduplication_ttl: "120s" # 2분 중복 제거
enable_deduplication: true
logging:
level: "info"
format: "json"
고성능 설정¶
# 높은 처리량 시나리오에 최적화
server:
bind_address: "0.0.0.0:8080"
workers: 16 # 높은 워커 수
connection_pool_size: 1000 # 큰 연결 풀
backends:
- name: "fast-backend-1"
url: "http://backend1:8000"
weight: 1
- name: "fast-backend-2"
url: "http://backend2:8000"
weight: 1
- name: "fast-backend-3"
url: "http://backend3:8000"
weight: 1
health_checks:
enabled: true
interval: "30s"
timeout: "5s" # 빠른 타임아웃
unhealthy_threshold: 2 # 빠른 실패
healthy_threshold: 1 # 빠른 복구
request:
timeout: "60s" # 높은 처리량을 위한 짧은 타임아웃
max_retries: 2 # 적은 재시도
retry:
max_attempts: 2
base_delay: "50ms" # 빠른 재시도
max_delay: "5s"
exponential_backoff: true
jitter: true
cache:
model_cache_ttl: "300s"
deduplication_ttl: "30s" # 짧은 중복 제거 윈도우
enable_deduplication: true
logging:
level: "warn" # 성능을 위한 최소 로깅
format: "json"
개발 설정¶
# 개발자 친화적 설정
server:
bind_address: "127.0.0.1:8080" # localhost만
workers: 2 # 개발용 적은 워커
connection_pool_size: 20 # 작은 풀
backends:
- name: "local-ollama"
url: "http://localhost:11434"
weight: 1
health_checks:
enabled: true
interval: "10s" # 빠른 피드백을 위한 잦은 확인
timeout: "3s"
unhealthy_threshold: 2
healthy_threshold: 1
request:
timeout: "300s" # 디버깅을 위한 긴 타임아웃
max_retries: 1 # 디버깅을 위한 최소 재시도
logging:
level: "debug" # 상세 로깅
format: "pretty" # 사람이 읽기 쉬운 형식
enable_colors: true # 컬러 출력
cache:
model_cache_ttl: "60s" # 빠른 테스트를 위한 짧은 캐시
deduplication_ttl: "10s" # 짧은 중복 제거
enable_deduplication: false # 테스트를 위해 비활성화
마이그레이션 가이드¶
명령줄 인수에서 마이그레이션¶
현재 명령줄 인수를 사용하고 있다면 설정 파일로 마이그레이션하세요:
이전:
이후: 1. 설정 파일 생성:
-
설정 편집:
-
설정 파일 사용:
환경 변수에서 마이그레이션¶
설정 파일과 함께 환경 변수를 오버라이드로 계속 사용할 수 있습니다:
설정 파일 (config.yaml):
환경 오버라이드:
export CONTINUUM_BIND_ADDRESS="0.0.0.0:9000"
export CONTINUUM_BACKEND_URLS="http://localhost:11434,http://localhost:1234"
continuum-router --config config.yaml
설정 유효성 검사¶
서버를 시작하지 않고 설정을 검증하려면:
# 설정 로딩 테스트
continuum-router --config config.yaml --help
# dry-run으로 설정 확인 (향후 기능)
continuum-router --config config.yaml --dry-run
이 설정 가이드는 Continuum Router에서 사용할 수 있는 모든 설정 옵션에 대한 포괄적인 내용을 제공합니다. 유연한 설정 시스템을 통해 명확한 우선순위 규칙과 유효성 검사를 유지하면서 모든 배포 시나리오에 라우터를 적용할 수 있습니다.