서버 & 백엔드¶
서버 섹션¶
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는 시작 시 구성되는 Axum 라우터 계층입니다. server.cors를 변경한 뒤에는 재시작하세요.
백엔드 섹션¶
요청을 라우팅할 LLM 백엔드를 정의합니다:
backends:
- name: "unique-identifier" # 모든 백엔드에서 고유해야 함
backend_id: "stable-identity" # 선택 사항: 안정적인 회계 식별자 (아래 참고)
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"] # 선택 사항: 명시적 모델 목록
internal: false # 선택 사항: 인프라 전용 모델을 사용자 노출에서 숨김 (기본값 false)
retry_override: # 선택 사항: 백엔드별 재시도 설정
max_attempts: 5
initial_delay: "200ms"
max_delay: "10s"
backoff_multiplier: 2.0
jitter: true
retryable_status_codes: [429, 502, 503, 504]
retryable_errors: [ConnectionError, TimeoutError]
timeout: "30s"
안정적인 백엔드 식별자¶
backend_id는 운영자가 직접 부여하는 선택적 백엔드 식별자입니다. 컨트롤 플레인을 켰을 때만 의미가 있습니다. 라우터는 이 값을 하트비트 인벤토리로 Continuum Hub에 보고하고, 각 요청을 실제로 처리한 백엔드의 사용량 레코드에 찍습니다. Hub는 이를 근거로 백엔드 단위 코스트 센터에 사용량을 청구합니다.
name으로는 이 일을 할 수 없어서 별도 필드가 필요합니다. name은 핫 리로드 때 언제든 바꿀 수 있는 표시용 레이블이고, (provider, model) 쌍은 백엔드가 아닙니다. 여러 백엔드가 같은 쌍을 제공할 수 있기 때문입니다. 둘 중 어느 쪽에 비용을 귀속시켜도 서로 다른 두 비용 주체가 조용히 합쳐지거나, 이름을 바꾸는 순간 하나의 비용 주체가 둘로 갈라집니다. 청구가 끝난 뒤에는 어느 쪽도 눈에 띄지 않습니다.
그래서 라우터는 이 값을 절대 유도하지 않습니다. name, url, type, 제공하는 provider/model 쌍, 목록상의 위치, 가변 필드의 해시 중 무엇으로도 합성하지 않습니다. 운영자가 부여하거나, 아니면 해당 백엔드는 식별자를 보고하지 않습니다.
backends:
- name: "openai-primary"
backend_id: "openai-eu-1"
type: "openai"
url: "https://api.openai.com/v1"
규칙¶
| 항목 | 값 |
|---|---|
| 선택 사항 | 생략하면 해당 백엔드는 식별자를 보고하지 않습니다. 오류가 아니라 정상 상태이며, 사용량은 기존과 똑같이 provider/model 단위로 귀속됩니다. |
| 길이 | 1~128자. |
| 문자 집합 | [A-Za-z0-9._-] (ASCII 영문자, 숫자, ., _, -). 공백, /, :, ,, 제어 문자, 비 ASCII는 거부합니다. 이 값은 Hub가 바이트 단위로 대조하고 정규화하지 않는 불투명한 키로 전달되기 때문입니다. |
| 고유성 | 한 라우터의 백엔드 안에서 고유해야 합니다. 두 백엔드가 같은 식별자를 쓰면 하나의 비용 주체로 합쳐지므로, 로드 시점, config validate, Admin API, 핫 리로드 모두에서 거부됩니다. |
| 범위 | 플릿 전체가 아니라 라우터 단위입니다. Hub가 인증된 라우터 단위로 식별자를 구분하므로, 두 라우터가 각자의 로컬 백엔드를 모두 primary라고 불러도 충돌하지 않습니다. |
안정성¶
이 값은 부팅할 때 새로 만들어내지 않고 설정 파일에서 읽으므로 재시작해도 그대로이고, 별도 필드이므로 이름을 바꿔도 그대로입니다.
# 변경 전: "openai-eu-1" 비용 주체로 청구
backends:
- name: "openai-primary"
backend_id: "openai-eu-1"
---
# 이름 변경 후: 여전히 "openai-eu-1"로 청구
backends:
- name: "openai-frankfurt"
backend_id: "openai-eu-1"
중복되거나 형식이 잘못된 식별자가 들어온 핫 리로드는 통째로 거부되고, 실행 중인 라우터는 식별자 맵이 일부만 적용된 상태로 넘어가지 않고 마지막 정상 설정으로 계속 서비스합니다.
무엇이 보고되는가¶
식별자는 백엔드로 요청을 보내는 시점에 확정됩니다. 따라서 두 번째 백엔드로 폴백한 요청은 처음 선택된 백엔드가 아니라 실제로 응답을 만든 백엔드에 귀속됩니다. 프로바이더 배치는 작업을 제출할 때 확정한 식별자를 그대로 들고 있으므로, 몇 시간 뒤 완료되어도 그 배치를 실행한 백엔드를 정확히 지목합니다. 라우터 로컬 캐시가 처리한 응답은 백엔드로 나간 적이 없으므로 식별자를 보고하지 않습니다. 식별자가 없을 때 라우터가 표시용 이름으로 대신 채우는 일은 없습니다.
하트비트 인벤토리도 같은 식별자를 보고합니다. Hub는 자체 백엔드 인벤토리를 갖고 있지 않으므로, 운영자가 코스트 센터에 배정할 수 있는 식별자 목록을 확인하는 경로가 바로 이것입니다.
내부(인프라 전용) 백엔드¶
백엔드에 internal: true를 설정하면 해당 백엔드의 모델을 사용자 노출 표면에서 제외하면서도 라우터 내부 트래픽(예: 가드레일 분류기, 내부 요약기, 임베딩 모델)에는 계속 제공할 수 있습니다. 기본값은 false(공개)입니다.
백엔드가 내부(internal)일 때:
- 모델이
GET /v1/models(그리고/v1/models/extended, 단일 모델/v1/models/{id}, Anthropic/anthropic/v1/models목록 변형)에서 제외됩니다. - 내부 백엔드에서만 제공되는 모델로의 사용자 요청은 model-not-found로 거부됩니다.
- 라우터 내부 호출자는 영향을 받지 않습니다: 가드레일 프로바이더가
backend:참조로 해당 백엔드를 이름으로 해석할 수 있고, 관리자 표면에서는 계속 표시됩니다.
내부 백엔드와 공개 백엔드가 동시에 제공하는 모델은 공개 백엔드를 통해 계속 노출되고 라우팅되며, 목록에 내부 백엔드 이름이 노출되지 않습니다.
backends:
- name: "guard-classifier"
type: "vllm"
url: "http://localhost:8001"
internal: true
models: ["llama-guard-3"]
백엔드 없이 시작하기¶
라우터는 빈 백엔드 목록(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로 추가
health_checks:
interval: "30s"
timeout: "5s"
unhealthy_threshold: 3
healthy_threshold: 2
endpoint: "/health"
logging:
level: info
format: json
admin:
auth:
method: bearer
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 |
bedrock |
Amazon Bedrock (mantle + runtime + converse; runtime과 converse는 --features bedrock-sigv4 빌드가 필요) |
https://bedrock-mantle.{region}.api.aws 또는 https://bedrock-runtime.{region}.amazonaws.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
# Codex 백엔드는 실제 모델 목록을 라이브 엔드포인트에서 가져옵니다(아래 "동적 모델
# 열거" 참고). 이 목록이 비어 있지 않으면 열거된 모델 중 노출할 것을 선택하며, 열거가
# 실패하거나 결과가 없을 때의 폴백으로도 쓰입니다. 비워 두면 열거된 모델을 모두 노출합니다.
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 호환 값입니다. |
동적 모델 열거¶
정적 OpenAI API 백엔드와 달리, ChatGPT Codex 백엔드는 표준 /v1/models 엔드포인트를 제공하지 않습니다. 대신 GET <base>/models?client_version=<ver> 주소에서 계정 구독 플랜에 따라 필터링된 모델 목록을 반환합니다. 라우터는 모델 검색 시(그리고 일반 갱신 주기에) 로드된 OAuth 토큰으로 이 엔드포인트를 조회하여 /v1/models 응답과 라우팅을 채웁니다.
- 요청에는
Authorization: Bearer <access_token>,originator: codex_cli_rs헤더, 그리고id_token에서 추출한chatgpt-account-id헤더가 함께 전송됩니다. - 사용자에게 노출되는 모델만 남깁니다.
visibility가list이고,available_in_plans가 비어 있거나 계정 플랜을 포함하는 항목만 통과합니다(id_token의chatgpt_plan_type클레임에서 읽음).codex-auto-review처럼visibility가hide인 내부 항목은 절대 노출되지 않습니다. - 다른 백엔드와 똑같이 설정한
models:선택이 열거 결과에도 적용됩니다. 목록이 비어 있지 않으면 열거된 모델을 그 부분집합으로 좁히고, 비어 있으면 열거된 전체 집합(예:gpt-5.5,gpt-5.4,gpt-5.4-mini)을 노출합니다. 열거된 모델의owned_by에는 원시 백엔드 이름 대신 정리된 값openai가 들어갑니다. - 네트워크 오류, 2xx 아닌 응답, 빈 목록 등 어떤 실패가 발생해도 라우터는 설정된
models:목록으로 폴백하여 라우팅이 정상적으로 이어집니다. 동적 열거는 Codex OAuth 백엔드에만 적용되며, 다른 OAuth 및 정적 키 백엔드는 설정된 모델 목록을 사용합니다.
요청 처리¶
ChatGPT Codex 백엔드는 추론 엔드포인트로 <base>/responses 하나만 제공하며(기본 주소 기준 https://chatgpt.com/backend-api/codex/responses), 공개 OpenAI Responses API의 더 엄격한 부분집합을 구현합니다. 라우터는 Codex OAuth 백엔드로 라우팅된 /v1/chat/completions 요청을 Responses API 형식으로 변환한 뒤 Codex가 받아들이는 형태에 맞게 다듬습니다.
input은 항상 아이템 리스트로 전송합니다. 단일 메시지 요청이 만들어내는 문자열 축약형은 Codex가 거부합니다.- Codex가 거부하는
max_output_tokens,temperature,top_p,presence_penalty,frequency_penalty,stop필드는 제거합니다. - 특정 함수를 강제하는
tool_choice는"required"로 낮추고 경고를 로그에 남깁니다. Codex는 문자열 모드만 지원하므로, 도구 이름을 지정하지 않은 채 도구 호출을 유도하는 형태로 대체됩니다. - 요청에 시스템 메시지가 없으면
instructions필드를 최소 기본값으로 채웁니다. Codex는 instructions가 없는 요청을 거부하기 때문입니다.
Codex는 stream: true와 store: false 조합 하나만 받아들이므로, 라우터는 변환된 업스트림 호출마다 두 값을 강제로 설정합니다. 스트리밍 클라이언트는 평소처럼 변환된 SSE 스트림을 받고, 비스트리밍 클라이언트(stream: false)에게는 라우터가 업스트림 SSE 스트림을 끝까지 읽어 단일 chat completion JSON 본문으로 합쳐서 응답합니다. Codex가 text/event-stream content type 없이 SSE 본문을 보내는 경우도 감지해 처리합니다. store가 항상 false이므로 OpenAI 쪽에는 아무것도 저장되지 않으며, 대화 상태는 여느 Chat Completions 클라이언트처럼 요청에 담아 보내야 합니다.
운영 시 유의사항¶
- 토큰은 로그, 트레이스, 메트릭 어디에도 노출되지 않습니다. 갱신 시 짧게 마스킹된 접두어만 로그에 남습니다.
- 갱신은
tokio::sync::Mutex로 single-flight 처리되므로, 만료 시점에 동시 진행 중인 요청들이 OAuth 공급자에 갱신 요청을 폭주시키지 않습니다. - 정적
api_key설정은 영향을 받지 않고 그대로 동작하며, OAuth는 백엔드별로auth.type: oauth블록을 통해 옵트인됩니다. continuum-router auth login --backend <name>을 다시 실행하면 토큰 저장소가 원자적으로 다시 기록되며, 라우터가 동작 중이어도 안전합니다.- 토큰 저장소는 읽을 때 형식을 관대하게 받아들입니다.
expires_at은 라우터가 기록하는 표준 형태인 epoch 초 외에 숫자 문자열과 RFC3339 날짜 문자열도 허용하므로, 다른 도구가 만든 토큰 파일도 수정 없이 로드됩니다. - 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 (선택 사항) |
모델 검색¶
집계 모델 서비스는 실행 중인 백엔드 카탈로그를 갱신하고 결과를 캐시합니다. 일반 HTTP 백엔드는 백엔드 URL에서 계산한 OpenAI 호환 모델 목록 경로를 요청하고 Unix 소켓 백엔드는 /v1/models를 사용합니다.
backends[].models가 비어 있지 않으면 검색 결과에 적용되는 허용 목록입니다.- 네이티브 Anthropic에는 모델 목록 엔드포인트가 없습니다.
models를 명시하세요. 실제 Anthropic과 Bedrock 백엔드는 검증 단계에서도 명시 목록을 요구합니다. - ChatGPT Codex 흐름이 아닌 OAuth 백엔드는 설정된
models목록을 사용합니다. Codex 흐름 백엔드는 인증된 Codex 모델 엔드포인트에서 계정에 표시되는 모델을 열거하고, 설정 목록이 비어 있지 않으면 허용 목록으로 적용합니다. - 실패한 백엔드는 해당 갱신 결과에서 빠집니다. 모든 백엔드가 실패하면 빈 모델 목록을 반환하고 캐시합니다. 일반 검색 실패에 사용하는 공급자별 하드코딩 폴백 카탈로그는 없습니다.
model_configs는 백엔드 메타데이터와 매칭 정보를 제공하지만 필수models명시 목록을 대체하지 않습니다.
즉시 갱신에는 POST /v1/models/refresh 또는 POST /admin/models/refresh를 사용하세요. 공개 갱신 경로에는 속도 제한이 적용됩니다.
Admin 클라이언트가 설정된 models: 허용 목록을 적용하기 전, 정확히 한 백엔드의 실시간 카탈로그가 필요하다면 POST /admin/backends/{name}/models/discover를 사용하세요. 이 엔드포인트는 읽기 전용이며 models:, 모델 alias, 헬스 상태, 서킷 브레이커 상태, 집계 모델 캐시를 바꾸지 않습니다. Codex OAuth 백엔드는 일반 집계와 동일한 계정/플랜 인식 검색 경로를 사용하고, 지원하지 않는 백엔드 타입은 설정 모델명으로 폴백하지 않고 기계가 읽을 수 있는 model_discovery_unsupported 오류를 반환합니다.
등록 전에 후보 URL과 키를 검증하고 초기 모델 선택기를 채워야 하는 관리 클라이언트는 POST /admin/backends/probe를 사용하세요. 후보는 등록된 검색과 같은 백엔드 타입, URL 구성, 인증 헤더, 파서, 정규화, 응답 크기 제한, 모델 개수 제한을 사용하지만 요청 범위 안에만 남고 설정, 기록, 풀, 헬스 상태, 서킷 상태, 캐시, 환경 파일, 토큰 저장소에 기록되지 않습니다. 설정 폴백은 models 또는 model_configs를 받고 백엔드별 모델 개수 제한을 그대로 적용합니다. 응답은 health.credential_status와 catalog.source를 분리하므로 인증이 필요 없는 헬스 성공을 자격 증명 유효성으로 오해하지 않습니다. 등록 전에는 수명 주기로 관리되는 OAuth 인증 전략을 사용할 수 없으므로 임시 OAuth 헬스 상태는 명시적으로 unknown입니다. 지원 여부는 /admin/capabilities의 transient_backend_probe_v1로 기계가 읽을 수 있게 노출됩니다.
네이티브 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
anthropic_fast_mode: false # opt-in: 적합한 모델에 fast mode 활성화 (기본값 false)
models:
- claude-fable-5
- claude-mythos-5 # 한정 출시(Project Glasswing); 승인된 액세스 필요
- claude-opus-4-8
- 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" |
finish_reason: "content_filter" |
stop_reason: "refusal" |
usage.prompt_tokens |
usage.input_tokens |
Anthropic이 stop_reason: "refusal"을 반환하면, 라우터는 이를 OpenAI 호환 finish_reason: "content_filter"로 매핑합니다. 업스트림 stop_details 객체(거부 카테고리 포함)는 응답 choice의 stop_details 필드에 그대로 전달되며, 없을 경우 생략됩니다.
요청 변환 예제:
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
}
}
도구 호출 id 정규화:
Anthropic 프로토콜에서 tool_use.id는 대화 전체 범위를 가지는데, 이 엔드포인트 뒤에 있는 일부 OpenAI 호환 백엔드는 응답 하나 안에서만 id 고유성을 보장합니다. 예를 들어 vLLM의 Kimi-K3 템플릿은 {tool_name}:{index} 카운터를 매 응답마다 다시 시작합니다. 라우터는 이미 대화 전체에서 고유하다고 알려지지 않은 아웃바운드 id를 crt_<nonce:12><base64url_nopad(raw_id)>로 다시 쓰고, 클라이언트가 그 id를 재전송하면 원본 바이트를 복원합니다. 그래서 설정을 따로 하지 않아도 턴을 넘나드는 tool_use/tool_result 짝짓기가 유지됩니다. 이미 대화 전체에서 고유한 id(toolu_, call_, fc_, chatcmpl-tool- 접두사)는 그대로 통과합니다.
Anthropic 확장 사고 모델¶
확장 사고를 지원하는 모델 (Claude Opus, Sonnet 4, Claude Opus 4.7, Claude Opus 4.8, Claude Sonnet 5)은 더 긴 응답 시간이 필요할 수 있습니다. 라우터는 자동으로:
- 사고 모델에 대해 더 높은 기본
max_tokens(16384) 설정 - 이 모델에 대해 확장 스트리밍 타임아웃 (600초) 사용
Claude Opus 4.7/4.8과 Mythos급 모델(Fable 5, Mythos 5)은 적응형 사고 API(thinking.type == "adaptive" + output_config.effort)를 필요로 하며, 레거시 budget_tokens 형식을 거부합니다. 라우터는 이 모델들에 대한 명시적 레거시 thinking.type == "enabled" 요청을 적응형 사고로 정규화합니다. 이 모델들은 temperature, top_p, top_k도 허용하지 않으며, 라우터가 자동으로 제거합니다. Fable 5와 Mythos 5는 명시적 thinking.type == "disabled"도 HTTP 400으로 거부하기 때문에 라우터가 claude-fable-5-*와 claude-mythos-5-*에 대해서는 thinking 파라미터를 통째로 생략합니다.
Claude Fable 5¶
Claude Fable 5(claude-fable-5-*, alias claude-fable-5-latest)는 Anthropic의 가장 강력한 모델로, Opus 4.8보다 한 단계 위에 있는 Mythos급 플래그십입니다. 주요 특성:
- 컨텍스트 윈도우: 입력 1M 토큰
- 최대 출력: 128K 토큰
- 가격: 입력/출력 100만 토큰당 \(10/\)50
- 사고: 적응형 전용(
thinking.type: "adaptive"+output_config.effort). 레거시enabled+budget_tokens형식은 HTTP 400을 반환하고, 명시적thinking.type: "disabled"도 HTTP 400을 반환합니다(라우터가 대신thinking파라미터를 생략). - Effort:
low,medium,high,max를 지원합니다(reasoning_effort: "xhigh"는max로 매핑). - 샘플링 파라미터:
temperature,top_p,top_k를 허용하지 않습니다. 라우터가 전달 전에 자동으로 제거합니다.
Claude Mythos 5(claude-mythos-5-*, alias claude-mythos-5-latest)는 Fable 5와 동일한 기반 모델에서 안전 분류기를 해제한 버전으로, Anthropic의 한정 출시 프로그램인 Project Glasswing을 통해서만 제공됩니다. 위의 모든 특성(컨텍스트 윈도우, 최대 출력, 가격, 사고, effort, 샘플링 파라미터 처리)을 그대로 공유하며, 라우터는 두 ID를 동일하게 취급합니다.
Claude Opus 4.8¶
Claude Opus 4.8(claude-opus-4-8-*, alias claude-opus-4-8-latest)은 Claude 4.8 계열의 플래그십 모델입니다. 주요 특성:
- 컨텍스트 윈도우: 입력 1M 토큰
- 최대 출력: 128K 토큰
- 가격: 입력/출력 100만 토큰당 \(5/\)25
- 학습 데이터 기준일: 2026년 1월
- 사고: 적응형 전용(
thinking.type: "adaptive"+output_config.effort). 레거시enabled+budget_tokens형식은 HTTP 400 반환 - 샘플링 파라미터:
temperature,top_p,top_k허용 안 됨. 라우터가 전달 전 자동으로 제거 - Effort 기본값:
high.reasoning_effort를 생략하거나auto로 설정하면output_config.effort를 명시하지 않는 한 Anthropic이higheffort를 적용
Anthropic Fast Mode¶
Fast mode는 Anthropic의 가속 추론 경로를 통해 적합한 Claude 모델의 지연 시간을 줄입니다. 백엔드별 opt-in 설정 플래그로 제어되며, 네이티브 Anthropic 백엔드에서만 적용됩니다(Bedrock, Vertex AI 제외).
Fast Mode 활성화¶
백엔드 설정에서 anthropic_fast_mode: true로 설정합니다:
backends:
- name: "anthropic-fast"
type: anthropic
api_key: "${CONTINUUM_ANTHROPIC_API_KEY}"
anthropic_fast_mode: true
models:
- claude-opus-4-8
- claude-opus-4-7
- claude-opus-4-6
anthropic_fast_mode가 활성화되면 라우터는 적합한 요청에 anthropic-beta: fast-mode-2026-02-01 헤더를 추가합니다.
적합한 모델¶
Fast mode는 네이티브 Anthropic 백엔드의 Opus 4.6, 4.7, 4.8 모델에 적용됩니다. Bedrock 또는 Vertex AI 백엔드 요청은 이 플래그가 설정되어 있어도 무시됩니다.
Speed 필드 패스스루¶
클라이언트도 요청 본문의 speed 필드로 fast mode를 명시적으로 요청할 수 있습니다:
speed 필드는 OpenAI 호환 경로에서 전달됩니다. 응답은 usage.speed에 결정된 속도를 반환합니다("fast" 또는 "standard").
가격
Fast mode 요청은 프리미엄 요금으로 청구됩니다. fast-mode 관련 비용은 Anthropic의 현재 가격 정책을 확인하세요.
백엔드 제한
Fast mode는 네이티브 Anthropic API 백엔드에서만 사용 가능합니다. Bedrock 및 Vertex AI 백엔드는 anthropic-beta: fast-mode-2026-02-01 헤더를 지원하지 않습니다.
대화 중간 시스템 메시지 (Claude Opus 4.8+)¶
Claude Opus 4.8부터 Anthropic API는 messages 배열 내 어느 위치에서나 role: "system" 메시지를 허용합니다. 라우터는 이를 지원하는 모델에서 이 기능을 활성화합니다.
모델 버전별 동작¶
| 모델 계열 | 대화 중간 role: "system" 지원 |
|---|---|
Claude Fable 5 / Mythos 5 (claude-fable-5-*, claude-mythos-5-*) |
배열 내 어느 위치에서든 유지 |
Claude Opus 4.8+ (claude-opus-4-8-*) |
배열 내 어느 위치에서든 유지 |
| Claude 4.7 이하 (및 모든 Sonnet/Haiku) | 평탄화: 첫 사용자 턴 이후의 시스템 메시지를 포함한 모든 시스템 메시지가 최상위 system 필드로 합쳐짐(배열 내 유지 안 됨) |
동작 방식¶
라우터가 첫 번째 사용자 턴 이후에 role: "system" 항목이 포함된 messages 배열을 수신하고, 대상 모델이 Opus 4.8 이상인 경우, 해당 항목은 messages 배열 내에 그대로 유지됩니다. 첫 번째 사용자 턴 이전의 선두 시스템/개발자 메시지는 여전히 최상위 system 필드를 채웁니다.
Claude 4.7 이하 모델에서는 기존 동작이 적용됩니다. 모든 시스템 메시지가 추출되어 최상위 system 필드로 합쳐지는데, 대화 중간의 시스템 메시지도 배열 내에 유지되지 않고 이 최상위 필드로 함께 합쳐집니다.
대화 중간 시스템 메시지가 포함된 요청 예시:
{
"model": "claude-opus-4-8",
"messages": [
{"role": "system", "content": "당신은 도움이 되는 어시스턴트입니다."},
{"role": "user", "content": "프랑스의 수도는 어디인가요?"},
{"role": "assistant", "content": "파리입니다."},
{"role": "system", "content": "이제 한국어로만 응답하세요."},
{"role": "user", "content": "독일의 수도는요?"}
]
}
Opus 4.8+ 에서는 라우터가 선두 시스템 메시지를 최상위 system 필드로 보내고, 대화 중간의 role: "system" 항목을 Anthropic으로 전송되는 messages 배열 내에 그대로 유지합니다.
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 백엔드용으로 자동 로드) |
Amazon Bedrock 백엔드¶
type: bedrock을 사용하면 라우터가 요청을 Amazon Bedrock을 통해 라우팅합니다. 진입점은 셋이 있습니다.
| 항목 | mantle |
runtime |
converse |
|---|---|---|---|
| URL | https://bedrock-mantle.{region}.api.aws/anthropic/v1/messages |
https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke[-with-response-stream] |
https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/converse[-stream] |
| 모델 | Anthropic Claude 전용 | Anthropic Claude 전용 | Bedrock의 모든 채팅 모델 (Claude, Nova, Llama, Mistral, Cohere, AI21 Jamba, DeepSeek 등) |
| 요청 본문 | 네이티브 Anthropic Messages API와 동일 | 같은 형식에 "anthropic_version": "bedrock-2023-05-31"을 추가하고, model은 URL 경로로 이동합니다. |
라우터가 OpenAI 형식에서 변환한 Converse 태그드 콘텐츠 블록 형식 |
| 인증 | Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK |
AWS SigV4 서명 | AWS SigV4 서명 |
| 스트리밍 | 표준 text/event-stream | AWS 이진 event-stream (application/vnd.amazon.eventstream) |
AWS 이진 event-stream (application/vnd.amazon.eventstream) |
| 헤더 | anthropic-version 없음, x-api-key 없음 |
Content-Type: application/json, Accept: application/json 또는 application/vnd.amazon.eventstream |
runtime과 동일 |
| Cargo 기능 | 항상 사용 가능 | 빌드 시 --features bedrock-sigv4 필요 |
빌드 시 --features bedrock-sigv4 필요 |
세 모드 모두 동일한 BackendTypeConfig::Bedrock 변형과 동일한 models: 설정 인터페이스를 공유합니다. 모드 구분은 클라이언트에 보이지 않으며, 클라이언트는 그대로 /v1/chat/completions로 호출하면 프록시가 본문과 헤더를 변환합니다. endpoint_type 설정값이 유일한 분기점이고, 기본값은 mantle입니다.
Mantle 설정¶
backends:
- name: bedrock
type: bedrock # 별칭: aws-bedrock, bedrock-anthropic, AmazonBedrock
endpoint_type: mantle # 기본값
region: us-east-1 # 필수; URL에 템플릿됨
api_key: ${AWS_BEARER_TOKEN_BEDROCK}
weight: 2
models:
- anthropic.claude-opus-4-7
- us.anthropic.claude-sonnet-4-5
- anthropic.claude-haiku-4-5
# global.anthropic.<family>은 AWS 최저가 크로스 리전 티어를 사용합니다.
# eu.<family>, jp.<family>, au.<family>은 해당 지역 내에서 라우팅됩니다.
리전 선택¶
라우터는 region을 https://bedrock-mantle.{region}.api.aws에 템플릿하여 업스트림 URL을 만듭니다. AWS가 리전을 정기적으로 추가하기 때문에 소문자로 된 비어 있지 않은 리전 식별자라면 모두 허용됩니다. us-east-1, us-west-2, eu-west-1, ap-northeast-1 등을 그대로 사용할 수 있습니다. 빈 값이나 대문자가 포함된 리전은 설정 로드 시 거부됩니다.
Bedrock 전용 포워드 프록시 환경에서는 백엔드에 명시적인 url: 필드를 지정해 리전 템플릿을 덮어쓸 수 있습니다. 일반 운영자는 url을 설정하지 않고 region만으로 URL을 결정하는 방식을 권장합니다.
모델 ID 형식¶
Bedrock 모델 식별자는 네 가지 형식을 가지며, 라우터는 모두 인식하고 업스트림으로 그대로 전달합니다:
| 형식 | 예시 | 동작 |
|---|---|---|
| 일반 Anthropic | anthropic.claude-opus-4-7 |
백엔드에 설정된 리전으로 라우팅됩니다. |
| 지리적 프로파일 | us.anthropic.claude-sonnet-4-5, eu.anthropic.claude-opus-4-7, jp.anthropic.claude-haiku-4-5, au.anthropic.claude-opus-4-7 |
AWS가 해당 지역 내에서 라우팅합니다. 데이터 레지던시 요건이 있을 때 선택합니다. |
| 글로벌 프로파일 | global.anthropic.claude-opus-4-7 |
AWS가 전 세계에서 가장 낮은 지연 리전을 선택합니다. 인퍼런스 프로파일의 최저가 티어입니다. |
| 전체 ARN | arn:aws:bedrock:us-east-1:123456789012:inference-profile/... |
고객 관리형 인퍼런스 프로파일이나 크로스 계정 참조에 사용합니다. |
모델 ID는 models: 목록에 명시적으로 나열합니다. 네이티브 Anthropic ID에서 Bedrock ID로 자동 매핑하는 기능은 의도적으로 구현하지 않았습니다. 지역 접두사는 실제 청구와 데이터 레지던시 결과를 수반하므로, 설정에서 명시적으로 표현되어야 합니다.
지원 기능¶
bedrock-mantle 경로는 네이티브 Anthropic 백엔드가 지원하는 모든 기능을 그대로 상속합니다:
- 스트리밍 SSE 응답 (Anthropic SSE → OpenAI SSE 변환기를 그대로 재사용)
- 시스템 프롬프트 (OpenAI
messages[role=system]에서 Anthropic의 별도system필드로 변환) - 도구 호출 및 도구 결과 왕복
- 비전 (base64 또는 URL 형식의 이미지 입력)
- Claude 4 시리즈 모델의 확장 사고, Opus 4.7의 adaptive thinking API 포함
인증¶
api_key: 필드에 Bedrock Bearer 토큰을 지정합니다. 운영 환경에서는 리터럴 문자열 대신 환경 변수를 사용합니다:
라우터는 모든 요청에 Authorization: Bearer ${AWS_BEARER_TOKEN_BEDROCK}를 전송하고, 클라이언트가 보낸 x-api-key와 anthropic-version 헤더를 포워딩 전에 제거합니다. Bedrock은 이 Anthropic 전용 헤더가 있으면 HTTP 400을 반환하기 때문입니다.
Runtime 설정 (bedrock-runtime)¶
runtime 모드는 AWS 네이티브 Invoke API인 https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke[-with-response-stream]로 요청을 보냅니다. 라우터가 각 요청에 SigV4 서명을 붙이고, 이진 application/vnd.amazon.eventstream 스트리밍 응답을 다시 OpenAI 형식 SSE로 디코딩해서 클라이언트에 전달합니다.
빌드 요건¶
runtime 경로는 bedrock-sigv4 Cargo 기능 뒤에 위치하며, 서명과 event-stream 파싱에 필요한 최소한의 AWS SDK 조각(aws-sigv4, aws-smithy-eventstream, aws-credential-types, aws-config)만 끌어옵니다. 기본 빌드에는 이 크레이트들이 포함되지 않습니다. runtime 백엔드를 사용하기 전에 기능을 활성화해서 빌드합니다.
기능 없이 endpoint_type: runtime을 설정하면 시작 시 재빌드 플래그를 안내하는 오류가 표시되고, 기본 mantle 경로는 기능 활성화 여부와 무관하게 동작합니다.
설정 예시¶
backends:
- name: bedrock-iam
type: bedrock
endpoint_type: runtime # SigV4 경로 선택
region: us-east-1
weight: 1
auth:
type: sigv4 # runtime에는 필수
# 아래 자격 증명 오버라이드 중 최대 하나만 지정합니다. 아무것도
# 지정하지 않으면 표준 AWS 체인(env, shared config, IMDS, IRSA,
# ECS)이 자격 증명을 해석합니다.
# aws:
# profile: my-bedrock-profile
# aws:
# access_key_id: ${AWS_ACCESS_KEY_ID}
# secret_access_key: ${AWS_SECRET_ACCESS_KEY}
# session_token: ${AWS_SESSION_TOKEN}
models:
- anthropic.claude-opus-4-7
- us.anthropic.claude-sonnet-4-5
- global.anthropic.claude-haiku-4-5
# 프로비저닝된 처리량이나 커스텀 인퍼런스 프로파일을 위한 전체 ARN도 지원합니다:
# - arn:aws:bedrock:us-east-1:123456789012:inference-profile/anthropic.claude-opus-4-7
자격 증명 해석 순서¶
runtime 백엔드는 다음 순서로 자격 증명을 해석합니다.
- 인라인 정적 자격 증명:
auth.aws.access_key_id+auth.aws.secret_access_key. STS에서 발급한 임시 자격 증명은 선택적session_token을 함께 지정합니다. - 이름 지정 프로파일:
auth.aws.profile로~/.aws/credentials와~/.aws/config에서 가져옵니다. - 표준 AWS 체인: 환경 변수(
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN), shared config, IMDS(EC2), IRSA / EKS pod identity, ECS task role 순으로 시도합니다.
요청마다 체인을 호출하지만, 내부 AWS 프로바이더가 자체 TTL로 캐시하므로 실제 비용은 보통 네트워크 호출이 아닌 해시맵 조회 수준입니다.
필요한 IAM 권한¶
사용하려는 모델 ARN에 대해 bedrock:InvokeModel과 bedrock:InvokeModelWithResponseStream 권한을 부여하는 정책을 연결합니다. 최소 예시는 다음과 같습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-opus-4-7",
"arn:aws:bedrock:us-east-1:*:inference-profile/us.anthropic.claude-sonnet-4-5"
]
}
]
}
지리적/글로벌 인퍼런스 프로파일을 사용하는 경우, 라우터는 프로파일 ID를 URL 경로로 그대로 사용하고 AWS가 서버 측에서 확장합니다. models:에서 인퍼런스 프로파일을 참조한다면 IAM 리소스 목록에 해당 인퍼런스 프로파일 ARN과 그 기반 파운데이션 모델 ARN을 둘 다 포함해야 합니다.
지리적/글로벌 프로파일¶
네 가지 모델 ID 형식은 runtime에서도 mantle과 동일하게 동작하지만, IAM 검증을 위해 위에서 언급한 자원 ARN을 정확히 지정해야 한다는 점만 다릅니다. 데이터 레지던시와 비용 요구사항에 따라 접두사를 선택합니다.
| 접두사 | AWS 라우팅 위치 | 청구 티어 |
|---|---|---|
anthropic.<family> |
백엔드에 설정된 리전. | 리전별 단가. |
us.<family>, eu.<family>, jp.<family>, au.<family> |
지정된 지리적 권역 내. | 리전별 단가; 단일 고정 리전 대비 지연이 낮아질 수 있습니다. |
global.<family> |
요청 시점에 AWS가 가장 빠르다고 판단한 리전. | 최저가 티어. |
| 전체 ARN | 인퍼런스 프로파일이 가리키는 위치. | ARN 기반 프로파일의 청구 기준. |
스트리밍 세부사항¶
runtime 스트리밍 응답은 application/vnd.amazon.eventstream 프레임으로 도착합니다. 각 chunk 프레임에는 base64로 인코딩된 JSON이 들어 있고, 디코딩하면 Anthropic SSE 이벤트(message_start, content_block_delta 등) 하나가 됩니다. 라우터가 이 흐름을 클라이언트가 기대하는 OpenAI 형식 SSE로 다시 변환해주기 때문에, mantle 경로에서 이미 동작하던 애플리케이션은 코드를 바꾸지 않고 그대로 사용할 수 있습니다. AWS 전용 오류 프레임(ThrottlingException, ValidationException 등)은 합성 event: error SSE 청크로 노출됩니다.
제한사항¶
- runtime 경로는 의도적으로 Invoke API에 범위를 한정합니다. 멀티 프로바이더 Bedrock 모델(Nova, Llama, Mistral 등)은 아래의
endpoint_type: converse모드로 접근합니다. - Bedrock Guardrails와
X-Amzn-Bedrock-GuardrailIdentifier헤더는 연결되어 있지 않습니다. - Bedrock Prompt management(
prompt-routerARN)는 범위 밖입니다. - 프로비저닝된 처리량과 Application Inference Profile ARN은 다른 ARN과 같은 URL 인코딩 경로로 동작해야 하지만, 자동 테스트는 파운데이션 모델 ARN과 인퍼런스 프로파일 ARN까지만 보장합니다.
Converse 설정 (멀티 프로바이더)¶
endpoint_type: converse 모드는 https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/converse[-stream]의 Converse API로 요청을 보내므로, 백엔드 하나로 Bedrock의 모든 채팅 모델을 하나의 통합 와이어 형식으로 사용할 수 있습니다. runtime 경로의 SigV4 서명, 자격 증명 체인, 이진 event-stream 디코딩을 그대로 재사용하기 때문에 동일한 bedrock-sigv4 Cargo 기능 뒤에 위치합니다.
backends:
- name: bedrock-converse
type: bedrock
endpoint_type: converse # 별칭: Converse, bedrock-converse
region: us-east-1
auth:
type: sigv4 # endpoint_type: runtime과 같은 인증 인터페이스
models:
# Anthropic (endpoint_type: runtime으로도 접근 가능; 여기 나열하면
# Converse를 통해 노출되어 통합 도구가 동작합니다)
- anthropic.claude-opus-4-7
- anthropic.claude-sonnet-4-5
# Amazon Nova
- amazon.nova-pro-v1:0
- amazon.nova-lite-v1:0
- amazon.nova-micro-v1:0
# Meta Llama
- meta.llama4-maverick-17b-instruct-v1:0
- meta.llama3-3-70b-instruct-v1:0
# Mistral
- mistral.mistral-large-2407-v1:0
# Cohere
- cohere.command-r-plus-v1:0
- cohere.command-r-v1:0
# AI21 Jamba
- ai21.jamba-1-5-large-v1:0
# DeepSeek
- deepseek.deepseek-r1-v1:0
클라이언트는 표준 OpenAI 형식의 /v1/chat/completions 요청을 그대로 보냅니다. 라우터가 메시지를 Converse 콘텐츠 블록으로 변환하고(시스템 프롬프트는 최상위 system 배열로 이동, 도구는 toolConfig / toolUse / toolResult로, 샘플링 파라미터는 inferenceConfig로 매핑), SigV4 서명을 붙이고, 응답(이진 converse-stream 이벤트 스트림 포함)을 다시 OpenAI 형식으로 변환합니다. 이미 Converse 네이티브 본문(inferenceConfig, toolConfig, 최상위 system 배열)을 담은 요청은 감지되어 최소한의 변형만 거쳐 전달되며 이중 변환은 일어나지 않습니다.
모델별 기능 매트릭스¶
Bedrock 모델들은 받아들이는 기능 범위가 크게 다릅니다. 라우터는 AWS 공개 데이터로 시딩한 하드코딩 기능 테이블을 유지하고, 대상 모델이 지원하지 않는 기능을 요구하는 요청을 서명이나 AWS 왕복 이전에 거부된 기능 이름을 담은 HTTP 4xx로 거절합니다. 라우터가 모르는 모델은 차단하지 않으며, 그 경우 AWS가 최종 판단합니다.
| 모델 패밀리 | 도구 | 비전 | 문서 | 프롬프트 캐싱 | 추론 |
|---|---|---|---|---|---|
anthropic.claude-* |
지원 | 지원 | 지원 | 지원 | 지원 |
amazon.nova-micro |
지원 | 미지원 | 미지원 | 지원 | 미지원 |
amazon.nova-lite / nova-pro |
지원 | 지원 | 지원 | 지원 | 미지원 |
meta.llama4-* |
지원 | 지원 | 미지원 | 미지원 | 미지원 |
meta.llama3-1-* / llama3-3-* |
지원 | 미지원 | 미지원 | 미지원 | 미지원 |
mistral.mistral-large-* |
지원 | 미지원 | 지원 | 미지원 | 미지원 |
cohere.command-r* |
지원 | 미지원 | 지원 | 미지원 | 미지원 |
ai21.jamba-1-5-* |
지원 | 미지원 | 지원 | 미지원 | 미지원 |
deepseek.* |
미지원 | 미지원 | 미지원 | 미지원 | 지원 |
나열된 모든 패밀리는 채팅, 스트리밍, 가드레일을 지원합니다. 지역 접두사(us., eu., apac., global. 등)와 전체 인퍼런스 프로파일 ARN은 조회 전에 정규화되므로 us.amazon.nova-pro-v1:0도 amazon.nova-pro 행에 매칭됩니다.
additionalModelRequestFields를 통한 벤더 전용 옵션¶
라우터가 모든 벤더 옵션을 개별 필드로 모델링하지는 않습니다. 벤더 전용 기능을 사용하려면 OpenAI 요청의 최상위 extra_body 객체에 넣거나(또는 additionalModelRequestFields를 직접 전송) 하면, 라우터가 해당 필드를 그대로 전달합니다. 예: Claude 확장 사고, Nova 추론 확장.
{
"model": "anthropic.claude-sonnet-4-5",
"messages": [{ "role": "user", "content": "..." }],
"extra_body": {
"thinking": { "type": "enabled", "budget_tokens": 2048 }
}
}
extra_body 안의 guardrailConfig 객체는 AWS가 기대하는 Converse 최상위 레벨로 올려집니다. 벤더 전용 응답 확장은 additionalModelResponseFields로 그대로 돌아옵니다.
비전 입력¶
이미지 콘텐츠 블록은 base64 데이터 URL을 직접 받습니다. 원격 http(s) 이미지 URL은 라우터가 가져와서(SSRF 검증, 크기 제한 적용) base64로 인라인합니다. Converse API에는 URL 이미지 소스가 없기 때문입니다.
Claude 라우팅 우선순위¶
전용 Claude 경로(endpoint_type: runtime)와 Converse 백엔드가 같은 Claude 모델을 나열하면 표준 로드 밸런서 규칙을 따르며 암묵적인 우선순위는 없습니다. Anthropic 네이티브 기능(anthropic_beta, 세밀한 사고 제어)이 필요한 운영자는 해당 모델을 runtime 백엔드로 명시적으로 라우팅하고, A/B 비교가 필요하면 가중치 로드 밸런서로 두 경로에 트래픽을 분산할 수 있습니다.
필요한 IAM 권한¶
Converse는 runtime 경로와 같은 bedrock:InvokeModel과 bedrock:InvokeModelWithResponseStream IAM 액션을 사용하므로, 위에 제시한 정책이 두 모드를 모두 커버합니다.
Converse 제한사항¶
- 채팅 모델 전용입니다. 이미지 생성(Stability, Titan Image), 임베딩(Titan Embeddings, Cohere Embed), 비디오 모델은 Converse API에 없으므로 범위 밖입니다.
- converse 백엔드는 네이티브 Anthropic Messages 엔드포인트(
/v1/messages)를 제공하지 않으며, 해당 요청은endpoint_type: runtime사용을 안내하는 오류로 거절됩니다. - 오디오 입력은 Converse API가 지원하지 않으므로 명확한 오류로 거절됩니다.
네이티브 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 소켓 백엔드에 대해 자동으로 헬스 체크를 수행합니다.
플랫폼 지원과 제한:
- 스트리밍: Server-Sent Events(SSE) 스트리밍은 Unix 소켓 백엔드에서도 동작하며, 채팅 완성과 Anthropic Messages 표면 모두 지원됩니다.
- Windows: Windows 10 1809+ (빌드 17063+)에서는
afunix.sys커널 드라이버를 통해 AF_UNIX 소켓이 지원되고, 이전 버전의 Windows에서는 연결 시점에 명확한 오류를 반환합니다. - 최대 응답 크기: 메모리 고갈을 방지하기 위해 응답 본문은 기본적으로 100MB로 제한됩니다.
문제 해결:
| 오류 | 원인 | 해결 방법 |
|---|---|---|
| "Socket file not found" | 서버가 실행되지 않음 | 백엔드 서버 시작 |
| "Permission denied" | 파일 권한 | chmod 660 socket.sock |
| "Connection timeout" | 서버가 연결을 수락하지 않음 | 서버가 리스닝 중인지 확인 |
| "Response body exceeds maximum size" | 응답이 너무 큼 | max_response_size 증가 또는 TCP 백엔드로 스트리밍 사용 |