태그 기반 라우팅

태그 기반 라우팅 (Tag Based Routing)

배포(deployment)에 태그를 붙여서 요청이 특정 태그와 일치하는 배포로만 라우팅되게 하는 기능이에요. 무료/유료 티어를 나누거나, 제공자별로 트래픽을 분리하거나, 특정 배포를 제외하고 싶을 때 유용해요.

빠른 시작

1. config.yaml에 태그 정의

config.yaml

    model_list:  
      - model_name: gpt-5.6-terra  
        litellm_params:  
          model: openai/fake  
          api_key: fake-key  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["free"] # 👈 Key Change  
      - model_name: gpt-5.6-terra  
        litellm_params:  
          model: openai/gpt-5.6-terra  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["paid"] # 👈 Key Change  
      - model_name: gpt-5.6-terra  
        litellm_params:  
          model: openai/gpt-5.6-terra  
          api_key: os.environ/OPENAI_API_KEY  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["default"] # OPTIONAL - All untagged requests will get routed to this  
      
    router_settings:  
      enable_tag_filtering: True # 👈 Key Change  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

2. tags=["free"]로 요청하기

    curl -i http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "gpt-5.6-terra",  
        "messages": [  
          {"role": "user", "content": "Hello, Claude gm!"}  
        ],  
        "tags": ["free"]  
      }'  
    

응답:

    {  
      "id": "chatcmpl-33c534e3d70148218e2d62496b81270b",  
      "choices": [  
        {  
          "finish_reason": "stop",  
          "index": 0,  
          "message": {  
            "content": "\n\nHello there, how may I assist you today?",  
            "role": "assistant"  
          }  
        }  
      ],  
      "model": "gpt-5.6-terra",  
      "object": "chat.completion",  
      "usage": {"completion_tokens": 12, "prompt_tokens": 9, "total_tokens": 21}  
    }  
    

3. tags=["paid"]로 요청하기

    curl -i http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "gpt-5.6-terra",  
        "messages": [  
          {"role": "user", "content": "Hello, Claude gm!"}  
        ],  
        "tags": ["paid"]  
      }'  
    

응답:

    {  
      "id": "chatcmpl-9maCcqQYTqdJrtvfakIawMOIUbEZx",  
      "choices": [  
        {  
          "finish_reason": "stop",  
          "index": 0,  
          "message": {  
            "content": "Good morning! How can I assist you today?",  
            "role": "assistant"  
          }  
        }  
      ],  
      "model": "gpt-5.6-terra",  
      "object": "chat.completion",  
      "usage": {"completion_tokens": 10, "prompt_tokens": 12, "total_tokens": 22}  
    }  
    

요청 헤더로 호출

    curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \  
    -H 'Content-Type: application/json' \  
    -H "Authorization: Bearer ***" \  
    -H 'x-litellm-tags: free,my-custom-tag' \  
    -d '{  
      "model": "gpt-5.6-terra",  
      "messages": [  
        {  
          "role": "user",  
          "content": "Hey, how's it going?"  
        }  
      ]  
    }'  
    

기본 태그 설정

1. yaml에 기본 태그 설정

config.yaml

    model_list:  
      - model_name: fake-openai-endpoint  
        litellm_params:  
          model: openai/fake  
          api_key: fake-key  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["default"] # 👈 Key Change - All untagged requests will get routed to this  
        model_info:  
          id: "default-model"  
    

2. 프록시 시작

    $ litellm --config /path/to/config.yaml  
    

3. 태그 없이 요청하기

    curl -i http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "fake-openai-endpoint",  
        "messages": [  
          {"role": "user", "content": "Hello, Claude gm!"}  
        ]  
      }'  
    

부정 태그 (Denylist)

태그 앞에 !를 붙이면 정확히 그 태그를 가진 배포를 제외해요. 허용되는 모든 대안을 나열하지 않고 특정 제공자나 모델 계열을 피하고 싶을 때 유용해요.

