콘텐츠로 이동

모델 변형 접미사: 설계 기록

모델 변형 접미사는 opt-in 방식의 요청별 선택 수단입니다. 클라이언트가 모델 이름 뒤에 :nitro, :floor, :thinking, :online을 붙이면 라우터가 그 요청 자체의 디스패치에 해당 선호를 적용합니다(이슈 #2405). 스위치를 켜지 않으면 아무것도 바뀌지 않습니다.

설정

proxy.variant_suffixes.enabled: true를 설정합니다. 기본값은 false입니다.

문법은 /v1/chat/completions에서만, 버퍼링과 스트리밍 모두에서 해석합니다. /v1/completions, /v1/responses, Anthropic Messages를 포함한 다른 인그레스는 기존 동작을 유지하며, 그 경로에서 접미사가 붙은 이름은 알 수 없는 모델입니다. 스위치는 즉시 재로드됩니다. 채팅 핸들러가 디스패치에 쓰는 것과 같은 설정 스냅숏에서 이름을 해석하기 때문입니다.

해석 순서

요청한 이름 전체를 먼저 해석하며, /v1/chat/completions가 이미 쓰는 계층을 그대로 따릅니다. 활성 백엔드의 model_configs에 선언된 ID나 별칭, 계층화된 모델 메타데이터 캐시, 설정된 models: 항목, 백엔드가 있는 라이브 카탈로그 항목 순서입니다. 이 해석이 실패한 경우에만 알려진 끝 접미사를 찾아 마지막 :에서 분리하고 베이스 ID를 해석합니다.

그 결과는 세 가지이며, 모두 테스트로 확인합니다.

  • 백엔드가 실제로 서빙하는 이름은 그대로 해석됩니다. Ollama의 name:tag를 변형으로 잘못 읽지 않습니다.
  • 라우터가 해석할 수 없는 베이스에 붙은 접미사는 알 수 없는 모델로 남습니다. 오류는 지금처럼 요청한 이름 전체를 가리킵니다.
  • 알 수 없는 끝 단어는 절대 분리하지 않습니다. base:cheap과 base:free는 알 수 없는 모델로 실패합니다. 다른 곳에서 쓰이는 :free와 :extended는 구현하지 않았습니다.

접미사 동작

접미사 동작
:nitro 정상이고 승인 가능한 후보 중 최신 generation_throughput_tps가 가장 높은 서버로 좁힌 뒤, 설정된 전략이 동률 후보 중에서 고릅니다.
:floor 이 요청의 추정 비용이 가장 낮은 서버로 좁힙니다. least_cost 선택과 같은 가격 메타데이터와 요청 비용 추정을 쓰고 동률이면 출력 단가로 가른 뒤, 설정된 전략이 동률 후보 중에서 고릅니다.
:thinking 메타데이터가 thinking 지원을 선언한 모델에서, 클라이언트가 지정하지 않은 reasoning_effort를 high로 올립니다. 클라이언트가 지정한 값이 우선합니다.
:online 기존 라우터 관리 web_search 루프를 실행합니다.

nitro와 floor는 후보를 좁힐 뿐 선택을 대체하지 않습니다. 상태 필터링과 엔진 포화 승인 게이트가 좁히기보다 먼저 실행되므로, 선호는 어차피 요청을 처리할 수 있었던 서버 중에서만 고를 수 있습니다. 순위에는 데이터가 필요합니다. 최신 엔진 통계 스냅숏이 없는 서버는 :nitro에 처리량 정보를 주지 않으며, 비교 가능한 가격이 없는 서버는 :floor 대상에서 빠집니다. 순위를 매길 후보가 하나도 없으면 집합을 그대로 두고 일반 선택이 요청을 처리하며, 응답은 선호를 적용하지 못했다고 알립니다.

거부

두 변형은 충족할 수 없을 수 있으며, 이때 베이스 모델로 조용히 대체하지 않고 거부합니다.

  • 메타데이터에 thinking 패턴도, 비어 있지 않은 reasoning_efforts도 없는 모델의 :thinking.
  • 사용할 수 있는 실행기가 없는 :online. web_search가 비활성이거나 없음, 라우터가 주입하지 않는 상용 백엔드, 또는 버퍼링 웹 검색 루프로 처리할 수 없는 스트리밍 요청입니다.

응답은 HTTP 404이며 error.type은 "invalid_request_error", error.code는 "unsupported_model_variant"이고, 메시지가 모델과 접미사를 명시합니다. :nitro와 :floor는 거부하지 않습니다.

식별자와 집계

요청의 대상은 베이스 ID입니다. 키별 allowed_models, 모델별 레이트 리밋, 모델별 토큰 예산은 모두 베이스 ID를 기준으로 하므로, 접미사로 모델에 설정된 한도를 벗어날 수 없고 베이스로 범위가 정해진 키는 그 변형을 쓸 수 있습니다. 백엔드에는 베이스 ID가 전달됩니다.

정확 일치 우선 규칙은 이 게이트에도 적용됩니다. 접미사 단어로 끝나더라도 그 자체가 서빙되거나 선언된 모델인 이름은 원래 식별자를 유지합니다. 따라서 base:nitro라는 이름으로 실제 서빙되는 모델은 base:nitro로 허용되고 집계되며, base로 취급되지 않습니다.

접미사는 클라이언트와 운영자가 확인할 수 있는 곳에 유지됩니다.

  • 응답의 최상위 model을 요청한 이름으로 바꿉니다. JSON 본문과 스트림의 데이터 라인 모두 해당합니다.
  • x-continuum-model-variant는 변형 단어(nitro, floor, thinking, online)를 담습니다.
  • x-continuum-model-variant-applied: false는 :nitro나 :floor 선호를 평가했지만 적용하지 못한 경우에만 붙습니다.
  • continuum_model_variant_requests_total{variant, applied}가 변형별, 선호 적용 여부별로 요청을 집계합니다.

변형은 요청 자체의 첫 시도에 속합니다. 폴백 체인 홉은 다른 모델을 디스패치하므로 접미사를 물려받지 않습니다. x-backend 선호는 지금처럼 첫 시도를 가져가며, 이때 후보 순위를 우회하므로 변형은 적용되지 않은 것으로 보고됩니다.

한계

  • 변형은 응답 캐시 식별자를 바꾸지 않습니다. 라우팅이 이미 후보를 한 백엔드로 좁힌 경우 base:nitro 요청과 base 요청이 같은 캐시 항목을 공유할 수 있습니다. 캐시 식별자는 요청 표기가 아니라 경로를 따릅니다. 이는 :nitro, :floor, :thinking에 적용됩니다.
  • :online은 정확, 접두사, 시맨틱 캐시 경로에서 응답 캐시를 읽지도 쓰지도 않습니다. 캐시 키는 web_search 도구가 주입되기 전의 페이로드로 만들어지므로, 캐시 항목을 제공하면 검색 없이 응답하게 되고, 사용 가능한 executor가 없는 배포에서도 캐시된 응답을 재생하는 대신 거부해야 합니다.
  • 스트리밍 :online은 검색 없이 처리하지 않고 거부합니다. 라우터 관리 루프는 버퍼링 경로에만 있기 때문입니다.
  • :thinking은 reasoning effort를 설정할 뿐입니다. 모델 메타데이터가 이미 설명하지 않은 제공자별 thinking 예산을 켜지 않습니다.

미해결 질문

  • 이슈에 함께 언급된 :free와 :extended가 자체 호스팅 환경에서 무엇을 의미하는지. 둘 다 구현하지 않았습니다.
  • /v1/completions나 Responses API처럼 /v1/chat/completions 외의 인그레스까지 문법을 확장할지.