API 레퍼런스¶
Continuum Router는 OpenAI 호환 API를 제공하며, 모니터링과 관리를 위한 추가 관리 엔드포인트도 함께 노출합니다. 이 문서는 사용 가능한 모든 엔드포인트, 요청/응답 형식, 오류 처리에 대한 상세 정보를 담고 있습니다.
개요¶
기본 URL¶
Content Type¶
별도로 명시되지 않는 한 모든 요청과 응답은 application/json을 사용합니다.
OpenAI 호환성¶
Continuum Router는 다음 OpenAI 호환 API 표면을 제공합니다:
- 스트리밍을 포함한 채팅 완료
- 텍스트 완료
- 임베딩
- 리랭킹 (Cohere 호환 /v1/rerank)
- 희소 임베딩 (TEI/Jina 호환 /embed_sparse)
- 이미지 생성
- 이미지 편집/인페인팅
- 이미지 변형
- Files API (업로드, 목록, 조회, 삭제)
- 채팅 완료에서의 파일 해결 (image_file 참조)
- 모델 목록
- 오류 응답 형식
인증¶
Continuum Router는 설정 가능한 적용 모드를 통한 API 키 인증을 지원합니다.
인증 모드¶
라우터는 API 엔드포인트에 대해 두 가지 인증 모드를 지원합니다:
| 모드 | 동작 |
|---|---|
permissive (기본값) |
API 키 없는 요청이 허용됩니다. 유효한 API 키가 있는 요청은 인증되어 사용자별 기능에 접근할 수 있습니다. |
blocking |
인증된 요청만 처리됩니다. 유효한 API 키가 없는 요청은 401 Unauthorized를 받습니다. |
설정¶
api_keys:
# 인증 모드: "permissive" (기본값) 또는 "blocking"
mode: blocking
# API 키 정의
api_keys:
- key: "${API_KEY_1}"
id: "key-production-1"
user_id: "user-admin"
organization_id: "org-main"
scopes: [read, write, files, admin]
보호된 엔드포인트 (mode가 blocking일 때)¶
API 키 미들웨어는 코어 API 라우터의 모든 경로를 보호합니다: /v1/models*, /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/responses*, /v1/images/*, /v1/rerank, /embed_sparse, /anthropic/v1/messages*, /anthropic/v1/models, 기능 게이트가 적용된 /v1/batches*.
참고: 헬스 및 버전 엔드포인트 (/health, /version)는 항상 인증 없이 접근 가능합니다. Admin, Files, Metrics 엔드포인트는 별도의 인증 메커니즘을 가집니다.
인증된 요청 만들기¶
보호된 모든 경로는 API 키를 Authorization: Bearer <api_key> 또는 Anthropic 고유의 x-api-key: <api_key> 두 헤더 형식 중 하나로 받습니다. 두 형식 모두 /anthropic/* 엔드포인트뿐 아니라 모든 경로에서 동작하므로 ANTHROPIC_API_KEY만 설정하고 이를 x-api-key로 보내는 Anthropic SDK와 Claude Code도 OpenAI 방식 클라이언트와 동일하게 인증됩니다. 요청에 두 헤더가 모두 있으면 Authorization: Bearer가 우선합니다.
POST /v1/chat/completions HTTP/1.1
Authorization: Bearer sk-your-api-key
Content-Type: application/json
{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello"}]
}
POST /v1/chat/completions HTTP/1.1
x-api-key: sk-your-api-key
Content-Type: application/json
{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello"}]
}
인증 오류¶
인증 실패 시 API는 다음을 반환합니다:
{
"error": {
"message": "Missing or invalid API key. Expected Authorization: Bearer <api_key> or x-api-key",
"type": "authentication_error",
"code": "invalid_api_key"
}
}
상태 코드:
401 Unauthorized: API 키가 없거나 유효하지 않음
Core API 엔드포인트¶
헬스 체크¶
라우터 서비스의 헬스 상태를 확인합니다.
응답:
상태 코드:
200: 서비스가 정상
버전¶
실행 중인 라우터 버전을 반환합니다.
응답:
상태 코드:
200: 항상 반환되며 인증이 필요하지 않음
모델 목록¶
모든 정상 백엔드에서 사용 가능한 모든 모델을 조회합니다.
응답:
{
"object": "list",
"data": [
{
"id": "gpt-4",
"object": "model",
"created": 1677610602,
"owned_by": "openai-compatible",
"permission": [],
"root": "gpt-4",
"parent": null
},
{
"id": "llama2:7b",
"object": "model",
"created": 1677610602,
"owned_by": "local-ollama",
"permission": [],
"root": "llama2:7b",
"parent": null
}
]
}
상태 코드:
200: 모델이 성공적으로 조회됨503: 모든 백엔드가 비정상
기능:
- 모델 집계: 모든 정상 백엔드의 모델을 결합
- 중복 제거: 백엔드 간 중복 모델 제거
- 캐싱: 내부 60초 하드 TTL, stale-while-revalidate 및 요청 병합 사용
- 헬스 인식: 정상 백엔드의 모델만 포함
백엔드 모델 목록 프록시¶
모든 백엔드를 집계하지 않고 선택된 적격 백엔드 하나의 원본 /v1/models 응답을 반환합니다. 이 호환 엔드포인트에는 일반 백엔드 선택, 재시도, 상태 및 호출자 백엔드 가시성 규칙이 적용됩니다.
라우터가 중복 제거한 통합 카탈로그에는 GET /v1/models를 사용하십시오. 단일 백엔드의 제공자 네이티브 모델 목록 페이로드가 필요한 클라이언트에만 이 엔드포인트를 사용하십시오.
상태 코드:
200: 선택된 백엔드 응답 반환502: 백엔드 연결 또는 게이트웨이 오류503: 적격 정상 백엔드 없음
모델 강제 새로고침¶
모델 집계 캐시를 즉시 무효화하고 구성된 모든 백엔드에서 동기적으로 다시 집계합니다. GET /v1/models와 동일한 OpenAI 호환 모델 목록 형태를 반환하므로 호출자가 응답을 바로 사용할 수 있습니다.
이 엔드포인트는 데스크톱 클라이언트의 대화형 '새로고침' 동작 (예: backend.ai-go의 '모델 새로고침' 버튼)을 위해 설계되었습니다. stale-while-revalidate와 소프트 TTL 윈도우를 우회하여, 응답하기 전에 모든 백엔드에서 업스트림 가져오기를 트리거합니다.
응답:
{
"object": "list",
"data": [
{
"id": "gpt-4o",
"object": "model",
"created": 1677610602,
"owned_by": "openai-compatible"
}
]
}
상태 코드:
200: 캐시가 비워지고 새로 가져온 모델 목록이 반환됨429 Too Many Requests: 강제 새로고침 속도 제한 초과 (5초 버스트 윈도우당 3 요청, 분당 12 요청)
속도 제한:
이 엔드포인트는 호출할 때마다 구성된 모든 백엔드에서 업스트림 가져오기가 트리거되기 때문에 일반 GET /v1/models보다 더 엄격한 예산을 적용합니다. 기본 제한은 5초 윈도우당 3 요청, 분당 12 요청입니다. 유효한 API 키는 API 키 ID를 기준으로 별도의 버킷을 받습니다. 익명 호출자와 유효하지 않은 bearer 토큰을 사용한 요청은 하나의 전역 익명 버킷을 공유하므로, Authorization, X-Forwarded-For, X-Real-IP 헤더를 위조해도 강제 새로고침 예산을 우회할 수 없습니다. 제한을 초과한 클라이언트는 429를 받으며 재시도 전에 기다려야 합니다.
캐시 정책:
모델 목록 캐시는 현재 내부 고정 기본값인 60초 하드 TTL, 80% 소프트 TTL, stale-while-revalidate, 요청 병합, 제한된 빈 결과 백오프를 사용합니다. 이 값과 강제 새로고침 사용 여부는 공개 설정 스키마의 필드가 아닙니다.
단일 모델 조회¶
특정 모델의 정보를 가용성 상태, 그리고 선택적으로 제공되는 풍부한 메타데이터와 함께 조회합니다.
경로 파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 모델 식별자 (예: "gpt-4", "llama2:7b") |
응답 (기본):
{
"id": "gpt-4",
"object": "model",
"created": 1677610602,
"owned_by": "openai",
"available": true
}
응답 (확장 메타데이터 포함):
model-metadata.yaml에 모델 메타데이터가 있으면 응답에 추가 필드가 포함됩니다:
{
"id": "gpt-4o",
"object": "model",
"created": 1704067200,
"owned_by": "openai",
"available": true,
"supported_methods": ["chat.completions"],
"features": ["chat", "vision", "audio", "code"],
"max_tokens": 16384,
"metadata": {
"display_name": "GPT-4o",
"developer": "OpenAI",
"summary": "Multimodal optimized model with text, image, and audio capabilities.",
"knowledge_cutoff": "2023-10",
"relative_speed": 4,
"pricing": {
"input_tokens": 2.50,
"output_tokens": 10.0
},
"limits": {
"context_window": 128000,
"max_output": 16384
}
}
}
응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
id |
string | 모델 식별자 |
object |
string | 객체 타입 (항상 "model") |
created |
integer | 모델 생성 시점의 Unix 타임스탬프 |
owned_by |
string | 모델을 소유/제공하는 조직 |
available |
boolean | 모델을 현재 사용할 수 있는지 여부 (최소 하나의 정상 백엔드에서 제공되면 true) |
supported_methods |
array | (선택) 이 모델이 지원하는 API 메서드 (예: ["chat.completions"], ["images.generations"]) |
features |
array | (선택) 모델 기능 (예: ["chat", "vision", "function_calling"]) |
max_tokens |
integer | (선택) 이 모델의 최대 출력 토큰 수 |
metadata |
object | (선택) 상세한 모델 정보를 담은 풍부한 메타데이터 객체 |
메타데이터 객체 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
display_name |
string | 사람이 읽기 쉬운 모델 표시 이름 |
developer |
string | 모델을 만든 개발사 또는 조직 |
summary |
string | 모델의 기능을 설명하는 간단한 요약 |
knowledge_cutoff |
string | 지식 컷오프 날짜 (예: "2025-01") |
relative_speed |
integer | 상대 속도 지표 (1-5, 1이 가장 느리고 5가 가장 빠름) |
pricing |
object | 가격 정보 (1K 토큰당 input_tokens, output_tokens) |
limits |
object | 모델 제한 (토큰 단위의 context_window, max_output) |
지원 메서드 매핑:
supported_methods 필드는 모델 기능에서 도출됩니다:
| 기능 | API 메서드 |
|---|---|
chat, vision, code, reasoning, audio, video, function_calling, tool |
chat.completions |
embedding |
embeddings |
image_generation |
images.generations |
image_edit |
images.edits |
image_variation |
images.variations |
moderation |
moderations |
상태 코드:
200: 모델이 조회되어 정보가 반환됨404: 구성된 모든 백엔드에 해당 모델이 존재하지 않음
기능:
- OpenAI 호환: 응답 형식이 OpenAI API와 일치하며 추가 확장 필드를 포함
- 헬스 인식:
available필드는 실시간 백엔드 헬스 상태를 반영 - 프라이버시: 내부 백엔드 정보를 노출하지 않음
- 선택적 메타데이터: 확장 필드는 메타데이터가 있을 때만 존재
- 풍부한 메타데이터: 클라이언트 측 모델 선택에 참고할 수 있도록 표시 이름, 개발사, 기능, 컨텍스트 윈도우 제한을 포함
모델 가용성 알고리즘:
available 필드는 다음 알고리즘으로 결정됩니다:
| 조건 | 결과 |
|---|---|
| 헬스 체커 활성화 + 모델을 제공하는 백엔드 중 최소 하나가 정상 | true |
| 헬스 체커 활성화 + 모든 백엔드가 비정상 | false |
| 헬스 체커 비활성화 + 모델의 백엔드 목록이 비어 있지 않음 | true |
| 헬스 체커 비활성화 + 모델의 백엔드 목록이 비어 있음 | false |
알고리즘은 단락 평가를 사용합니다: 첫 번째 정상 백엔드를 찾는 즉시 true를 반환하므로 나머지 백엔드에 대한 불필요한 헬스 체크를 피합니다. 백엔드가 대체로 정상일 때 성능이 최적화됩니다.
성능 최적화:
- 빠른 경로: 모델 캐시가 유효하면 조회는 O(n) (n = 모델 수)
- 느린 경로: 캐시가 비어 있거나 만료되었으면 캐시 스탬피드를 방지하는 singleflight 보호와 함께 일회성 집계를 트리거
- Stale-While-Revalidate: 캐시가 백그라운드에서 갱신되는 동안 오래된 데이터를 제공
요청 예시:
응답 예시 (전체 메타데이터와 함께 모델 사용 가능):
{
"id": "gpt-4o",
"object": "model",
"created": 1704067200,
"owned_by": "openai",
"available": true,
"supported_methods": ["chat.completions"],
"features": ["chat", "vision", "audio", "code"],
"max_tokens": 16384,
"metadata": {
"display_name": "GPT-4o",
"developer": "OpenAI",
"summary": "Multimodal optimized model with text, image, and audio capabilities; faster and cheaper than GPT-4 Turbo.",
"knowledge_cutoff": "2023-10",
"relative_speed": 4,
"pricing": {
"input_tokens": 2.50,
"output_tokens": 10.0
},
"limits": {
"context_window": 128000,
"max_output": 16384
}
}
}
응답 예시 (확장 메타데이터가 없는 모델):
model-metadata.yaml에 메타데이터가 없는 모델은 기본 필드만 반환됩니다:
{
"id": "custom-model",
"object": "model",
"created": 1677610602,
"owned_by": "local",
"available": true
}
응답 예시 (모델 존재하지만 사용 불가):
{
"id": "gpt-4o",
"object": "model",
"created": 1704067200,
"owned_by": "openai",
"available": false,
"supported_methods": ["chat.completions"],
"features": ["chat", "vision", "audio", "code"],
"max_tokens": 16384,
"metadata": {
"display_name": "GPT-4o",
"developer": "OpenAI"
}
}
채팅 완료¶
OpenAI Chat API 형식을 사용하여 채팅 완료를 생성합니다.
요청 본문:
{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Explain quantum computing in simple terms."
}
],
"temperature": 0.7,
"max_tokens": 150,
"top_p": 1.0,
"frequency_penalty": 0.0,
"presence_penalty": 0.0,
"stream": false,
"stop": null,
"logit_bias": {},
"user": "user123"
}
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 모델 식별자 (최소 하나의 정상 백엔드에서 사용 가능해야 함) |
messages |
array | 예 | role과 content를 포함하는 메시지 객체 배열 |
temperature |
number | 아니오 | 샘플링 온도 (0.0 ~ 2.0, 기본값: 1.0) |
max_tokens |
integer | 아니오 | 생성할 최대 토큰 수 |
top_p |
number | 아니오 | 핵 샘플링 파라미터 (0.0 ~ 1.0) |
frequency_penalty |
number | 아니오 | 빈도 패널티 (-2.0 ~ 2.0) |
presence_penalty |
number | 아니오 | 존재 패널티 (-2.0 ~ 2.0) |
stream |
boolean | 아니오 | 스트리밍 응답 활성화 (기본값: false) |
stop |
string/array | 아니오 | 중지 시퀀스 |
logit_bias |
object | 아니오 | 토큰 로짓 바이어스 |
user |
string | 아니오 | 추적을 위한 사용자 식별자 |
reasoning_effort |
string | 아니오 | 추론 가능 모델의 추론 노력 수준. 유효한 값은 백엔드와 모델마다 다릅니다(아래 참조). OpenAI 값은 모델별로 정확히 검증하며 조용히 다운그레이드하지 않습니다. auto는 Anthropic 적응형 사고용입니다. |
reasoning |
object | 아니오 | 대체 중첩 형식: {"effort": "high"}. 자동으로 reasoning_effort로 정규화됨. |
백엔드별 유효한 reasoning_effort 값:
- OpenAI O-series (o1, o3, o4-mini):
low,medium,high - OpenAI GPT-5 (mini/nano 포함):
minimal,low,medium,high - OpenAI GPT-5.1:
none,low,medium,high - OpenAI GPT-5.2 ~ GPT-5.5:
none,low,medium,high,xhigh - OpenAI GPT-5.6:
none,low,medium,high,xhigh,max - OpenAI GPT-5 Pro (Responses API 전용):
high - OpenAI GPT-5.⅖.⅘.5 Pro (Responses API 전용):
medium,high,xhigh - Anthropic Claude (Opus 4.7, Opus 4.6, Sonnet 4.6 등):
none,minimal,auto,low,medium,high,xhigh*auto는 적응형 사고({"type": "adaptive"})로 매핑됩니다. 모델이 사고의 시점과 정도를 동적으로 결정합니다.- *
xhigh는 Opus 4.6/4.7에서max로 매핑되고, 그 외 모델에서는high로 다운그레이드됩니다. Opus 4.7에서는temperature,top_p,top_k가 제거됩니다.
- Gemini (2.0-flash, 2.5-flash, 2.5-pro, 3-flash, 3-pro):
none,auto*,minimal,low,medium,high- *
none은 Flash 모델 (2.0-flash, 2.5-flash, 3-flash)에서만 지원됩니다. - **
auto는medium으로 자동 다운그레이드됩니다.
- *
OpenAI 모델은 스트리밍과 비스트리밍 요청에 동일한 모델별 검증을 적용합니다. 지원하지 않는 값은 400 invalid_request_error로 거절하며, auto, xhigh, max를 다른 effort로 바꾸지 않습니다. 다른 공급자는 위에 설명한 공급자별 변환 동작을 유지합니다.
응답 (비스트리밍):
{
"id": "chatcmpl-123456789",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Quantum computing is a revolutionary computing paradigm that harnesses quantum mechanical phenomena..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 150,
"total_tokens": 175
}
}
응답 (스트리밍):
stream: true일 때 응답은 Server-Sent Events (SSE)를 사용합니다:
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"delta":{"content":"Quantum"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"delta":{"content":" computing"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
data: [DONE]
상태 코드:
200: 완료가 성공적으로 생성됨400: 유효하지 않은 요청 형식 또는 파라미터404: 정상 백엔드에서 모델을 찾을 수 없음502: 백엔드 연결 오류504: 요청 타임아웃503: 모든 백엔드가 비정상
기능:
- 모델 기반 라우팅: 요청된 모델을 제공하는 백엔드로 자동 라우팅
- 로드 밸런싱: 정상 백엔드 간 로드 분산
- 스트리밍 지원: SSE를 통한 실시간 응답 스트리밍
- 오류 복구: 일시적 실패 시 자동 재시도
- 요청 중복 제거: 동일한 요청의 중복 처리 방지
- 추론 파라미터 정규화: 중첩된
reasoning형식을 평면reasoning_effort형식으로 자동 정규화; 지원하지 않는 모델에서는 추론 파라미터 제거 - 투명한 Responses-API 라우팅: 요청된 모델이
responses_only: true로 표시된 경우(예:gpt-5-pro,gpt-5.2-pro,gpt-5.4-pro,gpt-5.5-pro), 라우터가 업스트림/v1/responses엔드포인트로 투명하게 dispatch한 뒤 응답을 strict 모드의chat.completion(스트리밍은chat.completion.chunk) 봉투로 다시 변환합니다. 클라이언트는/v1/chat/completions를 그대로 사용하면 됩니다./v1/responses는 OpenAI와 Azure OpenAI 백엔드만 제공하며, 다른 백엔드 유형과 짝지어지면 dispatch 전에400 invalid_request_error가 반환됩니다. 전체 모델 목록과 설정은 Responses-API 전용 모델을 참조하세요.
Responses API¶
OpenAI의 Responses API 형식을 사용하여 응답을 생성합니다. 이 엔드포인트는 Chat Completions에 대한 대안적 인터페이스를 제공하며, 내부적으로 요청을 Chat Completions 형식으로 변환하여 백엔드에서 처리합니다.
요청 본문:
{
"model": "gpt-4o",
"input": "양자 컴퓨팅을 간단한 용어로 설명해주세요.",
"instructions": "당신은 복잡한 주제를 간단하게 설명하는 유용한 어시스턴트입니다.",
"max_output_tokens": 1000,
"temperature": 0.7,
"reasoning": {
"effort": "high"
}
}
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 모델 식별자 (최소 하나의 정상 백엔드에서 사용 가능해야 함) |
input |
string/array | 예 | 입력 텍스트 또는 입력 항목 배열 |
instructions |
string | 아니오 | 모델에 대한 시스템 지시사항 (시스템 메시지로 변환됨) |
max_output_tokens |
integer | 아니오 | 생성할 최대 토큰 수 |
temperature |
number | 아니오 | 샘플링 온도 (0.0 ~ 2.0) |
top_p |
number | 아니오 | 핵 샘플링 파라미터 (0.0 ~ 1.0) |
stream |
boolean | 아니오 | 스트리밍 응답 활성화 (기본값: false) |
store |
boolean | 아니오 | 응답을 세션 저장소에 보존할지 여부 (기본값: true). 저장하지 않을 임시(ephemeral) 요청은 false로 보냅니다 (Codex가 사용) |
include_reasoning |
boolean | 아니오 | 응답에 추론 콘텐츠 포함 |
reasoning |
object | 아니오 | 중첩 형식의 추론 설정 (아래 참조) |
tools |
array | 아니오 | 모델에서 사용 가능한 도구 목록 (플랫 형식) |
tool_choice |
string/object | 아니오 | 도구 사용 제어 |
previous_response_id |
string | 아니오 | 다중 턴 대화를 위한 이전 응답 참조 |
도구 정의 형식:
Responses API는 함수 속성이 type 필드와 같은 레벨에 있는 플랫 도구 형식을 사용합니다. 이는 중첩된 function 객체를 사용하는 Chat Completions API와 다릅니다.
{
"type": "function",
"name": "get_weather",
"description": "위치의 날씨 조회",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "도시 이름"}
},
"required": ["location"]
},
"strict": true
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
type |
string | 예 | "function"이어야 함 |
name |
string | 예 | 함수 이름 |
description |
string | 아니오 | 함수가 수행하는 작업 설명 |
parameters |
object | 아니오 | 함수 파라미터에 대한 JSON Schema |
strict |
boolean | 아니오 | 엄격한 파라미터 검증 활성화 |
기타 지원되는 도구 유형: code_interpreter, file_search, web_search, computer_use
멀티모달 입력 유형:
input이 항목 배열인 경우 각 항목은 멀티모달 콘텐츠 파트를 포함하는 메시지가 될 수 있습니다. 다음 콘텐츠 파트 유형이 지원됩니다:
| 유형 | 설명 |
|---|---|
text |
일반 텍스트 콘텐츠 |
input_text |
사용자 메시지용 텍스트 콘텐츠 (Responses API 대체 형식) |
output_text |
어시스턴트 메시지용 텍스트 콘텐츠. input 항목에서 어시스턴트가 생성한 콘텐츠를 사용자 입력(input_text)과 구분합니다 |
input_file |
파일 콘텐츠 (PDF, 이미지 또는 기타 파일) |
input_image |
이미지 콘텐츠 |
image_url |
URL 또는 base64 데이터의 이미지 |
메시지 역할:
각 메시지 항목은 다섯 가지 역할 중 하나를 가집니다. 라우터는 /v1/responses에서 다섯 가지를 모두 받아들이고, 백엔드별로 변환합니다.
| 역할 | 용도 | 백엔드별 매핑 |
|---|---|---|
developer |
OpenAI 추론 모델(GPT-5.x, o-시리즈)용 애플리케이션 개발자 지시. 명령 우선순위가 가장 높고, 해당 모델에서는 system을 대체합니다. |
OpenAI는 developer를 그대로 전달하고, Anthropic은 system 매개변수에 병합합니다(여러 개일 경우 \n\n로 이어붙임). Gemini는 system_instruction에 병합하고, Ollama / vLLM / LocalAI / LM Studio에서는 system으로 매핑합니다. |
system |
기존 애플리케이션 지시. | 기존과 동일하게 동작합니다. |
user |
사용자 입력. | 기존과 동일하게 동작합니다. |
assistant |
직전 모델 출력. | 기존과 동일하게 동작합니다. |
tool |
모델에 반환되는 도구 호출 결과. | 기존과 동일하게 동작합니다. |
developer와 system 메시지가 여러 개 들어오면 라우터는 도착 순서를 그대로 유지합니다. 대상 백엔드(Anthropic, Gemini)가 단일 지시 슬롯만 허용하는 경우, 빈 줄 구분자로 이어붙여서 어떤 지시 텍스트도 누락되지 않도록 합니다.
아이템 참조:
{"type": "item_reference", "id": "item_..."} 형태의 입력 항목은 이전에 저장된 응답의 출력 아이템을 가리킵니다. OpenAI와 Vercel AI SDK가 기본값으로 다단계 도구 왕복을 제출하는 방식이 바로 이것으로, 2단계 요청이 함수 호출 아이템 전체를 다시 보내는 대신 1단계에서 생성된 아이템을 참조합니다.
- OpenAI / Azure OpenAI 패스스루 경로에서는 참조가 변경 없이 전달되고 업스트림이 해소합니다.
- 변환 경로(Anthropic, Gemini, chat completions 기반 라우팅)에서는 라우터가 디스패치 전에 세션 저장소에서 각 참조를 해소해, 참조가 가리키는 인라인
function_call,message,function_call_output아이템으로 다시 씁니다. 참조는call_id기준으로 중복이 제거됩니다(먼저 온 것이 유지됨). - 스트리밍 응답도 똑같이 참여합니다. 스트림이 완료되면 누적된 응답이 저장되므로, 후속 요청이 스트리밍 단계에서 생성된 아이템을 참조할 수 있습니다.
- 해소할 수 없는 참조는 해당 id를 명시한 HTTP 400을 반환하며, 요청 하나가 가질 수 있는 참조는 최대 256개입니다.
Input File 형식:
input_file 콘텐츠 파트는 세 가지 입력 방법을 지원합니다:
{
"type": "input_file",
"filename": "document.pdf",
"file_data": "data:application/pdf;base64,JVBERi0xLjQ..."
}
| 필드 | 유형 | 설명 |
|---|---|---|
filename |
string | 파일의 선택적 파일명 |
file_data |
string | Base64 데이터 URL (예: data:application/pdf;base64,...) |
file_url |
string | 파일의 외부 URL (SSRF 검증됨) |
file_id |
string | Files API를 통해 업로드된 파일 참조 |
Input Image 형식:
| 필드 | 유형 | 설명 |
|---|---|---|
image_url |
string | 이미지 URL 또는 base64 데이터 URL |
file_id |
string | Files API를 통해 업로드된 이미지 파일 참조 |
detail |
string | 이미지 상세 수준: low, high 또는 auto (기본값) |
image_url과 file_id 중 하나는 반드시 있어야 합니다. 둘 다 없으면 400 오류가 반환됩니다. file_id를 사용하면 백엔드로 요청을 보내기 전에 파일이 인라인 base64 데이터 URL로 변환되며, input_file + file_id 경로와 동일하게 파일 소유권이 검증되고 10MB 크기 제한이 적용됩니다.
멀티모달 요청 예시:
curl -X POST http://localhost:8080/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"input": [
{
"type": "message",
"role": "user",
"content": [
{"type": "input_text", "text": "이 문서에 무엇이 적혀있나요?"},
{"type": "input_file", "filename": "report.pdf", "file_data": "data:application/pdf;base64,..."}
]
}
]
}'
보안 참고 사항:
file_url의 외부 URL은 SSRF 공격 방지를 위해 검증됩니다- 개인 IP 주소와 localhost URL은 거부됩니다
- 외부 파일에는 HTTPS URL만 권장됩니다
file_id필드는 Files API를 통해 업로드된 파일을 참조합니다. 파일은 백엔드로 전송되기 전에 base64로 변환됩니다. 파일 소유권이 검증되며 파일 주입에 10MB 크기 제한이 적용됩니다
Reasoning 파라미터:
reasoning 파라미터는 추론 가능 모델의 추론 노력 수준을 제어합니다. 중첩 형식을 사용합니다:
유효한 effort 값:
| 값 | 설명 |
|---|---|
none |
지원 모델에서 추론 비활성화 |
minimal |
원래 GPT-5 계열 및 지원되는 비OpenAI 모델의 최소 추론 |
auto |
적응형 사고 (Anthropic) |
low |
최소 추론 노력, 더 빠른 응답 |
medium |
균형 잡힌 추론 노력 |
high |
최대 표준 추론 노력 |
xhigh |
지원 모델 계열의 확장 추론 |
max |
GPT-5.6의 최대 추론 |
라우터는 이 중첩 형식을 Chat Completions 백엔드에서 사용하는 평면 reasoning_effort 형식으로 자동 변환합니다. 잘못된 노력 수준은 400 Bad Request 오류로 거부됩니다.
Reasoning을 포함한 요청 예시:
curl -X POST http://localhost:8080/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "o1",
"input": "이 복잡한 수학 문제를 단계별로 풀어주세요.",
"reasoning": {
"effort": "high"
}
}'
응답:
{
"id": "resp_abc123",
"object": "response",
"created_at": 1699000000,
"model": "o1",
"output": [
{
"type": "message",
"id": "msg_001",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "단계별로 풀어보겠습니다..."
}
]
}
],
"usage": {
"input_tokens": 25,
"output_tokens": 150
},
"status": "completed"
}
상태 코드:
200: 응답 생성 성공400: 잘못된 요청 형식, 파라미터 또는 잘못된 reasoning effort 값404: 정상 백엔드에서 모델을 찾을 수 없음502: 백엔드 연결 오류504: 요청 타임아웃503: 모든 백엔드가 비정상
기능:
- 스마트 라우팅: 백엔드 기능에 따라 요청 라우팅 - OpenAI/Azure는 네이티브 패스스루, 기타는 자동 변환
- 네이티브 패스스루: OpenAI 및 Azure OpenAI 백엔드의 경우 요청이
/v1/responses엔드포인트로 직접 전달되어 모든 네이티브 기능 유지 - 자동 변환: 기타 백엔드(Anthropic, Gemini, vLLM, Ollama 등)의 경우 Responses API 형식을 해당 네이티브 형식으로 변환
- 추론 지원: 타입 안전 검증을 통한 reasoning 파라미터 전체 지원
- 다중 백엔드 지원: OpenAI, Anthropic, Gemini, Ollama, vLLM 및 기타 백엔드와 호환
- 스트리밍 지원:
stream: true일 때 SSE를 통한 실시간 응답 스트리밍 - 세션 관리:
previous_response_id를 통한 다중 턴 대화 지원
라우팅 전략:
라우터는 각 백엔드에 대해 최적의 전략을 자동으로 결정합니다:
| 백엔드 유형 | 전략 | 설명 |
|---|---|---|
| OpenAI | 패스스루 | 네이티브 Responses API 지원 - 요청 직접 전달 |
| Azure OpenAI | 패스스루 | 네이티브 Responses API 지원 - 요청 직접 전달 |
| Anthropic | 네이티브 변환 | 네이티브 Anthropic Messages API 형식으로 변환, PDF/이미지 완전 지원 |
| Gemini | 변환 | Gemini generateContent API 형식으로 변환 |
| vLLM | 변환 | Chat Completions 형식으로 변환 |
| Ollama | 변환 | Chat Completions 형식으로 변환 |
| LlamaCpp | 변환 | Chat Completions 형식으로 변환 |
| Generic | 변환 | Chat Completions 형식으로 변환 |
패스스루 이점:
OpenAI 또는 Azure OpenAI 백엔드를 패스스루 모드로 사용할 때:
- 네이티브 PDF 파일 지원 (Chat Completions는 이미지만 지원)
- 턴 간 추론 상태 보존으로 성능 향상
- 내장 도구(web_search, file_search 등) 접근 가능
- 향상된 캐시 활용 (OpenAI 문서에 따르면 40-80% 개선)
- 최신 OpenAI Responses API 기능과의 완벽한 호환성
Anthropic 네이티브 변환 이점:
Anthropic (Claude) 백엔드를 네이티브 변환 모드로 사용할 때:
- Anthropic 문서 이해 기능을 통한 네이티브 PDF 파일 지원
- 자동 형식 감지를 통한 이미지 파일 지원
- Claude 3+ 모델의 확장 사고(Extended Thinking) 지원
- 외부 파일 URL에 대한 SSRF 보호
- 보안을 위한 미디어 타입 화이트리스트
저장된 응답 조회 또는 삭제¶
store: true로 생성한 응답은 ID로 조회하거나 삭제할 수 있습니다. 저장 세션은 프로세스 로컬이며 응답을 생성한 인증된 API 키 주체(api_keys[].user_id) 범위로 격리됩니다. Authorization: Bearer <key>와 X-API-Key: <key> 자격 증명은 같은 설정 identity namespace를 사용합니다. 없거나 만료되었거나 인증되지 않았거나 다른 범위의 요청은 모두 404를 반환합니다.
X-User-ID 범위 지정에서 마이그레이션¶
X-User-ID는 Responses API의 권한 경계가 아닙니다. 라우터는 저장된 응답 접근, previous_response_id 컨텍스트 재구성, item-reference 해석, 로컬 Files API 소유권 검사에서 이 헤더를 무시하며 백엔드로 전달하지 않습니다. 이전에 하나의 공유 라우터 API 키 뒤에서 여러 최종 사용자를 X-User-ID로 나누던 배포는 격리해야 하는 사용자 또는 테넌트마다 별도 라우터 API 키를 사용하거나, 라우터 앞의 신뢰할 수 있는 서비스에서 사용자별 상태를 관리하고 라우터의 저장된 Responses 세션 및 Files API 소유권 제어에 의존하지 않는 구조로 옮겨야 합니다.
성공한 GET은 저장된 응답 객체를 반환합니다. 성공한 DELETE는 다음을 반환합니다.
Responses Compact¶
input 항목 시퀀스를 요약하거나 압축해 대화 컨텍스트를 줄입니다. OpenAI와 Azure OpenAI 백엔드의 경우 요청이 백엔드의 네이티브 /v1/responses/compact 엔드포인트로 그대로 전달됩니다. 다른 백엔드는 컨텍스트 압축을 네이티브로 지원하지 않으므로 HTTP 501(Not Implemented)을 반환합니다.
요청 본문:
{
"model": "gpt-4o",
"instructions": "이 대화의 핵심 내용을 요약해주세요.",
"input": [
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "프랑스의 수도는 어디인가요?"}]
},
{
"type": "message",
"role": "assistant",
"content": [{"type": "output_text", "text": "프랑스의 수도는 파리입니다."}]
}
]
}
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 모델 식별자 |
input |
array | 예 | 압축할 대화를 나타내는 input 항목 배열 |
instructions |
string | 아니오 | 압축 과정을 안내하는 선택적 지시사항 |
응답:
{
"id": "cmpt_abc123",
"object": "response.compaction",
"created_at": 1699000000,
"output": [
{
"type": "text",
"text": "압축된 대화 요약..."
}
],
"usage": {
"input_tokens": 120,
"output_tokens": 45
}
}
상태 코드:
200: 압축 성공400: 잘못된 요청 (빈 모델, 빈 입력, 너무 긴 instructions)501: 백엔드가 네이티브 컨텍스트 압축을 지원하지 않음502: 백엔드 연결 오류504: 요청 타임아웃
Batch API (control-plane 빌드)¶
OpenAI 스타일 Batch API는 control-plane Cargo 기능으로 빌드할 때만 컴파일되며 기본 소스 빌드에는 없습니다. 공식 릴리스 바이너리에는 기능이 컴파일되지만, control_plane.enabled, 정책 적용, control_plane.batch.enabled가 활성화되고 Hub가 제시된 키/티어를 배치 적격으로 표시해야 라우트가 동작합니다.
| 메서드 | 경로 | 용도 |
|---|---|---|
POST |
/v1/batches |
제공자 배치 제출 |
GET |
/v1/batches |
호출 키가 소유한 배치 목록 |
GET |
/v1/batches/{batch_id} |
소유한 배치 상태 조회 |
POST |
/v1/batches/{batch_id}/cancel |
소유한 배치 취소 |
GET |
/v1/batches/{batch_id}/results |
완료 결과를 JSONL로 반환 |
Hub 동기화 API 키가 필요합니다. 소유권 검사는 알 수 없는 ID와 다른 키의 ID에 의도적으로 동일한 404를 반환합니다. 결과가 준비되기 전에는 409를 반환합니다. 선택된 배치 백엔드는 OpenAI 호환 또는 네이티브 Anthropic일 수 있으며 라우터는 하나의 공통 표면을 제공합니다. x-continuum-batch: off는 항상 요청을 배치에서 제외하며 정책이 이를 덮어쓸 수 없습니다.
이미지 생성¶
OpenAI의 DALL-E, GPT Image 모델 또는 Google의 Nano Banana (Gemini) 모델을 사용하여 이미지를 생성합니다.
요청 본문:
{
"model": "dall-e-3",
"prompt": "A serene Japanese garden with cherry blossoms",
"n": 1,
"size": "1024x1024",
"quality": "standard",
"response_format": "url"
}
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 이미지 모델: dall-e-2, dall-e-3, gpt-image-1, gpt-image-1.5, gpt-image-1-mini, nano-banana, 또는 nano-banana-pro |
prompt |
string | 예 | 생성할 이미지에 대한 설명 |
n |
integer | 아니오 | 이미지 수 (1-10, 모델에 따라 다름) |
size |
string | 아니오 | 이미지 크기 (모델에 따라 다름, 아래 참조) |
quality |
string | 아니오 | 이미지 품질 (모델에 따라 다름, 아래 참조) |
style |
string | 아니오 | 이미지 스타일: vivid 또는 natural (DALL-E 3만) |
response_format |
string | 아니오 | 응답 형식: url 또는 b64_json |
output_format |
string | 아니오 | 출력 파일 형식: png, jpeg, webp (GPT Image 모델만, 기본값: png) |
output_compression |
integer | 아니오 | jpeg/webp의 압축 레벨 0-100 (GPT Image 모델만) |
background |
string | 아니오 | 배경: transparent, opaque, auto (GPT Image 모델만) |
stream |
boolean | 아니오 | 부분 이미지 스트리밍 활성화 (GPT Image 모델만, 기본값: false) |
partial_images |
integer | 아니오 | 스트리밍 중 부분 이미지 수 0-3 (GPT Image 모델만) |
user |
string | 아니오 | 추적을 위한 사용자 식별자 |
모델별 제약 조건:
| 모델 | 크기 | n | 품질 | 참고 |
|---|---|---|---|---|
dall-e-2 |
256x256, 512x512, 1024x1024 |
1-10 | N/A | 클래식 DALL-E 2 |
dall-e-3 |
1024x1024, 1792x1024, 1024x1792 |
1 | standard, hd |
프롬프트 수정이 포함된 고품질 |
gpt-image-1 |
1024x1024, 1536x1024, 1024x1536, auto |
1 | low, medium, high, auto |
최신 GPT Image 모델, 스트리밍 지원 |
gpt-image-1.5 |
1024x1024, 1536x1024, 1024x1536, auto |
1 | low, medium, high, auto |
4배 빠름, 향상된 텍스트 렌더링 |
gpt-image-1-mini |
1024x1024, 1536x1024, 1024x1536, auto |
1 | low, medium, high, auto |
비용 효율적인 옵션 |
nano-banana |
256x256 ~ 1024x1024 |
1-4 | N/A | Gemini 2.5 Flash Image (빠름) |
nano-banana-pro |
256x256 ~ 4096x4096 |
1-4 | N/A | Gemini 2.0 Flash Image (고급, 최대 4K) |
품질 파라미터 (GPT Image 모델):
GPT Image 모델에서 standard는 medium으로, hd는 high로 매핑됩니다.
| 품질 | 설명 |
|---|---|
low |
낮은 품질로 빠른 생성 |
medium |
품질과 속도의 균형 (기본값) |
high |
최고 품질, 느린 생성 |
auto |
모델이 최적 품질 선택 |
출력 형식 옵션 (GPT Image 모델):
| 형식 | 설명 | 투명도 지원 |
|---|---|---|
png |
무손실 형식 (기본값) | 예 |
jpeg |
손실 형식, 작은 파일 크기 | 아니오 |
webp |
현대적인 형식, 좋은 압축률 | 예 |
참고: 투명 배경 (
background: "transparent")은png또는webp형식이 필요합니다.
Nano Banana (Gemini) 모델:
Nano Banana는 OpenAI 호환 인터페이스를 통해 Google의 Gemini 이미지 생성 기능에 대한 액세스를 제공합니다:
nano-banana: Gemini 2.5 Flash Image에 매핑 - 빠른 범용 이미지 생성nano-banana-pro: Gemini 2.0 Flash Image에 매핑 - 고해상도 지원 고급 모델 (최대 4K)
Nano Banana 크기 매핑:
라우터는 OpenAI 스타일 크기 파라미터를 Gemini의 aspectRatio 및 imageSize 형식으로 자동 변환합니다:
| OpenAI 크기 | Gemini aspectRatio | Gemini imageSize | 참고 |
|---|---|---|---|
256x256 |
1:1 | 1K | Gemini 최소값으로 폴백 |
512x512 |
1:1 | 1K | Gemini 최소값으로 폴백 |
1024x1024 |
1:1 | 1K | 기본값 |
1536x1024 |
3:2 | 1K | 가로 |
1024x1536 |
2:3 | 1K | 세로 |
1024x1792 |
9:16 | 1K | 긴 세로 |
1792x1024 |
16:9 | 1K | 넓은 가로 |
2048x2048 |
1:1 | 2K | Pro만 |
4096x4096 |
1:1 | 4K | Pro만 |
auto |
1:1 | 1K | 기본 폴백 |
변환은 다음 Gemini API 구조를 전송합니다:
{
"contents": [{"parts": [{"text": "Your prompt"}]}],
"generationConfig": {
"imageConfig": {
"aspectRatio": "3:2",
"imageSize": "1K"
}
}
}
Nano Banana 요청 예제:
{
"model": "nano-banana",
"prompt": "A white siamese cat with blue eyes, photorealistic",
"n": 1,
"size": "1024x1024",
"response_format": "b64_json"
}
응답:
{
"created": 1677652288,
"data": [
{
"url": "https://oaidalleapiprodscus.blob.core.windows.net/...",
"revised_prompt": "A tranquil Japanese garden featuring..."
}
]
}
응답 (b64_json 포함):
{
"created": 1677652288,
"data": [
{
"b64_json": "/9j/4AAQSkZJRgABAQAA...",
"revised_prompt": "A tranquil Japanese garden featuring..."
}
]
}
Nano Banana 응답 참고 사항:
- Nano Banana에서
response_format: "url"을 사용할 때, Gemini의 네이티브 API가 인라인 base64 데이터를 반환하므로 이미지는 data URL (data:image/png;base64,...)로 반환됩니다 revised_prompt필드에는 생성된 이미지를 설명하는 Gemini의 텍스트 응답이 포함됩니다
스트리밍 이미지 생성 (GPT Image 모델):
GPT Image 모델에 대해 stream: true가 지정되면 응답이 Server-Sent Events (SSE)로 스트리밍됩니다:
스트리밍 요청 예제:
{
"model": "gpt-image-1",
"prompt": "A beautiful sunset over mountains",
"stream": true,
"partial_images": 2,
"response_format": "b64_json"
}
스트리밍 응답 형식:
data: {"type":"image_generation.partial_image","partial_image_index":0,"b64_json":"...","created":1702345678}
data: {"type":"image_generation.partial_image","partial_image_index":1,"b64_json":"...","created":1702345679}
data: {"type":"image_generation.complete","b64_json":"...","created":1702345680}
data: {"type":"image_generation.usage","usage":{"input_tokens":25,"output_tokens":1024}}
data: {"type":"done"}
SSE 이벤트 타입:
| 이벤트 타입 | 설명 |
|---|---|
image_generation.partial_image |
생성 중 중간 이미지 |
image_generation.complete |
최종 완성 이미지 |
image_generation.usage |
토큰 사용량 정보 (비용 추적용) |
done |
스트림 완료 마커 |
새 옵션이 포함된 GPT Image 요청 예제:
{
"model": "gpt-image-1.5",
"prompt": "A white cat with blue eyes, photorealistic",
"size": "auto",
"quality": "high",
"output_format": "webp",
"output_compression": 85,
"background": "transparent",
"response_format": "b64_json"
}
상태 코드:
200: 이미지가 성공적으로 생성됨400: 유효하지 않은 요청 (예: 모델에 대해 유효하지 않은 크기, DALL-E 3에서 n > 1)401: 유효하지 않은 API 키429: 속도 제한 초과500: 백엔드 오류503: Gemini 백엔드 사용 불가 (Nano Banana 모델의 경우)
타임아웃 설정:
이미지 생성 요청은 설정 가능한 타임아웃을 사용합니다 (기본값: 3분). 설정의 timeouts.request.image_generation을 참조하세요.
이미지 편집 (인페인팅)¶
OpenAI의 인페인팅 기능을 사용하여 기존 이미지를 편집합니다. 이 엔드포인트를 사용하면 텍스트 프롬프트와 선택적 마스크를 기반으로 이미지의 특정 영역을 수정할 수 있습니다. GPT Image 모델과 DALL-E 2를 지원합니다.
요청 파라미터 (multipart/form-data):
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
image |
file | 예 | 편집할 소스 이미지 (PNG, < 4MB, 정사각형) |
prompt |
string | 예 | 원하는 편집에 대한 설명 |
mask |
file | 아니오 | 편집 영역을 나타내는 마스크 이미지 (PNG, 이미지와 동일한 크기) |
model |
string | 아니오 | 사용할 모델 (기본값: gpt-image-1) |
n |
integer | 아니오 | 생성할 이미지 수 (1-10, 기본값: 1) |
size |
string | 아니오 | 출력 크기 (모델에 따라 다름, 기본값: 1024x1024) |
response_format |
string | 아니오 | 응답 형식: url 또는 b64_json (기본값: url) |
user |
string | 아니오 | 추적을 위한 고유 사용자 식별자 |
지원되는 모델 및 크기:
| 모델 | 크기 | 참고 |
|---|---|---|
gpt-image-1 |
1024x1024, 1536x1024, 1024x1536, auto |
최신 GPT Image 모델 (권장) |
gpt-image-1-mini |
1024x1024, 1536x1024, 1024x1536, auto |
비용 최적화 버전 |
gpt-image-1.5 |
1024x1024, 1536x1024, 1024x1536, auto |
향상된 지시 따르기 기능의 최신 버전 |
dall-e-2 |
256x256, 512x512, 1024x1024 |
레거시 DALL-E 2 모델 |
참고: DALL-E 3와 Gemini (nano-banana)는 이 엔드포인트를 통한 이미지 편집을 지원하지 않습니다. Gemini는 자연어를 통한 시맨틱 마스킹을 사용하며, 이는 OpenAI의 마스크 기반 편집 형식과 호환되지 않습니다.
이미지 요구 사항:
- 형식: PNG만
- 크기: 4MB 미만
- 크기: 정사각형이어야 함 (가로 = 세로)
마스크 요구 사항:
- 형식: 알파 채널이 있는 PNG (RGBA)
- 크기: 소스 이미지와 정확히 일치해야 함
- 투명 영역: 편집/생성할 영역을 나타냄
- 불투명 영역: 보존할 영역을 나타냄
요청 예제:
curl -X POST http://localhost:8080/v1/images/edits \
-F "image=@source_image.png" \
-F "mask=@mask.png" \
-F "prompt=A sunlit indoor lounge area with a pool containing a flamingo" \
-F "n=1" \
-F "size=1024x1024" \
-F "response_format=url"
요청 예제 (마스크 없이):
curl -X POST http://localhost:8080/v1/images/edits \
-F "image=@source_image.png" \
-F "prompt=Add a sunset in the background" \
-F "n=1" \
-F "size=512x512"
응답:
{
"created": 1677652288,
"data": [
{
"url": "https://oaidalleapiprodscus.blob.core.windows.net/..."
}
]
}
응답 (b64_json 포함):
상태 코드:
200: 이미지가 성공적으로 편집됨400: 유효하지 않은 요청 (예: 정사각형이 아닌 이미지, 유효하지 않은 크기, 필수 필드 누락)401: 유효하지 않은 API 키503: OpenAI 백엔드 사용 불가
오류 예제:
정사각형이 아닌 이미지:
{
"error": {
"message": "Image must be square (800x600 is not square)",
"type": "invalid_request_error",
"param": "image",
"code": "image_not_square"
}
}
마스크 크기 불일치:
{
"error": {
"message": "Mask dimensions (256x256) do not match image dimensions (512x512)",
"type": "invalid_request_error",
"param": "mask",
"code": "dimension_mismatch"
}
}
지원되지 않는 모델:
{
"error": {
"message": "Model 'dall-e-3' does not support image editing. Supported models: gpt-image-1, gpt-image-1-mini, gpt-image-1.5, dall-e-2. Note: dall-e-3 does NOT support image editing.",
"type": "invalid_request_error",
"param": "model",
"code": "unsupported_model"
}
}
참고 사항:
- 지원되는 모델:
gpt-image-1,gpt-image-1-mini,gpt-image-1.5,dall-e-2 - DALL-E 3는 API를 통한 이미지 편집을 지원하지 않습니다
- Gemini (nano-banana)는 지원되지 않음 - 다른 편집 접근 방식 사용 (시맨틱 마스킹)
- 마스크가 제공되지 않으면 전체 이미지가 수정될 수 있습니다
- 마스크 없이 편집하는 경우 소스 이미지에 투명 영역이 있어야 합니다
- 요청 타임아웃은 이미지 생성 타임아웃 설정을 사용합니다
이미지 변형¶
OpenAI의 DALL-E 2 모델을 사용하여 기존 이미지의 변형을 생성합니다.
폼 필드:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
image |
file | 예 | 변형을 위한 소스 이미지 (PNG, < 4MB, 정사각형이어야 함) |
model |
string | 아니오 | 사용할 모델 (기본값: dall-e-2) |
n |
integer | 아니오 | 생성할 변형 수 (1-10, 기본값: 1) |
size |
string | 아니오 | 출력 크기: 256x256, 512x512, 1024x1024 (기본값: 1024x1024) |
response_format |
string | 아니오 | 응답 형식: url 또는 b64_json (기본값: url) |
user |
string | 아니오 | 추적을 위한 사용자 식별자 |
요청 예제:
curl -X POST http://localhost:8080/v1/images/variations \
-F "image=@source_image.png" \
-F "model=dall-e-2" \
-F "n=2" \
-F "size=512x512" \
-F "response_format=url"
응답:
{
"created": 1677652288,
"data": [
{
"url": "https://oaidalleapiprodscus.blob.core.windows.net/..."
},
{
"url": "https://oaidalleapiprodscus.blob.core.windows.net/..."
}
]
}
응답 (b64_json 포함):
모델 지원:
| 모델 | 변형 지원 | 참고 |
|---|---|---|
dall-e-2 |
예 (네이티브) | 완전 지원, 1-10개 변형 |
dall-e-3 |
아니오 | OpenAI API에서 지원하지 않음 |
gpt-image-1 |
아니오 | 지원되지 않음 |
nano-banana |
아니오 | Gemini는 변형 API를 지원하지 않음 |
nano-banana-pro |
아니오 | Gemini는 변형 API를 지원하지 않음 |
이미지 요구 사항:
- 형식: PNG만
- 크기: 4MB 미만
- 크기: 정사각형이어야 함 (가로 == 세로)
- 지원되는 입력 크기: 모든 정사각형 크기 (모델에서 처리됨)
오류 시나리오:
| 오류 | 상태 | 설명 |
|---|---|---|
| PNG가 아닌 이미지 | 400 | PNG 형식만 지원됨 |
| 정사각형이 아닌 이미지 | 400 | 이미지 크기가 동일해야 함 |
| 이미지가 너무 큼 | 400 | 이미지가 4MB 크기 제한 초과 |
| 지원되지 않는 모델 | 400 | 요청된 모델이 변형을 지원하지 않음 |
| 이미지 누락 | 400 | image 필드 필수 |
| 유효하지 않은 n 값 | 400 | n은 1과 10 사이여야 함 |
| 유효하지 않은 크기 | 400 | 크기는 지원되는 값 중 하나여야 함 |
상태 코드:
200: 변형이 성공적으로 생성됨400: 유효하지 않은 요청 (유효하지 않은 형식, 정사각형이 아닌 이미지, 지원되지 않는 모델)401: 유효하지 않은 API 키429: 속도 제한 초과500: 백엔드 오류503: 백엔드 사용 불가
텍스트 완료¶
OpenAI Completions API 형식을 사용하여 텍스트 완료를 생성합니다.
요청 본문:
{
"model": "gpt-3.5-turbo-instruct",
"prompt": "Once upon a time in a distant galaxy",
"max_tokens": 100,
"temperature": 0.7,
"top_p": 1.0,
"frequency_penalty": 0.0,
"presence_penalty": 0.0,
"stream": false,
"stop": null,
"logit_bias": {},
"user": "user123"
}
응답:
{
"id": "cmpl-123456789",
"object": "text_completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-instruct",
"choices": [
{
"text": ", there lived a young explorer named Zara who dreamed of discovering new worlds...",
"index": 0,
"finish_reason": "stop",
"logprobs": null
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 90,
"total_tokens": 100
}
}
상태 코드: 채팅 완료와 동일
임베딩¶
OpenAI 임베딩 API 형식을 사용하여 입력 텍스트에 대한 임베딩을 생성합니다.
요청 본문:
{
"model": "text-embedding-3-small",
"input": "The quick brown fox jumps over the lazy dog",
"encoding_format": "float",
"dimensions": 512,
"user": "user123"
}
요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 사용할 모델의 ID (예: text-embedding-3-small, gemini-embedding-2) |
input |
string, array, 또는 콘텐츠 파트 | 예 | 임베딩할 입력. 단일 문자열, 문자열 배열, 토큰 배열, 또는 멀티모달 콘텐츠 파트 배열(Gemini Embedding 2 전용)이 될 수 있음 |
encoding_format |
string | 아니오 | 임베딩 반환 형식: float 또는 base64 (기본값: float) |
dimensions |
integer | 아니오 | 출력 임베딩의 차원 수 (text-embedding-3-* 및 gemini-embedding-2* 모델 지원) |
user |
string | 아니오 | 최종 사용자의 고유 식별자 |
입력 형식 예시:
단일 문자열:
문자열 배열:
{
"model": "text-embedding-3-large",
"input": [
"임베딩할 첫 번째 텍스트",
"임베딩할 두 번째 텍스트",
"임베딩할 세 번째 텍스트"
]
}
토큰 배열:
멀티모달 콘텐츠 파트 (Gemini Embedding 2 전용):
{
"model": "gemini-embedding-2",
"input": [
{"type": "text", "text": "골든 리트리버 사진"},
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}}
]
}
응답:
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.0023064255, -0.009327292, 0.0045318254, ...]
}
],
"model": "text-embedding-3-small",
"usage": {
"prompt_tokens": 8,
"total_tokens": 8
}
}
응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
object |
string | 항상 "list" |
data |
array | 임베딩 객체 배열 |
data[].object |
string | 항상 "embedding" |
data[].index |
integer | 입력 배열에서 임베딩의 인덱스 |
data[].embedding |
array | 임베딩 벡터 (부동소수점 배열) |
model |
string | 임베딩 생성에 사용된 모델 |
usage |
object | 토큰 사용 정보 |
usage.prompt_tokens |
integer | 입력의 토큰 수 |
usage.total_tokens |
integer | 사용된 총 토큰 (임베딩의 경우 prompt_tokens와 동일) |
지원 모델:
| 백엔드 | 모델 | 참고 |
|---|---|---|
| OpenAI | text-embedding-3-small |
기본 1536 차원, dimensions 파라미터 지원 |
| OpenAI | text-embedding-3-large |
기본 3072 차원, dimensions 파라미터 지원 |
| OpenAI | text-embedding-ada-002 |
1536 차원, 레거시 모델 |
| Gemini | gemini-embedding-2 |
3072 차원, 멀티모달 지원 (텍스트, 이미지, 오디오, 비디오, 문서) |
| Gemini | gemini-embedding-2-preview |
Gemini Embedding 2 프리뷰, 멀티모달 지원 |
| Gemini | text-embedding-004 |
텍스트 전용, OpenAI 호환 엔드포인트 사용 |
| 자체 호스팅 | bge-m3 |
1024 차원, 100개 이상 언어, 8192 컨텍스트. Dense, sparse, ColBERT 검색 지원 |
| 자체 호스팅 | bge-large-en-v1.5 |
1024 차원, 영어 전용, 512 컨텍스트 |
| 자체 호스팅 | multilingual-e5-large |
1024 차원, 100개 이상 언어, 514 컨텍스트 |
| vLLM | 배포별 다름 | 배포된 모델에 따라 다름 |
| llama.cpp | 배포별 다름 | 네이티브 /v1/embeddings 지원 |
| TEI | 배포별 다름 | Hugging Face Text Embeddings Inference 서버 |
| Ollama | 배포별 다름 | Ollama 임베딩 모델 사용 |
상태 코드:
200: 임베딩이 성공적으로 생성됨400: 유효하지 않은 요청 (model/input 누락, 유효하지 않은 dimensions)401: 유효하지 않은 API 키404: 모델을 찾을 수 없거나 임베딩을 지원하지 않음429: 속도 제한 초과500: 백엔드 오류503: 백엔드 사용 불가
기능:
- 다양한 입력 형식: 단일 문자열, 문자열 배열, 토큰 배열 또는 멀티모달 콘텐츠 파트 지원
- 멀티모달 임베딩:
gemini-embedding-2및gemini-embedding-2-preview는 텍스트, 이미지, 오디오, 비디오, 문서를 하나의 요청에 혼합하여 처리 가능 - 차원 제어:
text-embedding-3-*및gemini-embedding-2*모델의 경우 축소된 벡터 크기를 위한 사용자 정의 차원 지정 - 백엔드 독립적: 모델에 따라 적절한 백엔드로 라우팅
- 로드 밸런싱: 설정된 로드 밸런싱 전략 적용
- 오류 처리: 텍스트 전용 모델에 멀티모달 입력을 전송하면
400오류와 명확한 메시지 반환
멀티모달 콘텐츠 파트 타입 (Gemini Embedding 2 전용):
| 타입 | 필드 | 제약 조건 |
|---|---|---|
text |
text (string) |
비어있지 않아야 함 |
image_url |
image_url.url (data URI 또는 file://file-* 참조) |
MIME: image/jpeg, image/png; 최대 20 MiB; 요청당 최대 6개 이미지 |
audio |
audio.data (base64), audio.format |
형식: mp3, wav; 최대 20 MiB |
video |
video.data (base64), video.format |
형식: mp4, mov; 최대 20 MiB |
document |
document.data (base64), document.format |
형식: pdf; 최대 20 MiB |
요청 예제:
curl -X POST http://localhost:8080/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "text-embedding-3-small",
"input": "The quick brown fox jumps over the lazy dog",
"encoding_format": "float"
}'
여러 입력 예제:
curl -X POST http://localhost:8080/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "text-embedding-3-large",
"input": [
"첫 번째 문서 텍스트",
"두 번째 문서 텍스트",
"세 번째 문서 텍스트"
]
}'
사용자 정의 차원 예제:
curl -X POST http://localhost:8080/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "text-embedding-3-small",
"input": "샘플 텍스트",
"dimensions": 512
}'
멀티모달 입력 예제 (Gemini Embedding 2):
curl -X POST http://localhost:8080/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "gemini-embedding-2",
"input": [
{"type": "text", "text": "바다 위 석양 사진"},
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}}
],
"dimensions": 768
}'
리랭킹¶
Cohere 호환 Rerank API를 사용하여 쿼리와의 관련성을 기준으로 문서를 리랭킹합니다. 이는 일반적으로 초기 벡터 검색 후 정확도를 향상시키기 위한 2단계 검색 단계로 사용됩니다.
요청 본문:
{
"model": "bge-reranker-v2-m3",
"query": "머신러닝이란 무엇인가요?",
"documents": ["문서 1 내용", "문서 2 내용", "문서 3 내용"],
"top_n": 3,
"return_documents": false
}
요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 사용할 리랭킹 모델의 ID (예: bge-reranker-v2-m3, rerank-english-v3.0, jina-reranker-v2-base-multilingual) |
query |
string | 예 | 문서와 비교할 검색 쿼리 |
documents |
array | 예 | 리랭킹할 문서 목록. 문자열 배열 또는 text 필드가 있는 객체 배열이 될 수 있음 |
top_n |
integer | 아니오 | 반환할 상위 결과 수. 지정하지 않으면 모든 문서가 순위대로 반환됨 |
return_documents |
boolean | 아니오 | 응답에 문서 텍스트를 포함할지 여부 (기본값: false) |
max_chunks_per_doc |
integer | 아니오 | 긴 문서 처리를 위해 문서당 처리할 최대 청크 수 |
문서 형식 옵션:
단순 문자열 배열:
{
"model": "bge-reranker-v2-m3",
"query": "딥러닝이란 무엇인가요?",
"documents": [
"딥러닝은 여러 층의 신경망을 사용합니다",
"머신러닝은 인공지능의 하위 분야입니다",
"자연어 처리는 텍스트 이해를 다룹니다"
]
}
text 필드가 있는 구조화된 문서:
{
"model": "rerank-english-v3.0",
"query": "딥러닝이란 무엇인가요?",
"documents": [
{"text": "딥러닝은 여러 층의 신경망을 사용합니다"},
{"text": "머신러닝은 인공지능의 하위 분야입니다"}
]
}
응답:
{
"results": [
{
"index": 0,
"relevance_score": 0.95
},
{
"index": 2,
"relevance_score": 0.72
},
{
"index": 1,
"relevance_score": 0.45
}
],
"model": "bge-reranker-v2-m3",
"id": "rerank-abc123",
"usage": {
"prompt_tokens": 150,
"total_tokens": 150
}
}
문서 포함 응답 (return_documents: true 인 경우):
{
"results": [
{
"index": 0,
"relevance_score": 0.95,
"document": {
"text": "딥러닝은 여러 층의 신경망을 사용합니다"
}
}
],
"model": "bge-reranker-v2-m3"
}
응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
results |
array | 관련성 점수 순으로 정렬된 리랭킹 결과 목록 (높은 순) |
results[].index |
integer | 원본 입력 목록에서 문서의 인덱스 |
results[].relevance_score |
number | 관련성 점수 (일반적으로 0.0에서 1.0, 높을수록 더 관련됨) |
results[].document |
object | 문서 텍스트 (return_documents가 true인 경우에만 존재) |
model |
string | 리랭킹에 사용된 모델 |
id |
string | 이 요청의 고유 식별자 (선택적) |
usage |
object | 토큰 사용 정보 (선택적) |
지원 백엔드:
| 백엔드 | 엔드포인트 | 참고 |
|---|---|---|
| vLLM | /v1/rerank |
Cohere 호환, BGE, Jina 리랭커 지원 |
| llama.cpp | /v1/rerank |
시작 시 --reranking 플래그 필요 |
| Hugging Face TEI | /rerank |
Text Embeddings Inference 서버 |
| Cohere API | /v1/rerank |
네이티브 Cohere rerank 엔드포인트 |
| Jina AI | /v1/rerank |
네이티브 Jina rerank 엔드포인트 |
상태 코드:
200: 문서가 성공적으로 리랭킹됨400: 유효하지 않은 요청 (model/query/documents 누락, 빈 documents 배열)401: 유효하지 않은 API 키404: 모델을 찾을 수 없거나 리랭킹을 지원하지 않음429: 속도 제한 초과500: 백엔드 오류503: 백엔드 사용 불가
사용 사례:
- 2단계 검색: 벡터 검색으로 후보를 검색한 다음 더 높은 정밀도를 위해 리랭킹
- RAG 시스템: LLM 처리 전에 검색된 문서를 리랭킹하여 컨텍스트 품질 향상
- 검색 결과 정제: 의미적 관련성에 따라 검색 결과 재정렬
요청 예제:
curl -X POST http://localhost:8080/v1/rerank \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "bge-reranker-v2-m3",
"query": "재생 에너지의 장점은 무엇인가요?",
"documents": [
"태양광 패널은 햇빛을 전기로 변환합니다",
"풍력 터빈은 바람에서 전력을 생성합니다",
"석탄은 전기에 사용되는 화석 연료입니다"
],
"top_n": 2
}'
희소 임베딩¶
TEI/Jina 호환 Sparse Embedding API를 사용하여 입력 텍스트에 대한 희소 임베딩을 생성합니다. 희소 임베딩(예: SPLADE)은 명시적인 용어 가중치를 통해 어휘 정보를 보존하여 하이브리드 검색에서 밀집 임베딩을 보완합니다.
요청 본문:
요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 사용할 희소 임베딩 모델의 ID (예: naver/splade-v3, naver/splade-cocondenser-ensembledistil) |
input |
string 또는 array | 예 | 임베딩할 입력 텍스트. 단일 문자열 또는 문자열 배열이 될 수 있음 |
입력 형식 예시:
단일 문자열:
문자열 배열:
응답:
{
"data": [
{
"index": 0,
"sparse_embedding": {
"indices": [123, 456, 789, 1024, 2048],
"values": [0.5, 0.3, 0.1, 0.8, 0.2]
}
}
],
"model": "naver/splade-v3",
"usage": {
"prompt_tokens": 8,
"total_tokens": 8
}
}
응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
data |
array | 희소 임베딩 객체 배열 |
data[].index |
integer | 입력 배열에서 임베딩의 인덱스 |
data[].sparse_embedding |
object | 희소 임베딩 벡터 |
data[].sparse_embedding.indices |
array | 어휘에서 0이 아닌 요소의 인덱스 |
data[].sparse_embedding.values |
array | 해당 인덱스의 값 |
model |
string | 임베딩 생성에 사용된 모델 (선택적) |
usage |
object | 토큰 사용 정보 (선택적) |
희소 벡터 이해:
희소 벡터는 어휘 인덱스와 함께 0이 아닌 값만 저장합니다. 예를 들어:
indices: [123, 456, 789]- 어휘에서의 위치values: [0.5, 0.3, 0.1]- 해당 용어의 가중치
이는 0이 아닌 요소가 적은 고차원 벡터에 대해 메모리 효율적입니다 (일반적으로 30,000개 이상의 어휘 크기 중 100-500개의 0이 아닌 값).
지원 백엔드:
| 백엔드 | 엔드포인트 | 참고 |
|---|---|---|
| vLLM | /embed_sparse |
OpenAI 호환 서버를 통한 SPLADE 모델 지원 |
| Hugging Face TEI | /embed_sparse |
--pooling splade 플래그 필요 |
| Jina AI | 네이티브 | 네이티브 희소 임베딩 지원 |
상태 코드:
200: 희소 임베딩이 성공적으로 생성됨400: 유효하지 않은 요청 (model/input 누락, 빈 입력)401: 유효하지 않은 API 키404: 모델을 찾을 수 없거나 희소 임베딩을 지원하지 않음429: 속도 제한 초과500: 백엔드 오류503: 백엔드 사용 불가
사용 사례:
- 하이브리드 검색: 더 나은 결과를 위해 밀집(의미) 및 희소(어휘) 검색 결합
- 키워드 매칭: 학습된 가중치를 통한 정확한 용어 매칭
- 도메인별 검색: 전문 용어 및 희귀 단어의 더 나은 처리
- 교차 언어 검색: 일부 SPLADE 모델은 다국어 희소 검색 지원
요청 예제:
curl -X POST http://localhost:8080/embed_sparse \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "naver/splade-v3",
"input": "검색을 위한 희소 임베딩의 장점은 무엇인가요?"
}'
여러 입력 예제:
curl -X POST http://localhost:8080/embed_sparse \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "naver/splade-v3",
"input": [
"첫 번째 쿼리 텍스트",
"두 번째 쿼리 텍스트"
]
}'
Files API¶
Files API를 사용하면 채팅 완료에서 파일을 업로드, 관리 및 사용할 수 있습니다. 업로드된 파일은 image_file 콘텐츠 타입을 사용하여 메시지에서 참조할 수 있으며, 라우터가 파일 콘텐츠를 주입하여 이러한 참조를 자동으로 해결합니다.
파일 업로드¶
채팅 완료에서 사용할 파일을 업로드합니다.
폼 필드:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
file |
file | 예 | 업로드할 파일 |
purpose |
string | 예 | 파일의 용도: vision, assistants, fine-tune, batch, user_data, evals |
예제:
응답:
{
"id": "file-abc123def456",
"object": "file",
"bytes": 12345,
"created_at": 1699061776,
"filename": "image.png",
"purpose": "vision"
}
상태 코드:
200: 파일이 성공적으로 업로드됨400: 유효하지 않은 요청 (파일 누락, 유효하지 않은 용도)413: 파일이 너무 큼 (설정된 max_file_size 초과)
파일 목록¶
업로드된 파일 목록을 조회합니다.
쿼리 파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
purpose |
string | 아니오 | 용도별 필터링 |
응답:
{
"object": "list",
"data": [
{
"id": "file-abc123def456",
"object": "file",
"bytes": 12345,
"created_at": 1699061776,
"filename": "image.png",
"purpose": "vision"
}
]
}
파일 메타데이터 가져오기¶
특정 파일의 메타데이터를 조회합니다.
응답:
{
"id": "file-abc123def456",
"object": "file",
"bytes": 12345,
"created_at": 1699061776,
"filename": "image.png",
"purpose": "vision"
}
상태 코드:
200: 파일 메타데이터 조회됨404: 파일을 찾을 수 없음
파일 콘텐츠 다운로드¶
업로드된 파일의 콘텐츠를 다운로드합니다.
응답: 적절한 Content-Type 헤더와 함께 바이너리 파일 콘텐츠.
상태 코드:
200: 파일 콘텐츠 반환됨404: 파일을 찾을 수 없음
파일 삭제¶
업로드된 파일을 삭제합니다.
응답:
상태 코드:
200: 파일이 성공적으로 삭제됨404: 파일을 찾을 수 없음
오류 응답 본문¶
Files API의 모든 오류는 OpenAI 형식을 따릅니다. message, type, code를 담은 error 객체입니다.
클라이언트 요청이 원인인 오류는 무엇이 잘못됐는지 알려주며, 호출자가 보낸 file_id도 그대로 포함합니다.
{
"error": {
"message": "file not found: file-abc123def456",
"type": "invalid_request_error",
"code": "file_not_found"
}
}
서버 쪽 오류는 라우터 내부 진단 문구 대신 고정된 메시지를 반환합니다. type과 code는 원인을 그대로 구분해주므로, 이 두 필드로 분기하는 클라이언트는 영향을 받지 않습니다.
{
"error": {
"message": "storage error: the file operation could not be completed",
"type": "server_error",
"code": "storage_error"
}
}
전체 진단 정보(files.storage_path 아래의 절대 경로, 업로드 임시 파일 이름, 하부 운영체제 오류)는 라우터 로그에 ERROR 수준으로 기록되며 응답 본문에는 절대 실리지 않습니다. 설정 스위치로 노출하지 않는 것은 의도된 선택입니다. 스토리지 장애를 조사하는 운영자는 로그를 보면 되고, 호출자에게는 라우터의 파일시스템 구조가 쓸모없기 때문입니다. /admin/files* 엔드포인트, 그리고 /v1/chat/completions와 /anthropic/v1/messages에서 드러나는 file_id 해결 실패에도 같은 규칙이 적용됩니다.
code |
상태 | 메시지 |
|---|---|---|
storage_error |
500 | 고정: storage error: the file operation could not be completed |
io_error |
500 | 고정: io error: the file operation could not be completed |
service_unavailable |
503 | 고정: files api is disabled |
| 그 외 | 4xx | 요청 내용을 설명하며 file_id를 포함 |
채팅 완료에서의 파일 해결¶
라우터는 채팅 완료 요청에서 파일 참조를 자동으로 해결합니다. 메시지에 image_file 콘텐츠 블록이 포함되면 라우터는:
- 파일 ID 형식을 검증합니다
- 스토리지에서 파일 콘텐츠를 로드합니다
- 파일을 base64 data URL로 변환합니다
image_file블록을image_url블록으로 대체합니다
지원되는 파일 타입¶
채팅 완료에서의 파일 해결은 다음 파일 타입을 지원합니다:
| 파일 타입 | MIME 타입 | 지원 |
|---|---|---|
| PNG | image/png |
모든 백엔드 |
| JPEG | image/jpeg |
모든 백엔드 |
| GIF | image/gif |
모든 백엔드 |
| WebP | image/webp |
모든 백엔드 |
application/pdf |
OpenAI, Anthropic | |
| Plain Text | text/plain |
Anthropic |
참고: PDF 및 일반 텍스트 지원은 Anthropic 백엔드(및 OpenAI의 PDF)에서 사용할 수 있습니다. 파일 변환기는 각 백엔드에 적합한 형식으로 문서 파일을 자동으로 변환합니다 (OpenAI는 PDF에
file블록을 사용하고, Anthropic은 PDF와 일반 텍스트 모두에document블록을 사용합니다). 이미지가 아닌 파일과 문서가 아닌 파일은 지원되는 파일 타입을 안내하는 유용한 메시지와 함께400 Bad Request오류를 반환합니다.
파일 참조가 포함된 요청:
{
"model": "gpt-4-vision-preview",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_file", "image_file": {"file_id": "file-abc123def456"}}
]
}
]
}
변환된 요청 (백엔드로 전송됨):
{
"model": "gpt-4-vision-preview",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
]
}
]
}
파일 해결 오류:
| 오류 | 상태 | 설명 |
|---|---|---|
| 유효하지 않은 파일 ID 형식 | 400 | 파일 ID는 file-로 시작해야 함 |
| 파일을 찾을 수 없음 | 404 | 참조된 파일이 존재하지 않음 |
| 파일 참조가 너무 많음 | 400 | 요청에 20개 이상의 파일 참조 포함 |
| 해결 타임아웃 | 504 | 파일 해결에 30초 이상 소요 |
지원되는 MIME 타입:
image/png- 모든 백엔드image/jpeg- 모든 백엔드image/gif- 모든 백엔드image/webp- 모든 백엔드application/pdf- OpenAI, Anthropic (최대 32MB, 100페이지)text/plain- Anthropic (최대 32MB)
Anthropic 네이티브 API¶
Continuum Router는 Anthropic의 API 형식을 직접 사용하면서도 라우터의 로드 밸런싱, 장애 조치 및 다중 백엔드 라우팅 기능을 활용할 수 있는 네이티브 Anthropic API 엔드포인트를 제공합니다.
메시지 API¶
Anthropic의 네이티브 API 형식을 사용하여 메시지를 전송합니다.
헤더:
| 헤더 | 필수 | 설명 |
|---|---|---|
x-api-key 또는 Authorization: Bearer |
예* | 인증용 API 키 (blocking 모드에서 필수). 두 헤더 형식 모두 허용되며, 둘 다 있으면 Authorization: Bearer가 우선 |
anthropic-version |
아니오 | API 버전 (예: 2023-06-01). 네이티브 Anthropic 백엔드로 전달됨 |
anthropic-beta |
아니오 | 베타 기능 (예: prompt-caching-2024-07-31). 네이티브 Anthropic 백엔드로 전달됨 |
요청 본문:
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "안녕하세요, Claude!"
}
],
"stream": false
}
요청 파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model |
string | 예 | 모델 식별자 |
max_tokens |
integer | 예 | 생성할 최대 토큰 수 |
messages |
array | 예 | 메시지 객체 배열 |
system |
string/array | 아니오 | 시스템 프롬프트 (문자열 또는 콘텐츠 블록 배열) |
stream |
boolean | 아니오 | 스트리밍 활성화 (기본값: false) |
temperature |
number | 아니오 | 샘플링 온도 (0-1) |
top_p |
number | 아니오 | 핵 샘플링 파라미터 |
top_k |
integer | 아니오 | Top-k 샘플링 파라미터 |
stop_sequences |
array | 아니오 | 정지 시퀀스 |
metadata |
object | 아니오 | 요청 메타데이터 |
tools |
array | 아니오 | 함수 호출을 위한 도구 정의 |
tool_choice |
object | 아니오 | 도구 선택 설정 |
응답 (비스트리밍):
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "안녕하세요! 무엇을 도와드릴까요?"
}
],
"model": "claude-sonnet-4-20250514",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 15,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
백엔드 라우팅:
라우터는 대상 백엔드에 따라 요청을 자동으로 변환합니다:
| 백엔드 타입 | 변환 |
|---|---|
| Anthropic | 네이티브 API 형식으로 패스스루 |
| Gemini | Gemini 형식으로 직접 변환 |
| OpenAI/vLLM/Ollama | OpenAI 형식으로 변환 후 응답을 다시 변환 |
responses_only 모델이 적용된 OpenAI / Azure OpenAI |
Responses API 형식으로 변환해 /v1/responses로 dispatch한 뒤, 응답을 Anthropic Messages JSON(또는 Anthropic SSE 이벤트)으로 다시 변환 |
responses_only 모델용 투명한 Responses-API 라우팅¶
요청한 model이 model-metadata.yaml이나 내장 OpenAI 레지스트리에서 responses_only: true로 표시된 경우(예: gpt-5-pro, gpt-5.2-pro, gpt-5.4-pro, gpt-5.5-pro), 라우터는 Anthropic 형식 요청을 /v1/chat/completions 대신 업스트림 /v1/responses로 투명하게 보냅니다:
- 요청은 dispatch 전에 Anthropic Messages 형식에서 Responses API 모양으로 변환됩니다.
- 업스트림 응답은 Anthropic Messages JSON으로 다시 변환되며, 스트리밍의 경우 Anthropic SSE 이벤트 시퀀스로 변환됩니다.
- 도구 호출 왕복, 웹 검색 에뮬레이션, Unix 소켓 전송 모두 같은 플래그에서 분기합니다.
dispatch는 클라이언트에 투명합니다. 클라이언트는 /anthropic/v1/messages를 그대로 호출하고 Anthropic 형식 응답을 계속 받게 됩니다. /v1/responses는 OpenAI와 Azure OpenAI 백엔드만 제공하며, responses_only 모델이 다른 유형의 백엔드와 짝지어지면 업스트림 호출 전에 400 invalid_request_error(이 surface에서는 Anthropic 모양)가 반환됩니다. 전체 모델 목록과 설정 방법, 새 모델을 표시하는 방법은 Responses-API 전용 모델을 참조하세요.
파일 참조와 해석 한도¶
이미지와 문서 블록은 base64를 직접 싣는 대신 Files API로 업로드한 파일을 가리킬 수 있습니다:
이런 참조는 요청을 백엔드로 보내기 전에 모두 로드되어 base64로 인코딩되며, tool_result 콘텐츠 배열 안에 중첩된 참조도 포함됩니다. 참조마다 인코딩된 데이터 사본이 따로 생기므로, 요청 비용은 서로 다른 파일의 개수가 아니라 참조 개수에 비례해 늘어납니다. 요청 단위로 다음 한도가 적용됩니다:
| 한도 | 값 | 초과 시 |
|---|---|---|
| 요청당 참조 개수 | 100 | 400 invalid_request_error, 파일을 읽기 전에 판정 |
| 참조 파일 합계 크기 | 32 MiB | 400 invalid_request_error. 참조마다 계산하므로 10 MiB 파일을 네 번 참조하면 40 MiB로 계산됩니다 |
| 동시 파일 로드 | 8 | 오류 없음. 초과분은 슬롯이 날 때까지 대기 |
| 전체 해석 시간 | 30초 | 504 |
참조된 개별 파일에는 기존과 동일하게 10 MiB 상한이 추가로 적용됩니다.
참조 개수 상한은 /v1/chat/completions의 20개보다 의도적으로 높게 잡았습니다. Messages API는 매 턴마다 대화 전체를 다시 보내므로, 긴 멀티모달 세션에서는 단일 chat-completions 호출에서는 나올 수 없는 개수의 참조가 정상적으로 쌓입니다. base64는 원본 바이트를 약 4/3배로 늘리므로 32 MiB 예산은 출력 기준 약 43 MiB에 해당합니다.
토큰 카운팅¶
메시지 요청 페이로드의 토큰 수를 계산합니다.
요청 본문:
{
"model": "claude-sonnet-4-20250514",
"messages": [
{
"role": "user",
"content": "안녕하세요, 어떠세요?"
}
],
"system": "당신은 도움이 되는 어시스턴트입니다."
}
응답:
계층적 토큰 카운팅¶
토큰 카운팅은 백엔드 타입에 따라 다른 전략을 사용합니다:
| 백엔드 타입 | 전략 | 정확도 |
|---|---|---|
| Anthropic | 네이티브 /v1/messages/count_tokens API 프록시 |
정확 |
| llama.cpp | 백엔드 /tokenize 엔드포인트 프록시 |
정확 |
| vLLM | 백엔드 /tokenize 엔드포인트 프록시 |
정확 |
| 기타 | 문자 기반 추정 (~4자/토큰) | 근사 |
참고: 네이티브 토큰화를 지원하지 않는 백엔드의 경우, 라우터는 토큰당 약 4자를 기준으로 하는 문자 기반 추정을 사용합니다. 이는 계획 목적으로 합리적인 근사치를 제공하지만 모델이 사용하는 정확한 토큰 수와 일치하지 않을 수 있습니다.
모델 목록¶
Anthropic API 형식으로 사용 가능한 모델을 나열합니다.
응답:
{
"data": [
{
"id": "claude-sonnet-4-20250514",
"type": "model",
"display_name": "Claude Sonnet 4",
"created_at": "2025-05-14T00:00:00Z"
},
{
"id": "claude-opus-4-20250514",
"type": "model",
"display_name": "Claude Opus 4",
"created_at": "2025-05-14T00:00:00Z"
}
],
"has_more": false,
"first_id": "claude-sonnet-4-20250514",
"last_id": "claude-opus-4-20250514"
}
Claude Code 호환성¶
Anthropic 네이티브 API는 Claude Code 및 고급 기능이 필요한 기타 Anthropic API 클라이언트와의 완전한 호환성을 포함합니다.
프롬프트 캐싱¶
프롬프트 캐싱은 콘텐츠 블록의 cache_control 필드를 통해 완벽하게 지원됩니다:
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "당신은 광범위한 지식을 가진 유용한 코딩 어시스턴트입니다...",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "이전 컨텍스트...",
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": "현재 질문"
}
]
}
]
}
지원되는 cache_control 위치:
- 시스템 프롬프트 텍스트 블록
- 사용자 메시지 텍스트 블록
- 사용자 메시지 이미지 블록
- 도구 정의
- 도구 사용 블록
- 도구 결과 블록
베타 기능 헤더¶
anthropic-beta 헤더는 네이티브 Anthropic 백엔드로 전달되어 베타 기능을 활성화합니다:
curl -X POST http://localhost:8080/anthropic/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: prompt-caching-2024-07-31,interleaved-thinking-2025-05-14" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "안녕하세요"}]
}'
스트리밍에서의 캐시 사용량¶
네이티브 Anthropic 백엔드로 스트리밍할 때 캐시 사용량 정보가 message_start 이벤트에 포함됩니다:
{
"type": "message_start",
"message": {
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [],
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 2159,
"cache_creation_input_tokens": 2048,
"cache_read_input_tokens": 0
}
}
}
인터리브드 씽킹¶
인터리브드 출력을 포함한 확장 사고(Extended Thinking)는 스트리밍 모드에서 지원됩니다. 모델이 사고와 텍스트 콘텐츠를 교대로 생성할 때, 스트리밍 이벤트는 각 블록에 대해 적절한 content_block_start, content_block_delta, content_block_stop 이벤트와 함께 이 인터리브드 구조를 올바르게 표현합니다.
Admin API¶
admin 기능으로 컴파일하면 관리 경로가 /admin 아래에 마운트됩니다. admin.auth를 설정하지 않으면 인증은 비활성 상태입니다.
자주 사용하는 조회 작업:
GET /admin/capabilities
GET /admin/health
GET /admin/backends
GET /admin/models
GET /admin/config/full
GET /admin/config/hot-reload-status
GET /admin/stats
변경, 스키마, 가져오기/내보내기, API 키, 프롬프트, 캐시, 스마트 라우팅, 가드레일, Files, ACP, 기능 조건부 컨트롤 플레인 경로는 Admin REST API 레퍼런스를 참고하세요. 해당 문서에는 현재의 재적용 경계와 대기 중인 변경을 적용하는 것처럼 사용하면 안 되는 설정 엔드포인트도 설명되어 있습니다.
오류 처리¶
오류 응답 형식¶
모든 오류는 일관된 JSON 구조를 따릅니다:
{
"error": {
"message": "Human-readable error description",
"type": "error_type_identifier",
"code": 404,
"details": {
"additional": "context information"
}
}
}
오류 타입¶
| 타입 | HTTP 코드 | 설명 |
|---|---|---|
bad_request |
400 | 유효하지 않은 요청 형식 또는 파라미터 |
unauthorized |
401 | 인증 필요 (차단 인증 모드) |
forbidden |
403 | 접근 거부 (스코프 또는 소유권 부족) |
model_not_found |
404 | 요청된 모델을 사용할 수 없음 |
rate_limit_exceeded |
429 | 속도 제한 초과 |
internal_error |
500 | 라우터 내부 오류 |
bad_gateway |
502 | 백엔드 연결/응답 오류 |
service_unavailable |
503 | 모든 백엔드가 비정상 |
gateway_timeout |
504 | 백엔드 요청 타임아웃 |
오류 응답 예제¶
모델을 찾을 수 없음:
{
"error": {
"message": "Model 'invalid-model' not found on any healthy backend",
"type": "model_not_found",
"code": 404,
"details": {
"requested_model": "invalid-model",
"available_models": ["gpt-4", "gpt-3.5-turbo", "llama2"]
}
}
}
백엔드 오류:
{
"error": {
"message": "Failed to connect to backend 'local-ollama'",
"type": "bad_gateway",
"code": 502,
"details": {
"backend": "local-ollama",
"backend_error": "Connection refused"
}
}
}
서비스 사용 불가:
{
"error": {
"message": "All backends are currently unhealthy",
"type": "service_unavailable",
"code": 503,
"details": {
"healthy_backends": 0,
"total_backends": 3
}
}
}
속도 제한¶
설정에서 rate_limiting을 활성화하면 라우터가 여러 차원에 걸쳐 토큰 버킷 제한을 적용합니다:
- 클라이언트(IP 주소)별 제한
- API 키별 제한 (키별 오버라이드 포함)
- 백엔드별 및 전역 제한
- 모델별 제한
제한을 초과한 요청은 Retry-After 헤더와 함께 429 Too Many Requests를 받고, 제한 내 응답에는 X-RateLimit-* 헤더가 실립니다. 설정, 클라이언트 식별, 우회 옵션, 스토리지 백엔드는 속도 제한을 참조하세요.
스트리밍¶
Server-Sent Events (SSE)¶
stream: true가 지정되면 응답은 다음과 함께 Server-Sent Events로 전송됩니다:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
SSE 형식¶
data: {"id":"chatcmpl-123","object":"chat.completion.chunk",...}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk",...}
data: [DONE]
SSE 호환성¶
라우터는 최대 호환성을 위해 여러 SSE 형식을 지원합니다:
- 표준 형식:
data: {...} - 스페이스 형식:
data: {...} - 혼합 줄 바꿈:
\r\n,\n,\r처리 - 빈 줄: 청크 구분자 올바르게 처리
연결 관리¶
- Keep-Alive: 스트리밍 중 연결 유지
- 타임아웃: 장시간 실행 요청에 대해 5분 타임아웃
- 오류 처리: 부분 응답에 오류 정보 포함
- 클라이언트 연결 해제: 클라이언트 연결 해제를 정상적으로 처리
예제¶
기본 채팅 완료¶
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "Hello, how are you?"}
]
}'
스트리밍 채팅 완료¶
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "Write a short story"}
],
"stream": true,
"max_tokens": 200
}'
파라미터가 포함된 텍스트 완료¶
curl -X POST http://localhost:8080/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo-instruct",
"prompt": "The future of AI is",
"max_tokens": 50,
"temperature": 0.8,
"top_p": 0.9
}'
백엔드 상태 확인¶
서비스 헬스 모니터링¶
사용 가능한 모델 목록¶
Python 클라이언트 예제¶
import requests
import json
# 클라이언트 설정
BASE_URL = "http://localhost:8080"
def chat_completion(messages, model="gpt-3.5-turbo", stream=False):
"""채팅 완료 요청 전송"""
response = requests.post(
f"{BASE_URL}/v1/chat/completions",
headers={"Content-Type": "application/json"},
json={
"model": model,
"messages": messages,
"stream": stream,
"temperature": 0.7
},
stream=stream
)
if stream:
# 스트리밍 응답 처리
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:] # 'data: ' 접두사 제거
if data == '[DONE]':
break
try:
chunk = json.loads(data)
content = chunk['choices'][0]['delta'].get('content', '')
if content:
print(content, end='', flush=True)
except json.JSONDecodeError:
continue
print() # 스트리밍 후 새 줄
else:
# 비스트리밍 응답 처리
result = response.json()
return result['choices'][0]['message']['content']
# 사용 예제
messages = [
{"role": "user", "content": "Explain machine learning in simple terms"}
]
print("스트리밍 응답:")
chat_completion(messages, stream=True)
print("\n비스트리밍 응답:")
response = chat_completion(messages, stream=False)
print(response)
JavaScript/Node.js 클라이언트 예제¶
const fetch = require('node-fetch');
const BASE_URL = 'http://localhost:8080';
async function chatCompletion(messages, options = {}) {
const response = await fetch(`${BASE_URL}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: options.model || 'gpt-3.5-turbo',
messages: messages,
stream: options.stream || false,
temperature: options.temperature || 0.7,
...options
})
});
if (options.stream) {
// 스트리밍 응답 처리
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
} catch (e) {
// JSON 파싱 오류 무시
}
}
}
}
console.log(); // 새 줄
} else {
const result = await response.json();
return result.choices[0].message.content;
}
}
// 사용 예제
const messages = [
{ role: 'user', content: 'What is the meaning of life?' }
];
// 스트리밍
console.log('스트리밍 응답:');
await chatCompletion(messages, { stream: true });
// 비스트리밍
console.log('\n비스트리밍 응답:');
const response = await chatCompletion(messages);
console.log(response);
다음 단계로는 운영 환경에 따라 Admin REST API로 런타임 설정을 조작하거나, 속도 제한과 로드 밸런싱 가이드에서 트래픽 제어 옵션을 확인할 수 있습니다. 오류 코드와 재시도 전략은 오류 처리에 정리되어 있습니다.