빠른 예시

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "gpt-5.6-terra",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["!provider:anthropic"]}  
      }'  
    

provider:anthropic으로 태그된 모든 배포는 라우팅 전에 후보 풀에서 제거돼요. 남은 모든 배포가 적격이에요.

config 예시

config.yaml

    model_list:  
      - model_name: chat  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["provider:anthropic"]  
      
      - model_name: chat  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["provider:openai"]  
      
      - model_name: chat  
        litellm_params:  
          model: vertex_ai/gemini-3.8-flash  
          api_key: os.environ/VERTEX_API_KEY  
          tags: ["provider:vertex"]  
      
    router_settings:  
      enable_tag_filtering: true  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

긍정 태그와 부정 태그 결합

긍정 태그로 티어를 선택하고 부정 태그로 그 티어 안에서 제공자를 제외할 수 있어요:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["paid", "!provider:anthropic"]}  
      }'  
    

여러 제공자 제외

! 태그를 여러 개 보내면 둘 이상의 배포 그룹을 제외해요:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["!provider:anthropic", "!provider:openai"]}  
      }'  
    

vertex 배포만 적격으로 남아요.

폴백 체인과의 부정

주 모델 그룹이 금지됐을 때, 라우터는 구성된 폴백으로 자동으로 넘어가요:

config.yaml

    model_list:  
      - model_name: primary  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["provider:anthropic"]  
      
      - model_name: fallback  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["provider:openai"]  
      
    router_settings:  
      enable_tag_filtering: true  
      fallbacks:  
        - {"primary": ["fallback"]}  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    
    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "primary",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["!provider:anthropic"]}  
      }'  
    # primary is banned -> falls through to fallback (provider:openai)  
    

부정 시맨틱

