콘텐츠로 이동

API 레퍼런스

Continuum Router는 OpenAI 호환 API를 제공하며, 모니터링과 관리를 위한 추가 관리 엔드포인트도 함께 노출합니다. 이 문서는 사용 가능한 모든 엔드포인트, 요청/응답 형식, 오류 처리에 대한 상세 정보를 담고 있습니다.

개요

기본 URL

http://localhost:8080

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 엔드포인트

헬스 체크

라우터 서비스의 헬스 상태를 확인합니다.

GET /health

응답:

{
  "status": "ok",
  "service": "continuum-router",
  "version": "1.14.0"
}

상태 코드:

  • 200: 서비스가 정상

버전

실행 중인 라우터 버전을 반환합니다.

GET /version

응답:

{
  "version": "1.14.0"
}

상태 코드:

  • 200: 항상 반환되며 인증이 필요하지 않음

모델 목록

모든 정상 백엔드에서 사용 가능한 모든 모델을 조회합니다.

GET /v1/models

응답:

{
  "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/proxy

라우터가 중복 제거한 통합 카탈로그에는 GET /v1/models를 사용하십시오. 단일 백엔드의 제공자 네이티브 모델 목록 페이로드가 필요한 클라이언트에만 이 엔드포인트를 사용하십시오.

상태 코드:

  • 200: 선택된 백엔드 응답 반환
  • 502: 백엔드 연결 또는 게이트웨이 오류
  • 503: 적격 정상 백엔드 없음

모델 강제 새로고침

모델 집계 캐시를 즉시 무효화하고 구성된 모든 백엔드에서 동기적으로 다시 집계합니다. GET /v1/models와 동일한 OpenAI 호환 모델 목록 형태를 반환하므로 호출자가 응답을 바로 사용할 수 있습니다.

이 엔드포인트는 데스크톱 클라이언트의 대화형 '새로고침' 동작 (예: backend.ai-go의 '모델 새로고침' 버튼)을 위해 설계되었습니다. stale-while-revalidate와 소프트 TTL 윈도우를 우회하여, 응답하기 전에 모든 백엔드에서 업스트림 가져오기를 트리거합니다.

POST /v1/models/refresh

응답:

{
  "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, 요청 병합, 제한된 빈 결과 백오프를 사용합니다. 이 값과 강제 새로고침 사용 여부는 공개 설정 스키마의 필드가 아닙니다.


단일 모델 조회

특정 모델의 정보를 가용성 상태, 그리고 선택적으로 제공되는 풍부한 메타데이터와 함께 조회합니다.

GET /v1/models/{model}

경로 파라미터:

파라미터 타입 필수 설명
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: 캐시가 백그라운드에서 갱신되는 동안 오래된 데이터를 제공

요청 예시:

curl http://localhost:8080/v1/models/gpt-4o

응답 예시 (전체 메타데이터와 함께 모델 사용 가능):

{
  "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 형식을 사용하여 채팅 완료를 생성합니다.

POST /v1/chat/completions

요청 본문:

{
  "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)에서만 지원됩니다.
    • **automedium으로 자동 다운그레이드됩니다.

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 형식으로 변환하여 백엔드에서 처리합니다.

POST /v1/responses

요청 본문:

{
  "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 모델에 반환되는 도구 호출 결과. 기존과 동일하게 동작합니다.

developersystem 메시지가 여러 개 들어오면 라우터는 도착 순서를 그대로 유지합니다. 대상 백엔드(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 형식:

{
  "type": "input_image",
  "image_url": "data:image/png;base64,iVBORw0KGgo...",
  "detail": "high"
}
필드 유형 설명
image_url string 이미지 URL 또는 base64 데이터 URL
file_id string Files API를 통해 업로드된 이미지 파일 참조
detail string 이미지 상세 수준: low, high 또는 auto (기본값)

image_urlfile_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 파라미터는 추론 가능 모델의 추론 노력 수준을 제어합니다. 중첩 형식을 사용합니다:

{
  "reasoning": {
    "effort": "high"
  }
}

유효한 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 /v1/responses/{response_id}
DELETE /v1/responses/{response_id}

성공한 GET은 저장된 응답 객체를 반환합니다. 성공한 DELETE는 다음을 반환합니다.

{
  "id": "resp_abc123",
  "object": "response.deleted",
  "deleted": true
}

Responses Compact

input 항목 시퀀스를 요약하거나 압축해 대화 컨텍스트를 줄입니다. OpenAI와 Azure OpenAI 백엔드의 경우 요청이 백엔드의 네이티브 /v1/responses/compact 엔드포인트로 그대로 전달됩니다. 다른 백엔드는 컨텍스트 압축을 네이티브로 지원하지 않으므로 HTTP 501(Not Implemented)을 반환합니다.

POST /v1/responses/compact

요청 본문:

{
  "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) 모델을 사용하여 이미지를 생성합니다.

POST /v1/images/generations

요청 본문:

{
  "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 모델에서 standardmedium으로, hdhigh로 매핑됩니다.

품질 설명
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의 aspectRatioimageSize 형식으로 자동 변환합니다:

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를 지원합니다.

POST /v1/images/edits
Content-Type: multipart/form-data

요청 파라미터 (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 포함):

{
  "created": 1677652288,
  "data": [
    {
      "b64_json": "/9j/4AAQSkZJRgABAQAA..."
    }
  ]
}

상태 코드:

  • 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 모델을 사용하여 기존 이미지의 변형을 생성합니다.

POST /v1/images/variations
Content-Type: multipart/form-data

폼 필드:

파라미터 타입 필수 설명
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 포함):

{
  "created": 1677652288,
  "data": [
    {
      "b64_json": "/9j/4AAQSkZJRgABAQAA..."
    }
  ]
}

모델 지원:

모델 변형 지원 참고
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 형식을 사용하여 텍스트 완료를 생성합니다.

POST /v1/completions

요청 본문:

{
  "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 형식을 사용하여 입력 텍스트에 대한 임베딩을 생성합니다.

POST /v1/embeddings

요청 본문:

{
  "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-small",
  "input": "Hello, world!"
}

문자열 배열:

{
  "model": "text-embedding-3-large",
  "input": [
    "임베딩할 첫 번째 텍스트",
    "임베딩할 두 번째 텍스트",
    "임베딩할 세 번째 텍스트"
  ]
}

토큰 배열:

{
  "model": "text-embedding-ada-002",
  "input": [[1234, 5678, 9012], [3456, 7890, 1234]]
}

멀티모달 콘텐츠 파트 (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-2gemini-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단계 검색 단계로 사용됩니다.

POST /v1/rerank

요청 본문:

{
  "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_documentstrue인 경우에만 존재)
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)은 명시적인 용어 가중치를 통해 어휘 정보를 보존하여 하이브리드 검색에서 밀집 임베딩을 보완합니다.

POST /embed_sparse

요청 본문:

{
  "model": "naver/splade-v3",
  "input": "희소 임베딩이란 무엇인가요?"
}

요청 파라미터:

필드 타입 필수 설명
model string 사용할 희소 임베딩 모델의 ID (예: naver/splade-v3, naver/splade-cocondenser-ensembledistil)
input string 또는 array 임베딩할 입력 텍스트. 단일 문자열 또는 문자열 배열이 될 수 있음

입력 형식 예시:

단일 문자열:

{
  "model": "naver/splade-v3",
  "input": "희소 임베딩이란 무엇인가요?"
}

문자열 배열:

{
  "model": "naver/splade-v3",
  "input": [
    "임베딩할 첫 번째 텍스트",
    "임베딩할 두 번째 텍스트",
    "임베딩할 세 번째 텍스트"
  ]
}

응답:

{
  "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 콘텐츠 타입을 사용하여 메시지에서 참조할 수 있으며, 라우터가 파일 콘텐츠를 주입하여 이러한 참조를 자동으로 해결합니다.

파일 업로드

채팅 완료에서 사용할 파일을 업로드합니다.

POST /v1/files
Content-Type: multipart/form-data

폼 필드:

필드 타입 필수 설명
file file 업로드할 파일
purpose string 파일의 용도: vision, assistants, fine-tune, batch, user_data, evals

예제:

curl -X POST http://localhost:8080/v1/files \
  -F "file=@image.png" \
  -F "purpose=vision"

응답:

{
  "id": "file-abc123def456",
  "object": "file",
  "bytes": 12345,
  "created_at": 1699061776,
  "filename": "image.png",
  "purpose": "vision"
}

상태 코드:

  • 200: 파일이 성공적으로 업로드됨
  • 400: 유효하지 않은 요청 (파일 누락, 유효하지 않은 용도)
  • 413: 파일이 너무 큼 (설정된 max_file_size 초과)

파일 목록

업로드된 파일 목록을 조회합니다.

GET /v1/files
GET /v1/files?purpose=vision

쿼리 파라미터:

파라미터 타입 필수 설명
purpose string 아니오 용도별 필터링

응답:

{
  "object": "list",
  "data": [
    {
      "id": "file-abc123def456",
      "object": "file",
      "bytes": 12345,
      "created_at": 1699061776,
      "filename": "image.png",
      "purpose": "vision"
    }
  ]
}


파일 메타데이터 가져오기

특정 파일의 메타데이터를 조회합니다.

GET /v1/files/{file_id}

응답:

{
  "id": "file-abc123def456",
  "object": "file",
  "bytes": 12345,
  "created_at": 1699061776,
  "filename": "image.png",
  "purpose": "vision"
}

상태 코드:

  • 200: 파일 메타데이터 조회됨
  • 404: 파일을 찾을 수 없음

파일 콘텐츠 다운로드

업로드된 파일의 콘텐츠를 다운로드합니다.

GET /v1/files/{file_id}/content

응답: 적절한 Content-Type 헤더와 함께 바이너리 파일 콘텐츠.

상태 코드:

  • 200: 파일 콘텐츠 반환됨
  • 404: 파일을 찾을 수 없음

파일 삭제

업로드된 파일을 삭제합니다.

DELETE /v1/files/{file_id}

응답:

{
  "id": "file-abc123def456",
  "object": "file",
  "deleted": true
}

상태 코드:

  • 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"
  }
}

서버 쪽 오류는 라우터 내부 진단 문구 대신 고정된 메시지를 반환합니다. typecode는 원인을 그대로 구분해주므로, 이 두 필드로 분기하는 클라이언트는 영향을 받지 않습니다.

{
  "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 콘텐츠 블록이 포함되면 라우터는:

  1. 파일 ID 형식을 검증합니다
  2. 스토리지에서 파일 콘텐츠를 로드합니다
  3. 파일을 base64 data URL로 변환합니다
  4. image_file 블록을 image_url 블록으로 대체합니다

지원되는 파일 타입

채팅 완료에서의 파일 해결은 다음 파일 타입을 지원합니다:

파일 타입 MIME 타입 지원
PNG image/png 모든 백엔드
JPEG image/jpeg 모든 백엔드
GIF image/gif 모든 백엔드
WebP image/webp 모든 백엔드
PDF 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 형식을 사용하여 메시지를 전송합니다.

POST /anthropic/v1/messages

헤더:

헤더 필수 설명
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 라우팅

요청한 modelmodel-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로 업로드한 파일을 가리킬 수 있습니다:

{"type": "image", "source": {"type": "file_id", "file_id": "file-abc123def456"}}

이런 참조는 요청을 백엔드로 보내기 전에 모두 로드되어 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에 해당합니다.

토큰 카운팅

메시지 요청 페이로드의 토큰 수를 계산합니다.

POST /anthropic/v1/messages/count_tokens

요청 본문:

{
  "model": "claude-sonnet-4-20250514",
  "messages": [
    {
      "role": "user",
      "content": "안녕하세요, 어떠세요?"
    }
  ],
  "system": "당신은 도움이 되는 어시스턴트입니다."
}

응답:

{
  "input_tokens": 18
}

계층적 토큰 카운팅

토큰 카운팅은 백엔드 타입에 따라 다른 전략을 사용합니다:

백엔드 타입 전략 정확도
Anthropic 네이티브 /v1/messages/count_tokens API 프록시 정확
llama.cpp 백엔드 /tokenize 엔드포인트 프록시 정확
vLLM 백엔드 /tokenize 엔드포인트 프록시 정확
기타 문자 기반 추정 (~4자/토큰) 근사

참고: 네이티브 토큰화를 지원하지 않는 백엔드의 경우, 라우터는 토큰당 약 4자를 기준으로 하는 문자 기반 추정을 사용합니다. 이는 계획 목적으로 합리적인 근사치를 제공하지만 모델이 사용하는 정확한 토큰 수와 일치하지 않을 수 있습니다.

모델 목록

Anthropic API 형식으로 사용 가능한 모델을 나열합니다.

GET /anthropic/v1/models

응답:

{
  "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
  }'

백엔드 상태 확인

curl http://localhost:8080/admin/backends | jq

서비스 헬스 모니터링

curl http://localhost:8080/admin/health | jq '.services'

사용 가능한 모델 목록

curl http://localhost:8080/v1/models | jq '.data[].id'

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로 런타임 설정을 조작하거나, 속도 제한로드 밸런싱 가이드에서 트래픽 제어 옵션을 확인할 수 있습니다. 오류 코드와 재시도 전략은 오류 처리에 정리되어 있습니다.