Skip to content

OpenAI

OPENAI_API_KEY로 매핑된 모델을 OpenAI Responses API로 라우팅하기 — 변환, 노력, 토큰 카운팅, 도구 검색.

Updated View as Markdown

내장 openai 프로바이더는 매핑된 모델의 추론을 OpenAI 플랫폼 API(api.openai.com)로 라우팅하며, OPENAI_API_KEY에 대해 토큰당 과금됩니다. 이는 kind = "responses" 프로바이더입니다: shunt는 Claude Code의 Anthropic Messages 요청을 OpenAI Responses API로 변환하고, 응답을 다시 스트리밍하며, 그것을 또 한 번 변환합니다 — 도구, 이미지, 스트리밍을 포함해서요.

빠른 시작

코딩 에이전트가 대신 구성하도록 하세요 — shunt add는 내장된 설정 블루프린트를 출력합니다 (오프라인·읽기 전용이며, 구성은 에이전트가 편집하고 이 명령은 절대 편집하지 않습니다):

shunt add upstream openai --print | claude

또는 아래의 수동 단계를 따르세요.

업스트림 구성

openai 프리셋은 kind = "responses", base_url = "https://api.openai.com/v1"(shunt가 /responses를 붙입니다), 그리고 OPENAI_API_KEY에서 오는 API 키 인증을 제공합니다:

[[upstreams]]
name = "anthropic"
provider = "anthropic"   # 라우트가 없는 모델(예: claude-*)의 기본값으로 Anthropic을 유지

[[upstreams]]
name = "openai"
provider = "openai"
# effort = "high"          # 선택적 기본 추론 노력
# count_tokens = "tiktoken" # 기본값; "estimate"는 로컬 카운팅을 끕니다

순서가 있는 [[upstreams]]는 shunt의 내장 프로바이더를 대체하므로, 구성은 여전히 폴백 대상인 anthropic 기본값을 선언해야 합니다(server.default_provider의 기본값은 anthropic입니다).

명시적으로 지정한 필드는 프리셋 기본값을 오버라이드합니다. 레거시 [providers.openai] 테이블 형식도 계속 지원됩니다 — 다만 한 파일에서 [[upstreams]]와 [providers.*]를 섞지 마세요.

자격 증명

shunt를 기동하는 환경에 키를 내보내세요 — 구성 파일에는 절대 쓰지 마세요:

export OPENAI_API_KEY='...'

shunt check는 구성의 구조를 검증하지만 키의 값을 읽지는 않습니다 — OPENAI_API_KEY가 설정되어 있지 않으면 openai로 라우팅된 첫 요청이 인증 오류를 반환합니다.

모델 & 라우팅

id를 정확히 라우팅하거나, 프리픽스로 라우팅하거나, Claude 이름의 별칭을 모델 디스커버리에 광고하세요:

# 권장: 노출 + 라우팅 + 변환을 한 선언으로
[[models]]
id = "claude-gpt-5-4-via-openai"
display_name = "GPT-5.4 (via OpenAI)"

[models.upstream_model]
openai = "gpt-5.4"

# 또는 클라이언트가 보내는 모든 gpt-* id를 받기
[[route_prefixes]]
prefix = "gpt-"
provider = "openai"

슬러그를 고르기 전에 본인 OpenAI 계정에서 현재 모델 가용성을 확인하세요. ChatGPT 계정 Codex 백엔드와 달리, 플랫폼 API는 자신의 일반 공개 슬러그를 받아들입니다.

추론 노력, 컨텍스트, 토큰 카운팅

Responses 변환은 프로바이더별 또는 라우트별 추론 노력 다이얼(low … max)과, Claude Code의 컨텍스트 집계가 계속 동작하도록 하는 로컬 tiktoken 기반 count_tokens를 지원합니다. 둘 다 다른 Responses 프로바이더와 공유되며 노력 & 컨텍스트에 문서화되어 있습니다.

도구 검색. Claude Code의 ENABLE_TOOL_SEARCH=true가 설정되면, shunt는 Responses 경로의 지연된 도구 디스커버리를 GPT-5.4+ 모델의 네이티브 클라이언트 실행 tool_search 프로토콜에 매핑합니다. 이 프로바이더는 api.openai.com을 대상으로 하는데, 이는 설정되지 않은(“auto”) 기본값이 tool_search 항목을 구현한다고 이미 신뢰하는 두 호스트 중 하나이므로 — ChatGPT/Codex 백엔드와 나란히 — 네이티브를 공짜로 얻습니다. 커스텀 OpenAI 호환 엔드포인트(LiteLLM, vLLM, OpenRouter, 자체 호스팅)는 같은 방식으로 자동 활성화되지 않으며 옵트인하려면 tool_search = true가 필요합니다. 여기서 tool_search = false로 설정하면 대신 텍스트 심을 강제할 수 있습니다. 도구 검색을 참고하세요.

정지 시퀀스

Responses API에는 stop 파라미터가 없습니다 — Chat Completions에는 있지만 Responses에는 없습니다 — 따라서 클라이언트의 Anthropic stop_sequences는 업스트림으로 전달할 수 없습니다. 대신 shunt가 Responses 변환 과정에서 게이트웨이 측으로 이를 흉내 냅니다. 모든 Responses 프로바이더와 모든 전송 방식(HTTP와 WebSocket, 스트리밍과 비스트리밍)에 적용됩니다.

  • 어시스턴트 텍스트는 정지 시퀀스가 처음 나타나는 지점에서 잘리며, 그 뒤의 텍스트는 클라이언트에 전달되지 않습니다.
  • 턴은 stop_reason: "stop_sequence"와 일치한 문자열을 담은 stop_sequence로 종료됩니다. 네이티브 Anthropic 업스트림이 보고하는 방식과 동일합니다.
  • 일치 시점에 업스트림 요청을 중단하며, 그대로 두지 않습니다 — 스트리밍 HTTP와 두 WebSocket 경로가 해당합니다. 비스트리밍 HTTP는 예외로, 응답 본문을 모두 읽은 뒤에야 변환하므로 자르는 동작은 다른 경로와 같지만 업스트림은 끝까지 진행됩니다. 일치 시점부터 중단까지 업스트림이 생성한 토큰은 여전히 업스트림에서 과금되지만, 정지 시퀀스를 주로 쓰는 짧은 응답에서는 무시할 만한 양입니다.

한 가지 알아둘 점이 있습니다. 정지 때문에 업스트림의 response.completed 사용량이 반영되기 전에 턴이 끝나므로, shunt는 자체 로컬 입력 추정치로 대체하고 output_tokens: 0을 보고합니다 — 스트리밍 턴에서는 message_delta.usage에, 비스트리밍 턴에서는 응답의 usage에 담깁니다. 이 추정치에는 프로바이더의 count_tokens = "tiktoken" 옵트인이 필요하며, 이를 켜지 않으면 정지된 턴은 input_tokens: 0을 보고합니다.

검증

shunt check    # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/models
curl -sS http://127.0.0.1:3001/v1/messages \
  -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' \
  -d '{"model":"claude-gpt-5-4-via-openai","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'

응답의 x-gateway-upstream 헤더가 openai를 가리키는지 확인한 다음, Claude Code를 shunt로 지정하세요.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close