서버 & 백엔드¶
서버 섹션¶
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 소켓 바인딩 (Linux, macOS, Windows 10 1809+ 지원):
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:접두사로 시작해야 합니다 - 기존 소켓 파일은 바인딩 전에 자동으로 삭제됩니다
- 소켓 파일은 정상적인 종료 시 정리됩니다
- Windows 10 1809+ (Build 17063+)에서는
socket2크레이트를 통해 Unix 소켓이 완전히 지원됩니다 - 기타 비Unix 플랫폼에서는
unix:주소가 경고를 기록하고 건너뜁니다 - Windows는 Unix 파일 권한 모드를 지원하지 않습니다.
socket_mode옵션은 허용되지만 무시됩니다 - 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 |
mlxcel |
MLxcel 서버 (MLX 기반, llama-server 호환, macOS 전용) | http://localhost:8080 |
lmstudio |
LM Studio 로컬 서버 | http://localhost:1234 |
anthropic |
Anthropic Claude API (요청/응답 변환 포함 네이티브) | https://api.anthropic.com |
continuum-router |
원격 Continuum Router 또는 Backend.AI GO 인스턴스 (연합 라우팅) | 지정 필수 |
네이티브 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
ChatGPT 구독 / Codex 헤드리스 로그인 (OAuth 디바이스 플로우)¶
유료 OpenAI API 키 없이도 ChatGPT Plus / Pro / Enterprise 구독으로 OpenAI Codex 백엔드(https://chatgpt.com/backend-api/codex)에 인증할 수 있습니다.
OpenAI 공급자는 RFC 8628 표준 디바이스 플로우를 구현하지 않고, 공식 Codex CLI가 헤드리스 디바이스 로그인에 사용하는 자체 3단계 흐름을 사용합니다:
auth.openai.com/api/accounts/deviceauth/usercode에서 일회성user_code를 받습니다.- 사용자가 브라우저에서 코드를 승인할 때까지
auth.openai.com/api/accounts/deviceauth/token을 폴링합니다. - 받은 인증 코드를
auth.openai.com/oauth/token에서 PKCE로 액세스/리프레시 토큰과 교환합니다.
auth.openai.com 앞단에 있는 Cloudflare가 트래픽을 받아주도록 모든 요청에 originator: codex_cli_rs 헤더가 함께 전송됩니다.
일회성 로그인¶
OpenAI 인증 URL을 브라우저로 열 수 있는 환경에서 디바이스 플로우 로그인을 실행합니다. 라우터가 인증 URL과 짧은 사용자 코드를 출력하고, 디바이스가 승인될 때까지 토큰 엔드포인트를 폴링합니다.
토큰은 설정된 token_store 경로에 Unix에서 0600 모드로 원자적으로 기록됩니다. 로그인이 끝나면 라우터가 토큰을 자동으로 사용하고, 만료 60초 전에 투명하게 갱신합니다. 백엔드가 401을 응답하면 강제로 한 번 갱신한 뒤 한 번 재시도하고, 그래도 실패하면 클라이언트에 오류가 전달됩니다.
백엔드 설정¶
backends:
- name: openai-chatgpt
type: openai
url: https://chatgpt.com/backend-api/codex
auth:
type: oauth
oauth:
provider: openai
token_store: ~/.continuum-router/auth/openai.json
models:
- gpt-5
- codex-mini
client_id와 scope는 auth.openai.com이 받아주는 공식 Codex CLI의 공개 값으로 기본 설정되어 있어, 자체 OAuth 클라이언트를 등록한 경우에만 따로 지정하면 됩니다.
설정 항목¶
| 필드 | 필수 | 설명 |
|---|---|---|
auth.type |
예 | 디바이스 플로우 인증을 켜려면 oauth로 지정합니다. |
auth.oauth.provider |
예 | OAuth 공급자. 현재 openai만 지원합니다. |
auth.oauth.client_id |
아니오 | 공개 OAuth 클라이언트 ID. 기본값은 auth.openai.com이 ChatGPT 구독 헤드리스 로그인에 받아주는 Codex CLI의 공개 client_id입니다. 별도 OAuth 클라이언트를 등록한 경우에만 오버라이드합니다. |
auth.oauth.scope |
아니오 | 디바이스 인가 시 요청하는 공백 구분 스코프 문자열. 기본값은 "openid profile email offline_access"입니다. |
auth.oauth.token_store |
예 | JSON 토큰 저장 경로 (예: ~/.continuum-router/auth/openai.json). 틸드와 ${ENV_VAR}가 확장됩니다. |
auth.oauth.device_code_endpoint |
아니오 | 디바이스 인가(사용자 코드) 엔드포인트 오버라이드. 기본값은 공급자별 well-known URL입니다. |
auth.oauth.token_poll_endpoint |
아니오 | 디바이스 플로우에서 사용하는 토큰 폴링 엔드포인트 오버라이드 (Codex 전용으로, 표준 token_endpoint와 별개입니다). 기본값은 공급자별 well-known URL입니다. |
auth.oauth.token_endpoint |
아니오 | PKCE 교환과 갱신에 사용하는 토큰 엔드포인트 오버라이드. 기본값은 공급자별 well-known URL입니다. |
auth.oauth.verification_url |
아니오 | auth login 실행 시 출력되는 사용자용 인증 URL 오버라이드. openai의 기본값은 https://auth.openai.com/codex/device입니다. |
auth.oauth.redirect_uri |
아니오 | PKCE 교환에 사용하는 redirect URI 오버라이드. 기본값은 공급자별 well-known URL입니다. |
auth.oauth.originator |
아니오 | originator 요청 헤더 오버라이드. provider: openai의 기본값은 auth.openai.com의 Cloudflare 프런트가 허용하는 codex_cli_rs입니다. 환경상 다른 값이 필요할 때만 변경합니다. |
auth.oauth.user_agent |
아니오 | 디바이스 플로우와 갱신 요청에 사용되는 User-Agent 헤더 오버라이드. auth.openai.com이 Cloudflare 뒤에 있고 reqwest 기본 UA를 JS 챌린지로 거부하기 때문에, provider: openai의 기본값은 Codex CLI 호환 값입니다. |
운영 시 유의사항¶
- 토큰은 로그, 트레이스, 메트릭 어디에도 노출되지 않습니다. 갱신 시 짧게 마스킹된 접두어만 로그에 남습니다.
- 갱신은
tokio::sync::Mutex로 single-flight 처리되므로, 만료 시점에 동시 진행 중인 요청들이 OAuth 공급자에 갱신 요청을 폭주시키지 않습니다. - 정적
api_key설정은 영향을 받지 않고 그대로 동작하며, OAuth는 백엔드별로auth.type: oauth블록을 통해 옵트인됩니다. continuum-router auth login --backend <name>을 다시 실행하면 토큰 저장소가 원자적으로 다시 기록되며, 라우터가 동작 중이어도 안전합니다.- OpenAI 공급자의 인증 엔드포인트는 Cloudflare로 보호되고 있어, 기본
user_agent와originator: codex_cli_rs헤더가 공식 Codex CLI를 모방하도록 설정되어 있습니다. 환경상 다른 값이 필요할 때만auth.oauth.user_agent또는auth.oauth.originator를 변경하세요. - YAML에서는
OAuth가oauth로 직렬화되며, 호환성을 위해 serde 기본 snake_case가 만들어내던o_auth표기도 별칭으로 받아들입니다.
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 엔드포인트에서 자동 검색 |
mlxcel |
✅ 예 | /v1/models 엔드포인트에서 자동 검색 |
lmstudio |
✅ 예 | /v1/models 엔드포인트에서 자동 검색 |
continuum-router |
✅ 예 | 원격 /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"
}'
네이티브 MLxcel 백엔드¶
type: mlxcel을 사용하면 라우터는 Apple Silicon을 사용하는 macOS용 MLX 기반 모델 서빙 백엔드인 MLxcel에 대한 네이티브 지원을 제공합니다:
- 기본 URL:
http://localhost:8080(llama-server와 동일) - API 호환성: llama-server (llama.cpp) API와 완전 호환
- 모델 형식: Apple의 MLX 프레임워크를 통해 SafeTensor 형식 모델 제공
- 헬스 체크:
/health를 기본으로 사용하고,/v1/models를 폴백으로 사용 - 플랫폼: Apple Silicon이 탑재된 macOS 전용
최소 MLxcel 설정:
전체 MLxcel 설정:
backends:
- name: "mlxcel-local"
type: mlxcel
url: "http://192.168.1.100:8080" # 필요시 커스텀 URL
weight: 2
models:
- mlx-community/Qwen3-4B-4bit
자동 감지 미지원
MLxcel은 llama.cpp와 동일한 응답 형식(owned_by: "llamacpp" 포함)을 반환하므로 /v1/models 응답에서 자동 감지할 수 없습니다. 설정에서 type: mlxcel을 명시적으로 설정해야 합니다. 이렇게 하면 모델 식별에 적절한 owned_by 메타데이터(mlxcel)가 사용됩니다.
네이티브 LM Studio 백엔드¶
type: lmstudio를 사용하면 라우터는 LM Studio 로컬 서버에 대한 네이티브 지원을 제공합니다:
- 기본 URL:
http://localhost:1234(LM Studio 기본 포트) - 헬스 체크:
/v1/models(OpenAI 호환)를 기본으로 사용하고,/api/v1/models(네이티브 API)를 폴백으로 사용 - 모델 검색:
/v1/models엔드포인트에서 모델 자동 검색 owned_by속성: 적절한 모델 속성을 위해"lmstudio"보고
최소 LM Studio 설정:
backends:
- name: "lmstudio"
type: lmstudio
# 기본 http://localhost:1234 사용 시 URL 불필요
# 로컬 서버에는 API 키 불필요
전체 LM Studio 설정:
backends:
- name: "lmstudio"
type: lmstudio
url: "http://192.168.1.100:1234" # 필요시 커스텀 URL
weight: 2
api_key: "${LM_API_TOKEN}" # 선택: LM Studio API 토큰 (v0.4.0+)
# 모델은 /v1/models 엔드포인트에서 자동 검색
LM Studio 기능¶
| 기능 | 설명 |
|---|---|
| OpenAI 호환 API | /v1/chat/completions, /v1/completions, /v1/embeddings 완전 지원 |
| 네이티브 REST API | 모델 관리를 위한 추가 /api/v1/* 엔드포인트 |
| 로컬 추론 | 클라우드 API 의존성 없음 |
| 자동 검색 | /v1/models에서 모델 자동 감지 |
| 선택적 인증 | Authorization: Bearer 헤더를 통한 API 토큰 지원 (v0.4.0+) |
네이티브 Continuum Router / Backend.AI GO 백엔드¶
type: continuum-router를 사용하면 라우터는 연합 LLM 라우팅을 위해 원격 Continuum Router 인스턴스 또는 Backend.AI GO 배포에 연결합니다. 지원 별칭: continuum-router, continuum_router, ContinuumRouter, backendai, backend-ai, backend_ai.
- 헬스 체크: 기본
/health, 폴백/v1/models - 모델 검색: 원격 인스턴스의
/v1/models엔드포인트에서 자동 검색 - 인증:
Authorization: Bearer <key>헤더를 통한 Bearer 토큰 - 요청 패스스루: 변환 없이 요청 전달 (두 시스템 모두 OpenAI 호환 API 사용)
owned_by속성: 검색된 모델에 대해"continuum-router"보고- 전송: HTTP 및 Unix Domain Socket 전송 모두 지원
최소 설정:
backends:
- name: "remote-cr"
type: continuum-router
url: "https://remote.example.com"
api_key: "${REMOTE_API_KEY}"
# 모델은 원격 /v1/models 엔드포인트에서 자동 검색됨
명시적 모델이 있는 전체 설정:
backends:
- name: "remote-backendai"
type: continuum-router
url: "https://remote-backend-ai.example.com"
api_key: "${REMOTE_BACKEND_AI_API_KEY}"
weight: 2
models:
- gpt-4o
- claude-sonnet-4-20250514
사용 사례:
- 다중 지역 배포: Continuum Router 인스턴스 간 지역 기반 요청 라우팅
- 연합 라우팅: 독립적인 CR 또는 Backend.AI GO 배포 연결
- 계층적 접근: 할당량 관리를 위해 중앙 Backend.AI GO 인스턴스를 통한 라우팅
- 고가용성: 장애 조치를 위한 여러 Backend.AI GO 인스턴스 구성
Continuum Router 백엔드 기능¶
| 기능 | 설명 |
|---|---|
| 연합 라우팅 | 원격 Continuum Router 또는 Backend.AI GO 인스턴스로 요청 전달 |
| 자동 검색 | 원격 /v1/models에서 모델 자동 검색 |
| Bearer 인증 | API 키를 Authorization: Bearer 헤더로 전달 |
| SSE 스트리밍 | 채팅 완성에 대한 완전한 스트리밍 지원 |
| 변환 없음 | 요청을 그대로 패스스루 (양쪽 모두 OpenAI 호환) |
| Unix 소켓 지원 | unix:///path/to/socket.sock 전송 URL 지원 |
Unix Domain Socket 백엔드¶
Continuum Router는 로컬 LLM 백엔드를 위해 TCP 대신 Unix Domain Socket(UDS)을 전송 수단으로 지원합니다. Unix 소켓은 다음을 제공합니다:
- 향상된 보안: TCP 포트 노출 없음 - 파일 시스템을 통한 통신
- 낮은 레이턴시: 로컬 통신에서 네트워크 스택 오버헤드 없음
- 더 나은 성능: 컨텍스트 스위칭 및 메모리 복사 감소
- 간단한 접근 제어: 표준 Unix 파일 권한 사용
URL 형식:
플랫폼 지원:
| 플랫폼 | 지원 |
|---|---|
| Linux | 네이티브 AF_UNIX를 통한 완전 지원 |
| macOS | 네이티브 AF_UNIX를 통한 완전 지원 |
| Windows | socket2 크레이트를 통한 완전 지원 (Windows 10 1809+ / Build 17063+) |
| 기타 | 미지원; 주소가 경고와 함께 건너뜀 |
설정 예제:
Windows에서는 드라이브 문자 경로를 사용합니다 (예: unix://C:/temp/llama.sock).
Linux/macOS에서는 표준 절대 경로를 사용합니다 (예: unix:///var/run/llama.sock).
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 |
사용자별 영구 소켓 |
~/Library/Application Support/*.sock |
macOS 애플리케이션 데이터 (공백이 포함된 경로 지원) |
헬스 체크: 라우터는 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 백엔드로 스트리밍 사용 |