동작 상세
매칭 정확한 태그 문자열 매칭. !provider:anthropic은 정확히 provider:anthropic으로 태그된 배포만 제거
정규식 아님 부정 태그는 평문 문자열이지 regex 패턴이 아님. `!provider:(anthropic
금지 전용 요청 요청이 ! 태그만 있고 긍정 태그가 없으면 기본 풀이 태그 없는 요청 동작을 따름: 기본 태그 배포가 있으면 그것들, 없으면 모든 배포. 그 배제 집합이 그 풀 위에 적용됨
전부 제외 부정 태그가 모든 후보를 제거하면 요청은 no_deployments_with_tag_routing으로 실패
태그 없는 배포 tags 필드가 없는 배포는 부정 태그로 절대 제외되지 않음
헤더 부정 태그는 x-litellm-tags 헤더로도 동작: -H 'x-litellm-tags: !provider:anthropic'

필수 태그 (AND)

태그 앞에 &를 붙여 필수로 만들어요. 배포는 요청의 모든 & 접두사 태그를 가져야 후보가 돼요. 일반 태그는 하나만 일치하면 되는 것과 달라요. 이는 독립적인 제약을 결합할 때 유용해요. 예를 들어 "높은 reasoning이어야 하고 정확히 Anthropic 출신이어야 함" 같은 경우인데, 일반 OR 태그는 낮은 reasoning의 Anthropic 배포나 높은 reasoning의 비-Anthropic 배포와 매치할 수 있어요.

빠른 예시

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["&reasoning_type:high", "&provider:anthropic"]}  
      }'  
    

reasoning_type:highprovider:anthropic 둘 다를 가진 배포만 적격이에요.

config 예시

config.yaml

    model_list:  
      - model_name: chat  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["reasoning_type:high", "provider:anthropic"]  
      
      - model_name: chat  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["reasoning_type:high", "provider:openai"]  
      
    router_settings:  
      enable_tag_filtering: true  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

필수·부정·일반 태그 결합

! 제외가 먼저 적용되고, 그 다음 & 필수 태그가 남은 것을 좁히며, 그 다음 일반 태그가 생존자들에 평소의 OR 선호를 적용해요:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["&reasoning_type:high", "provider:anthropic", "provider:openai", "!inference:cerebras"]}  
      }'  
    # must be high-reasoning, AND (anthropic OR openai), AND not cerebras-hosted  
    

필수-AND 시맨틱

동작 상세
매칭 부정 태그와 같은 정확한 태그 문자열 매칭. Regex 아님
필수 전용 요청 요청이 & 태그만 있으면(일반·부정 태그 없음) 기본 풀이 태그 없는 요청 동작을 따름: 기본 태그 배포가 있으면 그것들, 없으면 모든 배포. 필수 태그 필터가 그 풀 위에 적용됨
Regex/헤더 선호가 필수-AND 요청을 희석하지 않음 필수-AND 전용 요청은 필수 태그를 충족하는 모든 배포를 항상 반환함. 그 중 하나가 무관한 tag_regex/User-Agent 선호와 우연히 매치돼도. regex 배포 하나로만 좁혀지지 않음
전부 제거 필수 태그가 모든 후보를 제거하면, 모델 그룹이 allow_fail_open을 선택하지 않는 한 요청은 no_deployments_with_tag_routing으로 실패
헤더 필수 태그는 x-litellm-tags 헤더로도 동작: -H 'x-litellm-tags: &reasoning_type:high'

실패-열림 폴백 (allow_fail_open)

기본적으로 ! 또는 & 태그가 모델 그룹의 모든 배포를 제거하면 요청은 no_deployments_with_tag_routing으로 실패해요. 그룹의 모든 배포의 model_infoallow_fail_open: true를 설정하면 요청을 실패시키는 대신 기본 태그 풀로 폴백해요.

이것은 명시적 옵트인이에요. 없으면 동작은 변하지 않아요. 충족할 수 없는 ! 또는 & 제약은 항상 예외를 일으켜요. 현재 부정이 이미 그러하듯이.

config 예시

config.yaml

    model_list:  
      - model_name: chat  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["provider:anthropic"]  
        model_info:  
          allow_fail_open: true  
      
      - model_name: chat  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["provider:openai", "default"]  
        model_info:  
          allow_fail_open: true  
      
    router_settings:  
      enable_tag_filtering: true  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

allow_fail_open 없이

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["!provider:anthropic", "!provider:openai"]}  
      }'  
    # both deployments banned, allow_fail_open unset -> fails with no_deployments_with_tag_routing  
    

allow_fail_open 사용 시

위 config를 쓰면 같은 요청이 대신 기본 태그 배포로 폴백하는데, 요청이 금지하려던 것을 포함해요:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["!provider:openai"]}  
      }'  
    # openai deployment banned; anthropic deployment is not "default"-tagged, so the  
    # pool falls back to whichever deployment IS "default"-tagged (openai here) -> the  
    # ban is treated as advisory rather than failing the request  
    

warning

기본 태그 풀로의 폴백은 요청이 명시적으로 제외하려던 배포를 여전히 반환할 수 있어요. 호출자에 기인할 수 있는 어떤 제약에 대해서도 그래요. 충족할 수 없는 !/& 제약이 실패보다 성능 저하로 받아들여질 수 있는 모델 그룹에만 allow_fail_open을 설정하세요. 제약이 하드 컴플라이언스 요구 사항인 그룹("이 계정의 트래픽을 절대 Provider X로 라우팅하지 마세요")에는 설정하지 마세요.

키 수준이나 팀 수준 정책에서 상속된 제약은 버려지는 것으로부터 보호돼요. 프록시는 요청 자체가 공급한 것과 별도로 어떤 태그가 키/팀 메타데이터에서 왔는지를 추적해요(metadata.inherited_tags). 그래서 allow_fail_open은 호출자가 제어한 제약만 떨어뜨려요. 호출자가 상속 태그의 정확한 값을 충돌하는 값과 함께 재제출해도 그렇고, 이는 순수 집합 뺄셈이 정직한 호출자 전용 태그와 구분할 수 없는 값 충돌이에요. 호출자 제어 부분만 떨어뜨려도 라우팅할 것이 남지 않으면, 요청은 열리지 않고 예외를 일으켜요.

이 보호는 프록시 계층을 요구해요. 프록시를 우회하는 직접 SDK Router 호출(metadata.inherited_tags 미설정)은 무조건 완전히 제약 없는 기본 풀로 폴백해요. 모든 태그가 호출자 공급인 것처럼 말이죠. 이것은 allow_fail_open이 프록시 밖에서 항상 가졌던 동작과 같아요.

allow_fail_open 시맨틱

동작 상세
위치 litellm_params가 아닌 model_info.allow_fail_open. 모델 그룹을 공유하는 모든 배포에 설정해 일관된 동작을 만들 것. 라우터는 아무 한 멤버를 확인
기본값 미설정 (false). 그룹이 옵트인하지 않는 한 기존 ! 부정 동작은 변하지 않음
범위 ! 또는 &로 인한 소진만 게이트. 일반 태그 소진은 기존 동작 유지: 기본 풀이 있으면 거기로 폴백, 없으면 예외
폴백 풀 태그 없는 요청과 금지 전용 요청에 쓰는 것과 같은 기본 태그 풀. 요청 자신의 !/& 제약을 재적용하지 않음
상속 태그 보호 버림(discard)은 총량에서 호출자 자신의 태그를 빼는 것이 아니라 태그 출처(metadata.inherited_tags, 프록시가 키/팀 정책에서 채움)에 기반함. 키/팀 상속 제약은 호출자가 동일한 값을 별도로 제출해도 보호됨

명시적 라우팅 지시문 (tag_routing_prefix)

태그 기반 라우팅은 어떤 배포의 리터럴 태그 문자열이 우연히 매치하는지 확인해 "이 태그가 라우팅용인가"를 추론해요. 그 휴리스틱은 보통 맞지만, 아무것도 매치하지 않는 호출자 발명의 &/! 태그는 의심스러운 노이즈로 취급되어, 호출자가 정직하지만 충족 불가능한 요청을 진짜 원했어도 allow_fail_open의 폴백을 막을 수 있어요(위 참고). router_settings.tag_routing_prefix를 구성하면 호출자가 특정 태그를 신뢰할 수 있고 모호하지 않은 라우팅 지시문으로 표시할 수 있어, 그 태그들에 대해 그 모호함을 완전히 없앨 수 있어요.

구성된 접두사로 시작하는 모든 요청 태그는 접두사가 벗겨지고 평소 !/&/일반 태그 로직으로 매치돼요. 호출자가 라우팅 의도를 명시적으로 선언했으므로 어휘 검사가 필요 없어요. 접두사 없는 태그는 오늘의 기존 처리를 그대로 계속하므로, 접두사 채택에 마이그레이션이 필요 없어요.

빠른 예시

tag_routing_prefix: "route:"가 구성된 경우:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["feature:demo", "route:!provider:openai"]}  
      }'  
    # feature:demo is untouched attribution, never meant for routing; route:!provider:openai  
    # is stripped to !provider:openai and matched as an explicit ban  
    

config 예시

config.yaml

    model_list:  
      - model_name: chat  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["default", "provider:anthropic"]  
        model_info:  
          allow_fail_open: true  
      
      - model_name: chat  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["provider:openai"]  
      
    router_settings:  
      enable_tag_filtering: true  
      tag_routing_prefix: "route:" # opt-in: enables the prefix mechanism  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

접두사 태그와 미지의 태그 실패-열림 가드

접두사 붙은 &/! 태그는 어떤 배포의 리터럴 태그가 그것과 매치하는지와 무관하게 미지의 태그 실패-열림 가드에 알려진 것으로 집계돼요. 호출자가 라우팅 의도를 명시적으로 선언했기 때문이에요:

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["route:&provider:anthropic", "route:&custom-routing-key"]}  
      }'  
    # custom-routing-key matches no deployment's tags, but because it is prefix-marked  
    # the caller has explicitly declared it a routing directive -- allow_fail_open still  
    # proceeds to the default-tagged pool instead of being blocked by the "does an  
    # invented tag hide a satisfiable answer" guard  
    

tag_routing_prefix 시맨틱

동작 상세
위치 router_settings.tag_routing_prefix, 문자열, 기본 ""
기본값 "". str.startswith("")는 모든 문자열과 매치하므로, 구성 전까지 메커니즘은 완전한 no-op
매칭 정확한 리터럴 접두사. 구분자가 자동으로 붙지 않음. 자신이 후행 구분자를 구성할 것(예: "route"가 아니라 "route:") — 구분자가 없는 접두사는 우연히 같은 문자로 시작하는 무관한 태그와 매치할 수 있음
스트리핑 순서 접두사가 먼저 벗겨지고 그 다음 !/& 파싱. 그래서 route:!provider:xroute:&provider:x 둘 다 동작
접두사 없는 태그 오늘의 기존 처리를 그대로 계속: 일반 태그의 알려진-태그 어휘 휴리스틱, !/& 태그의 미지의 태그 실패-열림 가드
실패-열림과의 상호작용 접두사 붙은 &/! 태그는 배포 태그 어휘와 무관하게 allow_fail_open의 미지의 태그 가드에 알려진 것으로 집계됨

모델 그룹별 태그 필터링 (enable_tag_filtering)

model_infoenable_tag_filtering을 설정하면 오직 한 모델 그룹에 대해서만 어느 방향으로든 router_settings.enable_tag_filtering을 오버라이드해요. 모델 그룹 수준에서 확인돼요. 라우터는 요청된 model_name 그룹의 아무 배포를 보고, 그 배포의 model_info.enable_tag_filtering이 설정돼 있으면 그 그룹에 대한 모든 요청에서 라우터 전체 기본값을 대체해요. allow_fail_open이 이미 쓰는 것과 같은 관례인, 그룹을 공유하는 모든 배포에 일관되게 설정하세요.

이것은 서로 무관한 많은 모델 그룹을 서빙하는 프록시에서 중요해요. 한 그룹의 &/! 기반 컴플라이언스 라우팅을 충족시키려고 router_settings.enable_tag_filtering을 전역으로 켜면 다른 모든 그룹의 요청도 태그 평가에 노출돼요. 그 그룹의 배포에만 model_info.enable_tag_filtering: true를 설정하면 그걸 피할 수 있어요. 반대도 동작해요. 프록시의 나머지가 다른 곳에서 태그 필터링을 강제하는 동안, 태그 면역 catch-all 또는 사고 대응 모델 그룹 하나를 분리해 낼 수 있어요.

빠른 예시

    curl http://localhost:4000/v1/chat/completions \  
      -H "Content-Type: application/json" \  
      -H "Authorization: Bearer ***" \  
      -d '{  
        "model": "chat-compliance",  
        "messages": [{"role": "user", "content": "Hello"}],  
        "metadata": {"tags": ["&provider:anthropic"]}  
      }'  
    # router_settings.enable_tag_filtering is false, but chat-compliance opts in via  
    # model_info.enable_tag_filtering: true, so the "&" constraint still applies  
    

config 예시

config.yaml

    model_list:  
      - model_name: chat-compliance  
        litellm_params:  
          model: anthropic/claude-sonnet-5  
          api_key: os.environ/ANTHROPIC_API_KEY  
          tags: ["provider:anthropic"]  
        model_info:  
          enable_tag_filtering: true # opt in for this group only  
      
      - model_name: chat-compliance  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
          tags: ["provider:openai"]  
        model_info:  
          enable_tag_filtering: true  
      
      - model_name: incident-response  
        litellm_params:  
          model: openai/gpt-5.6-luna  
          api_key: os.environ/OPENAI_API_KEY  
        model_info:  
          enable_tag_filtering: false # opt out for this group only  
      
    router_settings:  
      enable_tag_filtering: false # router-wide default; chat-compliance overrides it  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

이 config에서 chat-compliance는 라우터 전체 기본값이 꺼져 있어도 모든 요청에 태그를 평가하고, incident-response를 포함한 다른 모든 모델 그룹은 태그를 무시하고 평범한 로드밸런싱 라우팅으로 폴백해요. 라우터 전체 기본값을 true로 뒤집어도 chat-compliance는 영향받지 않고 태그를 평가하는 반면, incident-response의 명시적 enable_tag_filtering: false는 그것을 면제로 유지해요.

enable_tag_filtering 시맨틱

동작 상세
위치 litellm_params가 아닌 model_info.enable_tag_filtering. 모델 그룹을 공유하는 모든 배포에 설정. 라우터는 아무 한 멤버를 확인
우선순위 낮음→높음: router_settings.enable_tag_filtering(라우터 전체 기본값), 그 다음 요청된 그룹에 설정된 경우 model_info.enable_tag_filtering(어느 방향으로든 그 그룹에 대해서만 라우터 기본값 오버라이드), 그 다음 키/팀 설정의 요청 수준 enable_tag_filtering
요청 수준 상승만 키/팀 설정에서 프록시가 설정하는 요청 수준 설정은 필터링을 켤 수만 있음. 요청 수준 enable_tag_filtering=True는 여전히 enable_tag_filtering: false로 스스로 옵트아웃한 그룹을 이김. 라우터와 모델 그룹이 이미 결정한 것 위로 필터링을 끌 요청 수준 방법은 없음
기본값 미설정. 이 오버라이드가 존재하기 전과 똑같이 모델 그룹이 router_settings.enable_tag_filtering에 위임
범위 그룹의 전체 태그 필터링 결정에 적용, &/! 처리만은 아님. 필터링이 비활성화된 그룹은 일반·부정·필수 태그를 모두 무시
헬스 무관 현재 건강한 배포뿐 아니라 모델 그룹에 구성된 모든 배포에서 해석됨. 단일 배포의 쿨다운이 오버라이드를 가진 유일한 배포를 순환에서 빼서 그룹 전체의 태그 정책을 조용히 비활성화(또는 활성화)할 수 없음

정규식 기반 태그 라우팅 (tag_regex)

배포에 tag_regex를 사용해 클라이언트가 명시적 태그를 보내지 않아도 그들의 헤더(예: User-Agent)로 들어오는 요청을 매치해요. 패턴은 운영자가 구성하고 서버 쪽에서 컴파일되며 호출자가 공급하지 않아요.

warning

User-Agent는 클라이언트가 공급하는 헤더이고 어떤 호출자도 어떤 값으로든 설정할 수 있어요. 트래픽 분류에는 tag_regex를, 접근 제어 강제에는 사용하지 마세요.

헤더 기반 라우팅은 그 자체로 보안 경계가 아니에요. 요청이 LiteLLM에 도달하기 전에 자격증명을 검증하고 인증되지 않은 트래픽을 거부하는 업스트림 인증 계층(예: API 게이트웨이나 리버스 프록시)을 통과할 때만 의미가 있어요. 그런 계층이 없으면 어떤 클라이언트든 User-Agent를 위조해서 도달해서는 안 되는 배포로 라우팅될 수 있어요.

1. Config

config.yaml

    model_list:  
      # Claude Code traffic → dedicated deployment, matched by User-Agent  
      - model_name: claude-sonnet  
        litellm_params:  
          model: bedrock/converse/anthropic-claude-sonnet-4-6  
          aws_region_name: us-east-1  
          aws_role_name: arn:aws:iam::111122223333:role/LiteLLMClaudeCode  
          tag_regex:  
            - "^User-Agent: claude-code\\/"   # matches claude-code/1.x, 2.x, etc.  
        model_info:  
          id: claude-code-deployment  
      # All other traffic falls back to the default deployment  
      - model_name: claude-sonnet  
        litellm_params:  
          model: bedrock/converse/anthropic-claude-sonnet-4-6  
          aws_region_name: us-east-1  
          aws_role_name: arn:aws:iam::444455556666:role/LiteLLMDefault  
          tags:  
            - default  
        model_info:  
          id: regular-deployment  
      
    router_settings:  
      enable_tag_filtering: true  
      tag_filtering_match_any: true  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

2. 라우팅 검증

    # Claude Code request (User-Agent set automatically by Claude Code)  
    curl http://localhost:4000/v1/chat/completions \  
      -H "Authorization: Bearer ***" \  
      -H "User-Agent: claude-code/1.2.3" \  
      -d '{"model": "claude-sonnet", "messages": [{"role": "user", "content": "hi"}]}'  
    # -> x-litellm-model-id: claude-code-deployment  
      
    # Any other client (no matching User-Agent) -> default deployment  
    curl http://localhost:4000/v1/chat/completions \  
      -H "Authorization: Bearer ***" \  
      -d '{"model": "claude-sonnet", "messages": [{"role": "user", "content": "hi"}]}'  
    # -> x-litellm-model-id: regular-deployment  
    

매칭 시맨틱

동작 상세
엔진 Python re.search — 문자열 시작(^)이나 끝($)에 고정하고 싶지 않으면 패턴을 앵커할 필요 없음
입력 형식 패턴은 "Header-Name: value" 문자열에 매치됨. 현재는 User-Agent만 노출: User-Agent: claude-code/1.2.3
로직 항상 OR — 어떤 단일 패턴이 매치돼도 배포를 선택하기에 충분. tag_filtering_match_any=False는 일반 tags에만 적용되고 tag_regex에는 아님
잘못된 패턴 re.compile에 실패하는 패턴은 로그되고 건너뜀. 절대 하드 에러를 일으키지 않음
일반 태그와의 상호작용 배포에 tagstag_regex가 둘 있고 tag_filtering_match_any=False면, 엄격 태그 검사가 이미 실패했을 때 regex 경로가 차단됨. Regex는 엄격 태그 정책을 오버라이드할 수 없음
신뢰된 입력 패턴은 config에서 운영자가 설정하며 호출자가 절대 공급하지 않음. 이것이 항상 평문 리터럴로 취급되는 부정 태그(요청 메타데이터의 !foo)와의 핵심 차이

부정 태그와의 상호작용

부정 제외는 tag_regex 매칭 전에 실행돼요. 배포가 일반 tags 목록과 tag_regex 둘 다 가질 때 순서가 중요해요:

  1. 라우터는 tags가 요청의 제외 집합과 교차하는 배포를 제거.
  2. tag_regex 매칭은 생존 후보에만 실행.

사례 1: 부정이 일반 태그 배포를 제거; tag_regex 배포는 영향받지 않음

    model_list:  
      - model_name: chat  
        litellm_params:  
          tag_regex: ["^User-Agent: claude-code\\/"]   # no plain tags  
        model_info: {id: claude-code-deployment}  
      
      - model_name: chat  
        litellm_params:  
          tags: ["provider:anthropic"]  
        model_info: {id: anthropic-deployment}  
    
    curl ... -H "User-Agent: claude-code/1.2.3" \  
      -d '{"model":"chat","metadata":{"tags":["!provider:anthropic"]}}'  
    # anthropic-deployment is excluded; claude-code-deployment is matched by User-Agent  
    # -> x-litellm-model-id: claude-code-deployment  
    

사례 2: 부정이 tag_regex를 가진 배포를 제거; 금지-전용 경로 발화

부정된 태그가 tag_regex와 같은 배포에 있으면 그 배포가 먼저 제외돼요. 후보 풀에 tag_regex 배포가 남지 않으면 has_tag_filterFalse가 되고, 금지-전용 경로가 발화하여 남은 배포가 직접 반환돼요.

    model_list:  
      - model_name: chat  
        litellm_params:  
          tag_regex: ["^User-Agent: claude-code\\/"]  
          tags: ["group:claude"]   # negation target is on the tag_regex deployment  
        model_info: {id: claude-code-deployment}  
      
      - model_name: chat  
        litellm_params:  
          tags: ["provider:openai"]  
        model_info: {id: openai-deployment}  
    
    curl ... -H "User-Agent: claude-code/1.2.3" \  
      -d '{"model":"chat","metadata":{"tags":["!group:claude"]}}'  
    # claude-code-deployment excluded; no tag_regex deployments remain  
    # ban-only path returns openai-deployment regardless of User-Agent  
    # -> x-litellm-model-id: openai-deployment  
    

관측성

    {  
      "tag_routing": {  
        "matched_via": "tag_regex",  
        "matched_value": "^User-Agent: claude-code\\/",  
        "user_agent": "claude-code/1.2.3",  
        "request_tags": []  
      }  
    }  
    

팀 기반 태그 라우팅 (Enterprise)

구성

config.yaml

    model_list:  
      - model_name: fake-openai-endpoint  
        litellm_params:  
          model: openai/fake  
          api_key: fake-key  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["teamA"] # 👈 Key Change  
        model_info:  
          id: "team-a-model"  
      - model_name: fake-openai-endpoint  
        litellm_params:  
          model: openai/fake  
          api_key: fake-key  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["teamB"] # 👈 Key Change  
        model_info:  
          id: "team-b-model"  
      - model_name: fake-openai-endpoint  
        litellm_params:  
          model: openai/fake  
          api_key: fake-key  
          api_base: https://exampleopenaiendpoint-production.up.railway.app/  
          tags: ["default"] # OPTIONAL - All untagged requests will get routed to this  
      
    router_settings:  
      enable_tag_filtering: True # 👈 Key Change  
      
    general_settings:  
      master_key: os.environ/LITELLM_MASTER_KEY  
    

태그로 팀 생성

    # Create Team A  
    curl -X POST http://0.0.0.0:4000/team/new \  
      -H "Authorization: Bearer ***" \  
      -H "Content-Type: application/json" \  
      -d '{"tags": ["teamA"]}'  
      
    # Create Team B  
    curl -X POST http://0.0.0.0:4000/team/new \  
      -H "Authorization: Bearer ***" \  
      -H "Content-Type: application/json" \  
      -d '{"tags": ["teamB"]}'  
    

팀 멤버용 키 생성

    # Generate key for Team A  
    curl -X POST http://0.0.0.0:4000/key/generate \  
      -H "Authorization: Bearer ***" \  
      -H "Content-Type: application/json" \  
      -d '{"team_id": "team_a_id_here"}'  
      
    # Generate key for Team B  
    curl -X POST http://0.0.0.0:4000/key/generate \  
      -H "Authorization: Bearer ***" \  
      -H "Content-Type: application/json" \  
      -d '{"team_id": "team_b_id_here"}'  
    

라우팅 검증

    curl -i -X POST http://0.0.0.0:4000/chat/completions \  
      -H "Authorization: Bearer ***" \  
      -H "Content-Type: application/json" \  
      -d '{  
        "model": "fake-openai-endpoint",  
        "messages": [  
          {"role": "user", "content": "Hello!"}  
        ]  
      }'  
    

출처: 문서

더 알아보기 (Learn more)

  • 자동 라우터의 폴백과 로드밸런싱 동작 살펴보기
  • 배포별 model_info 옵션과 태그 필터링 상호작용 이해하기