서버 & 백엔드¶
서버 섹션¶
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 |
bedrock |
Amazon Bedrock Claude (mantle + runtime; runtime은 --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 (선택 사항) |
모델 자동 검색:
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
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
}
}
Anthropic 확장 사고 모델¶
확장 사고를 지원하는 모델 (Claude Opus, Sonnet 4, Claude Opus 4.7, Claude Opus 4.8)은 더 긴 응답 시간이 필요할 수 있습니다. 라우터는 자동으로:
- 사고 모델에 대해 더 높은 기본
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 Claude 백엔드¶
type: bedrock을 사용하면 라우터가 Claude 요청을 Amazon Bedrock을 통해 라우팅합니다. 진입점은 둘이 있습니다.
| 항목 | bedrock-mantle (Phase 1) | bedrock-runtime (Phase 2) |
|---|---|---|
| URL | https://bedrock-mantle.{region}.api.aws/anthropic/v1/messages |
https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke[-with-response-stream] |
| 요청 본문 | 네이티브 Anthropic Messages API와 동일 | 같은 형식에 "anthropic_version": "bedrock-2023-05-31"을 추가하고, model은 URL 경로로 이동합니다. |
| 인증 | Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK |
AWS SigV4 서명 |
| 스트리밍 | 표준 text/event-stream | AWS 이진 event-stream (application/vnd.amazon.eventstream) |
| 헤더 | anthropic-version 없음, x-api-key 없음 |
Content-Type: application/json, Accept: application/json 또는 application/vnd.amazon.eventstream |
| Cargo 기능 | 항상 사용 가능 | 빌드 시 --features bedrock-sigv4 필요 |
두 모드 모두 동일한 BackendTypeConfig::Bedrock 변형과 동일한 models: 설정 인터페이스를 공유합니다. mantle과 runtime의 구분은 클라이언트에 보이지 않으며, 클라이언트는 그대로 /v1/chat/completions나 /anthropic/v1/messages로 호출하면 프록시가 본문과 헤더를 변환합니다. endpoint_type 설정값이 유일한 분기점이고, 기본값은 mantle입니다.
Phase 1 설정¶
backends:
- name: bedrock
type: bedrock # 별칭: aws-bedrock, bedrock-anthropic, AmazonBedrock
endpoint_type: mantle # 기본값; Phase 1 구현
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을 반환하기 때문입니다.
Phase 2 설정 (bedrock-runtime)¶
Phase 2는 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을 설정하면 시작 시 재빌드 플래그를 안내하는 오류가 표시되고, 기본 Phase 1 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 청크로 노출됩니다.
제한사항¶
Converse/ConverseStreamAPI는 사용하지 않습니다. runtime 경로는 의도적으로 Invoke API에 범위를 한정하므로, Converse로만 제공되는 멀티 프로바이더 Bedrock 모델(Nova, Llama, Mistral 등)에는 접근할 수 없습니다.- Bedrock Guardrails와
X-Amzn-Bedrock-GuardrailIdentifier헤더는 연결되어 있지 않습니다. - Bedrock Prompt management(
prompt-routerARN)는 범위 밖입니다. - 프로비저닝된 처리량과 Application Inference Profile ARN은 다른 ARN과 같은 URL 인코딩 경로로 동작해야 하지만, 자동 테스트는 파운데이션 모델 ARN과 인퍼런스 프로파일 ARN까지만 보장합니다.
네이티브 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 백엔드로 스트리밍 사용 |