백엔드 패스스루 계약¶
일부 백엔드 타입의 경우, Continuum Router는 요청이 전송 계층에 도달한 시점부터는 채팅 완성 요청 바디를 백엔드별 재구성 없이 그대로 전달합니다. 이는 엣지에서 업스트림까지의 바이트 동등성 보증보다 좁은 범위입니다. 앞단의 핸들러 단계는 make_http_request, make_unix_socket_request, LlamaCppBackend::execute_chat_completion이 페이로드를 보기 전에 여전히 이를 수정할 수 있습니다. 이 문서는 전송 계층 보증을 제공하는 백엔드, 알려진 소비자가 의존하는 필드, 그리고 요청을 변환하는 백엔드와의 경계를 정의합니다.
패스스루 백엔드¶
다음 백엔드 타입은 채팅 완성 전송 경로에서 백엔드별 변환을 적용하지 않습니다. 요청이 해당 경로에 도달한 뒤에는 비표준 최상위 필드, extra_body, 인식할 수 없는 키가 모두 변경 없이 전달됩니다.
OpenAI(아래 클라우드 경계 예외 참고)Llamacpp(llama-server)Mlxcel(LlamaCppBackend재사용;src/infrastructure/backends/factory/backend_factory.rs참고)OllamavLLMSGLang(VLLMBackend재사용;src/infrastructure/backends/factory/backend_factory.rs참고)LMStudioLocalAI
패스스루에는 동등한 세 가지 프록시 지점과 팩토리 경유 백엔드 지점 하나가 있습니다.
- HTTP 경로:
src/proxy/backend.rs::make_http_request. 주 패스스루 지점으로, 비-Anthropic, 비-Gemini 분기의else블록에서payload.clone()을 호출해 바디를 그대로 전달합니다. - Unix 소켓 경로: 같은 파일의
make_unix_socket_request내 형제else블록으로,backend.transport가UnixSocket일 때 실행됩니다. - 스트리밍 경로:
src/http/streaming/handler.rs의client.post(&backend_url).json(¤t_payload)호출 지점으로, 스트리밍 폴백 루프의 각 시도에서current_payload를 그대로 전달합니다. - 팩토리 경유 llama.cpp / MLxcel 경로:
src/infrastructure/backends/llamacpp/backend.rs::LlamaCppBackend::execute_chat_completion.src/infrastructure/backends/factory/backend_factory.rs를 통해BackendTypeConfig::Llamacpp와BackendTypeConfig::Mlxcel에 대해 실행됩니다.
바로 아래에서 설명하는 세 가지 경계 예외를 제외하면, 네 지점 모두 공급자별 변환을 적용하지 않습니다.
클라우드 OpenAI 예외¶
OpenAI 백엔드 타입은 OpenAI 클라우드와 로컬 OpenAI 호환 엔진을 모두 포괄하는데, 두 쪽은 알 수 없는 필드를 다르게 다룹니다. api.openai.com은 인식할 수 없는 최상위 키가 하나라도 있으면 HTTP 400으로 거부하고, 로컬 엔진은 그 필드들을 실제로 소비합니다. 그래서 전송 지점들은 게이트가 걸린 필터 하나를 적용합니다. /v1/chat/completions 요청의 목적지 URL에 api.openai.com이 포함되면, 엔진 전용 필드인 chat_template_kwargs, thinking_budget_tokens, enable_thinking, preserve_thinking, top_k, min_p, repeat_penalty(src/infrastructure/backends/field_filter.rs의 NON_OPENAI_FIELDS)를 전송 전에 제거합니다.
- 제거는 클라우드 OpenAI로 채팅을 보내는 모든 지점에서 실행됩니다.
make_http_request의 비스트리밍 패스스루 분기, 스트리밍 전송, 그리고 mid-stream 폴백과 자동 선택 폴백 루프의 각 홉이 해당합니다. 폴백은 백엔드를 바꿀 수 있으므로 게이트는 홉마다 다시 평가됩니다. - 같은 차단 목록이 클라우드 Gemini 제거에도 쓰입니다(Google의
/v1beta/openai/chat/completions엔드포인트도 필드 이름을 똑같이 엄격하게 검증합니다). Azure OpenAI와 Anthropic/v1/messages브리지는 별도 표면이라 이 게이트의 대상이 아닙니다. - 제거는 최상위 필드에만 적용됩니다.
extra_body와reasoning_effort는 절대 건드리지 않습니다.extra_body는 공급자별 설정을 위한 의도된 통로이고,reasoning_effort는 클라우드 OpenAI가 직접 받아들입니다(클라우드 Gemini는thinking_level로 매핑합니다). - 로컬 엔진 URL에는
api.openai.com이 들어가지 않으므로, llama.cpp, vLLM, SGLang, MLxcel, Ollama, LM Studio, LocalAI에는 이 필터가 아무 동작도 하지 않고 패스스루 보증도 그대로 유지됩니다.
Anthropic 고유 필드 예외¶
클라이언트를 향한 OpenAI 인그레스는 표준 chat-completions 본문 위에 실려 오는 Anthropic 고유 확장 필드 세 개를 받습니다. speed(fast mode)와 확장 사고 쌍인 thinking, output_config입니다(src/infrastructure/backends/field_filter.rs의 ANTHROPIC_NATIVE_FIELDS). 라우터는 이들을 디스패치의 Anthropic 쪽에서 변환합니다. 이 이름들을 읽는 OpenAI 와이어 대상은 없습니다. llama.cpp는 chat_template_kwargs와 thinking_budget_tokens를, vLLM과 SGLang은 chat_template_kwargs를 읽으며, 모든 대상이 이해하는 표준 추론 표기는 reasoning_effort입니다. 클라우드 OpenAI, Azure OpenAI, 클라우드 Gemini는 알 수 없는 최상위 키에 HTTP 400을 돌려주므로, 하나라도 새어 나가면 무시되는 것이 아니라 요청 자체가 실패합니다.
따라서 선택된 백엔드의 설정값 backend_type이 OpenAI 와이어를 쓰는 경우, /v1/chat/completions 본문에서 이 필드들을 모든 전송 지점에서 제거합니다. make_http_request와 make_unix_socket_request의 비스트리밍 패스스루 분기, 스트리밍 전송의 build_openai_chat_request_core(직접 스트리밍, mid-stream 홉 루프, 자동 백엔드 선택을 모두 포함), stream_via_unix_socket, 그리고 GeminiBackend::transform_request입니다.
- 게이트의 기준은 설정된 타입이며, URL도 모델 이름도 아닙니다. 위의 클라우드 OpenAI 제거와 달리 로컬 엔진도 대상입니다. 이 이름들을 읽는 로컬 엔진 역시 없기 때문입니다.
Generic,Openai,Azure,Gemini,Vllm,Ollama,Llamacpp,Mlxcel,Lmstudio,Sglang은 제거합니다.match에는 포괄 분기가 없어서, 백엔드 타입이 새로 생기면 반드시 명시적으로 결정하게 됩니다.Anthropic과Bedrock은 유지합니다.thinking과output_config를 실제로 읽는 코드가 이들 변환기이고,speed는 백엔드의 별도 옵트인인anthropic_fast_mode가 계속 관장합니다.Continuumrouter도 유지합니다. 하위 Continuum Router는 동일한 표준 확장 필드를 이해하고 자기 백엔드에 대해 같은 규칙을 적용하므로, 여기서 제거하면 Anthropic을 앞단에 둔 하위 라우터로 가는 홉에서 클라이언트의 추론 의도가 사라집니다.- 패스스루를 그대로 유지하는 경우는 선택된 백엔드의 이름이 전송 지점이 참조하는 설정 스냅샷에 없을 때뿐입니다. 예를 들어 요청이 진행 중인 동안 핫 리로드가 그 백엔드를 삭제하거나 이름을 바꾼 경우입니다.
type:을 생략한 백엔드는 이 경우가 아닙니다.backend_type은 기본값generic으로 해석되어 다른 OpenAI 와이어 유형과 마찬가지로 제거됩니다. - 제거는 최상위 필드에만 적용됩니다. 클라우드 OpenAI 제거와 같은 이유로
extra_body와reasoning_effort는 절대 건드리지 않습니다. - 제거 직전에 같은 게이트가, 제거가 일어나는 바로 그 요청들에 한해 변환을 먼저 수행합니다. thinking 설정을 선택된 대상이 받아들이는
reasoning_effort값으로 바꾸므로, 클라이언트의 추론 의도가 버려지는 대신 모든 OpenAI 와이어 대상이 이해하는 단 하나의 표기로 홉을 건너갑니다. 클라이언트가 보낸reasoning_effort(또는reasoning.effort)가 항상 우선하며, 형식이 잘못된 설정은 단순 제거로 격하됩니다. 매핑 표와 대상별 어휘 규칙은 모델 폴백에 정리되어 있습니다.
OpenAI 채팅 완성 호환성 예외¶
실제 백엔드와 모델을 선택한 뒤, OpenAI 및 Azure OpenAI 채팅 완성 요청은 공급자 경계에서 하나의 멱등 호환성 변환을 거칩니다. o1, o3, 구분자가 있는 GPT-5 계열(예: gpt-5, gpt-5-2025-08-07, gpt-5.6-luna)에서는 레거시 max_tokens 필드 이름을 max_completion_tokens로 바꿉니다. 대소문자를 구분하지 않되 계열 경계를 확인하므로 o10, gpt-50, gpt-5o처럼 관련 없는 식별자는 일치하지 않습니다.
선택된 HTTP 및 Unix 소켓 프록시 전송, 스트리밍 시도와 폴백, Anthropic Messages 호환성 브리지, 타입 기반 OpenAI 채팅 및 스트리밍 호출, 컨트롤 플레인 프로브, OpenAIBackend::transform_request가 모두 같은 변환을 사용합니다. 공개 continuum_router::proxy::transform_payload_for_openai 헬퍼도 이 단일 구현을 감싸는 얇은 래퍼로 유지됩니다.
변환의 우선순위와 보존 규칙은 다음과 같습니다.
max_tokens만 있으면 그 값을max_completion_tokens로 옮깁니다.- 두 필드가 모두 있으면 호출자가 제공한
max_completion_tokens값이 우선하며max_tokens는 제거합니다. - 변환을 다시 적용해도 결과는 달라지지 않습니다.
temperature,top_p,logprobs같은 샘플링 제어와extra_body및 관련 없는 모든 필드는 그대로 유지됩니다. - 모델 이름만으로는 OpenAI 규칙이 활성화되지 않습니다. Gemini와 자체 호스팅 백엔드는 선택된 모델 이름이
gpt-5.6-luna여도 원래 토큰 필드를 유지합니다.
운영자 설정 요청 확장¶
request_extensions를 설정한 백엔드(요청 확장 참고)는 공급자별 변환이 아닌, 운영자가 작성한 변경을 하나 더 받습니다. 설정한 body.defaults와 body.overrides 조각이 위의 모든 제거와 정규화 이후 완성된 와이어 페이로드에 병합되고, 설정한 headers가 붙습니다. 병합은 src/infrastructure/common/request_extensions.rs의 공유 함수 하나가 담당하며 각 전송 지점에서 선택된 백엔드의 설정으로 호출되므로, 시도마다 그리고 폴백 홉마다 적용됩니다. 설정 로드는 위의 제거 규칙이 해당 백엔드에서 제거하는 본문 키를 거부하므로, 이 순서 때문에 제거된 필드가 다시 추가되는 일은 없습니다. 이 블록이 없는 백엔드는 이 문서에서 설명한 그대로 패스스루됩니다.
알려진 소비자¶
다음 필드들은 실제 운영에서 사용되며 패스스루 과정에서 반드시 변경 없이 전달되어야 합니다.
chat_template_kwargs: llama.cpp / MLxcel용 Jinja 템플릿 파라미터 (예:{"enable_thinking": false, "preserve_thinking": true}). Qwen3 계열의 thinking-mode 제어에 필요합니다.thinking_budget_tokens: llama.cpp가 허용하는 요청별 thinking 예산 상한. llama-server를 통한 Qwen3 thinking 모드에 필요합니다.extra_body: OpenAI 클라이언트 라이브러리(예: PythonopenaiSDK의extra_bodykwarg)가 전달하는 추가 파라미터 객체.{"skip_special_tokens": false}같은 vLLM 전용 설정을 전달하는 데 사용됩니다.
변환 백엔드¶
다음 백엔드 타입은 요청을 전달하기 전에 공급자별 변환 레이어를 거칩니다. 비표준 필드는 원본 그대로 전달되지 않을 수 있습니다.
Anthropic:src/http/handlers/anthropic/transform.rs. 변환 과정에서thinking블록(budget_tokens포함)이 다운스트림 OpenAI 호환 호출용reasoning_effort로 변환됩니다.Gemini:src/infrastructure/backends/gemini/transform.rs. Gemini 네이티브 요청 재구성을 수행하며, 현재 필드 매핑은 변환 소스를 참고하세요.
통합 테스트¶
전송 계층 패스스루 계약은 다음 통합 테스트로 보호됩니다.
tests/llamacpp_passthrough_test.rs:chat_template_kwargs,thinking_budget_tokens,extra_body, 임의의 알 수 없는 필드가 llama-server 엔드포인트에 변경 없이 도달하는지 검증합니다.tests/openai_request_compat_test.rs: 타입 기반 OpenAI 채팅, 스트리밍, 요청 변환, 컨트롤 플레인 프로브를 실제 캡처된 와이어 요청으로 검증합니다.tests/mlxcel_passthrough_test.rs:Mlxcel백엔드 타입에 대한 별도 커버리지를 유지합니다. 프록시 경로 픽스처와 함께 팩토리 경유BackendFactory -> LlamaCppBackend::execute_chat_completion어서션을 포함해서, MLxcel 배선이 달라지면 별도의 실패 테스트로 드러납니다.tests/sglang_passthrough_test.rs와tests/sglang_streaming_passthrough_test.rs:Sglang백엔드 타입에 대한 별도 커버리지를 유지합니다. SGLang 요청 확장(top_k,min_p,lora_path,session_params,chat_template_kwargs등)이 비스트리밍과 스트리밍 양쪽 경로에서 엔진에 변경 없이 도달하는지, 응답 메시지와 스트림 델타에서reasoning_content가 보존되는지,stream_options.include_usage설정 시 마지막 usage 청크가 그대로 전달되는지를 검증합니다.tests/anthropic_input_test.rs:thinking.budget_tokens가 변환되어 다운스트림 서버에 원시thinking_budget_tokens필드로 도달하지 않음을 확인하는 부정 테스트(test_anthropic_thinking_budget_tokens_transforms_to_reasoning_effort)를 포함합니다.src/infrastructure/backends/field_filter.rs의 유닛 테스트: 클라우드 게이트 제거를 커버합니다. 클라우드 OpenAI 채팅 요청에서는 차단 목록의 모든 필드가 제거되고, 로컬 엔진 요청은 전부 유지되며, 채팅이 아닌 엔드포인트는 건드리지 않습니다.- 같은 유닛 테스트 모듈이 Anthropic 고유 필드 게이트도 분기별로 한 건씩 커버합니다. OpenAI 와이어 타입은 모두 제거,
Anthropic/Bedrock/Continuumrouter는 유지, 알 수 없는 타입도 유지, 채팅이 아닌 엔드포인트도 유지, 그리고extra_body와reasoning_effort는 살아남습니다. tests/cross_provider_fallback_payload_test.rs: 모의 백엔드가 실제로 받은 아웃바운드 본문에 대해 Anthropic 고유 필드 제거를 검증합니다. 사용자 지정 별칭에서 vLLM 타입 폴백으로 넘어가는 홉(비스트리밍과 스트리밍), 사용자 지정 별칭을 쓰는 Anthropic 타입 대상이thinking을 그대로 받아야 하는 경우, 그리고anthropic_fast_mode옵트인이 있을 때만speed가 와이어까지 나가는 Anthropic 대 Anthropic 홉을 포함합니다. 같은 스위트가 제거 직전의 변환도 함께 검증합니다. effort가 붙은 적응형 사고 설정, budget 구간,xhigh를 지원하는 대상과 지원하지 않는 대상으로 가는max,reasoning_effort자체를 지원하지 않는 대상, 기존reasoning_effort가 이기는 경우, 비활성 thinking, 형식이 잘못된 설정, 그리고 스트리밍 전송 경로입니다.
이 테스트 중 하나라도 리팩터링으로 인해 실패한다면, 일반적인 테스트 실패가 아닌 공개 계약에 대한 변경으로 취급해야 합니다. 병합 전에 이 문서를 업데이트하여 새로운 동작을 반영하세요.
보증 범위¶
이 보증은 위에 나열한 전송 지점에 적용됩니다. 업스트림 서버가 클라이언트가 제출한 JSON 바디의 바이트 단위 사본을 항상 받는다는 의미는 아닙니다.
패스스루 지점이 실행되기 전에 chat_completions는 여전히 라우터 수준 전처리를 수행할 수 있습니다.
global_prompts가 설정된 경우 글로벌 시스템 프롬프트 주입. 이 경우messages배열이 수정됩니다.web_search.enabled가 적용되는 자체 호스팅 백엔드 대상 요청에 라우터 관리web_search주입. 업스트림 호출 전에tools항목이 추가될 수 있습니다.
선택된 공급자 경계에서는 OpenAI 및 Azure OpenAI 채팅 요청만 위에서 설명한 토큰 필드 정규화를 적용받습니다. OpenAI가 아닌 백엔드는 모델 문자열만을 근거로 다시 작성되지 않습니다. 추가적인 최상위 JSON 키를 주입하지 않으며, 해당 정규화와 클라우드 OpenAI 제거, 그리고 모든 OpenAI 와이어 backend_type에 적용되는 Anthropic 고유 필드 제거를 제외하면 패스스루 필드를 걸러내거나 이름을 바꾸지 않습니다.
관련 문서¶
- Reasoning Effort: 공급자 간
reasoning_effort/thinking.budget_tokens매핑 방법 - Model Fallback: 폴백 체인 설정
- Architecture Overview: 주요 아키텍처 가이드