From dddbaa803d6e9fcc862ea7a5412f771cb1ae040e Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Sat, 24 Oct 2026 22:33:45 -0700 Subject: [PATCH 01/73] build(docker): refresh base images and ship only runtime files in component images (#48279) * build(docker): move to newer wolfi-base and nginx base images Bump the pinned wolfi-base digest in every Wolfi Dockerfile to 360896ef, which ships brotli 1.2.1-r1 and ca-certificates-bundle 20260909-r3. The previous digest already carried libbrotli 1.2.0-r6 as an OpenSSL dependency, and `apk add --no-cache` never upgrades a package the base already has Move the UI image to nginx 1.31.8-alpine3.24, whose CA bundle is the 20261020 Mozilla set, and upgrade brotli-libs in its runtime stage since no nginx digest ships 1.2.1 yet * build(docker): copy only runtime files into the gateway, backend and migrations images The component runtime stages copied the builder's whole /app, which is the entire repository plus build output: six lockfiles, helm, terraform, docs, .env.example and the Rust build tree. Measured at runtime with an audit hook on a booted container, the gateway reads only gateway/, litellm/ and enterprise/ from /app, the backend only backend/, litellm/ and enterprise/, and the migrations job only litellm/ and run.py. The pyproject.toml read is prisma-python's optional [tool.prisma] lookup, which ours does not define Copy just those, plus .venv, the entrypoint script and schema.prisma for the gateway and backend. PYTHONPATH=/app and the in-Dockerfile asserts are unchanged --- Dockerfile | 4 ++-- backend/Dockerfile | 11 ++++++++--- docker/Dockerfile.database | 4 ++-- docker/Dockerfile.non_root | 4 ++-- gateway/Dockerfile | 11 ++++++++--- migrations/Dockerfile | 8 +++++--- ui/Dockerfile | 4 +++- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index 713d7f8f5ab..4cef63c8d71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1.7 # Base image for building -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 # Runtime image -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 diff --git a/backend/Dockerfile b/backend/Dockerfile index 46d65c432bb..1c22f914100 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a FROM $UV_IMAGE AS uvbin @@ -124,7 +124,12 @@ ENV HOME=/home/nonroot \ PYTHONUNBUFFERED=1 \ PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries -COPY --from=builder --chown=nonroot:nonroot /app /app +COPY --from=builder --chown=nonroot:nonroot /app/.venv /app/.venv +COPY --from=builder --chown=nonroot:nonroot /app/litellm /app/litellm +COPY --from=builder --chown=nonroot:nonroot /app/enterprise /app/enterprise +COPY --from=builder --chown=nonroot:nonroot /app/backend /app/backend +COPY --from=builder --chown=nonroot:nonroot /app/docker/component_entrypoint.sh /app/docker/component_entrypoint.sh +COPY --from=builder --chown=nonroot:nonroot /app/schema.prisma /app/schema.prisma COPY --from=builder /opt/prisma /opt/prisma RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \ diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index 69d9c26b2a8..20f810686a3 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1.7 # Base image for building -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 # Runtime image -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index 54bf8c807aa..90bcc523d9e 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -1,8 +1,8 @@ # syntax=docker/dockerfile:1.7 # Base images -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 390a5f5666f..69068cfc8a5 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Checksum from https://www.pgbouncer.org/downloads/ (the Wolfi repo only carries 1.24.x) ARG PGBOUNCER_VERSION=1.26.0 @@ -140,7 +140,12 @@ ENV HOME=/home/nonroot \ PYTHONUNBUFFERED=1 \ PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries -COPY --from=builder --chown=nonroot:nonroot /app /app +COPY --from=builder --chown=nonroot:nonroot /app/.venv /app/.venv +COPY --from=builder --chown=nonroot:nonroot /app/litellm /app/litellm +COPY --from=builder --chown=nonroot:nonroot /app/enterprise /app/enterprise +COPY --from=builder --chown=nonroot:nonroot /app/gateway /app/gateway +COPY --from=builder --chown=nonroot:nonroot /app/docker/component_entrypoint.sh /app/docker/component_entrypoint.sh +COPY --from=builder --chown=nonroot:nonroot /app/schema.prisma /app/schema.prisma COPY --from=builder /opt/prisma /opt/prisma COPY --from=pgbouncer-builder /usr/local/bin/pgbouncer /usr/local/bin/pgbouncer diff --git a/migrations/Dockerfile b/migrations/Dockerfile index 84ea67fcc5f..2b1fd029143 100644 --- a/migrations/Dockerfile +++ b/migrations/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:81211e15a86ea1d5357f284482af45730c51de45eaf10ad9ce205d6bfc2df323 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:360896efb0753a5701c48f860e17130c205eae5a264797aa3e7ea126ee2923a7 ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a FROM $UV_IMAGE AS uvbin @@ -137,7 +137,9 @@ ENV HOME=/home/nonroot \ PRISMA_CLI_QUERY_ENGINE_TYPE=binary \ PRISMA_OFFLINE_MODE=true -COPY --from=builder --chown=nonroot:nonroot /app /app +COPY --from=builder --chown=nonroot:nonroot /app/.venv /app/.venv +COPY --from=builder --chown=nonroot:nonroot /app/litellm /app/litellm +COPY --from=builder --chown=nonroot:nonroot /app/run.py /app/run.py COPY --from=builder /opt/prisma /opt/prisma RUN chmod -R a+rX /opt/prisma && \ diff --git a/ui/Dockerfile b/ui/Dockerfile index 4b6b0c1581a..d78a155b874 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -3,7 +3,7 @@ # UI container — Next.js static export served by nginx. ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 -ARG NGINX_VERSION=1.31.5-alpine3.24@sha256:34f40471dea485273c5e2a04dd5e97a682332ceb4a9adecd67de450dcb2fb390 +ARG NGINX_VERSION=1.31.8-alpine3.24@sha256:1ef7ca94d7879df82421428ce13bd1eaa412404953859cf7a5b6512c25e67a60 # ---------- builder ---------- FROM ${UI_BUILD_IMAGE} AS builder @@ -27,6 +27,8 @@ RUN npm run build # ---------- runtime ---------- FROM nginx:${NGINX_VERSION} AS runtime +RUN apk upgrade --no-cache brotli-libs + # Drop the upstream default :80 server; we own the config. RUN rm -f /etc/nginx/conf.d/default.conf From ebf829c2faf672caa01cf1019c3920c8d3704011 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 09:00:11 -0700 Subject: [PATCH 02/73] fix: support Python 3.15 (#48359) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../test-mcp-dependency-resolution.yml | 4 +- .github/workflows/test-merge-smoke.yml | 8 +- litellm/_uuid.py | 8 +- .../complexity_router/config.py | 13 +- packaging/litellm-core/pyproject.toml | 10 +- pyproject.toml | 23 +- .../router_strategy/test_complexity_router.py | 5 + tests/unit/test_uuid_helper.py | 28 +- uv.lock | 1627 +++++++++++++---- 9 files changed, 1292 insertions(+), 434 deletions(-) diff --git a/.github/workflows/test-mcp-dependency-resolution.yml b/.github/workflows/test-mcp-dependency-resolution.yml index 3f0d1260e49..20efb351985 100644 --- a/.github/workflows/test-mcp-dependency-resolution.yml +++ b/.github/workflows/test-mcp-dependency-resolution.yml @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 @@ -132,7 +132,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: diff --git a/.github/workflows/test-merge-smoke.yml b/.github/workflows/test-merge-smoke.yml index 0d1cc6596de..f3f09523491 100644 --- a/.github/workflows/test-merge-smoke.yml +++ b/.github/workflows/test-merge-smoke.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] env: LITELLM_LOCAL_MODEL_COST_MAP: "True" steps: @@ -76,6 +76,12 @@ jobs: - name: Import and CLI checks run: $IN_NETNS .venv/bin/python .github/scripts/run_merge_smoke.py cli + - name: Routing and UUID compatibility + run: >- + $IN_NETNS .venv/bin/python -m pytest + tests/unit/router_strategy/test_complexity_router.py::TestCustomDimensions + tests/unit/test_uuid_helper.py -q + - name: Proxy startup check run: $IN_NETNS .venv/bin/python .github/scripts/run_merge_smoke.py proxy-startup --diagnostics-dir "${RUNNER_TEMP}/smoke-diagnostics" diff --git a/litellm/_uuid.py b/litellm/_uuid.py index e9578b7287f..55df233b553 100644 --- a/litellm/_uuid.py +++ b/litellm/_uuid.py @@ -1,10 +1,14 @@ """ Internal unified UUID helper. -Always uses fastuuid for performance. +Uses fastuuid where supported and the standard library on Python 3.15+. """ -import fastuuid as _uuid +import sys +import uuid as _uuid + +if sys.version_info < (3, 15): + import fastuuid as _uuid # Expose a module-like alias so callers can use: uuid.uuid4() uuid = _uuid diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 4a2c56daf00..ec960a19f79 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -7,12 +7,12 @@ All values are configurable via proxy config.yaml. import math import re -import warnings +import sys from collections.abc import Iterable, Mapping from dataclasses import dataclass from enum import Enum from types import MappingProxyType -from typing import Annotated, Final, Literal, NamedTuple +from typing import TYPE_CHECKING, Annotated, Final, Literal, NamedTuple from pydantic import ( ConfigDict, @@ -27,8 +27,13 @@ from pydantic import ( from litellm.types.llms.base import LiteLLMBaseModel -with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) +if TYPE_CHECKING: + import sre_constants + import sre_parse +elif sys.version_info >= (3, 11): + from re import _constants as sre_constants + from re import _parser as sre_parse +else: import sre_constants import sre_parse diff --git a/packaging/litellm-core/pyproject.toml b/packaging/litellm-core/pyproject.toml index 890fa0fd773..27681d9116c 100644 --- a/packaging/litellm-core/pyproject.toml +++ b/packaging/litellm-core/pyproject.toml @@ -3,14 +3,14 @@ name = "litellm-core" version = "1.105.0" description = "Library to easily interface with LLM API providers" readme = "README.md" -requires-python = ">=3.10, <3.15" +requires-python = ">=3.10, <3.16" license = "MIT" license-files = ["LICENSE"] authors = [ { name = "BerriAI" }, ] dependencies = [ - "fastuuid>=0.14.0,<1.0", + "fastuuid>=0.14.0,<1.0; python_version < '3.15'", "filelock>=3.16.1,<4.0", "httpx[http2]>=0.28.0,<1.0", "openai>=2.20.0,<4.0.0", @@ -20,14 +20,16 @@ dependencies = [ "packaging>=24.0", "importlib-metadata>=8.0.0,<9.0", "tiktoken>=0.8.0,<1.0; python_version < '3.14'", - "tiktoken>=0.12.0,<1.0; python_version >= '3.14'", + "tiktoken>=0.12.0,<1.0; python_version == '3.14'", + "tiktoken>=0.14.0,<1.0; python_version >= '3.15'", "click>=8.0.0,<9.0", "jinja2>=3.1.6,<4.0", "aiohttp>=3.14.2,<4.0", "anyio>=4.14.0,<5.0", "async-timeout>=4.0.3,<6.0; python_version < '3.11'", "pydantic>=2.11.0,<3.0.0; python_version < '3.14'", - "pydantic>=2.12.0,<3.0.0; python_version >= '3.14'", + "pydantic>=2.12.0,<3.0.0; python_version == '3.14'", + "pydantic>=2.14.0,<3.0.0; python_version >= '3.15'", "pydantic-settings>=2.14.1,<3.0", "jsonschema>=4.0.0,<5.0", "certifi>=2026.10.19", diff --git a/pyproject.toml b/pyproject.toml index 817c50df90b..b15eb042959 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "litellm" version = "1.108.0" description = "Library to easily interface with LLM API providers" readme = "README.md" -requires-python = ">=3.10, <3.15" +requires-python = ">=3.10, <3.16" license = "MIT" license-files = ["LICENSE"] authors = [ @@ -14,7 +14,7 @@ dependencies = [ # deps. Reproducibility for our Docker/CI comes from `uv.lock`. # When changing a floor, verify it installs + imports on every supported # Python with: `uv pip install --resolution=lowest-direct .` - "fastuuid>=0.14.0,<1.0", + "fastuuid>=0.14.0,<1.0; python_version < '3.15'", "filelock>=3.16.1,<4.0", "httpx[http2]>=0.28.0,<1.0", "openai>=2.20.0,<4.0.0", @@ -23,7 +23,8 @@ dependencies = [ "packaging>=24.0", "importlib-metadata>=8.0.0,<9.0", "tiktoken>=0.8.0,<1.0; python_version < '3.14'", - "tiktoken>=0.12.0,<1.0; python_version >= '3.14'", + "tiktoken>=0.12.0,<1.0; python_version == '3.14'", + "tiktoken>=0.14.0,<1.0; python_version >= '3.15'", "tokenizers>=0.21.0,<1.0", "huggingface-hub>=0.34.0,<2.0", "click>=8.0.0,<9.0", @@ -32,7 +33,8 @@ dependencies = [ "anyio>=4.14.0,<5.0", "async-timeout>=4.0.3,<6.0; python_version < '3.11'", "pydantic>=2.11.0,<3.0.0; python_version < '3.14'", - "pydantic>=2.12.0,<3.0.0; python_version >= '3.14'", + "pydantic>=2.12.0,<3.0.0; python_version == '3.14'", + "pydantic>=2.14.0,<3.0.0; python_version >= '3.15'", "pydantic-settings>=2.14.1,<3.0", "jsonschema>=4.0.0,<5.0", "boto3>=1.43.117,<2.0", @@ -55,17 +57,20 @@ Documentation = "https://docs.litellm.ai" proxy = [ "gunicorn>=23.0.0,<24.0", "uvicorn>=0.33.0,<1.0", - "granian>=2.7.4,<3.0", + "granian>=2.7.4,<3.0; python_version < '3.15'", + "granian>=2.8.0,<3.0; python_version >= '3.15'", "uvloop>=0.22.1,<1.0; sys_platform != 'win32'", "fastapi>=0.136.3,<1.0", "starlette>=1.0.1,<2.0", "backoff>=2.2.1,<3.0", "pyyaml>=6.0.3,<7.0", "rq>=2.7.0,<3.0", - "orjson>=3.11.6,<4.0", + "orjson>=3.11.6,<4.0; python_version < '3.15'", + "orjson>=3.12.0,<4.0; python_version >= '3.15'", # redis-py's C response parser. It arrives with redis (via rq) either way; naming # it here is what makes redis-py select _HiredisParser instead of the Python one. - "hiredis>=3.0.0,<4.0", + "hiredis>=3.0.0,<4.0; python_version < '3.15'", + "hiredis>=3.5.0,<4.0; python_version >= '3.15'", "apscheduler>=3.11.2,<4.0", "fastapi-sso>=0.19.0,<1.0", "PyJWT>=2.13.0,<3.0", @@ -210,8 +215,8 @@ dev = [ "tomli==2.4.1; python_version < '3.11'", "pytest-mock==3.15.1", "pytest-asyncio==1.3.0", - "psycopg==3.3.3", - "psycopg-binary==3.3.3", + "psycopg==3.3.6", + "psycopg-binary==3.3.6", "pytest-xdist==3.8.0", "requests-mock==1.12.1", "responses==0.26.0", diff --git a/tests/unit/router_strategy/test_complexity_router.py b/tests/unit/router_strategy/test_complexity_router.py index f956bb66e6c..9b4019bead6 100644 --- a/tests/unit/router_strategy/test_complexity_router.py +++ b/tests/unit/router_strategy/test_complexity_router.py @@ -1065,6 +1065,11 @@ class TestCustomDimensions: "create table widgets (id integer); ALTER TABLE widgets ADD label text;", id="regex", ), + pytest.param( + {"patterns": [r"\b(?Pcreate|alter)\s{1,4}?table\b"]}, + "CREATE TABLE widgets (id integer)", + id="named-group-alternation-lazy-repeat", + ), ], ) def test_custom_dimension_changes_only_matching_requests( diff --git a/tests/unit/test_uuid_helper.py b/tests/unit/test_uuid_helper.py index 589d3f96a17..f636942ba88 100644 --- a/tests/unit/test_uuid_helper.py +++ b/tests/unit/test_uuid_helper.py @@ -1,14 +1,20 @@ -import importlib +from typing import Final +from uuid import NAMESPACE_DNS, UUID, uuid5 as stdlib_uuid5 + +from litellm._uuid import uuid, uuid4 -def test_uses_fastuuid_and_uuid4_works(): - mod = importlib.import_module("litellm._uuid") - fastuuid_mod = importlib.import_module("fastuuid") - assert hasattr(mod, "uuid4") - assert hasattr(mod, "uuid") - assert mod.uuid is fastuuid_mod +def test_uuid4_returns_canonical_random_uuid() -> None: + for value in (uuid4(), uuid.uuid4()): + encoded: Final = str(value) + parsed: Final = UUID(encoded) + assert parsed.version == 4 + assert encoded == str(parsed) + assert value.bytes == parsed.bytes - # Ensure uuid4 returns something that looks like a UUID string - val = str(mod.uuid4()) - assert isinstance(val, str) - assert len(val) >= 8 + +def test_uuid5_accepts_byte_names_for_stable_ids() -> None: + namespace: Final = uuid.UUID(str(NAMESPACE_DNS)) + name: Final = "guardrail-\u00e9" + generated: Final = uuid.uuid5(namespace, name.encode("utf-8")) + assert str(generated) == str(stdlib_uuid5(NAMESPACE_DNS, name)) diff --git a/uv.lock b/uv.lock index 531082730bb..60bcb367e0f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,8 +1,9 @@ version = 1 revision = 5 -requires-python = ">=3.10, <3.15" +requires-python = ">=3.10, <3.16" resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -58,7 +59,8 @@ dependencies = [ { name = "json-rpc" }, { name = "packaging" }, { name = "protobuf" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/7e/8ac10bbf8b15b16574355f39b17dbdf617a282c27b41c7ff2116e30336df/a2a_sdk-1.1.0.tar.gz", hash = "sha256:e8102dad1b36709dbdc3d19319e38e6dfa3b3a79c30416030eb2d482576be204", size = 375726, upload-time = "2026-05-29T09:34:43.015Z" } wheels = [ @@ -107,7 +109,7 @@ dependencies = [ { name = "frozenlist" }, { name = "multidict" }, { name = "propcache" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, { name = "yarl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6c/4c/bdccd81e9ee225b69c60e7766c9a5b05364f118f4d383713b89a682d772d/aiohttp-3.14.5.tar.gz", hash = "sha256:5558a7f5a05af9ecf744af91e5baefc436f93c9333e656c27ec253f9a6bbe178", size = 8078052, upload-time = "2026-10-11T01:05:12.408Z" } @@ -230,6 +232,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/20/eacbecfea3b5c3dcbfc9b023e3a5460f43e5dda16d06a2877ebe7184c3f3/aiohttp-3.14.5-cp314-cp314t-win32.whl", hash = "sha256:33f706574e32c6e694f352a856e05caf18f7f2c871b3e87b41c55ea452b409ab", size = 503973, upload-time = "2026-10-11T01:03:10.481Z" }, { url = "https://files.pythonhosted.org/packages/ff/78/18eec294f6c8c5dc845dcf6d730a0147d8d0f17e86138a7bdb85e43a30fa/aiohttp-3.14.5-cp314-cp314t-win_amd64.whl", hash = "sha256:5ba14a839fbe87cf7c12a6b5661c05f324a296eb8363141edb3944ba63d4c9d3", size = 531190, upload-time = "2026-10-11T01:03:12.716Z" }, { url = "https://files.pythonhosted.org/packages/9d/39/e53f8169acc85271ebd12b5b32ad7f1541b35639ccbe0f49034c64785d10/aiohttp-3.14.5-cp314-cp314t-win_arm64.whl", hash = "sha256:1061b364556e8172e8d46b0b183adeeb73e8c42d30ebc745591e1bd89acad52e", size = 510360, upload-time = "2026-10-11T01:03:15.08Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1c/06d89f58b2db3ee92dd377217659d587e06f973e57bf0a97a0d8a4586c0c/aiohttp-3.14.5-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:788ecaa9c10533b786ce5ba70c4f2df78ad41819fd00a6c99d92b66f9a32e1da", size = 540674, upload-time = "2026-10-11T01:03:17.483Z" }, + { url = "https://files.pythonhosted.org/packages/18/39/5e822e038f496f0540ada91e27099d48f9e7f919b6c6deb4cf6db36cc706/aiohttp-3.14.5-cp315-cp315-android_24_x86_64.whl", hash = "sha256:5c76f1802bab718a68ac3cce447160605c734551f95c67ae90fa1132b215cb29", size = 554183, upload-time = "2026-10-11T01:03:19.71Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ff/0cf2619d902b5b160762422a7e5e02091295766a7fe4fcf5b9a655e386c1/aiohttp-3.14.5-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:a6d02b4c38de03d9c7617813433e6a0fb6b522797974177d69d9dad431900833", size = 523160, upload-time = "2026-10-11T01:03:22.193Z" }, + { url = "https://files.pythonhosted.org/packages/86/99/3553abfc53a40849dbaacc3f54c730ec58410809ffa2d05885ae56108f2d/aiohttp-3.14.5-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:20726f9782d5c2744c1c66255842d1d163bb3edcf768b8de25216bf47f7b6ccf", size = 530311, upload-time = "2026-10-11T01:03:24.435Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a4/5d25f73754bc1e8f983ba704e86d641aea290c967f195f2aeebdaed2bd84/aiohttp-3.14.5-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:248d779ad720b49d4fb355720e60c9e5f444f95887bc16974fea48fc56c41789", size = 540274, upload-time = "2026-10-11T01:03:26.698Z" }, + { url = "https://files.pythonhosted.org/packages/29/a4/07eda5db2e3ee017d9590f36c12a94ec6f1f50516e8df78672373dfc7185/aiohttp-3.14.5-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:0a8ea271867e360ac985ae607f4a23ad9a38414b9aca1d49ec98839ae660e49f", size = 819239, upload-time = "2026-10-11T01:03:29.481Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/a8723dd987a696dd48d4cf2f0088e589ce77caebff0b96f2a78c20424380/aiohttp-3.14.5-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:823c910f046f23f4c713b8d99a2242dc65f591cb45ee86418fa11762a3c2963c", size = 549832, upload-time = "2026-10-11T01:03:32.02Z" }, + { url = "https://files.pythonhosted.org/packages/29/5c/969a1b72692055fd2a419590c41847ec9144fefb97b75ff1cde5b6372891/aiohttp-3.14.5-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9b42db919715e91eb76acf3bc492a9a7ccd8bd9adc6745c1412b689735269f14", size = 549234, upload-time = "2026-10-11T01:03:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/38/05/8e3e07fd8a0d33d06955ff4e54a1cb92f4bce347ff55e441dc3e25a7b5e5/aiohttp-3.14.5-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3112585250b199296c26ca6e0131640b6a8d01bab8b232d2eb3763ed469de11", size = 1891314, upload-time = "2026-10-11T01:03:36.899Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b3/05a79ce2e25f024e93de30c94f39dc6aa6e2bc1e9c531a5c4b18dc61b7a4/aiohttp-3.14.5-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c147451b4a58e7050f7f7394e6c467867c84161560001f9ad4fb2d1446743946", size = 1884372, upload-time = "2026-10-11T01:03:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/94/52/0fd8af0717db109eea258191b326b5cb5847fb88928bfa6c862fd78b9ad3/aiohttp-3.14.5-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bf163cc701f3d4ac43ba7d97771bf5fd955220ef5500ef3ee847bc0ecfbf4ec1", size = 1930957, upload-time = "2026-10-11T01:03:42.172Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b3/fa78733da88812bf9fb193913fb0ce1548f8b6912047633fdf88a758ff8c/aiohttp-3.14.5-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d40ce41991e9d56fab4f5dc4a51fe59bc3b5c77c27f4b148963064d00232e8", size = 2038886, upload-time = "2026-10-11T01:03:44.646Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f5/2fcc5e30053a938286f17d0edf3f0850b8061b984256fa7c26850b9c8809/aiohttp-3.14.5-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:276a4fc00b1d9ae492b802763a789c5b86328b989c5ea169f2faa447d6a11c7c", size = 1894470, upload-time = "2026-10-11T01:03:47.304Z" }, + { url = "https://files.pythonhosted.org/packages/62/2a/f87feb42abe8e6a7c03814dbcb711540849a1e90aa392055f3183c643610/aiohttp-3.14.5-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50983e3be33d8c0942ab88cec3905b10602f64c469b20153c48c5d4e558dd016", size = 1716273, upload-time = "2026-10-11T01:03:50.121Z" }, + { url = "https://files.pythonhosted.org/packages/81/b2/adf1f960dd977722ed1347d33da512a1624807114f57a3b91f5cc828e081/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:16c8abd5bca220a47efe667d26f8460124c81810787e79ee87b242677563d9dd", size = 1857513, upload-time = "2026-10-11T01:03:52.959Z" }, + { url = "https://files.pythonhosted.org/packages/d7/fd/ef8d910e641de4160026a513ace5888b7f92826bbc3fd90ced05d55a828e/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:0790ec66fa4013e83c53b9025a45d454723da1a2fce28b3208c9b32d08af162f", size = 1887704, upload-time = "2026-10-11T01:03:55.641Z" }, + { url = "https://files.pythonhosted.org/packages/7e/db/6c9142f941cba8d35be8e1fae6ea2bd390e754fc14076b8147aee1a4592f/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:cb11a971a3aea10f9b8373be628f1df932964fc6c6b174516d318a48c3ac4412", size = 1895684, upload-time = "2026-10-11T01:03:58.18Z" }, + { url = "https://files.pythonhosted.org/packages/e6/7c/6a6bd9a72e576d376c668333b00c51c6147aba4fb863ed6c94996d497eca/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:932ce7e694bbc29b2bf6f64f2343c27d148d4997c771d01bdade4639b6749ff4", size = 1706299, upload-time = "2026-10-11T01:04:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/69/ec/d2cc494242f8c4d3d1cd70baf591742818a74386dc3b84af3195c5c4fced/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:a7d470cf7b206e6359fc77b1b860632fde400d5a2ed59cd0181b93a686bc81ee", size = 1916645, upload-time = "2026-10-11T01:04:03.892Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6e/e852c53647e815db09a1b6b5ab634f54bd736412397e11940feef4d2c89a/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:6e1d8637cf73eebc92eba2e11d4cfff98a3b562f2505bd75bba766d908926e8d", size = 1875408, upload-time = "2026-10-11T01:04:06.922Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f6/72ab6ef20c332399be593bac543d2c24a6d241e39d3540ce9f95a63e4bd2/aiohttp-3.14.5-cp315-cp315-win32.whl", hash = "sha256:fbdc5ec49f9ca3cd24955cf3520b10a4d4c901ba2572094c84274e9e7eb30534", size = 485590, upload-time = "2026-10-11T01:04:09.626Z" }, + { url = "https://files.pythonhosted.org/packages/06/eb/e9de75b8c6d2170c42c08ff303abf857ea8a6d9d9b6e99b5aba40f15e962/aiohttp-3.14.5-cp315-cp315-win_amd64.whl", hash = "sha256:a9d3983bd6ab7aa1cfd573544ae98df9b6cb6912a5185a198263e024a636861d", size = 511740, upload-time = "2026-10-11T01:04:12.277Z" }, + { url = "https://files.pythonhosted.org/packages/fc/25/455f3c2785eb0d50748cffd0abd07500815f419a9495b14610b7622d8d2d/aiohttp-3.14.5-cp315-cp315-win_arm64.whl", hash = "sha256:e29347c142cf6e99e0dff5e2995ead1d50fa3b51bf37a7c726a7ccfe5419745a", size = 495171, upload-time = "2026-10-11T01:04:14.667Z" }, + { url = "https://files.pythonhosted.org/packages/e7/6c/497f0a98782eebfcf0f02a7fbdef5428148bd426027140cbad494cf842b5/aiohttp-3.14.5-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:9bf1d5dcc15204d9ec8b8ea4c18fd66e6b80e5de1f4ecbafb3a2f2740f8039d4", size = 855638, upload-time = "2026-10-11T01:04:17.154Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c5/55c0cef2572af9b1ee81608f7c0a1bf74e6c9151a73b04915d933f192335/aiohttp-3.14.5-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:14f04769cfefe4734016a856a83af36133cd17779cef9ae817f812b8ba9d6d51", size = 567455, upload-time = "2026-10-11T01:04:19.702Z" }, + { url = "https://files.pythonhosted.org/packages/4d/47/e1a0e39f4a2b881f6071225afaf94a6547001b5aefc1566734d73c685934/aiohttp-3.14.5-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:6e4251c0ba4624a68a2c11471a1ac54c3306876c21f0ae86de085cc9241c8905", size = 567836, upload-time = "2026-10-11T01:04:22.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1d/817d85836f52b687160064a326e62e037dc42f1ad5b6b5188a70e5c134b5/aiohttp-3.14.5-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4f5cf4dc72a71c4cfa9751b4950be22f733626670230d46e7d606592aa22d59", size = 2051409, upload-time = "2026-10-11T01:04:24.821Z" }, + { url = "https://files.pythonhosted.org/packages/f7/25/e8ea6fc212a9aabce982917346ce8ecab0929c74b60232a056dd11c99da0/aiohttp-3.14.5-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dbf53ae2601b7fd5a93c3944deea3a78d40f495226d582c35ef7a433425ce2b2", size = 1936493, upload-time = "2026-10-11T01:04:27.456Z" }, + { url = "https://files.pythonhosted.org/packages/24/33/de0517f71f1a19feea4aff78a2ec4ec2d98634129ec30ead78fce2f8f81b/aiohttp-3.14.5-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:657291433bf4dd3142f3abac495764cd47d0c7c92087751e6666c6447e65fcef", size = 2021405, upload-time = "2026-10-11T01:04:30.276Z" }, + { url = "https://files.pythonhosted.org/packages/c4/11/ddaf2e7930543e9f0cad3a1c54e1af0c1bd2fae3973d568c4263d3b010e9/aiohttp-3.14.5-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e51a27980c3788e6e6b3325d694fdd4898087fa8a86b2763af77b39353da41e", size = 2142017, upload-time = "2026-10-11T01:04:33.022Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7b/58784353c06de8adc20f426daad3d85dd86fd55331c713a3f4b638c94573/aiohttp-3.14.5-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82c7583cd3dfdc7dcc927835b4f6c7faae7ecc1ba3ca5879321621ae2e6f8e84", size = 2002482, upload-time = "2026-10-11T01:04:35.94Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f0/417d9535caa9e165ffe6a53e2347a78107e7c87dcb0e10aca315c3af0344/aiohttp-3.14.5-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6fdcd6af7e2e51d1ba1b4bea16e97b074bcb7b5dd0246a9d8201341bb28085a0", size = 1791774, upload-time = "2026-10-11T01:04:38.733Z" }, + { url = "https://files.pythonhosted.org/packages/98/01/25e49c2e8a01b9f0e19ca0a8448ad50aa2bdf96c8cd41e92bd45af044784/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:c8859a013ae0de1074660992139a1a440df3e6b219b86cf0d3f11c2692bb4fe3", size = 1970358, upload-time = "2026-10-11T01:04:41.66Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fc/c132fd3465b6c7e4ce0193154f602c3e6c46b680e4da7eb3bd0d8a40d1c7/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:8966ecac808dd5f473c9c4cefd10cd3ffda71c18a4d3493b7c7d2ae1803bf2cc", size = 1969192, upload-time = "2026-10-11T01:04:44.66Z" }, + { url = "https://files.pythonhosted.org/packages/95/4e/d58b45e7dba4eb607eca11fc0a4aa77ae0f39c11afa804635a61ce18cff4/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:3093b72c215bda16ce961a6d073f6e71d46e022962a9d5d457c5d4d421c78b57", size = 1963618, upload-time = "2026-10-11T01:04:47.505Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d9/f6ac50946efb3490428ef52b56e62c1c6b7f9c6ff3ec6083535526c83e60/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:149fb56caf7acb67073126f675d0958d9c4b3125fcd3f6d4877df98aa8a97ce9", size = 1784817, upload-time = "2026-10-11T01:04:50.287Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2f/f255eb63da788cd8a452fe350869c03266ccad7d884925f6963c87808f24/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:293d3ae7c6a0ed176a42e59a1b5fde825ead65c835360f734148e96729f928d2", size = 1995956, upload-time = "2026-10-11T01:04:53.213Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/241aa3393eaafda0034470add1625f82a4c252901108723b283a9b0b32ca/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:3f2dcc00191fd563e9075181a14ec31d7dd63223ced7582cc70a15a499de0c79", size = 1967794, upload-time = "2026-10-11T01:04:55.958Z" }, + { url = "https://files.pythonhosted.org/packages/5f/7f/a68e689288c9e4bfcf8d0979f2b12b774bf01861985420df6150eba5448e/aiohttp-3.14.5-cp315-cp315t-win32.whl", hash = "sha256:7779cd97e61ebe583ec2f1c5616cdd038aa08a4453b1848c67842176d054948e", size = 503876, upload-time = "2026-10-11T01:04:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/a4/78/49b0299da6d54de19fc6fdc6889d50233ac47d192a461624f4fc010fde83/aiohttp-3.14.5-cp315-cp315t-win_amd64.whl", hash = "sha256:0e6f16f5e49c4b8267988c05ab07760d7064cea57d077c3d068d04b0fbb992cb", size = 530957, upload-time = "2026-10-11T01:05:02.23Z" }, + { url = "https://files.pythonhosted.org/packages/21/d4/b0afc936aeb6d2f93157e3408b069ec5d7934429ae7d023de5e0953b1887/aiohttp-3.14.5-cp315-cp315t-win_arm64.whl", hash = "sha256:1aead151c3abbac6b32942e452020cb66d7efc099d253cc6c20f748e926c858b", size = 510408, upload-time = "2026-10-11T01:05:05.362Z" }, { url = "https://files.pythonhosted.org/packages/68/30/173960c42b05a6c59f7558e4b12a4b0d9ba376cf6aa9bde7f9e08a30ca8d/aiohttp-3.14.5-py3-none-any.whl", hash = "sha256:efc21a454892828368b11c2c780de0ff8bc991f73f6b99c6b66e56205470929b", size = 279517, upload-time = "2026-10-11T01:05:08.523Z" }, ] @@ -239,7 +282,7 @@ version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/a7/809482759f40079f4c4328c7318bf569ae25d457f5017aad30a1b9aafedc/aiologic-0.17.0.tar.gz", hash = "sha256:65aa058e858c94cd208badb188e7f00b54dcabb3ba85b34f794db98074d108b9", size = 251625, upload-time = "2026-06-14T12:24:35.367Z" } @@ -253,7 +296,7 @@ version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ @@ -277,7 +320,8 @@ dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } wheels = [ @@ -312,9 +356,11 @@ dependencies = [ { name = "docstring-parser" }, { name = "httpx" }, { name = "jiter" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/ea/0869d6df9ef83dcf393aeefc12dd81677d091c6ffc86f783e51cf44062f2/anthropic-0.84.0.tar.gz", hash = "sha256:72f5f90e5aebe62dca316cb013629cfa24996b0f5a4593b8c3d712bc03c43c37", size = 539457, upload-time = "2026-02-25T05:22:38.54Z" } wheels = [ @@ -333,7 +379,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ @@ -405,7 +451,7 @@ name = "asgiref" version = "3.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } wheels = [ @@ -418,9 +464,11 @@ version = "0.52.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pydantic-settings", marker = "python_full_version >= '3.14'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/c9/d111b0546635fee21eccaef00edcac8146b450c840dd65ca8d193921ac07/assemblyai-0.52.4.tar.gz", hash = "sha256:c31f777409ba030690840faec6840e36bf0223851becd008e5e61df199034546", size = 57906, upload-time = "2026-02-25T15:30:22.667Z" } @@ -433,7 +481,7 @@ name = "astroid" version = "4.0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } wheels = [ @@ -535,7 +583,7 @@ dependencies = [ { name = "aiofiles" }, { name = "aiohttp" }, { name = "colorlog" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" } }, { name = "python-dotenv" }, { name = "requests" }, { name = "requests-toolbelt" }, @@ -634,7 +682,8 @@ version = "1.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a6/f3/b416179e408990df5db0d516283022dde0f5d0111d98c1a848e41853e81c/azure_core-1.41.0.tar.gz", hash = "sha256:f46ff5dfcd230f25cf1c19e8a34b8dc08a337b2503e268bb600a16c00db8ad5a", size = 381042, upload-time = "2026-05-07T23:30:54.302Z" } wheels = [ @@ -650,7 +699,8 @@ dependencies = [ { name = "cryptography" }, { name = "msal" }, { name = "msal-extensions" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/3a/439a32a5e23e45f6a91f0405949dc66cfe6834aba15a430aebfc063a81e7/azure_identity-1.25.2.tar.gz", hash = "sha256:030dbaa720266c796221c6cdbd1999b408c079032c919fef725fcc348a540fe9", size = 284709, upload-time = "2026-02-11T01:55:42.323Z" } wheels = [ @@ -664,7 +714,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, { name = "isodate" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/97/e5/3074e581b6e8923c4a1f2e42192ea6f390bb52de3600c68baaaed529ef05/azure_keyvault_secrets-4.10.0.tar.gz", hash = "sha256:666fa42892f9cee749563e551a90f060435ab878977c95265173a8246d546a36", size = 129695, upload-time = "2025-06-16T22:52:20.986Z" } wheels = [ @@ -679,7 +730,8 @@ dependencies = [ { name = "azure-core" }, { name = "cryptography" }, { name = "isodate" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/24/072ba8e27b0e2d8fec401e9969b429d4f5fc4c8d4f0f05f4661e11f7234a/azure_storage_blob-12.28.0.tar.gz", hash = "sha256:e7d98ea108258d29aa0efbfd591b2e2075fa1722a2fae8699f0b3c9de11eff41", size = 604225, upload-time = "2026-01-06T23:48:57.282Z" } wheels = [ @@ -694,7 +746,8 @@ dependencies = [ { name = "azure-core" }, { name = "azure-storage-blob" }, { name = "isodate" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/4b/66e68e1cb5655d3f290541151dc97c82ae565e4146b66f72f3465ba6e617/azure_storage_file_datalake-12.20.0.tar.gz", hash = "sha256:8d6932b75470de935998755d980a3296412d8ff2a11631c1d99267ae65f11f03", size = 283945, upload-time = "2025-03-27T17:43:19.42Z" } wheels = [ @@ -854,7 +907,8 @@ version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ @@ -1002,6 +1056,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/da/1ec2e9b4f500693e56e6391f128dd0d304d6d2b7f0d5090e80a802b722f1/brotli-1.2.1-cp314-cp314-win32.whl", hash = "sha256:0a30e5df5ba985b67f85e13dfd7e5a68e186d8d9b5cff7d9a8904832bb413e81", size = 338208, upload-time = "2026-10-22T13:58:40.927Z" }, { url = "https://files.pythonhosted.org/packages/24/5e/80cd38322263da5e92ea4935423f1d3cfa2509201673dce77b772c47b997/brotli-1.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:972a2e19dbaab47d0aa77c1d07f71c930bdc53538f2b33decc905e92a4be5cbd", size = 376707, upload-time = "2026-10-22T13:58:42.025Z" }, { url = "https://files.pythonhosted.org/packages/c5/35/cb35c489fcd0a813bbb7500465af5a7bac3bee7d996f6a211bac9096ef2c/brotli-1.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:a3dea7e1ac49e29306500a8627fa3eacabcc9e238d22495eea2d8e61de77065b", size = 390280, upload-time = "2026-10-22T13:58:43.256Z" }, + { url = "https://files.pythonhosted.org/packages/a2/72/85ea89c1088673a297e8b652cf0080668812144d5525db6512eadddf9d19/brotli-1.2.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:eea17e3cdc6f9aecffe15fe9a9eeba8e8c049a5d347de1561eed2240cdf35a79", size = 863002, upload-time = "2026-10-22T13:58:44.325Z" }, + { url = "https://files.pythonhosted.org/packages/81/6a/bd4db4ef0c4d083d8a2e5a1d0b20239b186f261e72b14a9a0ec91c078e92/brotli-1.2.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:761db18dc32b24d00fbedca1013089eb3470053cefa8492bd9c07b11102d72bf", size = 443263, upload-time = "2026-10-22T13:58:45.474Z" }, + { url = "https://files.pythonhosted.org/packages/79/1a/08635be453d523474fdfce81f588583a4e8049c75c04df849b5a278bf027/brotli-1.2.1-cp315-cp315-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:035f3bf861328d8b7fcb61f4a976f9913027158e5666116733d59ffc65f2745e", size = 1244379, upload-time = "2026-10-22T13:58:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/3b/94/52f2facc5ae118a68c4f0e715e03c9f854507a359608804d7de2dfe8d603/brotli-1.2.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:aa32b51702a0b6e9fddc0d1dc66cb6ed6af112f1bba0cac2be9039546a862b5c", size = 1404133, upload-time = "2026-10-22T13:58:47.756Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9b/768635c710b684574da55e12bcdbe36419fd8bf6cb97e01c72a4593f9747/brotli-1.2.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:810aea5958d6a2d92593fd27218df528644aeb72cc7f6d401e650752d6c98949", size = 1424875, upload-time = "2026-10-22T13:58:48.912Z" }, + { url = "https://files.pythonhosted.org/packages/46/39/7b7d53d7cea61f35f6b52ea149804523d01e532bf26017eed4351d82a7ad/brotli-1.2.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f00ff4b56caf151365c77988eb85a4a71eb26fe63047d4084d5614e06d65e3cf", size = 1425345, upload-time = "2026-10-22T13:58:50.253Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5e/652c788a4da5c6415c7ae31382626ba870020c9280e0f9be1a6dfec80261/brotli-1.2.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:dafaeb38712f99e5c31ad7f02c366f1ddd3792921b94066015f8c49f197163b0", size = 1487925, upload-time = "2026-10-22T13:58:51.593Z" }, + { url = "https://files.pythonhosted.org/packages/85/c9/1b7753d02c106ff70b3d88056586ed7c2fe329d2aa5707cdaebe90372942/brotli-1.2.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:972e34c5cbbfe6c90358bfae38edae1a29d3456e920da5a4664c98f221ee4df8", size = 1276526, upload-time = "2026-10-22T13:58:53.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5f/6ae3e85bfb7a3f4711cea5a1b92d4377fddf1319a666cda213cc83b6c040/brotli-1.2.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:59ca34c7a83d7a3b842247183c426f891a95a8f57db41b7aac92195c51e8a83a", size = 1594681, upload-time = "2026-10-22T13:58:54.571Z" }, + { url = "https://files.pythonhosted.org/packages/8a/52/2808406d83e5462c086d69dbebb892f4ed78d2eb4b6755ea4b9e58357e3b/brotli-1.2.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:90bd49511341dd7c40bb3acaa97cd8b9e5df4821a5714b842117e7069c7cc03e", size = 1486968, upload-time = "2026-10-22T13:58:56.008Z" }, + { url = "https://files.pythonhosted.org/packages/a9/53/35042a907c309af8defb423e58692bdc5e3e1fd3d03388892b7614270626/brotli-1.2.1-cp315-cp315-win32.whl", hash = "sha256:1880db6241276e191d3e34a6e4eea708c6629103c396f0ea21e75b0ce63a9a2c", size = 338212, upload-time = "2026-10-22T13:58:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/38/6c/883a32c23603e278ae93d8df31f0573b67382de827e6e05a6e9ee0b51896/brotli-1.2.1-cp315-cp315-win_amd64.whl", hash = "sha256:09a2f112a638aca15a88f298571788795fc45d62f2bca7f3870c1152153ef399", size = 376707, upload-time = "2026-10-22T13:58:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d5/2c574da9faf130a59c4c685ba1378f2f470b57fb9fa5d3b0f8b4e90579a0/brotli-1.2.1-cp315-cp315-win_arm64.whl", hash = "sha256:91097d66ebb8ca8d23372081a33405a44e2239445614888e38929bd7f5879dae", size = 390278, upload-time = "2026-10-22T13:58:59.512Z" }, { url = "https://files.pythonhosted.org/packages/7f/eb/803fb4003fc74fb7202eade0832057729837c0929b690e7f691c1778f230/brotli-1.2.1-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:93269f11303fc9a3e3990b57e0533f41949546156d9f0e535571f313032563cc", size = 416747, upload-time = "2026-10-22T13:59:59.612Z" }, { url = "https://files.pythonhosted.org/packages/60/fc/48f827dda08844d6313a97c6c34b7f0cffc8310bf4f2cd4d98fae6c198fb/brotli-1.2.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:87492842770cd772fce25e9cd279913eaea6c55a91a960b6302aedfa2ec7b152", size = 399671, upload-time = "2026-10-22T14:00:01.176Z" }, { url = "https://files.pythonhosted.org/packages/c3/2b/21aa1de7b7b5739f8d9b55c12100de794c42b10abd1a83da1a99072e2b46/brotli-1.2.1-pp311-pypy311_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f06dd6a30438692d48c0d54de4b659cd55a57c2c2f6591c969c98d0a5523ab07", size = 404664, upload-time = "2026-10-22T14:00:02.357Z" }, @@ -1289,7 +1356,7 @@ dependencies = [ { name = "jsonschema" }, { name = "mcp" }, { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/45/10f00a408b480926b5e340e3507562ede1e30d4ce5b73eea9ccc3468a9e6/claude_agent_sdk-0.2.165.tar.gz", hash = "sha256:1bfa8e7a6bb36e82de9a12324c9bcfa6eebd4f96ee5890f0c656d50445240ff8", size = 365169, upload-time = "2026-10-08T18:18:49.528Z" } wheels = [ @@ -1435,7 +1502,8 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -1655,7 +1723,7 @@ version = "50.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } wheels = [ @@ -1712,7 +1780,7 @@ version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiologic" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/d9/e3/49afa1bc180e0d28008ec6bcdf82a4072d1c7a41032b5b759b60814ca4b0/culsans-0.11.0.tar.gz", hash = "sha256:0b43d0d05dce6106293d114c86e3fb4bfc63088cfe8ff08ed3fe36891447fe33", size = 107546, upload-time = "2025-12-31T23:15:38.196Z" } wheels = [ @@ -1811,6 +1879,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/8e/b97984d9f43e2f36f5317034711140c3bd1fbb5d96880b1c11b9e1e8c3d3/ddtrace-4.11.0-cp314-cp314-win32.whl", hash = "sha256:c267fe4efceb71019f5e6833fdb4fbdff18a51641bdca9fceabbe579984461e4", size = 5831072, upload-time = "2026-07-10T08:57:03.578Z" }, { url = "https://files.pythonhosted.org/packages/e8/6a/7fb18925588f645d6d777c90d388f9a640adf7b986be7457943717b74170/ddtrace-4.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bebf3cb12ac51c422ad1ed74873746b8398cce845075b234115c2f518a7a652", size = 6455126, upload-time = "2026-07-10T08:57:06.362Z" }, { url = "https://files.pythonhosted.org/packages/a0/b2/44d7f07ba94c70bd1827669d446a2e979f7efd6e4a409c469a59684d9a7b/ddtrace-4.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:f596cb2da582600e091c8174ab4f8e44bf4f05c50c03cc450e0378c516e0c6e3", size = 6130096, upload-time = "2026-07-10T08:57:09.413Z" }, + { url = "https://files.pythonhosted.org/packages/99/22/dadad1a1213a846499f360344c4be0854635bbd9c5726d2902bc8e92b194/ddtrace-4.11.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9d9a7c4644e4ffc858e951eeab37ab86e9b717ef1bc2f2a47de69f1ec373bccb", size = 6454958, upload-time = "2026-07-10T08:57:12.714Z" }, + { url = "https://files.pythonhosted.org/packages/d4/45/4a5bb647d47f01d135c918ad33678d1dba5b0210cc688da5d2f69534830e/ddtrace-4.11.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b49433f0e85e16c34f578e01ec4e263ab5181d9636635add8d6194bd47dd163c", size = 6638706, upload-time = "2026-07-10T08:57:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/80/2b/175096efe3784f36d383e09d7bfa1defdba85689008417008f122a3e8d0c/ddtrace-4.11.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:2affbac04583808cba374976d07e59a4fe156d1de122f8def30550bf7717b745", size = 7466814, upload-time = "2026-07-10T08:57:18.873Z" }, + { url = "https://files.pythonhosted.org/packages/80/b3/8fb55c9183ed5a162525ae308e0aaa8237ceb2a3ba78d65a8f1336c16b21/ddtrace-4.11.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:51a28ab8072c741e104d8c6f47d990eb5989a359b7ea4d91bab7c5c1f5c1a03c", size = 7707994, upload-time = "2026-07-10T08:57:21.918Z" }, ] [[package]] @@ -1929,7 +2001,8 @@ name = "docutils" version = "0.22.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -1966,7 +2039,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1996,7 +2069,8 @@ name = "expression" version = "5.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/c7/bb061623b5815566bda69f5e9d156e38a97ebb383b8db3d2dedb26415466/expression-5.6.0.tar.gz", hash = "sha256:454f6fe138347194a43c7f878d958efe9b84b9cc770e462010c7a52e18058065", size = 59147, upload-time = "2025-02-19T09:37:37.432Z" } wheels = [ @@ -2010,7 +2084,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "redis" }, { name = "sortedcontainers" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/11/40/fd09efa66205eb32253d2b2ebc63537281384d2040f0a88bcd2289e120e4/fakeredis-2.34.1.tar.gz", hash = "sha256:4ff55606982972eecce3ab410e03d746c11fe5deda6381d913641fbd8865ea9b", size = 177315, upload-time = "2026-02-25T13:17:51.315Z" } wheels = [ @@ -2023,10 +2097,13 @@ version = "0.136.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-inspection", version = "0.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } wheels = [ @@ -2053,7 +2130,8 @@ dependencies = [ { name = "fastapi" }, { name = "httpx" }, { name = "oauthlib" }, - { name = "pydantic", extra = ["email"] }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, extra = ["email"], marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, extra = ["email"], marker = "python_full_version >= '3.15'" }, { name = "pyjwt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/74/fc/644bc8f82fc887fffcf9a3eab8eb3dea06ee9ea160ef20441455ed7a0001/fastapi_sso-0.19.0.tar.gz", hash = "sha256:629f00581f72ea7e57f7b8775f8d2c425629c428c194359a2b4ebaa6bcb8e12b", size = 17278, upload-time = "2025-12-17T15:18:06.721Z" } @@ -2546,7 +2624,8 @@ name = "google-api-core" version = "2.25.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", ] dependencies = [ { name = "google-auth" }, @@ -2659,8 +2738,10 @@ dependencies = [ { name = "packaging" }, { name = "proto-plus" }, { name = "protobuf" }, - { name = "pydantic" }, - { name = "typing-extensions" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d4/be/31ce7fd658ddebafbe5583977ddee536b2bacc491ad10b5a067388aec66f/google_cloud_aiplatform-1.133.0.tar.gz", hash = "sha256:3a6540711956dd178daaab3c2c05db476e46d94ac25912b8cf4f59b00b058ae0", size = 9921309, upload-time = "2026-01-08T22:11:25.079Z" } wheels = [ @@ -2774,7 +2855,8 @@ name = "google-cloud-storage" version = "3.4.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", ] dependencies = [ { name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" } }, @@ -2855,10 +2937,12 @@ dependencies = [ { name = "anyio" }, { name = "google-auth" }, { name = "httpx" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "requests" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/11/b321935a5b58a82f3f65f1bce560bfefae76a798ef5d2f6b5a9fa52ad27b/google_genai-1.37.0.tar.gz", hash = "sha256:1e9328aa9c0bde5fe2afd71694f9e6eaf77b59b458525d7a4a073117578189f4", size = 244696, upload-time = "2025-09-16T04:23:45.159Z" } @@ -2877,9 +2961,11 @@ dependencies = [ { name = "google-api-python-client" }, { name = "google-auth" }, { name = "protobuf" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "tqdm" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/97/0f/ef33b5bb71437966590c6297104c81051feae95d54b11ece08533ef937d3/google_generativeai-0.8.6-py3-none-any.whl", hash = "sha256:37a0eaaa95e5bbf888828e20a4a1b2c196cc9527d194706e58a68ff388aeb0fa", size = 155098, upload-time = "2025-12-16T17:53:58.61Z" }, @@ -2916,91 +3002,101 @@ grpc = [ [[package]] name = "granian" -version = "2.7.4" +version = "2.8.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/0c/27aa25280b6c1f323312e83088304da8a7f3e5c1e568d3a560365ec6fa67/granian-2.7.4.tar.gz", hash = "sha256:1dc0530d7ae6b0ae43aafafe771ac0b8c38af68bbd71ab355828817faf13aac1", size = 128212, upload-time = "2026-04-23T11:55:55.275Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/63/c2548102956296ee0f8671291dd5a6f3f3dc2fc7c4e62e0b86e3be6de571/granian-2.8.5.tar.gz", hash = "sha256:032588689e2d524d633fcb088ee54fad4c5655d0af1a75058d5b4240be4f6864", size = 130933, upload-time = "2026-10-20T11:43:38.079Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/23/ccaa1c786aa528a672d6f25ce236156aeb06a63931109aa6f2d4d3c8a350/granian-2.7.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1c2a13c5c119e34369f984d8414edb8ba3793d7c78c37bb795942648dda3eca1", size = 6354293, upload-time = "2026-04-23T11:53:32.922Z" }, - { url = "https://files.pythonhosted.org/packages/91/b7/2b0b0e2dc07cb17febbbf3b349c96f425a53d913ba76278091f821adfc22/granian-2.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91963c4928a355d772f14075057ff721423bce70612a619edc2daf04dd258577", size = 6050464, upload-time = "2026-04-23T11:53:34.912Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9c/1feb488abb85446ac7d12d05a93788a3a6b42810d64d5806ebc309c65fc9/granian-2.7.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9d068796cb7e8e0b7a4c8d51077701e37104a39cd103c655a5c232ad561fb07c", size = 6883201, upload-time = "2026-04-23T11:53:36.73Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ac/176b47bb48689fe3575ccbf372d67dd52fb0390686f4d8b7174ab2538ce5/granian-2.7.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c932f5c292b643019c4dd410a352789dbb8cb2cb41ec5b373779a87375de398a", size = 6135933, upload-time = "2026-04-23T11:53:38.552Z" }, - { url = "https://files.pythonhosted.org/packages/25/5d/4d894789683bd074bbe0361df7e3e524a2b763e44d771e326a16d4ea014b/granian-2.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a8f411408b0b65a07460e39cb53178e30a15ff5f0c77ed6aa31e1106590ea9", size = 6783971, upload-time = "2026-04-23T11:53:40.602Z" }, - { url = "https://files.pythonhosted.org/packages/88/e3/fe781d55306f1542b3db0fac13b70b39846b5e9a400fda9a3a2bf04a9af6/granian-2.7.4-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6b7ab6a1a0c0d77ec1dd1145b7c8f3da5251ec7926c005da22f7415bf1b217a7", size = 6906647, upload-time = "2026-04-23T11:53:42.352Z" }, - { url = "https://files.pythonhosted.org/packages/76/11/c10af6940d8bb73cfb4eaa6781eb3f5c7880e1b69d9d87edc63292171538/granian-2.7.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6036316f781f7ad1412d7aa10b49c5a25e69fae3f67ed766b0923ebb43aa5118", size = 6930262, upload-time = "2026-04-23T11:53:43.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/5d129792626ac990d3b14d484736b8eafa202966f1a541c5b665c30fc880/granian-2.7.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:acef581d94270a22763fba192fc8cef0df77dac125080ca27e6e847a5e59cd07", size = 7050428, upload-time = "2026-04-23T11:53:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e0/9a9d9f9d0e68277d49829d53746b2e8b3761e7b79a666fd9a4f445587321/granian-2.7.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:187a85fe36561c74a1db94b858175824c3154ebe6d0aa61c97124427f5c5a5fa", size = 6899482, upload-time = "2026-04-23T11:53:47.372Z" }, - { url = "https://files.pythonhosted.org/packages/19/de/04de408275968d4bff8e6fed9d6abf406beccac27412a08c8daaab7c9534/granian-2.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:c73c6099206288c903a305d975064fbb51f9d0c78d06c914b23dde56165105c9", size = 3995374, upload-time = "2026-04-23T11:53:48.751Z" }, - { url = "https://files.pythonhosted.org/packages/df/00/a7db7e3627992c59927f57d5447638be515e683e2c8037ab7845250270d2/granian-2.7.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:455c51baf51dd0c3d22004fc04f9afb0662cb84ab2b75b48e5d6bb8b3e4e3548", size = 6353285, upload-time = "2026-04-23T11:53:50.113Z" }, - { url = "https://files.pythonhosted.org/packages/b2/23/337ab1a0929cb0cfbdedc06879cff62d6c08cb725fa2d4e139c7e305fed3/granian-2.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2c54f3fe69790aa4b685372bcc8f382a8e9ba570b8ea4cb476e3b240a5a5a7c", size = 6050711, upload-time = "2026-04-23T11:53:51.887Z" }, - { url = "https://files.pythonhosted.org/packages/28/ba/3605834adaf5dc9ac3701b817bc9d42c73c89fb67815c7c87c7f64a9b6e1/granian-2.7.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9549c44b325fe51ee4fc57308761f5178add4d531f1cc333b4a1eedf4a5b7af", size = 6882199, upload-time = "2026-04-23T11:53:53.298Z" }, - { url = "https://files.pythonhosted.org/packages/2e/1e/f11c9773dbf07ed326efc26a771b39ce97f7ee25608c16d69248db2da8da/granian-2.7.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0910390ea8f893cc4c3f38a28c923a321609358cf46d31aa7df5c3d3e58e8337", size = 6135800, upload-time = "2026-04-23T11:53:55.186Z" }, - { url = "https://files.pythonhosted.org/packages/f1/96/ca238b4f5d813643264abace48ca630efb1ab6d10409bd9e2c05c1d1ef12/granian-2.7.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0de44552990b3dacb87ea3f37ebbcce67881712c0b0db500013821b14df7e4e", size = 6784786, upload-time = "2026-04-23T11:53:56.706Z" }, - { url = "https://files.pythonhosted.org/packages/ae/85/2feeffc37fe3c8a0f3e932393bcc99c8972984fe95907b34b380284caf1c/granian-2.7.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5c9c6d51a675d9b7084244e63157899dd1afe6f1a5ab014015bc86afd4871df5", size = 6906836, upload-time = "2026-04-23T11:53:58.991Z" }, - { url = "https://files.pythonhosted.org/packages/ae/b6/faed26e3abd741e1d261defc0e7e3b2ecb9a2189c557e829bb28c3281456/granian-2.7.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6be8c6ebbc53efea03284aef87de9b7367df3c9433f7df3b46c1edceaaa9d840", size = 6929985, upload-time = "2026-04-23T11:54:00.846Z" }, - { url = "https://files.pythonhosted.org/packages/25/5c/9f7a836177b5e94ad15da49046254e5b837e1d2c3d03981111d4af9a9d2b/granian-2.7.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:c10e056a6e76da640adb35f88d41ba40ae44065c5e04d4bc35f47c19a7f83a99", size = 7050668, upload-time = "2026-04-23T11:54:02.466Z" }, - { url = "https://files.pythonhosted.org/packages/08/88/19c01761d639b5e2c2eb2f12ff064c6765f32fa7f129c9f48162cdca0668/granian-2.7.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e60a3153456f8922ca73d3a427cc3bb594c021f70ec08ecded6581efe25f48c", size = 6899480, upload-time = "2026-04-23T11:54:03.948Z" }, - { url = "https://files.pythonhosted.org/packages/04/ec/9022f3c2728fcd99f9374ce633e254b201f4bf76fbe60031bbc137f1cf8a/granian-2.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:e9cafbf391d16ea8b8a2e9f88501783fac8da75eb948620899062a17929c4a84", size = 3996087, upload-time = "2026-04-23T11:54:05.771Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d9/148024fd3a8bd974bb5c68a0cb48d15df7763fd1364bf090ccc2d423028a/granian-2.7.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2c2f40aaecf2ba3d8232e55181c8f6db7bc68d9112a419ab8d5f9e2f33f631f5", size = 6374067, upload-time = "2026-04-23T11:54:07.293Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bb/c53b61a7cb67d33677d96913438eca3d79de1b1b7173a361fcdf2753ade7/granian-2.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a8111d5e74b27721e0fdda3edba7c154d44c41b469466857ca3c51b088e3846b", size = 6046338, upload-time = "2026-04-23T11:54:08.684Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/5c9dc91b9c9a05bf6ed0b795d30f4bb8f290d61502779a89ed2fd75f9fb6/granian-2.7.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74adbb6c1920dbf4271b824135639318b2a20ff5e33bc35639a8e2928a777234", size = 7000585, upload-time = "2026-04-23T11:54:10.451Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7c/c770593b24a472ab5265a44546f56079757efbf89f8e8b2229a8443e453b/granian-2.7.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b778d356b61e0389c823016ad2be50a634b80d3d28a33922f7ac39553e828ad", size = 6255544, upload-time = "2026-04-23T11:54:12.484Z" }, - { url = "https://files.pythonhosted.org/packages/15/46/796147587edb494a330294cb001cf68520ad8296a7da91d80ec672ac8615/granian-2.7.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3607b091c4ef225ee99150f3b02cb827de8d677b52fc75f0b28893244f7bab27", size = 6875124, upload-time = "2026-04-23T11:54:13.967Z" }, - { url = "https://files.pythonhosted.org/packages/c5/25/b867f624886e11053e7a6235244de26fd864a136e65d12295e728b3e5005/granian-2.7.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3d3cf4fe3cafd9b874d8b749c66c790cbf2b4225f2a7d9fb284c51b77a8e938d", size = 6982394, upload-time = "2026-04-23T11:54:15.733Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e1/5746bfe202bd2f6a1506346463ce52dd015c2b5d03d07a53ecf0fddefa3f/granian-2.7.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:846c9cbfea8684ab13d21d66855ad06dc077fb95b5590e7f5040e79994d6429d", size = 6991457, upload-time = "2026-04-23T11:54:17.325Z" }, - { url = "https://files.pythonhosted.org/packages/e0/45/fc6992839d367b6ae8fa8d88b5e70ec293162c3a2e0e6b90fc426f228df2/granian-2.7.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:d34d97cfe4a7805ecb5b1b1684f3f197bb4baf019d2a9f18e34fd1d697a03a7f", size = 7148499, upload-time = "2026-04-23T11:54:19.234Z" }, - { url = "https://files.pythonhosted.org/packages/fe/12/16ffd64a1213858d4cf824767b398758be807dd1a6df5a303dc76994b6d6/granian-2.7.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f11336e4bcd8ef5c5143b075b5260e37e8431eb36d68564cc39416ca526c797f", size = 7006829, upload-time = "2026-04-23T11:54:20.804Z" }, - { url = "https://files.pythonhosted.org/packages/95/9a/f2fcda200f8739ddf25be72591b7a28897be0ffd952a76ec655e5f877144/granian-2.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:9e0a4370773ec4a0e92a55a33fc700b60003e335480e5c7fe941f4bc3dda2e18", size = 4026771, upload-time = "2026-04-23T11:54:22.36Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0f/fa7c63afedcb214edb96703cade360d946d5f1ca59ddb0b3d8e04587fb45/granian-2.7.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d11da4a4527ba8dc28b5533d5e3241d8d9212e593195d27c6e72c8a422010af5", size = 6373513, upload-time = "2026-04-23T11:54:24.246Z" }, - { url = "https://files.pythonhosted.org/packages/be/39/3088ce32d940f7982102ea3bdc230090e34ac56dc0bce04f2d03b56ea435/granian-2.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:057a3db87e93eca1a11255dd13b45b5dd83f798a750fd87f02e14d54db5741b6", size = 6045232, upload-time = "2026-04-23T11:54:25.708Z" }, - { url = "https://files.pythonhosted.org/packages/ac/61/588f6b5397ea4f5bd9fc8de4b8cc092c555b8d95371c03d149b3bc419277/granian-2.7.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb63d64c686799cea850c0c328d21adf75e323991a20be04923afc729432d2b5", size = 7001059, upload-time = "2026-04-23T11:54:27.532Z" }, - { url = "https://files.pythonhosted.org/packages/58/63/2affbcecfe96f940744c2086ea3793935d5f6898207590a579c92fc8588f/granian-2.7.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f406648c47569e983f0c58bd0853bac30a2bcdc6227428255ee5cc65a8ee62b6", size = 6255487, upload-time = "2026-04-23T11:54:29.397Z" }, - { url = "https://files.pythonhosted.org/packages/87/ac/31f7155a467020e7640e91af15ca3a70b0e7da210de42e3d3344e5eba8d0/granian-2.7.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bd56306eed06e293f4848c5ea997e1d019d1ad13b8252dde1f0bc773aca85ef", size = 6875068, upload-time = "2026-04-23T11:54:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/99/22/402cc903e5c4e82bd363177392d4e1dcab8b27c1f7006c5316c37c597056/granian-2.7.4-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:732639e612e6b6e8d481f399f367e8c9bbb6f0e1b7b0aa74db340c574ee3dd98", size = 6982487, upload-time = "2026-04-23T11:54:32.704Z" }, - { url = "https://files.pythonhosted.org/packages/d3/92/3878f977bda82fc3a66fc7e95a54366a7b82edd53e6c9fdb3ec053693280/granian-2.7.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:47b8fdbfb369d52bb3fb884514a6a3a7e4d8e81c65fd26e5232985f2b46ebe0f", size = 6990683, upload-time = "2026-04-23T11:54:34.301Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b3/a1239f3bc4e9034e07cb32403e6a6d26db01bba1c244dd654f6a76bf2612/granian-2.7.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:b679086082bfd7c1aa8c248ef673b715616a4ce58eec6fbeef8b83b30ac84283", size = 7148570, upload-time = "2026-04-23T11:54:36.494Z" }, - { url = "https://files.pythonhosted.org/packages/89/3c/fef781ea7356b21f671615dd0d53adc00fad81031a9ea506f80d1f46a43d/granian-2.7.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a29191e949a99ffae2807abb7a864f7493f7a744e4fe2ddd2b5cd8db9b71378d", size = 7006976, upload-time = "2026-04-23T11:54:38.135Z" }, - { url = "https://files.pythonhosted.org/packages/56/54/ae2979fc45c06fbb37f595ee10eb6b138b6056202163b8e274d140d3f87b/granian-2.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:07d26325cc69371ea2dc9d3a9cd0cc851c1c8e3dce40aca90e8c204547b5ba7e", size = 4027044, upload-time = "2026-04-23T11:54:39.957Z" }, - { url = "https://files.pythonhosted.org/packages/21/51/10344430e495bfa128dccc114957b33e712e971f91668788c08fe791df73/granian-2.7.4-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:4e093fe9511387313ad7ec9a76b0c78397cc584ef3dff47d46c336c5aee9cd8d", size = 6249290, upload-time = "2026-04-23T11:54:41.738Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/c7eda2e71a89a13e174598649f721c63ed3d908c0904b62621e8a433af0f/granian-2.7.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:227889f821526b8b60c5edf31b01fc987c4193bb0fc198c0998e0841e0cb719c", size = 5901799, upload-time = "2026-04-23T11:54:43.708Z" }, - { url = "https://files.pythonhosted.org/packages/72/d8/79e51f9f794389a9d6cab3d7c6b834b87d65fba72a43784eb5d2664a57a6/granian-2.7.4-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2b28d4aec5a9f2758a48da1897649a01b70ee1c00f2c4649db574527a3d00943", size = 6037594, upload-time = "2026-04-23T11:54:45.595Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d8/835873a407279435fa0c8e8ac52392d3ba5c9a652bb15c0036aa07d9c302/granian-2.7.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f708fea5024a40e0dfba1c17c1c4b09e02e00ac0ac9ac1e345b409f0c11b71e5", size = 6966672, upload-time = "2026-04-23T11:54:47.242Z" }, - { url = "https://files.pythonhosted.org/packages/92/5f/21eacdda27c38e4194de5f9bef36c4045058daf6d58533fadb7c54c70573/granian-2.7.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7006dfe9852cded794bc60008a168faf4dc2ecc18f1d74b5fde545685b699ec", size = 6563668, upload-time = "2026-04-23T11:54:49.751Z" }, - { url = "https://files.pythonhosted.org/packages/bd/06/9b19956d75277df44ee380e873a86b9890c431f2e2bcde32b3ba341f0efa/granian-2.7.4-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:77103af44034e30505fb5577b8214b0ad39cd6cbdc854ff980d4755faf93adaa", size = 6664285, upload-time = "2026-04-23T11:54:51.502Z" }, - { url = "https://files.pythonhosted.org/packages/85/33/740e0c9478be49c0778c4ea1773357680980e10e84b59bc19664033996dc/granian-2.7.4-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b23194e1e0652297086224212605edb4998442511637e732d6009506277f8ff9", size = 6820367, upload-time = "2026-04-23T11:54:53.506Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/3453fc1212268a01fee957122f2b1699af0efe50eca07ac570e11d1be12b/granian-2.7.4-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:f62941a4ffa1f1c2c5750cfc0b0ad96aa85d63b016125289779eef8888f5340d", size = 7132366, upload-time = "2026-04-23T11:54:55.123Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ca/8479e4d2a02f210ce68b5dc73c77953ec1dfd3769bf725d06e6ec420d502/granian-2.7.4-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:ea6f97d2ade676f1bf49b79088fa4b5640b8b9804b7470218486df3d4be50046", size = 6842094, upload-time = "2026-04-23T11:54:56.665Z" }, - { url = "https://files.pythonhosted.org/packages/0d/96/71f95c73220726aee3e908b3ad2745c4c44fbfba508cb5ed615a9d4d367f/granian-2.7.4-cp313-cp313t-win_amd64.whl", hash = "sha256:759140ceef02ef72e57a184461927d72bcc2ddd3664c3cbbf4def7516f818041", size = 3974523, upload-time = "2026-04-23T11:54:58.541Z" }, - { url = "https://files.pythonhosted.org/packages/98/5d/a0c3d8778cd8aa68131974d34c439a38a00a32953e71e3b549759a5e3cdb/granian-2.7.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c19ebe797d7383cbb3497c599b8201af71f9fff6b18deaf9965d106f61588ab8", size = 6322736, upload-time = "2026-04-23T11:55:00.292Z" }, - { url = "https://files.pythonhosted.org/packages/5e/99/211da053030574f2402c750f3e3e5dc587f5192eac4888affe6ca8894a9f/granian-2.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4cee0bdba9179537669c2fa0afab2ce89327a372f1b2a82f280798da321c996c", size = 6052103, upload-time = "2026-04-23T11:55:02.797Z" }, - { url = "https://files.pythonhosted.org/packages/ab/9d/23ec1fd519a4c0db961b05d1821869ed6371cbaf8b3d3a0a85c04f89e6ca/granian-2.7.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4bc5b54845bfb5f87537483f25c8f8e6003c3c1b4b0eadf6b93a432d0604265", size = 7000868, upload-time = "2026-04-23T11:55:04.826Z" }, - { url = "https://files.pythonhosted.org/packages/98/35/b8798c98c90d3293d9c85580ea6021f148d5ab73ab99d1f82a0e66f73131/granian-2.7.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b550fb98b89465c8192b6e506993de6bfb956838e715ffb58e944aec1afdae99", size = 6257266, upload-time = "2026-04-23T11:55:06.962Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4f/5574db17193d90a5831120a0ce2a2dc64a711110ccb9af5a3630260c3597/granian-2.7.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7100a6a6d3835fec2a207fef536a259dd42d9efdb5c46933cf6f9d55d5bfaad", size = 6849667, upload-time = "2026-04-23T11:55:08.862Z" }, - { url = "https://files.pythonhosted.org/packages/66/a7/90b85cc6a31cbee772fc8ee731479429a64169e389444a5fdd685d44a342/granian-2.7.4-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:034ac1bfe8c19b5a7916d35a1ca426845db9ac11215f1b367566aec3b6530549", size = 6902612, upload-time = "2026-04-23T11:55:10.888Z" }, - { url = "https://files.pythonhosted.org/packages/06/6c/ba203ca40bd406db0412bca70281e44712f941bc6aafb59a628f4811d517/granian-2.7.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:baf1c390a25d3d9840204c39e7b801c909e99e896ae2713d898c46b563cbf962", size = 6927025, upload-time = "2026-04-23T11:55:12.663Z" }, - { url = "https://files.pythonhosted.org/packages/ee/52/77e2abfba54523943eea275ebbe733a6d186fe646304fe25f6d22b243d03/granian-2.7.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3bb99778ae05c1118cd694717d025cc0b85f5ee81f60cbcb2a8783692798db96", size = 7146800, upload-time = "2026-04-23T11:55:14.459Z" }, - { url = "https://files.pythonhosted.org/packages/1d/66/7209201856b7de8d3c643ba87e11272c4d651c216d05ea3fcbdce0da4ab0/granian-2.7.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:13f0a39872afa81c6aaa8e29832371fd831373140f1f04de459ff862824f488b", size = 6999983, upload-time = "2026-04-23T11:55:16.236Z" }, - { url = "https://files.pythonhosted.org/packages/c3/45/bd1e521284714615996dcee48dad47d8b97ca2767a7e7cccd392f25fc176/granian-2.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:97b5aeec98a9c6c0695bf8f068bd03aca83fc17c0d977a9c3a2e57bb5f10d47e", size = 3989433, upload-time = "2026-04-23T11:55:17.774Z" }, - { url = "https://files.pythonhosted.org/packages/45/a2/609f8f0dca7f596b5fb6e57b988b4b8f4d6579724b2720933c379d43301a/granian-2.7.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a7b1aca6c654f0e61c9e493dd6d3ddb1698f47dc33ed04566a6635948b081b64", size = 6251034, upload-time = "2026-04-23T11:55:19.29Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/2eefa8ff477cce7b119ed2fe97fc1f3b2d108397d4755e83a5198149f2c8/granian-2.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d4e0c8cc6850dec7180a26b6805b2c4cdbac4c1c48077fd7857a3cd8ff342d9d", size = 5912772, upload-time = "2026-04-23T11:55:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/ae/40/9a5070badaed4ceecf4082855985840c320f7232b8c1ddc93e1732c63265/granian-2.7.4-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7e6b1f6e0fe873efa3393ef28803ff699a94254f2a7dc07422cc01d9849e2136", size = 6037318, upload-time = "2026-04-23T11:55:23.855Z" }, - { url = "https://files.pythonhosted.org/packages/95/52/1db412e63425cb12f5ca61877956583c6d12f21657b1a3e47eb3200e9c1b/granian-2.7.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dce110217825cff60f68da83280bc20471b10e004e720fa94b845e01925d8698", size = 6962778, upload-time = "2026-04-23T11:55:26.095Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f2/fcca39f617bf70e29ef903bb7a4d037970c637023484f2112d9ed6882516/granian-2.7.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:058f9a4ebfc7b9c2577569c6ecfd333628d0d045de272afaa65ee9933849778c", size = 6566618, upload-time = "2026-04-23T11:55:28.233Z" }, - { url = "https://files.pythonhosted.org/packages/ee/20/0da1bb552746d74275017e1ffc7fc419dd1a33345f132f6f5a90f9f41142/granian-2.7.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:7c05f74fa5b5dcedc9f035a7c10b8afd90a3d941975a370f1e07c3f3095dd883", size = 6670850, upload-time = "2026-04-23T11:55:29.945Z" }, - { url = "https://files.pythonhosted.org/packages/11/2a/d0d9cdb10d2760e2f47bd4600c8eef02e326f8f7e253a80ce4ba384265e6/granian-2.7.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:8b992bbc667e3c74de4ad48ac8d735c7cddf3f709fc2097f7dd230ecc46fd7b3", size = 6824752, upload-time = "2026-04-23T11:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/3d/79/0432f92f9df6e54394e4dd1c159c0d4814d255a2d2541fa9a5c187d19152/granian-2.7.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:df05e0f85712b3e90ddf28cb8be358664b1afa8cb8f09978141ca70052dca3a7", size = 7130809, upload-time = "2026-04-23T11:55:33.807Z" }, - { url = "https://files.pythonhosted.org/packages/19/03/11cc0e08f59f03a3cd6a1fe46d7632a0f8690ef945a495b1303140bb7541/granian-2.7.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:dbc620f35b67cf6b03d2b6a24b9b442d1bf52961eaebadb2c3ff214d3d0c8dc4", size = 6845920, upload-time = "2026-04-23T11:55:35.583Z" }, - { url = "https://files.pythonhosted.org/packages/b4/49/bcbaaeec0f68d3d1a3dd1fdd21e4a6963d303ae18027c42b2b53f87d6b89/granian-2.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:b9df8aead4d71562753788264db23d32db34147bb73294ddd90833bef1f4cf35", size = 3981107, upload-time = "2026-04-23T11:55:37.597Z" }, - { url = "https://files.pythonhosted.org/packages/51/18/577637bb861ab688db8eb5d698ad700133818debd7ae6f58c0574c43f70e/granian-2.7.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ce50300cf876f418ba0545f6e8c56d8c75038fc503add0fd1b58d9a3057d95ea", size = 6363314, upload-time = "2026-04-23T11:55:39.837Z" }, - { url = "https://files.pythonhosted.org/packages/48/41/11a6219baa10270f1a6a2a101cfa372e5d55a46a839a43b49a8d087fac09/granian-2.7.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb7f727f14d7d485a5df4078e7cc3038864b4e7c380865968e75e1e51e62457a", size = 6027259, upload-time = "2026-04-23T11:55:42.122Z" }, - { url = "https://files.pythonhosted.org/packages/bf/58/12b19b17fb79ee064a8a77a865a031bb49f4ea813789ad63186458ea02c9/granian-2.7.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abbab303b502a770355c13c93569e6c0c71ccc864ab41b59636720d5a643f6b3", size = 6760497, upload-time = "2026-04-23T11:55:44.116Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/572711f882423f599707aae577ccdbc1700cf0cc3ceb4e9500e00c6b8d14/granian-2.7.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f0b0423fa33a1afb9730fbfb5700fef4dac16bf7a1b7a2a79d0349739c1b1f44", size = 6843897, upload-time = "2026-04-23T11:55:46.322Z" }, - { url = "https://files.pythonhosted.org/packages/31/f9/75d51721069a184cd00310c4b0b0d614a6370905c13a096ccee193432ba3/granian-2.7.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:efa0d4fc35ab42562747e4103124e1c4f21afab081c1591de6472174a3416802", size = 6876194, upload-time = "2026-04-23T11:55:48.231Z" }, - { url = "https://files.pythonhosted.org/packages/44/5e/fd81492529bc2b02dafc63c95d03c2c7faa26ac883ccd94aa93b21fc68c3/granian-2.7.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:03b5ce06df095b5db49bd4e976ac8d8419bb0e73dc160613fc3db5e5d5dcd1af", size = 7094066, upload-time = "2026-04-23T11:55:50.328Z" }, - { url = "https://files.pythonhosted.org/packages/c7/04/f2fa35dc2956edb9a5abaabc0840aed92b4121ce27adf684a1c75e3c70ac/granian-2.7.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9247db25dd66f74766a6a9488f1279c9b40cf422c6d7a04010492fa1aa7c9019", size = 6892580, upload-time = "2026-04-23T11:55:52.084Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/f45fa86c36fcc34f6e613bb223b10fd36c6acd9f7aa43d4f65d0f1eff4cc/granian-2.7.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:efccd6818a1ac4cba7eededf5e2768f56d4a8c7c93bd5e3a8d7a901510976944", size = 3971242, upload-time = "2026-04-23T11:55:53.834Z" }, + { url = "https://files.pythonhosted.org/packages/9b/82/71f4ecf6fbe2eb7f9f32c2b73527c52ad1213d8d765876cf4a2175caa2eb/granian-2.8.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:98f09b0539e9dcce6ee534232b2d933ba97f27173675ef5a177d01380aa76371", size = 4592856, upload-time = "2026-10-20T11:41:32.556Z" }, + { url = "https://files.pythonhosted.org/packages/45/27/56b851450114a64e492f2a5ee3cbfcabbaaace58ccba00ab0e7dc101c4ff/granian-2.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf900618c67163d7c881ed2cbc995fabd315a72618fd334d8101276fc50a5f68", size = 4311235, upload-time = "2026-10-20T11:41:34.606Z" }, + { url = "https://files.pythonhosted.org/packages/66/f1/64c4cb8de1aec462d99e9bb0f87709eee3a5c18951845d2a0d9d96f25d03/granian-2.8.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:88245684c009a66ab23c73ae1308763fc5e21a703cab75d4f9ee05ce1938836e", size = 5078378, upload-time = "2026-10-20T11:41:35.881Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d9/8445f7a40c6a245fe03654f9f498ca63a987352517fa96e854684a88e890/granian-2.8.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c84021e3f548bae8b3efdc27935a374f5348fffb1f0b478ae62a9b533d2aed", size = 4592000, upload-time = "2026-10-20T11:41:37.161Z" }, + { url = "https://files.pythonhosted.org/packages/46/bf/7e6a90f84363700f18e347be11edf642bc2ac8c839bc965b683a62abffb8/granian-2.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c54bc18a182cd57319076ef96d89c3be13605aaeafda1ecad999bd0449d201", size = 5047109, upload-time = "2026-10-20T11:41:38.627Z" }, + { url = "https://files.pythonhosted.org/packages/29/e1/656503580d5ca6f12872cd53ee452f9f222fb7a65468aaa39d7e3bad1602/granian-2.8.5-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:62f0d98695efa53b0f90ae211eff7c485009777378ad33af1ba46421173c239a", size = 5093769, upload-time = "2026-10-20T11:41:39.937Z" }, + { url = "https://files.pythonhosted.org/packages/99/92/40cf612d8ca16434ba3370a9a093d5a4d5040b52c0232ca960b0a719899b/granian-2.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:55b19896b5ddfff20a48bc07314d3f1136a04401e9e1b2a13dc5e35797f77f1a", size = 5125540, upload-time = "2026-10-20T11:41:41.565Z" }, + { url = "https://files.pythonhosted.org/packages/bd/16/50ecc5a575c38fb2d0bc2ffd2c35cd90dced4f0595b466782147788e80ea/granian-2.8.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:6baaace6a81a2068872bb60e550b9d9119c1143db7534d9cad8aa3e64d9c3059", size = 5136772, upload-time = "2026-10-20T11:41:42.992Z" }, + { url = "https://files.pythonhosted.org/packages/fc/08/6241869f4dcf7fed403331803fe0851b73b04da17f00bb18f461d4b39193/granian-2.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e531e9c22e527c9c8cfbf188fc446269011bb15370ec033b4f3b6decd52e215f", size = 5139950, upload-time = "2026-10-20T11:41:44.467Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f2/8d9241cf97c6487330dc313dd60016ea199ca4056ce79cb8064ae050eae2/granian-2.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b54346f0e8a83b6d6b2ff23160fcbac17329540c45c3ebd1f968966d70ff3cd8", size = 3243248, upload-time = "2026-10-20T11:41:46.15Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1d/c5eb0aa2ffcf6d18c34e2a0f77bc731b1f5c1159357315be036c5ac1eb30/granian-2.8.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3203ffa641bd6f16b2951afeec626178880b702d04e803a16dbbc38c1b25f859", size = 4602986, upload-time = "2026-10-20T11:41:47.423Z" }, + { url = "https://files.pythonhosted.org/packages/76/0d/731274bc814123f0ad1fac977202b65d2a703819a0b37c54315a4b7b78cd/granian-2.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dac7fe7e8263000f74387e07e3a178279d971039bd2b6d029129798ef9e2f59", size = 4311541, upload-time = "2026-10-20T11:41:48.658Z" }, + { url = "https://files.pythonhosted.org/packages/27/4f/d66f6701a770dedffe845581b9f03ff7ad7713a7ba5c38ad527fbdafbba4/granian-2.8.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23d77fd0fe124fce9829ddbaa5b73c357580634331ca294f57bf37e9b8cfe834", size = 5151321, upload-time = "2026-10-20T11:41:49.913Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/67d0578d42bf97ecba89510a384da94dbdc147550d9f06e814ad14b08593/granian-2.8.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8435d25cf9427e954d6133dab0a07ff5fecab38e243ba28172ffcda3e7c2664", size = 4661179, upload-time = "2026-10-20T11:41:51.147Z" }, + { url = "https://files.pythonhosted.org/packages/21/09/0a59603a3733ab46bef1df03538813de11a510bfd97a324d5072c8370563/granian-2.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fcdbdf012cdea0eb628b2858dac2e9137272c72d16b52d25b3cf014450f8c8", size = 5032423, upload-time = "2026-10-20T11:41:52.414Z" }, + { url = "https://files.pythonhosted.org/packages/50/89/a5883d04b5dad8f01fcb66803cadcf6fe900d1150cd715f5b08742e2e952/granian-2.8.5-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d733a133b381abfd4ab5035346071c81480e9ff2abedcb4375f10ce1c7c68794", size = 5052813, upload-time = "2026-10-20T11:41:53.829Z" }, + { url = "https://files.pythonhosted.org/packages/57/67/5151a391bc39fb2628e995332480e7efa5d83db69b499cb0aaac6832cf55/granian-2.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d9438061a319f5f621ba9c963baa760f62f907d95c21cdf10cda6434e75879f5", size = 5084084, upload-time = "2026-10-20T11:41:55.228Z" }, + { url = "https://files.pythonhosted.org/packages/20/ee/2de690e2dc0c6978f6d392f04c3c4d74c04fa7e780c669bce2332b868a4a/granian-2.8.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:ce17c1f040b8595ca65d6638a920590c8511e5c27365c7222653d0154480636e", size = 5204812, upload-time = "2026-10-20T11:41:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/11/0c/009b9fc714dfb4d0402b8ed36bad04a2567d4b77cbba56712549550a335d/granian-2.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:06fa5fbbe2b1dcc66a3384bbe1d156730ff0d6128173f9fa35b999d9b6eca500", size = 5123155, upload-time = "2026-10-20T11:41:58.531Z" }, + { url = "https://files.pythonhosted.org/packages/dc/34/65d14a23c9f3d84228862451d3d84aaf5c06289fa5628b18dfd42616bbce/granian-2.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:84db0de5ffd800bf912405daa08092ec19bab5aa05e1cc65236fc721c0aa0fb3", size = 3231999, upload-time = "2026-10-20T11:41:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/27/91/103c04ea8ef7d9d40cdecb96a07cae09bfe9e3857d39b8174fc5c1a771fd/granian-2.8.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3404a620364f3816f126863910e6dc86da833298608896e556c01c9bf52a63c2", size = 4600588, upload-time = "2026-10-20T11:42:01.027Z" }, + { url = "https://files.pythonhosted.org/packages/df/f2/52735de5caa6db560bfe91b60c97f5a1bf57a035ccd08004f21f18762830/granian-2.8.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dcb0ecee7650794738fa347ce1aac5a7f790bf36dc0dace4194578335ad3fa60", size = 4310695, upload-time = "2026-10-20T11:42:02.305Z" }, + { url = "https://files.pythonhosted.org/packages/60/e3/274741428e960bf59b12c51cdca2340698e708f3bab4ec6fa16b0765d12a/granian-2.8.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29a5e1459736acd617631524db3c430df7b7fe2fd55b6f21536eb9d30a603d41", size = 5156401, upload-time = "2026-10-20T11:42:03.738Z" }, + { url = "https://files.pythonhosted.org/packages/77/53/c9c16f814597303e4373eb3cdfc8b0ca75582f6939a1a84b9109c98cbfeb/granian-2.8.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:217c38e88aa324ef73c9c5d823434b5c5ee7a7181b36c23b408d439905ece7a9", size = 4658203, upload-time = "2026-10-20T11:42:05.252Z" }, + { url = "https://files.pythonhosted.org/packages/26/7e/9a4aebcc1fe18d849b79cbf2aded21e8d6023436b98a53b1514955c5470a/granian-2.8.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66f0de2cb17109da7ca070a2898e4543083914c2c440ba4a134a53cf4ec526be", size = 5067921, upload-time = "2026-10-20T11:42:06.555Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4b/d7bb8f98d739e62379d88131654ee68de441ccc5d6118f578784689257fe/granian-2.8.5-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:95bae2039b53bc532386a0b1327b52e73c16b3e11997a727024f9bd385242a2b", size = 5117522, upload-time = "2026-10-20T11:42:07.749Z" }, + { url = "https://files.pythonhosted.org/packages/59/3e/2c494c6afa6c4ae564c05d0611ce7ee108824812a74bb20e024e752a8f2c/granian-2.8.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c72079469aba9856d4d030b21ff75a5c2d9a201cab930926237ab1646e5596f", size = 5141659, upload-time = "2026-10-20T11:42:08.96Z" }, + { url = "https://files.pythonhosted.org/packages/5a/16/047f981331408110441c5adbdf9a21acd34846e13ee6546e77c7359b760a/granian-2.8.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d263fe13e16a612a0149e6948f556e5ed8fb4fdec9890812743d116025fb442", size = 5210772, upload-time = "2026-10-20T11:42:10.818Z" }, + { url = "https://files.pythonhosted.org/packages/3f/21/40f73b96a0fe0bcdea7a65ede042a029a10c1490a513fbf8909087eab02c/granian-2.8.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19902f90f272c089ecea998214c62f7eae846d454a9a82458fcb77b41adddd29", size = 5157211, upload-time = "2026-10-20T11:42:12.484Z" }, + { url = "https://files.pythonhosted.org/packages/14/a5/ac92e100d1f5141115ee796321d8f9d25d8f2c07198e5763b1ce53c457bc/granian-2.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:efc2ae72102983a32cf6cc524a595fae3211937bdeb5bf93767b01dc6798f8ea", size = 3212050, upload-time = "2026-10-20T11:42:13.894Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9e/c831afbdc6306b4c05ea89619b3e51d3350d499ab3a3078f42b3e1481a6f/granian-2.8.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:c30bf8ad2a9547b8efe4cffa67adef5d3f065cac7b37ff7ca6f7df12ed07bfe8", size = 4600095, upload-time = "2026-10-20T11:42:15.316Z" }, + { url = "https://files.pythonhosted.org/packages/29/d0/66c795135b0dc79f0a1e14fdbfc9c1d01d88ef7ceafa651185ef6f9d3e87/granian-2.8.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ea263ab8c64434080269a49dc706a1841f7939ceaf1ddec0f204edbab2d83fc", size = 4305333, upload-time = "2026-10-20T11:42:16.815Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/1b32e5d497d413903c5c58dded3751b8588ea8282d4c61ca57d694408fb9/granian-2.8.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:048feae9762a044357ed7e31b9117f646ca985b5cd57383120f93b2d3da5bb63", size = 5114313, upload-time = "2026-10-20T11:42:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/0d/94/063998b729003dab2a7a793a3deb4fec23581aa0a51f4a5e492a5fab194b/granian-2.8.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87eca4dbf606eb4a91f1c01f7cf7517c8290343cc083274a9e6d24d7b20577c1", size = 4599262, upload-time = "2026-10-20T11:42:19.529Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/c3db2af29473d37252a1629f9512c45479da3f4efe542a8ec07ebed24108/granian-2.8.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf44d5c35995f6009307d43c6b7c8310299f07f415d6cbecbacc5fdf8508c73f", size = 5065166, upload-time = "2026-10-20T11:42:20.995Z" }, + { url = "https://files.pythonhosted.org/packages/64/26/05d2bb66e8a6c07568a6ae334b7a5e9c674462ddc88cdcb9fc5a14e2b5cf/granian-2.8.5-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:30c85664d3a51397c97413f5c341e7031ea08614c016d9bfb2ee82bbd6029c12", size = 5114096, upload-time = "2026-10-20T11:42:22.261Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ca/41e56be77bcd77b4bab356302dc6b7755227ab33645185131ae3a40b1331/granian-2.8.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1be90ad793a14e367c3cc8a21aa82078b4a6e84df95700e3fd76f4207e558b3f", size = 5141240, upload-time = "2026-10-20T11:42:23.492Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cd89ab5a87ff71a3f6867fa9ec931a27810bb8e7da6b24d41288476a8f70/granian-2.8.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:9c84d73e53c774d8600d95c9eda1904ac72c3ce761407f4b41af20ce9a0b2859", size = 5172814, upload-time = "2026-10-20T11:42:24.719Z" }, + { url = "https://files.pythonhosted.org/packages/26/56/baf53317b674e5a8b8aeb1d188be42dae67982301da140b6214b448d2f7d/granian-2.8.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f66fd2c88fc3f9bed832df754dc4c05d685d5254fec54f77abd813ac28433b2e", size = 5155624, upload-time = "2026-10-20T11:42:26.048Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4b/d3fab8903e19053a1a877d12f61c9574f4eb22657ca8d16fa8dc8d66621f/granian-2.8.5-cp313-cp313-win_amd64.whl", hash = "sha256:34f5eba9ee539a45f83cfeeda5d21b84da165db1482c7be2525bca689eb37871", size = 3240628, upload-time = "2026-10-20T11:42:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/94/6c/44290c79496b69cdc68c1438428cd8207de00a8b14bbf45aca59273f1b7d/granian-2.8.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:6a2de1f78f4b54193f37f6c89562b9fd372dc68812e0f4aa82499770631ef6a3", size = 4575722, upload-time = "2026-10-20T11:42:28.764Z" }, + { url = "https://files.pythonhosted.org/packages/09/09/5b1e9d5ee47ca8e248ed4d8d41f0290a312c1c2d9415f558ae0106a6691a/granian-2.8.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0ba7323572d6f0b6cc1a86fac8f6bc42d467fe0034c85429252e93b111c79a1f", size = 4292154, upload-time = "2026-10-20T11:42:30.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/75/8e8ab830eb7d3bbeb5147bdd5e05e7f307f46950647e0277973811ce511c/granian-2.8.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdf207b3942b0c131f98cd01769b2e1e8f302891b220b39140c8d4a325d3af5d", size = 5111113, upload-time = "2026-10-20T11:42:31.924Z" }, + { url = "https://files.pythonhosted.org/packages/77/b4/1b3c164cdb8c16c130fa483ab15b18c4f068406e901b1bf30725ab2dca3b/granian-2.8.5-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a25ee45ab230a9983c6ba6401b1dbc7b3ee7de48793c52712c8ffb9662baefce", size = 4598387, upload-time = "2026-10-20T11:42:33.386Z" }, + { url = "https://files.pythonhosted.org/packages/c4/65/70d500aaf815be539d2761192b499fc7514a625eb92b1f55c4639db7d75c/granian-2.8.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8162358380d4b9398a2176779cefe553921fd5e433ccad7fe6d371e0845fe82", size = 5043379, upload-time = "2026-10-20T11:42:34.784Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b0/863f62924357de8b486f22a7a5b11e7bda2dbebcc44ce2950af210f3d57b/granian-2.8.5-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:091fa616adfbd83460433a99df0858ff3e831e937e478d1e8170a0fb71c61431", size = 5104873, upload-time = "2026-10-20T11:42:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/cc/19/4214986ccad3b6791b5b70ee8ee1545af4a12c8650726025280f69854655/granian-2.8.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:66f2ebe92dabfd986d99cb911c1027bacb35570119e244918e5beeef56dce1e3", size = 5133431, upload-time = "2026-10-20T11:42:37.506Z" }, + { url = "https://files.pythonhosted.org/packages/9d/68/fbeb8760e5854115d0573a998d2239d844f3681781f3d2ab1b732fc05224/granian-2.8.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:519d1461c2ea4086ff5643b8abaa78dadc5a3d0f2ab5ac4de1811f9980c3e153", size = 5168722, upload-time = "2026-10-20T11:42:38.779Z" }, + { url = "https://files.pythonhosted.org/packages/d1/72/063abeff9026aa62aa1412b327cb4b75b161dec5744e60b24eba6c98bbd7/granian-2.8.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2049b3b0d591c754ac89e313f88e8dd35c4aa741193e39f2329eece4e447c04b", size = 5130161, upload-time = "2026-10-20T11:42:40.216Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/fd9cee9460989df1caa497bfebe4cbe7d98d2de013e8ca92594e8b15d4a0/granian-2.8.5-cp314-cp314-win_amd64.whl", hash = "sha256:14d92199bf255db7ce64a63a63f42d52392f287c097a0427e60436111a7853a7", size = 3233798, upload-time = "2026-10-20T11:42:41.451Z" }, + { url = "https://files.pythonhosted.org/packages/fa/cd/a56c529ae01d5ebf68971a4e741844274d1565ac0d2e05ef8f2cb76ff878/granian-2.8.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9ae77ceee1148982c9d3f7ded52961355c81adae206e9621fe27c9a7c426a260", size = 4495468, upload-time = "2026-10-20T11:42:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ff/6c86c7cc796c440c383300aaf3c95736e5663a756a8164b4faa6ed0be2a9/granian-2.8.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3ab0046f99ccbb39198ecadac6df163ff312886b93630e1ca319a1fafecf6af4", size = 4176464, upload-time = "2026-10-20T11:42:44.035Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d4/6211c9839cf93b675221d388e4eb14e1eb1f3826f694b9d4b872d2a5f0af/granian-2.8.5-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ece3459a3652a6389ee663efdb39b5c5c27ebf349efdff0cbe4c6dcb7cbc9c", size = 4337940, upload-time = "2026-10-20T11:42:45.577Z" }, + { url = "https://files.pythonhosted.org/packages/05/fa/a4061b90996d5b6d8868d71b40a75f5d52618e4c8f59b5adf8d40bf420d9/granian-2.8.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2fd8a7956acfbd8b223e39103d63f80e6106d30fc9dc2f913eb3a638bc5c57", size = 5011790, upload-time = "2026-10-20T11:42:47.238Z" }, + { url = "https://files.pythonhosted.org/packages/27/b8/63070894c66028a35820d4d0c7a7ab976f74cc2c0e00cdbd1f60f79026ac/granian-2.8.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67a0fbdca1f7e923d1627b584a1f0dab66b7a18227acfd583ecdaeeff67fa4e", size = 4854816, upload-time = "2026-10-20T11:42:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d9/2f6e76dff34f81eaa4c45de9fbc86f5468f5a88e1224250c506a338bbf2a/granian-2.8.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:8efb3ab7e55f48c83695cc6cb196f56bc54874c45d2229e21c859135a4837788", size = 4884836, upload-time = "2026-10-20T11:42:50.035Z" }, + { url = "https://files.pythonhosted.org/packages/06/5c/a9459e7561b233420baec9dde2bdd1f76f9c1e026ebd51570b8f99013a6c/granian-2.8.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:91bdc54ddee28d5b809533937a086a2e1dc78e0b746b7d925785863036d815c6", size = 5082247, upload-time = "2026-10-20T11:42:51.343Z" }, + { url = "https://files.pythonhosted.org/packages/75/29/afa188c5f28b0d9ec09eda9016fa99e2a44ecd9ea399b3c3fa67f399300b/granian-2.8.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:9f9fbcfb98e19f649f70e12c432609c4d03eaf61e14b6e2093abde7042f9c533", size = 5053807, upload-time = "2026-10-20T11:42:52.739Z" }, + { url = "https://files.pythonhosted.org/packages/94/ff/b36c8988e56a2b3e7846fc63e5c93a15408d96786807427533fed87c49e6/granian-2.8.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c961cb4b17dc58bafa8a0c02a7be988c81f69fce76b5ecfe20c656fda0ede22", size = 5095841, upload-time = "2026-10-20T11:42:54.325Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9d/c25f5eaa94f5e57238c23686531833bbd5c9dc2896068f8b95f8faf860e8/granian-2.8.5-cp314-cp314t-win_amd64.whl", hash = "sha256:3012b24c51882581c6717f77f907e9ea2e8178a9f5170a4eaabcb1b67a059adf", size = 3166239, upload-time = "2026-10-20T11:42:55.75Z" }, + { url = "https://files.pythonhosted.org/packages/98/7e/7fb5e711fa2e973691175cd7629ad3ad2268359489cc00c928ee7326ce30/granian-2.8.5-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:4d7d369665b628aca1e9aead5942e017f796ddf1d894aae775b61430a76b8c34", size = 4575846, upload-time = "2026-10-20T11:42:57.195Z" }, + { url = "https://files.pythonhosted.org/packages/75/92/30313d9f420871f17937f460356a37161f0749ef4bf00f1e55a206845644/granian-2.8.5-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:5efdd2240bfa3c73b28823b600a536435e668c319b563056169dc2ccc8455c00", size = 4289515, upload-time = "2026-10-20T11:42:58.922Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/faebf1a75105cbb80fb5aaf10b0e847b5a3c95be4d1ae418574e0b9f471c/granian-2.8.5-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4643e7e06e41c5e6fd3f64120351b82f0355cac2fc55753b416d4ccfdcf3b096", size = 5107846, upload-time = "2026-10-20T11:43:00.087Z" }, + { url = "https://files.pythonhosted.org/packages/77/aa/74a3daefc65bae63aa69e83304ebc40b71f0cce697084f57ec08475062b4/granian-2.8.5-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f88a2287bd249e5412c48389eb8ef7abd402e224cae91a991c69038c05168a15", size = 4597929, upload-time = "2026-10-20T11:43:01.693Z" }, + { url = "https://files.pythonhosted.org/packages/82/7e/357ae2afd73f26f66afb10d5f56a05964034479202c6dcee0625acf11f0a/granian-2.8.5-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4dd03483d79831d9e67c3490aa4d68e717bbf68b15fd84d75d05413b341764", size = 5043750, upload-time = "2026-10-20T11:43:02.975Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ae/5a2323c84244954ea1f57cff8839b21dc160fc8867595ad02d14047c4e14/granian-2.8.5-cp315-cp315-manylinux_2_28_aarch64.whl", hash = "sha256:1eefe87b1d4294e6c5f0876f5f497af6ecca6b1ebd79a4a841cbdc9469981781", size = 5105156, upload-time = "2026-10-20T11:43:04.628Z" }, + { url = "https://files.pythonhosted.org/packages/40/6d/842f6ba44ef4c8eb58487de3b80e712c2863e79053272ad6dc6936b4052f/granian-2.8.5-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:bc75eabd606b76dbe31f2641d006152b27e8f317336faf18eb5ebe68e408b23f", size = 5135072, upload-time = "2026-10-20T11:43:05.938Z" }, + { url = "https://files.pythonhosted.org/packages/df/4c/413a653dff737322f37aa58bff198624a19a10bd949d269ae3851c98e296/granian-2.8.5-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:9db4955fdddcbf975cb6f5aa97862679647ead741ecdd08963b5fc8b8d4a3ec5", size = 5170126, upload-time = "2026-10-20T11:43:07.332Z" }, + { url = "https://files.pythonhosted.org/packages/65/2a/d2faa15f0e8dfce88abaa607946219dad89ace7c0c0654e77157db65ed24/granian-2.8.5-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:cf3ecf3a3244b9131ce0a847e50fa404b340a99b0904047d993eef0ed4cc5f4f", size = 5130094, upload-time = "2026-10-20T11:43:08.636Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f4/79b63eabb67cd36b43b98fa6d9a1a8ee95b8a01eadab64fbf9cfdfabd148/granian-2.8.5-cp315-cp315-win_amd64.whl", hash = "sha256:e23bcb230aaf0c15d8e8720f7950a7d4048d73e50032c17e5db6076af5dbbc69", size = 3233682, upload-time = "2026-10-20T11:43:10.701Z" }, + { url = "https://files.pythonhosted.org/packages/ee/35/6cb05558f4c3ceb75e8e5902450aa6ad40b11241684ef47cc9e2483584d0/granian-2.8.5-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:08b00072451bfc29cdd0dfef1066dbb8f9e9ca9d260b223f82c79d1168a194eb", size = 4494355, upload-time = "2026-10-20T11:43:12.011Z" }, + { url = "https://files.pythonhosted.org/packages/04/43/24df4c606be4d15dfca92a5ce3ee4b8f4fcf5327519af4d3c403a5b2fe4d/granian-2.8.5-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b18d114a56029d464ff2f782387b906b180177462d9d2254f4f73dda829c4ad3", size = 4176714, upload-time = "2026-10-20T11:43:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/99/ef/7ad359c036d88ae4801360bf5350e9976a9c662e2479964b96ba182463bf/granian-2.8.5-cp315-cp315t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ea673c5c98fd182138ed21d0a05528a11bd469e201f557a7b59bb8062b5b37d6", size = 4340682, upload-time = "2026-10-20T11:43:15.085Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2c/e23041ec7f44022fe9e5ffdc927008de62bc83c33eb8d3f3a6151a15a8bc/granian-2.8.5-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7aa3d26e71794cb7767b0909d92d76736b14aa0fdd451b94b6a0d4d0c875d403", size = 5012489, upload-time = "2026-10-20T11:43:16.573Z" }, + { url = "https://files.pythonhosted.org/packages/ce/95/7b9ba12477bd6c8fd34261c9586f303a9dc98928a15d8b05a0e50ced47a2/granian-2.8.5-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62fe53469efaf497f84a822537470fe8702df8125b0247ee38415e4684ee1c91", size = 4854972, upload-time = "2026-10-20T11:43:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/da/f4/b4d469cfdb0ca72e8990e4dd3183e668904f5c2e42cbda911c95bc502eb0/granian-2.8.5-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:fd39d21c95d49dd5be07b6e3031acef2590a0ef41218a38fca489700c7e05847", size = 4884122, upload-time = "2026-10-20T11:43:19.214Z" }, + { url = "https://files.pythonhosted.org/packages/55/c8/14db3b06e52ddf74b8b50e70b0ce7d8929aed24a828d35fc8bba7cf4cae3/granian-2.8.5-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:deb8d3bfb1ebefd8eaabbb2b25bb533f74a3e453bcaa419177e59d39682710e3", size = 5083706, upload-time = "2026-10-20T11:43:20.575Z" }, + { url = "https://files.pythonhosted.org/packages/d6/9e/4fcf222551e36358c66c6b971e0b2149ba8a0adb5ef99d0168033b84505d/granian-2.8.5-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:58713e4fd8b3ee5d51b5d1aacc2f800b6922dd5f19cd2609dbbcc47336bc6b7d", size = 5055522, upload-time = "2026-10-20T11:43:21.77Z" }, + { url = "https://files.pythonhosted.org/packages/c6/5c/6e1b5d5b498fae263145fe719d053810b38d86d754a92454d15a985274d2/granian-2.8.5-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:42aa50fcf6798f30bec90f9b5a22d3039be06932423b12ffa80b1ade2328a631", size = 5096223, upload-time = "2026-10-20T11:43:23.238Z" }, + { url = "https://files.pythonhosted.org/packages/72/ba/8f76547c8c52eed6ac2fe2b17e749b7fb8bef9bc23dcfd849897bc7ae7b1/granian-2.8.5-cp315-cp315t-win_amd64.whl", hash = "sha256:1fb135bb48bef82f9dcaef3462c4b49b24a6a423572c65e6b9db4647555b821e", size = 3166220, upload-time = "2026-10-20T11:43:24.509Z" }, + { url = "https://files.pythonhosted.org/packages/89/c9/751496c5332fe7b6d1775d7342f3856ba5a7b32b978c5a083d11a9025df0/granian-2.8.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7787e67de0aa08cf7ca24fdcb775cd03001d0240212a1294f73bf09356d40ba", size = 4586917, upload-time = "2026-10-20T11:43:25.967Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f1/d419875fe9fac243324dbc949e7b3dac63a70a54bb205f56a2d057e18765/granian-2.8.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:1915e09e4eb6cf3d5134633c5302e6525529b929d549a3861df011d4044f3706", size = 4257626, upload-time = "2026-10-20T11:43:27.845Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/520fe7f10a527b72294e6714370943fcd9d003da7c940c4c9695ca36943a/granian-2.8.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41017d36448de63a171c76e9e728bdad0645ca1f8a4b2a62267c61dedf13c19f", size = 5028778, upload-time = "2026-10-20T11:43:29.286Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c7/8c25aef4cde87871414eebd0ab081141af1645f0514a4179ded6d3555082/granian-2.8.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:93d8e1ebaa7af1599437a7e7bd627249374a4267ed4aad7ce08feabeea25bdbc", size = 5048930, upload-time = "2026-10-20T11:43:30.824Z" }, + { url = "https://files.pythonhosted.org/packages/d0/23/2ddcc44baabd39c32714cd3b868a1d27c6ae318a2bfac879a8241a8ac083/granian-2.8.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:8c052f5399c31eb5b08d6e67232cae69070341acb5e6d19b5aa0b45bed48f6c6", size = 5075940, upload-time = "2026-10-20T11:43:32.601Z" }, + { url = "https://files.pythonhosted.org/packages/16/24/24ae014a30c5eb98afbf88d75fe066a4cb803379e588247730966c9cdf7a/granian-2.8.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:fa4caf085a082fbc192bafe8f5bce37673b82258b56399520cf7203217eea48a", size = 5165526, upload-time = "2026-10-20T11:43:33.845Z" }, + { url = "https://files.pythonhosted.org/packages/d1/57/958e7f82de6947f95b6248c2ec2cc9979858f5f8a2ba0dd98665dfaa4c10/granian-2.8.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a51bc4b6a6c7692037bfa2a55969714318bfb72cab1901280829333be21899e7", size = 5123288, upload-time = "2026-10-20T11:43:35.321Z" }, + { url = "https://files.pythonhosted.org/packages/b3/3e/60d2c5b2e696fe59b4922bdb96d69661f073c60eb52519790e378593af5a/granian-2.8.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c6803546881c0a2e4ad21088fabdb8ef0f802c3edf5d1606cbd8b7146b7345bc", size = 3222429, upload-time = "2026-10-20T11:43:36.661Z" }, ] [[package]] @@ -3011,7 +3107,8 @@ dependencies = [ { name = "graphql-core" }, { name = "graphql-relay" }, { name = "python-dateutil" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } wheels = [ @@ -3124,7 +3221,8 @@ name = "grpcio" version = "1.84.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/3a/5c8e0eea2af3d30cb20d33ac325a98d9b799c069d63101ca4da20bcc776a/grpcio-1.84.1.tar.gz", hash = "sha256:446e7876b9b991fa124c1d414899fa7cf95f89a3e71237ea0161863dc0b2c09f", size = 13421202, upload-time = "2026-10-22T14:59:43.986Z" } wheels = [ @@ -3178,6 +3276,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/83/c87911b98ed766d7084d64053dc06f7855267f686b673dc0bb1ac8c07aee/grpcio-1.84.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f4b64099afaccdeeb987240af47aa1fc14f57285d3d2ff000c7327228624999a", size = 8088730, upload-time = "2026-10-22T14:59:13.431Z" }, { url = "https://files.pythonhosted.org/packages/88/bf/6b18690790c0c4d7062749785bc8e515881a8a4058f46cb2925c86adc9e0/grpcio-1.84.1-cp314-cp314-win32.whl", hash = "sha256:116b4796da0e7fca3fdf6edf422ea7521c25d8c4be64ab7e3b5e2a77c832b0ed", size = 4527749, upload-time = "2026-10-22T14:59:15.712Z" }, { url = "https://files.pythonhosted.org/packages/b8/d9/ee50fe67481254c45c9b709b31ba468e4c90925018f4507c424369a9abd5/grpcio-1.84.1-cp314-cp314-win_amd64.whl", hash = "sha256:3875f4c4caf0c54ac370b66322f0bf2d46e9d21414be36ea8b7f618badb3b87c", size = 5359432, upload-time = "2026-10-22T14:59:18.331Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c0/fc3341990f75cba2096d07ea5e7a8f78af8cb36f6b1fd78660b8c1220e11/grpcio-1.84.1-cp315-cp315-linux_armv7l.whl", hash = "sha256:775f98cb476625936061b91e3c1da229112b212a6b2a70fefc9c87e5aaf9fba6", size = 6393689, upload-time = "2026-10-22T14:59:20.402Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f4/6b9a70760bbd8e927f31452ed2ba33cc3dc1a327f1b46c44f65ddb6dbf0a/grpcio-1.84.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:cfdc7530e5707ab6e5c80fc624f89e26ddf8f38d3e89e5226dd7e55c080e9b65", size = 12325454, upload-time = "2026-10-22T14:59:22.805Z" }, + { url = "https://files.pythonhosted.org/packages/ea/87/e44f1c73d4c003a10a8c9b348b44296e99ec5623b70fb441ad250f4b532b/grpcio-1.84.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c61a0d5c60506b2371444716b56d6c633d3c56a17c703566bcca39604c02c12c", size = 6984778, upload-time = "2026-10-22T14:59:25.385Z" }, + { url = "https://files.pythonhosted.org/packages/19/9f/efd7828266b9e5ff0764af8420d52add5acfc13382d073596f15d7dfc183/grpcio-1.84.1-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:79b3ee4683290b3cfabadc718a008c3474073b585bc450231a842c21c2ef1f27", size = 7073415, upload-time = "2026-10-22T14:59:27.503Z" }, + { url = "https://files.pythonhosted.org/packages/80/5e/de257fc887b5a4e955173fb28a76a39fdb69f12e0d99987ed723c937c419/grpcio-1.84.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8ae49802f09cc272546592b3656207de6f719e50a6a0d1654e613aaba6336ac", size = 7165478, upload-time = "2026-10-22T14:59:30.083Z" }, + { url = "https://files.pythonhosted.org/packages/75/b0/66840129d46b06a5159277206a1f706ff755d725fdd0652e7acf6961a930/grpcio-1.84.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1e25f45414b3a53bfc1fe43b980f9d0cae9e194a62541d5f6bef0846758162ac", size = 7707719, upload-time = "2026-10-22T14:59:32.284Z" }, + { url = "https://files.pythonhosted.org/packages/10/0d/92682af96b05c1d5e5ce1ea82a3969b221c9d58112a097ff637946e395c6/grpcio-1.84.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:de3c31abc0d052511744a817506e03534f14e32dadab8ec19f63f2d4facd05f3", size = 8079074, upload-time = "2026-10-22T14:59:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/0c/69/1e426aa52c23640430eed1728c0f7c1167bb64eb7b0a31bb5ce9a500f73c/grpcio-1.84.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:6ce0835704f30d4954aa0a68352fc9cc30242cc85f39d7b6cd20c0790e05925b", size = 8118408, upload-time = "2026-10-22T14:59:37.082Z" }, + { url = "https://files.pythonhosted.org/packages/72/66/037c29a3668a32f2dbd287ceac43286797ffe14c95e7234db75c42ea10dc/grpcio-1.84.1-cp315-cp315-win32.whl", hash = "sha256:43d8b84b3e2b7505e4902ab6288ceee699cfec5435ed879bb53f876b2966ee33", size = 4527284, upload-time = "2026-10-22T14:59:39.362Z" }, + { url = "https://files.pythonhosted.org/packages/c3/87/4dea740311428c40fa0da674e0ecfa28f572809cba4cbc9c568918c82d2e/grpcio-1.84.1-cp315-cp315-win_amd64.whl", hash = "sha256:c8e1c15e81f65efb317906754fdba4a85d2eac9cff250d8a3fb3ccb3a4e52db9", size = 5360072, upload-time = "2026-10-22T14:59:41.328Z" }, ] [[package]] @@ -3309,6 +3417,13 @@ wheels = [ name = "hiredis" version = "3.4.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/1f/e2/1654d65851f39fd94e91a77a5655d09d4b64901fdc594020d8348db697b2/hiredis-3.4.0.tar.gz", hash = "sha256:da19331354433af6a2c54c21f2d70ba084933c0d7d2c43578ec5c5b446674ad5", size = 137169, upload-time = "2026-06-03T16:23:46.226Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4a/48/b0c0e2826eab7543c08980dfab871b3e7c83c47d7496134b04a94df55a1a/hiredis-3.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:69d0326f20354ce278cbb86f5ae47cb390e22bb94a66877031038af907c42fa5", size = 138470, upload-time = "2026-06-03T16:21:49.96Z" }, @@ -3397,6 +3512,129 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/5f/829287555ce7286be8d6c87c69f93aa1f38fe67c46740806416142231cf3/hiredis-3.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7ff29c9f5d3c91fda948c2fde58f457b3244550781d3bc0891b1b9d93c10f47f", size = 37968, upload-time = "2026-06-03T16:23:14.948Z" }, ] +[[package]] +name = "hiredis" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/72/64065841e93c3b8b507d7889d7de371e5f397b5efd60f18c2d82d5bd3636/hiredis-3.5.0.tar.gz", hash = "sha256:c227592cc56b7df247abe4755c26723965be68972be2149fa441ac33d82131ba", size = 143423, upload-time = "2026-10-15T18:00:45.176Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/92/d37313b1d536ce6099d5f53a00c2e1b0e59506379a209331df73a7d1abc6/hiredis-3.5.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:2e838fc213f1db0a69b2e15995ab35919fdd4f4108118454169c0b671ef9a5e7", size = 140483, upload-time = "2026-10-15T17:58:03.601Z" }, + { url = "https://files.pythonhosted.org/packages/41/1e/b937b843ed1dd59522f06cbd1522d1680fa1fad43932ecb729eee08c9991/hiredis-3.5.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:63d42b94b9800904249fdd9be9eb37f31677ba400d921d000dabfc5c0ce415cf", size = 74897, upload-time = "2026-10-15T17:58:05.446Z" }, + { url = "https://files.pythonhosted.org/packages/80/29/d0b359be616514831f24e303b2e47f15f2c882bf78b9da921db8bead267a/hiredis-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c7fbf6f70c8e3e3f6693cd52774f48e149fe2709828643ca0207021852540f", size = 71875, upload-time = "2026-10-15T17:58:06.592Z" }, + { url = "https://files.pythonhosted.org/packages/db/3a/5f9967b6ddd53174a6394fb2c7ae49531dea2ad771368e3b345c8d8c5577/hiredis-3.5.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:926d270b5a2dcd75f91ecbe784e42f515ae8fdfa57bf28031bc6d21accff5e3f", size = 306465, upload-time = "2026-10-15T17:58:07.759Z" }, + { url = "https://files.pythonhosted.org/packages/97/bc/56f5f121b0ed0afc32a8017efc462c27269b41d7d0dc596a7420a0295d9c/hiredis-3.5.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2ccb190ef81bcff7ef9650968782d446a899740fb478ec8f21988ce37d2dff6", size = 339191, upload-time = "2026-10-15T17:58:09.112Z" }, + { url = "https://files.pythonhosted.org/packages/92/3a/6bc12deb33c5494b792b327f071ddf2704c20ffcce02971876f8860bfa3c/hiredis-3.5.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d7639f8769908176bca3eaaf099f9aa182718b14f364380f5feeb1ff3ea2734", size = 349840, upload-time = "2026-10-15T17:58:10.252Z" }, + { url = "https://files.pythonhosted.org/packages/75/3f/ed12e566e6941d45db1f74ea5b799a4fa6a03d3e166a16a0b91e17251530/hiredis-3.5.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0188f80503cdfb9fc66e29f177e864280aa4e2dec50c7a66cfb29a3f08c129c6", size = 311786, upload-time = "2026-10-15T17:58:11.256Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/f46c0ac51ce5700fd8e7fe228f7c2f71c26946ad43452e3127d93a989316/hiredis-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a188c39bf426e14b91f71ec9ec54ef1c2377c1cf38a4c2b5d8b506e59f10e14a", size = 300621, upload-time = "2026-10-15T17:58:12.616Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/097c42a41ab28e33bbd4a73753ebbd13056413d40f4dfc25d58deb2ee178/hiredis-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6dd3b5eed6b208a6583ba75c54b01ac82bd547ea29835eee75837ca2348c158a", size = 330943, upload-time = "2026-10-15T17:58:13.757Z" }, + { url = "https://files.pythonhosted.org/packages/9a/93/890c870edec8c74e7f48090397e0e706044603e3425b0fc3b5fa9c4605d7/hiredis-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f5d97b54ce858857c12c3b60a89563dc1938fb4837fafa877cd870e05f4a2990", size = 331331, upload-time = "2026-10-15T17:58:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/33dc570d42c99c9656d84267517e2d095e51963d054aec02fb761993218d/hiredis-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46259a135ab298fe5197b039afc9d1783a67fb374d95ec096624aeac66ca51ec", size = 310414, upload-time = "2026-10-15T17:58:16.747Z" }, + { url = "https://files.pythonhosted.org/packages/da/f3/3d7356a605e026b442dc4e53e2deb51a16d9f9c4d7eb4d4d27ef3ae361da/hiredis-3.5.0-cp310-cp310-win32.whl", hash = "sha256:9684eb9f45daafc2a9212115c7dbf25426eb4469fe635eec6f7f4493c24d30d3", size = 38744, upload-time = "2026-10-15T17:58:17.781Z" }, + { url = "https://files.pythonhosted.org/packages/60/38/6d2a64bde7d66ef3f553c965590bf7367a4a8e02dc9d937a7031ea8502b0/hiredis-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9dc666937514d4518cc776aa2e079b79e9710536d3d65974c12b5ca257baf14c", size = 40576, upload-time = "2026-10-15T17:58:18.714Z" }, + { url = "https://files.pythonhosted.org/packages/ff/42/9225de321c4733dab591615327d79ccee33e3cf29b47dbc1e7c7178e6bee/hiredis-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:b253d6921b3a039a10dff5e3ea2c2e4c5c4f25d88eb5ba47858b6e4fe13ed99f", size = 38016, upload-time = "2026-10-15T17:58:19.625Z" }, + { url = "https://files.pythonhosted.org/packages/f1/cb/5e09c82ad64cb8820c0cdde30f8c74f51cbbde32fa907cf453a83808c967/hiredis-3.5.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a148b7fb5b6b258fd5f6cdcf0296b6905c9ff43d9607a00860ed14d3bd4a2944", size = 140467, upload-time = "2026-10-15T17:58:20.74Z" }, + { url = "https://files.pythonhosted.org/packages/d4/33/668281eaf59c61e8fd3021fc09ab2293449223cd23833b1be4d128275ae9/hiredis-3.5.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:ead3b37e15075aa2064b9c1c114cbb1b153a3ab0300fd8a69d3cf11a1ec742b0", size = 74892, upload-time = "2026-10-15T17:58:21.958Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/5e9de3a6347d980635727e3e9810c419650f6702a849b5f6c35fb3100bea/hiredis-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e115862e4e1a22aa78048117f88c21a35291dfbf9ce511359d97bd4abcbb9ebb", size = 71860, upload-time = "2026-10-15T17:58:22.933Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c4/ec550e62cd7cce8512f372e3c03fbce0f4b07f38d69b690a6b48d0b0e8de/hiredis-3.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b5f0e4114cf972d24ad651537d1b2e20449d6db006c6f09eeac58436886960", size = 306993, upload-time = "2026-10-15T17:58:24.347Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7b/89272dbab1804a56e5cb8633ecdf639b0d6d826b45ab5359cc6662ab4809/hiredis-3.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0c55c415af16dbc4158a2a7854063089383aa2504febea5b48d20267ff4892e5", size = 339745, upload-time = "2026-10-15T17:58:25.771Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a6/757edd625dafa3d6b8cdf5f3e047fa865dfd16a625359a21013cdc34d0f5/hiredis-3.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cc91b554353429715cb5e76d8f7c21ec40608831af2c16f9f93dd7462aa87107", size = 350509, upload-time = "2026-10-15T17:58:27.027Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1d/27fca9037040e83cf8b05f2a69d41ce0e28a376f12edb577d84b8c86bf9f/hiredis-3.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efe53f63d31f53088492f9e1a4f81fb84d8cf75541927679670ff0bdaf17e11f", size = 312420, upload-time = "2026-10-15T17:58:28.152Z" }, + { url = "https://files.pythonhosted.org/packages/1c/99/60a1629f4f4769b414fd0b3f816e34f2a4b1b38840ad5345ec7417f01966/hiredis-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:672e92fbbbe353697828f6c590802b5ae2d8c97bb78726ba4d107f87b0e6289c", size = 301091, upload-time = "2026-10-15T17:58:29.214Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9a/9604f0f6998af9566b921f5467100cd1a1ac916a24f9f3031ffc2be65bac/hiredis-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c404af8da2fadfbb538195f7ce1a31e1d71900c573879b6f1475415dd474f822", size = 331426, upload-time = "2026-10-15T17:58:30.511Z" }, + { url = "https://files.pythonhosted.org/packages/7b/49/4d7fc61d540d79bc0544baa162da973a838decd440bdb2100d3116de9c57/hiredis-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d86562ec68af7e5f332d7e4433c0892e6661082beba0718501c9e7ecbac63078", size = 331856, upload-time = "2026-10-15T17:58:32.017Z" }, + { url = "https://files.pythonhosted.org/packages/87/97/909850b4a4ea761731a6ac0043403ee0b3423ad7f7c6b8e2df8b7bfe8b35/hiredis-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1df3e11d7366026c4780324a5a0347dce84db2a5adc02e34d3232015854dd7c8", size = 310917, upload-time = "2026-10-15T17:58:33.493Z" }, + { url = "https://files.pythonhosted.org/packages/eb/71/7e4381d3c29b1e90605f3be571944fa2c67861fa954d0862b6464c0a02b8/hiredis-3.5.0-cp311-cp311-win32.whl", hash = "sha256:70fa3bef2838607e9fa911ce6d8cf256350d16057d249e2e4de2c877bc83fd09", size = 38744, upload-time = "2026-10-15T17:58:34.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/6f/100353601702c371ec7c2b846191216958ffb112a53cec7f58bd42955d32/hiredis-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:1453cbd66450be310fdf2c6eb2e1d89b7379ca0e396acc14345621eaf68d2be3", size = 40575, upload-time = "2026-10-15T17:58:35.466Z" }, + { url = "https://files.pythonhosted.org/packages/f8/54/a113debd9a6a21f6a425e9b2379fd10bf0eac7e9981a47868cac78e0d30d/hiredis-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:03f9b755123e10bf4c25f1ab67bd5223d461c63acb670c0bbe1588c4330d86d6", size = 38023, upload-time = "2026-10-15T17:58:36.473Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f0/af16bdd438ebe098da97bd4035047e673147e5dc36e9259671c0b5301d48/hiredis-3.5.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:10dff7baff891c8ec4174e0d228cad7deefaf827a90d89184aa695437557ce70", size = 140904, upload-time = "2026-10-15T17:58:37.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ef/f2324378e589d2ae146614a1da8f715a2b5e5c9d7f8ff93a0ef999521d98/hiredis-3.5.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2d1c87ba3303efdf259d119fa4ab28a79c8f2edae4b2d04cae7d44f75196864b", size = 75141, upload-time = "2026-10-15T17:58:38.794Z" }, + { url = "https://files.pythonhosted.org/packages/f6/86/368167b4a375473995db547b2b55d46ac4b0b6897bb0522c87946e02cd78/hiredis-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:328855d933ad0e25d731374ebb3fdbd18e9e748f8b7e04980ca6c720a59dbdbd", size = 72024, upload-time = "2026-10-15T17:58:39.746Z" }, + { url = "https://files.pythonhosted.org/packages/6c/36/8aa210342266d9b059a09cf7a360be498e7b7aa2cc53d8cc0a9bc0fdbc85/hiredis-3.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:231ec55363134d1036094b70b039cd2adef45d694deac635d437e9edfb5f2164", size = 308421, upload-time = "2026-10-15T17:58:40.862Z" }, + { url = "https://files.pythonhosted.org/packages/db/fa/33d1823b55fb67ac613ab60c566c18b78ef1159efa3f50d5fefc0e2d72ad/hiredis-3.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95343058583782388564cd0756089a13c827e34abe938720cba102e756df547f", size = 341662, upload-time = "2026-10-15T17:58:42.005Z" }, + { url = "https://files.pythonhosted.org/packages/f1/da/f5bfb59042382f942f42c09cc83cd4340dd0f42f7278c86d34c0776281e1/hiredis-3.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a8c6df1dd76b93e94197f7195f4035f7b45ebbd1c89a67887d4f9c19d74fdbe0", size = 353531, upload-time = "2026-10-15T17:58:43.206Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0b/861980a5ebbd00e812058c8838ff13ab16f6a807f394c549379d0f7cd641/hiredis-3.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a59975d62a1f7c55d49828fa3515824cc31993379a8f915cc20b5dd27e4c0d29", size = 314822, upload-time = "2026-10-15T17:58:44.322Z" }, + { url = "https://files.pythonhosted.org/packages/77/05/004ad0628f5df4b7dbf841ff817d879e6f55502571cff86de01a01055520/hiredis-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c043397db3bbb062d275dc3e7adf68544cd1ead1e2c9ab1bc30c616038b3ecd7", size = 302533, upload-time = "2026-10-15T17:58:45.532Z" }, + { url = "https://files.pythonhosted.org/packages/11/ab/da622bd27aa42a61d2b6726d6eaf820bc269f61bdea593b101ea3fd1d157/hiredis-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:879c774ac16276219d7f9013d28851a72338c703ddaf8509c20592537acedb25", size = 333799, upload-time = "2026-10-15T17:58:46.86Z" }, + { url = "https://files.pythonhosted.org/packages/da/d2/81fe508df01c14315a7bbbf61f7f3500799a8a1ca7950b35a10e876ee493/hiredis-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:55776bd0c298ddd4c07342e212fa50b3e753353c763b1b112a220ea99d2616a8", size = 334657, upload-time = "2026-10-15T17:58:48.114Z" }, + { url = "https://files.pythonhosted.org/packages/d4/01/886628d0b49d81a58b759b80a3594a3a9b4b5d8388b6cfe0e8facb55af9e/hiredis-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec5e911cfaa57cba61158839c0079bf9524571b4f03023c236bc86ac79a2c229", size = 313271, upload-time = "2026-10-15T17:58:49.438Z" }, + { url = "https://files.pythonhosted.org/packages/9e/27/2eeb6c6f21d3560dc72bb0f89012205eb1f4422d7f50e9358a85dd56a006/hiredis-3.5.0-cp312-cp312-win32.whl", hash = "sha256:cafaf4406d3a6bb1990b8437b94ed592dd81bbb5e99fed6f5290801cac93a8d6", size = 38738, upload-time = "2026-10-15T17:58:50.504Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/b802fc53a12845a69c880e3906d50f05f97bb8978c53bd2b8a070a5224d3/hiredis-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:601bfcb5655e85a79e2ded96ab0159cbac3a20ef991c468e8c9d2e374dc8cf54", size = 40605, upload-time = "2026-10-15T17:58:51.413Z" }, + { url = "https://files.pythonhosted.org/packages/9b/13/354336f3e5adfc97c21650a7be7b1e96df7b9736a7a701a52a90e523adf4/hiredis-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:fc5e619354f5ddc7381d244e0b00125180a7910a747ec3582bd4bc569da8e357", size = 38060, upload-time = "2026-10-15T17:58:52.388Z" }, + { url = "https://files.pythonhosted.org/packages/46/de/ec9f269bdc8fdfb3309aa7de184f7f19ce84399f8e76e9023a77c84f0b44/hiredis-3.5.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:eb9c5d20a593a19a5b9d3a5ce3ff7689adba2cd859cfb49a4aab53154210515f", size = 140915, upload-time = "2026-10-15T17:58:53.417Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8b/9d8b20f4ef669d20c7496786db3e44914c4d44255d49e8719c993ab9409e/hiredis-3.5.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:1286a8a950b6364ab5f87a173e41b39eaa090294fc120f5cbff9cfcf6d92fad2", size = 75143, upload-time = "2026-10-15T17:58:54.572Z" }, + { url = "https://files.pythonhosted.org/packages/67/c7/6af3f82b35339631d9f7f0d77fefd57c4cf20004f376441e1245ba07d9ae/hiredis-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:33f988f062da42e167d7887a4cc6df5e48727700d1c9966ee2e761db844a02e8", size = 72023, upload-time = "2026-10-15T17:58:55.649Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/35274f646f06a01f3c81faa3260478b0f09f330bd557a0d60180ee8d9d12/hiredis-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1170deb21494371005b4beb9451d904cf3adb2b8b62230b1c51f38267feedf3", size = 308391, upload-time = "2026-10-15T17:58:56.9Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/c47154c01e9e0ef1c22e29f61039686ed5df3e0f805c26a659dae451bb48/hiredis-3.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:808ca3e2bbaf4e82bc57508b3ac2a662a0317a1b12762b48d8bcaf31c631c966", size = 341603, upload-time = "2026-10-15T17:58:58.218Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ff/2a0c2e3334e31a048aaa9f5a073200c67fbfd5719f8b2e95d8407e6cde97/hiredis-3.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c107d375d5a5687297f99669617b2055e375df41f83ac976ac6aca2bc33c415e", size = 353483, upload-time = "2026-10-15T17:58:59.587Z" }, + { url = "https://files.pythonhosted.org/packages/0c/6d/ff4cb866c22e2a3b03e588d17976589683c9f4ebdbefe9c47e387abf3fc4/hiredis-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:588d083aace7439afa652e201df7388aeb1a5d3230ffe26fb297cfc6c7f216de", size = 314758, upload-time = "2026-10-15T17:59:00.893Z" }, + { url = "https://files.pythonhosted.org/packages/34/37/a650f8ac92f2a824dbe7fccc9264ce6875133a17b99920788b5f2c25fcf8/hiredis-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bd58ca4b34f7e86fe1de666073081973f26fc75fc7e2551d76b21f49a235855", size = 302581, upload-time = "2026-10-15T17:59:02.308Z" }, + { url = "https://files.pythonhosted.org/packages/22/1f/a883535a8ebd1f7f17754a1b05e7987c4b4f91ac70729c2c2cf0fd1a5b83/hiredis-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b116b8efa40a1789244129bc2dee9f8dc973d6a48d9331c919308034b27520da", size = 333923, upload-time = "2026-10-15T17:59:03.819Z" }, + { url = "https://files.pythonhosted.org/packages/de/ef/6f6521e8595d4dcfb658d10fb1d43c363a1e2fe06858e6790fc8241343ba/hiredis-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fbd9de72a958fcdc70633d0fbab3b8010555471c746f39ea48976a4da1680e4a", size = 334703, upload-time = "2026-10-15T17:59:05.142Z" }, + { url = "https://files.pythonhosted.org/packages/4b/40/1b5723e2464e6130e73de5ab91c558b43942ae3247c7ce9e75e8ac654d98/hiredis-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0037e3af5f457d47d29eb3dda70302758385670a3378be7daf8c5049403a172f", size = 313283, upload-time = "2026-10-15T17:59:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f9/b3885abe8a77c9ab74311eb824d224bd4ec5e3b53f947941a12555d0a57c/hiredis-3.5.0-cp313-cp313-win32.whl", hash = "sha256:5201151dfc4098f81485aeac651cbfb402c8aea92607a5ddef5bbff5097d10e6", size = 38743, upload-time = "2026-10-15T17:59:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/34/d9/a6bfa649cc1be25a3cbbc180a4fd77d91d57382a4f3335407cf705e4def1/hiredis-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:1ad14a185ece13a456dd8da27b7acd4105b4a2fccbdf54d86a3d455d6df7ffae", size = 40615, upload-time = "2026-10-15T17:59:08.584Z" }, + { url = "https://files.pythonhosted.org/packages/fa/95/fe6e3ada77b59c440ee212d00667336ecfc8957443a0384aefeb0ba0debf/hiredis-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:0f09667f00035ccfb83b942018b5e84dc560774372fb237b9f7f757d72603c37", size = 38070, upload-time = "2026-10-15T17:59:09.759Z" }, + { url = "https://files.pythonhosted.org/packages/34/d4/ec6c2ab107ffbb13311e0b086fc47a6ff7bfde4264c6d0208b8c9c4ec393/hiredis-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a9ef58e5a9c80a2159c25c6fe5f856734c4e80201b4cecd513f3bd9117d90417", size = 140958, upload-time = "2026-10-15T17:59:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/92/67/c1a29f6555ee51b2c8aa8cea78071e265e42ca6bd599c6623dd5ac0d20ba/hiredis-3.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7a47c7136637df55cc021038de2a37c7e8a1183e6331ea4874ce65ea3adf7bce", size = 75151, upload-time = "2026-10-15T17:59:11.91Z" }, + { url = "https://files.pythonhosted.org/packages/73/a9/2d098b3363717763f69e21fd04deba33795e3a6b7736d1f2e70330524207/hiredis-3.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:57f8112850e333a2efb12e8db1b7b0ac7585950a1e325b0c7693d98c361dc775", size = 72068, upload-time = "2026-10-15T17:59:12.961Z" }, + { url = "https://files.pythonhosted.org/packages/2e/bf/88b70c2e12efae647530b61bbb31729ed6097436851f590a6d579e8ec89a/hiredis-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3dc0b74b106f56a6129ea7fea856e5c4f0ada96f26b079a4c2ea7c6e1cf1a5a6", size = 308598, upload-time = "2026-10-15T17:59:14.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/99/c76d37b79b84891b1e747f1b6b3bec908ffc9f2962a64ceb533bbb10d07b/hiredis-3.5.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5001493d8ef9d699eb43bbc39a564796c2af4777cd1a198f0168814551596311", size = 341952, upload-time = "2026-10-15T17:59:15.487Z" }, + { url = "https://files.pythonhosted.org/packages/10/2c/2468faf3a073ccc0e03646cffcb9f517794da2e09aeec3ac547e2eef5aba/hiredis-3.5.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:60b9f1ab9d366f19d53da2c844fed58ea2e7ef3d394db95647ed87e07a2456ce", size = 353577, upload-time = "2026-10-15T17:59:16.816Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/d3a7b1bcb77ff5caac4e76a2eed61b47850233035d837d50d2cb029e4eb3/hiredis-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf61715d357c58996163ccedb972eec208a882b95bf04a90a95ef0486af5022e", size = 314739, upload-time = "2026-10-15T17:59:18.103Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f8/aa061e52d1f75672f1f7478b90dc69ad5a1873523e1cefab98d736014970/hiredis-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de694f45570bb9102904558a50e2ef82963413f37b0300c8f937f2f06d601ad1", size = 302798, upload-time = "2026-10-15T17:59:19.459Z" }, + { url = "https://files.pythonhosted.org/packages/d8/22/e05a1b19c8900465601b6a005a5fc92b5a7707d470f6997d49b268f2541b/hiredis-3.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:583de475ceaa052a8353a91d30c33b42a3267a4455550714935b52575a9d3211", size = 334070, upload-time = "2026-10-15T17:59:20.764Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2a/39002fea0ef3f1b14da719926eb5f8b36480a3c43d460a44e10eb14a38a3/hiredis-3.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1134ddc5f1b2a754adacc2c988485f9db0f5ae479b9a87472b9fdd4c4312a8f7", size = 334831, upload-time = "2026-10-15T17:59:22.011Z" }, + { url = "https://files.pythonhosted.org/packages/72/c2/5eb024e9a51c3fb35106e45f149f3284639d1c260af3fb15f8754c39698b/hiredis-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a890b8956729b366ef533249a1db8709714dbb02b6a292a40f1383a54cef0945", size = 313134, upload-time = "2026-10-15T17:59:23.342Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ae/043bfe0869b5ddd30c5d54f242ccf7e77aa5ba2274bb72c9544921159249/hiredis-3.5.0-cp314-cp314-win32.whl", hash = "sha256:2f1c2968ba739fccf94746247c10c869183530c99d0bdd5bddfaa28f0cbbe3fe", size = 39492, upload-time = "2026-10-15T17:59:24.429Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8e/9ede60bca300139eb74034b15c19f8046437c8763d4f65b3ff580805e5e8/hiredis-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:fcc7542689996df026486d75a29080e102c6b7131c064e22b762949f44d5e4c2", size = 41189, upload-time = "2026-10-15T17:59:25.447Z" }, + { url = "https://files.pythonhosted.org/packages/35/e1/8c1e7dec56d6735642001308846cfa423d0d7b34014358593f6c74f41c9e/hiredis-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:f714f562792f9777926d1cc0675445147b99ff2e3b951baf109d05195131eb10", size = 38773, upload-time = "2026-10-15T17:59:26.424Z" }, + { url = "https://files.pythonhosted.org/packages/a4/aa/f27ed6b41e97e1ce0c4a22f3489e26119ca11a95c3c8309514e250cca658/hiredis-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6bfdf75a934c7d73e660b9ebea3ade761f6c2c67b6725dc71e556fceb0f95194", size = 141946, upload-time = "2026-10-15T17:59:27.468Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ed/7bdbaade5f840901d76a7c9b61bce7018ddbde3bfe3c21d9ee51b9463e46/hiredis-3.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9d7f99fd94460f9bd83b042c9c5a05f746dbb55f116430b4daf93ee290246bfe", size = 75641, upload-time = "2026-10-15T17:59:28.891Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/e015902deb78dcf2decfb17ae4229fa18905d6aad5053977d4326d718def/hiredis-3.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:48634663d477efe8b8b76497e123d85d3ad1606cb919a833a4d447f00ff6a8c8", size = 72574, upload-time = "2026-10-15T17:59:30.226Z" }, + { url = "https://files.pythonhosted.org/packages/47/3c/5f615cf57771a10b76a8ab1aeb8302395efd6e05343fdb621416119b4858/hiredis-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1590dbc3fbb78715c9673db04c0ff37c0d1b97660740064146ae3ce68e604fb", size = 318075, upload-time = "2026-10-15T17:59:31.585Z" }, + { url = "https://files.pythonhosted.org/packages/77/96/b958bfb025d175f394e58829137967a58616d70f4c67273b6dcccf16e9ab/hiredis-3.5.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:185fdce9552b768ee6c10585c0358a8264d9fbc5e78bfd58f0a89fff0da44f44", size = 350457, upload-time = "2026-10-15T17:59:33.399Z" }, + { url = "https://files.pythonhosted.org/packages/67/2a/9da03e7e447f29f516660aec1b2624c4a498d6b7eba33c0f10f4dee6bd9b/hiredis-3.5.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6b2a3ae55fca9fe7e5ed23ec0310581dd5310d9c66b7420d821bf2d30fa3a4c6", size = 362348, upload-time = "2026-10-15T17:59:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/55/24/26e41a53ed053ca151a02a91e44474e65a1055b599228041fc1676d3c198/hiredis-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d0ccc663f871efb4ee52ad7ddc72780fb965df47caec38a2ef1995a9eada8b6", size = 323522, upload-time = "2026-10-15T17:59:36.352Z" }, + { url = "https://files.pythonhosted.org/packages/03/70/9452702a27316d48c62c943d5277d06e61107f63b9d2602a6b501250bbfa/hiredis-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dc892768e5fc87566919856cf5a5df189cb45a70c1eaa412c8ae8028202aa015", size = 311520, upload-time = "2026-10-15T17:59:37.633Z" }, + { url = "https://files.pythonhosted.org/packages/3d/02/83d3754a5f97c8e867b0ca2285dfcda4f7cca3dd3a3f955de20dc4c0b7f1/hiredis-3.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:7d217165dac546d7781b169c77573f8ef743eacca755ab44568e180b95ab44ae", size = 341899, upload-time = "2026-10-15T17:59:38.932Z" }, + { url = "https://files.pythonhosted.org/packages/ad/35/f6ea1b6bf97f73156ba492ec577c2f22a4af32fd6fc87c5352bc88de6be5/hiredis-3.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:7485db3f95ad46346ed702b99e380c2a61652bf5a8246dc101b8cc3f236dafd2", size = 343367, upload-time = "2026-10-15T17:59:40.179Z" }, + { url = "https://files.pythonhosted.org/packages/53/4e/bf80b882d945bba735d4f940892dfa2a46007b80cd96b9771d2cb1a1aebd/hiredis-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fca8f8974588f3563decb8812df201f08cd2ac1d4bb84fca295ff820cfbf0524", size = 322127, upload-time = "2026-10-15T17:59:41.45Z" }, + { url = "https://files.pythonhosted.org/packages/22/ac/79c9a3f5811a91ec84bc0c957ee090b6fc5e56d12c64e214f206a1711786/hiredis-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:ceb80b8c9bf8e818dbd934e1fa6eb8c002a82eaf9f323fa0e47da0b750d9e0de", size = 40077, upload-time = "2026-10-15T17:59:42.674Z" }, + { url = "https://files.pythonhosted.org/packages/71/b5/04761a2c2a110afda866cf48a1b64c11a43a427db817dcee749ae912e8ac/hiredis-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29812df61589038cc6d58e8349b95c1da56a6380820f93d5b41ffc9c7f4c5cb0", size = 41758, upload-time = "2026-10-15T17:59:43.716Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/a7519f202c2b2b5fe84e83e58147ed4b6eeae9d239e666de9f3c2076efe3/hiredis-3.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b4411b17db1e12fb9ff5e3f5b74292739887ad288039e98f8d804378b435abd1", size = 39224, upload-time = "2026-10-15T17:59:44.719Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c9/e37d1a4f57407da671ba4ea5e46824ab4c31f992e1cb93c1f5d786be7532/hiredis-3.5.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:8f2e4e3af78c3d8a8d800a1d252024185943ca210f1089daf20188526491b8a9", size = 141107, upload-time = "2026-10-15T17:59:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/89/cd/b3bb82dbf7825a817ec03d5ea1e79c70162059f9914e7a6231b62625bc43/hiredis-3.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:9132be543d677159d0e6c44bc86c887f2a07f47c163178aca02bc3c0851dfa50", size = 75152, upload-time = "2026-10-15T17:59:47.002Z" }, + { url = "https://files.pythonhosted.org/packages/c2/66/31a5c959ad64dd4c47373410d0414742532c88a639ebb8b749e401b85732/hiredis-3.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:931f585f15c1b165dbf6fcc781fb8975d36b99e6de5524485ab9c1164a3f0f91", size = 72221, upload-time = "2026-10-15T17:59:48.092Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d2/9850540d3601b484b88aa11fbcf9782241013644bfff4517f5483776fee1/hiredis-3.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92ee5f3fde72816784f12bfec4278a56f158e5a622a353663980b1f00c4eee64", size = 309583, upload-time = "2026-10-15T17:59:49.266Z" }, + { url = "https://files.pythonhosted.org/packages/31/88/905ff269ed52ba7a3ae470816321ff2162e657a88506f7a27bd45a22d5ea/hiredis-3.5.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f10b854dc1894b5a664b66508f0a0e3e935cf6560481ffac378d12138647fd32", size = 342180, upload-time = "2026-10-15T17:59:50.624Z" }, + { url = "https://files.pythonhosted.org/packages/fe/14/d1289646a82d34fe758c4487c53464d65ec99e927ed7df7b9b5130199d8f/hiredis-3.5.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d4f3360d8254ff3b58854705f70b01bf72ca550a4943ac76ef0f569239f05970", size = 353744, upload-time = "2026-10-15T17:59:52.101Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/3cf99726f5a5ef7c7402e024b7ff9b9105d5f0f648d325cbc7313f160f9e/hiredis-3.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86bca28fd6f7f9cb9577a6d4ae38bf55104c0da8ff0fdcca0f5336b6fafd4629", size = 315976, upload-time = "2026-10-15T17:59:53.45Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1d/21c8b325c254f8cb1286c6b23f045c646df386e6ed621b4904618c5b5814/hiredis-3.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:4fde6c6d67154c7ff84fd73c7aa9c5c03b9ea6ca0202fda5bd0c79ab99e51171", size = 303921, upload-time = "2026-10-15T17:59:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/52/f9/7d3681c29c0c18066d99abdf7124fde00ba1010ec9973d4fdb1d815a0d66/hiredis-3.5.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:dd328ff8bc665f82a42b1f9561789f72ebb90147dbd3413c0990fc8837f7cc8d", size = 334358, upload-time = "2026-10-15T17:59:56.398Z" }, + { url = "https://files.pythonhosted.org/packages/ee/22/fd572b5391ca8fba3b80fd8d06b95c4c85dbd9815cb3caa374e0f87a746b/hiredis-3.5.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:c73535d109c0d1f4a95f40dfb667c8baa722acc84187d128551706e8229d7223", size = 335369, upload-time = "2026-10-15T17:59:57.654Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e9/a2aff206d2c95fa354a675ed27c0b168a1e20ae349c4d3fd1de2351bb098/hiredis-3.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c5e8ea62c81282bb845a96460158190259a17a82124d876bb2291d01006fe83e", size = 314003, upload-time = "2026-10-15T17:59:59.026Z" }, + { url = "https://files.pythonhosted.org/packages/15/e9/4289d3b9d1b27cfec1ad82c6011748d039ad6d98757677b3e8c92c8e98dd/hiredis-3.5.0-cp315-cp315-win32.whl", hash = "sha256:ddc4eb264753d7f8d4b68cc164b509824d0d6c84119f896e209c9a679df3b386", size = 39503, upload-time = "2026-10-15T18:00:00.179Z" }, + { url = "https://files.pythonhosted.org/packages/3a/fb/ad69fea7e3a3f4eacd17a1de5197dc27b4181b634e594dc494a57c427828/hiredis-3.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:ef139e9d52843cc91ed3aa1659544ba10b19dfe48e30dac3b21e1f9a14640a22", size = 41185, upload-time = "2026-10-15T18:00:01.753Z" }, + { url = "https://files.pythonhosted.org/packages/74/e1/7d50ae62140d49e41df72de836ef0e22002daeae9119abd385600d906639/hiredis-3.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:392506b5ad5d926b764d6a5663a3833a0110fd9256ff86ac31f53ccd4971941c", size = 38785, upload-time = "2026-10-15T18:00:03.084Z" }, + { url = "https://files.pythonhosted.org/packages/29/7f/fae64ab5a59f8cce5fa3396cd73e88c0ab52488c2c4caf6c93c773776b44/hiredis-3.5.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:942771ff3d4121b242c8f3c55ba2c28bae06af426ad459b751bbc905c3c6e944", size = 142058, upload-time = "2026-10-15T18:00:04.278Z" }, + { url = "https://files.pythonhosted.org/packages/d2/70/3f3476ae5b511ef15d34458878808d2af94b311192ba0a2a1c2c9f09f982/hiredis-3.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3bc5378fe7a1a8226a42d31f05f6a553ac471dd495f38dbd3967a38794b4acc0", size = 75647, upload-time = "2026-10-15T18:00:05.591Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f5/27038d5e40be831dfb44fe8c7043b2f312010616608cfff43fe076bfb168/hiredis-3.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:43a7b471185ee560ac8b4070b6d7d0b022e298749e4bd0dc95e11321701c9de0", size = 72702, upload-time = "2026-10-15T18:00:06.897Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d6/f13fc5f3ad7f9718bf39383f0f640503442469f6933ad6caad369f64cc50/hiredis-3.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54733f0088369b834677dbb5368e2a4bd16fadfaa43882de4dc13e2466a00b8b", size = 318729, upload-time = "2026-10-15T18:00:08.68Z" }, + { url = "https://files.pythonhosted.org/packages/f4/27/12f54ff4a88dbb2c00f03c0b3d71ace167e6a05704323bc36da0774a8ab2/hiredis-3.5.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd26a99b1f703c870a9ce0a96615bd2530fcf04c1ba5dedca100e32bbf38d67e", size = 350641, upload-time = "2026-10-15T18:00:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/58/e4/737710596fcaf9cfeae44746717fdc3cc04f69ed7846b2377a8388786e97/hiredis-3.5.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0dc22d8c05468603e8fdd821189bd83e657f180b1af88720b51bdc71448f4346", size = 362164, upload-time = "2026-10-15T18:00:11.882Z" }, + { url = "https://files.pythonhosted.org/packages/44/73/18f43a29db4e632401c0f0a1a1a323280fbed18ed60d7ffda4054e8db616/hiredis-3.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae897c05c38267e550acc085cd45f1fa2d0f793abe2ab334f38e18b4e8c5c39d", size = 324653, upload-time = "2026-10-15T18:00:13.415Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5c/b12b06a29879857ac0ac5a62f54f466acc3c5400f9ca89326045db4b8c0f/hiredis-3.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:18a8bbb37efb39da559ea7ab1619a56bc051c4490506795d8686f56ca1063224", size = 312130, upload-time = "2026-10-15T18:00:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/711e69e532b856d3d1aa935667bf96233e5e9f4c21016e63a14edbcd3884/hiredis-3.5.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:5556c12e6c62cb96162135594876e48db98ae3951afac8ef38831ac779d7bd01", size = 342134, upload-time = "2026-10-15T18:00:16.651Z" }, + { url = "https://files.pythonhosted.org/packages/39/03/32044910ffdb441c97a2c7dd4f9a920a28f1921ae5ffb1893e1c22c276bb/hiredis-3.5.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:f6335c0d65104fd2f62fdff813067e72fdbcd4c9924066921ab931ef82591730", size = 343637, upload-time = "2026-10-15T18:00:18.164Z" }, + { url = "https://files.pythonhosted.org/packages/d1/8c/02d04f2e4a0339145e4fe00407d2c34fa29c4c1ae6cd3feab46c87fc25d4/hiredis-3.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:894391ee4f5465fa1f3e6a0a323c76bb7884eda2698a40f8157d6a96fa268899", size = 322470, upload-time = "2026-10-15T18:00:20.053Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a5/b9d5125ca54e338cae8342f94d937ea8e10b4f8c450f239248c102a7f4ce/hiredis-3.5.0-cp315-cp315t-win32.whl", hash = "sha256:c0b902612627b307dbab51ea556f82b61763fe42d43026e81060ca7ac8642df3", size = 40086, upload-time = "2026-10-15T18:00:21.484Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d3/29a674417766263387038d670137a34f213fdd1030acbb44036c5ce1e882/hiredis-3.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:1dd0db7d759ea43f0c37c6d841b8e9ea652bdec5e4f542872f773eef3cfcbfe4", size = 41769, upload-time = "2026-10-15T18:00:22.658Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/ed21d49c8db2460b94bd63e45505c75667231d0dc653d80eb6cf4255cb41/hiredis-3.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:c360575e546aa093b3bff6634e8c916cd5b52ca766f51b49474acda219dd3c88", size = 39201, upload-time = "2026-10-15T18:00:23.789Z" }, +] + [[package]] name = "hpack" version = "4.2.0" @@ -3483,7 +3721,7 @@ dependencies = [ { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" }, { name = "idna" }, { name = "truststore", marker = "sys_platform != 'emscripten'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/a0/e9deef4654132857b5a5dbe4eddd0ac59c2814500e11f2f5044cd81103ee/httpx2-2.13.0.tar.gz", hash = "sha256:81bd07dc67a3701729ef1f777a3c00c915d4539604fdb5afd327f8682f6b7b44", size = 100290, upload-time = "2026-09-14T14:18:05.486Z" } wheels = [ @@ -3522,7 +3760,8 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tqdm" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fe/0f/e83fdd856da8fca26bf78d71709ebd120432a0ce535e72b9597cab1eb5bf/huggingface_hub-1.32.0.tar.gz", hash = "sha256:ed70a45498abe86039df7c2f4e5f7575de524be908d3840e8f828d5525eafd6a", size = 1038662, upload-time = "2026-09-17T10:27:48.049Z" } wheels = [ @@ -3552,7 +3791,7 @@ dependencies = [ { name = "priority" }, { name = "taskgroup", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "wsproto" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/3a/df6c27642e0dcb7aff688ca4be982f0fb5d89f2afd3096dc75347c16140f/hypercorn-0.17.3.tar.gz", hash = "sha256:1b37802ee3ac52d2d85270700d565787ab16cf19e1462ccfa9f089ca17574165", size = 44409, upload-time = "2024-05-28T20:55:53.06Z" } @@ -4072,7 +4311,8 @@ dependencies = [ { name = "attrs" }, { name = "jsonschema-specifications" }, { name = "referencing" }, - { name = "rpds-py" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -4255,7 +4495,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/7c/651d0dc4913a7a892156c03dd343b99cfe19ee729e6911ab1f4fe7567b8b/langchain-1.3.9.tar.gz", hash = "sha256:9b14ef0db9ef314299ded858b22ca2a40b8f1b05c8c9cb6b82d53a53075fef00", size = 631514, upload-time = "2026-06-12T16:53:27.083Z" } wheels = [ @@ -4270,7 +4511,8 @@ dependencies = [ { name = "langchain-core" }, { name = "langchain-text-splitters" }, { name = "langsmith" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlalchemy" }, @@ -4311,7 +4553,8 @@ name = "langchain-community" version = "0.4.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -4345,10 +4588,12 @@ dependencies = [ { name = "langchain-protocol" }, { name = "langsmith" }, { name = "packaging" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pyyaml" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "uuid-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" } @@ -4375,7 +4620,8 @@ name = "langchain-protocol" version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } wheels = [ @@ -4405,8 +4651,10 @@ dependencies = [ { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-sdk" }, { name = "packaging" }, - { name = "pydantic" }, - { name = "typing-extensions" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/97/30/6a64dcf84de2f2eb4d03adbfd22cc7bdc95ce67e3e56cd6288405087fb8a/langfuse-4.15.2.tar.gz", hash = "sha256:7f818f38cc22daba88fdcec62d2addcee4e18d1af4529978b6d07501e86b6946", size = 391727, upload-time = "2026-09-09T16:01:25.73Z" } @@ -4423,7 +4671,8 @@ dependencies = [ { name = "langgraph-checkpoint" }, { name = "langgraph-prebuilt" }, { name = "langgraph-sdk" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "xxhash" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/7a/ea09b05bb0cbddfa43bd34fc581357e87fc3f21a751cc0d419688c3106da/langgraph-1.2.6.tar.gz", hash = "sha256:f9b45a34f13930c94d96cdb76277447ad2cc70ec2d18cd2764d7fdadb36cdc1b", size = 714400, upload-time = "2026-06-18T20:58:21.514Z" } @@ -4481,7 +4730,8 @@ dependencies = [ { name = "httpx" }, { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, { name = "packaging" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "requests" }, { name = "requests-toolbelt" }, { name = "uuid-utils" }, @@ -4636,6 +4886,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" }, { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" }, { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" }, + { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" }, + { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" }, + { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" }, + { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" }, + { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" }, + { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" }, + { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" }, + { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" }, + { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" }, + { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" }, + { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" }, + { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" }, + { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, ] [[package]] @@ -4649,7 +4930,7 @@ dependencies = [ { name = "boto3" }, { name = "certifi" }, { name = "click" }, - { name = "fastuuid" }, + { name = "fastuuid", marker = "python_full_version < '3.15'" }, { name = "filelock" }, { name = "httpx", extra = ["http2"] }, { name = "huggingface-hub" }, @@ -4658,13 +4939,15 @@ dependencies = [ { name = "jsonschema" }, { name = "openai" }, { name = "packaging" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "pyyaml" }, { name = "tiktoken" }, { name = "tokenizers" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] [package.optional-dependencies] @@ -4703,7 +4986,8 @@ grpc = [ mcp = [ { name = "httpx2" }, { name = "mcp" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] mlflow = [ { name = "mlflow" }, @@ -4721,7 +5005,8 @@ proxy = [ { name = "fastapi-sso" }, { name = "granian" }, { name = "gunicorn" }, - { name = "hiredis" }, + { name = "hiredis", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "hiredis", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "httpx2" }, { name = "inquirerpy" }, { name = "litellm-enterprise" }, @@ -4729,7 +5014,8 @@ proxy = [ { name = "mcp" }, { name = "orjson" }, { name = "polars" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pyjwt" }, { name = "pynacl" }, { name = "pyroscope-io", marker = "sys_platform != 'win32'" }, @@ -4927,7 +5213,7 @@ requires-dist = [ { name = "expression", marker = "extra == 'proxy'", specifier = ">=5.6.0,<6.0" }, { name = "fastapi", marker = "extra == 'proxy'", specifier = ">=0.136.3,<1.0" }, { name = "fastapi-sso", marker = "extra == 'proxy'", specifier = ">=0.19.0,<1.0" }, - { name = "fastuuid", specifier = ">=0.14.0,<1.0" }, + { name = "fastuuid", marker = "python_full_version < '3.15'", specifier = ">=0.14.0,<1.0" }, { name = "filelock", specifier = ">=3.16.1,<4.0" }, { name = "google-cloud-aiplatform", marker = "extra == 'google'", specifier = ">=1.133.0,<2.0" }, { name = "google-cloud-aiplatform", marker = "extra == 'proxy-runtime'", specifier = ">=1.133.0,<2.0" }, @@ -4936,11 +5222,13 @@ requires-dist = [ { name = "google-cloud-speech", marker = "extra == 'proxy-runtime'", specifier = ">=2.40.0,<3.0" }, { name = "google-cloud-speech", marker = "extra == 'stt-vertex-chirp'", specifier = ">=2.40.0,<3.0" }, { name = "google-genai", marker = "extra == 'proxy-runtime'", specifier = ">=1.37.0,<2.0" }, - { name = "granian", marker = "extra == 'proxy'", specifier = ">=2.7.4,<3.0" }, + { name = "granian", marker = "python_full_version >= '3.15' and extra == 'proxy'", specifier = ">=2.8.0,<3.0" }, + { name = "granian", marker = "python_full_version < '3.15' and extra == 'proxy'", specifier = ">=2.7.4,<3.0" }, { name = "grpcio", marker = "extra == 'grpc'", specifier = "==1.84.1" }, { name = "grpcio", marker = "extra == 'proxy-runtime'", specifier = "==1.84.1" }, { name = "gunicorn", marker = "extra == 'proxy'", specifier = ">=23.0.0,<24.0" }, - { name = "hiredis", marker = "extra == 'proxy'", specifier = ">=3.0.0,<4.0" }, + { name = "hiredis", marker = "python_full_version >= '3.15' and extra == 'proxy'", specifier = ">=3.5.0,<4.0" }, + { name = "hiredis", marker = "python_full_version < '3.15' and extra == 'proxy'", specifier = ">=3.0.0,<4.0" }, { name = "httpx", extras = ["http2"], specifier = ">=0.28.0,<1.0" }, { name = "httpx2", marker = "extra == 'mcp'", specifier = ">=2.10.0,<3" }, { name = "httpx2", marker = "extra == 'proxy'", specifier = ">=2.10.0,<3" }, @@ -4967,7 +5255,8 @@ requires-dist = [ { name = "opentelemetry-exporter-otlp", marker = "extra == 'proxy-runtime'", specifier = "==1.45.1" }, { name = "opentelemetry-instrumentation-fastapi", marker = "extra == 'proxy-runtime'", specifier = "==0.66b1" }, { name = "opentelemetry-sdk", marker = "extra == 'proxy-runtime'", specifier = "==1.45.1" }, - { name = "orjson", marker = "extra == 'proxy'", specifier = ">=3.11.6,<4.0" }, + { name = "orjson", marker = "python_full_version >= '3.15' and extra == 'proxy'", specifier = ">=3.12.0,<4.0" }, + { name = "orjson", marker = "python_full_version < '3.15' and extra == 'proxy'", specifier = ">=3.11.6,<4.0" }, { name = "packaging", specifier = ">=24.0" }, { name = "polars", marker = "extra == 'proxy'", specifier = ">=1.38.1,<2.0" }, { name = "prisma", marker = "extra == 'extra-proxy'", specifier = ">=0.11.0,<1.0" }, @@ -4975,7 +5264,8 @@ requires-dist = [ { name = "psycopg", marker = "extra == 'extra-proxy'", specifier = ">=3.2,<4.0" }, { name = "psycopg-binary", marker = "extra == 'extra-proxy'", specifier = ">=3.2,<4.0" }, { name = "pydantic", marker = "python_full_version < '3.14'", specifier = ">=2.11.0,<3.0.0" }, - { name = "pydantic", marker = "python_full_version >= '3.14'", specifier = ">=2.12.0,<3.0.0" }, + { name = "pydantic", marker = "python_full_version == '3.14.*'", specifier = ">=2.12.0,<3.0.0" }, + { name = "pydantic", marker = "python_full_version >= '3.15'", specifier = ">=2.14.0,<3.0.0" }, { name = "pydantic", marker = "extra == 'mcp'", specifier = ">=2.12.0,<3" }, { name = "pydantic", marker = "extra == 'proxy'", specifier = ">=2.12.0,<3" }, { name = "pydantic-settings", specifier = ">=2.14.1,<3.0" }, @@ -5002,7 +5292,8 @@ requires-dist = [ { name = "soundfile", marker = "extra == 'stt-nvidia-riva'", specifier = ">=0.12.1" }, { name = "starlette", marker = "extra == 'proxy'", specifier = ">=1.0.1,<2.0" }, { name = "tiktoken", marker = "python_full_version < '3.14'", specifier = ">=0.8.0,<1.0" }, - { name = "tiktoken", marker = "python_full_version >= '3.14'", specifier = ">=0.12.0,<1.0" }, + { name = "tiktoken", marker = "python_full_version == '3.14.*'", specifier = ">=0.12.0,<1.0" }, + { name = "tiktoken", marker = "python_full_version >= '3.15'", specifier = ">=0.14.0,<1.0" }, { name = "tokenizers", specifier = ">=0.21.0,<1.0" }, { name = "tomlkit", marker = "extra == 'cli'", specifier = ">=0.13.3,<1.0" }, { name = "tomlkit", marker = "extra == 'proxy'", specifier = ">=0.13.3,<1.0" }, @@ -5069,8 +5360,8 @@ dev = [ { name = "opentelemetry-instrumentation-fastapi", specifier = "==0.66b1" }, { name = "opentelemetry-sdk", specifier = "==1.45.1" }, { name = "parameterized", specifier = "==0.9.0" }, - { name = "psycopg", specifier = "==3.3.3" }, - { name = "psycopg-binary", specifier = "==3.3.3" }, + { name = "psycopg", specifier = "==3.3.6" }, + { name = "psycopg-binary", specifier = "==3.3.6" }, { name = "pytest", specifier = "==9.0.3" }, { name = "pytest-asyncio", specifier = "==1.3.0" }, { name = "pytest-cov", specifier = "==5.0.0" }, @@ -5169,7 +5460,8 @@ name = "llm-sandbox" version = "0.3.39" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/f1/30b41350f105dbd794639565a1f6e4ab28f95940fc2907cb721471dca105/llm_sandbox-0.3.39.tar.gz", hash = "sha256:7452317a054df3ac20fb652a2e100b5697624886282874fecfb738bac21b27bb", size = 611034, upload-time = "2026-04-20T16:59:45.133Z" } wheels = [ @@ -5196,7 +5488,7 @@ dependencies = [ { name = "pyzmq" }, { name = "requests" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "werkzeug" }, ] sdist = { url = "https://files.pythonhosted.org/packages/03/4e/62c3fee4af4720aceac2c102a6a1d69707b7b28e83fc44b82581e86155d7/locust-2.45.0.tar.gz", hash = "sha256:9fa840a4ef8c2624d7719f6e642bce3e19079aa4eea755c99c5282087903e13a", size = 1477628, upload-time = "2026-07-09T08:20:46.931Z" } @@ -5219,7 +5511,8 @@ dependencies = [ { name = "requests" }, { name = "rich" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/5a/e6cccc98d30358b8beee47c61c2a1ca66ac7a76a07de9ba333aa5219b9a6/logfire-5.2.0.tar.gz", hash = "sha256:5fb9ddda303759766e86f5704a1ae96f3a0072906d52e21fbaad2e3c3b03f823", size = 1404990, upload-time = "2026-10-13T17:36:30.746Z" } wheels = [ @@ -5239,7 +5532,7 @@ dependencies = [ { name = "jsonpickle" }, { name = "langchain-community", version = "0.3.31", source = { registry = "https://pypi.org/simple" } }, { name = "packaging" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" } }, { name = "pyhumps" }, { name = "requests" }, { name = "setuptools" }, @@ -5255,7 +5548,8 @@ name = "lunary" version = "1.4.37" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -5266,7 +5560,8 @@ dependencies = [ { name = "jsonpickle" }, { name = "langchain-community", version = "0.4.1", source = { registry = "https://pypi.org/simple" } }, { name = "packaging" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pyhumps" }, { name = "requests" }, { name = "setuptools" }, @@ -5412,7 +5707,8 @@ name = "mangum" version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a6/3d/c9664d56ad6b32d7c7ebf82a77cacc290ebed033f96adf0c98a9114ace4e/mangum-0.17.0.tar.gz", hash = "sha256:5b4e26375e12eed051687670466d17968f8b74beecaca432edd4eb4127f78509", size = 14222, upload-time = "2022-11-27T11:21:42.084Z" } wheels = [ @@ -5623,14 +5919,17 @@ dependencies = [ { name = "jsonschema" }, { name = "mcp-types" }, { name = "opentelemetry-api" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "pyjwt", extra = ["crypto"] }, { name = "python-multipart" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-inspection", version = "0.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/8d/e0d339616f4810e9051d4aba6887afab289ab1f81875fe908b606cdfd0e3/mcp-2.3.0.tar.gz", hash = "sha256:8b147a50441cf059dc88c684e0aeed3687f0aa0f39c6cde7b90330effd2b34d8", size = 4171955, upload-time = "2026-10-02T22:06:56.091Z" } @@ -5643,8 +5942,10 @@ name = "mcp-types" version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic" }, - { name = "typing-extensions" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/2d/7c251e34207f6c51000312fc8839111ac45cfe02023f90b44e7f1051dd8e/mcp_types-2.3.0.tar.gz", hash = "sha256:d1e46549edb35ee19a94940fcee6d1addd7e589ab7ea92dda83f5d84781fc362", size = 65898, upload-time = "2026-10-02T22:06:57.863Z" } wheels = [ @@ -5736,13 +6037,15 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "protobuf" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "python-dotenv" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlparse" }, { name = "starlette" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "uvicorn" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/b0/efbc07a721df135c681dd9f29ecb44b42e9664125f82a41168bc38240ed8/mlflow_skinny-3.17.2.tar.gz", hash = "sha256:5f2a0812d019a8f517a58f1377922f75af4f42832be30c8f1441005b51288596", size = 3488534, upload-time = "2026-10-21T02:07:10.993Z" } @@ -5762,7 +6065,8 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "protobuf" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/af/81/bf76610e755643d454fe1eb52fd6aae99d2e7d472a4a4cd119fa11a7a653/mlflow_tracing-3.17.2.tar.gz", hash = "sha256:35da9f6cc450feae7173941d0d119391f6fe3a41973c0a28ae4a021e6e285c99", size = 1688723, upload-time = "2026-10-21T02:07:48.333Z" } wheels = [ @@ -5891,7 +6195,7 @@ name = "multidict" version = "6.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d6/99/1d4d69c3512d0ddbfa3a1b69cfd9a151012ab2eb4eabbb096201b1f0b7d8/multidict-6.9.1.tar.gz", hash = "sha256:0f06e60fa190aa7abd0914c2a766736fdc8e9f34878c4346338534b73d1b20e2", size = 182404, upload-time = "2026-09-21T17:59:05.362Z" } wheels = [ @@ -6021,6 +6325,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/f2/f0ff3384a756227741c72f4ee94cadf1f9900df0ae961e48639f96115b1c/multidict-6.9.1-cp314-cp314t-win32.whl", hash = "sha256:35ba0263bae5dd3ad5aad767cc9afc01a8598c7dae30f1b3b2de98b1b32c28bd", size = 57776, upload-time = "2026-09-21T17:57:32.835Z" }, { url = "https://files.pythonhosted.org/packages/8d/87/013e1eed30ad46bec1eb68947bc9caf55ba2fd0d6aed04fe964e3b44c23b/multidict-6.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:7c8d5882ba25ac0282258be435d8a05aa0cbcacfce15799154a338f847f159b9", size = 64881, upload-time = "2026-09-21T17:57:34.489Z" }, { url = "https://files.pythonhosted.org/packages/7f/95/64dd049bb4c53426e2fee7afb874daaa8b4426c41878785883a94511ecf8/multidict-6.9.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ac348379cf4de5538a0a213be1532d289aa801ec5d267c5909446b9ea2f8e2c3", size = 58582, upload-time = "2026-09-21T17:57:36.19Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2c/26bc1723592cfe965b575652a7749026474b0625b04b04564e1fba265dc5/multidict-6.9.1-cp315-cp315-android_24_x86_64.whl", hash = "sha256:f04551dce5a7db8c9659f2e4245494c182d0663b83661803e08d46bfcae5eda1", size = 62339, upload-time = "2026-09-21T17:57:37.967Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c5/614d2eb9995232d066aae7f17d1d454060b6b5d882b22fbaf6150b05ef1d/multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c0c88085affd35c33e124e36930c5ad96aff9294ce195eaa0fd9cec962b64a82", size = 55009, upload-time = "2026-09-21T17:57:39.746Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/f7179ecdf9cdd3581ace3292c3b883a1a500a374aa4e619bf6eb521c62e5/multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a8b75dd3d3638d9a19f23e84af4ffab3b8940422c0df2da2a77005ef5aa3d7ea", size = 56273, upload-time = "2026-09-21T17:57:41.544Z" }, + { url = "https://files.pythonhosted.org/packages/ac/72/c9dffcecccd1f8b9a58f960ec4eaecab62c4edb08d5bb984d4de67fe3fdb/multidict-6.9.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:006c4478de0a1876f4834e14255776286f09b9846b505fe63f67f9d173a9487c", size = 96390, upload-time = "2026-09-21T17:57:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3f/76542360e655cecdb7c158c7af88c69c4db0d2aac3421659d41214927229/multidict-6.9.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2784090c30a586d5b45197bd9c32f87fb927216cde302f6cfd76d76577e90f08", size = 58900, upload-time = "2026-09-21T17:57:45.105Z" }, + { url = "https://files.pythonhosted.org/packages/04/15/f28c71af461dd4ff15653a165cfd8ddaf01f3387ff80d2dd0de057d527d0/multidict-6.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:7c708566da8014b120a64b1eb6d200c6c0c8cb36296383723cdb6fc82038270b", size = 56540, upload-time = "2026-09-21T17:57:46.913Z" }, + { url = "https://files.pythonhosted.org/packages/19/de/5ee4050c3ddad88c605a89fc1cd18e89abd40a64f183e6fb5276b6eb7f52/multidict-6.9.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:042fb0196047e786936a730bd302de83143950da45f2c16078da8f35e1cf7919", size = 331818, upload-time = "2026-09-21T17:57:48.825Z" }, + { url = "https://files.pythonhosted.org/packages/70/e2/2d4e3c3ee70f583334ff236fe6a3babf7a05ce56169a15df65453af647c0/multidict-6.9.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2212a0c842c723d919ea4a22a9296cb6b244b386e4e6ea92adfc7fbf3095519", size = 329800, upload-time = "2026-09-21T17:57:50.91Z" }, + { url = "https://files.pythonhosted.org/packages/d6/14/f64bd05667343a7250cdd99f81bda2270e3959f00832441f10c7f904e1c1/multidict-6.9.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd882aa29bf402b62bf1fd7c19fd5df4b6528cf468a908864b39368572b662a9", size = 312048, upload-time = "2026-09-21T17:57:53.053Z" }, + { url = "https://files.pythonhosted.org/packages/64/bf/a91256ce00199e7544e3eda79c4e9544eb7388c38fa22f11638c2e357ccb/multidict-6.9.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c57541034d12b215ab0a2bfa371d1a8a198da18176d0426c27105b9161a6862d", size = 343676, upload-time = "2026-09-21T17:57:55.069Z" }, + { url = "https://files.pythonhosted.org/packages/14/48/628b978413518159228c2b2ec38645362ceb6427e935d83d716e26490a49/multidict-6.9.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d73fed4e37158ff00cd271871170b79e40138db51e625fd352fa17c6acb34f67", size = 347028, upload-time = "2026-09-21T17:57:57.157Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/5f9dc2b5ac7dd7a82458ae9cd283b99c185834f23442ace295362dcc5e45/multidict-6.9.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:79da2491348b30810728050b4a8ec0416f85884125c2fd44655b3d01150d9c3e", size = 335670, upload-time = "2026-09-21T17:57:59.126Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/6f319ee000f5ab3fa0055b194ad706fddd552c60c76a21d581c87db1d46c/multidict-6.9.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b4da208a63434d21a3df64d29758e650fc4aa8cb05848554b76949c296539cca", size = 307804, upload-time = "2026-09-21T17:58:01.2Z" }, + { url = "https://files.pythonhosted.org/packages/3f/24/61be5fe1616d033532e8d63dc789c0f35b9793917834713eefe050589ba9/multidict-6.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:877ca17fdcfdf5c397493a71e5ff97a87bb181417fe717fdadc77c08c09301ac", size = 323409, upload-time = "2026-09-21T17:58:03.383Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/eab8aaf59892169a0f4e8e78a5516a6caa6abd8e96e6410111258f4ef0ef/multidict-6.9.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:40aec5299e1ed71fbb988389059da381c3c1a60e0c649acceb2a35d9b128848e", size = 315746, upload-time = "2026-09-21T17:58:05.372Z" }, + { url = "https://files.pythonhosted.org/packages/42/b1/d1e14bab80a74babf6e2ac971a878b84fdcf3a972f3ec3400ca12260fac5/multidict-6.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:4a409ccc42aefec904038695d5d7fd6d8f3af2721b7b6401d55135ec7d6d298e", size = 334529, upload-time = "2026-09-21T17:58:07.35Z" }, + { url = "https://files.pythonhosted.org/packages/f5/50/a05b1d36f6d3363a5a334cebbf11624c9c7a69d66fd23345eb53b1d43b7e/multidict-6.9.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0a5769559e3312dd96731fbe15b4abb6033368ac1cad5a98dadd21946a4c7d6c", size = 336455, upload-time = "2026-09-21T17:58:09.112Z" }, + { url = "https://files.pythonhosted.org/packages/cd/0d/bf33cb097380252b08e1177b5ae0cab822c47ce80d236f3f724953c76721/multidict-6.9.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:03fac50ddfd8302175b77863a015eccfae76767cda5506eef86df559ba861e1f", size = 305865, upload-time = "2026-09-21T17:58:10.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ee/a2bd133391204b1cec5a5caf327dc9a06710439b7403220860a8fb164560/multidict-6.9.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:5ecace251ccfa705bf3d7d35c5032cf750f5c629809740405697bce5c118c4a4", size = 337531, upload-time = "2026-09-21T17:58:12.846Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/c0972486f81a06c3bbb23063c85afaac53c51608f7e35bcb101a1006408b/multidict-6.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ecc89dbd4155b2f8a47f4bbd89242a35ed15e8e0ec581cab5ac65fa38407329d", size = 332464, upload-time = "2026-09-21T17:58:14.673Z" }, + { url = "https://files.pythonhosted.org/packages/69/4f/5cbbe852b45d8a7f0ab3ed042386e4894e463010eb230603596cb15d0fee/multidict-6.9.1-cp315-cp315-win32.whl", hash = "sha256:a3ffe881246d28a862f1985824f484cb7361f44d6b99c4c620436ef56462f38d", size = 51714, upload-time = "2026-09-21T17:58:16.409Z" }, + { url = "https://files.pythonhosted.org/packages/d4/77/1fa118386deeae4e7a48870fa5cb073e748b0ac5265a6990651cefc48068/multidict-6.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:59c123d0e948d760a5f930f316cfefa07e8d632ab84327c0693ee6a88171154f", size = 58619, upload-time = "2026-09-21T17:58:18.369Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ee/51c45748acfce9cb69d89dcfcd09c874b385407fd6993e885d6905659865/multidict-6.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:31199204b3ced121ff5407a2c342326a5d27e3870abbf94bd80dbd2451b7bc8f", size = 53969, upload-time = "2026-09-21T17:58:20.102Z" }, + { url = "https://files.pythonhosted.org/packages/aa/57/89d2d3dedfae2558853c1770c7cbcdfd5211eb4c4090b1e8cb570544a490/multidict-6.9.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:8879510a76940670517ea1cb589978da44b86e286ec3e50d664ef330817afce7", size = 107679, upload-time = "2026-09-21T17:58:21.98Z" }, + { url = "https://files.pythonhosted.org/packages/c8/df/0c2a28870181c1762f90952b897f6db46c934ce073efe0442604cbc276db/multidict-6.9.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:aec65b53a07f580606593f877eefbb29a45939bfc0d3fe6e6d9f42b41b749f68", size = 64753, upload-time = "2026-09-21T17:58:23.622Z" }, + { url = "https://files.pythonhosted.org/packages/43/7e/9c6a3e7619459407d8958f7e56ab3dda7f0738f5aed9bb4ad48076e8de83/multidict-6.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:14c56f73e78faa1f68bbb826197cd5871994e70e841b8590829c35912ece5c64", size = 62732, upload-time = "2026-09-21T17:58:25.46Z" }, + { url = "https://files.pythonhosted.org/packages/38/eb/fd56c9d83ba0cdc3cf66f9acb278b24ef1f043b620b96d145685a3b57338/multidict-6.9.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:38c9986f9ce50c459b10de216a05f4bd7ed5ac63887d56e500a52bb464b861ce", size = 288648, upload-time = "2026-09-21T17:58:27.115Z" }, + { url = "https://files.pythonhosted.org/packages/df/ef/51e79c2442b0f56ac4ac580108d713078b31297e028d468fc220971a305d/multidict-6.9.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15db6a102cbaf1949cf028ecf080aac76d20bcd29ad4e092574db6c6b7af78a5", size = 304919, upload-time = "2026-09-21T17:58:29.143Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e4/774700cc5739353fe88a7204ed30e78a9403d98d4bb2496cc91f096d2e71/multidict-6.9.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd82c4977196681a499bb6ca9e462afbc5c91c1c15b6a991dfbd72733fea4dd2", size = 289094, upload-time = "2026-09-21T17:58:30.938Z" }, + { url = "https://files.pythonhosted.org/packages/ca/41/0bec327bc3a8271825376b6d8a469d1f80e3ba1e2e730e019836e29663c3/multidict-6.9.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bb58ba73a3f96f9a3e46b1fab69929d7edbbddb3133ee74b5c3c54074a53c4f1", size = 312007, upload-time = "2026-09-21T17:58:32.964Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/9906bf5f1fe20e8e755affccc1a69ecd8aebb079ac65386e5ffc8eefbe75/multidict-6.9.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c6eecfab7ce4cd9487ff8ba936fe38cdfd68c04faf3d5c710363c6a8e695659", size = 314657, upload-time = "2026-09-21T17:58:35.285Z" }, + { url = "https://files.pythonhosted.org/packages/33/92/cf6d665a45663bbe216420fe632da10e9f4f842b07ce02bc3ddf8eb882b8/multidict-6.9.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c62e71e6289d78c0108d8aeb495f8bd3cad4bc1632fedddc9297ddf287ecc20", size = 301563, upload-time = "2026-09-21T17:58:37.472Z" }, + { url = "https://files.pythonhosted.org/packages/97/4c/9f1b7945526905d58b4b2d422e2ea81326dda71f9bdc9c32c8002639234a/multidict-6.9.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:539c2cd5fed0947c135cd7eabaaac55f48300dfa1de0f3ca4edb5efa6606f471", size = 278540, upload-time = "2026-09-21T17:58:39.551Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d9/133aa472fc1be181de29a0c2e1c4cd778280882f532b404395f646742e46/multidict-6.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3100d169ceb7bc8f05f89a6db11d1b21f119975fc26f29dc45a472e3569f0879", size = 294858, upload-time = "2026-09-21T17:58:41.791Z" }, + { url = "https://files.pythonhosted.org/packages/bd/38/21f2305dd20ac5038fa581b5d7e0d8dbbd70dc6b50231c22ec173dacddda/multidict-6.9.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:696477ad71385c4795e3b8e4cf10b0d2c28c2a1ca6a955e031cb1e62993e9ee3", size = 285461, upload-time = "2026-09-21T17:58:43.844Z" }, + { url = "https://files.pythonhosted.org/packages/ad/21/465a43c214d1d7e2d0c974e727d4b5935f8a0868838b5df9d0984c83c1dc/multidict-6.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:f5844e7befc707367807586f550fa97e23dcfef0728f02b98c7bc498a961a5df", size = 288964, upload-time = "2026-09-21T17:58:46.16Z" }, + { url = "https://files.pythonhosted.org/packages/2d/58/72a3e8d56c1e05146d2d1841dbaacf8986375eded1e5a58d30eef191a580/multidict-6.9.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:abc7c2e4b47bfe6a9aea434d3fdebb9597ee636e914e92ba352f2068b9142f4c", size = 303924, upload-time = "2026-09-21T17:58:48.395Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b9/911084c04530a682a54536b4f738d3992a593b5ff6fea1d6a1f02f3acc6b/multidict-6.9.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:7ab379f95caee071a37cbd8be86d4c65accc651d391f9f97748fef006f38769c", size = 278479, upload-time = "2026-09-21T17:58:50.494Z" }, + { url = "https://files.pythonhosted.org/packages/45/eb/9a333346c02c928a9e82534638bce85697c10c46b4901fcd70af19357356/multidict-6.9.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:557a4e1708df428ebe6c3081c83a273d275dad2446ce0d81fc648ac71afdb18d", size = 301650, upload-time = "2026-09-21T17:58:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/67/2f/1d936c4e45c8199db6082b6836e9182e31aa393de0de652637790831ca4b/multidict-6.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:475d04d5192eba487a3e2f935976340baa24529046e9c1c9c7a3b7bf80445ae1", size = 298024, upload-time = "2026-09-21T17:58:55.03Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0c/ca62ae2882b89a4cfeb0bbca873a4afdb5fc32994c4f8a37a7cb52ede7a0/multidict-6.9.1-cp315-cp315t-win32.whl", hash = "sha256:10083a8a0f4e1b26b599889e90b9802504ce5d3f7722f925bbb7ca47dd22a7c1", size = 57810, upload-time = "2026-09-21T17:58:57.141Z" }, + { url = "https://files.pythonhosted.org/packages/02/8b/b2c2805c3eb6fa4cdf5512fb95ac6174dde71e34dd601c5be52f4df5dc82/multidict-6.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e96ca64383efa107262ee3949f047af5ee4f1845ba09463466c04d35a83bd3ec", size = 64984, upload-time = "2026-09-21T17:58:59.446Z" }, + { url = "https://files.pythonhosted.org/packages/fd/34/03f1d204d698c408f4754c969882609b6b7a902c0e3d16a7033f219cdeda/multidict-6.9.1-cp315-cp315t-win_arm64.whl", hash = "sha256:501ed8b02a5990c67a91c732843609d43a6be1f7576fcdfc867331239f37fbd3", size = 58742, upload-time = "2026-09-21T17:59:01.726Z" }, { url = "https://files.pythonhosted.org/packages/be/59/e26cb779be4c591d1a910f59d29aca9fba4de70349840a833beba2652371/multidict-6.9.1-py3-none-any.whl", hash = "sha256:7bf6478188f4e47bf5686e8a33da4ae28bf43b1b2528d9ee144d28492bfac60b", size = 19176, upload-time = "2026-09-21T17:59:03.501Z" }, ] @@ -6033,7 +6380,8 @@ dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/3d/5b373635b3146264eb7a68d09e5ca11c305bbb058dfffbb47c47daf4f632/mypy-1.20.1.tar.gz", hash = "sha256:6fc3f4ecd52de81648fed1945498bf42fa2993ddfad67c9056df36ae5757f804", size = 3815892, upload-time = "2026-04-13T02:46:51.474Z" } wheels = [ @@ -6157,7 +6505,8 @@ name = "numpy" version = "2.4.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", ] @@ -6282,10 +6631,12 @@ dependencies = [ { name = "distro" }, { name = "httpx" }, { name = "jiter" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "sniffio" }, { name = "tqdm" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f0/ee/d056c82f63c05f06baac0cffb4a90952d8274f90c49dfe244f20497b9bbd/openai-2.33.0.tar.gz", hash = "sha256:f850c435e2a4685bba3295bd54912dd26315d9c1b7733068186134d6e0599f9a", size = 693254, upload-time = "2026-04-28T14:04:42.428Z" } wheels = [ @@ -6303,7 +6654,8 @@ dependencies = [ { name = "more-itertools" }, { name = "openapi-schema-validator" }, { name = "openapi-spec-validator" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "werkzeug" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/65/ee75f25b9459a02df6f713f8ffde5dacb57b8b4e45145cde4cab28b5abba/openapi_core-0.22.0.tar.gz", hash = "sha256:b30490dfa74e3aac2276105525590135212352f5dd7e5acf8f62f6a89ed6f2d0", size = 109242, upload-time = "2025-12-22T19:19:49.608Z" } @@ -6345,7 +6697,8 @@ name = "opentelemetry-api" version = "1.45.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/02/6e0ae9cc61bd3169d401077b507b3ebc344745171e1051ab430be012dcd9/opentelemetry_api-1.45.1.tar.gz", hash = "sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75", size = 72804, upload-time = "2026-10-06T17:32:58.133Z" } wheels = [ @@ -6418,7 +6771,8 @@ dependencies = [ { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d6/00/a82af0be959dc58495740b169c6669a86e0811f6cd353a01eda34d255db3/opentelemetry_exporter_otlp_proto_grpc-1.45.1.tar.gz", hash = "sha256:3b3dcfbfdcb4e35149fcf309972282054b45228f5c10547d0095d6578510a9a0", size = 25997, upload-time = "2026-10-06T17:33:05.114Z" } wheels = [ @@ -6438,7 +6792,8 @@ dependencies = [ { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1b/17/26487707ea4caa97b17e6e4b5fa72133a53512ffa2f5cf7a49ef284b29cb/opentelemetry_exporter_otlp_proto_http-1.45.1.tar.gz", hash = "sha256:45c218405ce3fd879596924b1874bf9a8f6880206d61065c5a912c8e5c297fb7", size = 28839, upload-time = "2026-10-06T17:33:05.713Z" } wheels = [ @@ -6964,7 +7319,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/79/7392e21a1c8f0c61d90b223e31c7e48cb9d452e91a6b820ad24cca5f23c4/opentelemetry_sdk-1.45.1.tar.gz", hash = "sha256:63d24a6ca645019a631e6a51999c73e93adcac1196ca640b8ae78a7cc4762bf3", size = 218324, upload-time = "2026-10-06T17:33:13.26Z" } wheels = [ @@ -6977,7 +7333,8 @@ version = "0.66b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/46/e4/dbbfb2a010c4db2224a5114638acede6fe563d33cc20fb1752cebcbe6298/opentelemetry_semantic_conventions-0.66b1.tar.gz", hash = "sha256:497ca63bf383723411e8eaf60c8779e9877633c936bb641080adab59d0eb6ec8", size = 150250, upload-time = "2026-10-06T17:33:14.073Z" } wheels = [ @@ -7004,83 +7361,68 @@ wheels = [ [[package]] name = "orjson" -version = "3.11.6" +version = "3.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/a3/4e09c61a5f0c521cba0bb433639610ae037437669f1a4cbc93799e731d78/orjson-3.11.6.tar.gz", hash = "sha256:0a54c72259f35299fd033042367df781c2f66d10252955ca1efb7db309b954cb", size = 6175856, upload-time = "2026-01-29T15:13:07.942Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/72/380b97dc45bd162d23afe5194721ef678d9eac7cfaa549fe2873f7f0a518/orjson-3.13.0.tar.gz", hash = "sha256:d1de5eb04485110c5da4c657e49168995d55e076b1ce60f1a042e254f4186c4f", size = 2732604, upload-time = "2026-10-07T14:09:25.719Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3c/098ed0e49c565fdf1ccc6a75b190115d1ca74148bf5b6ab036554a550650/orjson-3.11.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a613fc37e007143d5b6286dccb1394cd114b07832417006a02b620ddd8279e37", size = 250411, upload-time = "2026-01-29T15:11:17.941Z" }, - { url = "https://files.pythonhosted.org/packages/15/7c/cb11a360fd228ceebade03b1e8e9e138dd4b1b3b11602b72dbdad915aded/orjson-3.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46ebee78f709d3ba7a65384cfe285bb0763157c6d2f836e7bde2f12d33a867a2", size = 138147, upload-time = "2026-01-29T15:11:19.659Z" }, - { url = "https://files.pythonhosted.org/packages/4e/4b/e57b5c45ffe69fbef7cbd56e9f40e2dc0d5de920caafefcc6981d1a7efc5/orjson-3.11.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a726fa86d2368cd57990f2bd95ef5495a6e613b08fc9585dfe121ec758fb08d1", size = 135110, upload-time = "2026-01-29T15:11:21.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6e/4f21c6256f8cee3c0c69926cf7ac821cfc36f218512eedea2e2dc4a490c8/orjson-3.11.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:150f12e59d6864197770c78126e1a6e07a3da73d1728731bf3bc1e8b96ffdbe6", size = 140995, upload-time = "2026-01-29T15:11:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d0/78/92c36205ba2f6094ba1eea60c8e646885072abe64f155196833988c14b74/orjson-3.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a2d9746a5b5ce20c0908ada451eb56da4ffa01552a50789a0354d8636a02953", size = 144435, upload-time = "2026-01-29T15:11:24.124Z" }, - { url = "https://files.pythonhosted.org/packages/4d/52/1b518d164005811eb3fea92650e76e7d9deadb0b41e92c483373b1e82863/orjson-3.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd177f5dd91666d31e9019f1b06d2fcdf8a409a1637ddcb5915085dede85680", size = 142734, upload-time = "2026-01-29T15:11:25.708Z" }, - { url = "https://files.pythonhosted.org/packages/4b/11/60ea7885a2b7c1bf60ed8b5982356078a73785bd3bab392041a5bcf8de7c/orjson-3.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d777ec41a327bd3b7de97ba7bce12cc1007815ca398e4e4de9ec56c022c090b", size = 145802, upload-time = "2026-01-29T15:11:26.917Z" }, - { url = "https://files.pythonhosted.org/packages/41/7f/15a927e7958fd4f7560fb6dbb9346bee44a168e40168093c46020d866098/orjson-3.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f3a135f83185c87c13ff231fcb7dbb2fa4332a376444bd65135b50ff4cc5265c", size = 147504, upload-time = "2026-01-29T15:11:28.07Z" }, - { url = "https://files.pythonhosted.org/packages/66/1f/cabb9132a533f4f913e29294d0a1ca818b1a9a52e990526fe3f7ddd75f1c/orjson-3.11.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:2a8eeed7d4544cf391a142b0dd06029dac588e96cc692d9ab1c3f05b1e57c7f6", size = 421408, upload-time = "2026-01-29T15:11:29.314Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b9/09bda9257a982e300313e4a9fc9b9c3aaff424d07bcf765bf045e4e3ed03/orjson-3.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9d576865a21e5cc6695be8fb78afc812079fd361ce6a027a7d41561b61b33a90", size = 155801, upload-time = "2026-01-29T15:11:30.575Z" }, - { url = "https://files.pythonhosted.org/packages/98/19/4e40ea3e5f4c6a8d51f31fd2382351ee7b396fecca915b17cd1af588175b/orjson-3.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:925e2df51f60aa50f8797830f2adfc05330425803f4105875bb511ced98b7f89", size = 147647, upload-time = "2026-01-29T15:11:31.856Z" }, - { url = "https://files.pythonhosted.org/packages/5a/73/ef4bd7dd15042cf33a402d16b87b9e969e71edb452b63b6e2b05025d1f7d/orjson-3.11.6-cp310-cp310-win32.whl", hash = "sha256:09dded2de64e77ac0b312ad59f35023548fb87393a57447e1bb36a26c181a90f", size = 139770, upload-time = "2026-01-29T15:11:33.031Z" }, - { url = "https://files.pythonhosted.org/packages/b4/ac/daab6e10467f7fffd7081ba587b492505b49313130ff5446a6fe28bf076e/orjson-3.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:3a63b5e7841ca8635214c6be7c0bf0246aa8c5cd4ef0c419b14362d0b2fb13de", size = 136783, upload-time = "2026-01-29T15:11:34.686Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fd/d6b0a36854179b93ed77839f107c4089d91cccc9f9ba1b752b6e3bac5f34/orjson-3.11.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e259e85a81d76d9665f03d6129e09e4435531870de5961ddcd0bf6e3a7fde7d7", size = 250029, upload-time = "2026-01-29T15:11:35.942Z" }, - { url = "https://files.pythonhosted.org/packages/a3/bb/22902619826641cf3b627c24aab62e2ad6b571bdd1d34733abb0dd57f67a/orjson-3.11.6-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:52263949f41b4a4822c6b1353bcc5ee2f7109d53a3b493501d3369d6d0e7937a", size = 134518, upload-time = "2026-01-29T15:11:37.347Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/7a818da4bba1de711a9653c420749c0ac95ef8f8651cbc1dca551f462fe0/orjson-3.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6439e742fa7834a24698d358a27346bb203bff356ae0402e7f5df8f749c621a8", size = 137917, upload-time = "2026-01-29T15:11:38.511Z" }, - { url = "https://files.pythonhosted.org/packages/59/0f/02846c1cac8e205cb3822dd8aa8f9114acda216f41fd1999ace6b543418d/orjson-3.11.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b81ffd68f084b4e993e3867acb554a049fa7787cc8710bbcc1e26965580d99be", size = 134923, upload-time = "2026-01-29T15:11:39.711Z" }, - { url = "https://files.pythonhosted.org/packages/94/cf/aeaf683001b474bb3c3c757073a4231dfdfe8467fceaefa5bfd40902c99f/orjson-3.11.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5a5468e5e60f7ef6d7f9044b06c8f94a3c56ba528c6e4f7f06ae95164b595ec", size = 140752, upload-time = "2026-01-29T15:11:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fe/dad52d8315a65f084044a0819d74c4c9daf9ebe0681d30f525b0d29a31f0/orjson-3.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72c5005eb45bd2535632d4f3bec7ad392832cfc46b62a3021da3b48a67734b45", size = 144201, upload-time = "2026-01-29T15:11:42.537Z" }, - { url = "https://files.pythonhosted.org/packages/36/bc/ab070dd421565b831801077f1e390c4d4af8bfcecafc110336680a33866b/orjson-3.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b14dd49f3462b014455a28a4d810d3549bf990567653eb43765cd847df09145", size = 142380, upload-time = "2026-01-29T15:11:44.309Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d8/4b581c725c3a308717f28bf45a9fdac210bca08b67e8430143699413ff06/orjson-3.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bb2c1ea30ef302f0f89f9bf3e7f9ab5e2af29dc9f80eb87aa99788e4e2d65", size = 145582, upload-time = "2026-01-29T15:11:45.506Z" }, - { url = "https://files.pythonhosted.org/packages/5b/a2/09aab99b39f9a7f175ea8fa29adb9933a3d01e7d5d603cdee7f1c40c8da2/orjson-3.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:825e0a85d189533c6bff7e2fc417a28f6fcea53d27125c4551979aecd6c9a197", size = 147270, upload-time = "2026-01-29T15:11:46.782Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2f/5ef8eaf7829dc50da3bf497c7775b21ee88437bc8c41f959aa3504ca6631/orjson-3.11.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b04575417a26530637f6ab4b1f7b4f666eb0433491091da4de38611f97f2fcf3", size = 421222, upload-time = "2026-01-29T15:11:48.106Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b0/dd6b941294c2b5b13da5fdc7e749e58d0c55a5114ab37497155e83050e95/orjson-3.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b83eb2e40e8c4da6d6b340ee6b1d6125f5195eb1b0ebb7eac23c6d9d4f92d224", size = 155562, upload-time = "2026-01-29T15:11:49.408Z" }, - { url = "https://files.pythonhosted.org/packages/8e/09/43924331a847476ae2f9a16bd6d3c9dab301265006212ba0d3d7fd58763a/orjson-3.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1f42da604ee65a6b87eef858c913ce3e5777872b19321d11e6fc6d21de89b64f", size = 147432, upload-time = "2026-01-29T15:11:50.635Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e9/d9865961081816909f6b49d880749dbbd88425afd7c5bbce0549e2290d77/orjson-3.11.6-cp311-cp311-win32.whl", hash = "sha256:5ae45df804f2d344cffb36c43fdf03c82fb6cd247f5faa41e21891b40dfbf733", size = 139623, upload-time = "2026-01-29T15:11:51.82Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f9/6836edb92f76eec1082919101eb1145d2f9c33c8f2c5e6fa399b82a2aaa8/orjson-3.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:f4295948d65ace0a2d8f2c4ccc429668b7eb8af547578ec882e16bf79b0050b2", size = 136647, upload-time = "2026-01-29T15:11:53.454Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0c/4954082eea948c9ae52ee0bcbaa2f99da3216a71bcc314ab129bde22e565/orjson-3.11.6-cp311-cp311-win_arm64.whl", hash = "sha256:314e9c45e0b81b547e3a1cfa3df3e07a815821b3dac9fe8cb75014071d0c16a4", size = 135327, upload-time = "2026-01-29T15:11:56.616Z" }, - { url = "https://files.pythonhosted.org/packages/14/ba/759f2879f41910b7e5e0cdbd9cf82a4f017c527fb0e972e9869ca7fe4c8e/orjson-3.11.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6f03f30cd8953f75f2a439070c743c7336d10ee940da918d71c6f3556af3ddcf", size = 249988, upload-time = "2026-01-29T15:11:58.294Z" }, - { url = "https://files.pythonhosted.org/packages/f0/70/54cecb929e6c8b10104fcf580b0cc7dc551aa193e83787dd6f3daba28bb5/orjson-3.11.6-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:af44baae65ef386ad971469a8557a0673bb042b0b9fd4397becd9c2dfaa02588", size = 134445, upload-time = "2026-01-29T15:11:59.819Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6f/ec0309154457b9ba1ad05f11faa4441f76037152f75e1ac577db3ce7ca96/orjson-3.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c310a48542094e4f7dbb6ac076880994986dda8ca9186a58c3cb70a3514d3231", size = 137708, upload-time = "2026-01-29T15:12:01.488Z" }, - { url = "https://files.pythonhosted.org/packages/20/52/3c71b80840f8bab9cb26417302707b7716b7d25f863f3a541bcfa232fe6e/orjson-3.11.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8dfa7a5d387f15ecad94cb6b2d2d5f4aeea64efd8d526bfc03c9812d01e1cc0", size = 134798, upload-time = "2026-01-29T15:12:02.705Z" }, - { url = "https://files.pythonhosted.org/packages/30/51/b490a43b22ff736282360bd02e6bded455cf31dfc3224e01cd39f919bbd2/orjson-3.11.6-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba8daee3e999411b50f8b50dbb0a3071dd1845f3f9a1a0a6fa6de86d1689d84d", size = 140839, upload-time = "2026-01-29T15:12:03.956Z" }, - { url = "https://files.pythonhosted.org/packages/95/bc/4bcfe4280c1bc63c5291bb96f98298845b6355da2226d3400e17e7b51e53/orjson-3.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89d104c974eafd7436d7a5fdbc57f7a1e776789959a2f4f1b2eab5c62a339f4", size = 144080, upload-time = "2026-01-29T15:12:05.151Z" }, - { url = "https://files.pythonhosted.org/packages/01/74/22970f9ead9ab1f1b5f8c227a6c3aa8d71cd2c5acd005868a1d44f2362fa/orjson-3.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e2e2456788ca5ea75616c40da06fc885a7dc0389780e8a41bf7c5389ba257b", size = 142435, upload-time = "2026-01-29T15:12:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/29/34/d564aff85847ab92c82ee43a7a203683566c2fca0723a5f50aebbe759603/orjson-3.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a42efebc45afabb1448001e90458c4020d5c64fbac8a8dc4045b777db76cb5a", size = 145631, upload-time = "2026-01-29T15:12:08.351Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ef/016957a3890752c4aa2368326ea69fa53cdc1fdae0a94a542b6410dbdf52/orjson-3.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71b7cbef8471324966c3738c90ba38775563ef01b512feb5ad4805682188d1b9", size = 147058, upload-time = "2026-01-29T15:12:10.023Z" }, - { url = "https://files.pythonhosted.org/packages/56/cc/9a899c3972085645b3225569f91a30e221f441e5dc8126e6d060b971c252/orjson-3.11.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f8515e5910f454fe9a8e13c2bb9dc4bae4c1836313e967e72eb8a4ad874f0248", size = 421161, upload-time = "2026-01-29T15:12:11.308Z" }, - { url = "https://files.pythonhosted.org/packages/21/a8/767d3fbd6d9b8fdee76974db40619399355fd49bf91a6dd2c4b6909ccf05/orjson-3.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:300360edf27c8c9bf7047345a94fddf3a8b8922df0ff69d71d854a170cb375cf", size = 155757, upload-time = "2026-01-29T15:12:12.776Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0b/205cd69ac87e2272e13ef3f5f03a3d4657e317e38c1b08aaa2ef97060bbc/orjson-3.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:caaed4dad39e271adfadc106fab634d173b2bb23d9cf7e67bd645f879175ebfc", size = 147446, upload-time = "2026-01-29T15:12:14.166Z" }, - { url = "https://files.pythonhosted.org/packages/de/c5/dd9f22aa9f27c54c7d05cc32f4580c9ac9b6f13811eeb81d6c4c3f50d6b1/orjson-3.11.6-cp312-cp312-win32.whl", hash = "sha256:955368c11808c89793e847830e1b1007503a5923ddadc108547d3b77df761044", size = 139717, upload-time = "2026-01-29T15:12:15.7Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/e62fc50d904486970315a1654b8cfb5832eb46abb18cd5405118e7e1fc79/orjson-3.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:2c68de30131481150073d90a5d227a4a421982f42c025ecdfb66157f9579e06f", size = 136711, upload-time = "2026-01-29T15:12:17.055Z" }, - { url = "https://files.pythonhosted.org/packages/04/3d/b4fefad8bdf91e0fe212eb04975aeb36ea92997269d68857efcc7eb1dda3/orjson-3.11.6-cp312-cp312-win_arm64.whl", hash = "sha256:65dfa096f4e3a5e02834b681f539a87fbe85adc82001383c0db907557f666bfc", size = 135212, upload-time = "2026-01-29T15:12:18.3Z" }, - { url = "https://files.pythonhosted.org/packages/ae/45/d9c71c8c321277bc1ceebf599bc55ba826ae538b7c61f287e9a7e71bd589/orjson-3.11.6-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e4ae1670caabb598a88d385798692ce2a1b2f078971b3329cfb85253c6097f5b", size = 249828, upload-time = "2026-01-29T15:12:20.14Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7e/4afcf4cfa9c2f93846d70eee9c53c3c0123286edcbeb530b7e9bd2aea1b2/orjson-3.11.6-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:2c6b81f47b13dac2caa5d20fbc953c75eb802543abf48403a4703ed3bff225f0", size = 134339, upload-time = "2026-01-29T15:12:22.01Z" }, - { url = "https://files.pythonhosted.org/packages/40/10/6d2b8a064c8d2411d3d0ea6ab43125fae70152aef6bea77bb50fa54d4097/orjson-3.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:647d6d034e463764e86670644bdcaf8e68b076e6e74783383b01085ae9ab334f", size = 137662, upload-time = "2026-01-29T15:12:23.307Z" }, - { url = "https://files.pythonhosted.org/packages/5a/50/5804ea7d586baf83ee88969eefda97a24f9a5bdba0727f73e16305175b26/orjson-3.11.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8523b9cc4ef174ae52414f7699e95ee657c16aa18b3c3c285d48d7966cce9081", size = 134626, upload-time = "2026-01-29T15:12:25.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/2e/f0492ed43e376722bb4afd648e06cc1e627fc7ec8ff55f6ee739277813ea/orjson-3.11.6-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:313dfd7184cde50c733fc0d5c8c0e2f09017b573afd11dc36bd7476b30b4cb17", size = 140873, upload-time = "2026-01-29T15:12:26.369Z" }, - { url = "https://files.pythonhosted.org/packages/10/15/6f874857463421794a303a39ac5494786ad46a4ab46d92bda6705d78c5aa/orjson-3.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905ee036064ff1e1fd1fb800055ac477cdcb547a78c22c1bc2bbf8d5d1a6fb42", size = 144044, upload-time = "2026-01-29T15:12:28.082Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c7/b7223a3a70f1d0cc2d86953825de45f33877ee1b124a91ca1f79aa6e643f/orjson-3.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce374cb98411356ba906914441fc993f271a7a666d838d8de0e0900dd4a4bc12", size = 142396, upload-time = "2026-01-29T15:12:30.529Z" }, - { url = "https://files.pythonhosted.org/packages/87/e3/aa1b6d3ad3cd80f10394134f73ae92a1d11fdbe974c34aa199cc18bb5fcf/orjson-3.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cded072b9f65fcfd188aead45efa5bd528ba552add619b3ad2a81f67400ec450", size = 145600, upload-time = "2026-01-29T15:12:31.848Z" }, - { url = "https://files.pythonhosted.org/packages/f6/cf/e4aac5a46cbd39d7e769ef8650efa851dfce22df1ba97ae2b33efe893b12/orjson-3.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ab85bdbc138e1f73a234db6bb2e4cc1f0fcec8f4bd2bd2430e957a01aadf746", size = 146967, upload-time = "2026-01-29T15:12:33.203Z" }, - { url = "https://files.pythonhosted.org/packages/0b/04/975b86a4bcf6cfeda47aad15956d52fbeda280811206e9967380fa9355c8/orjson-3.11.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:351b96b614e3c37a27b8ab048239ebc1e0be76cc17481a430d70a77fb95d3844", size = 421003, upload-time = "2026-01-29T15:12:35.097Z" }, - { url = "https://files.pythonhosted.org/packages/28/d1/0369d0baf40eea5ff2300cebfe209883b2473ab4aa4c4974c8bd5ee42bb2/orjson-3.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f9959c85576beae5cdcaaf39510b15105f1ee8b70d5dacd90152617f57be8c83", size = 155695, upload-time = "2026-01-29T15:12:36.589Z" }, - { url = "https://files.pythonhosted.org/packages/ab/1f/d10c6d6ae26ff1d7c3eea6fd048280ef2e796d4fb260c5424fd021f68ecf/orjson-3.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75682d62b1b16b61a30716d7a2ec1f4c36195de4a1c61f6665aedd947b93a5d5", size = 147392, upload-time = "2026-01-29T15:12:37.876Z" }, - { url = "https://files.pythonhosted.org/packages/8d/43/7479921c174441a0aa5277c313732e20713c0969ac303be9f03d88d3db5d/orjson-3.11.6-cp313-cp313-win32.whl", hash = "sha256:40dc277999c2ef227dcc13072be879b4cfd325502daeb5c35ed768f706f2bf30", size = 139718, upload-time = "2026-01-29T15:12:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/88/bc/9ffe7dfbf8454bc4e75bb8bf3a405ed9e0598df1d3535bb4adcd46be07d0/orjson-3.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:f0f6e9f8ff7905660bc3c8a54cd4a675aa98f7f175cf00a59815e2ff42c0d916", size = 136635, upload-time = "2026-01-29T15:12:40.593Z" }, - { url = "https://files.pythonhosted.org/packages/6f/7e/51fa90b451470447ea5023b20d83331ec741ae28d1e6d8ed547c24e7de14/orjson-3.11.6-cp313-cp313-win_arm64.whl", hash = "sha256:1608999478664de848e5900ce41f25c4ecdfc4beacbc632b6fd55e1a586e5d38", size = 135175, upload-time = "2026-01-29T15:12:41.997Z" }, - { url = "https://files.pythonhosted.org/packages/31/9f/46ca908abaeeec7560638ff20276ab327b980d73b3cc2f5b205b4a1c60b3/orjson-3.11.6-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6026db2692041d2a23fe2545606df591687787825ad5821971ef0974f2c47630", size = 249823, upload-time = "2026-01-29T15:12:43.332Z" }, - { url = "https://files.pythonhosted.org/packages/ff/78/ca478089818d18c9cd04f79c43f74ddd031b63c70fa2a946eb5e85414623/orjson-3.11.6-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:132b0ab2e20c73afa85cf142e547511feb3d2f5b7943468984658f3952b467d4", size = 134328, upload-time = "2026-01-29T15:12:45.171Z" }, - { url = "https://files.pythonhosted.org/packages/39/5e/cbb9d830ed4e47f4375ad8eef8e4fff1bf1328437732c3809054fc4e80be/orjson-3.11.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b376fb05f20a96ec117d47987dd3b39265c635725bda40661b4c5b73b77b5fde", size = 137651, upload-time = "2026-01-29T15:12:46.602Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3a/35df6558c5bc3a65ce0961aefee7f8364e59af78749fc796ea255bfa0cf5/orjson-3.11.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:954dae4e080574672a1dfcf2a840eddef0f27bd89b0e94903dd0824e9c1db060", size = 134596, upload-time = "2026-01-29T15:12:47.95Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8e/3d32dd7b7f26a19cc4512d6ed0ae3429567c71feef720fe699ff43c5bc9e/orjson-3.11.6-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe515bb89d59e1e4b48637a964f480b35c0a2676de24e65e55310f6016cca7ce", size = 140923, upload-time = "2026-01-29T15:12:49.333Z" }, - { url = "https://files.pythonhosted.org/packages/6c/9c/1efbf5c99b3304f25d6f0d493a8d1492ee98693637c10ce65d57be839d7b/orjson-3.11.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:380f9709c275917af28feb086813923251e11ee10687257cd7f1ea188bcd4485", size = 144068, upload-time = "2026-01-29T15:12:50.927Z" }, - { url = "https://files.pythonhosted.org/packages/82/83/0d19eeb5be797de217303bbb55dde58dba26f996ed905d301d98fd2d4637/orjson-3.11.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8173e0d3f6081e7034c51cf984036d02f6bab2a2126de5a759d79f8e5a140e7", size = 142493, upload-time = "2026-01-29T15:12:52.432Z" }, - { url = "https://files.pythonhosted.org/packages/32/a7/573fec3df4dc8fc259b7770dc6c0656f91adce6e19330c78d23f87945d1e/orjson-3.11.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dddf9ba706294906c56ef5150a958317b09aa3a8a48df1c52ccf22ec1907eac", size = 145616, upload-time = "2026-01-29T15:12:53.903Z" }, - { url = "https://files.pythonhosted.org/packages/c2/0e/23551b16f21690f7fd5122e3cf40fdca5d77052a434d0071990f97f5fe2f/orjson-3.11.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cbae5c34588dc79938dffb0b6fbe8c531f4dc8a6ad7f39759a9eb5d2da405ef2", size = 146951, upload-time = "2026-01-29T15:12:55.698Z" }, - { url = "https://files.pythonhosted.org/packages/b8/63/5e6c8f39805c39123a18e412434ea364349ee0012548d08aa586e2bd6aa9/orjson-3.11.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f75c318640acbddc419733b57f8a07515e587a939d8f54363654041fd1f4e465", size = 421024, upload-time = "2026-01-29T15:12:57.434Z" }, - { url = "https://files.pythonhosted.org/packages/1d/4d/724975cf0087f6550bd01fd62203418afc0ea33fd099aed318c5bcc52df8/orjson-3.11.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e0ab8d13aa2a3e98b4a43487c9205b2c92c38c054b4237777484d503357c8437", size = 155774, upload-time = "2026-01-29T15:12:59.397Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a3/f4c4e3f46b55db29e0a5f20493b924fc791092d9a03ff2068c9fe6c1002f/orjson-3.11.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f884c7fb1020d44612bd7ac0db0babba0e2f78b68d9a650c7959bf99c783773f", size = 147393, upload-time = "2026-01-29T15:13:00.769Z" }, - { url = "https://files.pythonhosted.org/packages/ee/86/6f5529dd27230966171ee126cecb237ed08e9f05f6102bfaf63e5b32277d/orjson-3.11.6-cp314-cp314-win32.whl", hash = "sha256:8d1035d1b25732ec9f971e833a3e299d2b1a330236f75e6fd945ad982c76aaf3", size = 139760, upload-time = "2026-01-29T15:13:02.173Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b5/91ae7037b2894a6b5002fb33f4fbccec98424a928469835c3837fbb22a9b/orjson-3.11.6-cp314-cp314-win_amd64.whl", hash = "sha256:931607a8865d21682bb72de54231655c86df1870502d2962dbfd12c82890d077", size = 136633, upload-time = "2026-01-29T15:13:04.267Z" }, - { url = "https://files.pythonhosted.org/packages/55/74/f473a3ec7a0a7ebc825ca8e3c86763f7d039f379860c81ba12dcdd456547/orjson-3.11.6-cp314-cp314-win_arm64.whl", hash = "sha256:fe71f6b283f4f1832204ab8235ce07adad145052614f77c876fcf0dac97bc06f", size = 135168, upload-time = "2026-01-29T15:13:05.932Z" }, + { url = "https://files.pythonhosted.org/packages/11/8c/25b6e2bd4f6b8e67a6b5acbc11a8cff4970e35c79837a24ec7db8732238d/orjson-3.13.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4f66eac85b072092e9941c3111882afd7527bf926cbc717038fa3654b582002b", size = 223510, upload-time = "2026-10-07T14:07:54.539Z" }, + { url = "https://files.pythonhosted.org/packages/32/4d/5772e32ebc19d0b76b957a48e69a09546400db35cebe76c21b2c341d1a30/orjson-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:efa160215c4630836d3b1250af4c7a305acd8239e0d75aff986b8088c2fcacb6", size = 113481, upload-time = "2026-10-07T14:07:56.229Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6a/5ce6adad2c0cb734cb9d19b7b9d9c7bbdb16c136af453dd37adace806547/orjson-3.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e5c8175e1574dcbe446ee654275d353c1d78bbd9a0dc9f209bf35c9df72d171", size = 130791, upload-time = "2026-10-07T14:07:57.751Z" }, + { url = "https://files.pythonhosted.org/packages/96/49/d954f02229efb06850a5f9aaf06e77e03046a009d49eb78f499fbd798ded/orjson-3.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78a12d4f8d740cc9ae197f5223682e5e960ba61b4fb2ce5a6a3bb54e83fde28e", size = 129465, upload-time = "2026-10-07T14:07:59.143Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a2/abcb0647268f334cb85768170b164e4c97f7a2ed5fddd146f79297494d9e/orjson-3.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93c70a5e22bbbbdeafc7b273441e8452a196041d67fd4d9a9c450c66370a8486", size = 130727, upload-time = "2026-10-07T14:08:00.659Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b0/5672f0505e6cde410cc7916cc2fbf88d90216d667b37907df041a659db06/orjson-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7b3bc6b81835ce65f4729ae401607583d41139c6de95bc7453f450f1391d3e7b", size = 135280, upload-time = "2026-10-07T14:08:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/d9/58/c223e3ac16193d00c1c3cbc786cb6db47158bff0558c52133e6dd0be7a12/orjson-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6d0684895b119ad167fb4ec05113639dc7f728022deec4756a710e838ed92e7a", size = 126844, upload-time = "2026-10-07T14:08:03.549Z" }, + { url = "https://files.pythonhosted.org/packages/49/a2/f6fd98acef1e36b8c8ae0275f0268a0f22bb6a1b436ee4536e1cdaf31b03/orjson-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:7991921c5da527a963b6d4cffd0e4ea89c7e71d4be0c8be1bfe6edb223ce7d96", size = 121455, upload-time = "2026-10-07T14:08:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/0be3b115907fea61ed340639fb0e1562cd18969bad5b3f486f808197aaff/orjson-3.13.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:948bad47f2e2e43527f14248364a0e5dee26dd3184691010ec4a1ebeb0fd6771", size = 223146, upload-time = "2026-10-07T14:08:06.474Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f7/665935edb16163f8b764182e29a30cf056947a66893ed032191e5f01eb3d/orjson-3.13.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:1807c2fa49d393c7ee95fd1ef1b39cbb24aa3ccd81f30b84503ba59407666960", size = 123546, upload-time = "2026-10-07T14:08:08.324Z" }, + { url = "https://files.pythonhosted.org/packages/67/ec/e7cde480c0e212594d17ba2b2bd210c002052e9147fc1a1aeafaabe722fb/orjson-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:637dbca1fccffe83780e806fbc0f17427c0c59bf822528eb0acc8f0aa9f19acb", size = 113290, upload-time = "2026-10-07T14:08:09.816Z" }, + { url = "https://files.pythonhosted.org/packages/36/59/4455fb11a297af73611dfc437f0f89456220227ed1cb1544a5a0ee9d6c03/orjson-3.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:554948becd1110123ef9f6a6e1310fd92b2d07d2cbac6dbf65df3de75702e736", size = 130342, upload-time = "2026-10-07T14:08:11.253Z" }, + { url = "https://files.pythonhosted.org/packages/ca/80/0eec5fbde2e52407646b4cb3118f63175bdcee1e2390c2759dc96e0bc62a/orjson-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9d9a101bd8dbfad112170f009cd155e52bb8c936468821a0d03cbb96c0e426", size = 129138, upload-time = "2026-10-07T14:08:12.814Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cc/c0874f13819ae346d69ca00d074d464710b494abd4442bdebf75ac404a98/orjson-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89bcf2d4bc6c9a7e1763c8cf534f38712e66b76a0fefda7fb7785462f0d635e4", size = 130518, upload-time = "2026-10-07T14:08:14.392Z" }, + { url = "https://files.pythonhosted.org/packages/25/ab/140dd9adff84bf64b862c4fcfe2d055af6014d5ba03a075f95c9addb2ec7/orjson-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a79cdc4934fe81f593072c94e13da3095e9d41c2deef8f6ff2901794ca1c5042", size = 134924, upload-time = "2026-10-07T14:08:16.09Z" }, + { url = "https://files.pythonhosted.org/packages/08/0a/e8f6deb032b1d98a39043cf99b863d8b9e842e2ffc2d2067d2e2a88c18e4/orjson-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:50a5202ba388b3850ba24437951727d3aa6d79a21964a30ae8dc6a059a5fd34c", size = 126704, upload-time = "2026-10-07T14:08:17.439Z" }, + { url = "https://files.pythonhosted.org/packages/af/cf/be64b99ff75f7983488390d4ef5df72115119770eed295691c0a715d492a/orjson-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a0377d6962fa431c93ecd78fdea771bb62ec545b24ee0c5d4e32acf2260af259", size = 121287, upload-time = "2026-10-07T14:08:18.843Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ab/1b8ca186baf3420f12db1f2819fcc5f2cae69e4cf051168501726a64c0fa/orjson-3.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:1d84820b2ec4ac975cba482214032de5b0dbdd17046170c98e642ef9c4a4ee4b", size = 126314, upload-time = "2026-10-07T14:08:20.452Z" }, + { url = "https://files.pythonhosted.org/packages/98/17/ed65f84ed5ed6a1e06eb628611b4172e7480fc4ad92594856751a6363cac/orjson-3.13.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb8644dc6d705e1269ed2842bf4dbe2b4e50d670de503bf79d5cef3a5148a4c7", size = 223063, upload-time = "2026-10-07T14:08:21.979Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/9332eb96d2e379384be0f211f543835eebc81f460c9403b84abe1294c431/orjson-3.13.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6ff2a2c67f35202f7d823753d38ad371a9b7fc297567cdfff4420e763cb9f6f8", size = 123364, upload-time = "2026-10-07T14:08:24.026Z" }, + { url = "https://files.pythonhosted.org/packages/b4/06/558456b7da27e974a8c9ea09117b07119f6fa131cd62b8b9ecad9eea94e1/orjson-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:65c4e0e106ccc7265b488385659117a6805c37d042f737558ecd68aa0c67ad8f", size = 113199, upload-time = "2026-10-07T14:08:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f2/1187a9c09965620348262ec0f406868f6d7c234b2e9b5ee51020bdde5748/orjson-3.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:fbbad6b9b1da43f25c1f5b20cd5a268e028a2fc95d5a8d1ade6059973bc71584", size = 130329, upload-time = "2026-10-07T14:08:26.877Z" }, + { url = "https://files.pythonhosted.org/packages/46/07/5d1a151bc11600434fe799e73abfc6a4d463d02e149a20e47c59d3a985ae/orjson-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d895cf7bbfd50ef34bb63bb727b14514f259f3e3f8dd010783bd38e864c6e", size = 129072, upload-time = "2026-10-07T14:08:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8c/bb07c368abbf4021c4cd01c12edb526e00090f7f750ff1b88da6e6b6c7a6/orjson-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceadfd314bd238f584fc229a4bbaf0e573597e7a026dec5429fbf29fd66c641", size = 130612, upload-time = "2026-10-07T14:08:30.041Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8d/4b66d19619ed344ac000ffea7c006477d0061d580646e736ef0e203759e8/orjson-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b74c30e56346aad067937d766846ee74c231d1d18aad3f324e9b9261de3b2d5e", size = 134632, upload-time = "2026-10-07T14:08:31.474Z" }, + { url = "https://files.pythonhosted.org/packages/ea/88/f8221f6593e37eb26ec4706e185b9ac6f38ff0c8f7bad5459844031ffd2d/orjson-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4329c19b8a25693f60a77b867c9d2a3ab637b20e36f5b7bea7f5acb492b44b15", size = 126807, upload-time = "2026-10-07T14:08:32.914Z" }, + { url = "https://files.pythonhosted.org/packages/58/9d/a1ca7321eeafd7d72e174cdc388cc96301f41516d863e7b1f64f0a1735be/orjson-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:b571236d8393edcd3236e07423f762bfcf571f852aad667a3bce9e7b755e0790", size = 121538, upload-time = "2026-10-07T14:08:34.325Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a0/1f19b4779c910104370932fceb9ed436b47ac077f297db74008062525c04/orjson-3.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:8594956a75223f657e1e68c568c0eeb3dd145f02cd6b78a47fd9a8095dbc4eae", size = 126259, upload-time = "2026-10-07T14:08:35.765Z" }, + { url = "https://files.pythonhosted.org/packages/a9/56/f8ad2546150168858c16915c452b00eecb79597597524d1ad6ae14ad4eab/orjson-3.13.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:64e8f345048d988c8b68d3882e5d41028fca1219a9939b32e4a77be34c8ae8e3", size = 222892, upload-time = "2026-10-07T14:08:37.495Z" }, + { url = "https://files.pythonhosted.org/packages/1f/19/725d23160b2471a3f27026c55bb79af34687652d8be8f5f583cee5dcd42f/orjson-3.13.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ded33b972cffdaf4ca0ac917338ab61d2bb10d68987dbcae641c313fbfdbf499", size = 123319, upload-time = "2026-10-07T14:08:38.989Z" }, + { url = "https://files.pythonhosted.org/packages/ac/08/e5d81a00b22c73dfcb60d80da3bd92d5a7684346593536565f184dbae3c9/orjson-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:45e34deb3437509f4ec9888dd9ee5dc426cfe21be10f1eb4ea3a9e4d33034f9e", size = 113196, upload-time = "2026-10-07T14:08:40.383Z" }, + { url = "https://files.pythonhosted.org/packages/67/78/fda6117c69a43e470b1e9dff38dd8c5f0bc6fd8a47e4d4561ab023039335/orjson-3.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9825b954155b345c4759f24e5f8d652b9aec2261bb5d4e1abe06bba0a1200535", size = 130245, upload-time = "2026-10-07T14:08:41.878Z" }, + { url = "https://files.pythonhosted.org/packages/6d/31/d0cfebd456defb234414795ae7599696bf124843dfe077d0c9ece0c93554/orjson-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b081f0e7b600ff24513dec4ca75507fa05e904607847e386e8310d5b7b96b6c7", size = 128981, upload-time = "2026-10-07T14:08:43.716Z" }, + { url = "https://files.pythonhosted.org/packages/45/46/f8d83189ff5b7b2ff225a58c5908618cc4e86afe09e65d17a30ac68c9da4/orjson-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbed5f4c4b88d94bcc36115f4c3bb3aa25da1563a5c3328aa3acebce2b083040", size = 130370, upload-time = "2026-10-07T14:08:45.132Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6a/d6344c305003ea826b3fa0482645a897a3cd6d477ed74e1fe15d3322cb23/orjson-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9b61676116f755126b90e740a9cff36b91562f47ec330056cc88cc3b9f02f4b", size = 134595, upload-time = "2026-10-07T14:08:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/d73fa44f88d53e02d10de1cf77c16ed13204ff5bca47e1692da6b406619c/orjson-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ef75ed7e81dae34a3649f82df52cd85f9ac839a7d6ec78ab355b33b3b27ef7f", size = 126513, upload-time = "2026-10-07T14:08:48.111Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f8/bcfc50b4ab851c4f9c0ee62f52bf3b28f0bcd0d9fe08e0ad98d4585148db/orjson-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ee06e53b998c71ce3eb93b86222912fdd9dcced685ac64d4525d36fac338ea4", size = 121371, upload-time = "2026-10-07T14:08:49.549Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7a/d6927845712ec2b1e89263cd12d7203531db185dbad67f914226f2fca156/orjson-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:89efecad02515df7f318d0613b5dfd6d2a1acd323a2b8294712789a715945525", size = 126134, upload-time = "2026-10-07T14:08:51.118Z" }, + { url = "https://files.pythonhosted.org/packages/f0/10/98b5a3cdc086abf78d8cd20bb0cba124485d4b6a745722197bd209d967a5/orjson-3.13.0-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a7bfc7db961c7d96cb75889dc6a1e4ae1e91d87ee61da564f582bd742b8dfeef", size = 222889, upload-time = "2026-10-07T14:08:52.673Z" }, + { url = "https://files.pythonhosted.org/packages/22/7c/7728c5280ab5202f4891ff4b0b96e2e1dbd5520dfee53edf083c54409a64/orjson-3.13.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:91d933e668ff0ffe164d7c2daec36beba6d1ce7fadb71538fbe142a71f8a1e6e", size = 123312, upload-time = "2026-10-07T14:08:54.25Z" }, + { url = "https://files.pythonhosted.org/packages/a9/a5/d9a44321e6f66c0f64b45be587395f87ad94cb447bce7d92286f6b97d46a/orjson-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:6c8bfe728b81b0fd58a3c7f3f9c5a113f87f2992c9948e0f28707aafd737c0bc", size = 113146, upload-time = "2026-10-07T14:08:55.803Z" }, + { url = "https://files.pythonhosted.org/packages/80/da/d95c80d413f288feb471e16d82e5c1512d2439728e3bac917d058c31f098/orjson-3.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e8e05549f3b30f9d8a8e28c5aba11cc2a4b90b90961ec685ca58444b0815fc09", size = 130348, upload-time = "2026-10-07T14:08:57.31Z" }, + { url = "https://files.pythonhosted.org/packages/04/0f/36fdfb32ad1852997bac00e3ce52c7888d8a1094ba9dcdcbb22fcc6b953a/orjson-3.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c749ab3ac30b5ab1ffb7677f8b92eacfdfdc5260210baa398f845bc3714c05d8", size = 128971, upload-time = "2026-10-07T14:08:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/25/de/a82acf93bdcca0c79ccff25ef0c6868d24ccbc2e72f21fae39c8cabce4f1/orjson-3.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a9619d88f8818d9ab6b39d70d203789457ba13c1ed5d274f33ce9ae7e81a36", size = 130359, upload-time = "2026-10-07T14:09:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/71/ca/2bc4f7697cb9f6897bf61aca11803df096a5d971bf69ef5538b243bb1fa8/orjson-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2715c4808d1571029ed18fd07a82140bf3ba7def0dc89f8d015c416e3649bf87", size = 134583, upload-time = "2026-10-07T14:09:02.047Z" }, + { url = "https://files.pythonhosted.org/packages/23/b3/12b1af9b87ff9fa0aaf4e5724c87672b30bb5de76f275f7fac64e8219c1b/orjson-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08bf722f923d2100bc5e5a5dcf72c656db557049c1bea26582fdd5dd9d5395a1", size = 126500, upload-time = "2026-10-07T14:09:03.863Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ea/cf257fc8a7f4b18f5677c22b3a9673a1b51d4b7161f25177ed389b76560e/orjson-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:6adcaa85d79977659a448b4123a88eb33511a11ed2db243535ad7ea88a6668e0", size = 121378, upload-time = "2026-10-07T14:09:05.375Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/9f4643f849e9918eab11983b83928af3aac14bedb04002e28e885ee1936f/orjson-3.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:83705c12b4afde10c62a5dd3fe6fdb21b7900bd0dcd5af1c85612ae94d0ee590", size = 126123, upload-time = "2026-10-07T14:09:07.085Z" }, + { url = "https://files.pythonhosted.org/packages/8c/15/d265f2b556c0c7c0b30ea830316d6e5af5b85dde08f234a1ebed60fab386/orjson-3.13.0-cp315-cp315-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5ef4d4157392a0439b74f7e49e5636b4ea43d9616bd0884effc0195fffcaa2d5", size = 223305, upload-time = "2026-10-07T14:09:08.84Z" }, + { url = "https://files.pythonhosted.org/packages/0c/97/781be8b80a33b8171b3f5acea941af47182c8b4b5827c2b7c3fea706f21c/orjson-3.13.0-cp315-cp315-macosx_15_0_arm64.whl", hash = "sha256:84d87e322e1674408f85adea63f11aa19201eba082755aec20ebc217f493bbd2", size = 123515, upload-time = "2026-10-07T14:09:10.792Z" }, + { url = "https://files.pythonhosted.org/packages/20/68/011bb98fa7da7b430b363db1bb7ef9160c438fc5c43e7468fb593c220037/orjson-3.13.0-cp315-cp315-manylinux_2_39_aarch64.whl", hash = "sha256:8c2ac5c09b017c484df1b4c68b2cf250b4e8ba08204cb58e7cd6cbbc71a9c902", size = 129222, upload-time = "2026-10-07T14:09:12.542Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d96fa2aedaaec14c095ea9cd48d2158fdf33c0f4fd6e7a598d899d536b03/orjson-3.13.0-cp315-cp315-manylinux_2_39_armv7l.whl", hash = "sha256:51d11525bc3ca736fa97ce4e4c7da9999cc00bf261522bede43b4e7531bd7965", size = 113152, upload-time = "2026-10-07T14:09:14.059Z" }, + { url = "https://files.pythonhosted.org/packages/e9/2d/ee77aa685c54bd920a1f0e2936986b46269adb0d72bf5098c2c694dbeb36/orjson-3.13.0-cp315-cp315-manylinux_2_39_i686.whl", hash = "sha256:ac81530647c3423107cf61c3481e91f57134e9ddfb6ef83f5150ccbdcbc3a3ee", size = 130749, upload-time = "2026-10-07T14:09:15.835Z" }, + { url = "https://files.pythonhosted.org/packages/48/eb/3411fbfdad61b3f3af22343b5af7ed5c8a1679e35f442e8f1b229b33040e/orjson-3.13.0-cp315-cp315-manylinux_2_39_x86_64.whl", hash = "sha256:0526a3456db67b264c6d661b5f090077f326b6cd074d0ef53a72763595dec5d7", size = 130471, upload-time = "2026-10-07T14:09:17.463Z" }, + { url = "https://files.pythonhosted.org/packages/87/71/abdc2b8c70b8d85a6cb22f404da0f52d7d712f9d49cda039a0cb1adcb973/orjson-3.13.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:dd61e64802d51d1e4f16531c64536354fc3bc67932dc0cff254044f72bf0f187", size = 134793, upload-time = "2026-10-07T14:09:19.084Z" }, + { url = "https://files.pythonhosted.org/packages/0a/2e/1c13552d8b0241083116de02b2f284ee38501ef06ebfb79893f741538168/orjson-3.13.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c5e3ccaac3106e8fa6e2f2f6962449d7c757d7b067e41b395a19d6f0d6cec892", size = 126711, upload-time = "2026-10-07T14:09:20.645Z" }, + { url = "https://files.pythonhosted.org/packages/85/f8/d4ece953a519d064cf690adaa68cd389d5b64fd261726334841b32978d6a/orjson-3.13.0-cp315-cp315-win_amd64.whl", hash = "sha256:7804dd1d6161da0e53b284c2aebf20f23e78eaac617300803e1467d1828d987f", size = 121496, upload-time = "2026-10-07T14:09:22.359Z" }, + { url = "https://files.pythonhosted.org/packages/70/cf/f691388c4a9bc4af7dcc1648c4b40845869908b517d7c0009d005c7d1fa1/orjson-3.13.0-cp315-cp315-win_arm64.whl", hash = "sha256:f5c05a8fee59309f537590a1ff12d3c1009c485e96a50a9ac60dd085c09d0fc0", size = 126260, upload-time = "2026-10-07T14:09:23.928Z" }, ] [[package]] @@ -7315,6 +7657,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, { url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" }, { url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" }, { url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" }, @@ -7327,7 +7690,8 @@ name = "pillow" version = "13.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -7385,6 +7749,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/af/c30dd9ef6a3dce84ecb1b2393cac629047a32e66279056c6cb46c3ffd65c/pillow-13.0.0-cp314-cp314t-win32.whl", hash = "sha256:06903aac074d00c988e77a1fc435286dc498506d4ca8d6f69df9babddf4ecc15", size = 6517204, upload-time = "2026-10-16T22:17:03.722Z" }, { url = "https://files.pythonhosted.org/packages/85/d5/ef3a8ae7609c8ab26570abf18194882534b480d245a9148b895a2262c004/pillow-13.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9a635068d9793c7f0d929f39f18b58f1bd86c236e8e7b64167b86af9232ebca0", size = 7286695, upload-time = "2026-10-16T22:17:05.543Z" }, { url = "https://files.pythonhosted.org/packages/84/3c/0861db0c69a03689ebe3e7599d35668a414deb5aaafd8255215e4e62e3d3/pillow-13.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0d1a9cf7bc6917538b20f8d06b9ab2147e542f68849e96fc58eb66bdf6acc692", size = 2698098, upload-time = "2026-10-16T22:17:07.674Z" }, + { url = "https://files.pythonhosted.org/packages/6e/eb/e86a41962a3a31ad495a3d6a912d9002b01bba4a6fa71c28703abdca7f69/pillow-13.0.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7d5e097eabd5d4915c27ac9f6e9325bb1e01a7c94d7348b5c4451d3fc30e8521", size = 4191213, upload-time = "2026-10-16T22:17:09.449Z" }, + { url = "https://files.pythonhosted.org/packages/dd/04/7e7445e706afec84ef06dc25b2c77f9cecff242c23730c62928ad1089d4a/pillow-13.0.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:51ebb529fc5e8abdd55302f6c3f8943037348ec2384329a3e55677f69ae2fdfc", size = 4290650, upload-time = "2026-10-16T22:17:11.196Z" }, + { url = "https://files.pythonhosted.org/packages/46/85/d534089068ac40c0f61f650389a296c812eab745a8a733f81feec39ae955/pillow-13.0.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:c87a04e7f152ee4db5dbe15f0bfa3fe628a1aeedbe66b81c43b87f52c813f9ac", size = 3723078, upload-time = "2026-10-16T22:17:12.772Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2b/00b5c3b401df538b5dbe311d3899730fae5ba1e21cb8b80a128887ac6747/pillow-13.0.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:20722cd5375533ad754053363a80b8fb30c79d37930a6f7228030e237ce3bd5d", size = 5572292, upload-time = "2026-10-16T22:17:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/72/f5/a6b0610f946e9b54ae888ba7ad29d6e2f8d9b11f69e212d70416b7186c09/pillow-13.0.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:04a63720f79e22fd54d897c233a68314c50ca0603360f54ce2bd5db8f0f43782", size = 4852222, upload-time = "2026-10-16T22:17:16.521Z" }, + { url = "https://files.pythonhosted.org/packages/1e/d0/c0ff25bb0427ec70f32aac995453321fc3c4494a46c7292ff02d9d260b04/pillow-13.0.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f8db54177035a2b5f717f94475b74907e12a96dc74f6d487f3966b0a4694071", size = 6527349, upload-time = "2026-10-16T22:17:18.534Z" }, + { url = "https://files.pythonhosted.org/packages/85/11/e46fa0bcc0fc117f1eacc206b3d51f04d583fab328b4b740672d0438d83f/pillow-13.0.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f36aa77f74b14a2103622b196402c71330e4e7cf4122ee45c854e9a01e0eecf", size = 7152873, upload-time = "2026-10-16T22:17:20.415Z" }, + { url = "https://files.pythonhosted.org/packages/7c/53/2bdeca705f3a58679fa9201233193c86a30238340ca29b3a152c3fcc719b/pillow-13.0.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:149a1c323ba366f90b48e6f1a6db6582eb50512df374588de56fc909abc0f0f3", size = 6626871, upload-time = "2026-10-16T22:17:22.361Z" }, + { url = "https://files.pythonhosted.org/packages/14/de/3829030f209722ade3753bb456b805a0e7f885cf885b483407365c82e9ca/pillow-13.0.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e892354a424b1e82a78ba16785822bdd40de06f19446a3d4a28047df1a4f8932", size = 7236439, upload-time = "2026-10-16T22:17:24.352Z" }, + { url = "https://files.pythonhosted.org/packages/44/b1/67fc37388382bc036a74f04da5bb6fcbf90ec3ac1f45893e06176c373fd3/pillow-13.0.0-cp315-cp315-win32.whl", hash = "sha256:8e459b613cf31f8980f584387b4fe47bf08e076e288d61aa8eba1f7fd4ddbb81", size = 6508688, upload-time = "2026-10-16T22:17:26.314Z" }, + { url = "https://files.pythonhosted.org/packages/44/05/fdb6a79aee10861165526b41b7b440e36ee1ff58613b2fbefe3acbf06d4f/pillow-13.0.0-cp315-cp315-win_amd64.whl", hash = "sha256:d4737f50d23ddd11d6685faacbb9252887dde0bc7997127757247983d74399d6", size = 7281107, upload-time = "2026-10-16T22:17:28.198Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9c/6c6f7dc2a566467baf121394e83de25ac287cffe34cc030726beb4be0224/pillow-13.0.0-cp315-cp315-win_arm64.whl", hash = "sha256:2310e2adfc13c6d457d7d0f2ffdd5c56a74a317650cf765887df8d515a647ac0", size = 2695513, upload-time = "2026-10-16T22:17:30.013Z" }, + { url = "https://files.pythonhosted.org/packages/23/16/e9120defdb8bff693b582d035312acc6f530cdc2bc5238a87f410efccd82/pillow-13.0.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:21c32061a5512dc13f87cc7303e120e4d77c2f70df89a35af0e3ce246e5ac57f", size = 5578129, upload-time = "2026-10-16T22:17:31.642Z" }, + { url = "https://files.pythonhosted.org/packages/47/22/76faf2bb2448f48a84cf894c1e2bb1090d7d2b1aa521ece131cb2f05d631/pillow-13.0.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:1c774b9c0ba0d0b356f71530677b39028878a2cae5a93e0a13779cfd17120f88", size = 4857428, upload-time = "2026-10-16T22:17:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/b3/64/0861ea4e03c50b14af9ed9dd8b9f388ab42f445c623a9fcbab19aef7aa55/pillow-13.0.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20822140c9a2ba9c5e42df342b0a892ab2e102fc0e3db967a133bb9ce3f898bb", size = 6612582, upload-time = "2026-10-16T22:17:35.415Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/cd1e9b3f60a9f3d5ebe46da36955c26004092a501f7f2c42f9fcc26611bc/pillow-13.0.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffea6b2986a797c84b6d1d27d4f43e0b83c73fc16281a930891cd237cf0d08fe", size = 7230176, upload-time = "2026-10-16T22:17:37.362Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ef/ffb0a2fc85690714fb7ac662956949645d57f15bf4426f520da361e7b6e3/pillow-13.0.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:05cd2cd39055d0b9650060e4513ef4a9a0aea870083950bd046a0d6490f93a22", size = 6703640, upload-time = "2026-10-16T22:17:39.313Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c2/6402ee3ccb3363aeed8fdb3f9fe56703ac5377a539f0b81b53999d7e121f/pillow-13.0.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c6fd031333dedcad7ce3e03d244093f43504f209e86925ebdb2b868fa4037630", size = 7309231, upload-time = "2026-10-16T22:17:41.021Z" }, + { url = "https://files.pythonhosted.org/packages/fc/96/d6b82e8b8c06ad143b66b12e0b31051e4bf489b4fabca1f390a14fe1ebde/pillow-13.0.0-cp315-cp315t-win32.whl", hash = "sha256:c50c80e3591e5cf9a8dfa6007d4f10732f02e32263850999286e56ccd650cfb2", size = 6517200, upload-time = "2026-10-16T22:17:42.886Z" }, + { url = "https://files.pythonhosted.org/packages/60/d4/0062e6005e9b270b165015aafd343364ac378ec5a685431ea42baad33357/pillow-13.0.0-cp315-cp315t-win_amd64.whl", hash = "sha256:adb3c2124c68e480f4632d06cb7caa3b48a51678475f483b4edb1c6dee29ea43", size = 7287488, upload-time = "2026-10-16T22:17:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/e4/81/b8eb349746be7bce47dd95337c6634e84910af91b59aaa9159c034c614fa/pillow-13.0.0-cp315-cp315t-win_arm64.whl", hash = "sha256:4a25756244bdcce3b50580baadf320fa48297f0eeb8e114ba64139a40d4d0fc8", size = 2698073, upload-time = "2026-10-16T22:17:47.556Z" }, { url = "https://files.pythonhosted.org/packages/68/c0/fb7b1caf25ed5ac5d46116f09b8b0706b9559833c65c667cecb1a53153f2/pillow-13.0.0-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:405be6058e206a86fedce2f715ecc2343f94072d7925251632dffa2c05f70dca", size = 5521630, upload-time = "2026-10-16T22:17:50.008Z" }, { url = "https://files.pythonhosted.org/packages/1a/e6/4d11d5b1137f4fc080605cce410cfaae4f303ad400bdec75d27d3083fa29/pillow-13.0.0-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:b84be41387fc00db28c13c02e30d42b02d8d8f6a4a741a1aee6012ca7f1f6039", size = 4806023, upload-time = "2026-10-16T22:17:51.932Z" }, { url = "https://files.pythonhosted.org/packages/f1/45/0923202a658e8ff6aafd72c1a4395ebadcdebaa182c437d8595d3dd3db32/pillow-13.0.0-pp311-pypy311_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a192360d512305fe92d3053e4f7627bbad1f7708f1f4ad38cccd9336acabe9ad", size = 5464831, upload-time = "2026-10-16T22:17:53.742Z" }, @@ -7509,10 +7894,12 @@ dependencies = [ { name = "httpx" }, { name = "jinja2" }, { name = "nodeenv" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "python-dotenv" }, { name = "tomlkit" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2b/62/23a2638aa14d8eefd16bc4b230a6e65d8de5f69bfaeeaf954654c923f02e/prisma-0.11.0.tar.gz", hash = "sha256:3f2f2fd2361e1ec5ff655f2a04c7860c2f2a5bc4c91f78ca9c5c6349735bf693", size = 94181, upload-time = "2023-10-22T22:59:55.817Z" } wheels = [ @@ -7724,77 +8111,88 @@ wheels = [ [[package]] name = "psycopg" -version = "3.3.3" +version = "3.3.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/b6/379d0a960f8f435ec78720462fd94c4863e7a31237cf81bf76d0af5883bf/psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9", size = 165624, upload-time = "2026-02-18T16:52:16.546Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/26/3ea4ca5eaea1c0debcdf7ee7c1613fbe721dc27a03c461c0817ffd8a0601/psycopg-3.3.6.tar.gz", hash = "sha256:c081f2250df751a943036e42db6df4571c66cd0aabe8291a7a506512b12007d2", size = 168171, upload-time = "2026-09-18T13:22:55.152Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/5b/181e2e3becb7672b502f0ed7f16ed7352aca7c109cfb94cf3878a9186db9/psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698", size = 212768, upload-time = "2026-02-18T16:46:27.365Z" }, + { url = "https://files.pythonhosted.org/packages/4e/de/748bd7609c71cae5d737f0ba9192f19329f70180ecda8fff3cac02c5abe3/psycopg-3.3.6-py3-none-any.whl", hash = "sha256:a1db9f7148b06a28606767efaca51fa6f9398c5c0a3810519be69d7000bdb631", size = 215490, upload-time = "2026-09-18T13:15:29.374Z" }, ] [[package]] name = "psycopg-binary" -version = "3.3.3" +version = "3.3.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/d8/a763308a41e2ecfb6256ba0877d340c2f2b124c8b2746401863d96fa2c7a/psycopg_binary-3.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b3385b58b2fe408a13d084c14b8dcf468cd36cbbe774408250facc128f9fa75c", size = 4609758, upload-time = "2026-02-18T16:46:33.132Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a9/f8a683e85400c1208685e7c895abc049dc13aa0b6ea989e6adf0a3681fe0/psycopg_binary-3.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1bef235a50a80f6aba05147002bc354559657cb6386dbd04d8e1c97d1d7cbe84", size = 4676740, upload-time = "2026-02-18T16:46:42.904Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7d/03512c4aaac8a58fc3b1221f38293aa517a1950d10ef8646c72c49addc7d/psycopg_binary-3.3.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:97c839717bf8c8df3f6d983a20949c4fb22e2a34ee172e3e427ede363feda27b", size = 5496335, upload-time = "2026-02-18T16:46:51.517Z" }, - { url = "https://files.pythonhosted.org/packages/8a/bc/23319b4b1c2c0b810d225e1b6f16efbb16150074fc0ea96bfcabdf59ee09/psycopg_binary-3.3.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:48e500cf1c0984dacf1f28ea482c3cdbb4c2288d51c336c04bc64198ab21fc51", size = 5172032, upload-time = "2026-02-18T16:47:00.878Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c8/6d61dc0a56654c558a37b2d9b2094e470aa12621305cc7935fd769122e32/psycopg_binary-3.3.3-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb36a08859b9432d94ea6b26ec41a2f98f83f14868c91321d0c1e11f672eeae7", size = 6763107, upload-time = "2026-02-18T16:47:11.784Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b5/e2a3c90aa1059f5b5f593379caad7be3cc3c2ce1ddfc7730e39854e174fe/psycopg_binary-3.3.3-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dde92cfde09293fb63b3f547919ba7d73bd2654573c03502b3263dd0218e44e", size = 5006494, upload-time = "2026-02-18T16:47:17.062Z" }, - { url = "https://files.pythonhosted.org/packages/5d/3e/bf126e0a1f864e191b7f3eeea667ee2ce13d582b036255fb8b12946d1f7a/psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78c9ce98caaf82ac8484d269791c1b403d7598633e0e4e2fa1097baae244e2f1", size = 4533850, upload-time = "2026-02-18T16:47:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d8/bb5e8d395deb945629aa0c65d12ab90ec3bfcbdf56be89e2a84d001864c9/psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d593612758d0041cb13cb0003f7f8d3fabb7ad9319e651e78afae49b1cf5860e", size = 4223316, upload-time = "2026-02-18T16:47:25.82Z" }, - { url = "https://files.pythonhosted.org/packages/c2/70/33eef61b0f0fd41ebf93b9699f44067313a45016827f67b3c8cc41f0a7ab/psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f24e8e17035200a465c178e9ea945527ad0738118694184c450f1192a452ff25", size = 3954515, upload-time = "2026-02-18T16:47:30.434Z" }, - { url = "https://files.pythonhosted.org/packages/ea/db/27c2b3b9698e713e83e11e8540daa27516f9e90390ec21a41091cb15fcaf/psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e7b607f0e14f2a4cf7e78a05ebd13df6144acfba87cb90842e70d3f125d9f53f", size = 4260274, upload-time = "2026-02-18T16:47:36.128Z" }, - { url = "https://files.pythonhosted.org/packages/a1/3b/71e5d603059bf5474215f573a3e2d357a4e95672b26e04d41674400d4862/psycopg_binary-3.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b27d3a23c79fa59557d2cc63a7e8bb4c7e022c018558eda36f9d7c4e6b99a6e0", size = 3557375, upload-time = "2026-02-18T16:47:42.799Z" }, - { url = "https://files.pythonhosted.org/packages/be/c0/b389119dd754483d316805260f3e73cdcad97925839107cc7a296f6132b1/psycopg_binary-3.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a89bb9ee11177b2995d87186b1d9fa892d8ea725e85eab28c6525e4cc14ee048", size = 4609740, upload-time = "2026-02-18T16:47:51.093Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9976eef20f61840285174d360da4c820a311ab39d6b82fa09fbb545be825/psycopg_binary-3.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f7d0cf072c6fbac3795b08c98ef9ea013f11db609659dcfc6b1f6cc31f9e181", size = 4676837, upload-time = "2026-02-18T16:47:55.523Z" }, - { url = "https://files.pythonhosted.org/packages/9f/f2/d28ba2f7404fd7f68d41e8a11df86313bd646258244cb12a8dd83b868a97/psycopg_binary-3.3.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:90eecd93073922f085967f3ed3a98ba8c325cbbc8c1a204e300282abd2369e13", size = 5497070, upload-time = "2026-02-18T16:47:59.929Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/6c5c54b815edeb30a281cfcea96dc93b3bb6be939aea022f00cab7aa1420/psycopg_binary-3.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dac7ee2f88b4d7bb12837989ca354c38d400eeb21bce3b73dac02622f0a3c8d6", size = 5172410, upload-time = "2026-02-18T16:48:05.665Z" }, - { url = "https://files.pythonhosted.org/packages/51/75/8206c7008b57de03c1ada46bd3110cc3743f3fd9ed52031c4601401d766d/psycopg_binary-3.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62cf8784eb6d35beaee1056d54caf94ec6ecf2b7552395e305518ab61eb8fd2", size = 6763408, upload-time = "2026-02-18T16:48:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/d4/5a/ea1641a1e6c8c8b3454b0fcb43c3045133a8b703e6e824fae134088e63bd/psycopg_binary-3.3.3-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a39f34c9b18e8f6794cca17bfbcd64572ca2482318db644268049f8c738f35a6", size = 5006255, upload-time = "2026-02-18T16:48:22.176Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fb/538df099bf55ae1637d52d7ccb6b9620b535a40f4c733897ac2b7bb9e14c/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:883d68d48ca9ff3cb3d10c5fdebea02c79b48eecacdddbf7cce6e7cdbdc216b8", size = 4532694, upload-time = "2026-02-18T16:48:27.338Z" }, - { url = "https://files.pythonhosted.org/packages/a1/d1/00780c0e187ea3c13dfc53bd7060654b2232cd30df562aac91a5f1c545ac/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:cab7bc3d288d37a80aa8c0820033250c95e40b1c2b5c57cf59827b19c2a8b69d", size = 4222833, upload-time = "2026-02-18T16:48:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/7a/34/a07f1ff713c51d64dc9f19f2c32be80299a2055d5d109d5853662b922cb4/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:56c767007ca959ca32f796b42379fc7e1ae2ed085d29f20b05b3fc394f3715cc", size = 3952818, upload-time = "2026-02-18T16:48:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/d33f268a7759b4445f3c9b5a181039b01af8c8263c865c1be7a6444d4749/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:da2f331a01af232259a21573a01338530c6016dcfad74626c01330535bcd8628", size = 4258061, upload-time = "2026-02-18T16:48:41.365Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3b/0d8d2c5e8e29ccc07d28c8af38445d9d9abcd238d590186cac82ee71fc84/psycopg_binary-3.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:19f93235ece6dbfc4036b5e4f6d8b13f0b8f2b3eeb8b0bd2936d406991bcdd40", size = 3558915, upload-time = "2026-02-18T16:48:46.679Z" }, - { url = "https://files.pythonhosted.org/packages/90/15/021be5c0cbc5b7c1ab46e91cc3434eb42569f79a0592e67b8d25e66d844d/psycopg_binary-3.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6698dbab5bcef8fdb570fc9d35fd9ac52041771bfcfe6fd0fc5f5c4e36f1e99d", size = 4591170, upload-time = "2026-02-18T16:48:55.594Z" }, - { url = "https://files.pythonhosted.org/packages/f1/54/a60211c346c9a2f8c6b272b5f2bbe21f6e11800ce7f61e99ba75cf8b63e1/psycopg_binary-3.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:329ff393441e75f10b673ae99ab45276887993d49e65f141da20d915c05aafd8", size = 4670009, upload-time = "2026-02-18T16:49:03.608Z" }, - { url = "https://files.pythonhosted.org/packages/c1/53/ac7c18671347c553362aadbf65f92786eef9540676ca24114cc02f5be405/psycopg_binary-3.3.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:eb072949b8ebf4082ae24289a2b0fd724da9adc8f22743409d6fd718ddb379df", size = 5469735, upload-time = "2026-02-18T16:49:10.128Z" }, - { url = "https://files.pythonhosted.org/packages/7f/c3/4f4e040902b82a344eff1c736cde2f2720f127fe939c7e7565706f96dd44/psycopg_binary-3.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:263a24f39f26e19ed7fc982d7859a36f17841b05bebad3eb47bb9cd2dd785351", size = 5152919, upload-time = "2026-02-18T16:49:16.335Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e7/d929679c6a5c212bcf738806c7c89f5b3d0919f2e1685a0e08d6ff877945/psycopg_binary-3.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5152d50798c2fa5bd9b68ec68eb68a1b71b95126c1d70adaa1a08cd5eefdc23d", size = 6738785, upload-time = "2026-02-18T16:49:22.687Z" }, - { url = "https://files.pythonhosted.org/packages/69/b0/09703aeb69a9443d232d7b5318d58742e8ca51ff79f90ffe6b88f1db45e7/psycopg_binary-3.3.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d6a1e56dd267848edb824dbeb08cf5bac649e02ee0b03ba883ba3f4f0bd54f2", size = 4979008, upload-time = "2026-02-18T16:49:27.313Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a6/e662558b793c6e13a7473b970fee327d635270e41eded3090ef14045a6a5/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73eaaf4bb04709f545606c1db2f65f4000e8a04cdbf3e00d165a23004692093e", size = 4508255, upload-time = "2026-02-18T16:49:31.575Z" }, - { url = "https://files.pythonhosted.org/packages/5f/7f/0f8b2e1d5e0093921b6f324a948a5c740c1447fbb45e97acaf50241d0f39/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:162e5675efb4704192411eaf8e00d07f7960b679cd3306e7efb120bb8d9456cc", size = 4189166, upload-time = "2026-02-18T16:49:35.801Z" }, - { url = "https://files.pythonhosted.org/packages/92/ec/ce2e91c33bc8d10b00c87e2f6b0fb570641a6a60042d6a9ae35658a3a797/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:fab6b5e37715885c69f5d091f6ff229be71e235f272ebaa35158d5a46fd548a0", size = 3924544, upload-time = "2026-02-18T16:49:41.129Z" }, - { url = "https://files.pythonhosted.org/packages/c5/2f/7718141485f73a924205af60041c392938852aa447a94c8cbd222ff389a1/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a4aab31bd6d1057f287c96c0effca3a25584eb9cc702f282ecb96ded7814e830", size = 4235297, upload-time = "2026-02-18T16:49:46.726Z" }, - { url = "https://files.pythonhosted.org/packages/57/f9/1add717e2643a003bbde31b1b220172e64fbc0cb09f06429820c9173f7fc/psycopg_binary-3.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:59aa31fe11a0e1d1bcc2ce37ed35fe2ac84cd65bb9036d049b1a1c39064d0f14", size = 3547659, upload-time = "2026-02-18T16:49:52.999Z" }, - { url = "https://files.pythonhosted.org/packages/03/0a/cac9fdf1df16a269ba0e5f0f06cac61f826c94cadb39df028cdfe19d3a33/psycopg_binary-3.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05f32239aec25c5fb15f7948cffdc2dc0dac098e48b80a140e4ba32b572a2e7d", size = 4590414, upload-time = "2026-02-18T16:50:01.441Z" }, - { url = "https://files.pythonhosted.org/packages/9c/c0/d8f8508fbf440edbc0099b1abff33003cd80c9e66eb3a1e78834e3fb4fb9/psycopg_binary-3.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c84f9d214f2d1de2fafebc17fa68ac3f6561a59e291553dfc45ad299f4898c1", size = 4669021, upload-time = "2026-02-18T16:50:08.803Z" }, - { url = "https://files.pythonhosted.org/packages/04/05/097016b77e343b4568feddf12c72171fc513acef9a4214d21b9478569068/psycopg_binary-3.3.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e77957d2ba17cada11be09a5066d93026cdb61ada7c8893101d7fe1c6e1f3925", size = 5467453, upload-time = "2026-02-18T16:50:14.985Z" }, - { url = "https://files.pythonhosted.org/packages/91/23/73244e5feb55b5ca109cede6e97f32ef45189f0fdac4c80d75c99862729d/psycopg_binary-3.3.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:42961609ac07c232a427da7c87a468d3c82fee6762c220f38e37cfdacb2b178d", size = 5151135, upload-time = "2026-02-18T16:50:24.82Z" }, - { url = "https://files.pythonhosted.org/packages/11/49/5309473b9803b207682095201d8708bbc7842ddf3f192488a69204e36455/psycopg_binary-3.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae07a3114313dd91fce686cab2f4c44af094398519af0e0f854bc707e1aeedf1", size = 6737315, upload-time = "2026-02-18T16:50:35.106Z" }, - { url = "https://files.pythonhosted.org/packages/d4/5d/03abe74ef34d460b33c4d9662bf6ec1dd38888324323c1a1752133c10377/psycopg_binary-3.3.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d257c58d7b36a621dcce1d01476ad8b60f12d80eb1406aee4cf796f88b2ae482", size = 4979783, upload-time = "2026-02-18T16:50:42.067Z" }, - { url = "https://files.pythonhosted.org/packages/f0/6c/3fbf8e604e15f2f3752900434046c00c90bb8764305a1b81112bff30ba24/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07c7211f9327d522c9c47560cae00a4ecf6687f4e02d779d035dd3177b41cb12", size = 4509023, upload-time = "2026-02-18T16:50:50.116Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6b/1a06b43b7c7af756c80b67eac8bfaa51d77e68635a8a8d246e4f0bb7604a/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8e7e9eca9b363dbedeceeadd8be97149d2499081f3c52d141d7cd1f395a91f83", size = 4185874, upload-time = "2026-02-18T16:50:55.97Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d3/bf49e3dcaadba510170c8d111e5e69e5ae3f981c1554c5bb71c75ce354bb/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:cb85b1d5702877c16f28d7b92ba030c1f49ebcc9b87d03d8c10bf45a2f1c7508", size = 3925668, upload-time = "2026-02-18T16:51:03.299Z" }, - { url = "https://files.pythonhosted.org/packages/f8/92/0aac830ed6a944fe334404e1687a074e4215630725753f0e3e9a9a595b62/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d4606c84d04b80f9138d72f1e28c6c02dc5ae0c7b8f3f8aaf89c681ce1cd1b1", size = 4234973, upload-time = "2026-02-18T16:51:09.097Z" }, - { url = "https://files.pythonhosted.org/packages/2e/96/102244653ee5a143ece5afe33f00f52fe64e389dfce8dbc87580c6d70d3d/psycopg_binary-3.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:74eae563166ebf74e8d950ff359be037b85723d99ca83f57d9b244a871d6c13b", size = 3551342, upload-time = "2026-02-18T16:51:13.892Z" }, - { url = "https://files.pythonhosted.org/packages/a2/71/7a57e5b12275fe7e7d84d54113f0226080423a869118419c9106c083a21c/psycopg_binary-3.3.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:497852c5eaf1f0c2d88ab74a64a8097c099deac0c71de1cbcf18659a8a04a4b2", size = 4607368, upload-time = "2026-02-18T16:51:19.295Z" }, - { url = "https://files.pythonhosted.org/packages/c7/04/cb834f120f2b2c10d4003515ef9ca9d688115b9431735e3936ae48549af8/psycopg_binary-3.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:258d1ea53464d29768bf25930f43291949f4c7becc706f6e220c515a63a24edd", size = 4687047, upload-time = "2026-02-18T16:51:23.84Z" }, - { url = "https://files.pythonhosted.org/packages/40/e9/47a69692d3da9704468041aa5ed3ad6fc7f6bb1a5ae788d261a26bbca6c7/psycopg_binary-3.3.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:111c59897a452196116db12e7f608da472fbff000693a21040e35fc978b23430", size = 5487096, upload-time = "2026-02-18T16:51:29.645Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b6/0e0dd6a2f802864a4ae3dbadf4ec620f05e3904c7842b326aafc43e5f464/psycopg_binary-3.3.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:17bb6600e2455993946385249a3c3d0af52cd70c1c1cdbf712e9d696d0b0bf1b", size = 5168720, upload-time = "2026-02-18T16:51:36.499Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0d/977af38ac19a6b55d22dff508bd743fd7c1901e1b73657e7937c7cccb0a3/psycopg_binary-3.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:642050398583d61c9856210568eb09a8e4f2fe8224bf3be21b67a370e677eead", size = 6762076, upload-time = "2026-02-18T16:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/34/40/912a39d48322cf86895c0eaf2d5b95cb899402443faefd4b09abbba6b6e1/psycopg_binary-3.3.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:533efe6dc3a7cba5e2a84e38970786bb966306863e45f3db152007e9f48638a6", size = 4997623, upload-time = "2026-02-18T16:51:47.707Z" }, - { url = "https://files.pythonhosted.org/packages/98/0c/c14d0e259c65dc7be854d926993f151077887391d5a081118907a9d89603/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5958dbf28b77ce2033482f6cb9ef04d43f5d8f4b7636e6963d5626f000efb23e", size = 4532096, upload-time = "2026-02-18T16:51:51.421Z" }, - { url = "https://files.pythonhosted.org/packages/39/21/8b7c50a194cfca6ea0fd4d1f276158307785775426e90700ab2eba5cd623/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a6af77b6626ce92b5817bf294b4d45ec1a6161dba80fc2d82cdffdd6814fd023", size = 4208884, upload-time = "2026-02-18T16:51:57.336Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2c/a4981bf42cf30ebba0424971d7ce70a222ae9b82594c42fc3f2105d7b525/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:47f06fcbe8542b4d96d7392c476a74ada521c5aebdb41c3c0155f6595fc14c8d", size = 3944542, upload-time = "2026-02-18T16:52:04.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/e9/b7c29b56aa0b85a4e0c4d89db691c1ceef08f46a356369144430c155a2f5/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7800e6c6b5dc4b0ca7cc7370f770f53ac83886b76afda0848065a674231e856", size = 4254339, upload-time = "2026-02-18T16:52:10.444Z" }, - { url = "https://files.pythonhosted.org/packages/98/5a/291d89f44d3820fffb7a04ebc8f3ef5dda4f542f44a5daea0c55a84abf45/psycopg_binary-3.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:165f22ab5a9513a3d7425ffb7fcc7955ed8ccaeef6d37e369d6cc1dff1582383", size = 3652796, upload-time = "2026-02-18T16:52:14.02Z" }, + { url = "https://files.pythonhosted.org/packages/52/92/00350a66de0af05e41d01aa3134e3970045e816afed3f99d58ec1abe15b2/psycopg_binary-3.3.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7beb3e41c9a1e509f3ed85263386588cbe3e975aa67be21f79f44fd35ffaeefc", size = 4728682, upload-time = "2026-09-18T13:15:36.605Z" }, + { url = "https://files.pythonhosted.org/packages/91/fc/afa9c7fd316a469af7ede6ebb020eac482f5d827fae57d5310c9bc0c41ae/psycopg_binary-3.3.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa73160077345ec21b3f51e8e24b3de2e99586217e497629326eb9b2ea88c52e", size = 4777111, upload-time = "2026-09-18T13:15:46.566Z" }, + { url = "https://files.pythonhosted.org/packages/f2/44/7c1e015f1bc56b36ff1369f09e852b2d83ccefd5a669a42633a916cdedc4/psycopg_binary-3.3.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f87dbdc42e78ee0f7ea180c03f8c78e80a949e373066629bd90fefff10552dff", size = 5592755, upload-time = "2026-09-18T13:15:52.886Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ae/314a251ca918cdac380bce1b87839ade9355382ea749e6ef3ba75ba0c09f/psycopg_binary-3.3.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a9348c5b43a3bb5ef8c2e89d5237c9c87eeafb01d338c84a7aebbc5cd0313299", size = 5267949, upload-time = "2026-09-18T13:16:00.53Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9f/3bb0cfe9bb0f31ca57cf486ddc8c9ac51251aed8181bf88ff870b2623105/psycopg_binary-3.3.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a52991594ac4db888c7d39bccef331797e30cb31a95cae02cf2607f83a42dc2", size = 6862403, upload-time = "2026-09-18T13:16:10.385Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d6/7032c10309c3155e9b24300fdcc9a1afa539cfd20ce52fdef74a46f10161/psycopg_binary-3.3.6-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ea8beeb5541780b4b50b462eeacbc4f594ce3b911dc20c81c75f267876f71d2", size = 5107271, upload-time = "2026-09-18T13:16:16.843Z" }, + { url = "https://files.pythonhosted.org/packages/61/cc/79add2cf92684cf1a81da134b32caa662c25c72d0cc905d181ef4455f834/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:198a48e68cc99ccac03ba95ac857e73aa66f3bf6be77019fafb0832a05f7ad03", size = 4627984, upload-time = "2026-09-18T13:16:23.889Z" }, + { url = "https://files.pythonhosted.org/packages/c9/48/6dfb14f9350c14af6a2edb3c31262051b8cd94e2186e4b831e46dbbe8cd9/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fa34eb47969297471db7b7f193622c7e3ee839ec05abd05f1fe104d5b1b1dcf4", size = 4322054, upload-time = "2026-09-18T13:16:29.33Z" }, + { url = "https://files.pythonhosted.org/packages/29/35/2982338716a91cbb4dfc866be015be4457ee8106a445aabf3d1fb6a270e0/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b979a42815410432420275412633960807178b1ce26591a16ce06e78a5bd4bb2", size = 4048471, upload-time = "2026-09-18T13:16:34.119Z" }, + { url = "https://files.pythonhosted.org/packages/24/e1/171b1db1542c5f76a678b7ee0a7800bebc9735a0a03417c76cf948bfd63c/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:889e42acec10450185e0cdfb396f375e2c1a8d7737c114830a7fde4654f59e30", size = 4353931, upload-time = "2026-09-18T13:16:38.692Z" }, + { url = "https://files.pythonhosted.org/packages/08/89/4424e62a944eef40bd9326ada4ae23802b28eab6502af91e84ef7bba74fb/psycopg_binary-3.3.6-cp310-cp310-win_amd64.whl", hash = "sha256:cbd5f73073ed19c378d4c35499db1e3e703a5b1a324e521204065967bfaa7a18", size = 3674362, upload-time = "2026-09-18T13:16:44.454Z" }, + { url = "https://files.pythonhosted.org/packages/70/86/b71166048974d49c6d136b2ed1c0e5bec0b974d8c4de5cbce7e86a9e412a/psycopg_binary-3.3.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:be4f9b3c9338ac5dd217c5847e21521b396c8117f78dc420d495a5c49bbef874", size = 4728002, upload-time = "2026-09-18T13:16:53.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/1d/1e06c0de7ed5aed898acb87544eac6ef0bc7d752a67ec6e5d6b835e9b40c/psycopg_binary-3.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0535693ce476a722b718b002d5d2c27d47e71ca945276ac194409c98e74c492", size = 4775403, upload-time = "2026-09-18T13:16:58.939Z" }, + { url = "https://files.pythonhosted.org/packages/84/02/2ffcbc43f8e4bbc38e5286a22013bcac01898d13cd38325f60dd5428a8af/psycopg_binary-3.3.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3c9e663b2e800e3218994cf948c11bcc2844e6491b34aa80d089baf6531827bf", size = 5594145, upload-time = "2026-09-18T13:17:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/e1/25/031dae2c7d2e7e77dcf5b1962c1e0684fa548d7af0ff6707b6b5e6054ca7/psycopg_binary-3.3.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a2e44a342d2aee40508e28a563d8961c39d9bbd8cae36d8578f0a3c6658aab0f", size = 5267985, upload-time = "2026-09-18T13:17:16.24Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/94c89ada3c003a4d858178f3bba49a35e0297ef2aad659b80eb5e380e690/psycopg_binary-3.3.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f598f19fa9a91540b5cee17932ffd227b7b53a481605bcc4573c0eafa647300", size = 6860986, upload-time = "2026-09-18T13:17:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a0/81bf499d095adee8413bd19822a6872fbfa21663ec78014a68d83a8db83c/psycopg_binary-3.3.6-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ff05561e4a067d35507dc5c90f1deb2ec1c9703ac5cccc1bc26e08a197f9c5a", size = 5106702, upload-time = "2026-09-18T13:17:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/00/75/99d56da64c27bd985fd82c6ecbf7976b724ac638fdd1654ef995323a1a26/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:566dd827f17728efdf7d88a5b066f815170f6fdad13967ae952842d90e6aaa9f", size = 4627090, upload-time = "2026-09-18T13:17:36.668Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0c/0222171d11233332c6a24b1cef1578215f0ffddf3642eb8dd8c4448ad69f/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9b2f11794e017ce340934e35de46181c46ef71ec75ea3d85dd75cd836761c01e", size = 4320353, upload-time = "2026-09-18T13:17:42.526Z" }, + { url = "https://files.pythonhosted.org/packages/62/6f/e1cc2a28dd1228c67c969ba6fd37cd8726b312e2ff51380f847ddb38ccde/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:910ace140e3e7b7596898d083f37a8fe90c5c40684252ad4e682364b2cd3deba", size = 4047253, upload-time = "2026-09-18T13:17:47.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fd/38b64790ce7a515b1dbd2bab3d119637a858aeb22c380cf4859bc4ce0e42/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37e517c146b185f9c0c6e8d0a0ebbdeeeb67896af28466e032bc810d0c7dc7a7", size = 4353208, upload-time = "2026-09-18T13:17:52.41Z" }, + { url = "https://files.pythonhosted.org/packages/f7/dc/45386530ceb2a8c789a226de9b9b34eca8fccf1feba2e4ef68a6aca50c56/psycopg_binary-3.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:c7f92daa0d2a1c76f07264abddf8cbabd30152a2f09c3270e50f0c7efdf5dcac", size = 3675638, upload-time = "2026-09-18T13:17:58.112Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/2cdd1824e58b4467ee0b9498664cd28c42d8794db6b1e35b6bcb834f0044/psycopg_binary-3.3.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3f84dab25e0385692ee13274c68678377e0b1a70ab9d14e56264cbf61f60c62d", size = 4707086, upload-time = "2026-09-18T13:18:05.138Z" }, + { url = "https://files.pythonhosted.org/packages/f6/76/de9948ac06895261c84d5b9fbe283d8f3c5bc9f070691b8d9eaa1b51e322/psycopg_binary-3.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:612382ac3ed13651c7fa44b5fee9fbf7baaa2ddbc6f500391672682c5f1df9e0", size = 4769607, upload-time = "2026-09-18T13:18:12.83Z" }, + { url = "https://files.pythonhosted.org/packages/76/a9/72436c9915ee4905964689e7f0e182ce7767cc0a0390b3ce703be8177625/psycopg_binary-3.3.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:366db6e97e66b37211475f20c4c1324a2dc0dd825e46d4e87f9d599304d276f9", size = 5554134, upload-time = "2026-09-18T13:18:21.175Z" }, + { url = "https://files.pythonhosted.org/packages/0a/42/948bb3d2617795093512613fd96ba380e922992c7908fbc073858147d196/psycopg_binary-3.3.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1679a1cb93fbe5a6d1fd58d82cbddcc6fcb8c61446ba7cae6eb2a7b19bc585de", size = 5235723, upload-time = "2026-09-18T13:18:27.071Z" }, + { url = "https://files.pythonhosted.org/packages/99/47/93e823ff1b0088400703410939c9bda3e63ed9c850b3ee088e8769f4c10b/psycopg_binary-3.3.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37d40450659401600e6d043ff586c89a71a69f33cbb8bcdba6cdb2569beecdbe", size = 6833587, upload-time = "2026-09-18T13:18:33.794Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2d/ecc69c847795aa704041a9f5667a6b0938a088cf1853636d762a6938e493/psycopg_binary-3.3.6-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a5165300324efd5a772c48a88ab3a928513ab3979fca76553e62ee815f7b2b9c", size = 5070013, upload-time = "2026-09-18T13:18:39.628Z" }, + { url = "https://files.pythonhosted.org/packages/92/36/6126f0dac21713dcae91404f2a76da18598a6252339a8c669c46370d43b2/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d636338c8f21b0df2f84657b00bc34f9313f826ef93f1155bc743607e4a0c5eb", size = 4597367, upload-time = "2026-09-18T13:18:45.023Z" }, + { url = "https://files.pythonhosted.org/packages/4d/29/7ecfc04243b46c89ffd49924e9c5634ea904ef96c7d0f37e4073623584c1/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a4ee3bdd5468a725f2a4d9aab8a74b6d0279f768c8b5d3aeb102c5307ff3d59c", size = 4275419, upload-time = "2026-09-18T13:18:49.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/90/2f46d2e0de79706ac170df0a3637fe63c4498fc04f131f6049520b78b806/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:289aadd6a00e151203c081f708348ec89f1e483c9b510ef4ac3981f847f01f79", size = 4007358, upload-time = "2026-09-18T13:18:53.944Z" }, + { url = "https://files.pythonhosted.org/packages/03/48/6744e91291b751a8cf12d63d719977974bb94c84ceba913e7ddb2e478e51/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f21d057f3e5f5491067e5b292498073b73847d48799b099803fef100775fcc52", size = 4320156, upload-time = "2026-09-18T13:18:59.258Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9b/94ff7fce53a64d5b286e2ec454e0a025cf3d6e6b4a9189bef16aa5de98b2/psycopg_binary-3.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:e23a66a763fbe83fcc210bc77c27e5a5ea380ebf091c06f34d8561b695e5a40f", size = 3658864, upload-time = "2026-09-18T13:19:06.503Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c3/c072584b69ad44a747b448cfc9766fecb8aae56e372a017e2ef668790057/psycopg_binary-3.3.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ad8f35e67cc16d1fad1fa8c88972dc9b3a3141ea67897399904edab96a301b6", size = 4712284, upload-time = "2026-09-18T13:19:13.451Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b9/4283b785339e8e2318d03048994b093d650ea6289fabaa806b765dc0d449/psycopg_binary-3.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373704aea331d3f3e3402c125a1543f5875e2986ebb54f97d1647942161f803f", size = 4772031, upload-time = "2026-09-18T13:19:18.524Z" }, + { url = "https://files.pythonhosted.org/packages/6f/72/7a1321d359246769fff1affffbd0132785a28f7f63c18524c15a502398f4/psycopg_binary-3.3.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b82491019b884d62318b5f30706c3d7e6d4e5a6cb7eabcb3edc0c1b0fdaceae9", size = 5556392, upload-time = "2026-09-18T13:19:24.418Z" }, + { url = "https://files.pythonhosted.org/packages/de/b0/c6f8a0585a5dacbea74e130bcfc66629390e8f5bbc79d2a8e806e8952150/psycopg_binary-3.3.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cec5ea900390897d0b46130f60bc2883bf19c314f9044235217c8be88b0ef269", size = 5237855, upload-time = "2026-09-18T13:19:31.257Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fc/c3a7a8bbef7e945ec584ac61d460a612363ea398511cd0e220242b1d69f1/psycopg_binary-3.3.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98c02090d88f2ebc0ec1e8da538f77d225ce0fffecf372aa39262e62a1b054ef", size = 6833856, upload-time = "2026-09-18T13:19:43.622Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f2/8e80b921db728ebb68fc105bd7c4277f908210ad755bd6481d5ea7add740/psycopg_binary-3.3.6-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ee2c4728c691245e24501fcd7a97b5b381236b9985bc445bba88cdce7d1b5784", size = 5070730, upload-time = "2026-09-18T13:19:49.968Z" }, + { url = "https://files.pythonhosted.org/packages/54/6a/5b313e0c5348244f0e973aff3258bf86766656256d5ece8d541a53e35b4a/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f19cc87343eaa55255e76b31259a570072ac95d6ae82c92dd34b97691f5e49dc", size = 4598089, upload-time = "2026-09-18T13:19:56.426Z" }, + { url = "https://files.pythonhosted.org/packages/32/e9/db7f76ec24bf6699e92bf604e5c4bae10664a681a8999ef42aa0faf0f2c6/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdccb3a0e184b03e9baa673b15a809cf36c339c85dbda0ebc25a698846dfbee8", size = 4278481, upload-time = "2026-09-18T13:20:04.681Z" }, + { url = "https://files.pythonhosted.org/packages/61/83/72c67013656f4d6b547caabffb193e91d57e63f90eefdcc6d045c400e97d/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9892188bb15e5803beb51afe8a25add6b56be391a53058e8bca03b74e1e6bf22", size = 4009229, upload-time = "2026-09-18T13:20:11.905Z" }, + { url = "https://files.pythonhosted.org/packages/82/35/5e4500df2c999eb0faed8b184e6958b834172128274f06167a5deef4c19c/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3af90f92769d8cc10f94515ee7a0aef36ea85ca733a0ce22858f6e0953f41138", size = 4321467, upload-time = "2026-09-18T13:20:17.949Z" }, + { url = "https://files.pythonhosted.org/packages/55/7f/e350e1cf498ba2565c3f87b12f429d2012eb86b76c2b3845a19ee5fbb4d6/psycopg_binary-3.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:0ebfad5d131de9f892ae9e70cc7616207768b6714b66a52d4612b8ceaf78b372", size = 3658179, upload-time = "2026-09-18T13:20:22.691Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b9/60711317c284a442511644ea7185b56ebe627606d6741e732cd16108c47b/psycopg_binary-3.3.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b3f75dee0f9afafabe4edc52c4842f1e1878ed2069bd05b22d6fe961e97e4dba", size = 4720512, upload-time = "2026-09-18T13:20:29.278Z" }, + { url = "https://files.pythonhosted.org/packages/63/da/28befc84454cbc6374550de7746f591f8fe1b6165c1fce249652cc8291c4/psycopg_binary-3.3.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5927b7ba63153cd8e9862987290a2b783a5c590daf2a4ef981700cc3569166d4", size = 4782318, upload-time = "2026-09-18T13:20:35.401Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8a/0d21c2c833cdc0d4244c77e858e0ed37fa2abec2623be4fd686f617109ce/psycopg_binary-3.3.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0bf08b749cc144f33b44a91b78e3f71c60eb07963746a0df5a100b36ce3d7475", size = 5567460, upload-time = "2026-09-18T13:20:41.902Z" }, + { url = "https://files.pythonhosted.org/packages/49/6d/7692d0d4e656b6cc9868d8acc2e3b42f17a0db4a625400a6d093cb0533a1/psycopg_binary-3.3.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31cd942c23f613276b81a6e6598cefa12960058b0f46e1e874b540c793f6aca5", size = 5246902, upload-time = "2026-09-18T13:20:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c1/b8a1f18fb1b7558a17f57f7cb3fc8bc93189feea2958925950b3acb15743/psycopg_binary-3.3.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4690cf67738f0e0e49a32aeec99bf0e4595cc2b4f1af984a4345394b1dcff91a", size = 6847192, upload-time = "2026-09-18T13:20:56.874Z" }, + { url = "https://files.pythonhosted.org/packages/a5/76/404f33519167c65cca88ec4998776f1dbebccc301ee977f0e62c47fb0826/psycopg_binary-3.3.6-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ad1c785e784cfd87e8436c6b7702f2d321fc39601bbaf29bc63a41a867091638", size = 5079573, upload-time = "2026-09-18T13:21:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d9/79e8fbc8f37262a415f3550f0bcc5f98037442bf3d12ef6cbae2056655ae/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:79a2a1c3449f6c3409427078ed1cec10de79f3023cb5f2504f0597d350ad46c7", size = 4613633, upload-time = "2026-09-18T13:21:10.664Z" }, + { url = "https://files.pythonhosted.org/packages/d4/47/96225db74be7d2ce04b3a58678b53cda610225055edf5faa775c9f501d8b/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:86147cb5d140341c3363fb5bacce31f8d5543902a46699d3c536b101bbceaf9e", size = 4293375, upload-time = "2026-09-18T13:21:16.027Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d2/18e9c779a5efd565250329adaf529ecc2b8b2ed5be5cb0f6ccee208cbfd9/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7308c93cf0b19bbaf8e6ff0a6ad50d3c442385739245fe15a8d593bf841734a6", size = 4019883, upload-time = "2026-09-18T13:21:21.587Z" }, + { url = "https://files.pythonhosted.org/packages/ef/28/0cc654afc6c2cda982767f5679d3646b30b1ec86545bdaa9402202d6776c/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05a83ac9fd52b9bca7cb5ab04b3691163170bd16f53defa27216ea3aa07ee781", size = 4332607, upload-time = "2026-09-18T13:21:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3e/0a753a74fbd7aef120f286c016e09d3cc3f1daf7688f4a145d27281260b2/psycopg_binary-3.3.6-cp314-cp314-win_amd64.whl", hash = "sha256:1fbd30e537dab22cafdf080608f10148fe2a5f3a61294ddb5113caac8a623840", size = 3755671, upload-time = "2026-09-18T13:21:33.855Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b1/a372b9c02aea50148e71c9853e19efca8fa5ae2010a8e27243b9b8f790c0/psycopg_binary-3.3.6-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:bf8c8481d026b85dd70c5fa7dde85b2333aed0b32a2602bcd38a900cbd78a49c", size = 4719571, upload-time = "2026-09-18T13:21:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/65/7c/811e3828c6b82e2f10c6c9cdd963cfc66f3e024026e5a69ac18530bad984/psycopg_binary-3.3.6-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:b599defe9190b17e9907c8b4d114c181e702c87efcd1b8a0ad40971cdcc4634a", size = 4781230, upload-time = "2026-09-18T13:21:49.516Z" }, + { url = "https://files.pythonhosted.org/packages/3e/15/9a784eed813ea9e97c294af3ead63d02b7b203502c66380336c50065e441/psycopg_binary-3.3.6-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b8ece331509f7a975b90501f41e83ad905e4141753fedf3f2711b2bc70a8efbc", size = 5566111, upload-time = "2026-09-18T13:21:58.089Z" }, + { url = "https://files.pythonhosted.org/packages/68/16/47194e002007c27337b11e49bf459c4b19727463f9aff2e1a90917bcc806/psycopg_binary-3.3.6-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c61617eaae0112ca154da87ffb99b73af2c74067acac28dfb9a4455b019dff2e", size = 5249963, upload-time = "2026-09-18T13:22:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/53/84/5dcf9f310b11f0675cd860c6b2c70f58ce61798a3ee3f6f962b53fa358ca/psycopg_binary-3.3.6-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6d19cb4999d03231e8730a5f66c8f5068bc3b532677eb39dab0f600bff3e312", size = 6847925, upload-time = "2026-09-18T13:22:13.088Z" }, + { url = "https://files.pythonhosted.org/packages/f3/06/1957a06dc22963c418c27b284929579de84f29c37ad1abe6dc6ee9e8cf25/psycopg_binary-3.3.6-cp315-cp315-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8cbb54454dbf1bbf2ff08dd7693e8d94ac94b1a20f70f4b3b813d52ecb5cbc1", size = 5087720, upload-time = "2026-09-18T13:22:17.959Z" }, + { url = "https://files.pythonhosted.org/packages/21/43/ac07d042bae99b57bf123bb473632f29af544008094da0ffd285ab8011e2/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:dc75da5a20951049f7b773145f998f69d181adad9c58a0ff36e0cf1d73c10e10", size = 4613412, upload-time = "2026-09-18T13:22:26.719Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/019156fbeafcefb4cccc9d109de4699493bceb8313c7545c8349e089dfbc/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:955e3dd94da361e052d2e49acf591017158dc8f8ed2c8a42c2e3943403c39dc2", size = 4292618, upload-time = "2026-09-18T13:22:33.042Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0f/62113dc6b1df65983a1f2fc816c04b1edfa22f2ae9d4abee74ed267f4a96/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:c7753871eb57e6a5f4646f6168590c6653073dea5e9e720b201c8875332df4c8", size = 4027121, upload-time = "2026-09-18T13:22:38.334Z" }, + { url = "https://files.pythonhosted.org/packages/5d/d5/cf0cbd1ea5a7d8167fe2c6953efde19101f7b193bd61a23e6d622ad6854c/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:303732e798fe6729f8e12021b9c96107df8e95ecec4dd487c67b98ec2a59435e", size = 4336388, upload-time = "2026-09-18T13:22:45.576Z" }, + { url = "https://files.pythonhosted.org/packages/98/33/e2a5b36edf8aa422f6fa4b894756eb33dc93b36df5f65121280bb8b929c4/psycopg_binary-3.3.6-cp315-cp315-win_amd64.whl", hash = "sha256:2f122603f36050937982abf9668d8bc4769a79f7c93a65013b1c49f1cab7b56b", size = 3756154, upload-time = "2026-09-18T13:22:51.283Z" }, ] [[package]] @@ -7951,11 +8349,18 @@ wheels = [ name = "pydantic" version = "2.13.4" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] dependencies = [ { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, + { name = "pydantic-core", version = "2.46.4", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ @@ -7967,12 +8372,42 @@ email = [ { name = "email-validator" }, ] +[[package]] +name = "pydantic" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core", version = "2.50.1", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-inspection", version = "0.4.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/0b/8e10b2e693af8ec54346a14caf36221334775975a747c9ceaa3f8371d96d/pydantic-2.14.1.tar.gz", hash = "sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26", size = 900362, upload-time = "2026-10-11T18:37:55.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl", hash = "sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454", size = 488106, upload-time = "2026-10-11T18:37:53.437Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + [[package]] name = "pydantic-core" version = "2.46.4" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ @@ -8083,14 +8518,166 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, ] +[[package]] +name = "pydantic-core" +version = "2.50.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] +dependencies = [ + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/24/af4ec4be49fbc810f35b0bdcacc3d433b56bbfa469fd3bfd4ae116cd9bf1/pydantic_core-2.50.1.tar.gz", hash = "sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09", size = 513515, upload-time = "2026-10-11T18:35:44.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/db/c9cc34707ba2c23ce90d54e8a4ee9307583ad054b9df2ce570ba550cb576/pydantic_core-2.50.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b281a3b0f0822618fe5e3e0d8a2048b6356b14388505dc9374ccffeb69989713", size = 2154222, upload-time = "2026-10-11T18:31:38.345Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/6963f4ee55b7df7658f5c036c0f90baf8cd3d413f5d8b551f227e8467afc/pydantic_core-2.50.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87", size = 2048126, upload-time = "2026-10-11T18:31:41.061Z" }, + { url = "https://files.pythonhosted.org/packages/bf/be/f75637e222085d29a3076edbc5cfd0dd9b77d145e310ff7dc1150f07558a/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0", size = 2052460, upload-time = "2026-10-11T18:31:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/3d192ffa39ce3bd0e98e988fb5c055cfbf4af6e6e155a4ca5baa86d1f2cc/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de531ce1e2a3364e8767878b58f4ff728a434b4fde089781fe30b1e08e2396e0", size = 2190750, upload-time = "2026-10-11T18:31:43.572Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/12ea94df5220ab316ee5bb498ebcf710cff2db352653039fb6033f6997d1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c58106de36ac6a56314182958de20db8d3a29dfd5db527192cc754e4f8e7fb", size = 2390043, upload-time = "2026-10-11T18:31:44.745Z" }, + { url = "https://files.pythonhosted.org/packages/f6/bc/4fbb6dc90af038628ec824434a9841be9d46854f9267bb658f09e315024d/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b4c3df25bd323bf1d36a648d563cf1fc69d717451569927151bdad7cad07a77", size = 2432397, upload-time = "2026-10-11T18:31:46.186Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3", size = 2160719, upload-time = "2026-10-11T18:31:47.557Z" }, + { url = "https://files.pythonhosted.org/packages/73/6a/d5b2c5876162fd2c444eaed0064b5d9d9d0ef0d31530fc6a7997e8685ad8/pydantic_core-2.50.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:d939de9c82e2126f7f48a7e658f8a85ed46d57662d53f44c49b8895fe94a3eb7", size = 2256164, upload-time = "2026-10-11T18:31:48.797Z" }, + { url = "https://files.pythonhosted.org/packages/af/e8/1a6625d6e99250388dd0ae817c5d75839cd6103e7b790f59e0ffff74955d/pydantic_core-2.50.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:30ddf019d082c117b5d309e5b86710c2a78909907ec1a9381feec3eec02eca0b", size = 2271176, upload-time = "2026-10-11T18:31:49.987Z" }, + { url = "https://files.pythonhosted.org/packages/19/9f/bbdd137d7ec41284701dab4d1489ea597d401dff2f5f3e6b41ea872e0b12/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2ab756b72bd5054e4c7ef3ded331b35786cbd3cf931531a508f79a9537517064", size = 2304637, upload-time = "2026-10-11T18:31:51.251Z" }, + { url = "https://files.pythonhosted.org/packages/49/5d/7bd322e649b41b45130bc00bfa34dc57498c853aa8e6fc9604e33bc7087c/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:b087b1c5be7ac687cf22eabfe4b6b608d40df23610651e93611e1f49118baf84", size = 2467374, upload-time = "2026-10-11T18:31:52.587Z" }, + { url = "https://files.pythonhosted.org/packages/53/48/41a91111590eb7ba68a7c64c03139235a4b8349930abcb87931aa80da796/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a44101320cfe99432db74237545a63057dc7a88dfe792cbcad0647f2af56cb81", size = 2480832, upload-time = "2026-10-11T18:31:54.06Z" }, + { url = "https://files.pythonhosted.org/packages/2a/02/4ac93a3d3f0ff412df1a36a9619743336fe6eec68c0df8a914163a3d22b4/pydantic_core-2.50.1-cp310-cp310-win32.whl", hash = "sha256:a4aaaa791bdae1c972a7e81765f4f3571c926b8e0b9b6e47346499fb80079665", size = 2123154, upload-time = "2026-10-11T18:31:55.396Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ba/3a7af3dcd480c4739aa82cd9fedee79a5531a7c553346abcf095bd05f0de/pydantic_core-2.50.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eedf82ee4753cdab8e50044c6bd569577eebc3859b11fecf4eb9223761ff966", size = 2179366, upload-time = "2026-10-11T18:31:56.697Z" }, + { url = "https://files.pythonhosted.org/packages/45/1b/ca81811da3d2aea724fb3c4ba6b83f50ab8f210576306ccd601973142d35/pydantic_core-2.50.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d", size = 2152145, upload-time = "2026-10-11T18:31:58.046Z" }, + { url = "https://files.pythonhosted.org/packages/59/20/938ef7e0541af0a314e2f9d63848dbd1656ba2ebfe5ca0469f7ebee00d5e/pydantic_core-2.50.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71", size = 2045761, upload-time = "2026-10-11T18:31:59.35Z" }, + { url = "https://files.pythonhosted.org/packages/2f/4b/456497fe2affa18f49b226e89bde31e8a204751ee343dd9beaac5438123e/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350", size = 2049557, upload-time = "2026-10-11T18:32:01.121Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/cd5dfe13928f55e236dff3a1bee06721d318ca6c955d9ca993b0a5d09f39/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1", size = 2188153, upload-time = "2026-10-11T18:32:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8d/d0b25bc484f8d9136ea76ece69e0b2d24ab370cc2e7bbd1b79e61723b763/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63", size = 2388308, upload-time = "2026-10-11T18:32:03.767Z" }, + { url = "https://files.pythonhosted.org/packages/c5/64/6513cac7c9f78492abafa01d57a69de1d246bf3085fd7b0330bf14b025b3/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5", size = 2431187, upload-time = "2026-10-11T18:32:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/ed/60/7f2ae89c37e69a3dc0da9ff57b0388c285542c688d05f36767770429de85/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133", size = 2157623, upload-time = "2026-10-11T18:32:06.616Z" }, + { url = "https://files.pythonhosted.org/packages/87/02/e3a71373b85a7973099b9a229a4302c7792262717e0b4997e41c01f7dd16/pydantic_core-2.50.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253", size = 2254539, upload-time = "2026-10-11T18:32:07.968Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d5/5b620aaf40dc631d911565b64d49d3fa8422027c046a7a96dc4e4921ae98/pydantic_core-2.50.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b", size = 2267344, upload-time = "2026-10-11T18:32:09.399Z" }, + { url = "https://files.pythonhosted.org/packages/e0/48/f46baf4580d8a56ca4782aa65842e16d3cf39736b29fcce8e9c0521b3422/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae", size = 2300877, upload-time = "2026-10-11T18:32:10.903Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/e7d28505e6092c1fe075838a4f793ccbe6dd676e44ab16175815ee06cfe0/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef", size = 2464270, upload-time = "2026-10-11T18:32:12.329Z" }, + { url = "https://files.pythonhosted.org/packages/22/b1/3bf63b82efd93ebc9968f9c12fc46037a28bcb452c88e4688f33e5ff7dca/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980", size = 2478653, upload-time = "2026-10-11T18:32:13.735Z" }, + { url = "https://files.pythonhosted.org/packages/28/17/26d1e4b9c2ba6a92b7dc75bbcb99ef44456b7e3af1bced868403667ea7d3/pydantic_core-2.50.1-cp311-cp311-win32.whl", hash = "sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2", size = 2120246, upload-time = "2026-10-11T18:32:15.591Z" }, + { url = "https://files.pythonhosted.org/packages/24/65/3e875e7991eaecd4d70686293ff0d8a99aa8f93dc295e6a21c3d702912fb/pydantic_core-2.50.1-cp311-cp311-win_amd64.whl", hash = "sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc", size = 2176332, upload-time = "2026-10-11T18:32:17.153Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5c/58dbd17c61f209d91c70f549eb7a3bbec7023e89ea12dead091462d6d658/pydantic_core-2.50.1-cp311-cp311-win_arm64.whl", hash = "sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e", size = 2182471, upload-time = "2026-10-11T18:32:18.49Z" }, + { url = "https://files.pythonhosted.org/packages/ee/94/101b49a6c63f99586755690696eae14532772587d9e39e93f7b8d28a49da/pydantic_core-2.50.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0", size = 2222428, upload-time = "2026-10-11T18:32:20.228Z" }, + { url = "https://files.pythonhosted.org/packages/db/a3/f8b1b39349c33037c73d7a1b794c9c29d8f07d5bd2b74399c1375a529cc6/pydantic_core-2.50.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e", size = 2025779, upload-time = "2026-10-11T18:32:21.723Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2d/862a7d115305cc621358e51ba13f296e0ffdf4fbd984be9f63bc7862730b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41", size = 2048429, upload-time = "2026-10-11T18:32:23.3Z" }, + { url = "https://files.pythonhosted.org/packages/c0/7d/fed95d18bf2b5abe2d5ab2bbec4184bfc22b6805994a33ccdae8edffd25a/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3", size = 2173274, upload-time = "2026-10-11T18:32:24.697Z" }, + { url = "https://files.pythonhosted.org/packages/93/1e/c6f22ceac65dc3d291ff79bea0eb74cbd1de8b128f01fee48af03bcea1e1/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5", size = 2360403, upload-time = "2026-10-11T18:32:26.281Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/190b5b6bfe4c72f265feb657913eb30749193927a5ad7b83cfa7866a25a2/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6", size = 2439799, upload-time = "2026-10-11T18:32:27.844Z" }, + { url = "https://files.pythonhosted.org/packages/90/24/48cd98388e4ac2c08d0800af36d15413db15e20387f1038123a2c86cb2f6/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e", size = 2160157, upload-time = "2026-10-11T18:32:29.553Z" }, + { url = "https://files.pythonhosted.org/packages/00/86/d909133d0b9979ed14c20f110f21124a0e346caf071bd66ac5af3009062b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a", size = 2229523, upload-time = "2026-10-11T18:32:31.027Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/4b43f08131b2098e73a852fd932063385f3103b33839fd9d1abdb89e673d/pydantic_core-2.50.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9", size = 2242700, upload-time = "2026-10-11T18:32:32.622Z" }, + { url = "https://files.pythonhosted.org/packages/88/77/de39658569dc7a4c293d110a89629f61145e7538f00d1e4280c28debefd3/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff", size = 2278452, upload-time = "2026-10-11T18:32:34.165Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/1eeb9e4e791e81210ef9096f58080b32ba15b5b13c108d50aaa3f8ce9f73/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4", size = 2450098, upload-time = "2026-10-11T18:32:35.547Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/b2accfdff17e5a88d6358536702146de00f9339adaa4d5bbe1ee93cb4832/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2", size = 2476839, upload-time = "2026-10-11T18:32:37.119Z" }, + { url = "https://files.pythonhosted.org/packages/3b/7e/c8a83eb5d2cd42c2e13507150fb777b1edea5a018adc9cbf2858a1d08285/pydantic_core-2.50.1-cp312-cp312-win32.whl", hash = "sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e", size = 2101425, upload-time = "2026-10-11T18:32:38.593Z" }, + { url = "https://files.pythonhosted.org/packages/c5/17/8bbd530b8659e9d963f5b16f6cb8e159f4cd174b4d6304e48969ab12dba8/pydantic_core-2.50.1-cp312-cp312-win_amd64.whl", hash = "sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498", size = 2172522, upload-time = "2026-10-11T18:32:40.112Z" }, + { url = "https://files.pythonhosted.org/packages/af/9f/a79d690b127e4e3cff143d031538ab9aa715f6b2fbeecbd07cd6f2bf9fb7/pydantic_core-2.50.1-cp312-cp312-win_arm64.whl", hash = "sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1", size = 2161366, upload-time = "2026-10-11T18:32:41.606Z" }, + { url = "https://files.pythonhosted.org/packages/ae/57/0e237d7091d2cd44a35d243b7344227f90440166860469cd036afc23a04d/pydantic_core-2.50.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b", size = 2222774, upload-time = "2026-10-11T18:32:43.103Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b9/c720e56858d4e1539503297ed37063e0c08e0f3541c41b777f6a800f75fa/pydantic_core-2.50.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482", size = 2024738, upload-time = "2026-10-11T18:32:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b8/fbfc25875219cc060e613170ff10e850c6d8924beb4910da57c2ee3ba1d2/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d", size = 2047049, upload-time = "2026-10-11T18:32:46.164Z" }, + { url = "https://files.pythonhosted.org/packages/d7/43/34210124d504c553688f2f04b48500b131237528ac545b445e0d6d30e0d5/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658", size = 2173415, upload-time = "2026-10-11T18:32:47.722Z" }, + { url = "https://files.pythonhosted.org/packages/65/cf/6e178e8fdc11da5965bef980983bf46326a42f436871dd51ba0a57f39df1/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4", size = 2361719, upload-time = "2026-10-11T18:32:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/11/14/bd5169d356aa91bf777e28ba0b281c192d286d03c2812c9c9db023a2f00e/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255", size = 2439803, upload-time = "2026-10-11T18:32:51.025Z" }, + { url = "https://files.pythonhosted.org/packages/1c/bc/d79d000e5203ebef39af839f2ce77a777fcad6e08ad26af9a2fcd114ffc8/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec", size = 2158402, upload-time = "2026-10-11T18:32:52.714Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/4902cb5fd599422c130bd3124ab31ee8b771199bd56662702eed07d3fec7/pydantic_core-2.50.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72", size = 2229755, upload-time = "2026-10-11T18:32:54.126Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f5/c1481f8669f6060d89110c9b1374173fc8767ca276b84f4870bd8acd5e3f/pydantic_core-2.50.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c", size = 2242303, upload-time = "2026-10-11T18:32:55.641Z" }, + { url = "https://files.pythonhosted.org/packages/04/f9/77fc3c7653ba9b6e42049e17e96b25388187d4a7c274ab1cb07f58ac8419/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568", size = 2278345, upload-time = "2026-10-11T18:32:57.38Z" }, + { url = "https://files.pythonhosted.org/packages/95/9b/0579c5d12e7f2b16b27e6782427987341fcce07b0725e02ddb0b74add0c4/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8", size = 2449947, upload-time = "2026-10-11T18:32:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ac/1b677db91eba54cc5922f4de6edc46ba45c2bd712dfdc0130382d158e214/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4", size = 2476889, upload-time = "2026-10-11T18:33:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/4d/2a/3a9f6624ee3ea9ccba5249dde11418bb4c35780a7a92608f8768bd3fea39/pydantic_core-2.50.1-cp313-cp313-win32.whl", hash = "sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685", size = 2101118, upload-time = "2026-10-11T18:33:02.329Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1f/323f78ddd9d9938aac420c1abb4e8ba799fc8bdab0acc67c0593b837c979/pydantic_core-2.50.1-cp313-cp313-win_amd64.whl", hash = "sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f", size = 2170840, upload-time = "2026-10-11T18:33:03.919Z" }, + { url = "https://files.pythonhosted.org/packages/cb/09/8497c52a739ae425c3ac2f7f56414cbc711c67d374346174f40fe2062644/pydantic_core-2.50.1-cp313-cp313-win_arm64.whl", hash = "sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662", size = 2161633, upload-time = "2026-10-11T18:33:05.518Z" }, + { url = "https://files.pythonhosted.org/packages/49/33/28b96e81677153715e3eafb9f26663a80841e859fde282a380359b0d3fa1/pydantic_core-2.50.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a", size = 2224428, upload-time = "2026-10-11T18:33:07.153Z" }, + { url = "https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9", size = 2023740, upload-time = "2026-10-11T18:33:08.763Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c", size = 2049435, upload-time = "2026-10-11T18:33:10.366Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8e/f195aebec49ad12318876ac2368c197a7939f5d52f8e156d2238ef8a4588/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb", size = 2172975, upload-time = "2026-10-11T18:33:12.368Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f5/7ad9fb83010cd5ea0948409db105676ed779c4e709e2d3d89b9fe2558794/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597", size = 2364197, upload-time = "2026-10-11T18:33:14.244Z" }, + { url = "https://files.pythonhosted.org/packages/ca/fb/bf0aab3e78301d202b82a0322ec968cd703b11fc0623fde9a28708936f62/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899", size = 2444259, upload-time = "2026-10-11T18:33:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea", size = 2159857, upload-time = "2026-10-11T18:33:17.51Z" }, + { url = "https://files.pythonhosted.org/packages/65/a0/fb0a3ca10f139dcf765b2d312d10cf0f65c57c59993229d00ad12b14ceff/pydantic_core-2.50.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e", size = 2233954, upload-time = "2026-10-11T18:33:19.591Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6a/e63842252702aa4ec6e6b7178ca85e541a77459076592c23ebe2d9840b33/pydantic_core-2.50.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20", size = 2243876, upload-time = "2026-10-11T18:33:21.393Z" }, + { url = "https://files.pythonhosted.org/packages/39/25/5991cf8318b37e0dfab47b87541619a1df8501a793cba0d978846cba37a7/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc", size = 2277908, upload-time = "2026-10-11T18:33:23.324Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3e/3ee8baaa6cc25a6961c69168cf9ff0f002d56f4e0d541ad6724c18fb61f3/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396", size = 2449285, upload-time = "2026-10-11T18:33:24.942Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/acbec6deac13fe697a80c275a9b6661db62c4d323bac8a47347b1f39c7cd/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb", size = 2479013, upload-time = "2026-10-11T18:33:26.925Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/21a3f237b264389f6219d053ee78fc1b5c2fd402c1b1cb2b6cb8b85f9834/pydantic_core-2.50.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5", size = 1484400, upload-time = "2026-10-11T18:33:28.693Z" }, + { url = "https://files.pythonhosted.org/packages/09/ce/077a6d262d12ef09108377ac0717f029f420d773ace63cafd6143af75568/pydantic_core-2.50.1-cp314-cp314-win32.whl", hash = "sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2", size = 2103222, upload-time = "2026-10-11T18:33:30.326Z" }, + { url = "https://files.pythonhosted.org/packages/14/4c/350a2415209c43d670eb71d3c040f332c04a30583d39e311b05c7ac15762/pydantic_core-2.50.1-cp314-cp314-win_amd64.whl", hash = "sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f", size = 2171586, upload-time = "2026-10-11T18:33:32.139Z" }, + { url = "https://files.pythonhosted.org/packages/bf/92/9bea6ca96580a0902fed366f064f0829e404f41889b34543279a1162b888/pydantic_core-2.50.1-cp314-cp314-win_arm64.whl", hash = "sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea", size = 2155773, upload-time = "2026-10-11T18:33:33.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/8c/f121f073cf32bdd5cba7e6230b1ce0ac845b0cf43e44dd597d95af272db2/pydantic_core-2.50.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070", size = 2234017, upload-time = "2026-10-11T18:33:35.588Z" }, + { url = "https://files.pythonhosted.org/packages/a4/69/2bb2bcd6146cffe98d760a46c42ae71efd5151d9b2f9c9bf6619a3b32083/pydantic_core-2.50.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e", size = 2001016, upload-time = "2026-10-11T18:33:37.57Z" }, + { url = "https://files.pythonhosted.org/packages/20/b3/fbf854c7d07ec114260c26e9e2071a4381740f9ae09641dbfcbdf2a18c45/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5", size = 2023015, upload-time = "2026-10-11T18:33:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/65/20/6de55b2f92cdb614b745c6e9ced639fc4fd7e1e77604825a88bdece6fcbd/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb", size = 2192543, upload-time = "2026-10-11T18:33:41.381Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d9/19e91c94bd5c405945ce2f15526808aea37e162c253160de8ed7bf70b406/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e", size = 2388704, upload-time = "2026-10-11T18:33:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b6/bc/2e24c8415eae1a25ee5a5484946a917123bad2e9d01689a759236928175a/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b", size = 2464118, upload-time = "2026-10-11T18:33:44.856Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1f/945b8053cb061c64e102bcaf7bfb9ed740c0bd4349f9bb978a7d4ddff4ab/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019", size = 2150142, upload-time = "2026-10-11T18:33:46.661Z" }, + { url = "https://files.pythonhosted.org/packages/2b/78/96a3e50bf0d64aaae781107eb9335b7529d05a85793ed6e7241c4cd1d931/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112", size = 2245577, upload-time = "2026-10-11T18:33:48.574Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5d/a6038a0322232758a6ebfa709f4ca14a60bf5b93940cd0b345056a557da4/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a", size = 2263728, upload-time = "2026-10-11T18:33:50.527Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4b/76ded3333a457a9344c4f2d63f2d82d0101654b05178fa4ddfe7d3627674/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb", size = 2293630, upload-time = "2026-10-11T18:33:52.362Z" }, + { url = "https://files.pythonhosted.org/packages/a9/00/9eca378335c9c1b72bc779bf6e4c2a82ce784f14ec48a0e87102c9870e05/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b", size = 2468048, upload-time = "2026-10-11T18:33:54.118Z" }, + { url = "https://files.pythonhosted.org/packages/35/ca/e3832e9cf93651251de43c8c5c029ed680a3c1a0a1b17ee26c81bed08cbd/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807", size = 2512159, upload-time = "2026-10-11T18:33:55.99Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f2/773469b5a10a39116a2cb17edaf6d722f017dd8da07161b371465311c542/pydantic_core-2.50.1-cp314-cp314t-win32.whl", hash = "sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19", size = 2098406, upload-time = "2026-10-11T18:33:58.043Z" }, + { url = "https://files.pythonhosted.org/packages/ee/42/0bb74f8f25204b259b11ab7c12dc7f180893b6bd706118b385147fb6efd5/pydantic_core-2.50.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c", size = 2148481, upload-time = "2026-10-11T18:33:59.913Z" }, + { url = "https://files.pythonhosted.org/packages/47/0d/d801646c9679a4e630e15cf521d4108b93854b42a6e6e02391bd4c6b1095/pydantic_core-2.50.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86", size = 2174972, upload-time = "2026-10-11T18:34:01.875Z" }, + { url = "https://files.pythonhosted.org/packages/b2/84/23984b763d8862a02a13d27a44b6e8169428fd85ecfed88f54c29108604d/pydantic_core-2.50.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102", size = 2224599, upload-time = "2026-10-11T18:34:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/4d/90/a63cf8586abc1d1a3f6d9b18f0224789ebf003851f092ebda3c7c863fd32/pydantic_core-2.50.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c", size = 2024191, upload-time = "2026-10-11T18:34:05.901Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6a/f34bff9808ffb4907fbf5f5040457d253cacf2da7fce9efbc99ca1b1a44d/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7", size = 2048252, upload-time = "2026-10-11T18:34:07.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/54/13f419bf1eb59852003818e25935aaf175687d978f4c4bca70e08fe40a3b/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43", size = 2173376, upload-time = "2026-10-11T18:34:09.592Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/b5a34d24cd0c81669d8f8339d74e6815abcf2f8fb48ab4b49b84c09be1d5/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78", size = 2364559, upload-time = "2026-10-11T18:34:11.534Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8d/d64d6216a8df365082665927ff923f183056f9049fee08e9777c9ac05296/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831", size = 2444697, upload-time = "2026-10-11T18:34:13.535Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0d/1b1149f60a00ea21ba5f70e28acbd40feb4598af414f80f53c921fac07c9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa", size = 2159961, upload-time = "2026-10-11T18:34:15.56Z" }, + { url = "https://files.pythonhosted.org/packages/1e/35/f236549299dcc78e71e945495d7ec67e78d20844d0999c60601685003031/pydantic_core-2.50.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5", size = 2234581, upload-time = "2026-10-11T18:34:17.502Z" }, + { url = "https://files.pythonhosted.org/packages/6a/85/26901a490522b7f75ef9bb9a7afb73e5bb550f0e1cb5b99a0069183e2eb9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415", size = 2244090, upload-time = "2026-10-11T18:34:19.402Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2c/481bcfc70ceeb77a778ca6e5b705fe592cb64735c108157f81b7dd9c280e/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10", size = 2277948, upload-time = "2026-10-11T18:34:21.317Z" }, + { url = "https://files.pythonhosted.org/packages/24/eb/f1e09333faa7ba447cde967310f758430cc7c5e987bdab5228816c70030d/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f", size = 2449688, upload-time = "2026-10-11T18:34:23.321Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b3/036bde636db8f76d92996e81aefc75678ab5cec4a07eea1ad0c72a893fc3/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459", size = 2479286, upload-time = "2026-10-11T18:34:25.214Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/07f018294ee18d144afeb6df1a47d9e92960f014be45c5ed13519fa5af95/pydantic_core-2.50.1-cp315-cp315-win32.whl", hash = "sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290", size = 2103683, upload-time = "2026-10-11T18:34:27.42Z" }, + { url = "https://files.pythonhosted.org/packages/5f/98/f9bd7e1f9b6709f155acb9ef826d9c3884fe925f811fd8e55b9b52280bad/pydantic_core-2.50.1-cp315-cp315-win_amd64.whl", hash = "sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed", size = 2171681, upload-time = "2026-10-11T18:34:29.508Z" }, + { url = "https://files.pythonhosted.org/packages/10/87/4bb3e1e7f385c076ab5af4d6dd0571b22042cd8207eb810d5b9fef15ae31/pydantic_core-2.50.1-cp315-cp315-win_arm64.whl", hash = "sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54", size = 2156251, upload-time = "2026-10-11T18:34:31.455Z" }, + { url = "https://files.pythonhosted.org/packages/47/47/83643225b08f2aef6c8cc4bbe6e3f79c5e139c4364a6e450d0f399d87774/pydantic_core-2.50.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a", size = 2234363, upload-time = "2026-10-11T18:34:33.65Z" }, + { url = "https://files.pythonhosted.org/packages/5e/66/127ca649ba2f2462039dc1e694c6c01e00a3689f023a617364d3507e6d97/pydantic_core-2.50.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08", size = 2000972, upload-time = "2026-10-11T18:34:36.037Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/e421e0a5d653b1203b090b2e48745976988d7338a647940704b5b9c2b399/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe", size = 2022925, upload-time = "2026-10-11T18:34:37.972Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/ea5568e2491e1a71100f15ae8c2d01ef52db42a184a2d4e716bc79e5eb8f/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f", size = 2192867, upload-time = "2026-10-11T18:34:39.921Z" }, + { url = "https://files.pythonhosted.org/packages/ae/5e/3b8c3a35acbe219909ada5defad5d7d9fed845fb2b37bd5ec518f453c119/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8", size = 2388687, upload-time = "2026-10-11T18:34:42.184Z" }, + { url = "https://files.pythonhosted.org/packages/b6/aa/7889b4e515f91a2e8c0ae6b5081fec0feb30c4398d1434e14793c60f173a/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a", size = 2464415, upload-time = "2026-10-11T18:34:44.384Z" }, + { url = "https://files.pythonhosted.org/packages/08/78/93449e628eb8a6fdcce3eff9043081179d1bc7ce6f1bff32dc5006f41e00/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8", size = 2150823, upload-time = "2026-10-11T18:34:46.392Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f1/72c5bc129fceb0d00f05dc1e67f518c1728de1928c55f81fc13d7690de39/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709", size = 2245806, upload-time = "2026-10-11T18:34:48.805Z" }, + { url = "https://files.pythonhosted.org/packages/28/2a/922a0e78f3aa6ab837b59f88190233fb8dad546c17995fde9bb3ed3b9b49/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2", size = 2264160, upload-time = "2026-10-11T18:34:50.876Z" }, + { url = "https://files.pythonhosted.org/packages/52/a8/0f1449e3e1b20941c9372faa18e7b6a092e30cdfa841902473f7989532ac/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a", size = 2294031, upload-time = "2026-10-11T18:34:52.876Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d0/1031f492857de70355fb16524bbb03efce5fd34c93ed4a1ec60be07e0d4b/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f", size = 2468423, upload-time = "2026-10-11T18:34:54.995Z" }, + { url = "https://files.pythonhosted.org/packages/46/52/269ffffa645b8e47906395a39cf9db1151ae9fa4bcd7f47b960bc31baf37/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b", size = 2512598, upload-time = "2026-10-11T18:34:57.149Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/e47e28281f20326ff6f3c31d626f0a83e615d2d94ba41bb0ad6ec237184a/pydantic_core-2.50.1-cp315-cp315t-win32.whl", hash = "sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f", size = 2098535, upload-time = "2026-10-11T18:34:59.313Z" }, + { url = "https://files.pythonhosted.org/packages/03/ad/759e181e69c1b472c60b2049e5f61d5da1deefdd5ce1df4bb2ebcf771f25/pydantic_core-2.50.1-cp315-cp315t-win_amd64.whl", hash = "sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b", size = 2148645, upload-time = "2026-10-11T18:35:01.571Z" }, + { url = "https://files.pythonhosted.org/packages/6d/56/8a702c27e5be9f47e5f19d8669227424290e4c024e7c370279cbaf244b4e/pydantic_core-2.50.1-cp315-cp315t-win_arm64.whl", hash = "sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44", size = 2175297, upload-time = "2026-10-11T18:35:04.079Z" }, + { url = "https://files.pythonhosted.org/packages/e7/73/a23a327237d9bb985298ce47702e196f995ce850224c12c70886bd66bb5c/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769", size = 2162560, upload-time = "2026-10-11T18:35:06.274Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/33b37b5e82408f402a5e82649a43e7c95cd5dab7beaeef2c35cedbfd18f2/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9", size = 2075250, upload-time = "2026-10-11T18:35:08.455Z" }, + { url = "https://files.pythonhosted.org/packages/04/62/c7d5b9249466ccab90f3ce4790c2399dbfb8172ae9e74a7753dd295fe65a/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92", size = 2107891, upload-time = "2026-10-11T18:35:10.918Z" }, + { url = "https://files.pythonhosted.org/packages/d4/7e/28d29bc1657195933147f772dee0558d9e0f045bbc11db7f992f40c37bb5/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87", size = 2268026, upload-time = "2026-10-11T18:35:13.336Z" }, + { url = "https://files.pythonhosted.org/packages/a5/09/1bcf160f3cd6333e2a76982fae5963e5032de0bcaaba38464223dc266bfe/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242", size = 2217070, upload-time = "2026-10-11T18:35:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/3d/81/f02de95eec7794dfc7a5bc69a834f0e834b31b3009014ce6de903855f775/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5", size = 2039027, upload-time = "2026-10-11T18:35:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/4d/16/227746ed3771d9bf2304568b55134849ece34e13b38b1d76de763015c77f/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295", size = 2109009, upload-time = "2026-10-11T18:35:20.218Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b9/7664d1592a0e5a74903f357cd5b28ceb7336f3473256c5a4e8c8432f1881/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61", size = 2269883, upload-time = "2026-10-11T18:35:22.48Z" }, + { url = "https://files.pythonhosted.org/packages/bd/35/002a378302ff91d2a7f149bc8d22363faf581ba40ac39d0123444315a32d/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84", size = 2154076, upload-time = "2026-10-11T18:35:24.743Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5c/7a034330082c00f3c9795052bd9b161aaaf6e1951e897ac00a094823ef7e/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f", size = 2085850, upload-time = "2026-10-11T18:35:27.009Z" }, + { url = "https://files.pythonhosted.org/packages/57/fa/d5b44a7d3d4eceed6ef8713a0003af56fefde6ec75915513bcdd5d730cd9/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906", size = 2259754, upload-time = "2026-10-11T18:35:29.763Z" }, + { url = "https://files.pythonhosted.org/packages/b6/88/e01dd37301710bd8b3b2daf34ec9e2a6e83a333130831c0d468b0292a48c/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760", size = 2268712, upload-time = "2026-10-11T18:35:32.654Z" }, + { url = "https://files.pythonhosted.org/packages/3e/43/5bb80d4a4d2b2d84612a30205662cc521a20d209c94abc079ea48b4e0df2/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad", size = 2305049, upload-time = "2026-10-11T18:35:35.392Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7b/e3ef76b269cfe4a5bd3bdcf5d7124cf05e5a8909d443b91639307116bdae/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe", size = 2466001, upload-time = "2026-10-11T18:35:37.791Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7a/ea0947aadc1d9f72d4be00d2e19939611ec17363bcb5e6eb9ec7d8221c34/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5", size = 2481777, upload-time = "2026-10-11T18:35:40.287Z" }, + { url = "https://files.pythonhosted.org/packages/24/f3/d15bc0b1fb0c4f1e07b3326ffb7489f7ae70da2b56a0884d22641b0eb47c/pydantic_core-2.50.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf", size = 2336787, upload-time = "2026-10-11T18:35:42.558Z" }, +] + [[package]] name = "pydantic-settings" version = "2.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "python-dotenv" }, - { name = "typing-inspection" }, + { name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-inspection", version = "0.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } wheels = [ @@ -8102,7 +8689,8 @@ name = "pyee" version = "13.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } wheels = [ @@ -8117,7 +8705,8 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"] }, { name = "pynacl" }, { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c1/74/e560bdeffea72ecb26cff27f0fad548bbff5ecc51d6a155311ea7f9e4c4c/pygithub-2.8.1.tar.gz", hash = "sha256:341b7c78521cb07324ff670afd1baa2bf5c286f8d9fd302c1798ba594a5400c9", size = 2246994, upload-time = "2025-09-02T17:41:54.674Z" } @@ -8148,7 +8737,7 @@ name = "pyjwt" version = "2.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/a5/5197bfd06417837ac079921c66fa6393f1dea3557272a263cebfef69e432/pyjwt-2.15.0.tar.gz", hash = "sha256:b11c5f9791d7bf51c2b39a81ed669f6b2dbbd669df2942f6c60167e9e3d1abe4", size = 120513, upload-time = "2026-09-23T16:56:00.689Z" } wheels = [ @@ -8228,7 +8817,7 @@ name = "pypdf" version = "6.21.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/28/081f1cbcca7042441dafc8de00f48ba60853f89e5bff92c5f26477346d81/pypdf-6.21.2.tar.gz", hash = "sha256:0a3e1c5c886a1be03646c16a04827cc166c2a0575c971b26a4bf89686551c2d4", size = 7144571, upload-time = "2026-10-21T16:44:55.272Z" } wheels = [ @@ -8283,7 +8872,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ @@ -8699,7 +9288,8 @@ dependencies = [ { name = "ml-dtypes" }, { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "python-ulid" }, { name = "pyyaml" }, { name = "redis" }, @@ -8717,8 +9307,9 @@ version = "0.36.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } wheels = [ @@ -8905,7 +9496,8 @@ version = "2.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/a3/20003e7d14604fef778bd30c69604df3560a657a95a5c29a9688610759b6/resend-2.23.0.tar.gz", hash = "sha256:df613827dcc40eb1c9de2e5ff600cd4081b89b206537dec8067af1a5016d23c7", size = 31416, upload-time = "2026-02-23T19:01:57.603Z" } wheels = [ @@ -8966,7 +9558,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } wheels = [ @@ -8986,6 +9578,9 @@ wheels = [ name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, @@ -9104,6 +9699,148 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] +[[package]] +name = "rpds-py" +version = "2026.9.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/42/68/3bd46b8a5e01d3c2ebdf9c5e9497912e3fe0cde02bac21a7130ca866e403/rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12", size = 63948, upload-time = "2026-10-04T16:32:36.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/6d/ddb8dfcf41892ba9c672d27988592c038d30db61708230313831b6780a1b/rpds_py-2026.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2", size = 344947, upload-time = "2026-10-04T16:28:55.207Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b2/632c3d034961149eff606e3e357547620a9ab37b849ae5815f67838753fd/rpds_py-2026.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044", size = 346770, upload-time = "2026-10-04T16:28:56.884Z" }, + { url = "https://files.pythonhosted.org/packages/6d/45/4df346410bb873d03ec60c3b891d1fceb8cabde1971d29844bbb625cc296/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa", size = 377795, upload-time = "2026-10-04T16:28:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/a70f302207e4f29f873c01a986fcac5def49b9c8224328cef1ad7da6b7a9/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52", size = 383137, upload-time = "2026-10-04T16:28:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/13/e3/3aa28a48a01169d0e20728bf0e5ebbea3ae2774285b40853ae49b3ef332a/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f", size = 496789, upload-time = "2026-10-04T16:29:01.011Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a7/414e8d088602d66ad6f47c65bf07fe5090992a8574cdd4571c1a83b647ed/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7", size = 395929, upload-time = "2026-10-04T16:29:02.465Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5c/aac3b960fd55d37b49f77c0eebbe043eaf36762ebd48e7b46951e59c1c05/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff", size = 375426, upload-time = "2026-10-04T16:29:03.944Z" }, + { url = "https://files.pythonhosted.org/packages/d3/80/77987a41c40be7202a78d9aed4a650bfb05792237cfd3b9e22638d990fe5/rpds_py-2026.9.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e", size = 381497, upload-time = "2026-10-04T16:29:05.308Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/640b02eb1ecd71a913c1178b6235287b31006266bca9a26862e80e497b54/rpds_py-2026.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888", size = 405740, upload-time = "2026-10-04T16:29:06.578Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8c/d034195d45d215625a23f2ae85dbbdb86a49a93a51272061c97c80b9d316/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834", size = 555850, upload-time = "2026-10-04T16:29:08.115Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/532f1addfc86207bc3cf23749638bb0d457bce891595012ed8a08d0543b8/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391", size = 618661, upload-time = "2026-10-04T16:29:09.554Z" }, + { url = "https://files.pythonhosted.org/packages/50/5b/b0f2c9833a36a5b1f6ac5cb0e635fabacf713266f46ceefba2102da0bf51/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a", size = 583530, upload-time = "2026-10-04T16:29:11.072Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/464ed4b98d3c8c6a334f3b5e24d3886de04b05658a95fc40a37e0e6e4775/rpds_py-2026.9.1-cp311-cp311-win32.whl", hash = "sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1", size = 208125, upload-time = "2026-10-04T16:29:12.408Z" }, + { url = "https://files.pythonhosted.org/packages/df/c9/384a9b62f8cd89c8e3e609b8c9052c140e8c01d807261e0517c904b8bf29/rpds_py-2026.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f", size = 226045, upload-time = "2026-10-04T16:29:13.751Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/0a691f0dcb2301fa9c736346da2d0d4991778373283301c5c12b8f952f3d/rpds_py-2026.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12", size = 222636, upload-time = "2026-10-04T16:29:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/5d/34/a828586ea3329fbb50895b50e9cf98ca3d924a9f41a0b26d443bff1b3794/rpds_py-2026.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af", size = 346822, upload-time = "2026-10-04T16:29:16.258Z" }, + { url = "https://files.pythonhosted.org/packages/90/81/ac6a0d064982251856ce009c9e1dd51a34110b3c055aa7d1aad18b4899a3/rpds_py-2026.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b", size = 341380, upload-time = "2026-10-04T16:29:17.706Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/bf7d54f362748fd6a49277b9d6531c394074110fedf50ad791ec59133fbb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a", size = 373053, upload-time = "2026-10-04T16:29:19.063Z" }, + { url = "https://files.pythonhosted.org/packages/60/de/74b0ccdbbd28687b9b5fdb34c1cabed88352182facaced9d6f5486b8b9ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07", size = 377742, upload-time = "2026-10-04T16:29:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6d/b979775a3057b2a5c26d75ecbff60a20a24ef081db6dd76836fca3f20247/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69", size = 487289, upload-time = "2026-10-04T16:29:21.958Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5d/7c34734ce3ece943d9d6ee0122a74a21bbc75b47acb5b7053c83f6efb1ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d", size = 390710, upload-time = "2026-10-04T16:29:23.378Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/b26eb1e75395925b3a142ed351cbed2ec8212f5c9d937aee3df32c701baa/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab", size = 371261, upload-time = "2026-10-04T16:29:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/b2fdfe10301a9e27af21e634337fbba7baac0bc17fe44619a17c26bba2cb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e", size = 376075, upload-time = "2026-10-04T16:29:26.748Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/c4b8d954e49c3c69cf8063c0c9e0919c99f3d2cecc46311606a1cbe835dc/rpds_py-2026.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237", size = 399358, upload-time = "2026-10-04T16:29:28.282Z" }, + { url = "https://files.pythonhosted.org/packages/b3/59/9559a7293c97dff0cbb293efffbd36644103883ecc27741af8ef90c84ca8/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3", size = 550267, upload-time = "2026-10-04T16:29:29.851Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/6dc60e49511de4c8b00e74f9c1d43aed4d27f712cdb1ade92f2c199cbf64/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc", size = 614018, upload-time = "2026-10-04T16:29:31.54Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bc/92a4ecf27301b886232a413360f6b348a81d55ffef654f2ebda5970ebc3d/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6", size = 578509, upload-time = "2026-10-04T16:29:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/08/53/efb97f2e6589b7ab8394385a73870e0b2ab2a195281c117b21fd6d7d4855/rpds_py-2026.9.1-cp312-cp312-win32.whl", hash = "sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3", size = 205401, upload-time = "2026-10-04T16:29:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/1c/84/1700cc748d0eaa747486e4e82882d2575224e6e9a3148df583058850c629/rpds_py-2026.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f", size = 222923, upload-time = "2026-10-04T16:29:35.709Z" }, + { url = "https://files.pythonhosted.org/packages/c3/89/0302215373c2f8b4408b0fdfee955368cc378a98ff59508d47a7e0dc2dbf/rpds_py-2026.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02", size = 218415, upload-time = "2026-10-04T16:29:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/ee88fd9e756ff93fb6b1182a47ec09504a242620e33ce1d20679efefe841/rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9", size = 346229, upload-time = "2026-10-04T16:29:38.82Z" }, + { url = "https://files.pythonhosted.org/packages/57/71/a097d6552f837500fc36e6b23d09cfb9890c3cc47531f9ca64e149799615/rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858", size = 340514, upload-time = "2026-10-04T16:29:40.405Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b7/497e85768bf4e0d8ddbaa096a4cac31d1509251dee2728a8490aa367e0b5/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063", size = 372611, upload-time = "2026-10-04T16:29:41.778Z" }, + { url = "https://files.pythonhosted.org/packages/52/4b/74ab4108916250b6e198e0d3af05bc6835eb046315f22f7a0ceb49667c5a/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025", size = 377712, upload-time = "2026-10-04T16:29:43.242Z" }, + { url = "https://files.pythonhosted.org/packages/0c/8e/067e77d9d7b3cc793c9d909b7e97e7aadbbb1fb094093b6876902cc96d38/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5", size = 488106, upload-time = "2026-10-04T16:29:44.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b4/c5aae6c2dde269bf955f6b7d35065c655a57e47750d9668052ad67e74dda/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0", size = 390795, upload-time = "2026-10-04T16:29:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/a0/36/76fab39973ee11e7f9f357c55138197bb01c86f6502cb76487e3b4f42db0/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b", size = 370794, upload-time = "2026-10-04T16:29:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fe/cd2a80e6d7b871937a60e935c5d507aa390d143f4ff3636f640b9733d5df/rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33", size = 375673, upload-time = "2026-10-04T16:29:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/7464a9953724e55a3b3206062fa0ffdeaa519584c6aabf65d3956d94f131/rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83", size = 398758, upload-time = "2026-10-04T16:29:50.601Z" }, + { url = "https://files.pythonhosted.org/packages/c0/86/1534b436700fd49ff411063b7c4d7e938adfabf90895b6cf1622d5a7d1f6/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c", size = 550370, upload-time = "2026-10-04T16:29:52.002Z" }, + { url = "https://files.pythonhosted.org/packages/57/1c/e1fa82a8a01e3c5820f3ba98a8b2673f642128eb368fa88871b01dd2c909/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36", size = 613106, upload-time = "2026-10-04T16:29:53.655Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/b20b8c4c3dde8755bfcd5b08492a02d0cd2e26929aedf6199ca2a377d42a/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d", size = 578235, upload-time = "2026-10-04T16:29:55.157Z" }, + { url = "https://files.pythonhosted.org/packages/55/42/df3f7bbc3f7ab37a8a9db8d6c2ff2c985422899f1f7926afbf7ec3c0b8b4/rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c", size = 205293, upload-time = "2026-10-04T16:29:56.669Z" }, + { url = "https://files.pythonhosted.org/packages/31/9c/ba5a9569d719bfdd6ce863df4133ac6a1658cf1b07cc3534c31db729fbbc/rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9", size = 222409, upload-time = "2026-10-04T16:29:58.049Z" }, + { url = "https://files.pythonhosted.org/packages/35/72/f28ca566f6c23c35bbf7445f65eb0364577b25a026305995e24f78b83d94/rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f", size = 217681, upload-time = "2026-10-04T16:29:59.449Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f2/67b94be1532767803415c1c5a1fd88ea487643d74a673cec1ba140af77bb/rpds_py-2026.9.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b", size = 347178, upload-time = "2026-10-04T16:30:00.865Z" }, + { url = "https://files.pythonhosted.org/packages/04/37/b751de2b59b0197a1d92a5dd491de88e8a5e928c2e6562581974f1e85263/rpds_py-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35", size = 341878, upload-time = "2026-10-04T16:30:02.564Z" }, + { url = "https://files.pythonhosted.org/packages/72/e2/5873bc4643c250db9e05d48dc0c93763d4aa68bc3b81164cb1af3b45b284/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0", size = 373963, upload-time = "2026-10-04T16:30:04.026Z" }, + { url = "https://files.pythonhosted.org/packages/51/03/5acf7632158247f3f6386ff0af3a1ee48167d575037e8d0920594b76d92b/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477", size = 377975, upload-time = "2026-10-04T16:30:05.555Z" }, + { url = "https://files.pythonhosted.org/packages/e6/00/63fda451b8bffa5808fc8bb311ee7c073b340974b09f273c7b2a145d3d62/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4", size = 490347, upload-time = "2026-10-04T16:30:07.156Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ae/c093ffd070ba0fb02f76c565d06fecc65ad6e4afdbae78f7031076d3cdac/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef", size = 392962, upload-time = "2026-10-04T16:30:08.77Z" }, + { url = "https://files.pythonhosted.org/packages/22/9d/d08a1128ab199b2f0cf25bfeb0639bd05119fff4b7c47bec24ef9a8ec23f/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd", size = 371623, upload-time = "2026-10-04T16:30:10.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/c7/4758ddcbb75609414bbccfcb11d612436f9b3ee821bd2f33f0f1604ee648/rpds_py-2026.9.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35", size = 376997, upload-time = "2026-10-04T16:30:12.124Z" }, + { url = "https://files.pythonhosted.org/packages/87/e4/947bd7f608ff60faf46dc9d389c3dffd0e3d767d78a0be19978448ef0ce7/rpds_py-2026.9.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86", size = 400169, upload-time = "2026-10-04T16:30:13.804Z" }, + { url = "https://files.pythonhosted.org/packages/4b/35/fe93e020a0543b5670472c18d7e6af3197c08da571240ce1965c84f85c0f/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e", size = 551615, upload-time = "2026-10-04T16:30:15.332Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4f/5d2a0136bb03b2a56a39dc6ff92d58a6e3e53a2e17079238b86228882f16/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785", size = 614655, upload-time = "2026-10-04T16:30:16.92Z" }, + { url = "https://files.pythonhosted.org/packages/09/1c/3f1025aaf70d9bf7272cc41f8b64ee76b48bf01726248138430e16f23b38/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118", size = 579602, upload-time = "2026-10-04T16:30:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/53/0d/5c72e6204f76610608706da32b6b7e11ef7e10317558a7bb15a122e008dc/rpds_py-2026.9.1-cp314-cp314-win32.whl", hash = "sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc", size = 206315, upload-time = "2026-10-04T16:30:20.05Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0b/489d48abbcc7d70cf3fbf662d9d22abf1f4650761c0a9ae05260800800d3/rpds_py-2026.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c", size = 222852, upload-time = "2026-10-04T16:30:21.604Z" }, + { url = "https://files.pythonhosted.org/packages/91/16/bbb05a7e6a10cf79ba639be7f799d770ee15f64175cc61d081b218dd402a/rpds_py-2026.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31", size = 218709, upload-time = "2026-10-04T16:30:23.036Z" }, + { url = "https://files.pythonhosted.org/packages/22/ac/ac507a0a4ec478ca470440a09583db4be5259ba7670aeba0620822f1e57a/rpds_py-2026.9.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e", size = 349596, upload-time = "2026-10-04T16:30:24.558Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f2/817a46b658d5070f477f722c298ee9a24525b0e4017347964146ef5fdd0e/rpds_py-2026.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96", size = 343114, upload-time = "2026-10-04T16:30:26.048Z" }, + { url = "https://files.pythonhosted.org/packages/6c/42/6ade976b13ac1b4cb3bf2eb603f1be2fe74df19a29988d4c2b386be59d6f/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445", size = 374197, upload-time = "2026-10-04T16:30:27.699Z" }, + { url = "https://files.pythonhosted.org/packages/d9/70/77cdf1d3f1a07faabe936016ae623aec7981f73108a8fe7a203ed2e21998/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69", size = 378025, upload-time = "2026-10-04T16:30:29.451Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6b/18a44a3beaa9b7931acb04af7bd9539836477c630a794452d4826d6185d4/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495", size = 491969, upload-time = "2026-10-04T16:30:30.995Z" }, + { url = "https://files.pythonhosted.org/packages/73/27/fb39cfd6bddaf741b024f813890374578ff8ac1f1adc473659c048b03b05/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c", size = 393905, upload-time = "2026-10-04T16:30:32.628Z" }, + { url = "https://files.pythonhosted.org/packages/ed/71/0fa7bb77b57af0d710273964180d11b503f62b8a5c358eb2d8c3f62feff6/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e", size = 374569, upload-time = "2026-10-04T16:30:34.257Z" }, + { url = "https://files.pythonhosted.org/packages/54/22/f41cfac269af3b449513ef1bc3d7f32fde52abbbd2d01c7e76b47743acd9/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346", size = 377482, upload-time = "2026-10-04T16:30:35.997Z" }, + { url = "https://files.pythonhosted.org/packages/b4/fc/312b49006e7f8f9ca5f96647577b8aa6f3df30519c46bd448f5c425af0b2/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299", size = 400040, upload-time = "2026-10-04T16:30:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a6/18cca7a878dc7fa95165a83343fd4d7b65643fd22e54e47340a451121d5c/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531", size = 551849, upload-time = "2026-10-04T16:30:39.443Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6d/1f5685e20f39604691bdc3c05aaa6b8bd2f954e9e996477adf2376768e33/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb", size = 614842, upload-time = "2026-10-04T16:30:41.231Z" }, + { url = "https://files.pythonhosted.org/packages/c6/25/98652109fd9f7e10268dd4571aa52b81987001b806f37ef1a18260de714a/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa", size = 582606, upload-time = "2026-10-04T16:30:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/19/03/11ca09099bab5f53373a80a334c424ec917c13d050760a59499d2af5171e/rpds_py-2026.9.1-cp314-cp314t-win32.whl", hash = "sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569", size = 203341, upload-time = "2026-10-04T16:30:44.954Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8a/88909e3ffd9f47f5b58211473875d8c3c09079f0058c46fb72c55a702a26/rpds_py-2026.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e", size = 222843, upload-time = "2026-10-04T16:30:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/a662f367d4896dd0a10cef2fc91f10b7f08af1c10858e287e019563f338d/rpds_py-2026.9.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9", size = 347463, upload-time = "2026-10-04T16:30:48.203Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3f/ad45d03df4f84ebae5439577ee81f3999d182711e82235c8037b6528890e/rpds_py-2026.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25", size = 342051, upload-time = "2026-10-04T16:30:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/3dcd68c13d4bcc59c1f7eb33ac8e80698f06f3eb0d8a1e06419836071c20/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871", size = 374143, upload-time = "2026-10-04T16:30:51.508Z" }, + { url = "https://files.pythonhosted.org/packages/cf/0d/68c1f058a250fbd1380ebda9fc227cbf50117adf8ffe8161ef383ea79f68/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91", size = 378604, upload-time = "2026-10-04T16:30:53.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d6/2d4c59b85397cb4800594fadf692688ccf5ce556adc930e7a5bf21061a5e/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f", size = 490790, upload-time = "2026-10-04T16:30:54.925Z" }, + { url = "https://files.pythonhosted.org/packages/da/04/7e05dc3aebaf52f4e026766bd668fdd09a9d0e23f64a14686b36b3501892/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d", size = 393146, upload-time = "2026-10-04T16:30:56.625Z" }, + { url = "https://files.pythonhosted.org/packages/57/ca/e2e9a0a46a74ed51a0498ba1fe10f4ea6b2d9a155f372a2f91e51f18cf10/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945", size = 372022, upload-time = "2026-10-04T16:30:58.295Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cb/8f8774df5134e23424372838bcc5c7ed4127d723e1ff52f7bebd4dcb2563/rpds_py-2026.9.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297", size = 377092, upload-time = "2026-10-04T16:30:59.984Z" }, + { url = "https://files.pythonhosted.org/packages/90/02/8d7095d73bf9114219be40230baa5df00611e0a82ed9517779ff9c19f82b/rpds_py-2026.9.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0", size = 400830, upload-time = "2026-10-04T16:31:01.721Z" }, + { url = "https://files.pythonhosted.org/packages/ec/02/8206856f8f363cd042a8315dc86b3912f5dcb6d3c66bbb24b69c2bfb0775/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9", size = 551478, upload-time = "2026-10-04T16:31:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/41/6b/36211f1bb1f0b0313f496d92f5905b74ea107f27cb16fa3355a82f04575e/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6", size = 615173, upload-time = "2026-10-04T16:31:05.281Z" }, + { url = "https://files.pythonhosted.org/packages/35/77/cda0c4a6f055446b692f0ed5692f73707cafd3dff82672ede31b1a9b59de/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7", size = 579653, upload-time = "2026-10-04T16:31:07.065Z" }, + { url = "https://files.pythonhosted.org/packages/74/ec/d8385f446240aed643b9e92a5055cff3015cc04a13c61f73b2883c478ed5/rpds_py-2026.9.1-cp315-cp315-win32.whl", hash = "sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429", size = 206498, upload-time = "2026-10-04T16:31:08.989Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a5/71b5cd00e0521e3b6b81828baea368c62b6b700ebdd9554cd7d41ddf12fa/rpds_py-2026.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15", size = 223121, upload-time = "2026-10-04T16:31:10.84Z" }, + { url = "https://files.pythonhosted.org/packages/33/58/dba857c3bc8221b31b62eb170a3080f4f191de79f047200389b7ed1b06a7/rpds_py-2026.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009", size = 218741, upload-time = "2026-10-04T16:31:12.592Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d0/320ab28ccc1415eeb509d68682b0014fb74690cd49f1c2d29a232475af50/rpds_py-2026.9.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0", size = 349719, upload-time = "2026-10-04T16:31:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/56/88/f5b12f1358f443c08b7ce3cc8391d82d335f4872580e2e097847fd36087a/rpds_py-2026.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a", size = 343267, upload-time = "2026-10-04T16:31:16.827Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0c/c765b0059d532acb3b9c45d781ccc22f15a96dbe443d00903f643ba9df10/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d", size = 374555, upload-time = "2026-10-04T16:31:18.931Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8a/cafddfda77564a10cd21184640c3bffda6a2b8d20972fb5dedd5e0166328/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db", size = 378346, upload-time = "2026-10-04T16:31:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/b9/01/5e626016eff72c183bf6c96539240cace15d402468647a453ec08415b2fc/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250", size = 492373, upload-time = "2026-10-04T16:31:22.614Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9c/15a2469e9389242f46896b3f0a01d68caea8a5a35c011fcb05ef333aae73/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f", size = 394371, upload-time = "2026-10-04T16:31:24.768Z" }, + { url = "https://files.pythonhosted.org/packages/63/f5/c100ff77e1e6366e947c75969c258fdfc4b7bc5bbfe7351e62ffbf2a1228/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018", size = 375132, upload-time = "2026-10-04T16:31:26.588Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/fe0269c9de253e99c81cabc12b8971a5feaa083debdaff1221e06264d9e3/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d", size = 377642, upload-time = "2026-10-04T16:31:28.677Z" }, + { url = "https://files.pythonhosted.org/packages/05/65/b34a7b257baccff8f4a24a722933166d4941d5ebdc9c3f4bc4ffcd5ce4f4/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b", size = 400855, upload-time = "2026-10-04T16:31:30.802Z" }, + { url = "https://files.pythonhosted.org/packages/98/32/844e54176b6071b90b38a564e6940bc6eb8f97b2890dc709c19db9dec0f4/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6", size = 552573, upload-time = "2026-10-04T16:31:32.709Z" }, + { url = "https://files.pythonhosted.org/packages/17/73/6041d20729dffbfdf155c02d65be58bc225a1c1fb548fd87c23ef306138f/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad", size = 615840, upload-time = "2026-10-04T16:31:34.565Z" }, + { url = "https://files.pythonhosted.org/packages/af/9e/418094adaee6b056ce199051b255448ed872829051e341b2294c80da0977/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903", size = 582764, upload-time = "2026-10-04T16:31:36.665Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9b/1698ebf6b840ddfe8b472198abbed6ace35c6e398faeecd6c47dae742a4e/rpds_py-2026.9.1-cp315-cp315t-win32.whl", hash = "sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b", size = 203795, upload-time = "2026-10-04T16:31:38.53Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e2/91f70d804c61f8eac39a417e82aa1024f655ab9e8bdd7393b196242bc41b/rpds_py-2026.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567", size = 223037, upload-time = "2026-10-04T16:31:40.468Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/f0eba548e297987964ef8088403db875b333963bf8189f3f563b6936b20e/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30", size = 384112, upload-time = "2026-10-04T16:31:42.643Z" }, + { url = "https://files.pythonhosted.org/packages/33/89/891fa4d81f1eae7fe262ecd0aba2c51de94a116c7f3ccfb034fa75f4c537/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050", size = 389662, upload-time = "2026-10-04T16:31:44.642Z" }, + { url = "https://files.pythonhosted.org/packages/4f/46/912cf623b2263f9c9c2d04e8508fc38dbe89983faeba5fb36ca4558ae550/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb", size = 504637, upload-time = "2026-10-04T16:31:46.667Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5c/7f3cfa7c5f01765c9f47c8398210ed0c1eb686c0e99c223cb1c6776d3fb9/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df", size = 402607, upload-time = "2026-10-04T16:31:49.068Z" }, + { url = "https://files.pythonhosted.org/packages/f0/76/2e915d4115ee453c33e6af17bc1df7e22ddc52974e178eeeb92a78c09983/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a", size = 382762, upload-time = "2026-10-04T16:31:51.24Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e5/844382925d7b84a4074ad79fc2b9d05eb6231ea243b34f247e68e519b606/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608", size = 389563, upload-time = "2026-10-04T16:31:53.549Z" }, + { url = "https://files.pythonhosted.org/packages/81/82/d6e3951602dc2e24790ef9d299aa24bf5e2493df5043f2c6da00480cc447/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9", size = 416842, upload-time = "2026-10-04T16:31:55.76Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1c/8d248e4ea671d525ab7daa0703ad81cc2175076fa0ccd7ef088facbd2b01/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c", size = 562434, upload-time = "2026-10-04T16:31:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/e7ac0db898120d15dea25e3661b1242a40cb3b23d1f4db86c15a01fe0ba1/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f", size = 627909, upload-time = "2026-10-04T16:31:59.977Z" }, + { url = "https://files.pythonhosted.org/packages/03/c8/37e555514932cb1ee36c75349187aa882a98dee5d5654c761699ed8a0d27/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e", size = 593658, upload-time = "2026-10-04T16:32:02.14Z" }, + { url = "https://files.pythonhosted.org/packages/12/5c/5f69a91add94a8a33acea68a2b034a464946a654ff7296af9d957ef0d6a5/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32", size = 351769, upload-time = "2026-10-04T16:32:04.675Z" }, + { url = "https://files.pythonhosted.org/packages/41/22/35ea8bf3b2682b90ab687c74e4828418ff12298a5b0b0a0c033f61c86a30/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31", size = 351719, upload-time = "2026-10-04T16:32:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/a3/90/a9ba81408369d34c1aca73319c15adc91ed45c1d21e9e8ee4832ffb7fe0b/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4", size = 381109, upload-time = "2026-10-04T16:32:08.994Z" }, + { url = "https://files.pythonhosted.org/packages/b3/44/5192a0bed94cec86bd2a5e1cc5a1bb8f7eec3aec907eacd2deb13e87e326/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759", size = 388222, upload-time = "2026-10-04T16:32:11.321Z" }, + { url = "https://files.pythonhosted.org/packages/92/cd/5356549711448f18b52f7a11ffbe90f1228774996fbf3c6cd1b18d22abaf/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb", size = 500384, upload-time = "2026-10-04T16:32:13.478Z" }, + { url = "https://files.pythonhosted.org/packages/7a/88/ddda9d28adfe33119c75b2e733d4ba7e326f41d7e1b1093951771768ca48/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82", size = 401661, upload-time = "2026-10-04T16:32:15.653Z" }, + { url = "https://files.pythonhosted.org/packages/32/c3/bb59ba16a6b4a57995d15b8c04c00f28df3b938c4dcdde48fbe0f73edb0c/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c", size = 380748, upload-time = "2026-10-04T16:32:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b8/0580faa1c5a32ddc160130dd7ae54d2d007272a89a40a6b5db3e50f9f585/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14", size = 385731, upload-time = "2026-10-04T16:32:19.855Z" }, + { url = "https://files.pythonhosted.org/packages/bf/70/f73564642bbe3322c7eeef2c2f258cf040b71c41a430b532e59d04a1b838/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c", size = 412131, upload-time = "2026-10-04T16:32:22.285Z" }, + { url = "https://files.pythonhosted.org/packages/8c/5d/17fff2e1f8f68721a2afb5cb48f7e442c751bb6b4883157c5abaf618cd08/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68", size = 558193, upload-time = "2026-10-04T16:32:24.4Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d0/869ab6fb08531f97aae4a780b4d61b4190d8aef48fe054ee28bcf7114466/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_i686.whl", hash = "sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da", size = 622448, upload-time = "2026-10-04T16:32:26.844Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a5/9672e532fe02cd3b92c78cfd8743abca939d96853006c63835009a1ecb6f/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448", size = 591126, upload-time = "2026-10-04T16:32:29.274Z" }, + { url = "https://files.pythonhosted.org/packages/80/ef/3a9f8e4279c7920af561deed4cb7ebebed2794a8f608cf404d5eef14a105/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_10_12_x86_64.whl", hash = "sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0", size = 357108, upload-time = "2026-10-04T16:32:31.641Z" }, + { url = "https://files.pythonhosted.org/packages/b7/55/4b2fa381a583760aea5c92841e4e928a358e1c6511b129219e9c2826a226/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37", size = 346877, upload-time = "2026-10-04T16:32:34.061Z" }, +] + [[package]] name = "rq" version = "2.7.0" @@ -9207,7 +9944,8 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -9323,7 +10061,8 @@ name = "scipy" version = "1.17.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", @@ -9422,7 +10161,7 @@ dependencies = [ { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, { name = "openai" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" } }, { name = "pyyaml" }, { name = "regex" }, { name = "tiktoken" }, @@ -9703,7 +10442,8 @@ name = "sphinx" version = "9.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", ] @@ -9791,7 +10531,8 @@ version = "2.0.49" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" } wheels = [ @@ -9907,7 +10648,7 @@ version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } wheels = [ @@ -9929,7 +10670,7 @@ version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/f0/8d/e218e0160cc1b692e6e0e5ba34e8865dbb171efeb5fc9a704544b3020605/taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d", size = 11504, upload-time = "2025-01-03T09:24:13.761Z" } wheels = [ @@ -9956,63 +10697,70 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.12.0" +version = "0.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/62/167a842aa0429d45f5e797354fd4343a96f6043d67d0513c675c7b8d36e6/tiktoken-0.14.0.tar.gz", hash = "sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874", size = 38898, upload-time = "2026-08-17T19:49:49.514Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", size = 1051991, upload-time = "2025-10-06T20:21:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", size = 995798, upload-time = "2025-10-06T20:21:35.579Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", size = 1129865, upload-time = "2025-10-06T20:21:36.675Z" }, - { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", size = 1152856, upload-time = "2025-10-06T20:21:37.873Z" }, - { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", size = 1195308, upload-time = "2025-10-06T20:21:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", size = 1255697, upload-time = "2025-10-06T20:21:41.154Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", size = 879375, upload-time = "2025-10-06T20:21:43.201Z" }, - { url = "https://files.pythonhosted.org/packages/de/46/21ea696b21f1d6d1efec8639c204bdf20fde8bafb351e1355c72c5d7de52/tiktoken-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb", size = 1051565, upload-time = "2025-10-06T20:21:44.566Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d9/35c5d2d9e22bb2a5f74ba48266fb56c63d76ae6f66e02feb628671c0283e/tiktoken-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa", size = 995284, upload-time = "2025-10-06T20:21:45.622Z" }, - { url = "https://files.pythonhosted.org/packages/01/84/961106c37b8e49b9fdcf33fe007bb3a8fdcc380c528b20cc7fbba80578b8/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc", size = 1129201, upload-time = "2025-10-06T20:21:47.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d0/3d9275198e067f8b65076a68894bb52fd253875f3644f0a321a720277b8a/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded", size = 1152444, upload-time = "2025-10-06T20:21:48.139Z" }, - { url = "https://files.pythonhosted.org/packages/78/db/a58e09687c1698a7c592e1038e01c206569b86a0377828d51635561f8ebf/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd", size = 1195080, upload-time = "2025-10-06T20:21:49.246Z" }, - { url = "https://files.pythonhosted.org/packages/9e/1b/a9e4d2bf91d515c0f74afc526fd773a812232dd6cda33ebea7f531202325/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967", size = 1255240, upload-time = "2025-10-06T20:21:50.274Z" }, - { url = "https://files.pythonhosted.org/packages/9d/15/963819345f1b1fb0809070a79e9dd96938d4ca41297367d471733e79c76c/tiktoken-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def", size = 879422, upload-time = "2025-10-06T20:21:51.734Z" }, - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, + { url = "https://files.pythonhosted.org/packages/5e/82/d60a7a5d7bff7b4641d556ea68ea5914ea6edc3774a12eb1c0d444701382/tiktoken-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91", size = 1095817, upload-time = "2026-08-17T19:48:31.788Z" }, + { url = "https://files.pythonhosted.org/packages/18/e2/d39ae33d3dc30a0c229ff0cb683df961ebb5e7b8691feb2d08b3ee6ac327/tiktoken-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1", size = 1043064, upload-time = "2026-08-17T19:48:33.138Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e9/8e18cbee0c3ae8321c7e9696bef6090a24eed99a4a75a4c4a7f5115e5a2f/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c", size = 1190381, upload-time = "2026-08-17T19:48:34.386Z" }, + { url = "https://files.pythonhosted.org/packages/af/c8/051e7b72a816ff50eb34a1c7c5b185cd2429ffdf59a497baea35b2b6b2dd/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7", size = 1206869, upload-time = "2026-08-17T19:48:35.581Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b3/7795db206adb6a57d6137fe48ef2cca6b9707e90b86ee8244671592ddc33/tiktoken-0.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33", size = 1255197, upload-time = "2026-08-17T19:48:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/c8/39/5234783af6b81af645ccdf9438f2f02af472f14e91d876ca2079af641841/tiktoken-0.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14", size = 1319329, upload-time = "2026-08-17T19:48:37.944Z" }, + { url = "https://files.pythonhosted.org/packages/88/cf/f2d955c8c5c6c67cc86ba6fb132c47c710465ebe6a6dcec1c3b6e250660e/tiktoken-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c", size = 944146, upload-time = "2026-08-17T19:48:39.011Z" }, + { url = "https://files.pythonhosted.org/packages/8f/c5/9d848b7f408241171e1f843deb8bfa626086452bc9c78beee500829583e3/tiktoken-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79", size = 1094971, upload-time = "2026-08-17T19:48:40.347Z" }, + { url = "https://files.pythonhosted.org/packages/2d/a9/d94302340304328961d6f0c35ca4e60617fbb57a5cf667e2ed1692cb9e57/tiktoken-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948", size = 1042916, upload-time = "2026-08-17T19:48:41.541Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b6/31da98ee871383509cae2ba96a9ddef1965e3c4f8cb6dc7bcda3379398db/tiktoken-0.14.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f", size = 1188650, upload-time = "2026-08-17T19:48:42.729Z" }, + { url = "https://files.pythonhosted.org/packages/24/65/8c5dddd7cb67f6571d154a58d7c6e2f07da54bf84c49b6a1839965b7c35e/tiktoken-0.14.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513", size = 1206378, upload-time = "2026-08-17T19:48:44.013Z" }, + { url = "https://files.pythonhosted.org/packages/d1/04/522ec59d30dd9a2f3ab837011cd4fc5d1178dc4a2fa07c9fa4b90af6ba9d/tiktoken-0.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78", size = 1253694, upload-time = "2026-08-17T19:48:45.597Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/9019e272bad188a1c61ecf44f25a9ba2368744644e3ac1f3d6516f3c9e80/tiktoken-0.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e", size = 1317873, upload-time = "2026-08-17T19:48:46.792Z" }, + { url = "https://files.pythonhosted.org/packages/24/7f/fff1217240343c0c11b5938b98aeae0e3a266cacfac25f86f91cdcd748f0/tiktoken-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da", size = 944395, upload-time = "2026-08-17T19:48:48.028Z" }, + { url = "https://files.pythonhosted.org/packages/8c/da/e273746b9d24a63c776bc60fba914351573ad9c575b52601eb5e60632564/tiktoken-0.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36", size = 1094408, upload-time = "2026-08-17T19:48:49.269Z" }, + { url = "https://files.pythonhosted.org/packages/69/9f/fe6b1aca23331aa5271df5a4bd07bf68a7059254d47faee1b8272592a777/tiktoken-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4", size = 1038499, upload-time = "2026-08-17T19:48:50.666Z" }, + { url = "https://files.pythonhosted.org/packages/0b/35/e9f47647c9e163bd1de30fe1a491669b7248cfc67b7404c35c009a701e1a/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6", size = 1186355, upload-time = "2026-08-17T19:48:51.93Z" }, + { url = "https://files.pythonhosted.org/packages/51/11/9976ad86980a00cdef05e730a0127a2578a1bc6d11644d8d47246de2eb26/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d", size = 1204197, upload-time = "2026-08-17T19:48:53.18Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9c/7035b0bcfaa68d1ee4803fc5be5214ad865669b05bd20e7105ae8a18afc6/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482", size = 1250635, upload-time = "2026-08-17T19:48:54.392Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/69cabf18bed7f4366da076735816abce0d4db3fae491ae338a6612128777/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6", size = 1316085, upload-time = "2026-08-17T19:48:55.525Z" }, + { url = "https://files.pythonhosted.org/packages/bd/bd/a2e884fb1402cba5be08836590320012b2d8ada0e2eef9911a64df4bcd2d/tiktoken-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3", size = 941208, upload-time = "2026-08-17T19:48:56.938Z" }, + { url = "https://files.pythonhosted.org/packages/50/53/ee1453623bf65f019328721ccb6587846d2c5b7b82f34e73ca09101f072e/tiktoken-0.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f", size = 1094198, upload-time = "2026-08-17T19:48:57.955Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5f/6448cfe278c3664ba9ec5b5ac08344341f7dc3d42888476e215a14eda2be/tiktoken-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94", size = 1038820, upload-time = "2026-08-17T19:48:59.015Z" }, + { url = "https://files.pythonhosted.org/packages/69/3b/d67eac1bcce9dee3abe23aff5e3ded3116bbebaf67b80a0811c06d3806fc/tiktoken-0.14.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06", size = 1186175, upload-time = "2026-08-17T19:49:00.068Z" }, + { url = "https://files.pythonhosted.org/packages/37/62/cae690d9783146b0f81f564ada0f8f611de68178c0c9c7e1e969f0516b48/tiktoken-0.14.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d", size = 1203884, upload-time = "2026-08-17T19:49:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1e/633e30237b94e383cf814145499079f3bb9cdd4aeafc1bc42e01b0f810a6/tiktoken-0.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010", size = 1250980, upload-time = "2026-08-17T19:49:02.274Z" }, + { url = "https://files.pythonhosted.org/packages/cb/56/4c12f07b812f84206f38d723eb1ebfdd34bad9309b5dbc0bee6bbcff4cbf/tiktoken-0.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632", size = 1315434, upload-time = "2026-08-17T19:49:03.434Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e0/c65603f0c44811def666d3fbf611bf2af3b5e1ef613e06c19411419830b3/tiktoken-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1", size = 940883, upload-time = "2026-08-17T19:49:04.583Z" }, + { url = "https://files.pythonhosted.org/packages/59/b0/1cf129f4af8fc513931f931023def596b7c4bfc77026513cd9d851da9e88/tiktoken-0.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450", size = 1096273, upload-time = "2026-08-17T19:49:05.807Z" }, + { url = "https://files.pythonhosted.org/packages/62/85/2ae74575e321148484147e10b53c3b1717c59ebaa9edb4fe18b1f5c055f8/tiktoken-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b", size = 1040269, upload-time = "2026-08-17T19:49:06.943Z" }, + { url = "https://files.pythonhosted.org/packages/89/29/92a1120a12e4bcf2d5464350d1a91b68a433d63ce656bb7f806c27aec09c/tiktoken-0.14.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e", size = 1186101, upload-time = "2026-08-17T19:49:08.102Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7d/144af98dc5ad68108451a82e2f5a17f80e2663f5115058b8dfd215c1ad02/tiktoken-0.14.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42", size = 1204457, upload-time = "2026-08-17T19:49:09.28Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1f/be7cb06ab2108f612f3e92e7b76cf391e192db0db37a984616f0cc32aafc/tiktoken-0.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c", size = 1251716, upload-time = "2026-08-17T19:49:10.509Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6b/81f158d0f90adb826cd704069c2129a046cb784a2a09861009519fc41cf4/tiktoken-0.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771", size = 1315432, upload-time = "2026-08-17T19:49:11.844Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ec/f5fa35ec13f07279fdcaf3cc9c04bbb154ea591d23978651f2b672593e8a/tiktoken-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098", size = 988046, upload-time = "2026-08-17T19:49:13.282Z" }, + { url = "https://files.pythonhosted.org/packages/68/c9/7756717408d3d0dfea3f046c9466144b28afde39ff69d5808f2475dcd7f5/tiktoken-0.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438", size = 1096261, upload-time = "2026-08-17T19:49:14.351Z" }, + { url = "https://files.pythonhosted.org/packages/79/29/46ad8061f57bd9f8b2ea0aa82bf574e0f2aa040b0857a1582adba9957899/tiktoken-0.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa", size = 1040183, upload-time = "2026-08-17T19:49:15.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/7c/3184d17b868456f17b60b1a75f5ec0405618a43aa753336df341d8f11781/tiktoken-0.14.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037", size = 1186719, upload-time = "2026-08-17T19:49:16.84Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e8/46de4400d5bf859f640feee85bd7e32235f68ddf25db53c63be78e581e3a/tiktoken-0.14.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef", size = 1204660, upload-time = "2026-08-17T19:49:17.987Z" }, + { url = "https://files.pythonhosted.org/packages/29/ce/af8964c38bc8226dd8950305b7a255fa33345d5572f78af7275a313d28e0/tiktoken-0.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a", size = 1250932, upload-time = "2026-08-17T19:49:19.28Z" }, + { url = "https://files.pythonhosted.org/packages/1d/4b/323631116fc986d9cc5bbeb2b8223c7c85e61a8bb94ea5ab4951023b149b/tiktoken-0.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58", size = 1315190, upload-time = "2026-08-17T19:49:20.467Z" }, + { url = "https://files.pythonhosted.org/packages/18/8b/ba48a73729c9270989b36f37ab2ed5525e52690d715097c9fa791aaa5d05/tiktoken-0.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0", size = 987717, upload-time = "2026-08-17T19:49:21.704Z" }, + { url = "https://files.pythonhosted.org/packages/1d/10/b73b7e319179e0f60b32475f783b044f9cece872c53b6662664e9084b0d0/tiktoken-0.14.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232", size = 1096280, upload-time = "2026-08-17T19:49:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6b/09999a9bf1d559670d1680e8f8e419ac0e2c5f6aac82e9bfdf70f260b30a/tiktoken-0.14.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695", size = 1040433, upload-time = "2026-08-17T19:49:23.998Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7b/8537be0836f3df99b2a636b44399bfa43cd757f2b8b4097dacb794cf24a7/tiktoken-0.14.0-cp315-cp315-manylinux_2_28_aarch64.whl", hash = "sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49", size = 1186989, upload-time = "2026-08-17T19:49:25.021Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9d/f9c56d7a943a4468abf9ef37661bb9b8e0cd3aa8aa87368c7146cc3f3222/tiktoken-0.14.0-cp315-cp315-manylinux_2_28_x86_64.whl", hash = "sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4", size = 1204615, upload-time = "2026-08-17T19:49:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d2/98a38579db25c4a8a84e31dd95d9072ec5f21f7e70de591da0412e29b25b/tiktoken-0.14.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871", size = 1251828, upload-time = "2026-08-17T19:49:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/0c/83/467be424746c039c5493c0f4102feab16b9b48eb6f5c089b2a2438e3cde2/tiktoken-0.14.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f", size = 1316260, upload-time = "2026-08-17T19:49:29.101Z" }, + { url = "https://files.pythonhosted.org/packages/02/ee/ddf46ca78e371f5890e96b6e7d089a85b3536432be219851eb0481786ca8/tiktoken-0.14.0-cp315-cp315-win_amd64.whl", hash = "sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea", size = 988230, upload-time = "2026-08-17T19:49:30.246Z" }, + { url = "https://files.pythonhosted.org/packages/2a/00/5162e90c851a28da18ed382d34898b79a8022548e5619a64e14c03ce7c3d/tiktoken-0.14.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890", size = 1096186, upload-time = "2026-08-17T19:49:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/65/97/a5a7bfccf25b1bb65e82bae8edff11ac3c9c041c374b7b4a823d60c38133/tiktoken-0.14.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5", size = 1039947, upload-time = "2026-08-17T19:49:32.848Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ba/ef427fc638f1439181c5e12dd26b70e881861f89c007aa7e5b36300f8342/tiktoken-0.14.0-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae", size = 1186997, upload-time = "2026-08-17T19:49:34.121Z" }, + { url = "https://files.pythonhosted.org/packages/3e/88/2f3f85a968cdc514152129af0a060ebcccb067005a2f29b0d5ef3c838514/tiktoken-0.14.0-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1", size = 1205211, upload-time = "2026-08-17T19:49:35.284Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f6/80760e98a08e6649d2d68afb6035af713121dfb615acce8c4f73810ec438/tiktoken-0.14.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89", size = 1251479, upload-time = "2026-08-17T19:49:36.419Z" }, + { url = "https://files.pythonhosted.org/packages/c5/84/50966fb6918a0fb9b32721277e5342bf729a2d74350074d662fbedf9772e/tiktoken-0.14.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3", size = 1316673, upload-time = "2026-08-17T19:49:37.756Z" }, + { url = "https://files.pythonhosted.org/packages/35/5e/9b01afd037bfa22a0033963fa091e0f75b6fb15cd85bffb42ff86e697323/tiktoken-0.14.0-cp315-cp315t-win_amd64.whl", hash = "sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9", size = 987929, upload-time = "2026-08-17T19:49:38.947Z" }, ] [[package]] @@ -10179,7 +10927,8 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "opentelemetry-semantic-conventions-ai" }, { name = "posthog" }, - { name = "pydantic" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "pydantic", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, { name = "tenacity" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0a/b1/fd7360d97c651098da505e95600e067a7eedb1b78635b2f1d23545ee4a46/traceloop_sdk-0.34.0.tar.gz", hash = "sha256:4aa26003dfa2e417f73728bd847284a12d6da43a946dd588603a0966e753b3e6", size = 19808, upload-time = "2024-12-12T21:03:41.647Z" } @@ -10212,7 +10961,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore-stubs" }, { name = "types-s3transfer" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bd/9c/904b71c1ffb9ddbfe0367e36ddd142c12a192b958cc10701d09888fb8beb/types_boto3-1.43.30.tar.gz", hash = "sha256:f4d9295a136325f5086f3967e33ec769555004b299bd11173875772393d5d907", size = 103364, upload-time = "2026-06-15T21:23:31.718Z" } wheels = [ @@ -10247,7 +10996,7 @@ name = "types-boto3-bedrock" version = "1.43.26" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/99/d7/22e117e8077f51b704d67a4c48deca60a893fc6c6efd13a1e582ab8b4049/types_boto3_bedrock-1.43.26.tar.gz", hash = "sha256:55c338ae47aef6f98ba1f188bc2e9f02794efbc346b68606bbe9751d4e1405a5", size = 67312, upload-time = "2026-06-09T20:33:02.407Z" } wheels = [ @@ -10259,7 +11008,7 @@ name = "types-boto3-bedrock-agent" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ea/d0/7a4111691706006ba3e9ad9ddd1cd7bb562ade4138171126e0c27d2e7901/types_boto3_bedrock_agent-1.43.0.tar.gz", hash = "sha256:a3f5d8404e31c8315318e6149a6714930cdbddae84c610bb2483a13cac0a89fa", size = 53500, upload-time = "2026-04-29T22:59:28.167Z" } wheels = [ @@ -10271,7 +11020,7 @@ name = "types-boto3-bedrock-runtime" version = "1.43.30" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/28/dd863429fbcc7a38389b5d287836e40d9df20398d6c215387122b3453779/types_boto3_bedrock_runtime-1.43.30.tar.gz", hash = "sha256:0e79ec50a26b12b2da17a203983c81b60982abe7e17c464a5cf74c3a6637f504", size = 31282, upload-time = "2026-06-15T21:23:19.526Z" } wheels = [ @@ -10283,7 +11032,7 @@ name = "types-boto3-kms" version = "1.43.12" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/46/7343b52e16eaa9dec7099cdd6a901317df583473b1658d04cb42885c8d03/types_boto3_kms-1.43.12.tar.gz", hash = "sha256:f9a06ca5a1cbf02f820208f1e84983a750daa1bce305bd11231961a9d770d9cd", size = 30696, upload-time = "2026-05-20T20:01:12.294Z" } wheels = [ @@ -10295,7 +11044,7 @@ name = "types-boto3-s3" version = "1.43.14" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3c/79/ddd397734d7c6368492447c95be54e76158e7dc0d4e616117bf2b2430af0/types_boto3_s3-1.43.14.tar.gz", hash = "sha256:50d1fc0082f07be097184cf647e2dec6101fd1f8378a6c353100ccd067b95e4d", size = 76899, upload-time = "2026-05-22T20:48:17.311Z" } wheels = [ @@ -10307,7 +11056,7 @@ name = "types-boto3-sagemaker-runtime" version = "1.43.29" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/99/57/cc95a58135f2e1ec7af94e4b29f79ad6bdb6a44da9e4983c8e545f4693c1/types_boto3_sagemaker_runtime-1.43.29.tar.gz", hash = "sha256:a7efd7828f52f2d6b2656ea2d99eb1de56b846304ac7ad1b5d603770ad27b789", size = 15771, upload-time = "2026-06-12T20:09:00.99Z" } wheels = [ @@ -10319,7 +11068,7 @@ name = "types-boto3-sts" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/a7/ea448e34f9b519b68505df256e8cc185d60ef8aeb41552553f66da5a7b35/types_boto3_sts-1.43.0.tar.gz", hash = "sha256:d8e0061fed51bb246bd966b9968104bc44411450faa8848f26170bf271913ab1", size = 16823, upload-time = "2026-04-29T23:07:24.448Z" } wheels = [ @@ -10407,18 +11156,38 @@ wheels = [ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + [[package]] name = "typing-inspect" version = "0.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } wheels = [ @@ -10429,14 +11198,36 @@ wheels = [ name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] +dependencies = [ + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + [[package]] name = "tzdata" version = "2026.2" @@ -10596,7 +11387,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } wheels = [ @@ -11165,6 +11956,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/aa/157b940428da80c104ca09666a740e51c94963df65d5b112e06b52e4d7a8/yarl-1.25.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65b5b2066651b7432d389e9799d979c703bcc6ef44266bb8153ef54e91e4aab3", size = 115822, upload-time = "2026-09-15T19:33:26.886Z" }, { url = "https://files.pythonhosted.org/packages/7e/af/19fbdce41412e1b96825544cc52cd7029d3724655d0988237972f078bd29/yarl-1.25.1-cp314-cp314t-win_amd64.whl", hash = "sha256:734f6e5400352ac4254456003d462866c684703570929cff7a7bde015d0cb371", size = 107386, upload-time = "2026-09-15T19:33:29.009Z" }, { url = "https://files.pythonhosted.org/packages/2a/99/f6431c8968e89be608d74b28ae2d024521b2953f27dd44e0dece5e04f67a/yarl-1.25.1-cp314-cp314t-win_arm64.whl", hash = "sha256:287e99ff5aa4dc1c7630bfc683ded6f106d756c99dec432a2d7f197a784f51c6", size = 102094, upload-time = "2026-09-15T19:33:31.151Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3b/4f51eab40c2eabea6c3d5b121dff4b8988dc35087732ffede12d2be8b8dd/yarl-1.25.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9b1bdaae98bc016825dd3c9d8ee1832f829b3341f9cc6ebd1a1b0a7fef7367cc", size = 143875, upload-time = "2026-09-15T19:33:33.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/05/bbd58fc063f5f299a883f810760b265ca26c8167c91cb9a494d0fe2387e1/yarl-1.25.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:e7011b8fb8c4054bf0c12e5edc6cd83778b0028e99ce59b18586ed036f92cfdc", size = 104028, upload-time = "2026-09-15T19:33:36.22Z" }, + { url = "https://files.pythonhosted.org/packages/12/ee/2fba0aecb52e7020e189f684148783aa0b9cfa3b3bfb0b400646eef70ad4/yarl-1.25.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f074e8d4aa0a5798920ddb6de3d08b228c614ff3724c3e8bd7577f4bafea867b", size = 104041, upload-time = "2026-09-15T19:33:38.86Z" }, + { url = "https://files.pythonhosted.org/packages/38/35/884beab53ed88c7247d1671972b5ef116f7351fe0c7e6de8c3558372cb16/yarl-1.25.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d42e7e3ca399555578b4d617e3a6ecf13371b3743a115995fa010c7bf341459", size = 116018, upload-time = "2026-09-15T19:33:44.265Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/1a91b685afb55cc18608443ace95280e96e263a97565811732b6788d3269/yarl-1.25.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aa4ed3dd308548f9e707d9caaf005d2d7f8c1e7868f858dfeb47fe76e16b391d", size = 107033, upload-time = "2026-09-15T19:33:46.45Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c1/58b379fcb1d68d907b7fcf75200c44321896509b2a6a74abbb4b19d864d2/yarl-1.25.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42a66563d8cc056ee32e6191e05097a7b2b3bc302e0bc3133daf8710eb18bd26", size = 123257, upload-time = "2026-09-15T19:33:48.631Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2d/1fe96cf5c2aeab10095e48f38585cf5a8451fb7253234822398e52aa5336/yarl-1.25.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98d370568f393215d605304cdb77b3d5539bd192c75b623c7304c42c8d6d8273", size = 126745, upload-time = "2026-09-15T19:33:50.999Z" }, + { url = "https://files.pythonhosted.org/packages/ad/60/8674394ce43f4dadae573a1d6f451716438e9eab7d7fe8d643c673a32d85/yarl-1.25.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23bf5b403c879a54964e0feac7285688e04bb220074878d737d331522da0a5bf", size = 117255, upload-time = "2026-09-15T19:33:53.456Z" }, + { url = "https://files.pythonhosted.org/packages/2f/72/0faa30e02605d56127d42bb987dcc97da3863b7bf70b9bfbf5f739c05e30/yarl-1.25.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d45673badd08456d0340e9364eddafe1c53a9d2896424294de4d7dd71ad3ee57", size = 115166, upload-time = "2026-09-15T19:33:55.669Z" }, + { url = "https://files.pythonhosted.org/packages/8c/90/9a46eac564c437e128285c5c1d7bb385d268394e9209d84f6bf4a14471ef/yarl-1.25.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0136d640dfa9b0523853e411430a99f8a91eca85774c6420285a33b755bc6de3", size = 116082, upload-time = "2026-09-15T19:33:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/4b1a686a3758878f93d2f1ea943f5a165f3555769cd16e761cfd0efdca17/yarl-1.25.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:59ba3a6e1aa8cfe5adf4bd270fd965db21955401b7ca6f1696010c55ed4daec2", size = 108048, upload-time = "2026-09-15T19:34:00.25Z" }, + { url = "https://files.pythonhosted.org/packages/da/14/f348eb967f31a58348612a2b93bd8a2ab664548e2b5e879cac7f592f7201/yarl-1.25.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:87796fedc3ba97ec14fab55acb48584276e6c1e4c1e89c422bda62c838e754a9", size = 122775, upload-time = "2026-09-15T19:34:02.455Z" }, + { url = "https://files.pythonhosted.org/packages/ab/e9/4f7b79700f88cb9e8bb66f8b54f9bce1844c013a2c39fdc47112e9334c95/yarl-1.25.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bd0912757081f89b107d6c00b2ff8a194401b0b87eadcf4481de2b865a8fd44f", size = 115096, upload-time = "2026-09-15T19:34:05.283Z" }, + { url = "https://files.pythonhosted.org/packages/cf/37/f9cb020331997d3eb887bd28d5410ecfd3d80bf163c23d7cec490d78dade/yarl-1.25.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:b51c159a9794633f5e0db7ecec7b2b6e3734eca1f5d17dc989ff3552a43ff78b", size = 120655, upload-time = "2026-09-15T19:34:07.382Z" }, + { url = "https://files.pythonhosted.org/packages/12/83/52fceb22891a41f168db7ec22fd1d81e06b6a0b8d9f70921bd3e785defd0/yarl-1.25.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:319e070a01db9920fb63761843f96a104c8e2b9427266731810dc1e22595b17c", size = 117488, upload-time = "2026-09-15T19:34:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5c/ce6c4ff1247fcbe4b33d462c23a097106d909b173fde7042bc52290466e2/yarl-1.25.1-cp315-cp315-win_amd64.whl", hash = "sha256:a2059a2d891bd156bc5184e7ab7a56e78a84dfcfdeac8c501b552533ad1c36ee", size = 103434, upload-time = "2026-09-15T19:34:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/766a906b0704fb26d52b19dc22bed48a8ba0544203b70dcf44da350e8194/yarl-1.25.1-cp315-cp315-win_arm64.whl", hash = "sha256:a78b50b4f7918a3de71105d5c0b93bbc57bb8339a4d03a9dfd449f9068e76f3d", size = 99155, upload-time = "2026-09-15T19:34:15.132Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/a1d09b32cb6ab14f66b44939f5b4255b8b9e747aef3974af1d5d80ccc2fd/yarl-1.25.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:b5402a340723fa7da00b5cff987ddab61276be6d11251ea71ae02bcac54890d8", size = 149284, upload-time = "2026-09-15T19:34:17.452Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/fe554d879692650bca70bfbc0df124e82e4d2bb7456f698c7756f1279a96/yarl-1.25.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:eda19ea5ee88742f47a2340816e6f2d40b53bed3ab5b69794769f36af9f35bb4", size = 106399, upload-time = "2026-09-15T19:34:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4b/e7af56177ac8d40094c82d7728224c0b8472157d50d362e5fb3b014b2bc8/yarl-1.25.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:75baa6cf9b6d1c52f3e111a130e202fd8cf0a5b3a066c3f73d615e885092e4ec", size = 106968, upload-time = "2026-09-15T19:34:23.619Z" }, + { url = "https://files.pythonhosted.org/packages/da/4f/2df41fd738d46f23ef829ae8b4468d94bb6070038fc6dfab6165ed44fea8/yarl-1.25.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbcef5a9119ef653653132cccaf999b30a0af6f33bb0a4ba80bec30056868487", size = 114717, upload-time = "2026-09-15T19:34:25.879Z" }, + { url = "https://files.pythonhosted.org/packages/69/ea/002b66df53bbd1aed1c23358ff99c9bdc744fe3f4d2740e1b2fdd7192885/yarl-1.25.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7efc9f082dfed77c316edffa9deb52888e1bc6789171887cc1f68e06d65465c8", size = 105198, upload-time = "2026-09-15T19:34:28.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7c/95c8bc0c8f97d71e59c94525ad60d76f5c57d3f2820f08137ca8b9f0542a/yarl-1.25.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe01645169a2112aa1d4ebc3e4c5f029c5c8f97adfc32e5d37c993b39a994d75", size = 120271, upload-time = "2026-09-15T19:34:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/6fe9da56ec77927baa669fd86c39c567ce6205bab53d581082c6744c8ae7/yarl-1.25.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c557dfd5e3db046053a0bdc72261ade790ebe8e2c7a41b36b0ca1f14cb95f3", size = 123572, upload-time = "2026-09-15T19:34:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/8b/83/35f222d17fa70a14c7c74fdf112ccf5515e0c2a87082b1f9b99f7693bf57/yarl-1.25.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ce4d6ccafb33d39bd78444612d14938ead674c25702ded2ee9c54a47735d225", size = 115228, upload-time = "2026-09-15T19:34:35.344Z" }, + { url = "https://files.pythonhosted.org/packages/da/6f/fbaaf619423578a7d898d0f226ae47bc1906c293865c416d83c17b828b0e/yarl-1.25.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80a063f8297fc796296f00f100be520f209b23dc98f93ce8eba6ee7122598209", size = 111618, upload-time = "2026-09-15T19:34:37.656Z" }, + { url = "https://files.pythonhosted.org/packages/ba/78/7383278f1b3cf8e0496bd95b3281a7b09b89217b6b428db24c6b99b3deca/yarl-1.25.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7cb414a73e21a7ab58254926073f2930cb22f5b4314ea4260a687e2b3fd4dce3", size = 114839, upload-time = "2026-09-15T19:34:40.099Z" }, + { url = "https://files.pythonhosted.org/packages/62/49/5506e5b6d29aab91bd845cc9016d88c3d3f81b81bc242b8100bdd5737825/yarl-1.25.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:85a18376073f8a39aa07be34f9fc77e2869aa72c55c441efdd2cf79a0407504d", size = 106212, upload-time = "2026-09-15T19:34:42.768Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/19877c193b7c5929f4b07118c18bbe390f3fadd0f59dd98c0cd12b31fa5c/yarl-1.25.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:77716e245c90f058466a05e6a465bb8600f767a8f4b18b4d40f3aff958e5f73c", size = 119985, upload-time = "2026-09-15T19:34:44.976Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6c/0a46fbbf9ecbcbd0cc20d2193394254b9e19817f22c814aab60f99847400/yarl-1.25.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:1e80dcf1446e1b080b1932b0d103c464a04112f5bc31f0f983ad418172063cde", size = 112081, upload-time = "2026-09-15T19:34:47.45Z" }, + { url = "https://files.pythonhosted.org/packages/ef/30/93f5d471230c74ccd06255d0842739f86551f937f9e63a5e947853c6244a/yarl-1.25.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:bdc8d8b8c22e9e43ac68316b5e6cf083dec537f4ec213cb4aa967b583bc3fa64", size = 116995, upload-time = "2026-09-15T19:34:49.972Z" }, + { url = "https://files.pythonhosted.org/packages/2b/80/c386593035ee3f9c6c6af0847b5578f2830c674794a9d7701b744a3ebd42/yarl-1.25.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbf531053a0935f2e871bcd4753f90313688772ff8c017f5ea402e315a78c1f", size = 115570, upload-time = "2026-09-15T19:34:52.628Z" }, + { url = "https://files.pythonhosted.org/packages/38/02/eef443559563ef8f2e10469387b8b1e97cb5efee95b288a56da60801f7ee/yarl-1.25.1-cp315-cp315t-win_amd64.whl", hash = "sha256:b13b88747769537f3d32e89e3a735da10c0a9e35d7322928c701b5f93d3afffd", size = 106811, upload-time = "2026-09-15T19:34:54.935Z" }, + { url = "https://files.pythonhosted.org/packages/88/91/41e284ca2cf5211e05dae031d126a3668aea88fa759df56e7e35c6ad25ba/yarl-1.25.1-cp315-cp315t-win_arm64.whl", hash = "sha256:783dd1467083f4d3f7722ad6a313f24c173e7571372738fcb7a6e6d1ba48df25", size = 101804, upload-time = "2026-09-15T19:34:57.231Z" }, { url = "https://files.pythonhosted.org/packages/54/22/318c7980066769c6bcd9221ed2248294f5698811da099013098c670565ed/yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3", size = 63617, upload-time = "2026-09-15T19:34:59.616Z" }, ] From e0982d62485bc0bbec67de3ebe58c16c465fd7c2 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 26 Oct 2026 10:10:57 -0700 Subject: [PATCH 03/73] build(deps): bump webpki-roots and webpki-root-certs to 1.0.11 (#48301) 1.0.11 drops emSign Root CA - C1 and emSign ECC Root CA - C3 from the built-in roots --- litellm-rust/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litellm-rust/Cargo.lock b/litellm-rust/Cargo.lock index c868d3e4b8a..cb50324e1dd 100644 --- a/litellm-rust/Cargo.lock +++ b/litellm-rust/Cargo.lock @@ -8202,18 +8202,18 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ec80e73fd8baf57bf0d9615554c75348bda990d6d5bbeab964dd068319fecb" +checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1c8e20c8a38fc7d65abfe74eb7280e4aa667e8eec8bc84996d2874810748d9" +checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" dependencies = [ "rustls-pki-types", ] From 96792d09138deecd5bc9eb5a746e040ea6f49beb Mon Sep 17 00:00:00 2001 From: Arsh Goyal Date: Mon, 26 Oct 2026 23:33:37 +0530 Subject: [PATCH 04/73] test(streaming): pin that a stream stuck on one chunk closes its provider stream and logs one failure (#38753) * fix(streaming): stop re-raising and re-logging a failed stream on every next() A stream that raised keeps its provider iterator alive, so a consumer that iterates past the error re-detected, re-raised and re-logged the same failure on every call, flooding failure callbacks for one request. Latch the failure and re-raise it without logging again. * fix(streaming): drop the retained traceback before re-raising a failed stream * test(streaming): count failures through a real logging callback and require a clean stop on later reads * test(streaming): require the stuck stream to close early and release its socket The provider stream now records a GeneratorExit and caps its chunks, so draining it or detecting the loop late fails. Two wire cases check a sync and an async stuck stream release their TCP session. * test(streaming): avoid failure recorder name collision --------- Co-authored-by: Devin AI Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: moe-berri --- ...est_sync_stream_connection_release_wire.py | 33 +++++ .../test_streaming_handler.py | 118 ++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/tests/integration/sdk/test_sync_stream_connection_release_wire.py b/tests/integration/sdk/test_sync_stream_connection_release_wire.py index a8b49d285c3..21774df66d0 100644 --- a/tests/integration/sdk/test_sync_stream_connection_release_wire.py +++ b/tests/integration/sdk/test_sync_stream_connection_release_wire.py @@ -212,3 +212,36 @@ def test_sync_stream_that_raises_mid_stream_releases_the_connection() -> None: assert _open_sessions_settling_to(wire, at_most=0) == 0, ( "a stream that raised mid-way left its TCP session open" ) + + +_STUCK: Final = _Shape( + "hosted_vllm", + "hosted_vllm/stuck-model", + _OPENAI_OPENING, + (_openai_delta("the model is stuck on this", None),) * (10 * litellm.REPEATED_STREAMING_CHUNK_LIMIT), +) + + +def _still_generating_peer(request: Request) -> Reply: + return Reply(content_type="text/event-stream", chunks=(_STUCK.opening, *_STUCK.closing), pause_between_chunks=0.01) + + +def test_sync_stream_stuck_repeating_one_chunk_releases_the_connection() -> None: + with wire_server(_still_generating_peer, keep_alive=True) as wire: + stuck: Final = litellm.completion(**_request_kwargs(_STUCK, wire)) + with pytest.raises(Exception, match="repeating the same chunk"): + for _ in stuck: + pass + assert _open_sessions_settling_to(wire, at_most=0) == 0, "a stream stuck on one chunk left its TCP session open" + + +@pytest.mark.asyncio +async def test_async_stream_stuck_repeating_one_chunk_releases_the_connection() -> None: + with wire_server(_still_generating_peer, keep_alive=True) as wire: + stuck: Final = await litellm.acompletion(**_request_kwargs(_STUCK, wire)) + with pytest.raises(Exception, match="repeating the same chunk"): + async for _ in stuck: + pass + assert await _open_sessions_settling_to_while_the_loop_runs(wire, at_most=0) == 0, ( + "a stream stuck on one chunk left its TCP session open" + ) diff --git a/tests/unit/litellm_core_utils/test_streaming_handler.py b/tests/unit/litellm_core_utils/test_streaming_handler.py index c13cea51183..cc1fa1b8803 100644 --- a/tests/unit/litellm_core_utils/test_streaming_handler.py +++ b/tests/unit/litellm_core_utils/test_streaming_handler.py @@ -1,6 +1,7 @@ import asyncio import datetime import json +import queue import sys import threading import time @@ -2819,6 +2820,123 @@ def test_raise_on_model_repetition_tolerates_empty_choices( wrapper.raise_on_model_repetition() +_STUCK_CHUNK: Final = "the model is stuck on this" +_STUCK_PROVIDER_CHUNK_CAP: Final = 10 * litellm.REPEATED_STREAMING_CHUNK_LIMIT + + +class _StuckProvider: + def __init__(self) -> None: + self.closed_early = threading.Event() + self.chunks_sent = 0 + + def _next_chunk(self) -> ModelResponseStream: + self.chunks_sent += 1 + return _make_chunk(_STUCK_CHUNK) + + def sync_stream(self) -> Iterator[ModelResponseStream]: + try: + yield from (self._next_chunk() for _ in range(_STUCK_PROVIDER_CHUNK_CAP)) + except GeneratorExit: + self.closed_early.set() + raise + + async def async_stream(self) -> AsyncIterator[ModelResponseStream]: + try: + for _ in range(_STUCK_PROVIDER_CHUNK_CAP): + yield self._next_chunk() + except GeneratorExit: + self.closed_early.set() + raise + + +class _StuckStreamFailureRecorder(CustomLogger): + def __init__(self) -> None: + super().__init__() + self.failures: Final = queue.SimpleQueue[str]() + + def log_failure_event(self, kwargs: object, response_obj: object, start_time: object, end_time: object) -> None: + self.failures.put(str(cast(Mapping[str, object], kwargs)["exception"])) + + async def async_log_failure_event( + self, kwargs: object, response_obj: object, start_time: object, end_time: object + ) -> None: + self.failures.put(str(cast(Mapping[str, object], kwargs)["exception"])) + + +def _stuck_stream_wrapper( + provider_stream: Iterator[ModelResponseStream] | AsyncIterator[ModelResponseStream], + call_type: Literal["completion", "acompletion"], + recorder: _StuckStreamFailureRecorder, +) -> CustomStreamWrapper: + logging_obj: Final = Logging( + model="test-model", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type=call_type, + start_time=datetime.datetime(2026, 1, 1), + litellm_call_id=f"stuck-stream-{call_type}", + function_id=f"stuck-stream-{call_type}", + dynamic_failure_callbacks=[recorder], + dynamic_async_failure_callbacks=[recorder], + ) + logging_obj.update_environment_variables( + model="test-model", + optional_params={}, + litellm_params=get_litellm_params(custom_llm_provider="openai", acompletion=call_type == "acompletion"), + ) + return CustomStreamWrapper( + completion_stream=provider_stream, + model="test-model", + logging_obj=logging_obj, + custom_llm_provider="openai", + ) + + +@pytest.mark.asyncio +async def test_async_stream_stuck_on_one_chunk_closes_the_provider_stream_and_logs_one_failure(): + """https://github.com/BerriAI/litellm/issues/19428 and https://github.com/BerriAI/litellm/issues/13786""" + provider: Final = _StuckProvider() + recorder: Final = _StuckStreamFailureRecorder() + wrapper: Final = _stuck_stream_wrapper(provider.async_stream(), "acompletion", recorder) + + with pytest.raises(Exception, match="repeating the same chunk"): + async for _ in wrapper: + pass + assert provider.closed_early.is_set() + assert provider.chunks_sent <= litellm.REPEATED_STREAMING_CHUNK_LIMIT + 1 + chunks_sent_at_failure: Final = provider.chunks_sent + + with pytest.raises(StopAsyncIteration): + await wrapper.__anext__() + await wrapper.wait_for_failure_logging(timeout=5) + + assert provider.chunks_sent == chunks_sent_at_failure + assert "repeating the same chunk" in recorder.failures.get_nowait() + assert recorder.failures.empty() + + +def test_sync_stream_stuck_on_one_chunk_closes_the_provider_stream_and_logs_one_failure(): + """Sync counterpart of the async test above""" + provider: Final = _StuckProvider() + recorder: Final = _StuckStreamFailureRecorder() + wrapper: Final = _stuck_stream_wrapper(provider.sync_stream(), "completion", recorder) + + with pytest.raises(Exception, match="repeating the same chunk"): + for _ in wrapper: + pass + assert provider.closed_early.is_set() + assert provider.chunks_sent <= litellm.REPEATED_STREAMING_CHUNK_LIMIT + 1 + chunks_sent_at_failure: Final = provider.chunks_sent + assert "repeating the same chunk" in recorder.failures.get(timeout=5) + + with pytest.raises(StopIteration): + next(wrapper) + + assert provider.chunks_sent == chunks_sent_at_failure + with pytest.raises(queue.Empty): + recorder.failures.get(timeout=0.5) + + def test_usage_chunk_after_finish_reason_updates_hidden_params(logging_obj): """ Test that provider-reported usage from a post-finish_reason chunk From be3a7be1db6c4ee38d0d61957a2ae5179446c874 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 14:12:11 -0400 Subject: [PATCH 05/73] fix(ui): restore clipboard copying in key and log details (#48425) --- ui/litellm-dashboard/eslint-suppressions.json | 5 - .../AuditLogDrawer/AuditLogDrawer.test.tsx | 23 +- .../audit/AuditLogDrawer/AuditLogDrawer.tsx | 19 +- .../logs/detail/DrawerHeader.test.tsx | 25 ++ .../components/logs/detail/DrawerHeader.tsx | 6 +- .../components/logs/detail/InputCard.test.tsx | 42 +-- .../src/components/logs/detail/InputCard.tsx | 5 +- .../LogDetailContent.integration.test.tsx | 41 +++ .../logs/detail/LogDetailContent.tsx | 6 +- .../logs/detail/LogDetailsDrawer.test.tsx | 22 ++ .../logs/detail/LogDetailsDrawer.tsx | 6 +- .../logs/detail/OutputCard.test.tsx | 35 +-- .../src/components/logs/detail/OutputCard.tsx | 5 +- .../logs/detail/RealtimePrettyView.test.tsx | 22 +- .../logs/detail/RealtimePrettyView.tsx | 3 +- .../src/components/shared/CopyButton.test.tsx | 55 +--- .../src/components/shared/CopyButton.tsx | 12 +- .../src/utils/dataUtils.test.ts | 277 +++++++----------- ui/litellm-dashboard/src/utils/dataUtils.ts | 23 +- 19 files changed, 287 insertions(+), 345 deletions(-) diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 38d4c435e9a..32777b43504 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -2175,11 +2175,6 @@ "count": 1 } }, - "src/utils/dataUtils.test.ts": { - "max-nested-callbacks": { - "count": 1 - } - }, "tests/CreateKeyPage.expiredToken.test.tsx": { "@typescript-eslint/no-require-imports": { "count": 3 diff --git a/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.test.tsx b/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.test.tsx index da158fb8450..8ac99760c01 100644 --- a/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.test.tsx @@ -1,10 +1,16 @@ +import { copyToClipboard } from "@/utils/dataUtils"; import { describe, it, expect, vi, beforeEach } from "vitest"; import userEvent from "@testing-library/user-event"; -import { render, screen, waitFor, within } from "@testing-library/react"; +import { render, screen, within } from "@testing-library/react"; import moment from "moment"; import { AuditLogDrawer } from "./AuditLogDrawer"; import { AuditLogEntry } from "../AuditLogsTableColumns"; +vi.mock("@/utils/dataUtils", async (importOriginal) => ({ + ...(await importOriginal()), + copyToClipboard: vi.fn(), +})); + vi.mock("../../../common_components/DefaultProxyAdminTag", () => ({ default: ({ userId }: { userId: string }) => {userId}, })); @@ -120,16 +126,17 @@ describe("AuditLogDrawer", () => { expect(screen.queryByText(/"spend"/)).not.toBeInTheDocument(); }); - it("should copy the json of a block to the clipboard", async () => { + it.each([true, false])("confirms copied JSON only when copying succeeds (%s)", async (success) => { const user = userEvent.setup(); - const writeText = vi.fn().mockResolvedValue(undefined); - Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true }); - Object.defineProperty(window, "isSecureContext", { value: true, configurable: true }); - + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + vi.mocked(copyToClipboard).mockResolvedValue(success); render(); - await user.click(within(blockNamed("Before")).getByTitle("Copy JSON")); - await waitFor(() => expect(writeText).toHaveBeenCalledWith(JSON.stringify({ max_budget: 10 }, null, 2))); + const button = within(blockNamed("Before")).getByRole("button", { name: "Copy JSON" }); + await user.click(button); + + expect(copyToClipboard).toHaveBeenCalledWith(JSON.stringify({ max_budget: 10 }, null, 2)); + expect(button.querySelector(".lucide-check") !== null).toBe(success); }); describe("width", () => { diff --git a/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.tsx b/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.tsx index fae3c35b80c..eaddf1e507f 100644 --- a/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.tsx +++ b/ui/litellm-dashboard/src/components/logs/audit/AuditLogDrawer/AuditLogDrawer.tsx @@ -1,3 +1,4 @@ +import { copyToClipboard } from "@/utils/dataUtils"; import { Check, Copy } from "lucide-react"; import { useState, useCallback } from "react"; import moment from "moment"; @@ -27,25 +28,9 @@ function CopyableJsonBlock({ label, value }: { label: string; value: Record { - try { - const text = JSON.stringify(value, null, 2); - if (navigator.clipboard && window.isSecureContext) { - await navigator.clipboard.writeText(text); - } else { - const el = document.createElement("textarea"); - el.value = text; - el.style.position = "fixed"; - el.style.opacity = "0"; - document.body.appendChild(el); - el.focus(); - el.select(); - document.execCommand("copy"); - document.body.removeChild(el); - } + if (await copyToClipboard(JSON.stringify(value, null, 2))) { setCopied(true); setTimeout(() => setCopied(false), 2000); - } catch (e) { - console.error("Copy failed:", e); } }, [value]); diff --git a/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.test.tsx index 564862acccd..b24667c6a2e 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.test.tsx @@ -6,6 +6,12 @@ import { describe, expect, it, vi } from "vitest"; import { render } from "../../../../tests/test-utils"; import type { LogEntry } from "../types"; import { DrawerHeader } from "./DrawerHeader"; +import { copyToClipboard } from "@/utils/dataUtils"; + +vi.mock("@/utils/dataUtils", async (importOriginal) => ({ + ...(await importOriginal()), + copyToClipboard: vi.fn(), +})); const logEntry = (overrides: Partial): LogEntry => ({ @@ -118,3 +124,22 @@ describe("DrawerHeader back link", () => { expect(screen.queryByRole("button", { name: /^Back to/ })).not.toBeInTheDocument(); }); }); + +describe("DrawerHeader copying", () => { + it.each([true, false])("confirms both ids only when copying succeeds (%s)", async (success) => { + const user = userEvent.setup(); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + vi.mocked(copyToClipboard).mockReset().mockResolvedValue(success); + renderHeader(logEntry({ request_id: "request-copy", litellm_call_id: "call-copy" }), false); + + const requestButton = screen.getByRole("button", { name: "Copy Request ID" }); + const callButton = screen.getByRole("button", { name: "Copy x-litellm-call-id" }); + await user.click(requestButton); + await user.click(callButton); + + expect(copyToClipboard).toHaveBeenNthCalledWith(1, "request-copy"); + expect(copyToClipboard).toHaveBeenNthCalledWith(2, "call-copy"); + expect(requestButton).toHaveAccessibleName(success ? "Copied!" : "Copy Request ID"); + expect(callButton).toHaveAccessibleName(success ? "Copied!" : "Copy x-litellm-call-id"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.tsx b/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.tsx index 8ec615d444d..312a0797f1d 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/DrawerHeader.tsx @@ -1,3 +1,4 @@ +import { copyToClipboard } from "@/utils/dataUtils"; import { type ReactNode, useState } from "react"; import { ArrowLeft, Check, ChevronDown, ChevronUp, Copy, X } from "lucide-react"; import moment from "moment"; @@ -183,12 +184,9 @@ function CopyableId({ const [copied, setCopied] = useState(false); const handleCopy = async () => { - try { - await navigator.clipboard.writeText(value); + if (await copyToClipboard(value)) { setCopied(true); setTimeout(() => setCopied(false), 1200); - } catch { - /* clipboard unavailable in non-secure contexts */ } }; diff --git a/ui/litellm-dashboard/src/components/logs/detail/InputCard.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/InputCard.test.tsx index dfd9dd54dd7..87105d3f48f 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/InputCard.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/InputCard.test.tsx @@ -1,12 +1,15 @@ import React from "react"; -import { render, screen, waitFor, fireEvent, act } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { InputCard } from "./InputCard"; import { ParsedMessage } from "./prettyMessagesTypes"; +import { copyToClipboard } from "@/utils/dataUtils"; +import { toast } from "@/lib/toast"; + +vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn() })); describe("InputCard", () => { - const mockWriteText = vi.fn().mockResolvedValue(undefined); const mockMessages: ParsedMessage[] = [ { role: "user", @@ -16,13 +19,7 @@ describe("InputCard", () => { beforeEach(() => { vi.clearAllMocks(); - Object.defineProperty(navigator, "clipboard", { - value: { - writeText: mockWriteText, - }, - writable: true, - configurable: true, - }); + vi.mocked(copyToClipboard).mockResolvedValue(true); }); it("should render the InputCard component", () => { @@ -91,27 +88,16 @@ describe("InputCard", () => { expect(screen.getByText(/Cost: \$0\.001500/)).toBeInTheDocument(); }); - it("should copy last message content when copy button is clicked", async () => { - const messages: ParsedMessage[] = [ - { - role: "user", - content: "Copy this text", - }, - ]; + it.each([true, false])("delegates copying and feedback to the helper (%s)", async (success) => { + const user = userEvent.setup(); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + vi.mocked(copyToClipboard).mockResolvedValue(success); + render(); - render(); + await user.click(screen.getByRole("button", { name: /copy/i })); - const copyButton = screen.getByRole("button", { name: /copy/i }); - - expect(copyButton).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(copyButton); - }); - - await waitFor(() => { - expect(mockWriteText).toHaveBeenCalledWith("Copy this text"); - }); + expect(copyToClipboard).toHaveBeenCalledWith("Copy this text", "Input copied"); + expect(toast.success).not.toHaveBeenCalled(); }); it("should toggle collapse state when header is clicked", async () => { diff --git a/ui/litellm-dashboard/src/components/logs/detail/InputCard.tsx b/ui/litellm-dashboard/src/components/logs/detail/InputCard.tsx index d367c69f263..0b0bde85b91 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/InputCard.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/InputCard.tsx @@ -4,7 +4,7 @@ */ import { useState } from "react"; -import { toast } from "@/lib/toast"; +import { copyToClipboard } from "@/utils/dataUtils"; import { ParsedMessage } from "./prettyMessagesTypes"; import { SectionHeader } from "./SectionHeader"; import { CollapsibleMessage } from "./CollapsibleMessage"; @@ -32,8 +32,7 @@ export function InputCard({ messages, promptTokens, inputCost }: InputCardProps) const handleCopy = () => { const content = lastMessage?.content || ""; - navigator.clipboard.writeText(content); - toast.success("Input copied"); + void copyToClipboard(content, "Input copied"); }; return ( diff --git a/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.integration.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.integration.test.tsx index 7f39a0d8fe5..21ecf19235e 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.integration.test.tsx @@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { GuardrailJumpLink, LogDetailContent } from "./LogDetailContent"; import type { LogEntry } from "../types"; +import { toast } from "@/lib/toast"; vi.mock("./sections/GuardrailViewer/GuardrailViewer", () => ({ default: ({ data }: { data: unknown }) =>
{JSON.stringify(data)}
, @@ -295,6 +296,46 @@ describe("LogDetailContent", () => { expect(screen.queryByRole("tab", { name: "Request" })).not.toBeInTheDocument(); }); + it.each([ + ["Request", true], + ["Response", true], + ["Metadata", true], + ["Request", false], + ] as const)("copies %s through the fallback and reports its outcome (%s)", async (section, success) => { + const user = userEvent.setup(); + const clipboardDescriptor = Object.getOwnPropertyDescriptor(navigator, "clipboard"); + const execCommandDescriptor = Object.getOwnPropertyDescriptor(document, "execCommand"); + const writeText = vi.fn<(value: string) => boolean>().mockReturnValue(success); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + Object.defineProperty(document, "execCommand", { + configurable: true, + value: (command: string) => + command === "copy" && writeText((document.activeElement as HTMLTextAreaElement).value), + }); + vi.mocked(toast.success).mockClear(); + const log = createLogEntry(); + const expected = { Request: log.messages, Response: log.response, Metadata: log.metadata }[section]; + + try { + render(); + if (section !== "Metadata") { + await user.click(screen.getByRole("tab", { name: "JSON", exact: true })); + await user.click(screen.getByRole("tab", { name: section, exact: true })); + } + const label = section === "Metadata" ? "Copy Metadata" : "Copy JSON"; + const button = screen.getByRole("button", { name: label, exact: true }); + await user.click(button); + + expect(writeText).toHaveBeenCalledWith(JSON.stringify(expected, null, 2)); + expect(button).toHaveAccessibleName(success ? "Copied!" : label); + expect(toast.success).toHaveBeenCalledTimes(success ? 1 : 0); + } finally { + if (clipboardDescriptor) Object.defineProperty(navigator, "clipboard", clipboardDescriptor); + if (execCommandDescriptor) Object.defineProperty(document, "execCommand", execCommandDescriptor); + else Reflect.deleteProperty(document, "execCommand"); + } + }); + it.each(["object", "serialized", "messages only", "null captures"])( "preserves request inspection and copying for classifier logs with %s data", async (shape) => { diff --git a/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.tsx b/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.tsx index f6c63582938..f740b6c586a 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/LogDetailContent.tsx @@ -1,3 +1,4 @@ +import { copyToClipboard } from "@/utils/dataUtils"; import { useState } from "react"; import { Check, ChevronDown, ChevronRight, CircleAlert, Copy, Info } from "lucide-react"; import moment from "moment"; @@ -304,12 +305,9 @@ function CopyButton({ const [copied, setCopied] = useState(false); const handleCopy = async () => { - try { - await navigator.clipboard.writeText(getText()); + if (await copyToClipboard(getText())) { setCopied(true); setTimeout(() => setCopied(false), 1200); - } catch { - /* clipboard unavailable in non-secure contexts */ } }; diff --git a/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.test.tsx index 1321a4a152a..36ab70ecf2a 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.test.tsx @@ -1,3 +1,5 @@ +import userEvent from "@testing-library/user-event"; +import { copyToClipboard } from "@/utils/dataUtils"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; @@ -6,6 +8,11 @@ import { sessionSpendLogsCall } from "../../networking"; import { LogEntry } from "../types"; import { AutoRouterModelGroupsProvider } from "@/components/shared/table_cells"; +vi.mock("@/utils/dataUtils", async (importOriginal) => ({ + ...(await importOriginal()), + copyToClipboard: vi.fn(), +})); + vi.mock("../../networking", () => ({ sessionSpendLogsCall: vi.fn(), })); @@ -201,3 +208,18 @@ describe("LogDetailsDrawer session sidebar auto-router icon", () => { expect(directRow.querySelector(".lucide-waypoints")).toBeNull(); }); }); + +describe("LogDetailsDrawer copying", () => { + it.each([true, false])("confirms a session id only when copying succeeds (%s)", async (success) => { + const user = userEvent.setup(); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + vi.mocked(copyToClipboard).mockReset().mockResolvedValue(success); + renderSessionDrawer(); + + const button = await screen.findByRole("button", { name: "Copy trace id" }); + await user.click(button); + + expect(copyToClipboard).toHaveBeenCalledWith("session-1"); + expect(button.querySelector(".lucide-check") !== null).toBe(success); + }); +}); diff --git a/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.tsx index 48b89ee8ac1..23ad629d572 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/LogDetailsDrawer.tsx @@ -1,3 +1,4 @@ +import { copyToClipboard } from "@/utils/dataUtils"; import { useEffect, useMemo, useState } from "react"; import { Bot, Check, Copy, Sparkles, Wrench } from "lucide-react"; import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"; @@ -289,12 +290,9 @@ export function LogDetailsDrawer({ const handleCopyLeftPanelId = async () => { if (!leftPanelId) return; - try { - await navigator.clipboard.writeText(leftPanelId); + if (await copyToClipboard(leftPanelId)) { setCopiedLeftPanelId(true); setTimeout(() => setCopiedLeftPanelId(false), 1200); - } catch { - /* clipboard unavailable in non-secure contexts */ } }; diff --git a/ui/litellm-dashboard/src/components/logs/detail/OutputCard.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/OutputCard.test.tsx index 3dda7c5fa65..7b4c1a987a1 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/OutputCard.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/OutputCard.test.tsx @@ -1,12 +1,15 @@ import React from "react"; -import { render, screen, waitFor, fireEvent, act } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { OutputCard } from "./OutputCard"; import { ParsedMessage } from "./prettyMessagesTypes"; +import { copyToClipboard } from "@/utils/dataUtils"; +import { toast } from "@/lib/toast"; + +vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn() })); describe("OutputCard", () => { - const mockWriteText = vi.fn().mockResolvedValue(undefined); const mockMessage: ParsedMessage = { role: "assistant", content: "This is a test response", @@ -14,13 +17,7 @@ describe("OutputCard", () => { beforeEach(() => { vi.clearAllMocks(); - Object.defineProperty(navigator, "clipboard", { - value: { - writeText: mockWriteText, - }, - writable: true, - configurable: true, - }); + vi.mocked(copyToClipboard).mockResolvedValue(true); }); it("should render the OutputCard component", () => { @@ -53,20 +50,16 @@ describe("OutputCard", () => { expect(screen.getByText(/Cost: \$0\.002500/)).toBeInTheDocument(); }); - it("should copy message content when copy button is clicked", async () => { + it.each([true, false])("delegates copying and feedback to the helper (%s)", async (success) => { + const user = userEvent.setup(); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + vi.mocked(copyToClipboard).mockResolvedValue(success); render(); - const copyButton = screen.getByRole("button", { name: /copy/i }); + await user.click(screen.getByRole("button", { name: /copy/i })); - expect(copyButton).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(copyButton); - }); - - await waitFor(() => { - expect(mockWriteText).toHaveBeenCalledWith("This is a test response"); - }); + expect(copyToClipboard).toHaveBeenCalledWith("This is a test response", "Output copied"); + expect(toast.success).not.toHaveBeenCalled(); }); it("should not copy when message is null and copy button is clicked", async () => { @@ -80,7 +73,7 @@ describe("OutputCard", () => { await user.click(copyButton); await waitFor(() => { - expect(mockWriteText).not.toHaveBeenCalled(); + expect(copyToClipboard).not.toHaveBeenCalled(); }); }); diff --git a/ui/litellm-dashboard/src/components/logs/detail/OutputCard.tsx b/ui/litellm-dashboard/src/components/logs/detail/OutputCard.tsx index 7d0ec62ffe2..7daa8b02546 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/OutputCard.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/OutputCard.tsx @@ -4,7 +4,7 @@ */ import { useState } from "react"; -import { toast } from "@/lib/toast"; +import { copyToClipboard } from "@/utils/dataUtils"; import { COLOR_BORDER } from "./constants"; import { ParsedMessage } from "./prettyMessagesTypes"; import { SectionHeader } from "./SectionHeader"; @@ -22,8 +22,7 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard const handleCopy = () => { if (!message) return; - navigator.clipboard.writeText(message.content || ""); - toast.success("Output copied"); + void copyToClipboard(message.content || "", "Output copied"); }; return ( diff --git a/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.test.tsx b/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.test.tsx index 201c9e74ce8..d5280c35983 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.test.tsx @@ -1,9 +1,12 @@ import React from "react"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { copyToClipboard } from "@/utils/dataUtils"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { RealtimePrettyView, isRealtimeResponse } from "./RealtimePrettyView"; +vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn().mockResolvedValue(true) })); + const sampleRealtimeResponse = { usage: { total_tokens: 587, @@ -142,15 +145,20 @@ describe("isRealtimeResponse", () => { }); describe("RealtimePrettyView", () => { - const mockWriteText = vi.fn().mockResolvedValue(undefined); - beforeEach(() => { vi.clearAllMocks(); - Object.defineProperty(navigator, "clipboard", { - value: { writeText: mockWriteText }, - writable: true, - configurable: true, - }); + }); + + it("copies all transcript turns when the Clipboard API is unavailable", async () => { + const user = userEvent.setup(); + Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); + render(); + + await user.click(screen.getByRole("button", { name: /copy/i })); + + expect(copyToClipboard).toHaveBeenCalledWith( + "assistant: Hello! How's your day going?\nassistant: I'm here to help with information and general questions.", + ); }); it("should render the component successfully", () => { diff --git a/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.tsx b/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.tsx index 82f0d3e7fb2..04f0a178462 100644 --- a/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.tsx +++ b/ui/litellm-dashboard/src/components/logs/detail/RealtimePrettyView.tsx @@ -1,3 +1,4 @@ +import { copyToClipboard } from "@/utils/dataUtils"; /** * RealtimePrettyView - Structured pretty view for OpenAI Realtime API logs * Displays session config, conversation turns, and token usage @@ -276,7 +277,7 @@ function ConversationCard({ (r.output || []).flatMap((o) => (o.content || []).map((c) => `${o.role}: ${c.transcript || c.text || ""}`)), ) .join("\n"); - navigator.clipboard.writeText(transcripts); + void copyToClipboard(transcripts); }; return ( diff --git a/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx b/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx index c2c2ee199b4..311b536d89c 100644 --- a/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx @@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { ComponentProps } from "react"; import { copyToClipboard } from "@/utils/dataUtils"; -import { renderWithProviders, screen, waitFor } from "../../../tests/test-utils"; +import { renderWithProviders, screen } from "../../../tests/test-utils"; import CopyButton from "./CopyButton"; vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn() })); @@ -13,13 +13,9 @@ vi.mock("lucide-react", async (importOriginal) => ({ Check: (props: ComponentProps<"svg">) => , })); -const clipboardDescriptor = Object.getOwnPropertyDescriptor(navigator, "clipboard"); - afterEach(() => { vi.useRealTimers(); vi.resetAllMocks(); - if (clipboardDescriptor) Object.defineProperty(navigator, "clipboard", clipboardDescriptor); - else Reflect.deleteProperty(navigator, "clipboard"); }); describe("CopyButton", () => { @@ -28,55 +24,37 @@ describe("CopyButton", () => { expect(screen.queryByRole("button")).not.toBeInTheDocument(); }); - it("shows the confirmation checkmark only after a successful write", async () => { + it("shows the icon confirmation only after copying succeeds", async () => { const user = userEvent.setup(); - const writeText = vi.fn().mockResolvedValue(undefined); - Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true }); + const pendingCopy = Promise.withResolvers(); + vi.mocked(copyToClipboard).mockReturnValue(pendingCopy.promise); + renderWithProviders(); - renderWithProviders(); + await user.click(screen.getByRole("button", { name: "Copy value" })); - const button = screen.getByRole("button", { name: "Copy value" }); + expect(copyToClipboard).toHaveBeenCalledWith("secret-value", "Value copied"); expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument(); + expect(screen.queryByRole("img", { name: "Copied icon" })).not.toBeInTheDocument(); - await user.click(button); - - expect(writeText).toHaveBeenCalledWith("secret-value"); - expect(await screen.findByRole("img", { name: "Copied icon" })).toBeInTheDocument(); + await act(async () => pendingCopy.resolve(true)); + expect(screen.getByRole("img", { name: "Copied icon" })).toBeInTheDocument(); }); - it("does not show the checkmark when the clipboard write is rejected", async () => { + it("keeps the icon unchanged when copying fails", async () => { const user = userEvent.setup(); - const writeText = vi.fn().mockRejectedValue(new Error("permission denied")); - Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true }); - + vi.mocked(copyToClipboard).mockResolvedValue(false); renderWithProviders(); - const button = screen.getByRole("button", { name: "Copy value" }); - await user.click(button); + await user.click(screen.getByRole("button", { name: "Copy value" })); - await waitFor(() => expect(writeText).toHaveBeenCalledWith("secret-value")); + expect(copyToClipboard).toHaveBeenCalledWith("secret-value", "Copied"); expect(screen.queryByRole("img", { name: "Copied icon" })).not.toBeInTheDocument(); expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument(); }); - it("does not show the checkmark when the clipboard API is unavailable", async () => { - const user = userEvent.setup(); - Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true }); - - renderWithProviders(); - - const button = screen.getByRole("button", { name: "Copy value" }); - await user.click(button); - - expect(screen.queryByRole("img", { name: "Copied icon" })).not.toBeInTheDocument(); - expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument(); - expect(copyToClipboard).not.toHaveBeenCalled(); - }); - it("keeps the default icon styling, custom classes, title and 1200ms confirmation", async () => { vi.useFakeTimers(); - const writeText = vi.fn().mockResolvedValue(undefined); - Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true }); + vi.mocked(copyToClipboard).mockResolvedValue(true); renderWithProviders( , ); @@ -87,8 +65,7 @@ describe("CopyButton", () => { expect(screen.getByRole("img", { name: "Copy icon" })).toHaveClass("size-3"); await act(async () => fireEvent.click(button)); - expect(writeText).toHaveBeenCalledWith("request-id"); - expect(copyToClipboard).not.toHaveBeenCalled(); + expect(copyToClipboard).toHaveBeenCalledWith("request-id", "Copied"); act(() => vi.advanceTimersByTime(1199)); expect(screen.getByRole("img", { name: "Copied icon" })).toBeInTheDocument(); act(() => vi.advanceTimersByTime(1)); diff --git a/ui/litellm-dashboard/src/components/shared/CopyButton.tsx b/ui/litellm-dashboard/src/components/shared/CopyButton.tsx index 57ae86a26d0..225154e4bcf 100644 --- a/ui/litellm-dashboard/src/components/shared/CopyButton.tsx +++ b/ui/litellm-dashboard/src/components/shared/CopyButton.tsx @@ -54,17 +54,7 @@ const CopyButton: React.FC = ({ if (value == null || (!value && !isAction)) return null; const handleCopy = async () => { - if (isAction) { - setCopied(await copyToClipboard(value, copiedLabel)); - return; - } - if (!navigator.clipboard) return; - try { - await navigator.clipboard.writeText(value); - setCopied(true); - } catch { - setCopied(false); - } + setCopied(await copyToClipboard(value, copiedLabel)); }; const button = ( diff --git a/ui/litellm-dashboard/src/utils/dataUtils.test.ts b/ui/litellm-dashboard/src/utils/dataUtils.test.ts index af249ca601a..23b425b47e0 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.test.ts @@ -1,5 +1,6 @@ // @vitest-environment jsdom +import "@testing-library/jest-dom/vitest"; import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; import { copyToClipboard, @@ -16,8 +17,6 @@ const mockToast = vi.mocked(toast); describe("dataUtils", () => { beforeEach(() => { vi.clearAllMocks(); - // Reset document.execCommand mock - delete (document as any).execCommand; }); afterEach(() => { @@ -134,194 +133,114 @@ describe("dataUtils", () => { }); describe("copyToClipboard", () => { - describe("when Clipboard API is available (HTTPS scenario)", () => { - beforeEach(() => { - // Mock modern Clipboard API - Object.assign(navigator, { - clipboard: { - writeText: vi.fn().mockResolvedValue(undefined), - }, - }); - }); - - it("should use navigator.clipboard.writeText when available", async () => { - const result = await copyToClipboard("test text"); - - expect(navigator.clipboard.writeText).toHaveBeenCalledWith("test text"); - expect(mockToast.success).toHaveBeenCalledWith("Copied to clipboard"); - expect(result).toBe(true); - }); - - it("should use custom message when provided", async () => { - await copyToClipboard("test text", "Custom message"); - - expect(mockToast.success).toHaveBeenCalledWith("Custom message"); - }); - - it("should return false for null/undefined text", async () => { - expect(await copyToClipboard(null)).toBe(false); - expect(await copyToClipboard(undefined)).toBe(false); - expect(navigator.clipboard.writeText).not.toHaveBeenCalled(); - }); - - it("should fall back to execCommand when clipboard API fails", async () => { - // Make clipboard API fail - navigator.clipboard.writeText = vi.fn().mockRejectedValue(new Error("Permission denied")); - - // Mock successful execCommand - document.execCommand = vi.fn().mockReturnValue(true); - - // Mock DOM methods - const mockTextArea = { - value: "", - style: {} as Record, - setAttribute: vi.fn(), - focus: vi.fn(), - select: vi.fn(), - }; - document.createElement = vi.fn().mockReturnValue(mockTextArea); - document.body.appendChild = vi.fn(); - document.body.removeChild = vi.fn(); - - const result = await copyToClipboard("test text"); - - expect(navigator.clipboard.writeText).toHaveBeenCalledWith("test text"); - expect(document.execCommand).toHaveBeenCalledWith("copy"); - expect(mockToast.success).toHaveBeenCalledWith("Copied to clipboard"); - expect(result).toBe(true); + const clipboardDescriptor = Object.getOwnPropertyDescriptor(navigator, "clipboard"); + const execCommandDescriptor = Object.getOwnPropertyDescriptor(document, "execCommand"); + const writeText = vi.fn<(text: string) => Promise>(); + const execCommand = vi.fn<(command: string) => boolean>(); + const selectedText = vi.fn(() => { + const element = document.activeElement; + return element instanceof HTMLTextAreaElement + ? element.value.slice(element.selectionStart, element.selectionEnd) + : undefined; + }); + const trigger = document.createElement("button"); + const text = "Request α\nsecond line"; + + beforeEach(() => { + writeText.mockResolvedValue(undefined); + execCommand.mockImplementation(() => { + selectedText(); + return true; }); + Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText } }); + Object.defineProperty(document, "execCommand", { configurable: true, value: execCommand }); + vi.spyOn(console, "error").mockImplementation(() => {}); + document.body.appendChild(trigger); + trigger.focus(); }); - describe("when Clipboard API is not available (HTTP scenario)", () => { - beforeEach(() => { - // Mock HTTP scenario - no clipboard API - Object.assign(navigator, { - clipboard: undefined, - }); - - // Mock successful execCommand - document.execCommand = vi.fn().mockReturnValue(true); - - // Mock DOM methods - const mockTextArea = { - value: "", - style: {} as Record, - setAttribute: vi.fn(), - focus: vi.fn(), - select: vi.fn(), - }; - document.createElement = vi.fn().mockReturnValue(mockTextArea); - document.body.appendChild = vi.fn(); - document.body.removeChild = vi.fn(); - }); - - it("should fall back to execCommand when clipboard API is not available", async () => { - const result = await copyToClipboard("test text"); - - expect(document.createElement).toHaveBeenCalledWith("textarea"); - expect(document.execCommand).toHaveBeenCalledWith("copy"); - expect(mockToast.success).toHaveBeenCalledWith("Copied to clipboard"); - expect(result).toBe(true); - }); - - it("should set textarea properties correctly", async () => { - const mockTextArea = { - value: "", - style: {} as Record, - setAttribute: vi.fn(), - focus: vi.fn(), - select: vi.fn(), - }; - document.createElement = vi.fn().mockReturnValue(mockTextArea); - - await copyToClipboard("test text"); - - expect(mockTextArea.value).toBe("test text"); - /* eslint-disable jest-dom/prefer-to-have-style -- the subject is a plain mock object, not a DOM node, so toHaveStyle cannot read it */ - expect(mockTextArea.style.position).toBe("fixed"); - expect(mockTextArea.style.left).toBe("-999999px"); - expect(mockTextArea.style.top).toBe("-999999px"); - /* eslint-enable jest-dom/prefer-to-have-style */ - expect(mockTextArea.setAttribute).toHaveBeenCalledWith("readonly", ""); - expect(mockTextArea.focus).toHaveBeenCalled(); - expect(mockTextArea.select).toHaveBeenCalled(); - }); - - it("should handle execCommand failure", async () => { - document.execCommand = vi.fn().mockReturnValue(false); - - const result = await copyToClipboard("test text"); - - expect(document.execCommand).toHaveBeenCalledWith("copy"); - expect(mockToast.fromError).toHaveBeenCalledWith("Failed to copy to clipboard"); - expect(result).toBe(false); - }); - - it("should handle DOM manipulation errors", async () => { - document.createElement = vi.fn().mockImplementation(() => { - throw new Error("DOM error"); - }); - - const result = await copyToClipboard("test text"); - - expect(mockToast.fromError).toHaveBeenCalledWith("Failed to copy to clipboard"); - expect(result).toBe(false); - }); - - it("should clean up textarea element after successful copy", async () => { - const mockTextArea = { - value: "", - style: {} as Record, - setAttribute: vi.fn(), - focus: vi.fn(), - select: vi.fn(), - }; - document.createElement = vi.fn().mockReturnValue(mockTextArea); - - await copyToClipboard("test text"); - - expect(document.body.appendChild).toHaveBeenCalledWith(mockTextArea); - expect(document.body.removeChild).toHaveBeenCalledWith(mockTextArea); - }); + afterEach(() => { + document.body.replaceChildren(); + if (clipboardDescriptor) Object.defineProperty(navigator, "clipboard", clipboardDescriptor); + else Reflect.deleteProperty(navigator, "clipboard"); + if (execCommandDescriptor) Object.defineProperty(document, "execCommand", execCommandDescriptor); + else Reflect.deleteProperty(document, "execCommand"); }); - describe("edge cases", () => { - beforeEach(() => { - // Mock scenario where navigator exists but clipboard is null - Object.assign(navigator, { - clipboard: null, - }); + it("confirms a modern clipboard write only after it succeeds", async () => { + const pendingWrite = Promise.withResolvers(); + writeText.mockReturnValue(pendingWrite.promise); + const result = copyToClipboard(text, "Request copied"); - document.execCommand = vi.fn().mockReturnValue(true); - const mockTextArea = { - value: "", - style: {} as Record, - setAttribute: vi.fn(), - focus: vi.fn(), - select: vi.fn(), - }; - document.createElement = vi.fn().mockReturnValue(mockTextArea); - document.body.appendChild = vi.fn(); - document.body.removeChild = vi.fn(); + expect(writeText).toHaveBeenCalledWith(text); + expect(mockToast.success).not.toHaveBeenCalled(); + pendingWrite.resolve(); + + expect(await result).toBe(true); + expect(mockToast.success).toHaveBeenCalledWith("Request copied"); + expect(execCommand).not.toHaveBeenCalled(); + expect(trigger).toHaveFocus(); + }); + + it.each([null, undefined, ""])("does not copy absent content %s", async (value) => { + expect(await copyToClipboard(value)).toBe(false); + expect(writeText).not.toHaveBeenCalled(); + expect(execCommand).not.toHaveBeenCalled(); + expect(mockToast.success).not.toHaveBeenCalled(); + }); + + it.each([undefined, null, {}])("copies the full selection when the Clipboard API is %s", async (clipboard) => { + Object.defineProperty(navigator, "clipboard", { configurable: true, value: clipboard }); + + expect(await copyToClipboard(text)).toBe(true); + + expect(execCommand).toHaveBeenCalledWith("copy"); + expect(selectedText).toHaveReturnedWith(text); + expect(mockToast.success).toHaveBeenCalledWith("Copied to clipboard"); + expect(document.querySelector("textarea")).not.toBeInTheDocument(); + expect(trigger).toHaveFocus(); + }); + + it("falls back after a rejected modern write", async () => { + writeText.mockRejectedValue(new Error("Permission denied")); + + expect(await copyToClipboard(text, "Request copied")).toBe(true); + + expect(writeText).toHaveBeenCalledWith(text); + expect(execCommand).toHaveBeenCalledWith("copy"); + expect(selectedText).toHaveReturnedWith(text); + expect(mockToast.success).toHaveBeenCalledExactlyOnceWith("Request copied"); + expect(document.querySelector("textarea")).not.toBeInTheDocument(); + expect(trigger).toHaveFocus(); + }); + + it.each(["false", "throw"])("cleans up and reports failure when the fallback returns %s", async (outcome) => { + Object.defineProperty(navigator, "clipboard", { configurable: true, value: undefined }); + execCommand.mockImplementation(() => { + selectedText(); + if (outcome === "throw") throw new Error("Copy denied"); + return false; }); - it("should handle navigator.clipboard being null", async () => { - const result = await copyToClipboard("test text"); + expect(await copyToClipboard(text)).toBe(false); - expect(document.execCommand).toHaveBeenCalledWith("copy"); - expect(result).toBe(true); + expect(selectedText).toHaveReturnedWith(text); + expect(mockToast.fromError).toHaveBeenCalledWith("Failed to copy to clipboard"); + expect(mockToast.success).not.toHaveBeenCalled(); + expect(document.querySelector("textarea")).not.toBeInTheDocument(); + expect(trigger).toHaveFocus(); + }); + + it("reports failure when the fallback cannot create its selection", async () => { + Object.defineProperty(navigator, "clipboard", { configurable: true, value: undefined }); + vi.spyOn(document, "createElement").mockImplementationOnce(() => { + throw new Error("DOM error"); }); - it("should handle navigator.clipboard.writeText being undefined", async () => { - Object.assign(navigator, { - clipboard: {}, // clipboard exists but writeText doesn't - }); - - const result = await copyToClipboard("test text"); - - expect(document.execCommand).toHaveBeenCalledWith("copy"); - expect(result).toBe(true); - }); + expect(await copyToClipboard(text)).toBe(false); + expect(mockToast.fromError).toHaveBeenCalledWith("Failed to copy to clipboard"); + expect(mockToast.success).not.toHaveBeenCalled(); + expect(trigger).toHaveFocus(); }); }); }); diff --git a/ui/litellm-dashboard/src/utils/dataUtils.ts b/ui/litellm-dashboard/src/utils/dataUtils.ts index c813a69b999..a05b8d6b0c2 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.ts @@ -103,19 +103,20 @@ const fallbackCopyToClipboard = (text: string, messageText: string): boolean => textArea.setAttribute("readonly", ""); document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); + try { + textArea.focus(); + textArea.select(); - const successful = document.execCommand("copy"); - document.body.removeChild(textArea); - if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus(); - - if (successful) { - toast.success(messageText); - return true; - } else { - throw new Error("execCommand failed"); + if (document.execCommand("copy")) { + toast.success(messageText); + return true; + } + } finally { + textArea.remove(); + if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus(); } + toast.fromError("Failed to copy to clipboard"); + return false; } catch (err) { toast.fromError("Failed to copy to clipboard"); console.error("Failed to copy: ", err); From 4c674e8e3f0758531308b68f4d42399811cf3b8c Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 14:18:23 -0400 Subject: [PATCH 06/73] fix(mcp): report oversized SSE catalog errors (#48424) --- .../mcp_server/faults/list_outcomes.py | 46 +++++++++++-- .../mcp_server/rest_endpoints.py | 2 +- .../test_mcp_client.py | 56 ++++++++++++++++ .../mcp_server/faults/test_list_outcomes.py | 65 ++++++++++++++++++- 4 files changed, 163 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/faults/list_outcomes.py b/litellm/proxy/_experimental/mcp_server/faults/list_outcomes.py index a3e33929f6d..e148e28e826 100644 --- a/litellm/proxy/_experimental/mcp_server/faults/list_outcomes.py +++ b/litellm/proxy/_experimental/mcp_server/faults/list_outcomes.py @@ -3,8 +3,8 @@ The aggregate listing deliberately keeps serving the healthy subset when one server fails, but a failed server must contribute a classified outcome instead of silently shrinking the list: an empty contribution with no signal makes a broken upstream indistinguishable from a healthy server with no -tools. Outcomes carry only machine fields (category and status code) so nothing from an upstream -body crosses the trust boundary; classification is total, so any exception out of a server fetch +tools. Outcomes carry machine fields and controlled diagnostics so nothing from an upstream body +crosses the trust boundary; classification is total, so any exception out of a server fetch becomes an outcome, never a second failure. """ @@ -21,8 +21,10 @@ from urllib.request import parse_http_list, parse_keqv_list import anyio import httpx import httpx2 +from mcp import MCPError +from mcp.types import CONNECTION_CLOSED from mcp.types import Tool as MCPTool -from pydantic import ConfigDict, TypeAdapter +from pydantic import ConfigDict, PositiveInt, TypeAdapter from typing_extensions import assert_never from litellm._logging import verbose_logger @@ -65,6 +67,16 @@ class ServerListFault(LiteLLMBaseModel): model_config = ConfigDict(frozen=True) tag: ListFaultCategory status_code: int | None = None + sse_event_limit_bytes: PositiveInt | None = None + + @property + def public_message(self) -> str | None: + if self.sse_event_limit_bytes is None: + return None + return ( + f"Upstream MCP SSE event exceeded the {self.sse_event_limit_bytes} byte per-event limit. " + "Paginate tools/list responses so each event stays below the limit." + ) ServerOutcome: TypeAlias = ServerListOk | ServerListFault @@ -155,6 +167,18 @@ def raise_classified_list_failure( raise MCPServerListError(classify_list_exception(exc), server_name) from exc +def _sse_event_limit(exc: BaseException) -> int | None: + """The SDK replaces decoder exceptions with CONNECTION_CLOSED and a fixed diagnostic.""" + if not isinstance(exc, (httpx2.SSEError, MCPError)): + return None + if isinstance(exc, MCPError) and exc.error.code != CONNECTION_CLOSED: + return None + prefix: Final = "SSE stream failed: " if isinstance(exc, MCPError) else "" + message: Final = exc.error.message if isinstance(exc, MCPError) else str(exc) + matched: Final = re.fullmatch(prefix + r"Server-sent event exceeded the ([1-9][0-9]{0,18}) byte limit\.", message) + return int(matched[1]) if matched is not None else None + + def classify_list_exception(exc: BaseException) -> ServerListFault: """Classify a per-server listing failure into exactly one outcome. Total: an exception this function cannot recognize is the gateway's own fault (``internal``), never a re-raise.""" @@ -179,6 +203,11 @@ def classify_list_exception(exc: BaseException) -> ServerListFault: response: Final = _find_upstream_response(exc) if response is not None: return ServerListFault(tag="upstream_error", status_code=response.status_code) + sse_event_limit: Final = next( + (limit for error in iter_exception_tree(exc) if (limit := _sse_event_limit(error)) is not None), None + ) + if sse_event_limit is not None: + return ServerListFault(tag="upstream_error", sse_event_limit_bytes=sse_event_limit) if isinstance(exc, (httpx.TimeoutException, httpx2.TimeoutException)): return ServerListFault(tag="timeout") if isinstance(exc, (httpx.TransportError, httpx2.TransportError)): @@ -295,7 +324,7 @@ def listing_failed_outright(outcomes: Mapping[str, ServerOutcome]) -> bool: def outcome_wire_value(outcome: ServerOutcome) -> dict[str, object]: """The client-visible form of one outcome, for the tools/list result ``_meta`` and the REST - response: category plus status code only, never upstream prose or URLs.""" + response: category, status code and controlled diagnostics, never upstream prose or URLs.""" match outcome.tag: case "ok": return {"status": "ok", "tool_count": outcome.tool_count} @@ -303,6 +332,15 @@ def outcome_wire_value(outcome: ServerOutcome) -> dict[str, object]: return { "status": outcome.tag, **({"http_status": outcome.status_code} if outcome.status_code is not None else {}), + **( + { + "reason": "sse_event_too_large", + "limit_bytes": outcome.sse_event_limit_bytes, + "message": outcome.public_message, + } + if outcome.sse_event_limit_bytes is not None + else {} + ), } case _: assert_never(outcome.tag) diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index ee063b81ae4..62387b7cc28 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -957,7 +957,7 @@ if MCP_AVAILABLE: status_code=list_fault_http_status(fault), detail={ "error": fault.tag, - "message": f"Failed to list tools from server {get_server_prefix(server)}", + "message": fault.public_message or f"Failed to list tools from server {get_server_prefix(server)}", }, ) from e except Exception as e: diff --git a/tests/unit/experimental_mcp_client/test_mcp_client.py b/tests/unit/experimental_mcp_client/test_mcp_client.py index b4485bcc5f1..60600afab84 100644 --- a/tests/unit/experimental_mcp_client/test_mcp_client.py +++ b/tests/unit/experimental_mcp_client/test_mcp_client.py @@ -1921,6 +1921,62 @@ async def test_list_tools_classifies_upstream_status_for_post_failures(failure_m assert rate_limit is None +@pytest.mark.asyncio +@pytest.mark.parametrize("paginated", (False, True)) +async def test_sse_catalog_limit_reports_the_failure_and_allows_smaller_pages(paginated: bool) -> None: + from mcp.types import Tool + + limit: Final = 1024 + tools: Final = tuple( + Tool(name=f"tool_{index}", description="private-upstream-secret " * 25, input_schema={"type": "object"}) + for index in range(2) + ) + catalog: Final = tuple(tool.model_dump(by_alias=True, exclude_none=True) for tool in tools) + assert len(json.dumps({"tools": catalog}).encode()) > limit + + def respond(request: httpx2.Request) -> httpx2.Response: + if request.method != "POST": + return httpx2.Response(200) + payload: Final = _JSONRPC_MESSAGE_ADAPTER.validate_json(request.content) + assert isinstance(payload, (JSONRPCRequest, JSONRPCNotification)) + if isinstance(payload, JSONRPCNotification): + return httpx2.Response(202) + if payload.method == "initialize": + return httpx2.Response(200, json={ + "jsonrpc": "2.0", "id": payload.id, "result": { + "protocolVersion": LATEST_HANDSHAKE_VERSION, + "capabilities": {"tools": {}}, "serverInfo": {"name": "catalog", "version": "1"}, + }, + }) + assert payload.method == "tools/list" + cursor: Final = (payload.params or {}).get("cursor") + page: Final = catalog[1:] if cursor == "next-page" else catalog[:1] if paginated else catalog + result: Final = { + "tools": page, + **({"nextCursor": "next-page"} if paginated and cursor is None else {}), + } + event: Final = f"data: {json.dumps({'jsonrpc': '2.0', 'id': payload.id, 'result': result})}\n\n" + assert (len(event.encode()) < limit) == paginated + return httpx2.Response(200, headers={"content-type": "text/event-stream"}, content=event) + + def transport_context(url: str, http_client: httpx2.AsyncClient) -> _TransportContext: + return streamable_http_client(url, http_client=http_client, max_sse_event_size=limit) + + client: Final = _MockTransportClient( + respond, server_url="https://upstream.example/mcp", transport_context=transport_context, + ) + if paginated: + assert await client.list_tools(raise_on_error=True) == list(tools) + return + with pytest.raises(MCPError) as caught: + await client.list_tools(raise_on_error=True) + fault: Final = classify_list_exception(caught.value) + assert fault == ServerListFault(tag="upstream_error", sse_event_limit_bytes=limit) + assert list_fault_http_status(fault) == 502 + assert fault.public_message is not None + assert "private-upstream-secret" not in fault.public_message + + @pytest.mark.asyncio async def test_http_status_check_allows_auth_refresh_before_rejecting() -> None: from litellm.proxy._experimental.mcp_server.outbound_credentials.client_credentials import ( diff --git a/tests/unit/proxy/_experimental/mcp_server/faults/test_list_outcomes.py b/tests/unit/proxy/_experimental/mcp_server/faults/test_list_outcomes.py index f6902c778e5..fdae575276d 100644 --- a/tests/unit/proxy/_experimental/mcp_server/faults/test_list_outcomes.py +++ b/tests/unit/proxy/_experimental/mcp_server/faults/test_list_outcomes.py @@ -9,9 +9,10 @@ if sys.version_info < (3, 11): # BaseExceptionGroup is a builtin only from 3.11 from exceptiongroup import BaseExceptionGroup import httpx +import httpx2 import pytest from mcp import MCPError -from mcp.types import ErrorData +from mcp.types import CONNECTION_CLOSED, ErrorData from litellm.proxy._experimental.mcp_server.exceptions import ( MCPServerListError, @@ -102,6 +103,68 @@ def test_unknown_exception_is_internal(): assert classify_list_exception(ValueError("who knows")).tag == "internal" +@pytest.mark.parametrize("sdk_wrapped", (False, True)) +@pytest.mark.parametrize("nested", (False, True)) +def test_sse_event_limit_is_a_safe_upstream_fault(sdk_wrapped: bool, nested: bool) -> None: + limit: Final = 1024 + decoder_message: Final = f"Server-sent event exceeded the {limit} byte limit." + failure: Final = ( + MCPError(code=CONNECTION_CLOSED, message=f"SSE stream failed: {decoder_message}") + if sdk_wrapped else httpx2.SSEError(decoder_message) + ) + wrapped: Final = BaseExceptionGroup("private-upstream-secret", [failure]) if nested else failure + fault: Final = classify_list_exception(wrapped) + message: Final = ( + f"Upstream MCP SSE event exceeded the {limit} byte per-event limit. " + "Paginate tools/list responses so each event stays below the limit." + ) + assert fault == ServerListFault(tag="upstream_error", sse_event_limit_bytes=limit) + assert fault.public_message == message + assert list_fault_http_status(fault) == 502 + assert outcome_wire_value(fault) == { + "status": "upstream_error", "reason": "sse_event_too_large", "limit_bytes": limit, "message": message, + } + + +@pytest.mark.parametrize( + "failure", + ( + MCPError(code=CONNECTION_CLOSED, message="Connection closed"), + MCPError(code=-32603, message="SSE stream failed: Server-sent event exceeded the 1024 byte limit."), + MCPError(code=CONNECTION_CLOSED, message="SSE stream failed: Server-sent event exceeded the 0 byte limit."), + MCPError(code=CONNECTION_CLOSED, message="SSE stream failed: Server-sent event exceeded the -1 byte limit."), + MCPError(code=CONNECTION_CLOSED, message=( + "SSE stream failed: Server-sent event exceeded the 10000000000000000000 byte limit." + )), + MCPError(code=CONNECTION_CLOSED, message=( + "SSE stream failed: Server-sent event exceeded the 1024 byte limit. private-secret" + )), + httpx2.SSEError("private-secret Server-sent event exceeded the 1024 byte limit."), + httpx2.SSEError("Expected response with content type 'text/event-stream', got 'private-secret'."), + RuntimeError("SSE stream failed: Server-sent event exceeded the 1024 byte limit."), + ), +) +def test_unrelated_errors_do_not_claim_an_sse_event_limit(failure: Exception) -> None: + fault: Final = classify_list_exception(failure) + assert fault.sse_event_limit_bytes is None + assert fault.public_message is None + assert "private-secret" not in str(outcome_wire_value(fault)) + + +@pytest.mark.parametrize("status_code", (401, 403)) +def test_upstream_auth_takes_precedence_over_sse_event_limit(status_code: int) -> None: + request: Final = httpx2.Request("POST", "https://upstream.example/mcp") + response: Final = httpx2.Response(status_code, request=request) + failure: Final = BaseExceptionGroup("upstream errors", [ + httpx2.SSEError("Server-sent event exceeded the 1024 byte limit."), + httpx2.HTTPStatusError("private-secret", request=request, response=response), + ]) + fault: Final = classify_list_exception(failure) + assert fault.tag == ("auth_required" if status_code == 401 else "forbidden") + assert list_fault_http_status(fault) == status_code + assert fault.sse_event_limit_bytes is None + + def test_wire_value_carries_no_prose(): fault = ServerListFault(tag="upstream_error", status_code=500) assert outcome_wire_value(fault) == {"status": "upstream_error", "http_status": 500} From 1c022d1d06dba1cb42f9190c7b2641a1402bd2d9 Mon Sep 17 00:00:00 2001 From: Kerry Lu Date: Mon, 26 Oct 2026 11:31:26 -0700 Subject: [PATCH 07/73] chore(cost-map): add openai gpt-6.1-terra from the pricing page (#48429) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 102 ++++++++++++++++++ model_prices_and_context_window.json | 102 ++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9a7d88b8eda..0a85d8a00cd 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -78652,5 +78652,107 @@ "prompt_cache_min_tokens": 512, "source": "https://platform.claude.com/docs/en/about-claude/pricing#claude-platform-on-aws-pricing", "supports_web_search": true + }, + "gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_12hr": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 1e-05, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-06, + "cache_creation_input_token_cost_above_12hr_flex": 1.25e-06, + "cache_creation_input_token_cost_above_12hr_priority": 5e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_creation_input_token_cost_above_272k_tokens_batches": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens_flex": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens_priority": 5e-06, + "cache_creation_input_token_cost_batches": 6.25e-07, + "cache_creation_input_token_cost_flex": 6.25e-07, + "cache_creation_input_token_cost_priority": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "cache_read_input_token_cost_above_272k_tokens_batches": 5e-08, + "cache_read_input_token_cost_above_272k_tokens_flex": 5e-08, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-07, + "cache_read_input_token_cost_batches": 2.5e-08, + "cache_read_input_token_cost_flex": 2.5e-08, + "cache_read_input_token_cost_priority": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_above_272k_tokens_batches": 1e-06, + "input_cost_per_token_above_272k_tokens_flex": 1e-06, + "input_cost_per_token_above_272k_tokens_priority": 4e-06, + "input_cost_per_token_batches": 5e-07, + "input_cost_per_token_flex": 5e-07, + "input_cost_per_token_priority": 2e-06, + "litellm_provider": "openai", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "output_cost_per_token_above_272k_tokens_batches": 3.75e-06, + "output_cost_per_token_above_272k_tokens_flex": 3.75e-06, + "output_cost_per_token_above_272k_tokens_priority": 1.5e-05, + "output_cost_per_token_batches": 2.5e-06, + "output_cost_per_token_flex": 2.5e-06, + "output_cost_per_token_priority": 1e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "search_context_cost_per_query": {"search_context_size_high":0.01,"search_context_size_low":0.01,"search_context_size_medium":0.01}, + "source": "https://developers.openai.com/api/docs/pricing", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_computer_use": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_cache_breakpoint": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_tool_search": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "chatgpt/gpt-6.1-terra": { + "litellm_provider": "chatgpt", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "responses", + "source": "https://developers.openai.com/api/docs/pricing", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9a7d88b8eda..0a85d8a00cd 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -78652,5 +78652,107 @@ "prompt_cache_min_tokens": 512, "source": "https://platform.claude.com/docs/en/about-claude/pricing#claude-platform-on-aws-pricing", "supports_web_search": true + }, + "gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_12hr": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 1e-05, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-06, + "cache_creation_input_token_cost_above_12hr_flex": 1.25e-06, + "cache_creation_input_token_cost_above_12hr_priority": 5e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_creation_input_token_cost_above_272k_tokens_batches": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens_flex": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens_priority": 5e-06, + "cache_creation_input_token_cost_batches": 6.25e-07, + "cache_creation_input_token_cost_flex": 6.25e-07, + "cache_creation_input_token_cost_priority": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "cache_read_input_token_cost_above_272k_tokens_batches": 5e-08, + "cache_read_input_token_cost_above_272k_tokens_flex": 5e-08, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-07, + "cache_read_input_token_cost_batches": 2.5e-08, + "cache_read_input_token_cost_flex": 2.5e-08, + "cache_read_input_token_cost_priority": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_above_272k_tokens_batches": 1e-06, + "input_cost_per_token_above_272k_tokens_flex": 1e-06, + "input_cost_per_token_above_272k_tokens_priority": 4e-06, + "input_cost_per_token_batches": 5e-07, + "input_cost_per_token_flex": 5e-07, + "input_cost_per_token_priority": 2e-06, + "litellm_provider": "openai", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "output_cost_per_token_above_272k_tokens_batches": 3.75e-06, + "output_cost_per_token_above_272k_tokens_flex": 3.75e-06, + "output_cost_per_token_above_272k_tokens_priority": 1.5e-05, + "output_cost_per_token_batches": 2.5e-06, + "output_cost_per_token_flex": 2.5e-06, + "output_cost_per_token_priority": 1e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "search_context_cost_per_query": {"search_context_size_high":0.01,"search_context_size_low":0.01,"search_context_size_medium":0.01}, + "source": "https://developers.openai.com/api/docs/pricing", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_computer_use": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_cache_breakpoint": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_tool_search": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "chatgpt/gpt-6.1-terra": { + "litellm_provider": "chatgpt", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "responses", + "source": "https://developers.openai.com/api/docs/pricing", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true } } From 38f318e73659c5ae73c7ce26f387e1933ba210e9 Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 26 Oct 2026 11:44:30 -0700 Subject: [PATCH 08/73] fix(otel): decide per-team Arize sampling from the trace id so every pod agrees (#47468) * fix(otel): decide per-team Arize sampling from the trace id so every pod agrees * test(otel): cover per-team sampling across pods, workers, endpoints and restarts * fix(otel): remember only a failed tree's sampling verdict so a later failed call answers to the error rate A successful tree's verdict follows from the trace id alone, so remembering it only made a later failed call of the same trace follow the success rate on the pod that served both, while other pods judged it at the error rate * test(otel): flush every worker before the restart cell kills it and cover a failed call after a dropped one * fix(otel): decide team sampling per request, not per trace Calls sharing a caller's trace id are separate requests, keyed by their root span. Each is decided by the trace hash at the rate for its own outcome, and only its own late spans follow its verdict, so no pod applies an earlier call's verdict to a later call of the trace * fix(otel): forget a span's request root when it ends, not when newer spans start The root of each open span was kept in a first-in-first-out table of 16384 entries, so a span two levels below the root that stayed open while the worker started more spans than that lost its entry and ended under its parent's id. Its request then never closed and was held from every sampled destination until shutdown. Each open span now keeps its entry until it ends, and a span starting under a parent that has already ended reads the root from the parent span itself * fix(otel): decide a request as its spans end once it falls out of the open span table A request whose span was pushed out of the table of open spans, because the worker held more than 16384 open at once, ended that span under another key and was held from every sampled destination until shutdown. Pushing a span out now drops its request's open count too, so the request is decided as each of its spans ends, the way one pushed out of the open request table already is * test(otel): annotate the team sampling test locals as Final * test(otel): build the caller's remote parent in one helper instead of rebinding it per trace * fix(otel): keep each open span's request until it ends, bounded by the open requests The span table had its own bound of 16384, so a worker holding more spans open than that pushed out the oldest, and a failure in such a span no longer reached its request. Each request now holds the ids of its open spans, and a span's entry in the lookup goes when the span ends or when its request is pushed out of the open request table, the same bound main already applies. A failure anywhere in a tracked request reaches its verdict however many spans are open --------- Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com> --- .../integrations/otel/model/destination.py | 2 +- .../integrations/otel/plumbing/providers.py | 160 ++-- .../test_otel_team_sampling_across_pods.py | 723 ++++++++++++++++++ .../otel/test_otel_v2_destinations.py | 403 ++++++++-- 4 files changed, 1180 insertions(+), 108 deletions(-) create mode 100644 tests/integration/observability/test_otel_team_sampling_across_pods.py diff --git a/litellm/integrations/otel/model/destination.py b/litellm/integrations/otel/model/destination.py index 4226b4361dd..e760ba3600b 100644 --- a/litellm/integrations/otel/model/destination.py +++ b/litellm/integrations/otel/model/destination.py @@ -42,7 +42,7 @@ class OtelDestination(LiteLLMBaseModel): success_sampling_rate: float | None = Field( default=None, description=( - "Share of the request trees forwarded, 0.0..1.0, drawn once per request when its root span ends; " + "Share of the request trees forwarded, 0.0..1.0, picked by a hash of the trace id; " "``None`` forwards every one." ), ) diff --git a/litellm/integrations/otel/plumbing/providers.py b/litellm/integrations/otel/plumbing/providers.py index 898eab5715b..fc1e9b7099d 100644 --- a/litellm/integrations/otel/plumbing/providers.py +++ b/litellm/integrations/otel/plumbing/providers.py @@ -1,13 +1,14 @@ """Provider / exporter factory + the Baggage span processor.""" import queue -import random import threading import time +import weakref from collections import OrderedDict from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence +from hashlib import sha256 from types import MappingProxyType -from typing import TYPE_CHECKING, Final, Literal +from typing import TYPE_CHECKING, Final, Literal, TypeAlias from opentelemetry import _logs, baggage, metrics, trace from opentelemetry._logs import Logger, LoggerProvider, NoOpLoggerProvider @@ -486,15 +487,15 @@ def is_llm_call_span(span: ReadableSpan) -> bool: return GenAI.OPERATION_NAME in attributes and MCP.METHOD_NAME not in attributes -#: Request trees held back for a sampled destination until the last span of the trace +#: Request trees held back for a sampled destination until the last span of the request #: still open ends, the verdicts kept for the spans that start after that (a post-call -#: database write), and the spans in flight per trace. All bounded, so a span that never -#: ends, or a flood of requests, cannot hold spans for ever: the oldest tree is decided -#: on what it has. +#: database write), the spans in flight per request, and the request each open span +#: belongs to. All bounded, so a span that never ends, or a flood of requests, cannot +#: hold spans for ever: the oldest tree is decided on what it has. _MAX_PENDING_TREES: Final = 1024 _MAX_PENDING_SPANS_PER_TREE: Final = 512 _MAX_REMEMBERED_VERDICTS: Final = 4096 -_MAX_OPEN_TRACES: Final = 16384 +_MAX_OPEN_REQUESTS: Final = 16384 class _PendingTree: @@ -512,25 +513,35 @@ def _is_sampled(destination: "OtelDestination") -> bool: return destination.success_sampling_rate is not None or destination.error_sampling_rate is not None -#: A verdict is one request tree's draw for one destination at its rates: the exporter +#: A request is its trace id and the id of its root, the span of it whose parent is +#: remote or missing. Calls sharing a caller's trace are separate requests, each decided +#: on its own outcome. +_RequestKey: TypeAlias = tuple[int, int] + +#: A verdict is one request's keep or drop for one destination at its rates: the exporter #: ``cache_key`` leaves the rates out, so two destinations sharing an exporter with -#: different rates would otherwise share one draw. -_VerdictKey = tuple[int, object, float | None, float | None] +#: different rates would otherwise share one verdict. +_VerdictKey: TypeAlias = tuple[int, int, object, float | None, float | None] -def _verdict_key(trace_id: int, destination: "OtelDestination") -> _VerdictKey: - return (trace_id, destination.cache_key(), destination.success_sampling_rate, destination.error_sampling_rate) +def _verdict_key(request: _RequestKey, destination: "OtelDestination") -> _VerdictKey: + return (*request, destination.cache_key(), destination.success_sampling_rate, destination.error_sampling_rate) -def _keeps_tree(destination: "OtelDestination", failed: bool, draw: Callable[[], float]) -> bool: - """Whether one draw against the destination's rate keeps a request tree, read the - way the legacy Arize callback reads ``arize_success_sampling_rate`` / - ``arize_error_sampling_rate``: a tree with a failed span answers to the error - rate, an unset rate keeps everything and ``0.0`` keeps nothing.""" +def _keeps_tree(destination: "OtelDestination", failed: bool, trace_id: int) -> bool: + """Whether the destination's rate keeps a request tree of ``trace_id``: a tree with + a failed span answers to ``arize_error_sampling_rate``, the rest to + ``arize_success_sampling_rate``, an unset rate keeps everything and ``0.0`` keeps + nothing. + + The first eight bytes of a SHA-256 of the trace id are read against the rate, so + every process deciding a request of the trace with the same outcome reaches the same + verdict.""" rate: Final = destination.error_sampling_rate if failed else destination.success_sampling_rate if rate is None: return True - return rate > 0.0 and draw() <= rate + digest: Final = sha256(trace_id.to_bytes(16, "big")).digest() + return int.from_bytes(digest[:8], "big") < round(rate * 2**64) def _in_scope(span: ReadableSpan, scope: "OtelSpanScope") -> bool: @@ -667,10 +678,8 @@ class TenantFanOutSpanProcessor(SpanProcessor): default_capture_by_backend: Mapping[str, str] = MappingProxyType({}), pending_drains: int = _MAX_PENDING_DRAINS, drain_pool: _DrainPool | None = None, - sampling_draw: Callable[[], float] | None = None, ) -> None: self._operator_sinks: Final = operator_sinks - self._draw: Final = sampling_draw if sampling_draw is not None else random.random self._excluded_db_systems: Final = excluded_db_systems self._default_capture: Final = default_capture self._default_capture_by_backend: Final = default_capture_by_backend @@ -682,24 +691,32 @@ class TenantFanOutSpanProcessor(SpanProcessor): self._processors: OrderedDict[object, SpanProcessor] = OrderedDict() # mutable-ok: bounded LRU self._retired: OrderedDict[int, SpanProcessor] = OrderedDict() # mutable-ok: drains as exports finish self._exporting: dict[int, int] = {} # mutable-ok: per-processor in-flight export count - self._pending: OrderedDict[int, _PendingTree] = OrderedDict() # mutable-ok: bounded, by trace id - self._open: OrderedDict[int, int] = OrderedDict() # mutable-ok: bounded, spans in flight by trace id + self._pending: OrderedDict[_RequestKey, _PendingTree] = OrderedDict() # mutable-ok: bounded, by request + self._open: OrderedDict[_RequestKey, set[int]] = OrderedDict() # mutable-ok: bounded, open span ids by request + self._roots: dict[int, _RequestKey] = {} # mutable-ok: holds only the spans ``_open`` holds, request by span id + self._span_roots: weakref.WeakKeyDictionary[SDKSpan, int] = weakref.WeakKeyDictionary() self._verdicts: OrderedDict[_VerdictKey, bool] = OrderedDict() # mutable-ok: bounded LRU self._drain: Final = drain_pool if drain_pool is not None else _DrainPool(capacity=pending_drains) def on_start(self, span: SDKSpan, parent_context: Context | None = None) -> None: context: Final = span.context - trace_id: Final = context.trace_id if context is not None else 0 with self._lock: - self._open[trace_id] = self._open.get(trace_id, 0) + 1 - while len(self._open) > _MAX_OPEN_TRACES: - self._open.popitem(last=False) + request: Final = self._request_of(span, trace.get_current_span(parent_context)) + if context is not None: + self._span_roots[span] = request[1] + self._roots[context.span_id] = request + self._open.setdefault(request, set()).add(context.span_id) + while len(self._open) > _MAX_OPEN_REQUESTS: + for forgotten in self._open.popitem(last=False)[1]: + self._roots.pop(forgotten, None) def on_end(self, span: ReadableSpan) -> None: suppressed: Final = suppressed_backends() attributes: Final = span.attributes or _NO_ATTRIBUTES - context: Final = span.context - trace_id: Final = context.trace_id if context is not None else 0 + with self._lock: + request: Final = self._request_of(span) + if span.context is not None: + self._roots.pop(span.context.span_id, None) failed: Final = span.status.status_code is StatusCode.ERROR for destination in request_destinations(): if ( @@ -716,11 +733,32 @@ class TenantFanOutSpanProcessor(SpanProcessor): if not _is_sampled(destination): self._forward(span, destination) continue - for held_span, held_destination in self._route(trace_id, span, destination, failed): + for held_span, held_destination in self._route(request, span, destination, failed): self._forward(held_span, held_destination) - for held_span, held_destination in self._settle(trace_id, failed): + for held_span, held_destination in self._settle(request, span, failed): self._forward(held_span, held_destination) + def _request_of(self, span: ReadableSpan, parent_span: Span | None = None) -> _RequestKey: + """The request ``span`` belongs to, read under ``_lock``: the request recorded while + it is open, else the span itself when its parent is remote or missing, else its + parent's request, recorded while the parent is open or on ``parent_span`` for a + child starting under a parent that has ended, else the parent itself.""" + context: Final = span.context + if context is None: + return (0, 0) + recorded: Final = self._roots.get(context.span_id) + if recorded is not None: + return recorded + parent: Final = span.parent + if parent is None or parent.is_remote: + return (context.trace_id, context.span_id) + parent_request: Final = self._roots.get(parent.span_id) + if parent_request is not None: + return parent_request + if isinstance(parent_span, SDKSpan): + return (context.trace_id, self._span_roots.get(parent_span, parent.span_id)) + return (context.trace_id, parent.span_id) + def _default_capture_for(self, destination: "OtelDestination") -> str: backend: Final = destination.callback_name return ( @@ -743,14 +781,14 @@ class TenantFanOutSpanProcessor(SpanProcessor): self._release(processor) def _route( - self, trace_id: int, span: ReadableSpan, destination: "OtelDestination", failed: bool + self, request: _RequestKey, span: ReadableSpan, destination: "OtelDestination", failed: bool ) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": """The spans to forward now for ``span`` on a sampled ``destination``: the span itself when its tree's verdict is already known and keeps it, nothing while the - tree is held for the rest of its trace, or what the bounds made the fan-out decide + tree is held for the rest of its request, or what the bounds made the fan-out decide early (a tree that outgrew its cap is decided on what it has, and so is the oldest tree once too many are waiting, so a span that never ends holds nothing back for - ever). A failed span marks its tree before any such early draw. Once shutdown + ever). A failed span marks its tree before any such early decision. Once shutdown has started flushing, nothing is held: a span ending then is decided at once, since no later flush would reach it. @@ -758,58 +796,62 @@ class TenantFanOutSpanProcessor(SpanProcessor): another thread between the two would leave this span in a fresh tree. """ with self._lock: - verdict: Final = self._verdicts.get(_verdict_key(trace_id, destination)) + verdict: Final = self._verdicts.get(_verdict_key(request, destination)) if verdict is not None: return ((span, destination),) if verdict else () - tree: Final = self._pending.setdefault(trace_id, _PendingTree()) + tree: Final = self._pending.setdefault(request, _PendingTree()) tree.held.append((span, destination)) if failed: tree.failed = True if self._draining or len(tree.held) >= _MAX_PENDING_SPANS_PER_TREE: - return self._decide_locked(trace_id) + return self._decide_locked(request) if len(self._pending) > _MAX_PENDING_TREES: oldest: Final = next(iter(self._pending)) return self._decide_locked(oldest) return () - def _decide(self, trace_id: int) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": + def _decide(self, request: _RequestKey) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": with self._lock: - return self._decide_locked(trace_id) + return self._decide_locked(request) - def _settle(self, trace_id: int, failed: bool) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": - """What the trace's held tree exports once the span that just ended was the last - of its trace still open. The model-call span ends in the post-call callback, - after the server span, so a tree is decided when its trace goes quiet rather than + def _settle( + self, request: _RequestKey, span: ReadableSpan, failed: bool + ) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": + """What the request's held tree exports once the span that just ended was the last + of its request still open. The model-call span ends in the post-call callback, + after the server span, so a tree is decided when its request goes quiet rather than when its root ends, and a model call that fails late still answers to the error - rate. A trace whose starts this fan-out never saw (attached mid-flight, or forgotten - by the bound) is decided as each of its spans ends. + rate. A request whose starts this fan-out never saw (attached mid-flight, or + forgotten by the bound) is decided as each of its spans ends. """ with self._lock: - tree: Final = self._pending.get(trace_id) + tree: Final = self._pending.get(request) if tree is not None and failed: tree.failed = True - still_open: Final = self._open.pop(trace_id, 1) - 1 - if still_open > 0: - self._open[trace_id] = still_open + still_open: Final = self._open.pop(request, set()) + if span.context is not None: + still_open.discard(span.context.span_id) + if still_open: + self._open[request] = still_open return () - return self._decide_locked(trace_id) + return self._decide_locked(request) - def _decide_locked(self, trace_id: int) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": - """Draw once per destination for the held tree; the spans the draws keep, and a - verdict remembered for the spans of this trace that start later.""" - tree: Final = self._pending.pop(trace_id, None) + def _decide_locked(self, request: _RequestKey) -> "tuple[tuple[ReadableSpan, OtelDestination], ...]": + """Decide once per destination for the held tree; the spans the verdicts keep, and + a verdict remembered for the spans of this request that start later.""" + tree: Final = self._pending.pop(request, None) if tree is None: return () - verdicts: dict[_VerdictKey, bool] = {} # mutable-ok: one draw per destination of this tree + verdicts: dict[_VerdictKey, bool] = {} # mutable-ok: one verdict per destination of this tree for _, destination in tree.held: - key = _verdict_key(trace_id, destination) + key = _verdict_key(request, destination) if key not in verdicts: - verdicts[key] = _keeps_tree(destination, tree.failed, self._draw) + verdicts[key] = _keeps_tree(destination, tree.failed, request[0]) self._verdicts[key] = verdicts[key] while len(self._verdicts) > _MAX_REMEMBERED_VERDICTS: self._verdicts.popitem(last=False) return tuple( - (span, destination) for span, destination in tree.held if verdicts[_verdict_key(trace_id, destination)] + (span, destination) for span, destination in tree.held if verdicts[_verdict_key(request, destination)] ) def _operator_already_writes( @@ -850,8 +892,8 @@ class TenantFanOutSpanProcessor(SpanProcessor): with self._lock: self._draining = True undecided: Final = tuple(self._pending) - for trace_id in undecided: - for held_span, held_destination in self._decide(trace_id): + for request in undecided: + for held_span, held_destination in self._decide(request): self._forward(held_span, held_destination) deadline: Final = time.monotonic() + self._drain_seconds with self._lock: diff --git a/tests/integration/observability/test_otel_team_sampling_across_pods.py b/tests/integration/observability/test_otel_team_sampling_across_pods.py new file mode 100644 index 00000000000..dc7b295febd --- /dev/null +++ b/tests/integration/observability/test_otel_team_sampling_across_pods.py @@ -0,0 +1,723 @@ +"""A team's Arize sampling rate decides once per trace, so every pod and worker serving a call of it agrees. + +Two proxies share one database, as two pods behind a load balancer do: the first runs one worker, the second +two. Calls of one trace carry the caller's ``traceparent`` and land on different pods and workers; the team's +Arize space must receive every call of a trace or none of them. +""" + +import asyncio +import json +import re +import uuid +from collections.abc import Callable, Generator, Iterator, Mapping, Sequence +from concurrent.futures import ThreadPoolExecutor +from contextlib import contextmanager +from dataclasses import dataclass, replace +from itertools import chain, product +from pathlib import Path +from typing import Final, Literal, TypeAlias +from urllib.parse import urlparse + +import httpx +import pytest +import yaml +from integration._support.client import ( + Scenario, + eventually, + gateway_from_environment, + list_value, + object_value, + until_asserted, +) +from integration._support.database import scratch_database +from integration._support.otlp_sink import Span, SpanSinks, configure_sink, recorded_requests, recorded_spans +from integration._support.process import OwnedProxy, WorkerHealthcheck, owned_proxy_process +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue, TypeAdapter + +MODEL: Final = "team-sampling" +SERVER: Final = 2 +CALL_ID: Final = "litellm.call_id" +OPERATION: Final = "gen_ai.operation.name" +SPACE: Final = "space-sampled" +CREDENTIALS: Final = {"arize_space_key": SPACE, "arize_api_key": "arize-sampled"} +FAILING: Final = re.compile(rb"sampling-fail") +JSON: Final[TypeAdapter[JsonValue]] = TypeAdapter(JsonValue) +TRACES: Final = 16 +Endpoint: TypeAlias = Literal["chat", "responses", "messages"] +Verdicts: TypeAlias = Mapping[str, tuple[bool, ...]] + + +def _chat_reply(stream: bool) -> Reply: + usage: Final[JsonValue] = {"prompt_tokens": 7, "completion_tokens": 2, "total_tokens": 9} + if not stream: + return Reply( + body=json.dumps( + { + "id": "chatcmpl-sampling", + "object": "chat.completion", + "created": 1, + "model": "gpt-4o-mini", + "choices": [ + {"index": 0, "message": {"role": "assistant", "content": "ok"}, "finish_reason": "stop"} + ], + "usage": usage, + } + ).encode() + ) + chunk: Final = {"id": "chatcmpl-sampling", "object": "chat.completion.chunk", "created": 1, "model": "gpt-4o-mini"} + deltas: Final[tuple[JsonValue, ...]] = ( + {**chunk, "choices": [{"index": 0, "delta": {"role": "assistant", "content": "ok"}}]}, + {**chunk, "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}, + {**chunk, "choices": [], "usage": usage}, + ) + return Reply( + content_type="text/event-stream", + chunks=(*(b"data: " + json.dumps(delta).encode() + b"\n\n" for delta in deltas), b"data: [DONE]\n\n"), + ) + + +def _responses_reply(stream: bool) -> Reply: + response: Final[Mapping[str, JsonValue]] = { + "id": "resp_sampling", + "object": "response", + "created_at": 1, + "status": "completed", + "model": "gpt-4o-mini", + "output": [ + { + "id": "msg_sampling", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "ok", "annotations": []}], + } + ], + "usage": {"input_tokens": 7, "output_tokens": 2, "total_tokens": 9}, + } + if not stream: + return Reply(body=json.dumps(response).encode()) + events: Final[tuple[Mapping[str, JsonValue], ...]] = ( + { + "type": "response.created", + "sequence_number": 0, + "response": {**response, "status": "in_progress", "output": []}, + }, + { + "type": "response.output_text.delta", + "sequence_number": 1, + "item_id": "msg_sampling", + "output_index": 0, + "content_index": 0, + "delta": "ok", + }, + {"type": "response.completed", "sequence_number": 2, "response": dict(response)}, + ) + return Reply( + content_type="text/event-stream", + chunks=tuple(f"event: {event['type']}\ndata: {json.dumps(event)}\n\n".encode() for event in events), + ) + + +def _upstream(request: Request) -> Reply: + if FAILING.search(request.body) is not None: + return Reply(status=500, body=b'{"error":{"message":"scripted upstream failure","type":"server_error"}}') + stream: Final = object_value(JSON.validate_json(request.body)).get("stream") is True + if request.target.endswith("/responses"): + return _responses_reply(stream) + return _chat_reply(stream) + + +@dataclass(frozen=True, slots=True) +class Sent: + trace_id: str + parent_span_id: str + call_id: str + + +@dataclass(frozen=True, slots=True) +class Call: + path: str + body: Mapping[str, JsonValue] + headers: Mapping[str, str] + sent: Sent + expected: int + + +def _call( + key: str, trace_id: str | None, endpoint: Endpoint = "chat", stream: bool = False, content: str = "hello" +) -> Call: + parent: Final = uuid.uuid4().hex[:16] + call_id: Final = str(uuid.uuid4()) + messages: Final[JsonValue] = [{"role": "user", "content": content}] + path, body = { + "chat": ("/v1/chat/completions", {"model": MODEL, "messages": messages, "stream": stream}), + "responses": ("/v1/responses", {"model": MODEL, "input": content, "stream": stream}), + "messages": ("/v1/messages", {"model": MODEL, "max_tokens": 16, "messages": messages, "stream": stream}), + }[endpoint] + headers: Final = { + "Authorization": f"Bearer {key}", + "x-litellm-call-id": call_id, + "connection": "close", + **({} if trace_id is None else {"traceparent": f"00-{trace_id}-{parent}-01"}), + } + expected: Final = 500 if FAILING.search(content.encode()) else 200 + return Call(path, body, headers, Sent(trace_id or "", parent if trace_id else "", call_id), expected) + + +@dataclass(frozen=True, slots=True) +class Pods: + """``first`` serves with one worker, ``second`` with two.""" + + first: OwnedProxy + second: OwnedProxy + scenario: Scenario + sinks: SpanSinks + + def team_key(self, variables: Mapping[str, str]) -> str: + team: Final = self.scenario.team() + attached: Final = self.first.gateway.request( + "POST", f"/team/{team}/callback", {"callback_name": "arize", "callback_vars": dict(variables)} + ) + assert attached.status_code == 200, attached.text + return self.scenario.key(team_id=team) + + def send(self, pod: OwnedProxy, call: Call) -> Sent: + with pod.gateway.client.stream("POST", call.path, json=call.body, headers=call.headers) as response: + text: Final = response.read().decode() + assert response.status_code == call.expected, text + return call.sent + + async def send_async(self, client: httpx.AsyncClient, call: Call) -> Sent: + async with client.stream("POST", call.path, json=call.body, headers=call.headers) as response: + text: Final = (await response.aread()).decode() + assert response.status_code == call.expected, text + return call.sent + + def on_each_worker(self, pod: OwnedProxy, call: Callable[[], Call]) -> tuple[Sent, ...]: + """One call served by each worker of ``pod``, the others suspended while it runs.""" + workers: Final = sorted(pod.worker_pids()) + assert len(workers) == pod.gateway.workers, workers + sent: Final[list[Sent]] = [] # mutable-ok: one call per worker, in worker order + for serving in workers: + with _suspended_all_but(pod, workers, serving): + sent.append(self.send(pod, call())) + return tuple(sent) + + def fence(self, key: str) -> tuple[Sent, ...]: + """Calls of an unsampled team on the sampled team's exporter, one per worker of both pods. + + Each worker sends spans to that exporter in the order they were handed to it, so all of these + arriving means every call any worker decided before them has been sent too. + """ + return (self.send(self.first, _call(key, uuid.uuid4().hex)),) + self.on_each_worker( + self.second, lambda: _call(key, uuid.uuid4().hex) + ) + + +@contextmanager +def _suspended_all_but(pod: OwnedProxy, workers: Sequence[int], serving: int) -> Generator[None]: + if len(workers) == 1: + yield + return + others: Final = [pid for pid in workers if pid != serving] + with pod.suspended(others[0]): + yield + + +def _space_id(request: Mapping[str, JsonValue]) -> JsonValue: + return {name.lower(): value for name, value in object_value(request.get("headers") or {}).items()}.get( + "arize-space-id" + ) + + +def _space_span_ids(url: str) -> frozenset[str]: + """Span ids the Arize sink received in export requests sent with the team's space id.""" + ours: Final = ( + list_value(request.get("span_ids") or []) for request in recorded_requests(url) if _space_id(request) == SPACE + ) + return frozenset(span_id for span_id in chain.from_iterable(ours) if isinstance(span_id, str)) + + +def _team_spans(pods: Pods, since: int) -> tuple[Span, ...]: + owned: Final = _space_span_ids(pods.sinks.arize) + return tuple(span for span in recorded_spans(pods.sinks.arize, since)[1] if span["span_id"] in owned) + + +def _delivered(spans: tuple[Span, ...], sent: Sequence[Sent]) -> frozenset[str]: + """Call ids of ``sent`` whose server span is among ``spans``.""" + by_parent: Final = {(call.trace_id, call.parent_span_id): call.call_id for call in sent} + return frozenset( + by_parent[(span["trace_id"], span["parent_span_id"])] + for span in spans + if span["kind"] == SERVER and (span["trace_id"], span["parent_span_id"]) in by_parent + ) + + +def _await_logged(pods: Pods, since: int, sent: Sequence[Sent]) -> None: + """Wait until every call of ``sent`` has its model span in the operator's unsampled copy.""" + expected: Final = {call.call_id for call in sent} + eventually( + lambda: ( + { + str(span["attributes"][CALL_ID]) + for span in recorded_spans(pods.sinks.operator, since)[1] + if OPERATION in span["attributes"] and CALL_ID in span["attributes"] + } + & expected + ), + lambda logged: logged == expected, + seconds=60, + ) + + +@dataclass(frozen=True, slots=True) +class Cursors: + operator: int + arize: int + + +def _cursors(pods: Pods) -> Cursors: + return Cursors(recorded_spans(pods.sinks.operator)[0], recorded_spans(pods.sinks.arize)[0]) + + +def _await_flushed(pods: Pods, cursors: Cursors, fence_key: str) -> None: + """Wait until every worker of both pods has sent the team's exporter everything it decided so far.""" + fences: Final = pods.fence(fence_key) + + def arrived() -> None: + delivered: Final = _delivered(_team_spans(pods, cursors.arize), fences) + assert {fence.call_id for fence in fences} <= delivered, "the unsampled team's calls have not arrived yet" + + until_asserted(arrived, seconds=30) + + +def _settled( + pods: Pods, + cursors: Cursors, + by_trace: Mapping[str, Sequence[Sent]], + fence_key: str, + holds: Callable[[tuple[bool, ...]], bool] = lambda kept: len(set(kept)) == 1, +) -> Verdicts: + """Per trace, which of its calls reached the team's space, once every worker has sent what it decided + and ``holds`` is true of every trace.""" + calls: Final = tuple(chain.from_iterable(by_trace.values())) + _await_logged(pods, cursors.operator, calls) + _await_flushed(pods, cursors, fence_key) + + def check() -> Verdicts: + delivered: Final = _delivered(_team_spans(pods, cursors.arize), calls) + verdicts: Final = { + trace_id: tuple(call.call_id in delivered for call in sent) for trace_id, sent in by_trace.items() + } + broken: Final = {trace_id: kept for trace_id, kept in verdicts.items() if not holds(kept)} + assert broken == {}, f"{len(broken)} of {len(verdicts)} traces were split (per call, kept): {broken}" + return verdicts + + return until_asserted(check, seconds=30) + + +def _trace_ids(cell: str, count: int = TRACES) -> tuple[str, ...]: + """Fixed RFC 4122 shaped trace ids, like the uuid4 hex a caller mints for ``traceparent``.""" + return tuple(uuid.uuid5(uuid.NAMESPACE_URL, f"team-sampling/{cell}/{index}").hex for index in range(count)) + + +def _kept(verdicts: Verdicts) -> int: + return sum(1 for kept in verdicts.values() if all(kept)) + + +@pytest.mark.parametrize( + ("variables", "content"), + [({"arize_success_sampling_rate": "0.5"}, "hello"), ({"arize_error_sampling_rate": "0.5"}, "sampling-fail")], + ids=["success-rate", "error-rate"], +) +def test_each_trace_reaches_the_team_space_whole_or_not_at_all( + pods: Pods, variables: Mapping[str, str], content: str +) -> None: + """The ticket's case: three calls per trace load balanced over the pods.""" + sampled: Final = pods.team_key({**CREDENTIALS, **variables}) + cursors: Final = _cursors(pods) + by_trace: Final = { + trace_id: tuple( + pods.send(pod, _call(sampled, trace_id, content=content)) for pod in (pods.first, pods.second, pods.first) + ) + for trace_id in _trace_ids("-".join(sorted(variables))) + } + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < TRACES, f"a rate of 0.5 kept {_kept(verdicts)} of {TRACES} traces" + + +@pytest.mark.parametrize("client", ["sync", "async"]) +def test_every_endpoint_follows_the_verdict_of_its_trace(pods: Pods, client: str) -> None: + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + plan: Final[tuple[tuple[OwnedProxy, Endpoint, bool], ...]] = ( + (pods.first, "chat", False), + (pods.second, "chat", True), + (pods.first, "responses", False), + (pods.second, "messages", True), + ) + + async def send_trace(trace_id: str) -> tuple[Sent, ...]: + clients: Final = { + id(pod): httpx.AsyncClient(base_url=str(pod.gateway.client.base_url), timeout=15, trust_env=False) + for pod in (pods.first, pods.second) + } + try: + return tuple( + await asyncio.gather( + *( + pods.send_async(clients[id(pod)], _call(sampled, trace_id, endpoint, stream)) + for pod, endpoint, stream in plan + ) + ) + ) + finally: + for opened in clients.values(): + await opened.aclose() + + by_trace: Final = { + trace_id: ( + tuple(pods.send(pod, _call(sampled, trace_id, endpoint, stream)) for pod, endpoint, stream in plan) + if client == "sync" + else asyncio.run(send_trace(trace_id)) + ) + for trace_id in _trace_ids(f"endpoints-{client}") + } + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < TRACES + + +def test_the_workers_of_one_pod_agree(pods: Pods) -> None: + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + by_trace: Final = { + trace_id: pods.on_each_worker(pods.second, lambda trace_id=trace_id: _call(sampled, trace_id)) + for trace_id in _trace_ids("workers") + } + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < TRACES + + +def test_a_replaced_worker_reaches_the_verdict_its_predecessor_reached(pods: Pods) -> None: + """A worker that restarts forgets the verdicts it remembered; the calls it serves next for the same + traces still answer the way the first ones did.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + fence_key: Final = pods.team_key(CREDENTIALS) + traces: Final = _trace_ids("replaced") + before: Final = { + trace_id: pods.on_each_worker(pods.second, lambda t=trace_id: _call(sampled, t)) for trace_id in traces + } + _await_logged(pods, cursors.operator, tuple(chain.from_iterable(before.values()))) + _await_flushed(pods, cursors, fence_key) + for victim in sorted(pods.second.worker_pids()): + pods.second.kill_worker(victim) + by_trace: Final = { + trace_id: before[trace_id] + pods.on_each_worker(pods.second, lambda t=trace_id: _call(sampled, t)) + for trace_id in traces + } + + verdicts: Final = _settled(pods, cursors, by_trace, fence_key) + + assert 0 < _kept(verdicts) < TRACES + + +def test_a_failed_call_is_kept_wherever_a_successful_call_of_its_trace_is(pods: Pods) -> None: + """One call of the trace fails on one pod and answers to the error rate, another succeeds on the other + pod and answers to the success rate: with the error rate above the success rate, no trace keeps its + successful call and drops its failed one.""" + sampled: Final = pods.team_key( + {**CREDENTIALS, "arize_success_sampling_rate": "0.3", "arize_error_sampling_rate": "0.7"} + ) + cursors: Final = _cursors(pods) + by_trace: Final = { + trace_id: ( + pods.send(pods.first, _call(sampled, trace_id, content="sampling-fail")), + pods.send(pods.second, _call(sampled, trace_id)), + ) + for trace_id in _trace_ids("mixed", 32) + } + + verdicts: Final = _settled( + pods, cursors, by_trace, pods.team_key(CREDENTIALS), holds=lambda kept: kept[0] or not kept[1] + ) + + assert any(kept == (True, False) for kept in verdicts.values()), "no trace sat between the two rates" + assert any(kept == (True, True) for kept in verdicts.values()), "no trace sat under both rates" + + +def test_a_failed_call_answers_to_the_error_rate_on_a_pod_that_dropped_its_trace(pods: Pods) -> None: + """A pod that already dropped a successful call of the trace still keeps a later failed call of it at an + unset error rate, as the pod that only served the failed call does.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.3"}) + cursors: Final = _cursors(pods) + traces: Final = _trace_ids("failed-after-dropped") + succeeded: Final = {trace_id: pods.send(pods.first, _call(sampled, trace_id)) for trace_id in traces} + _await_logged(pods, cursors.operator, tuple(succeeded.values())) + by_trace: Final = { + trace_id: ( + succeeded[trace_id], + pods.send(pods.first, _call(sampled, trace_id, content="sampling-fail")), + pods.send(pods.second, _call(sampled, trace_id, content="sampling-fail")), + ) + for trace_id in traces + } + + verdicts: Final = _settled( + pods, cursors, by_trace, pods.team_key(CREDENTIALS), holds=lambda kept: kept[1] and kept[2] + ) + + assert any(not kept[0] for kept in verdicts.values()), "no successful call was dropped before its trace failed" + + +def test_a_successful_call_answers_to_the_success_rate_on_a_pod_that_kept_its_failed_call(pods: Pods) -> None: + """A pod that already kept a failed call of the trace drops a later successful call of it at a zero + success rate, as the pod that only served the successful call does.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.0"}) + cursors: Final = _cursors(pods) + traces: Final = _trace_ids("succeeded-after-kept") + failed: Final = { + trace_id: pods.send(pods.first, _call(sampled, trace_id, content="sampling-fail")) for trace_id in traces + } + _await_logged(pods, cursors.operator, tuple(failed.values())) + by_trace: Final = { + trace_id: ( + failed[trace_id], + pods.send(pods.first, _call(sampled, trace_id)), + pods.send(pods.second, _call(sampled, trace_id)), + ) + for trace_id in traces + } + + _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS), holds=lambda kept: kept == (True, False, False)) + + +def test_concurrent_calls_of_one_trace_on_one_worker_each_answer_to_their_own_outcome(pods: Pods) -> None: + """A failed and a successful call of the trace in flight at once on the single worker of the first pod + are decided apart: the failed one is kept at an unset error rate, the successful one dropped at a zero + success rate.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.0"}) + cursors: Final = _cursors(pods) + traces: Final = _trace_ids("concurrent-mixed") + plan: Final = tuple(product(traces, ("sampling-fail", "hello"))) + + def send(job: tuple[str, str]) -> Sent: + return pods.send(pods.first, _call(sampled, job[0], content=job[1])) + + with ThreadPoolExecutor(max_workers=8) as pool: + sent: Final = tuple(pool.map(send, plan)) + by_trace: Final = {trace_id: tuple(call for call in sent if call.trace_id == trace_id) for trace_id in traces} + + _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS), holds=lambda kept: kept == (True, False)) + + +@pytest.mark.parametrize( + ("variables", "expected"), + [({"arize_success_sampling_rate": "1.0"}, TRACES), ({"arize_success_sampling_rate": "0.0"}, 0), ({}, TRACES)], + ids=["one", "zero", "unset"], +) +def test_a_rate_at_either_end_keeps_every_trace_or_none( + pods: Pods, variables: Mapping[str, str], expected: int +) -> None: + sampled: Final = pods.team_key({**CREDENTIALS, **variables}) + cursors: Final = _cursors(pods) + by_trace: Final = { + trace_id: tuple(pods.send(pod, _call(sampled, trace_id)) for pod in (pods.first, pods.second)) + for trace_id in _trace_ids(f"edge-{'-'.join(f'{k}={v}' for k, v in sorted(variables.items()))}") + } + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert _kept(verdicts) == expected + + +def test_a_request_without_a_traceparent_is_sampled_whole_at_the_rate(pods: Pods) -> None: + """With no caller trace the proxy starts one per request: each request's server and model spans travel + together and the rate still keeps some requests and drops others.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + sent: Final = tuple( + pods.send(pod, _call(sampled, None)) for _, pod in product(range(12), (pods.first, pods.second)) + ) + _await_logged(pods, cursors.operator, sent) + trace_of: Final = { + str(span["attributes"][CALL_ID]): span["trace_id"] + for span in recorded_spans(pods.sinks.operator, cursors.operator)[1] + if CALL_ID in span["attributes"] + } + fences: Final = pods.fence(pods.team_key(CREDENTIALS)) + + def check() -> Mapping[str, tuple[bool, bool]]: + spans: Final = _team_spans(pods, cursors.arize) + assert {fence.call_id for fence in fences} <= _delivered(spans, fences), ( + "the unsampled team's calls have not arrived yet" + ) + kinds: Final = { + call.call_id: ( + any(span["kind"] == SERVER for span in spans if span["trace_id"] == trace_of[call.call_id]), + any(OPERATION in span["attributes"] for span in spans if span["trace_id"] == trace_of[call.call_id]), + ) + for call in sent + } + partial: Final = {call_id: kept for call_id, kept in kinds.items() if kept[0] != kept[1]} + assert partial == {}, f"requests reached the space without their whole tree (server, model): {partial}" + return kinds + + kept: Final = sum(1 for server, _ in until_asserted(check, seconds=30).values() if server) + assert 0 < kept < len(sent), f"a rate of 0.5 kept {kept} of {len(sent)} requests" + + +def test_a_key_level_destination_follows_the_verdict_of_its_trace(pods: Pods) -> None: + team: Final = pods.scenario.team() + sampled: Final = pods.scenario.key( + team_id=team, + metadata={ + "logging": [ + { + "callback_name": "arize", + "callback_type": "success", + "callback_vars": {**CREDENTIALS, "arize_success_sampling_rate": "0.5"}, + } + ] + }, + ) + cursors: Final = _cursors(pods) + by_trace: Final = { + trace_id: tuple(pods.send(pod, _call(sampled, trace_id)) for pod in (pods.first, pods.second, pods.first)) + for trace_id in _trace_ids("key-level") + } + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < TRACES + + +def test_a_concurrent_burst_keeps_every_trace_whole(pods: Pods) -> None: + """Four calls per trace, all in flight at once across both pods.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + traces: Final = _trace_ids("burst", 24) + plan: Final = tuple(product(traces, (pods.first, pods.second, pods.first, pods.second))) + + def send(job: tuple[str, OwnedProxy]) -> Sent: + return pods.send(job[1], _call(sampled, job[0])) + + with ThreadPoolExecutor(max_workers=16) as pool: + sent: Final = list(pool.map(send, plan)) + by_trace: Final = {trace_id: tuple(call for call in sent if call.trace_id == trace_id) for trace_id in traces} + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < len(traces) + + +def test_a_stalled_team_space_during_a_burst_keeps_serving_and_each_trace_whole(pods: Pods) -> None: + """The team's Arize endpoint stops answering while a burst of calls runs over both pods: every call is + still served, and once the endpoint answers again each trace arrives whole or not at all.""" + sampled: Final = pods.team_key({**CREDENTIALS, "arize_success_sampling_rate": "0.5"}) + cursors: Final = _cursors(pods) + traces: Final = _trace_ids("stalled") + plan: Final = tuple(product(traces, (pods.first, pods.second))) + + def send(job: tuple[str, OwnedProxy]) -> Sent: + return pods.send(job[1], _call(sampled, job[0])) + + configure_sink(pods.sinks.arize, paused=True) + try: + with ThreadPoolExecutor(max_workers=8) as pool: + sent: Final = tuple(pool.map(send, plan)) + finally: + configure_sink(pods.sinks.arize, paused=False) + by_trace: Final = {trace_id: tuple(call for call in sent if call.trace_id == trace_id) for trace_id in traces} + + verdicts: Final = _settled(pods, cursors, by_trace, pods.team_key(CREDENTIALS)) + + assert 0 < _kept(verdicts) < len(traces) + + +def _write_config(directory: Path, sinks: SpanSinks, provider: Wire) -> Path: + shared: Final = object_value( + JSON.validate_python(yaml.safe_load(Path("tests/integration/proxy_config.yaml").read_text())) + ) + config: Final = { + **shared, + "model_list": [ + *list_value(shared.get("model_list") or []), + { + "model_name": MODEL, + "litellm_params": { + "model": "openai/gpt-4o-mini", + "api_key": "integration-provider-key", + "api_base": provider.url + "/v1", + }, + }, + ], + "litellm_settings": { + **object_value(shared.get("litellm_settings") or {}), + "callbacks": ["langfuse_otel"], + "otel_tenant_destination_mode": "additive", + "provider_url_destination_allowed_hosts": [urlparse(sinks.operator).netloc, urlparse(sinks.arize).netloc], + }, + "router_settings": {**object_value(shared.get("router_settings") or {}), "num_retries": 0}, + "general_settings": {**object_value(shared.get("general_settings") or {}), "user_api_key_cache_ttl": 2}, + } + path: Final = directory / "team-sampling.yaml" + path.write_text(yaml.safe_dump(config)) + return path + + +@contextmanager +def _two_pods(provider: Wire, sinks: SpanSinks, directory: Path) -> Generator[Pods]: + environment: Final = { + "LITELLM_OTEL_V2": "1", + "OTEL_BSP_SCHEDULE_DELAY": "300", + "MAPPER_NAMES": "genai,openinference", + "LANGFUSE_HOST": sinks.operator, + "LANGFUSE_PUBLIC_KEY": "pk-lf-operator", + "LANGFUSE_SECRET_KEY": "sk-lf-operator", + "ARIZE_SPACE_KEY": "space-operator", + "ARIZE_API_KEY": "arize-operator", + "ARIZE_HTTP_ENDPOINT": sinks.arize + "/v1/traces", + } + removed: Final = ("LITELLM_OTEL_TENANT_DESTINATION_MODE", "OTEL_TRACES_SAMPLER", "OTEL_TRACES_SAMPLER_ARG") + config: Final = _write_config(directory, sinks, provider) + for pod in ("first", "second"): + (directory / pod).mkdir() + with gateway_from_environment() as shared, scratch_database("team_sampling") as database_url: + overrides: Final = {**environment, "DATABASE_URL": database_url} + with ( + owned_proxy_process( + shared, directory / "first", overrides, config=config, remove_environment=removed + ) as first, + owned_proxy_process( + shared, + directory / "second", + overrides, + config=config, + remove_environment=removed, + workers=2, + healthcheck=WorkerHealthcheck.LENIENT, + ) as second, + replace(first.gateway, database_url=database_url).scenario() as scenario, + ): + eventually(second.worker_pids, lambda pids: len(pids) == 2, seconds=120) + yield Pods(first, second, scenario, sinks) + + +@pytest.fixture(scope="module") +def provider() -> Iterator[Wire]: + with wire_server(_upstream) as wire: + yield wire + + +@pytest.fixture(scope="module") +def pods(provider: Wire, audit_sinks: SpanSinks, tmp_path_factory: pytest.TempPathFactory) -> Iterator[Pods]: + with _two_pods(provider, audit_sinks, tmp_path_factory.mktemp("team-sampling")) as started: + yield started diff --git a/tests/unit/integrations/otel/test_otel_v2_destinations.py b/tests/unit/integrations/otel/test_otel_v2_destinations.py index c7a057cca5c..782442aad22 100644 --- a/tests/unit/integrations/otel/test_otel_v2_destinations.py +++ b/tests/unit/integrations/otel/test_otel_v2_destinations.py @@ -4,6 +4,7 @@ import asyncio import contextvars import threading import time +import uuid from base64 import b64encode from collections.abc import Mapping from dataclasses import replace @@ -14,10 +15,12 @@ from typing import Final import pytest from opentelemetry import trace as trace_api +from opentelemetry.context import Context from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import ReadableSpan, TracerProvider from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter +from opentelemetry.sdk.trace.id_generator import RandomIdGenerator from opentelemetry.trace import Status, StatusCode import litellm @@ -1159,12 +1162,36 @@ class TestFanOut: ARIZE_TEAM_PARAMS = {"arize_space_id": "space-team", "arize_api_key": "key-team"} +def caller_context(trace_id: int) -> Context: + return trace_api.set_span_in_context( + trace_api.NonRecordingSpan( + trace_api.SpanContext(trace_id=trace_id, span_id=1, is_remote=True, trace_flags=trace_api.TraceFlags(0x01)) + ) + ) + + def arize_destination(**rates: str) -> OtelDestination: destination = destination_for("arize", {**ARIZE_TEAM_PARAMS, **rates}) assert destination is not None, "the fixture must resolve for the test to mean anything" return destination +#: Trace ids whose SHA-256 reads 0.40 and 0.80 of the way through its first eight bytes, +#: so a rate of 0.5 keeps the first and drops the second. +KEPT_AT_HALF: Final = 4 +DROPPED_AT_HALF: Final = 5 + + +class _FixedTraceId(RandomIdGenerator): + """Every trace the provider starts gets ``trace_id``.""" + + def __init__(self, trace_id: int) -> None: + self._trace_id = trace_id + + def generate_trace_id(self) -> int: + return self._trace_id + + class TestTenantSampling: """A team's ``arize_success_sampling_rate`` and ``arize_error_sampling_rate`` decide how much of its traffic the fan-out delivers to its space, the way they already decide what @@ -1173,14 +1200,17 @@ class TestTenantSampling: NAMES = {"POST /v1/chat/completions", "auth /v1/chat/completions", "chat gpt-4"} @staticmethod - def _fan_out(dest_exporter, draw=None, global_exporter=None): - provider = TracerProvider() + def _fan_out( + dest_exporter: InMemorySpanExporter, + trace_id: int | None = None, + global_exporter: InMemorySpanExporter | None = None, + ) -> TracerProvider: + provider: Final = TracerProvider() if trace_id is None else TracerProvider(id_generator=_FixedTraceId(trace_id)) if global_exporter is not None: provider.add_span_processor(_OverriddenBackendFilter(SimpleSpanProcessor(global_exporter), "arize")) - kwargs = {"processor_factory": lambda _d: SimpleSpanProcessor(dest_exporter)} - if draw is not None: - kwargs["sampling_draw"] = draw - provider.add_span_processor(TenantFanOutSpanProcessor(**kwargs)) + provider.add_span_processor( + TenantFanOutSpanProcessor(processor_factory=lambda _d: SimpleSpanProcessor(dest_exporter)) + ) return provider @staticmethod @@ -1224,7 +1254,7 @@ class TestTenantSampling: def test_an_unset_rate_exports_everything(self): dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.99) + provider: Final = self._fan_out(dest_exporter, trace_id=DROPPED_AT_HALF) self._run(provider, (arize_destination(),)) @@ -1300,10 +1330,10 @@ class TestTenantSampling: def test_a_span_that_ends_after_the_root_follows_the_requests_verdict(self): """A post-call database write that starts after the whole request tree has ended - goes where the rest of the tree went, without a second draw.""" + goes where the rest of the tree went, without a second verdict.""" kept_exporter, dropped_exporter = InMemorySpanExporter(), InMemorySpanExporter() - kept = self._fan_out(kept_exporter, draw=lambda: 0.1) - dropped = self._fan_out(dropped_exporter, draw=lambda: 0.9) + kept: Final = self._fan_out(kept_exporter, trace_id=KEPT_AT_HALF) + dropped: Final = self._fan_out(dropped_exporter, trace_id=DROPPED_AT_HALF) destinations = (arize_destination(arize_success_sampling_rate="0.5"),) def late_tree(provider): @@ -1327,14 +1357,263 @@ class TestTenantSampling: } assert dropped_exporter.get_finished_spans() == () + def test_a_span_that_ends_after_a_failed_root_follows_the_failed_requests_verdict(self): + """A post-call database write after a failed request goes where that request went, + though on its own it would answer to the success rate.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions") as root: + root.set_status(Status(StatusCode.ERROR, "upstream 500")) + root_context: Final = trace_api.set_span_in_context(root) + with tracer.start_as_current_span("postgres INSERT LiteLLM_SpendLogs", context=root_context): + pass + + in_fresh_context(run) + + assert {s.name for s in dest_exporter.get_finished_spans()} == { + "POST /v1/chat/completions", + "postgres INSERT LiteLLM_SpendLogs", + } + + def test_a_failed_call_after_a_dropped_call_of_its_trace_still_answers_to_the_error_rate(self): + """A process that dropped a successful call of the trace keeps a later failed call of + it at an unset error rate, as a process that only served the failed call does.""" + served_both, served_failed = InMemorySpanExporter(), InMemorySpanExporter() + destinations: Final = (arize_destination(arize_success_sampling_rate="0.5"),) + both: Final = self._fan_out(served_both, trace_id=DROPPED_AT_HALF) + + self._run(both, destinations) + self._run(both, destinations, failed=True) + self._run(self._fan_out(served_failed, trace_id=DROPPED_AT_HALF), destinations, failed=True) + + assert [sorted(s.name for s in exporter.get_finished_spans()) for exporter in (served_both, served_failed)] == [ + sorted(self.NAMES) + ] * 2 + + def test_a_successful_call_after_a_kept_failed_call_of_its_trace_answers_to_the_success_rate(self): + """A process that kept a failed call of the trace drops a later successful call of it + at a zero success rate, as a process that only served the successful call does.""" + served_both, served_success = InMemorySpanExporter(), InMemorySpanExporter() + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + both: Final = self._fan_out(served_both, trace_id=KEPT_AT_HALF) + + self._run(both, destinations, failed=True) + self._run(both, destinations) + self._run(self._fan_out(served_success, trace_id=KEPT_AT_HALF), destinations) + + assert len(served_both.get_finished_spans()) == len(self.NAMES), "only the failed call" + assert all(s.name in self.NAMES for s in served_both.get_finished_spans()) + assert served_success.get_finished_spans() == () + + def test_a_late_child_that_fails_after_its_request_was_dropped_is_dropped_with_it(self): + """A post-call write that fails after its successful request was dropped goes where + the request went, rather than reaching the space on its own at the error rate.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions") as root: + root_context: Final = trace_api.set_span_in_context(root) + with tracer.start_as_current_span("postgres INSERT LiteLLM_SpendLogs", context=root_context) as write: + write.set_status(Status(StatusCode.ERROR, "connection reset")) + + in_fresh_context(run) + + assert dest_exporter.get_finished_spans() == () + + def test_a_failure_deep_in_a_request_marks_the_whole_request(self): + """A database span that fails under the auth span puts the whole request, root + included, on the error rate.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions"): + with tracer.start_as_current_span("auth /v1/chat/completions"): + with tracer.start_as_current_span("postgres SELECT LiteLLM_VerificationToken") as lookup: + lookup.set_status(Status(StatusCode.ERROR, "connection reset")) + with tracer.start_as_current_span("chat gpt-4"): + pass + + in_fresh_context(run) + + assert {s.name for s in dest_exporter.get_finished_spans()} == { + *self.NAMES, + "postgres SELECT LiteLLM_VerificationToken", + } + + def test_a_span_two_levels_down_that_outlives_many_other_spans_still_closes_its_request(self): + """A classifier call under the route span that stays open while the worker serves + more requests than it tracks at once still counts against its own request, so the + request is decided once it ends, on its own outcome.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + + def other_request(): + set_request_destinations(()) + with get_tracer(provider, "litellm").start_as_current_span("POST /v1/chat/completions"): + pass + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions") as root: + with tracer.start_as_current_span("route gpt-4"): + with tracer.start_as_current_span("chat classifier"): + for _ in range(otel_providers._MAX_OPEN_REQUESTS + 1): + contextvars.Context().run(other_request) + with tracer.start_as_current_span("chat gpt-4"): + pass + root.set_status(Status(StatusCode.ERROR, "upstream 500")) + + in_fresh_context(run) + + assert {s.name for s in dest_exporter.get_finished_spans()} == { + "POST /v1/chat/completions", + "route gpt-4", + "chat classifier", + "chat gpt-4", + } + + def test_a_failure_two_levels_down_reaches_its_request_however_many_spans_are_open(self): + """A classifier call that fails while another request holds more spans open than + the worker tracks requests marks its own request, which is then kept at the error + rate as a whole.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + others: Final = contextvars.Context() + + def open_other_request(): + set_request_destinations(()) + tracer: Final = get_tracer(provider, "litellm") + root: Final = tracer.start_span("POST /v1/chat/completions") + parent: Final = trace_api.set_span_in_context(root) + return ( + root, + *(tracer.start_span("chat flood", context=parent) for _ in range(otel_providers._MAX_OPEN_REQUESTS)), + ) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions"): + with tracer.start_as_current_span("route gpt-4"): + with tracer.start_as_current_span("chat classifier") as classifier: + held_open: Final = others.run(open_other_request) + classifier.set_status(Status(StatusCode.ERROR, "classifier timed out")) + return held_open + + held_open: Final = in_fresh_context(run) + + assert {s.name for s in dest_exporter.get_finished_spans()} == { + "POST /v1/chat/completions", + "route gpt-4", + "chat classifier", + } + for span in held_open: + span.end() + + def test_the_fan_out_forgets_a_span_once_it_ends_and_a_request_once_it_is_pushed_out(self): + """The request lookup holds only spans still open, and only those of the requests + the fan-out still counts, so neither ended spans nor a flood of open requests grow + it past the open request bound.""" + fan_out: Final = TenantFanOutSpanProcessor( + processor_factory=lambda _d: SimpleSpanProcessor(InMemorySpanExporter()) + ) + provider: Final = TracerProvider() + provider.add_span_processor(fan_out) + + def run(): + set_request_destinations((arize_destination(arize_success_sampling_rate="1.0"),)) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions"): + with tracer.start_as_current_span("chat gpt-4"): + pass + assert (fan_out._roots, dict(fan_out._open)) == ({}, {}) + root: Final = tracer.start_span("POST /v1/chat/completions", context=trace_api.Context()) + tracer.start_span("route gpt-4", context=trace_api.set_span_in_context(root)) + return tuple( + tracer.start_span(f"POST {index}", context=trace_api.Context()) + for index in range(otel_providers._MAX_OPEN_REQUESTS) + ) + + held_open: Final = in_fresh_context(run) + + assert len(fan_out._roots) == otel_providers._MAX_OPEN_REQUESTS + for span in held_open: + span.end() + assert (fan_out._roots, dict(fan_out._open)) == ({}, {}) + + def test_a_span_starting_under_an_ended_span_below_the_root_follows_its_request(self): + """A post-call write started under the route span after its failed request ended + is kept with the request at the error rate, rather than decided on its own at the + success rate.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + with tracer.start_as_current_span("POST /v1/chat/completions") as root: + with tracer.start_as_current_span("route gpt-4") as route: + route_context: Final = trace_api.set_span_in_context(route) + root.set_status(Status(StatusCode.ERROR, "upstream 500")) + with tracer.start_as_current_span("postgres INSERT LiteLLM_SpendLogs", context=route_context): + pass + + in_fresh_context(run) + + assert {s.name for s in dest_exporter.get_finished_spans()} == { + "POST /v1/chat/completions", + "route gpt-4", + "postgres INSERT LiteLLM_SpendLogs", + } + + def test_concurrent_calls_of_one_trace_each_answer_to_their_own_outcome(self): + """Two calls of the caller's trace in flight at once on one process, one failing, + are decided as two processes serving one each would decide them.""" + dest_exporter: Final = InMemorySpanExporter() + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.0", arize_error_sampling_rate="1.0"),) + remote: Final = trace_api.SpanContext( + trace_id=KEPT_AT_HALF, span_id=1, is_remote=True, trace_flags=trace_api.TraceFlags(0x01) + ) + + def run(): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + caller: Final = trace_api.set_span_in_context(trace_api.NonRecordingSpan(remote)) + with tracer.start_as_current_span("POST failed", context=caller) as failed: + with tracer.start_as_current_span("POST succeeded", context=caller): + pass + failed.set_status(Status(StatusCode.ERROR, "upstream 500")) + + in_fresh_context(run) + + assert [s.name for s in dest_exporter.get_finished_spans()] == ["POST failed"] + def test_a_late_span_whose_verdict_was_forgotten_is_decided_on_its_own_not_stranded(self): """Once enough other requests have been decided to evict a request's verdict, a span of it that starts late (a post-call database write) opens and closes its own count, - so it is decided on a draw of its own as soon as it ends instead of waiting in a tree - nothing would ever close.""" + so it is decided on its own as soon as it ends instead of waiting in a tree nothing + would ever close.""" dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.1) - destinations = (arize_destination(arize_success_sampling_rate="0.5"),) + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="1.0"),) def run(): set_request_destinations(destinations) @@ -1353,18 +1632,17 @@ class TestTenantSampling: assert names[0] == "POST /v1/chat/completions" assert names[-1] == "postgres INSERT LiteLLM_SpendLogs", "exported as it ended, not held for a root" - def test_a_callers_traceparent_neither_steers_the_draw_nor_hides_the_root(self): - """The draw is random, not read from the trace id a caller may have chosen, and the - server span under a remote parent still closes the tree.""" + def test_a_callers_traceparent_decides_by_its_trace_id_and_still_closes_the_tree(self): + """A request continuing a caller's trace is decided on that trace id, and the server + span under a remote parent still closes the tree.""" dest_exporter = InMemorySpanExporter() - draws = iter([0.9, 0.1]) - provider = self._fan_out(dest_exporter, draw=lambda: next(draws)) + provider: Final = self._fan_out(dest_exporter) destinations = (arize_destination(arize_success_sampling_rate="0.5"),) def run(): set_request_destinations(destinations) tracer = get_tracer(provider, "litellm") - for trace_id in (1, 2): + for trace_id in (DROPPED_AT_HALF, KEPT_AT_HALF): remote = trace_api.SpanContext( trace_id=trace_id, span_id=1, is_remote=True, trace_flags=trace_api.TraceFlags(0x01) ) @@ -1377,12 +1655,12 @@ class TestTenantSampling: kept = dest_exporter.get_finished_spans() assert [s.name for s in kept] == ["chat gpt-4", "POST /v1/chat/completions"] - assert all(s.context.trace_id == 2 for s in kept), "the second request, whose draw of 0.1 passed" + assert all(s.context.trace_id == KEPT_AT_HALF for s in kept) def test_a_root_that_never_ends_holds_the_tree_only_up_to_the_bound(self): dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.0) - destinations = (arize_destination(arize_success_sampling_rate="0.5"),) + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="1.0"),) def run(): set_request_destinations(destinations) @@ -1397,8 +1675,8 @@ class TestTenantSampling: def test_a_flood_of_waiting_trees_decides_the_oldest_on_what_it_has(self): dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.0) - destinations = (arize_destination(arize_success_sampling_rate="0.5"),) + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="1.0"),) def run(): set_request_destinations(destinations) @@ -1413,21 +1691,21 @@ class TestTenantSampling: assert [s.name for s in dest_exporter.get_finished_spans()] == ["chat 0"] - def test_a_flood_of_open_traces_forgets_the_oldest_and_decides_it_as_its_spans_end(self): - """The fan-out counts open spans for a bounded number of traces, a bound far above - what one process keeps in flight, since every trace of the provider is counted, not - only the sampled ones. A trace pushed out of that count is decided on what it has as + def test_a_flood_of_open_requests_forgets_the_oldest_and_decides_it_as_its_spans_end(self): + """The fan-out counts open spans for a bounded number of requests, a bound far above + what one process keeps in flight, since every request of the provider is counted, not + only the sampled ones. A request pushed out of that count is decided on what it has as its next span ends, root still open, rather than held until a bound gets to it.""" dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.0) - destinations = (arize_destination(arize_success_sampling_rate="0.5"),) + provider: Final = self._fan_out(dest_exporter) + destinations: Final = (arize_destination(arize_success_sampling_rate="1.0"),) def run(): set_request_destinations(destinations) tracer = get_tracer(provider, "litellm") root = tracer.start_span("POST /v1/chat/completions", context=trace_api.Context()) tracer.start_span("auth /v1/chat/completions", context=trace_api.set_span_in_context(root)).end() - for index in range(otel_providers._MAX_OPEN_TRACES): + for index in range(otel_providers._MAX_OPEN_REQUESTS): tracer.start_span(f"POST {index}", context=trace_api.Context()) assert dest_exporter.get_finished_spans() == (), "still held before the count is forgotten" tracer.start_span("chat gpt-4", context=trace_api.set_span_in_context(root)).end() @@ -1443,14 +1721,12 @@ class TestTenantSampling: def test_shutdown_decides_what_is_still_held(self): dest_exporter = InMemorySpanExporter() - fan_out = TenantFanOutSpanProcessor( - processor_factory=lambda _d: SimpleSpanProcessor(dest_exporter), sampling_draw=lambda: 0.0 - ) + fan_out: Final = TenantFanOutSpanProcessor(processor_factory=lambda _d: SimpleSpanProcessor(dest_exporter)) provider = TracerProvider() provider.add_span_processor(fan_out) def run(): - set_request_destinations((arize_destination(arize_success_sampling_rate="0.5"),)) + set_request_destinations((arize_destination(arize_success_sampling_rate="1.0"),)) tracer = get_tracer(provider, "litellm") with tracer.start_as_current_span("POST /v1/chat/completions"): with tracer.start_as_current_span("chat gpt-4"): @@ -1476,14 +1752,12 @@ class TestTenantSampling: straggler.end() super().on_end(span) - fan_out = TenantFanOutSpanProcessor( - processor_factory=lambda _d: _EndsAStragglerOnExport(dest_exporter), sampling_draw=lambda: 0.0 - ) + fan_out: Final = TenantFanOutSpanProcessor(processor_factory=lambda _d: _EndsAStragglerOnExport(dest_exporter)) provider = TracerProvider() provider.add_span_processor(fan_out) def run(): - set_request_destinations((arize_destination(arize_success_sampling_rate="0.5"),)) + set_request_destinations((arize_destination(arize_success_sampling_rate="1.0"),)) tracer = get_tracer(provider, "litellm") root = tracer.start_span("POST /v1/chat/completions", context=trace_api.Context()) tracer.start_span("chat gpt-4", context=trace_api.set_span_in_context(root)).end() @@ -1495,20 +1769,20 @@ class TestTenantSampling: assert {s.name for s in dest_exporter.get_finished_spans()} == {"chat gpt-4", "embed ada"} - def test_a_zero_rate_drops_even_a_draw_of_zero(self): + def test_a_zero_rate_drops_a_trace_a_half_rate_keeps(self): dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.0) + provider: Final = self._fan_out(dest_exporter, trace_id=KEPT_AT_HALF) self._run(provider, (arize_destination(arize_success_sampling_rate="0.0"),)) assert dest_exporter.get_finished_spans() == () - def test_the_draw_is_compared_to_the_rate_the_way_the_legacy_callback_compares_it(self): + def test_the_trace_id_is_read_against_the_rate(self): dropped_exporter, kept_exporter = InMemorySpanExporter(), InMemorySpanExporter() - destinations = (arize_destination(arize_success_sampling_rate="0.2"),) + destinations: Final = (arize_destination(arize_success_sampling_rate="0.5"),) - self._run(self._fan_out(dropped_exporter, draw=lambda: 0.3), destinations) - self._run(self._fan_out(kept_exporter, draw=lambda: 0.2), destinations) + self._run(self._fan_out(dropped_exporter, trace_id=DROPPED_AT_HALF), destinations) + self._run(self._fan_out(kept_exporter, trace_id=KEPT_AT_HALF), destinations) assert dropped_exporter.get_finished_spans() == () assert {s.name for s in kept_exporter.get_finished_spans()} == self.NAMES @@ -1527,6 +1801,39 @@ class TestTenantSampling: assert all(names == self.NAMES for names in by_trace.values()) assert 0 < len(by_trace) < 64 + @pytest.mark.parametrize( + ("rates", "failed"), + [({"arize_success_sampling_rate": "0.5"}, False), ({"arize_error_sampling_rate": "0.5"}, True)], + ids=["success-rate", "error-rate"], + ) + def test_two_processes_serving_one_trace_reach_the_same_verdict(self, rates: Mapping[str, str], failed: bool): + """Two pods behind a load balancer each serve one call of a trace the caller + continued with its ``traceparent``: the team's space gets both calls or neither, + and a rate of 0.5 over UUID-shaped trace ids keeps some traces and drops others.""" + exporters: Final = (InMemorySpanExporter(), InMemorySpanExporter()) + pods: Final = tuple(self._fan_out(exporter) for exporter in exporters) + destinations: Final = (arize_destination(**rates),) + trace_ids: Final = tuple(uuid.uuid5(uuid.NAMESPACE_URL, f"pod/{index}").int for index in range(32)) + + def run(provider: TracerProvider): + set_request_destinations(destinations) + tracer: Final = get_tracer(provider, "litellm") + for trace_id in trace_ids: + with tracer.start_as_current_span("POST /v1/chat/completions", context=caller_context(trace_id)): + with tracer.start_as_current_span("chat gpt-4") as span: + if failed: + span.set_status(Status(StatusCode.ERROR, "upstream 500")) + + for provider in pods: + in_fresh_context(run, provider) + + first, second = ( + {s.context.trace_id for s in exporter.get_finished_spans() if s.context is not None} + for exporter in exporters + ) + assert first == second + assert 0 < len(first) < len(trace_ids) + def test_in_additive_mode_the_operators_own_copy_is_not_sampled(self, monkeypatch): """The rates are the team's setting for the team's space; the operator's backbone keeps every span.""" @@ -1547,7 +1854,7 @@ class TestTenantSampling: assert {s.name for s in dest_exporter.get_finished_spans()} == self.NAMES - def test_two_views_of_one_exporter_each_draw_at_their_own_rate(self): + def test_two_views_of_one_exporter_each_answer_to_their_own_rate(self): """Two destinations to the same space with different rates share an exporter, not a verdict: the 1.0 view gets the tree once and the 0.0 view adds nothing.""" dest_exporter = InMemorySpanExporter() @@ -1582,7 +1889,7 @@ class TestTenantSampling: @pytest.mark.parametrize("bad", ["abc", "-0.1", "1.5", "nan"]) def test_an_unusable_rate_exports_rather_than_dropping(self, bad): dest_exporter = InMemorySpanExporter() - provider = self._fan_out(dest_exporter, draw=lambda: 0.99) + provider: Final = self._fan_out(dest_exporter, trace_id=DROPPED_AT_HALF) self._run(provider, (arize_destination(arize_success_sampling_rate=bad),)) From 1ecc782986162c8359d38eb6b22a213fd7c8a0e0 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 14:49:50 -0400 Subject: [PATCH 09/73] fix(vertex_ai): reuse rerank credential cache (#48430) --- litellm/utils.py | 7 +- .../test_vertex_ai_rerank_transformation.py | 86 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/litellm/utils.py b/litellm/utils.py index e2820f9c682..4315ae426cf 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -9571,7 +9571,7 @@ class ProviderConfigManager: return get_nvidia_nim_rerank_config(model) elif litellm.LlmProviders.VERTEX_AI == provider: - return litellm.VertexAIRerankConfig() + return ProviderConfigManager._get_vertex_ai_rerank_config_cached() elif litellm.LlmProviders.FIREWORKS_AI == provider: return litellm.FireworksAIRerankConfig() elif litellm.LlmProviders.VOYAGE == provider: @@ -9590,6 +9590,11 @@ class ProviderConfigManager: return get_dashscope_family_rerank_config(provider.value) return litellm.CohereRerankConfig() + @staticmethod + @lru_cache(maxsize=1) + def _get_vertex_ai_rerank_config_cached() -> BaseRerankConfig: + return litellm.VertexAIRerankConfig() + @staticmethod def get_provider_decisions_config(model: str, provider: LlmProviders) -> BaseDecisionsConfig | None: if provider == LlmProviders.PERPLEXITY: diff --git a/tests/unit/llms/vertex_ai/rerank/test_vertex_ai_rerank_transformation.py b/tests/unit/llms/vertex_ai/rerank/test_vertex_ai_rerank_transformation.py index 630b2e1eb34..8abd012f9d8 100644 --- a/tests/unit/llms/vertex_ai/rerank/test_vertex_ai_rerank_transformation.py +++ b/tests/unit/llms/vertex_ai/rerank/test_vertex_ai_rerank_transformation.py @@ -5,13 +5,19 @@ Based on the test patterns from other rerank providers and the current Vertex AI import json import os +from datetime import datetime, timedelta +from typing import Final from unittest.mock import MagicMock, patch import httpx import pytest +import responses +import respx +import litellm from litellm.llms.vertex_ai.rerank.transformation import VertexAIRerankConfig from litellm.types.rerank import RerankResponse +from litellm.utils import ProviderConfigManager class TestVertexAIRerankTransform: @@ -646,3 +652,83 @@ class TestVertexAIRerankTransform: second_call = mock_ensure_access_token.call_args_list[1] assert first_call.kwargs["credentials"] == "path/to/credentials.json" assert second_call.kwargs["credentials"] == "path/to/credentials.json" + + +@pytest.fixture +def clear_vertex_rerank_cache(request: pytest.FixtureRequest) -> None: + ProviderConfigManager._get_vertex_ai_rerank_config_cached.cache_clear() + request.addfinalizer( + ProviderConfigManager._get_vertex_ai_rerank_config_cached.cache_clear + ) + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("clear_vertex_rerank_cache") +@pytest.mark.parametrize("is_async", [False, True], ids=["sync", "async"]) +async def test_rerank_reuses_scoped_credentials_and_refreshes_expired_tokens( + is_async: bool, +) -> None: + now: Final = datetime(2025, 1, 1) + credentials: Final = tuple( + json.dumps( + { + "type": "authorized_user", + "client_id": identity, + "client_secret": "synthetic-client-secret", + "refresh_token": f"synthetic-refresh-{identity}", + } + ) + for identity in ("first-principal", "second-principal") + ) + with ( + patch("litellm.disable_aiohttp_transport", True), + patch("google.auth._helpers.utcnow", return_value=now) as clock, + responses.RequestsMock(assert_all_requests_are_fired=False) as token_http, + respx.mock as rank_http, + ): + for token in ("initial", "other-credential", "other-project", "renewed"): + token_http.post( + "https://oauth2.googleapis.com/token", + json={"access_token": token, "expires_in": 3600, "token_type": "Bearer"}, + ) + rank_http.post( + url__regex=r"https://discoveryengine\.googleapis\.com/v1/projects/[^/]+/" + r"locations/global/rankingConfigs/default_ranking_config:rank" + ).respond(200, json={"records": [{"id": "0", "score": 0.9}]}) + + async def rank( + credential: str, project: str, expected_token: str, refresh_count: int + ) -> None: + params: Final = { + "model": "vertex_ai/semantic-ranker-default@latest", + "query": f"Query for {expected_token}", + "documents": ["A relevant document"], + "vertex_credentials": credential, + "vertex_project": project, + "caching": False, + "num_retries": 0, + } + result: Final = ( + await litellm.arerank(**params) + if is_async + else litellm.rerank(**params) + ) + assert isinstance(result, RerankResponse) + assert result.results[0]["index"] == 0 + assert len(token_http.calls) == refresh_count + request: Final = rank_http.calls.last.request + assert request.headers["Authorization"] == f"Bearer {expected_token}" + assert request.headers["X-Goog-User-Project"] == project + assert str(request.url) == ( + f"https://discoveryengine.googleapis.com/v1/projects/{project}/" + "locations/global/rankingConfigs/default_ranking_config:rank" + ) + assert json.loads(request.content)["query"] == params["query"] + + await rank(credentials[0], "first-project", "initial", 1) + await rank(credentials[0], "first-project", "initial", 1) + await rank(credentials[1], "first-project", "other-credential", 2) + await rank(credentials[0], "second-project", "other-project", 3) + await rank(credentials[0], "first-project", "initial", 3) + clock.return_value = now + timedelta(hours=2) + await rank(credentials[0], "first-project", "renewed", 4) From 85871f791c12ca381b53dad8cbb7c8a47076ef4c Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 14:50:31 -0400 Subject: [PATCH 10/73] fix(ui): render string inputs in general settings (#48431) --- .../general_settings.integration.test.tsx | 36 +++++++++++++++++++ .../_components/general_settings.tsx | 9 +++++ 2 files changed, 45 insertions(+) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx index 35434830d98..d7dfb8151fc 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx @@ -80,6 +80,42 @@ describe("GeneralSettings General tab", () => { vi.mocked(deleteConfigFieldSetting).mockClear(); }); + it.each([ + ["maximum_spend_logs_retention_period", null, "7d"], + ["maximum_health_check_retention_period", "30d", "14d"], + ["maximum_daily_tag_spend_retention_period", undefined, "90d"], + ["maximum_spend_logs_cleanup_run_budget", "5m", "10m"], + ["maximum_spend_logs_cleanup_batch_timeout", "", "45s"], + ["mcp_client_id_header", "x-client-id", "X-MCP-Client.ID"], + ])("displays, updates and resets the String setting %s", async (fieldName, initialValue, updatedValue) => { + vi.mocked(getGeneralSettingsCall).mockResolvedValue([ + ...SETTINGS_FIXTURE, + { + field_name: fieldName, + field_type: "String", + field_value: initialValue, + field_description: "string setting", + stored_in_db: initialValue != null, + field_default_value: null, + }, + ]); + const user = userEvent.setup(); + renderWithProviders(); + await user.click(screen.getByRole("tab", { name: "General" })); + const input = await screen.findByRole("textbox", { name: fieldName }); + expect(input).toHaveValue(initialValue ?? ""); + fireEvent.change(input, { target: { value: updatedValue } }); + const row = screen.getByRole("row", { name: new RegExp(fieldName) }); + await user.click(within(row).getByRole("button", { name: "Update" })); + expect(vi.mocked(updateConfigFieldSetting).mock.calls).toEqual([["token", fieldName, updatedValue]]); + expect(within(row).getByText("In DB")).toBeInTheDocument(); + const resetIcon = row.querySelectorAll("td")[3].querySelector("svg"); + expect(resetIcon).not.toBeNull(); + await user.click(resetIcon as unknown as Element); + expect(vi.mocked(deleteConfigFieldSetting).mock.calls).toEqual([["token", fieldName]]); + expect(input).toHaveValue(""); + }); + it("updates max_ui_session_budget with its own value, not the value at its filtered index", async () => { const user = userEvent.setup(); renderWithProviders(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx index 5d28adb7ef3..67712d78a13 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx @@ -68,6 +68,15 @@ const SettingValueEditor: React.FC<{ /> ); } + if (setting.field_type === "String") { + return ( + onChange(setting.field_name, event.target.value)} + /> + ); + } if (setting.field_type === "Integer") { return ( Date: Mon, 26 Oct 2026 12:07:58 -0700 Subject: [PATCH 11/73] fix(guardrails): exclude litellm_metadata from pass-through guardrail scan text (#48276) * fix(guardrails): exclude litellm_metadata from pass-through guardrail scan text The pass-through guardrail translation json-dumps the request body as the scan text when no request_fields are configured. The unified guardrail hook injects litellm_metadata into that body first, so the dump carried the caller's key metadata. Exclude it alongside metadata and litellm_logging_obj; vendors still receive it through request_data. * test(guardrails): type the pass-through scan-text tests per AGENTS.md --- .../guardrail_translation/handler.py | 2 +- .../test_passthrough_guardrail_scan_text.py | 153 ++++++++++++++++++ .../guardrail_translation/test_handler.py | 16 ++ .../test_unified_guardrail.py | 33 ++++ 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 tests/integration/observability/test_passthrough_guardrail_scan_text.py diff --git a/litellm/llms/pass_through/guardrail_translation/handler.py b/litellm/llms/pass_through/guardrail_translation/handler.py index 3ce8d7ddad5..dea8bcb73a4 100644 --- a/litellm/llms/pass_through/guardrail_translation/handler.py +++ b/litellm/llms/pass_through/guardrail_translation/handler.py @@ -90,7 +90,7 @@ class PassThroughEndpointHandler(BaseTranslation): payload_to_check: Final = { k: v for k, v in without_server_streaming_classification(data).items() - if not k.startswith("_") and k not in ("metadata", "litellm_logging_obj") + if not k.startswith("_") and k not in ("metadata", "litellm_metadata", "litellm_logging_obj") } verbose_proxy_logger.debug("PassThroughEndpointHandler: Using full payload for guardrail") return safe_dumps(payload_to_check) diff --git a/tests/integration/observability/test_passthrough_guardrail_scan_text.py b/tests/integration/observability/test_passthrough_guardrail_scan_text.py new file mode 100644 index 00000000000..7437f696d4c --- /dev/null +++ b/tests/integration/observability/test_passthrough_guardrail_scan_text.py @@ -0,0 +1,153 @@ +import json +import uuid +from collections.abc import Iterator, Mapping +from contextlib import ExitStack +from dataclasses import dataclass +from pathlib import Path +from types import MappingProxyType +from typing import Final + +import pytest +import yaml +from integration._support.anthropic_sse import message_json +from integration._support.client import Gateway +from integration._support.openai_wire import chat_reply +from integration._support.process import OwnedProxy, owned_proxy_process +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue, TypeAdapter + +GENERIC: Final = "passthrough-scan-generic" +AIRS: Final = "passthrough-scan-airs" +OPENAI_ROUTE: Final = "/openai/chat/completions" +ANTHROPIC_ROUTE: Final = "/anthropic/v1/messages" +ROUTE_BODIES: Final[Mapping[str, dict[str, JsonValue]]] = MappingProxyType( + { + OPENAI_ROUTE: {"model": "gpt-5.6", "messages": [{"role": "user", "content": "Say hi in one word"}]}, + ANTHROPIC_ROUTE: { + "model": "claude-sonnet-5-5", + "max_tokens": 16, + "messages": [{"role": "user", "content": "Say hi in one word"}], + }, + } +) +_CONFIG: Final = Path(__file__).resolve().parents[1] / "proxy_config.yaml" +_JSON: Final = TypeAdapter(dict[str, JsonValue]) + + +def _generic_verdict(_request: Request) -> Reply: + return Reply(body=json.dumps({"action": "NONE"}).encode()) + + +def _airs_verdict(_request: Request) -> Reply: + return Reply(body=json.dumps({"action": "allow", "category": "benign", "scan_id": "s", "report_id": "r"}).encode()) + + +def _openai_reply(_request: Request) -> Reply: + return chat_reply("chatcmpl-passthrough-scan", "gpt-5.6", "Hi", stream=False) + + +def _anthropic_reply(_request: Request) -> Reply: + return Reply(body=message_json("msg_passthrough_scan", "claude-sonnet-5-5", "Hi")) + + +def _parsed_texts(texts: JsonValue, vendor_body: dict[str, JsonValue]) -> list[JsonValue]: + assert isinstance(texts, list), vendor_body + return [json.loads(text) if isinstance(text, str) else text for text in texts] + + +def _scanned_payloads(guardrail: str, vendor_body: dict[str, JsonValue]) -> list[JsonValue]: + if guardrail == GENERIC: + return _parsed_texts(vendor_body["texts"], vendor_body) + contents: Final = vendor_body["contents"] + assert isinstance(contents, list), vendor_body + return _parsed_texts([content.get("prompt") for content in contents if isinstance(content, dict)], vendor_body) + + +@dataclass(frozen=True, slots=True) +class PassThroughRig: + owned: OwnedProxy + vendors: Mapping[str, Wire] + upstreams: Mapping[str, Wire] + + +@pytest.fixture(scope="module") +def rig(session_gateway: Gateway, tmp_path_factory: pytest.TempPathFactory) -> Iterator[PassThroughRig]: + directory: Final = tmp_path_factory.mktemp("passthrough-guardrail-scan") + with ExitStack() as stack: + vendors: Final = MappingProxyType( + { + GENERIC: stack.enter_context(wire_server(_generic_verdict)), + AIRS: stack.enter_context(wire_server(_airs_verdict)), + } + ) + upstreams: Final = MappingProxyType( + { + OPENAI_ROUTE: stack.enter_context(wire_server(_openai_reply)), + ANTHROPIC_ROUTE: stack.enter_context(wire_server(_anthropic_reply)), + } + ) + config: Final = _JSON.validate_python(yaml.safe_load(_CONFIG.read_text())) + path: Final = directory / "passthrough-guardrail-scan.yaml" + path.write_text( + yaml.safe_dump( + { + **config, + "guardrails": [ + { + "guardrail_name": GENERIC, + "litellm_params": { + "guardrail": "generic_guardrail_api", + "mode": "pre_call", + "default_on": False, + "api_base": vendors[GENERIC].url, + "api_key": "synthetic-guardrail-key", + }, + }, + { + "guardrail_name": AIRS, + "litellm_params": { + "guardrail": "panw_prisma_airs", + "mode": "pre_call", + "default_on": False, + "api_base": vendors[AIRS].url, + "api_key": "synthetic-airs-key", + "profile_name": "integration", + }, + }, + ], + } + ) + ) + environment: Final = { + "OPENAI_API_BASE": upstreams[OPENAI_ROUTE].url, + "OPENAI_API_KEY": "synthetic-openai-key", + "ANTHROPIC_API_BASE": upstreams[ANTHROPIC_ROUTE].url, + "ANTHROPIC_API_KEY": "synthetic-anthropic-key", + } + owned: Final = stack.enter_context(owned_proxy_process(session_gateway, directory, environment, config=path)) + yield PassThroughRig(owned, vendors, upstreams) + + +@pytest.mark.parametrize("guardrail", [GENERIC, AIRS]) +@pytest.mark.parametrize("route", [OPENAI_ROUTE, ANTHROPIC_ROUTE]) +def test_pass_through_guardrail_scans_the_client_body_without_the_callers_key_metadata( + rig: PassThroughRig, route: str, guardrail: str +) -> None: + gateway: Final = rig.owned.gateway + with gateway.scenario() as scenario: + team: Final = scenario.team() + user: Final = scenario.user( + user_email=f"passthrough-scan-{uuid.uuid4().hex}@example.com", user_role="internal_user", teams=[team] + ) + key: Final = scenario.key(user_id=user, team_id=team) + body: Final = {**ROUTE_BODIES[route], "guardrails": [guardrail]} + + response: Final = gateway.request("POST", route, body, key=key) + + assert response.status_code == 200, response.text + scanned: Final = [ + _scanned_payloads(guardrail, _JSON.validate_json(request.body)) + for request in rig.vendors[guardrail].drain() + ] + assert scanned == [[body]], scanned + assert len(rig.upstreams[route].drain()) == 1 diff --git a/tests/unit/llms/pass_through/guardrail_translation/test_handler.py b/tests/unit/llms/pass_through/guardrail_translation/test_handler.py index 0dba0d7b90f..7cbabb95931 100644 --- a/tests/unit/llms/pass_through/guardrail_translation/test_handler.py +++ b/tests/unit/llms/pass_through/guardrail_translation/test_handler.py @@ -19,6 +19,22 @@ def test_full_payload_guardrail_text_excludes_the_server_streaming_marker(): assert json.loads(text) == body, text +def test_full_payload_guardrail_text_excludes_the_injected_litellm_metadata(): + body: Final = {"model": "m", "messages": [{"role": "user", "content": "hi"}], "guardrails": ["g"]} + litellm_metadata: Final = { + "user_api_key_token": "hashed-key", + "user_api_key_user_email": "someone@example.com", + "user_api_key_user_role": "proxy_admin", + } + + text: Final = PassThroughEndpointHandler()._extract_text_for_guardrail( + {**body, "litellm_metadata": litellm_metadata}, + None, + ) + + assert json.loads(text) == body, text + + @pytest.mark.parametrize( "marker", [ diff --git a/tests/unit/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py b/tests/unit/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py index 3efbe6475b2..a132ce8eada 100644 --- a/tests/unit/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py +++ b/tests/unit/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py @@ -3,6 +3,7 @@ import asyncio import contextlib import io +import json import logging from collections.abc import AsyncGenerator, AsyncIterable, AsyncIterator from types import SimpleNamespace @@ -35,6 +36,7 @@ from litellm.llms.openai.chat.guardrail_translation.handler import ( from litellm.llms.openai.responses.guardrail_translation.handler import ( OpenAIResponsesHandler, ) +from litellm.llms.pass_through.guardrail_translation.handler import PassThroughEndpointHandler from litellm.proxy._experimental.mcp_server.guardrail_translation.handler import ( MCPGuardrailTranslationHandler, ) @@ -3053,3 +3055,34 @@ async def test_pre_call_hook_skips_attachment_scan_when_guardrail_has_none(): ) assert len(guardrail.apply_calls) == 1 + + +@pytest.mark.parametrize("hook", ["async_pre_call_hook", "async_moderation_hook"]) +@pytest.mark.asyncio +async def test_pass_through_guardrail_text_is_the_client_body_without_the_injected_key_metadata( + monkeypatch: pytest.MonkeyPatch, hook: str +) -> None: + _patch_translation_mappings(monkeypatch, {CallTypes.pass_through: PassThroughEndpointHandler}) + guardrail: Final = RecordingGuardrail() + body: Final = { + "model": "gpt-4o", + "messages": [{"role": "user", "content": "Say hi in one word"}], + "guardrails": ["recording-guardrail"], + } + data: Final = {**body, "guardrail_to_apply": guardrail} + user_api_key_dict: Final = UserAPIKeyAuth( + token="hashed-key", user_email="someone@example.com", team_id="team-1", user_role="proxy_admin" + ) + + if hook == "async_pre_call_hook": + await UnifiedLLMGuardrails().async_pre_call_hook( + user_api_key_dict=user_api_key_dict, cache=DualCache(), data=data, call_type=CallTypes.pass_through.value + ) + else: + await UnifiedLLMGuardrails().async_moderation_hook( + data=data, user_api_key_dict=user_api_key_dict, call_type=CallTypes.pass_through.value + ) + + texts: Final = [call["inputs"]["texts"] for call in guardrail.apply_calls] + assert [[json.loads(text) for text in call_texts] for call_texts in texts] == [[body]], texts + assert data["litellm_metadata"]["user_api_key_user_email"] == "someone@example.com", data["litellm_metadata"] From dab39d7402cb1e772045c5a702abe74f69f66970 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 12:09:14 -0700 Subject: [PATCH 12/73] fix(azure): set azure_ai/Microsoft-Decision-1 max_input_tokens to 32768 (#48434) Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/model_prices_and_context_window_backup.json | 3 ++- model_prices_and_context_window.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 0a85d8a00cd..af2b4655d6f 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -76439,9 +76439,10 @@ "azure_ai/Microsoft-Decision-1": { "input_cost_per_token": 4.2e-08, "litellm_provider": "azure_ai", + "max_input_tokens": 32768, "mode": "evaluation", "output_cost_per_token": 0.0, - "source": "https://prices.azure.com/api/retail/prices?$filter=productName eq 'Microsoft Decision Models'" + "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure" }, "gpt-rosalind-discovery": { "cache_read_input_token_cost": 5e-07, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 0a85d8a00cd..af2b4655d6f 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -76439,9 +76439,10 @@ "azure_ai/Microsoft-Decision-1": { "input_cost_per_token": 4.2e-08, "litellm_provider": "azure_ai", + "max_input_tokens": 32768, "mode": "evaluation", "output_cost_per_token": 0.0, - "source": "https://prices.azure.com/api/retail/prices?$filter=productName eq 'Microsoft Decision Models'" + "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure" }, "gpt-rosalind-discovery": { "cache_read_input_token_cost": 5e-07, From c69e82f538690c40f16e7aa6519c7e05579a55ab Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 12:18:19 -0700 Subject: [PATCH 13/73] chore(cost-map): add openai batch and ultrafast 12-hour cache write rates from the pricing page (#48433) Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...model_prices_and_context_window_backup.json | 18 ++++++++++++++++++ model_prices_and_context_window.json | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index af2b4655d6f..6ec1bd29beb 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -36840,10 +36840,14 @@ "cache_creation_input_token_cost": 1.25e-05, "cache_creation_input_token_cost_above_12hr": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 0.0001, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_ultrafast": 0.0003, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-05, "cache_creation_input_token_cost_above_12hr_flex": 1.25e-05, "cache_creation_input_token_cost_above_12hr_priority": 5e-05, + "cache_creation_input_token_cost_above_12hr_ultrafast": 0.00015, "cache_creation_input_token_cost_above_272k_tokens": 2.5e-05, "cache_creation_input_token_cost_above_272k_tokens_flex": 1.25e-05, "cache_creation_input_token_cost_above_272k_tokens_priority": 5e-05, @@ -36931,8 +36935,10 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, @@ -37014,8 +37020,10 @@ "cache_creation_input_token_cost": 1.25e-07, "cache_creation_input_token_cost_above_12hr": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-07, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 1e-06, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-07, "cache_creation_input_token_cost_above_12hr_flex": 1.25e-07, "cache_creation_input_token_cost_above_12hr_priority": 5e-07, "cache_creation_input_token_cost_decisions": 1.25e-07, @@ -37186,8 +37194,10 @@ "cache_creation_input_token_cost": 5e-06, "cache_creation_input_token_cost_above_12hr": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 2e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 4e-05, + "cache_creation_input_token_cost_above_12hr_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_priority": 2e-05, "cache_creation_input_token_cost_above_272k_tokens": 1e-05, @@ -37268,8 +37278,10 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, @@ -37349,8 +37361,10 @@ "cache_creation_input_token_cost": 2.5e-07, "cache_creation_input_token_cost_above_12hr": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-06, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-07, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-07, "cache_creation_input_token_cost_above_12hr_priority": 1e-06, "cache_creation_input_token_cost_above_272k_tokens": 5e-07, @@ -75046,10 +75060,14 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_ultrafast": 6e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, + "cache_creation_input_token_cost_above_12hr_ultrafast": 3e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, "cache_creation_input_token_cost_above_272k_tokens_batches": 2.5e-06, "cache_creation_input_token_cost_above_272k_tokens_flex": 2.5e-06, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index af2b4655d6f..6ec1bd29beb 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -36840,10 +36840,14 @@ "cache_creation_input_token_cost": 1.25e-05, "cache_creation_input_token_cost_above_12hr": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 0.0001, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_ultrafast": 0.0003, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-05, "cache_creation_input_token_cost_above_12hr_flex": 1.25e-05, "cache_creation_input_token_cost_above_12hr_priority": 5e-05, + "cache_creation_input_token_cost_above_12hr_ultrafast": 0.00015, "cache_creation_input_token_cost_above_272k_tokens": 2.5e-05, "cache_creation_input_token_cost_above_272k_tokens_flex": 1.25e-05, "cache_creation_input_token_cost_above_272k_tokens_priority": 5e-05, @@ -36931,8 +36935,10 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, @@ -37014,8 +37020,10 @@ "cache_creation_input_token_cost": 1.25e-07, "cache_creation_input_token_cost_above_12hr": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 5e-07, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 2.5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 1e-06, + "cache_creation_input_token_cost_above_12hr_batches": 1.25e-07, "cache_creation_input_token_cost_above_12hr_flex": 1.25e-07, "cache_creation_input_token_cost_above_12hr_priority": 5e-07, "cache_creation_input_token_cost_decisions": 1.25e-07, @@ -37186,8 +37194,10 @@ "cache_creation_input_token_cost": 5e-06, "cache_creation_input_token_cost_above_12hr": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 2e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 1e-05, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 4e-05, + "cache_creation_input_token_cost_above_12hr_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_priority": 2e-05, "cache_creation_input_token_cost_above_272k_tokens": 1e-05, @@ -37268,8 +37278,10 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, @@ -37349,8 +37361,10 @@ "cache_creation_input_token_cost": 2.5e-07, "cache_creation_input_token_cost_above_12hr": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-06, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-07, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-06, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-07, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-07, "cache_creation_input_token_cost_above_12hr_priority": 1e-06, "cache_creation_input_token_cost_above_272k_tokens": 5e-07, @@ -75046,10 +75060,14 @@ "cache_creation_input_token_cost": 2.5e-06, "cache_creation_input_token_cost_above_12hr": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens": 1e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_batches": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_flex": 5e-06, "cache_creation_input_token_cost_above_12hr_above_272k_tokens_priority": 2e-05, + "cache_creation_input_token_cost_above_12hr_above_272k_tokens_ultrafast": 6e-05, + "cache_creation_input_token_cost_above_12hr_batches": 2.5e-06, "cache_creation_input_token_cost_above_12hr_flex": 2.5e-06, "cache_creation_input_token_cost_above_12hr_priority": 1e-05, + "cache_creation_input_token_cost_above_12hr_ultrafast": 3e-05, "cache_creation_input_token_cost_above_272k_tokens": 5e-06, "cache_creation_input_token_cost_above_272k_tokens_batches": 2.5e-06, "cache_creation_input_token_cost_above_272k_tokens_flex": 2.5e-06, From 52c11bf38393b81732b687a41fc3ebfe2740c4a1 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:39:48 +0000 Subject: [PATCH 14/73] feat(bedrock): add global. and us. openai.gpt-6.1-terra rows (#48441) * feat(bedrock): add global. and us. openai.gpt-6.1-terra rows * chore: rerun ci after flaky event loop timing test --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 78 +++++++++++++++++++ model_prices_and_context_window.json | 78 +++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 6ec1bd29beb..3bb8b9f8029 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -78773,5 +78773,83 @@ "supports_response_schema": true, "supports_vision": true, "supports_xhigh_reasoning_effort": true + }, + "global.openai.gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://developers.openai.com/api/docs/models/gpt-6.1-terra", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_bedrock_converse_tool_result_images": false, + "supports_bedrock_runtime_chat_completions_response_format": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true + }, + "us.openai.gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.375e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.75e-06, + "cache_read_input_token_cost": 5.5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5.5e-06, + "output_cost_per_token_above_272k_tokens": 8.25e-06, + "source": "https://developers.openai.com/api/docs/models/gpt-6.1-terra", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_bedrock_converse_tool_result_images": false, + "supports_bedrock_runtime_chat_completions_response_format": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 6ec1bd29beb..3bb8b9f8029 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -78773,5 +78773,83 @@ "supports_response_schema": true, "supports_vision": true, "supports_xhigh_reasoning_effort": true + }, + "global.openai.gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://developers.openai.com/api/docs/models/gpt-6.1-terra", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_bedrock_converse_tool_result_images": false, + "supports_bedrock_runtime_chat_completions_response_format": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true + }, + "us.openai.gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.375e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.75e-06, + "cache_read_input_token_cost": 5.5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5.5e-06, + "output_cost_per_token_above_272k_tokens": 8.25e-06, + "source": "https://developers.openai.com/api/docs/models/gpt-6.1-terra", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_bedrock_converse_tool_result_images": false, + "supports_bedrock_runtime_chat_completions_response_format": true, + "supports_function_calling": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": false, + "supports_none_reasoning_effort": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true } } From f96200bcea3a08adcd08ad4eb5ad169d226cdda5 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 15:56:13 -0400 Subject: [PATCH 15/73] fix(vertex_ai): bound shared credential cache retention (#48442) --- litellm/llms/vertex_ai/vertex_llm_base.py | 141 +++++++----------- .../llms/vertex_ai/test_vertex_llm_base.py | 125 +++++++++++++++- .../test_google_secret_manager.py | 3 +- 3 files changed, 182 insertions(+), 87 deletions(-) diff --git a/litellm/llms/vertex_ai/vertex_llm_base.py b/litellm/llms/vertex_ai/vertex_llm_base.py index 6cff9729189..0cdd92bb071 100644 --- a/litellm/llms/vertex_ai/vertex_llm_base.py +++ b/litellm/llms/vertex_ai/vertex_llm_base.py @@ -7,12 +7,14 @@ Handles Authentication and generating request urls for Vertex AI and Google AI S import asyncio import json import threading +from collections import OrderedDict from collections.abc import Mapping from typing import TYPE_CHECKING, Any, Final, Literal, Protocol from urllib.parse import urlparse import litellm from litellm._logging import verbose_logger +from litellm.constants import DEFAULT_MAX_LRU_CACHE_SIZE from litellm.litellm_core_utils.asyncify import asyncify from litellm.litellm_core_utils.url_utils import join_api_base from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler @@ -70,16 +72,20 @@ class _VertexCredentialsObject(Protocol): def refresh(self, request: object) -> None: ... +_VertexCredentialCacheKey = tuple[str | None, str | None] + + class VertexBase: def __init__(self) -> None: super().__init__() self.access_token: str | None = None self.refresh_token: str | None = None self._credentials: GoogleCredentialsObject | None = None - self._credentials_project_mapping: dict[ - tuple[VERTEX_CREDENTIALS_TYPES | None, str | None], + self._credentials_project_mapping: OrderedDict[ + _VertexCredentialCacheKey, tuple[_VertexCredentialsObject, str | None], - ] = {} + ] = OrderedDict() + self._credentials_cache_lock = threading.RLock() self.project_id: str | None = None self.async_handler: AsyncHTTPHandler | None = None # Per-credential-key asyncio.Lock for single-flight async refresh. @@ -91,10 +97,10 @@ class VertexBase: # each lock; the entry is pruned when the count reaches zero, so the # dict stays bounded even in long-running high-cardinality deployments # without depending on any private asyncio internals. - self._async_refresh_locks: dict[tuple, asyncio.Lock] = {} - self._async_refresh_lock_refcounts: dict[tuple, int] = {} + self._async_refresh_locks: dict[_VertexCredentialCacheKey, asyncio.Lock] = {} + self._async_refresh_lock_refcounts: dict[_VertexCredentialCacheKey, int] = {} # Tracks in-flight background refresh tasks to avoid duplicate refreshes. - self._background_refresh_tasks: dict[tuple, asyncio.Task] = {} + self._background_refresh_tasks: dict[_VertexCredentialCacheKey, asyncio.Task] = {} # Protects the sync get_access_token refresh path. # Use RLock so that the reauthentication retry path (which calls # back into get_access_token while still holding the lock) can @@ -378,7 +384,7 @@ class VertexBase: with self._sync_refresh_lock: credentials.refresh(Request()) - def _acquire_async_refresh_lock(self, credential_cache_key: tuple) -> asyncio.Lock: + def _acquire_async_refresh_lock(self, credential_cache_key: _VertexCredentialCacheKey) -> asyncio.Lock: """Increment the refcount and return the lock for ``credential_cache_key``. Every call must be paired with ``_release_async_refresh_lock`` once the @@ -391,7 +397,7 @@ class VertexBase: ) return lock - def _release_async_refresh_lock(self, credential_cache_key: tuple, lock: asyncio.Lock) -> None: + def _release_async_refresh_lock(self, credential_cache_key: _VertexCredentialCacheKey, lock: asyncio.Lock) -> None: """Decrement the refcount and drop the lock entry when it reaches zero. Must be called only after the caller has released ``lock`` (i.e. once @@ -409,7 +415,7 @@ class VertexBase: def _try_get_cached_token( self, - credential_cache_key: tuple, + credential_cache_key: _VertexCredentialCacheKey, project_id: str | None, ) -> tuple[str, str] | None: """ @@ -432,7 +438,7 @@ class VertexBase: def _try_get_usable_cached_token( self, - credential_cache_key: tuple, + credential_cache_key: _VertexCredentialCacheKey, project_id: str | None, ) -> tuple[str, str, "TokenState", _VertexCredentialsObject, str | None] | None: """ @@ -458,19 +464,37 @@ class VertexBase: return creds.token, resolved_project, token_state, creds, cached_project_id def _unpack_cached_credentials( - self, credential_cache_key: tuple + self, credential_cache_key: _VertexCredentialCacheKey ) -> tuple[_VertexCredentialsObject | None, str | None]: """ Return (credentials, project_id) from the cache, or (None, None) if not cached. Handles both tuple and legacy cache formats. """ - if credential_cache_key not in self._credentials_project_mapping: - return None, None - cached_entry: Final = self._credentials_project_mapping[credential_cache_key] + with self._credentials_cache_lock: + cached_entry: Final = self._credentials_project_mapping.get(credential_cache_key) + if cached_entry is None: + return None, None + self._credentials_project_mapping.move_to_end(credential_cache_key) if isinstance(cached_entry, tuple): return cached_entry return cached_entry, cached_entry.quota_project_id or getattr(cached_entry, "project_id", None) + def _cache_credentials( + self, + credential_cache_key: _VertexCredentialCacheKey, + credentials: _VertexCredentialsObject, + project_id: str | None, + *, + replace: bool = True, + ) -> None: + with self._credentials_cache_lock: + if not replace and credential_cache_key in self._credentials_project_mapping: + return + self._credentials_project_mapping[credential_cache_key] = (credentials, project_id) + self._credentials_project_mapping.move_to_end(credential_cache_key) + while len(self._credentials_project_mapping) > max(0, DEFAULT_MAX_LRU_CACHE_SIZE): + self._credentials_project_mapping.popitem(last=False) + def _get_token_state(self, credentials: _VertexCredentialsObject) -> "TokenState": """ Return the token state using google-auth's TokenState enum. @@ -494,7 +518,7 @@ class VertexBase: self, credentials: VERTEX_CREDENTIALS_TYPES | None, project_id: str | None, - credential_cache_key: tuple, + credential_cache_key: _VertexCredentialCacheKey, ) -> tuple[_VertexCredentialsObject, str | None]: """Load credentials via load_auth (in thread) and cache the result.""" try: @@ -507,17 +531,12 @@ class VertexBase: raise if _credentials is None: raise ValueError("Could not resolve credentials") - self._credentials_project_mapping[credential_cache_key] = ( - _credentials, - credential_project_id, - ) + self._cache_credentials(credential_cache_key, _credentials, credential_project_id) return _credentials, credential_project_id async def _background_refresh_credentials( self, credentials: _VertexCredentialsObject, - credential_cache_key: tuple, - credential_project_id: str | None, ) -> None: """ Refresh credentials in the background without blocking the calling request. @@ -528,25 +547,13 @@ class VertexBase: try: verbose_logger.debug("Background proactive credential refresh") await asyncify(self.refresh_auth)(credentials) - # Only update the cache if it still points at the credentials - # object we just refreshed. The per-key async lock is not held - # here, so a concurrent INVALID path may have already replaced - # this entry (e.g. via _handle_reauthentication_async, which - # creates a fresh credentials object). In that case our write - # would clobber the newer entry with a stale reference. - cached_creds, _ = self._unpack_cached_credentials(credential_cache_key) - if cached_creds is credentials: - self._credentials_project_mapping[credential_cache_key] = ( - credentials, - credential_project_id, - ) except Exception: verbose_logger.debug( "Background credential refresh failed, will retry on next request", exc_info=True, ) - async def _await_in_flight_background_refresh(self, credential_cache_key: tuple) -> None: + async def _await_in_flight_background_refresh(self, credential_cache_key: _VertexCredentialCacheKey) -> None: """Wait for an in-flight background refresh to finish, if any. google-auth's ``Credentials.refresh()`` is not safe to invoke @@ -568,8 +575,7 @@ class VertexBase: def _schedule_background_refresh( self, credentials: _VertexCredentialsObject, - credential_cache_key: tuple, - credential_project_id: str | None, + credential_cache_key: _VertexCredentialCacheKey, ) -> None: """Kick off a single background refresh for ``credential_cache_key``. @@ -581,9 +587,7 @@ class VertexBase: if existing is not None and not existing.done(): return self._background_refresh_tasks.pop(credential_cache_key, None) - task: Final = asyncio.create_task( - self._background_refresh_credentials(credentials, credential_cache_key, credential_project_id) - ) + task: Final = asyncio.create_task(self._background_refresh_credentials(credentials)) def _drop_background_refresh_task(_fut: asyncio.Future[None]) -> None: if self._background_refresh_tasks.get(credential_cache_key) is _fut: @@ -796,7 +800,7 @@ class VertexBase: self, credentials: VERTEX_CREDENTIALS_TYPES | None, project_id: str | None, - credential_cache_key: tuple, + credential_cache_key: _VertexCredentialCacheKey, error: Exception, ) -> tuple[str, str]: """ @@ -822,8 +826,8 @@ class VertexBase: ) # Clear the cached credentials - if credential_cache_key in self._credentials_project_mapping: - del self._credentials_project_mapping[credential_cache_key] + with self._credentials_cache_lock: + self._credentials_project_mapping.pop(credential_cache_key, None) # Retry once with _retry_reauth=True to prevent infinite recursion try: @@ -846,7 +850,7 @@ class VertexBase: self, credentials: VERTEX_CREDENTIALS_TYPES | None, project_id: str | None, - credential_cache_key: tuple, + credential_cache_key: _VertexCredentialCacheKey, error: Exception, ) -> tuple[str, str]: """ @@ -856,7 +860,8 @@ class VertexBase: "Handling async reauthentication for project_id: %s. Clearing cache and retrying once.", project_id ) - self._credentials_project_mapping.pop(credential_cache_key, None) + with self._credentials_cache_lock: + self._credentials_project_mapping.pop(credential_cache_key, None) try: ( @@ -875,10 +880,7 @@ class VertexBase: # references the OLD credentials object we just replaced, and # leaving it would force the next request to do a redundant # refresh/reauth before realizing the cached creds are stale. - self._credentials_project_mapping[resolved_cache_key] = ( - _credentials, - credential_project_id, - ) + self._cache_credentials(resolved_cache_key, _credentials, credential_project_id) if _credentials.token is None or not isinstance(_credentials.token, str): raise ValueError( @@ -924,21 +926,11 @@ class VertexBase: # Convert dict credentials to string for caching cache_credentials: Final = json.dumps(credentials) if isinstance(credentials, dict) else credentials credential_cache_key: Final = (cache_credentials, project_id) - _credentials: _VertexCredentialsObject | None = None - verbose_logger.debug("Checking cached credentials for project_id: %s", project_id) - if credential_cache_key in self._credentials_project_mapping: + _credentials, credential_project_id = self._unpack_cached_credentials(credential_cache_key) + if _credentials is not None: verbose_logger.debug("Cached credentials found for project_id: %s.", project_id) - # Retrieve both credentials and cached project_id - cached_entry: Final = self._credentials_project_mapping[credential_cache_key] - verbose_logger.debug("cached_entry: %s", cached_entry) - if isinstance(cached_entry, tuple): - _credentials, credential_project_id = cached_entry - else: - # Backward compatibility with old cache format - _credentials = cached_entry - credential_project_id = _credentials.quota_project_id or getattr(_credentials, "project_id", None) verbose_logger.debug( "Using cached credentials for project_id: %s", credential_project_id, @@ -963,10 +955,7 @@ class VertexBase: f"Could not resolve credentials - either dynamically or from environment, for project_id: {project_id}" ) # Cache the project_id and credentials from load_auth result (resolved project_id) - self._credentials_project_mapping[credential_cache_key] = ( - _credentials, - credential_project_id, - ) + self._cache_credentials(credential_cache_key, _credentials, credential_project_id) ## VALIDATE CREDENTIALS verbose_logger.debug("Validating credentials") @@ -974,11 +963,7 @@ class VertexBase: project_id = credential_project_id # Update cache with resolved project_id for future lookups resolved_cache_key: Final = (cache_credentials, project_id) - if resolved_cache_key not in self._credentials_project_mapping: - self._credentials_project_mapping[resolved_cache_key] = ( - _credentials, - credential_project_id, - ) + self._cache_credentials(resolved_cache_key, _credentials, credential_project_id, replace=False) # Check if credentials are None before accessing attributes if _credentials is None: @@ -991,10 +976,6 @@ class VertexBase: try: verbose_logger.debug("Credentials expired, refreshing") self.refresh_auth(_credentials) - self._credentials_project_mapping[credential_cache_key] = ( - _credentials, - credential_project_id, - ) except Exception as e: # if refresh fails, it's possible the user has re-authenticated via `gcloud auth application-default login` # in this case, we should try to reload the credentials by clearing the cache and retrying @@ -1043,9 +1024,9 @@ class VertexBase: # callers on the lock just to schedule that refresh. usable: Final = self._try_get_usable_cached_token(credential_cache_key, project_id) if usable is not None: - cached_token, resolved_project, token_state, creds, cached_project_id = usable + cached_token, resolved_project, token_state, creds, _cached_project_id = usable if token_state == TokenState.STALE: - self._schedule_background_refresh(creds, credential_cache_key, cached_project_id) + self._schedule_background_refresh(creds, credential_cache_key) return cached_token, resolved_project # === SLOW PATH (per-key lock) === @@ -1075,10 +1056,7 @@ class VertexBase: # reauth that only repopulated the unresolved key), which # would force the next request through an unnecessary # refresh/reauth cycle. - self._credentials_project_mapping[resolved_cache_key] = ( - _credentials, - credential_project_id, - ) + self._cache_credentials(resolved_cache_key, _credentials, credential_project_id) # Use google-auth's token_state to decide refresh strategy: # - STALE: token is usable but within REFRESH_THRESHOLD (3:45) of @@ -1098,7 +1076,6 @@ class VertexBase: self._schedule_background_refresh( _credentials, credential_cache_key, - credential_project_id, ) return current_token, project_id @@ -1117,10 +1094,6 @@ class VertexBase: try: verbose_logger.debug("Credentials expired, refreshing") await asyncify(self.refresh_auth)(_credentials) - self._credentials_project_mapping[credential_cache_key] = ( - _credentials, - credential_project_id, - ) except Exception as e: if "Reauthentication is needed" in str(e): verbose_logger.debug("Reauthentication needed, clearing cache and retrying") diff --git a/tests/unit/llms/vertex_ai/test_vertex_llm_base.py b/tests/unit/llms/vertex_ai/test_vertex_llm_base.py index dd2b86b6f81..fa332cef5d8 100644 --- a/tests/unit/llms/vertex_ai/test_vertex_llm_base.py +++ b/tests/unit/llms/vertex_ai/test_vertex_llm_base.py @@ -6,13 +6,17 @@ import os import pathlib import re import traceback -from typing import Final +from datetime import datetime, timedelta +from threading import Event +from typing import Final, Literal from unittest.mock import MagicMock, call, patch import google.auth.exceptions import pytest +import requests +import responses -from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH +from litellm.constants import DEFAULT_MAX_LRU_CACHE_SIZE, DEFAULT_MAX_RECURSE_DEPTH import litellm @@ -2589,3 +2593,120 @@ def test_get_access_token_reraises_malformed_service_account_credentials_without assert [record.getMessage().split(". Error: ")[0] for record in caplog.records] == [ "Failed to load vertex credentials. Check to see if credentials containing partial/invalid information" ] + + +def _authorized_user_cache_credentials(identity: str) -> str: + return json.dumps( + { + "type": "authorized_user", + "client_id": identity, + "client_secret": "synthetic-secret", + "refresh_token": identity, + "quota_project_id": identity, + } + ) + + +@pytest.mark.parametrize("is_async", [False, True], ids=["sync", "async"]) +@pytest.mark.parametrize("inferred_project", [False, True], ids=["explicit", "inferred"]) +@pytest.mark.asyncio +async def test_credential_cache_bounds_retention_and_reloads_least_recently_used( + is_async: bool, inferred_project: bool +) -> None: + vertex: Final = VertexBase() + limit: Final = max(0, DEFAULT_MAX_LRU_CACHE_SIZE) + with ( + patch("google.auth._helpers.utcnow", return_value=datetime(2025, 1, 1)), + responses.RequestsMock() as oauth, + ): + def token_response(request: requests.PreparedRequest) -> tuple[int, dict[str, str], str]: + return 200, {}, json.dumps({"access_token": f"token-{len(oauth.calls) + 1}", "expires_in": 3600}) + + oauth.add_callback(responses.POST, "https://oauth2.googleapis.com/token", callback=token_response) + + async def access(index: int, infer: bool = False) -> tuple[str, str]: + identity: Final = f"project-{index}" + credentials: Final = _authorized_user_cache_credentials(identity) + project: Final = None if infer else identity + result: Final = ( + await vertex.get_access_token_async(credentials, project) + if is_async + else vertex.get_access_token(credentials, project) + ) + assert result[1] == identity + assert len(vertex._credentials_project_mapping) <= limit + return result + + if limit == 0: + assert await access(0, inferred_project) == ("token-1", "project-0") + assert await access(0, inferred_project) == ("token-2", "project-0") + return + + for index in range(limit): + assert await access(index) == (f"token-{index + 1}", f"project-{index}") + assert await access(0) == ("token-1", "project-0") + assert len(oauth.calls) == limit + assert await access(limit, inferred_project) == (f"token-{limit + 1}", f"project-{limit}") + assert len(oauth.calls) == limit + 1 + if limit > (2 if inferred_project else 1): + assert await access(0) == ("token-1", "project-0") + assert len(oauth.calls) == limit + 1 + evicted: Final = 1 if limit > 1 else 0 + assert await access(evicted) == (f"token-{limit + 2}", f"project-{evicted}") + assert len(oauth.calls) == limit + 2 + + +@pytest.mark.parametrize("mode", ["sync", "async", "background"]) +@pytest.mark.skipif(DEFAULT_MAX_LRU_CACHE_SIZE < 2, reason="Eviction during refresh requires two retained entries") +@pytest.mark.asyncio +async def test_evicted_credentials_finish_refresh_without_being_readmitted( + mode: Literal["sync", "async", "background"], +) -> None: + vertex: Final = VertexBase() + limit: Final = DEFAULT_MAX_LRU_CACHE_SIZE + now: Final = datetime(2025, 1, 1) + refresh_started: Final = Event() + release_refresh: Final = Event() + donor: Final = _authorized_user_cache_credentials("donor") + target: Final = _authorized_user_cache_credentials("target") + with patch("google.auth._helpers.utcnow", return_value=now) as clock, responses.RequestsMock() as oauth: + def token_response(request: requests.PreparedRequest) -> tuple[int, dict[str, str], str]: + body: Final = request.body.decode() if isinstance(request.body, bytes) else request.body + assert isinstance(body, str) + is_target: Final = "refresh_token=target" in body + if is_target and len(oauth.calls) == limit: + refresh_started.set() + assert release_refresh.wait(5), "OAuth refresh was not released" + return 200, {}, json.dumps( + {"access_token": f"token-{len(oauth.calls) + 1}", "expires_in": 3600 if is_target else 86400} + ) + + oauth.add_callback(responses.POST, "https://oauth2.googleapis.com/token", callback=token_response) + assert vertex.get_access_token(donor, None) == ("token-1", "donor") + assert vertex.get_access_token(donor, None) == ("token-1", "donor") + for index in range(limit - 2): + assert vertex.get_access_token(donor, f"filler-{index}") == (f"token-{index + 2}", f"filler-{index}") + assert vertex.get_access_token(target, "target") == (f"token-{limit}", "target") + clock.return_value = now + timedelta(seconds=3550 if mode == "background" else 7200) + refresh: Final = asyncio.create_task( + asyncio.to_thread(vertex.get_access_token, target, "target") + if mode == "sync" + else vertex.get_access_token_async(target, "target") + ) + try: + assert await asyncio.to_thread(refresh_started.wait, 5), "OAuth refresh did not start" + for index in range(limit - 2): + assert vertex.get_access_token(donor, f"filler-{index}") == (f"token-{index + 2}", f"filler-{index}") + assert vertex.get_access_token(donor, None) == ("token-1", "donor") + assert len(vertex._credentials_project_mapping) == limit + assert (target, "target") not in vertex._credentials_project_mapping + finally: + release_refresh.set() + completed: Final = await refresh + await asyncio.gather(*tuple(vertex._background_refresh_tasks.values())) + expected_token: Final = f"token-{limit if mode == 'background' else limit + 1}" + assert completed == (expected_token, "target") + assert len(oauth.calls) == limit + 1 + assert (target, "target") not in vertex._credentials_project_mapping + assert vertex.get_access_token(target, "target") == (f"token-{limit + 2}", "target") + assert len(vertex._credentials_project_mapping) == limit diff --git a/tests/unit/secret_managers/test_google_secret_manager.py b/tests/unit/secret_managers/test_google_secret_manager.py index 2051be08f7c..7effde4fb30 100644 --- a/tests/unit/secret_managers/test_google_secret_manager.py +++ b/tests/unit/secret_managers/test_google_secret_manager.py @@ -1,6 +1,7 @@ import base64 import binascii import json +from collections import OrderedDict from dataclasses import dataclass from typing import Final @@ -120,7 +121,7 @@ def manager(monkeypatch: pytest.MonkeyPatch) -> GoogleSecretManager: monkeypatch.setattr(litellm, "secret_manager_client", None) monkeypatch.setattr(litellm, "_key_management_system", None) monkeypatch.setattr(litellm, "_key_management_settings", KeyManagementSettings()) - monkeypatch.setattr(litellm.vertex_chat_completion, "_credentials_project_mapping", {}) + monkeypatch.setattr(litellm.vertex_chat_completion, "_credentials_project_mapping", OrderedDict()) monkeypatch.setattr(google.auth, "default", _default_credentials) monkeypatch.setenv("GOOGLE_SECRET_MANAGER_PROJECT_ID", PROJECT_ID) monkeypatch.delenv("GOOGLE_SECRET_MANAGER_ALWAYS_READ_SECRET_MANAGER", raising=False) From 9cb102b72159d4809256d4f87b3a31b3551e3364 Mon Sep 17 00:00:00 2001 From: shrey-berri Date: Mon, 26 Oct 2026 13:41:15 -0700 Subject: [PATCH 16/73] chore(deps): remove expired boto3 botocore and grpcio overrides (#48447) --- pyproject.toml | 5 ----- uv.lock | 3 --- 2 files changed, 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b15eb042959..88bb0b13d91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -379,11 +379,6 @@ exclude-newer = "3 days" [tool.uv.exclude-newer-package] # TODO: ryan, remove after 2026-10-23, once certifi 2026.10.19 (drops the compromised eMudhra root) clears the 3-day window certifi = "2026-10-20T03:00:00Z" -# TODO(shrey): remove after 2026-10-25, once grpcio 1.84.1 (drops the compromised eMudhra roots from roots.pem) clears the 3-day window -grpcio = "2026-10-22T15:00:00Z" -# TODO(shrey): remove after 2026-10-24, once boto3/botocore 1.43.117 (drops the compromised eMudhra roots from cacert.pem) clear the 3-day window -boto3 = "2026-10-21T19:31:00Z" -botocore = "2026-10-21T19:31:00Z" # TODO: remove after 2026-10-25, once brotli 1.2.1 (CVE-2026-111497) clears the 3-day window brotli = "2026-10-22T14:01:00Z" # TODO: ryan, remove after 2026-10-24, once mlflow 3.17.2 (GHSA-wqmh-qccw-pqwf) clears the 3-day window diff --git a/uv.lock b/uv.lock index 60bcb367e0f..fc4155524d7 100644 --- a/uv.lock +++ b/uv.lock @@ -15,11 +15,8 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for exclude-newer-span = "P3D" [options.exclude-newer-package] -boto3 = "2026-10-21T19:31:00Z" -botocore = "2026-10-21T19:31:00Z" brotli = "2026-10-22T14:01:00Z" certifi = "2026-10-20T03:00:00Z" -grpcio = "2026-10-22T15:00:00Z" mlflow = "2026-10-21T03:00:00Z" mlflow-skinny = "2026-10-21T03:00:00Z" mlflow-tracing = "2026-10-21T03:00:00Z" From 768960610bc7e77e854bdb79e14a47268f1c04fb Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:41:58 +0000 Subject: [PATCH 17/73] feat(cost-map): add baseten elephant-1 and mammoth-1 decision models (#48453) Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...model_prices_and_context_window_backup.json | 18 ++++++++++++++++++ model_prices_and_context_window.json | 18 ++++++++++++++++++ tests/unit/test_utils.py | 1 + 3 files changed, 37 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 3bb8b9f8029..4ee3bcb831d 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -78851,5 +78851,23 @@ "supports_tool_choice": true, "supports_vision": true, "supports_xhigh_reasoning_effort": true + }, + "baseten/decision-models/baseten-elephant-9b-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "baseten", + "max_input_tokens": 16384, + "mode": "decisions", + "output_cost_per_token": 0.0, + "source": "https://inference.baseten.co/v1/models", + "supports_vision": true + }, + "baseten/decision-models/baseten-mammoth-27b-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "baseten", + "max_input_tokens": 16384, + "mode": "decisions", + "output_cost_per_token": 0.0, + "source": "https://inference.baseten.co/v1/models", + "supports_vision": true } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 3bb8b9f8029..4ee3bcb831d 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -78851,5 +78851,23 @@ "supports_tool_choice": true, "supports_vision": true, "supports_xhigh_reasoning_effort": true + }, + "baseten/decision-models/baseten-elephant-9b-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "baseten", + "max_input_tokens": 16384, + "mode": "decisions", + "output_cost_per_token": 0.0, + "source": "https://inference.baseten.co/v1/models", + "supports_vision": true + }, + "baseten/decision-models/baseten-mammoth-27b-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "baseten", + "max_input_tokens": 16384, + "mode": "decisions", + "output_cost_per_token": 0.0, + "source": "https://inference.baseten.co/v1/models", + "supports_vision": true } } diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 55b7bc78a11..b9baef4056a 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1086,6 +1086,7 @@ _COST_MAP_ROW_SCHEMA: Final = { "chat", "completion", "container", + "decisions", "image_edit", "embedding", "evaluation", From 3beee3cfe97f3b3c04458ba5ff2612c8bd9358a7 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 13:42:01 -0700 Subject: [PATCH 18/73] fix(elevenlabs): map wav and pcm TTS formats to 24kHz output formats (#48455) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../text_to_speech/transformation.py | 4 +-- .../test_transformation.py | 2 +- ...levenlabs_text_to_speech_transformation.py | 28 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/litellm/llms/elevenlabs/text_to_speech/transformation.py b/litellm/llms/elevenlabs/text_to_speech/transformation.py index f910ef8c057..23ccf41c9da 100644 --- a/litellm/llms/elevenlabs/text_to_speech/transformation.py +++ b/litellm/llms/elevenlabs/text_to_speech/transformation.py @@ -56,9 +56,9 @@ class ElevenLabsTextToSpeechConfig(BaseTextToSpeechConfig): # Response format mappings from OpenAI to ElevenLabs FORMAT_MAPPINGS = { "mp3": "mp3_44100_128", - "pcm": "pcm_44100", + "pcm": "pcm_24000", "opus": "opus_48000_128", - # ElevenLabs does not support WAV, AAC, or FLAC formats. + "wav": "wav_24000", } ELEVENLABS_QUERY_PARAMS_KEY = "__elevenlabs_query_params__" diff --git a/tests/unit/llms/elevenlabs/audio_transcription/test_transformation.py b/tests/unit/llms/elevenlabs/audio_transcription/test_transformation.py index 86252a1a95d..f55644eccae 100644 --- a/tests/unit/llms/elevenlabs/audio_transcription/test_transformation.py +++ b/tests/unit/llms/elevenlabs/audio_transcription/test_transformation.py @@ -314,4 +314,4 @@ class TestElevenLabsTextToSpeechTransformation: ) assert voice_id in url - assert "output_format=pcm_44100" in url + assert "output_format=pcm_24000" in url diff --git a/tests/unit/llms/elevenlabs/test_elevenlabs_text_to_speech_transformation.py b/tests/unit/llms/elevenlabs/test_elevenlabs_text_to_speech_transformation.py index d05371d7df9..88f0ae1d524 100644 --- a/tests/unit/llms/elevenlabs/test_elevenlabs_text_to_speech_transformation.py +++ b/tests/unit/llms/elevenlabs/test_elevenlabs_text_to_speech_transformation.py @@ -55,3 +55,31 @@ def test_speech_keeps_an_internal_prefixed_kwarg_out_of_the_elevenlabs_request(r sent: Final = json.loads(respx_mock.calls[0].request.content) assert "_litellm_undeclared_sentinel" not in sent, sent assert sent["text"] == "hi" + + +@pytest.mark.parametrize( + ("response_format", "expected_output_format"), + [("wav", "wav_24000"), ("pcm", "pcm_24000")], +) +def test_speech_maps_wav_and_pcm_to_elevenlabs_output_formats( + respx_mock: respx.MockRouter, + response_format: str, + expected_output_format: str, +) -> None: + api_base: Final = "http://localhost:12346" + mock_route: Final = respx_mock.post(url__regex=rf"{api_base}/v1/text-to-speech/.*").mock( + return_value=httpx.Response(status_code=200, content=b"audio", headers={"content-type": "audio/mpeg"}) + ) + + litellm.speech( + model="elevenlabs/eleven_multilingual_v2", + input="hi", + voice="21m00Tcm4TlvDq8ikWAM", + response_format=response_format, + api_base=api_base, + api_key="fake_elevenlabs_api_key", + ) + + assert mock_route.called + request_url: Final = httpx.URL(str(respx_mock.calls[0].request.url)) + assert request_url.params["output_format"] == expected_output_format From c843cee062e08113f5afa101b0b6879824dd9025 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:55:51 +0000 Subject: [PATCH 19/73] feat(pricing): add github_copilot/gpt-6.1-terra (#48450) * feat(pricing): add github_copilot/gpt-6.1-terra * fix(pricing): raise github_copilot gpt-6 max_input_tokens to 922000 --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 32 ++++++++++++++++--- model_prices_and_context_window.json | 32 ++++++++++++++++--- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 4ee3bcb831d..b7e0c881455 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -33698,7 +33698,7 @@ "input_cost_per_token": 1e-05, "input_cost_per_token_above_272k_tokens": 2e-05, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33722,7 +33722,7 @@ "input_cost_per_token": 1e-07, "input_cost_per_token_above_272k_tokens": 2e-07, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33746,7 +33746,7 @@ "input_cost_per_token": 2e-06, "input_cost_per_token_above_272k_tokens": 4e-06, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33770,7 +33770,7 @@ "input_cost_per_token": 2e-06, "input_cost_per_token_above_272k_tokens": 4e-06, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -78869,5 +78869,29 @@ "output_cost_per_token": 0.0, "source": "https://inference.baseten.co/v1/models", "supports_vision": true + }, + "github_copilot/gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "litellm_provider": "github_copilot", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "responses", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://github.blog/changelog/2026-10-26-gpt-6-1-terra-in-github-copilot", + "supported_endpoints": [ + "/v1/responses" + ], + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_vision": true } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 4ee3bcb831d..b7e0c881455 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -33698,7 +33698,7 @@ "input_cost_per_token": 1e-05, "input_cost_per_token_above_272k_tokens": 2e-05, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33722,7 +33722,7 @@ "input_cost_per_token": 1e-07, "input_cost_per_token_above_272k_tokens": 2e-07, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33746,7 +33746,7 @@ "input_cost_per_token": 2e-06, "input_cost_per_token_above_272k_tokens": 4e-06, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -33770,7 +33770,7 @@ "input_cost_per_token": 2e-06, "input_cost_per_token_above_272k_tokens": 4e-06, "litellm_provider": "github_copilot", - "max_input_tokens": 272000, + "max_input_tokens": 922000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "responses", @@ -78869,5 +78869,29 @@ "output_cost_per_token": 0.0, "source": "https://inference.baseten.co/v1/models", "supports_vision": true + }, + "github_copilot/gpt-6.1-terra": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "litellm_provider": "github_copilot", + "max_input_tokens": 922000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "responses", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://github.blog/changelog/2026-10-26-gpt-6-1-terra-in-github-copilot", + "supported_endpoints": [ + "/v1/responses" + ], + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_vision": true } } From b7a2226f438499509730c9fe649beb026b7cb4fe Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 16:58:51 -0400 Subject: [PATCH 20/73] test(proxy): make tokenizer offload check deterministic (#48458) --- ...test_proxy_server_endpoints_and_startup.py | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/tests/unit/proxy/test_proxy_server_endpoints_and_startup.py b/tests/unit/proxy/test_proxy_server_endpoints_and_startup.py index 14196d4a1ac..a859ce96c70 100644 --- a/tests/unit/proxy/test_proxy_server_endpoints_and_startup.py +++ b/tests/unit/proxy/test_proxy_server_endpoints_and_startup.py @@ -15207,21 +15207,23 @@ async def test_token_counter_keeps_the_event_loop_free_during_a_huggingface_coun assert_loop_stayed_free(took, lags) -async def test_token_counter_loads_a_custom_tokenizer_off_the_event_loop(monkeypatch): - from litellm.rust_bridge._native import Tokenizer +async def test_token_counter_loads_a_custom_tokenizer_off_the_event_loop(monkeypatch: pytest.MonkeyPatch) -> None: + from queue import SimpleQueue + from threading import Thread, current_thread + + from tokenizers import Tokenizer from litellm import Router - from tests.unit.litellm_core_utils.event_loop_lag import assert_loop_stayed_free, timed_with_loop_lags - claude_tokenizer: Final = litellm.utils.select_tokenizer("claude-fable-5")["tokenizer"] + claude_tokenizer: Final = Tokenizer.from_str(litellm.utils.claude_json_str) + loop_thread: Final = current_thread() + loader_threads: Final[SimpleQueue[Thread]] = SimpleQueue() - class SlowHubTokenizer: - @staticmethod - def from_pretrained(identifier: str, revision: str = "main", token: str | None = None) -> Tokenizer: - time.sleep(0.3) - return claude_tokenizer + def from_pretrained(identifier: str, revision: str = "main", token: str | None = None) -> Tokenizer: + loader_threads.put(current_thread()) + return claude_tokenizer - monkeypatch.setattr("litellm.rust_bridge.tokenizer.from_pretrained", SlowHubTokenizer.from_pretrained) + monkeypatch.setattr("tokenizers.Tokenizer", types.SimpleNamespace(from_pretrained=from_pretrained)) monkeypatch.setattr( "litellm.proxy.proxy_server.llm_router", Router( @@ -15237,15 +15239,16 @@ async def test_token_counter_loads_a_custom_tokenizer_off_the_event_loop(monkeyp ), ) - response, took, lags = await timed_with_loop_lags( - lambda: proxy_server_module.token_counter( + litellm.utils._select_custom_tokenizer_helper.cache_clear() + try: + response: Final = await proxy_server_module.token_counter( TokenCountRequest(model="self-hosted", prompt="count me off the loop") ) - ) - - assert response.tokenizer_type == "huggingface_tokenizer" - assert response.total_tokens > 0 - assert_loop_stayed_free(took, lags) + assert loader_threads.get_nowait() is not loop_thread + assert response.tokenizer_type == "huggingface_tokenizer" + assert response.total_tokens > 0 + finally: + litellm.utils._select_custom_tokenizer_helper.cache_clear() async def test_token_counter_loads_a_custom_tokenizer_once_per_identifier_revision_and_token(monkeypatch): From 6d9344535093b6ad5a1b246af449c20d1db46004 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 17:09:19 -0400 Subject: [PATCH 21/73] fix(proxy): preserve limiter state across SSE keepalive tasks (#48452) --- litellm/proxy/common_request_processing.py | 3 + .../hooks/test_parallel_request_limiter_v3.py | 136 +++++++++++++++++- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index cec176ac559..61cf4d696a1 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -119,6 +119,7 @@ from litellm.proxy.common_utils.sse_keepalive import ( ) from litellm.proxy.dd_span_tagger import DDSpanTagger from litellm.proxy.guardrails.auto_router_compression import arm_pre_call as _arm_auto_router_compression +from litellm.proxy.hooks.parallel_request_limiter_v3 import get_or_create_request_stash from litellm.proxy.native_compaction import with_proxy_compaction_executor from litellm.proxy.route_llm_request import ( route_request, @@ -1416,6 +1417,8 @@ async def open_sse_before_first_byte( if interval is None: return await produce_response + # Share admission state with stream cleanup before the producer copies this context. + get_or_create_request_stash() produce_task: Final = asyncio.ensure_future(produce_response) await asyncio.wait((produce_task,), timeout=interval) if produce_task.done(): diff --git a/tests/unit/proxy/hooks/test_parallel_request_limiter_v3.py b/tests/unit/proxy/hooks/test_parallel_request_limiter_v3.py index 1ec505362dd..345f37f1db4 100644 --- a/tests/unit/proxy/hooks/test_parallel_request_limiter_v3.py +++ b/tests/unit/proxy/hooks/test_parallel_request_limiter_v3.py @@ -7,7 +7,7 @@ import contextvars import logging import sys import time -from collections.abc import Iterator, Sequence +from collections.abc import AsyncGenerator, Iterator, Sequence from contextlib import contextmanager from datetime import datetime, timedelta, timezone from typing import Any, Dict, Final, List, Optional @@ -4644,6 +4644,140 @@ async def test_success_event_releases_parallel_slot_v3(monkeypatch): ) +_SSE_KEEPALIVE_CALLBACK_TIME: Final = datetime(2026, 1, 1) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("after_ping", [False, True], ids=["fast", "pinged"]) +@pytest.mark.parametrize("fails", [False, True], ids=["success", "failure"]) +@pytest.mark.parametrize("preadmitted", [False, True], ids=["producer-admission", "existing-admission"]) +async def test_sse_keepalive_releases_parallel_capacity( + after_ping: bool, fails: bool, preadmitted: bool +) -> None: + from fastapi.responses import StreamingResponse + + from litellm.proxy.common_request_processing import open_sse_before_first_byte + from litellm.proxy.common_utils.sse_keepalive import SSE_COMMENT_PING_BYTES + + cache: Final = DualCache() + handler: Final = _PROXY_MaxParallelRequestsHandler(internal_usage_cache=InternalUsageCache(cache)) + api_key: Final = hash_token("sk-keepalive-release") + auth: Final = UserAPIKeyAuth(api_key=api_key, max_parallel_requests=1) + counter: Final = f"{{api_key:{api_key}}}:max_parallel_requests" + + async def request(call_id: str) -> None: + data: Final = {"model": "test-model", "litellm_call_id": call_id} + upstream_answers: Final = asyncio.Event() + if not after_ping: + upstream_answers.set() + + async def admit() -> None: + await handler.async_pre_call_hook( + user_api_key_dict=auth, cache=cache, data=data, call_type="acompletion" + ) + + async def failure(exc: Exception) -> None: + await handler.async_post_call_failure_hook( + request_data=data, original_exception=exc, user_api_key_dict=auth + ) + + async def stream() -> AsyncGenerator[bytes, None]: + yield b"data: [DONE]\n\n" + await handler.async_log_success_event( + kwargs={ + "litellm_call_id": call_id, + "standard_logging_object": {"metadata": {"user_api_key_hash": api_key}}, + }, + response_obj=ModelResponse(usage=Usage(prompt_tokens=1, completion_tokens=1, total_tokens=2)), + start_time=_SSE_KEEPALIVE_CALLBACK_TIME, + end_time=_SSE_KEEPALIVE_CALLBACK_TIME, + ) + + async def produce() -> StreamingResponse: + if not preadmitted: + await admit() + await upstream_answers.wait() + if fails: + raise HTTPException(status_code=502, detail="upstream failed") + return StreamingResponse(stream()) + + if preadmitted: + await admit() + response_call: Final = open_sse_before_first_byte( + produce(), 0.001 if after_ping else 60.0, on_late_failure=failure + ) + if fails and not after_ping: + with pytest.raises(HTTPException) as rejected: + await response_call + assert rejected.value.status_code == 502 + await failure(rejected.value) + return + response: Final = await response_call + first: Final = await anext(response.body_iterator) + if after_ping: + upstream_answers.set() + rest: Final = tuple([chunk async for chunk in response.body_iterator]) + assert first == (SSE_COMMENT_PING_BYTES if after_ping else b"data: [DONE]\n\n") + if fails: + error_frame: Final = rest[-2] + assert isinstance(error_frame, bytes) and b"upstream failed" in error_frame + assert rest[-1] == b"data: [DONE]\n\n" + elif after_ping: + assert rest[-1] == b"data: [DONE]\n\n" + else: + assert rest == () + + for index in range(3): + await contextvars.Context().run(asyncio.create_task, request(f"keepalive-{index}")) + assert handler._gauge_in_flight_from_cache_value(await cache.async_get_cache(counter)) == 0 + + +@pytest.mark.asyncio +async def test_sse_keepalive_rejects_overlap_and_reuses_completed_capacity() -> None: + from fastapi.responses import StreamingResponse + + from litellm.proxy.common_request_processing import open_sse_before_first_byte + + cache: Final = DualCache() + handler: Final = _PROXY_MaxParallelRequestsHandler(internal_usage_cache=InternalUsageCache(cache)) + api_key: Final = hash_token("sk-keepalive-overlap") + auth: Final = UserAPIKeyAuth(api_key=api_key, max_parallel_requests=1) + admitted: Final = asyncio.Event() + finish: Final = asyncio.Event() + + async def request(call_id: str) -> None: + async def stream() -> AsyncGenerator[bytes, None]: + await finish.wait() + yield b"data: [DONE]\n\n" + await handler.async_post_call_success_hook( + data={"litellm_call_id": call_id}, user_api_key_dict=auth, response=ModelResponse() + ) + + async def produce() -> StreamingResponse: + await handler.async_pre_call_hook( + user_api_key_dict=auth, + cache=cache, + data={"model": "test-model", "litellm_call_id": call_id}, + call_type="acompletion", + ) + admitted.set() + return StreamingResponse(stream()) + + response: Final = await open_sse_before_first_byte(produce(), 60.0) + assert tuple([chunk async for chunk in response.body_iterator]) == (b"data: [DONE]\n\n",) + + first: Final = contextvars.Context().run(asyncio.create_task, request("first")) + await admitted.wait() + try: + with pytest.raises(HTTPException) as rejected: + await contextvars.Context().run(asyncio.create_task, request("overlapping")) + assert rejected.value.status_code == 429 + finally: + finish.set() + await first + await contextvars.Context().run(asyncio.create_task, request("after-completion")) + + @pytest.mark.asyncio async def test_read_only_gauge_check_counts_without_acquiring_v3(): """ From ccd783d907d8b8a88add48c35732371e8701febf Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 17:16:15 -0400 Subject: [PATCH 22/73] fix(proxy): reject negative RPM and TPM request limits (#48444) --- litellm/proxy/_types.py | 12 ++- .../management_endpoints/key_requests.py | 4 + .../management_endpoints/user_requests.py | 4 + tests/unit/proxy/test__types.py | 83 ++++++++++++++++++- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 2b01e02bb06..db433f07f3b 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1766,8 +1766,10 @@ class BudgetNewRequest(LiteLLMPydanticObjectBase): max_parallel_requests: int | None = Field( default=None, description="Max concurrent requests allowed for this budget id." ) - tpm_limit: int | None = Field(default=None, description="Max tokens per minute, allowed for this budget id.") - rpm_limit: int | None = Field(default=None, description="Max requests per minute, allowed for this budget id.") + tpm_limit: int | None = Field(default=None, ge=0, description="Max tokens per minute, allowed for this budget id.") + rpm_limit: int | None = Field( + default=None, ge=0, description="Max requests per minute, allowed for this budget id." + ) tpd_limit: int | None = Field( default=None, description="Max tokens per day, charged by batch submissions, allowed for this budget id." ) @@ -1878,6 +1880,8 @@ RouterSettingsDict = Annotated[ class NewTeamRequest(TeamBase): + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) router_settings: RouterSettingsDict | None = None model_aliases: dict | None = None model_max_budget: GenericBudgetConfigType | None = Field( @@ -1957,8 +1961,8 @@ class UpdateTeamRequest(LiteLLMPydanticObjectBase): team_alias: str | None = None organization_id: str | None = None metadata: dict | None = None - tpm_limit: int | None = None - rpm_limit: int | None = None + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) tpd_limit: int | None = None max_budget: float | None = None soft_budget: float | None = None diff --git a/litellm/types/proxy/management_endpoints/key_requests.py b/litellm/types/proxy/management_endpoints/key_requests.py index baa02b68d98..172357d8ac4 100644 --- a/litellm/types/proxy/management_endpoints/key_requests.py +++ b/litellm/types/proxy/management_endpoints/key_requests.py @@ -55,6 +55,8 @@ class LiteLLMKeyType(str, enum.Enum): class GenerateKeyRequest(KeyRequestBase): + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) soft_budget: float | None = None send_invite_email: bool | None = None key_type: LiteLLMKeyType | None = Field( @@ -82,6 +84,8 @@ def _is_mapping(value: object) -> TypeIs[Mapping[object, object]]: # guard-ok: class UpdateKeyRequest(KeyRequestBase): + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) # Note: the defaults of all Params here MUST BE NONE # else they will get overwritten duration: str | None = None diff --git a/litellm/types/proxy/management_endpoints/user_requests.py b/litellm/types/proxy/management_endpoints/user_requests.py index 49dda5995bc..e53dc4152c3 100644 --- a/litellm/types/proxy/management_endpoints/user_requests.py +++ b/litellm/types/proxy/management_endpoints/user_requests.py @@ -16,6 +16,8 @@ class NewUserRequestTeam(LiteLLMPydanticObjectBase): class NewUserRequest(GenerateRequestBase): + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) max_budget: float | None = None user_email: str | None = None user_alias: str | None = None @@ -47,6 +49,8 @@ class NewUserRequest(GenerateRequestBase): class UpdateUserRequestNoUserIDorEmail(GenerateRequestBase): # shared with BulkUpdateUserRequest + tpm_limit: int | None = Field(default=None, ge=0) + rpm_limit: int | None = Field(default=None, ge=0) # repr=False keeps the plaintext out of management-endpoint alerts, which str() the request model password: str | None = Field(default=None, repr=False) spend: float | None = None diff --git a/tests/unit/proxy/test__types.py b/tests/unit/proxy/test__types.py index 68bfe4b34e5..76ca49362b4 100644 --- a/tests/unit/proxy/test__types.py +++ b/tests/unit/proxy/test__types.py @@ -2,23 +2,35 @@ import json from typing import Final import pytest -from pydantic import ValidationError +from pydantic import BaseModel, ValidationError from litellm.proxy._types import ( ROLES_WITHIN_ORG, + BudgetNewRequest, ChangePasswordRequest, GenerateKeyRequest, + GenerateKeyResponse, KeyRequest, LiteLLM_AuditLogs, + LiteLLM_BudgetTable, + LiteLLM_EndUserTable, LiteLLM_TeamMembership, + LiteLLM_TeamTable, + LiteLLM_UserTable, LitellmUserRoles, + NewCustomerRequest, NewMCPServerRequest, + NewTeamRequest, NewUserRequest, + NewUserResponse, OrganizationMemberUpdateRequest, + RegenerateKeyRequest, ResetSpendRequest, UpdateKeyRequest, UpdateMCPServerRequest, + UpdateTeamRequest, UpdateUserRequest, + UpdateUserRequestNoUserIDorEmail, UserAPIKeyAuth, ) @@ -213,6 +225,75 @@ def test_a_max_budget_that_was_actually_sent_is_kept(sent): assert GenerateKeyRequest(max_budget=sent).max_budget == sent +RATE_LIMIT_REQUESTS: Final = ( + (GenerateKeyRequest, {}), + (RegenerateKeyRequest, {}), + (UpdateKeyRequest, {"key": "sk-test"}), + (NewUserRequest, {}), + (UpdateUserRequestNoUserIDorEmail, {}), + (UpdateUserRequest, {"user_id": "user-test"}), + (NewTeamRequest, {}), + (UpdateTeamRequest, {"team_id": "team-test"}), + (BudgetNewRequest, {}), + (NewCustomerRequest, {"user_id": "customer-test"}), +) + + +@pytest.mark.parametrize(("request_model", "identifiers"), RATE_LIMIT_REQUESTS) +@pytest.mark.parametrize("field", ("rpm_limit", "tpm_limit")) +@pytest.mark.parametrize("value", (-1, -100)) +def test_management_requests_reject_negative_rate_limits( + request_model: type[BaseModel], identifiers: dict[str, str], field: str, value: int +) -> None: + with pytest.raises(ValidationError) as error: + request_model.model_validate({**identifiers, field: value}) + assert error.value.errors()[0]["loc"] == (field,) + + +@pytest.mark.parametrize(("request_model", "identifiers"), RATE_LIMIT_REQUESTS) +@pytest.mark.parametrize("value", (None, 0, 100)) +def test_management_requests_preserve_explicit_rate_limits( + request_model: type[BaseModel], identifiers: dict[str, str], value: int | None +) -> None: + limits: Final = {"rpm_limit": value, "tpm_limit": value} + request: Final = request_model.model_validate({**identifiers, **limits}) + assert request.model_dump(include={"rpm_limit", "tpm_limit"}, exclude_unset=True) == limits + + +@pytest.mark.parametrize(("request_model", "identifiers"), RATE_LIMIT_REQUESTS) +def test_management_requests_leave_omitted_rate_limits_unset( + request_model: type[BaseModel], identifiers: dict[str, str] +) -> None: + request: Final = request_model.model_validate({**identifiers}) + assert request.model_dump(include={"rpm_limit", "tpm_limit"}) == {"rpm_limit": None, "tpm_limit": None} + assert request.model_dump(include={"rpm_limit", "tpm_limit"}, exclude_unset=True) == {} + + +@pytest.mark.parametrize(("response_model", "identifiers"), ( + (GenerateKeyResponse, {"key": "sk-test"}), + (NewUserResponse, {"key": "sk-test"}), + (LiteLLM_TeamTable, {"team_id": "team-test"}), + (LiteLLM_UserTable, {"user_id": "user-test"}), + (LiteLLM_BudgetTable, {"budget_id": "budget-test"}), +)) +def test_existing_negative_rate_limits_remain_readable( + response_model: type[BaseModel], identifiers: dict[str, str] +) -> None: + limits: Final = {"rpm_limit": -1, "tpm_limit": -100} + response: Final = response_model.model_validate({**identifiers, **limits}) + assert response.model_dump(include={"rpm_limit", "tpm_limit"}) == limits + + +def test_existing_customer_negative_rate_limits_remain_readable() -> None: + customer: Final = LiteLLM_EndUserTable.model_validate({ + "user_id": "customer-test", "blocked": False, + "litellm_budget_table": {"rpm_limit": -1, "tpm_limit": -100}, + }) + assert customer.litellm_budget_table is not None + assert customer.litellm_budget_table.rpm_limit == -1 + assert customer.litellm_budget_table.tpm_limit == -100 + + USER_IDENTIFIER_CASES = ( ({"user_id": "user-1"}, True), ({"user_email": "user@example.com"}, True), From dbd55d32023157fa1e13db994645878f8f870fd8 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 14:55:08 -0700 Subject: [PATCH 23/73] fix(ui): keep Aria accessible in the dashboard header (#48454) * fix(ui): keep Aria accessible in the dashboard header * test(ui): wait for every pagination fixture log * test(ui): update assistant setup for Aria branding --- tests/e2e/ui/globalSetup.ts | 4 +- tests/e2e/ui/helpers/navigation.ts | 23 ++++-- .../e2e/ui/tests/logs/logsPagination.spec.ts | 61 ++++++++++++++- ui/litellm-dashboard/AGENTS.md | 2 +- .../src/components/DashboardHeader.test.tsx | 3 +- .../src/components/DashboardHeader.tsx | 75 ++++++++++--------- .../liteadmin/LiteAdmin.integration.test.tsx | 67 ++++++++--------- .../src/components/liteadmin/LiteAdmin.tsx | 37 +++------ 8 files changed, 161 insertions(+), 111 deletions(-) diff --git a/tests/e2e/ui/globalSetup.ts b/tests/e2e/ui/globalSetup.ts index d97789c227e..e18d1be390c 100644 --- a/tests/e2e/ui/globalSetup.ts +++ b/tests/e2e/ui/globalSetup.ts @@ -2,7 +2,7 @@ import { chromium, expect, request } from "@playwright/test"; import { users, Role, STORAGE_PATHS } from "./fixtures/users"; import { ARTIFACT_DIR, UI_BASE_URL } from "./constants"; import { expectUnrestrictedDashboard, setInvitedUserPassword } from "./helpers/userOnboarding"; -import { hideLiteAdmin } from "./helpers/navigation"; +import { setAriaHidden } from "./helpers/navigation"; import { masterKey as getMasterKey } from "./helpers/traffic"; import * as fs from "fs"; import * as path from "path"; @@ -78,7 +78,7 @@ async function globalSetup() { await dismiss.click(); } if (role === Role.ProxyAdmin) { - await hideLiteAdmin(page); + await setAriaHidden(page, true); } // The login flow stores a post-login return URL in the litellm_return_url // cookie. If the snapshot captures it before the app consumes it, every diff --git a/tests/e2e/ui/helpers/navigation.ts b/tests/e2e/ui/helpers/navigation.ts index 78288fab743..be46fcf0395 100644 --- a/tests/e2e/ui/helpers/navigation.ts +++ b/tests/e2e/ui/helpers/navigation.ts @@ -62,18 +62,29 @@ export async function dismissFeedbackPopup(page: PlaywrightPage): Promise } } -export async function hideLiteAdmin(page: PlaywrightPage): Promise { +export async function setAriaHidden(page: PlaywrightPage, hidden: boolean): Promise { await page.getByRole("button", { name: /Account menu/i }).click(); const panel = page.getByTestId("sidebar-account-menu-panel"); await expect(panel).toBeVisible({ timeout: 5_000 }); - const toggle = panel.getByRole("switch", { name: "Toggle hide LiteAdmin" }); - if ((await toggle.getAttribute("aria-checked")) !== "true") { - await toggle.click(); + const premium = await panel.getByText("Premium", { exact: true }).isVisible(); + const toggle = panel.getByRole("switch", { name: "Toggle hide Aria Agent", exact: true }); + if (premium) { + await expect(toggle).toBeVisible(); + if ((await toggle.getAttribute("aria-checked")) !== String(hidden)) { + await toggle.click(); + } + await expect(toggle).toHaveAttribute("aria-checked", String(hidden)); + } else { + await expect(toggle).toHaveCount(0); } - await expect(toggle).toHaveAttribute("aria-checked", "true"); await page.keyboard.press("Escape"); await expect(panel).toBeHidden(); - await expect(page.getByRole("button", { name: "LiteAdmin", exact: true })).toBeHidden(); + const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true }); + if (premium && hidden) { + await expect(aria).toBeHidden(); + } else { + await expect(aria).toBeVisible(); + } } /** diff --git a/tests/e2e/ui/tests/logs/logsPagination.spec.ts b/tests/e2e/ui/tests/logs/logsPagination.spec.ts index 416e1c3171f..838a829a3e2 100644 --- a/tests/e2e/ui/tests/logs/logsPagination.spec.ts +++ b/tests/e2e/ui/tests/logs/logsPagination.spec.ts @@ -1,8 +1,14 @@ import { test, expect, type APIRequestContext, type Locator, type Page as PlaywrightPage } from "@playwright/test"; import { ADMIN_STORAGE_PATH } from "../../constants"; -import { navigateToPage, dismissFeedbackPopup } from "../../helpers/navigation"; +import { navigateToPage, dismissFeedbackPopup, setAriaHidden } from "../../helpers/navigation"; import { Page } from "../../fixtures/pages"; -import { CHAT_MODEL_A, createVirtualKey, sendChatCompletion, waitForSpendLog } from "../../helpers/traffic"; +import { + CHAT_MODEL_A, + createVirtualKey, + deleteVirtualKey, + sendChatCompletion, + waitForSpendLog, +} from "../../helpers/traffic"; /** * Session-grouped pagination (#38060): a page of N rows must render exactly N session rows, a @@ -45,6 +51,57 @@ async function setRowsPerPage(page: PlaywrightPage, size: "25" | "50" | "100"): await page.getByRole("option", { name: size, exact: true }).click(); } +test.describe("Logs pagination with Aria in the header", () => { + test.use({ storageState: ADMIN_STORAGE_PATH }); + + for (const viewport of [ + { width: 1440, height: 900 }, + { width: 390, height: 844 }, + ]) { + test(`keeps pagination reachable and Aria available at ${viewport.width}px`, async ({ page, request }) => { + await page.setViewportSize(viewport); + const alias = `e2e-aria-header-${uniqueSuffix()}`; + const key = await createVirtualKey(request, { key_alias: alias }); + try { + const ids = await Promise.all( + Array.from({ length: 26 }, (_, index) => + sendChatCompletion(request, { + model: CHAT_MODEL_A, + prompt: `${alias}-${index}`, + apiKey: key.key, + }), + ), + ); + await Promise.all(ids.map((id) => waitForSpendLog(request, id))); + await openLogs(page); + await setAriaHidden(page, false); + await applyKeyAliasFilter(page, await openFilterDrawer(page), alias); + await setRowsPerPage(page, "25"); + await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 1-25 of 26"); + await visibleTestId(page, "pagination-next").click(); + await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 26-26 of 26"); + + const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true }); + await aria.click(); + await expect(page.getByRole("dialog", { name: "Aria Agent", exact: true })).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(aria).toBeFocused(); + await visibleTestId(page, "pagination-first").click(); + await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 1-25 of 26"); + for (const destination of [Page.Usage, Page.LlmPlayground, Page.ApiKeys]) { + await navigateToPage(page, destination); + await aria.click(); + await expect(page.getByRole("dialog", { name: "Aria Agent", exact: true })).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(aria).toBeFocused(); + } + } finally { + await deleteVirtualKey(request, key.token); + } + }); + } +}); + test.describe("Logs page session-grouped pagination", () => { test.use({ storageState: ADMIN_STORAGE_PATH }); diff --git a/ui/litellm-dashboard/AGENTS.md b/ui/litellm-dashboard/AGENTS.md index bbf2e49aad7..b6fe5219022 100644 --- a/ui/litellm-dashboard/AGENTS.md +++ b/ui/litellm-dashboard/AGENTS.md @@ -30,7 +30,7 @@ Type tests are `*.test-d.ts` files run by the `types` vitest project (`npm run t -## This is NOT the Next.js you know +# This is NOT the Next.js you know This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx index 770315af49f..202a111ea97 100644 --- a/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx @@ -124,7 +124,8 @@ describe("DashboardHeader breadcrumb", () => { const tools = await screen.findByRole("dialog", { name: "Gateway tools" }); expect(within(tools).getByRole("button", { name: "Switch workspace, current: AI Gateway" })).toBeInTheDocument(); expect(within(tools).getByRole("link", { name: "Docs" })).toBeInTheDocument(); - expect(within(tools).getByRole("button", { name: "Aria Agent" })).toBeInTheDocument(); + expect(within(screen.getByRole("banner")).getByRole("button", { name: "Aria Agent" })).toBeInTheDocument(); + expect(screen.getAllByRole("button", { name: "Aria Agent" })).toHaveLength(1); }); it("closes mobile tools when switching to desktop and keeps them closed when returning", async () => { diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.tsx index b31fc794f79..55f163c2495 100644 --- a/ui/litellm-dashboard/src/components/DashboardHeader.tsx +++ b/ui/litellm-dashboard/src/components/DashboardHeader.tsx @@ -73,43 +73,46 @@ export function DashboardHeader({ -
- {showWorkerSwitch && ( - <> - - - - )} +
- - - {!hideCommunityLinks && } - - - {deploymentHealth} - -
-
- - {deploymentHealth} - - - }> - - - - Gateway tools - - {showWorkerSwitch && } -
- - - - - {!hideCommunityLinks && } -
-
-
+
+ {showWorkerSwitch && ( + <> + + + + )} + + + {!hideCommunityLinks && } + + + {deploymentHealth} + +
+
+ + {deploymentHealth} + + + } + > + + + + Gateway tools + + {showWorkerSwitch && } +
+ + + + {!hideCommunityLinks && } +
+
+
+
); diff --git a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx index f32c99bedf3..ed66f7ce2ef 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx @@ -240,7 +240,7 @@ describe("Aria Agent in the gateway", () => { session(role); const requests = gateway([answer("Here is your weekly spend.")]); renderWidget(); - await userEvent.setup().click(await screen.findByRole("button", { name: "Launch Aria Agent" })); + await userEvent.setup().click(await screen.findByRole("button", { name: "Aria Agent" })); const question = "How much have I spent this week?"; const suggestion = await screen.findByRole("button", { name: question }); await waitFor(() => expect(suggestion).toBeEnabled()); @@ -268,7 +268,7 @@ describe("Aria Agent in the gateway", () => { it("should disable suggested questions when no chat model is available", async () => { const requests = gateway([], { models: [] }); renderWidget(); - await userEvent.setup().click(await screen.findByRole("button", { name: "Launch Aria Agent" })); + await userEvent.setup().click(await screen.findByRole("button", { name: "Aria Agent" })); const picker = await screen.findByRole("combobox", { name: "Aria Agent model" }); expect(picker).toBeEnabled(); expect(screen.queryByText("No chat models are available for your account.")).not.toBeInTheDocument(); @@ -285,7 +285,7 @@ describe("Aria Agent in the gateway", () => { expect(requests.filter((request) => request.url.endsWith("/chat/completions"))).toHaveLength(0); }); - it("should restore launcher focus and preserve the conversation when the popup closes with Escape", async () => { + it("should restore header trigger focus and preserve the conversation when the popup closes with Escape", async () => { const requests = gateway([answer("Your current usage is $12.34."), answer("Last month was $10.")]); const user = userEvent.setup(); const backgroundAction = vi.fn(); @@ -293,7 +293,7 @@ describe("Aria Agent in the gateway", () => { return ; } renderWidget(PageAction); - await user.click(await screen.findByRole("button", { name: "Launch Aria Agent" })); + await user.click(await screen.findByRole("button", { name: "Aria Agent" })); const popup = await screen.findByRole("dialog", { name: "Aria Agent" }); expect(popup).toBeVisible(); await waitFor(() => expect(screen.getByPlaceholderText("Ask Aria…")).toBeEnabled()); @@ -306,8 +306,8 @@ describe("Aria Agent in the gateway", () => { await user.click(screen.getByPlaceholderText("Ask Aria…")); await user.keyboard("{Escape}"); await waitFor(() => expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument()); - const launcher = screen.getByRole("button", { name: "Launch Aria Agent" }); - await waitFor(() => expect(launcher).toHaveFocus()); + const trigger = screen.getByRole("button", { name: "Aria Agent" }); + await waitFor(() => expect(trigger).toHaveFocus()); await user.keyboard("{Enter}"); expect(await screen.findByRole("dialog", { name: "Aria Agent" })).toBeVisible(); expect(screen.getByText("Your current usage is $12.34.")).toBeInTheDocument(); @@ -324,26 +324,25 @@ describe("Aria Agent in the gateway", () => { ]); await user.click(screen.getByRole("button", { name: "Close Aria Agent" })); await waitFor(() => expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument()); - await waitFor(() => expect(screen.getByRole("button", { name: "Launch Aria Agent" })).toHaveFocus()); + await waitFor(() => expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveFocus()); }); - it("should show the same product name on Home, Usage, the launcher and the popup", async () => { + it("should show the same product name on Home, Usage, the header trigger and the popup", async () => { gateway([]); const view = renderWidget(undefined, true); const home = await screen.findByRole("region", { name: "Ask Aria Agent" }); expect(within(home).getByRole("heading", { name: "Aria Agent" })).toHaveTextContent("Aria Agent"); expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveTextContent("Aria Agent"); - expect(screen.getByRole("button", { name: "Launch Aria Agent" })).toHaveTextContent("Aria Agent"); view.refresh(false, true); const usage = await screen.findByRole("region", { name: "Ask Aria about usage" }); expect(within(usage).getByRole("heading", { name: "Aria Agent" })).toHaveTextContent("Aria Agent"); expect(screen.getByRole("button", { name: "Open Aria Agent" })).toHaveTextContent("Aria Agent"); - act(() => fireEvent.click(screen.getByRole("button", { name: "Launch Aria Agent" }))); + act(() => fireEvent.click(screen.getByRole("button", { name: "Aria Agent" }))); const popup = await screen.findByRole("dialog", { name: "Aria Agent" }); expect(within(popup).getByRole("heading", { name: "Aria Agent" })).toHaveTextContent("Aria Agent"); }); - it("should keep the same scoped conversation when leaving Usage and reopening the launcher", async () => { + it("should keep the same scoped conversation when leaving Usage and reopening the header trigger", async () => { session("internal_user"); const requests = gateway( [answer("Your usage is within budget."), answer("The same usage conversation continues.")], @@ -355,7 +354,7 @@ describe("Aria Agent in the gateway", () => { }, ); const view = renderWidget(undefined, false, true); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); const usage = await screen.findByRole("region", { name: "Ask Aria about usage" }); await selectModel("other-chat-model"); act(() => { @@ -366,14 +365,13 @@ describe("Aria Agent in the gateway", () => { view.refresh(false, false); expect(screen.queryByRole("region", { name: "Ask Aria about usage" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); - const launcher = screen.getByRole("button", { name: "Launch Aria Agent" }); - expect(launcher).toHaveTextContent("Aria Agent"); - act(() => launcher.focus()); + const trigger = screen.getByRole("button", { name: "Aria Agent" }); + expect(trigger).toHaveTextContent("Aria Agent"); + act(() => trigger.focus()); await userEvent.setup().keyboard("{Enter}"); expect(await screen.findByPlaceholderText("Ask Aria…")).toHaveValue("Check my usage"); expect(screen.getByRole("combobox", { name: "Aria Agent model" })).toHaveValue("other-chat-model"); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveAttribute("aria-expanded", "true"); act(() => fireEvent.click(screen.getByRole("button", { name: "Send message" }))); await screen.findByText("Your usage is within budget."); act(() => { @@ -385,7 +383,7 @@ describe("Aria Agent in the gateway", () => { const restoredUsage = await screen.findByRole("region", { name: "Ask Aria about usage" }); expect(within(restoredUsage).getByRole("textbox", { name: "Message Aria Agent" })).toHaveValue("And last month?"); view.refresh(false, false); - act(() => fireEvent.click(screen.getByRole("button", { name: "Launch Aria Agent" }))); + act(() => fireEvent.click(screen.getByRole("button", { name: "Aria Agent" }))); expect(screen.getAllByRole("dialog", { name: "Aria Agent" })).toHaveLength(1); expect(screen.getByText("Your usage is within budget.")).toBeInTheDocument(); expect(screen.getByPlaceholderText("Ask Aria…")).toHaveValue("And last month?"); @@ -405,13 +403,13 @@ describe("Aria Agent in the gateway", () => { ]); }); - it("should keep the launcher unavailable without a signed-in session", async () => { + it("should keep the header trigger unavailable without a signed-in session", async () => { document.cookie = "token=; Max-Age=0; Path=/"; const requests = gateway([]); const { client } = renderWidget(); await screen.findByText("Session ready"); await waitFor(() => expect(client.isFetching()).toBe(0)); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); act(() => fireEvent.keyDown(document, { key: "j", ctrlKey: true })); expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument(); expect(requests.every((request) => request.url.endsWith("/litellm-ui-config"))).toBe(true); @@ -494,7 +492,7 @@ describe("Aria Agent in the gateway", () => { expect(await screen.findByText("Your usage totals $12.34.")).toBeInTheDocument(); expect(screen.getAllByRole("dialog", { name: "Aria Agent" })).toHaveLength(1); expect(usage).not.toBeVisible(); - if (!writable) expect(screen.queryByRole("button", { name: "Aria Agent", hidden: true })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveAttribute("aria-expanded", "true"); const read = requests.find((request) => request.url.includes("/user/daily/activity/aggregated")); expect(read).toBeDefined(); expect(new URL(read!.url).searchParams.get("user_id")).toBe(scope === "user" ? "first-admin" : null); @@ -553,7 +551,7 @@ describe("Aria Agent in the gateway", () => { await waitFor(() => expect(usageInput).toBeEnabled()); expect(usageInput).toHaveValue(""); expect(screen.queryByRole("button", { name: "Confirm change" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveAttribute("aria-expanded", "false"); expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument(); act(() => send("Show my usage")); expect(await screen.findByText("Only your own usage is available.")).toBeInTheDocument(); @@ -812,7 +810,6 @@ describe("Aria Agent in the gateway", () => { await user.click(toggle); expect(toggle).toBeChecked(); expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); expect(screen.queryByRole("region", { name: "Ask Aria Agent", hidden: true })).not.toBeInTheDocument(); view.unmount(); @@ -820,13 +817,12 @@ describe("Aria Agent in the gateway", () => { await screen.findByText("Session ready"); await waitFor(() => expect(restored.client.isFetching()).toBe(0)); expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); await user.click(screen.getByRole("button", { name: /account menu/i })); const savedToggle = await screen.findByRole("switch", { name: "Toggle hide Aria Agent" }); expect(savedToggle).toBeChecked(); await user.click(savedToggle); expect(await screen.findByRole("button", { name: "Aria Agent" })).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Launch Aria Agent" })).toBeVisible(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toBeVisible(); expect(await screen.findByRole("region", { name: "Ask Aria Agent" })).toBeVisible(); for (const premiumUser of [false, null, undefined, "true"]) { @@ -838,27 +834,26 @@ describe("Aria Agent in the gateway", () => { }); it.each([false, null, undefined, "true"])( - "should offer the scoped launcher without admin Home or header access when premium_user is %s", + "should offer the scoped header trigger without admin Home access when premium_user is %s", async (premiumUser) => { session("proxy_admin", "first-admin", { premium_user: premiumUser }); const requests = gateway([]); const { client } = renderWidget(undefined, true); await screen.findByText("Session ready"); await waitFor(() => expect(client.isFetching()).toBe(0)); - expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toBeVisible(); expect(screen.queryByRole("region", { name: "Ask Aria Agent", hidden: true })).not.toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Launch Aria Agent" })).toBeVisible(); expect(requests.every((request) => request.url.endsWith("/litellm-ui-config"))).toBe(true); act(() => fireEvent.keyDown(document, { key: "j", metaKey: true })); expect(await screen.findByRole("dialog", { name: "Aria Agent" })).toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Launch Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveAttribute("aria-expanded", "true"); act(() => fireEvent.keyDown(document, { key: "j", ctrlKey: true })); expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Launch Aria Agent" })).toBeVisible(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toBeVisible(); }, ); - it("should discard admin state and preserve scoped launcher access when enterprise access is lost", async () => { + it("should discard admin state and preserve scoped header trigger access when enterprise access is lost", async () => { const requests = gateway([answer("Your usage remains available.")]); const view = renderWidget(); await openWidget(); @@ -866,9 +861,9 @@ describe("Aria Agent in the gateway", () => { await waitFor(() => expect(view.client.isFetching()).toBe(0)); session("proxy_admin", "first-admin", { premium_user: false }); view.refresh(); - expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toHaveAttribute("aria-expanded", "false"); expect(screen.queryByRole("dialog", { name: "Aria Agent" })).not.toBeInTheDocument(); - act(() => fireEvent.click(screen.getByRole("button", { name: "Launch Aria Agent" }))); + act(() => fireEvent.click(screen.getByRole("button", { name: "Aria Agent" }))); await waitFor(() => expect(screen.getByPlaceholderText("Ask Aria…")).toBeEnabled()); expect(screen.getByPlaceholderText("Ask Aria…")).toHaveValue(""); act(() => send("Show my usage")); @@ -997,17 +992,17 @@ describe("Aria Agent in the gateway", () => { }); it.each(["proxy_admin_viewer", "internal_user", "internal_user_viewer", "org_admin"])( - "should expose only scoped usage tools from the dashboard launcher to %s", + "should expose only scoped usage tools from the dashboard header trigger to %s", async (role) => { session(role); const requests = gateway([answer("Usage is available.")]); const { client } = renderWidget(undefined, true); await screen.findByText("Session ready"); await waitFor(() => expect(client.isFetching()).toBe(0)); - expect(screen.queryByRole("button", { name: "Aria Agent" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Aria Agent" })).toBeVisible(); expect(screen.queryByRole("region", { name: "Ask Aria Agent", hidden: true })).not.toBeInTheDocument(); expect(requests.every((request) => request.url.endsWith("/litellm-ui-config"))).toBe(true); - act(() => fireEvent.click(screen.getByRole("button", { name: "Launch Aria Agent" }))); + act(() => fireEvent.click(screen.getByRole("button", { name: "Aria Agent" }))); await waitFor(() => expect(screen.getByPlaceholderText("Ask Aria…")).toBeEnabled()); act(() => send("Show my usage")); await screen.findByText("Usage is available."); diff --git a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx index 66c1ffbc9b7..02194aabe7b 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx +++ b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx @@ -45,7 +45,7 @@ type LiteAdminState = { toggle: () => void; show: () => void; access: LiteAdminSession["access"]; - launcherRef: RefObject; + triggerRef: RefObject; homeContainer: HTMLDivElement | null; setHomeContainer: (container: HTMLDivElement | null) => void; usageContainer: HTMLDivElement | null; @@ -78,12 +78,11 @@ function useLiteAdminSession() { }; } -/** Keeps one Aria conversation available from the dashboard and its floating launcher. */ export function LiteAdminFrame({ children }: { children: ReactNode }) { const configured = useLiteAdminSession(); const sessionKey = configured?.key ?? null; const [open, setOpen] = useState(false); - const launcherRef = useRef(null); + const triggerRef = useRef(null); const [started, setStarted] = useState(false); const [homeContainer, setHomeContainer] = useState(null); const [usageContainer, updateUsageContainer] = useState(null); @@ -128,7 +127,7 @@ export function LiteAdminFrame({ children }: { children: ReactNode }) { toggle, show, access: configured.session.access, - launcherRef, + triggerRef, homeContainer, setHomeContainer, usageContainer, @@ -138,7 +137,6 @@ export function LiteAdminFrame({ children }: { children: ReactNode }) { } > {children} - {configured && !open && } {configured && showPanel && ( setOpen(false)} /> )} @@ -146,23 +144,6 @@ export function LiteAdminFrame({ children }: { children: ReactNode }) { ); } -function AriaLauncher({ show, buttonRef }: { show: () => void; buttonRef: RefObject }) { - return ( - - ); -} - export function LiteAdminHome() { const state = useContext(LiteAdminContext); if (!state || state.access.mode !== "admin") return null; @@ -189,18 +170,20 @@ export function LiteAdminHome() { export default function LiteAdminTrigger() { const state = useContext(LiteAdminContext); - if (!state || state.access.mode !== "admin") return null; + if (!state) return null; + const { triggerRef, open, toggle } = state; return ( ); @@ -358,7 +341,7 @@ function Panel({ initialFocus={() => initialFocus?.current ?? ref.current?.querySelector("textarea:enabled") ?? ref.current } - finalFocus={state?.launcherRef} + finalFocus={state?.triggerRef} className="flex h-[min(48rem,calc(100dvh-2rem))] flex-col gap-0 overflow-hidden p-0 shadow-xl sm:max-w-4xl" > {children} From a6fdc7c5a3fa8952f69623d56a52ada73dc5c893 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 15:01:40 -0700 Subject: [PATCH 24/73] fix(proxy): scope dashboard search grants to user (#48464) Co-authored-by: Jodok Batlogg Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/auth/auth_checks.py | 35 +++++- .../auth/test_search_tool_deny_by_default.py | 114 +++++++++++++++++- 2 files changed, 139 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 79e385c376a..448643ef59b 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -6461,8 +6461,29 @@ def _search_tool_deny_by_default(general_settings: Mapping[str, object]) -> bool return True +def _is_dashboard_session(valid_token: UserAPIKeyAuth | None) -> bool: + return valid_token is not None and valid_token.team_id == UI_TEAM_ID + + +def _is_search_tool_strict_identity(valid_token: UserAPIKeyAuth) -> bool: + if not _is_dashboard_session(valid_token): + return _is_strict_grant_identity(valid_token) + return valid_token.user_role not in ( + LitellmUserRoles.PROXY_ADMIN, + LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY, + ) + + +def _search_tool_strict_layers( + valid_token: UserAPIKeyAuth, team_object: LiteLLM_TeamTable | None +) -> frozenset[GrantLayer]: + if _is_dashboard_session(valid_token): + return frozenset(("user",)) + return _strict_grant_layers(True, valid_token, team_object) + + def is_search_tool_deny_by_default_applied(valid_token: UserAPIKeyAuth, general_settings: Mapping[str, object]) -> bool: - return _search_tool_deny_by_default(general_settings) and _is_strict_grant_identity(valid_token) + return _search_tool_deny_by_default(general_settings) and _is_search_tool_strict_identity(valid_token) def _search_tool_denied(object_type: GrantLayer, message: str) -> ProxyException: @@ -6506,12 +6527,12 @@ async def resolve_search_tool_grants( load_team_object: TeamObjectLoader, ) -> SearchToolGrants: """ - Without `general_settings.search_tool_deny_by_default`, or for the master key and dashboard sessions, + Without `general_settings.search_tool_deny_by_default`, or for the master key and admin dashboard sessions, the key and team lists only restrict when nonempty. With it, the identities `_strict_grant_layers` names must each list the tool, and a missing record, `null`, `[]`, or a team or user that fails to load grants nothing """ if not is_search_tool_deny_by_default_applied(valid_token, general_settings): - team_object: Final = await load_team_object() + team_object: Final = await load_team_object() if not _is_dashboard_session(valid_token) else None return SearchToolGrants( grants=MappingProxyType( {"key": valid_token.object_permission, "team": team_object.object_permission if team_object else None} @@ -6521,8 +6542,10 @@ async def resolve_search_tool_grants( from litellm.proxy.proxy_server import prisma_client, user_api_key_cache - strict_team_object: Final = await _strict_team_object(load_team_object) - strict_layers: Final = _strict_grant_layers(True, valid_token, strict_team_object) + strict_team_object: Final = ( + await _strict_team_object(load_team_object) if not _is_dashboard_session(valid_token) else None + ) + strict_layers: Final = _search_tool_strict_layers(valid_token, strict_team_object) if prisma_client is None: return SearchToolGrants(grants=MappingProxyType({}), strict_layers=strict_layers) user_object: Final = ( @@ -6607,7 +6630,7 @@ def check_unregistered_search_fallback( """The unregistered provider fallback names no search tool that a grant could list, so deny-by-default denies it""" if not is_search_tool_deny_by_default_applied(valid_token, general_settings): return True - layer: Final = min(_strict_grant_layers(True, valid_token, None), key=("key", "team", "user").index) + layer: Final = min(_search_tool_strict_layers(valid_token, None), key=("key", "team", "user").index) raise _search_tool_denied( layer, "No registered search tool is available and search_tool_deny_by_default is enabled", diff --git a/tests/unit/proxy/auth/test_search_tool_deny_by_default.py b/tests/unit/proxy/auth/test_search_tool_deny_by_default.py index cd285e0386f..da036795889 100644 --- a/tests/unit/proxy/auth/test_search_tool_deny_by_default.py +++ b/tests/unit/proxy/auth/test_search_tool_deny_by_default.py @@ -1,8 +1,9 @@ from collections.abc import Mapping -from typing import Final, TypedDict +from typing import Final, TypedDict, cast from unittest.mock import AsyncMock, MagicMock import pytest +from fastapi import HTTPException from litellm.constants import LITELLM_PROXY_MASTER_KEY_ALIAS from litellm.proxy import proxy_server @@ -78,6 +79,14 @@ def _team_loader(team_tools: list[str] | None | str) -> TeamObjectLoader: return load +def _missing_dashboard_team_loader() -> tuple[TeamObjectLoader, AsyncMock]: + missing_team: Final = HTTPException( + status_code=404, detail="Team doesn't exist in db. Team=litellm-dashboard" + ) + load_team: Final = AsyncMock(side_effect=missing_team) + return cast(TeamObjectLoader, load_team), load_team + + async def _no_team() -> None: return None @@ -194,9 +203,6 @@ async def test_search_tool_access_is_unchanged_without_search_tool_deny_by_defau id="proxy admin virtual key not exempt", ), pytest.param(CallerFields(api_key=LITELLM_PROXY_MASTER_KEY_ALIAS), None, None, None, id="master key exempt"), - pytest.param( - CallerFields(team_id=UI_TEAM_ID, virtual_key=False), None, None, None, id="dashboard session exempt" - ), ], ) @pytest.mark.asyncio @@ -248,6 +254,79 @@ async def test_search_tool_deny_by_default_does_not_load_the_user_when_off( get_user_object.assert_not_awaited() +@pytest.mark.parametrize( + "general_settings", + [{}, {"search_tool_deny_by_default": False}], + ids=["omitted", "false"], +) +@pytest.mark.parametrize( + "user_role", + [LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.INTERNAL_USER], + ids=["admin", "internal-user"], +) +@pytest.mark.parametrize( + "key_tools, expected", + [ + pytest.param(None, None, id="unrestricted-key"), + pytest.param(["search-a"], None, id="key-grants-search-a"), + pytest.param(["search-b"], _LEGACY, id="key-excludes-search-a"), + ], +) +@pytest.mark.asyncio +async def test_dashboard_search_tool_grants_without_deny_by_default( + general_settings: Mapping[str, object], + user_role: LitellmUserRoles, + key_tools: list[str] | None, + expected: ProxyErrorTypes | None, +) -> None: + caller: Final = _caller( + virtual_key=False, + key_tools=key_tools, + team_id=UI_TEAM_ID, + user_role=user_role, + ) + load_team, load_team_mock = _missing_dashboard_team_loader() + assert await _denied_by(general_settings, caller, load_team) == expected + load_team_mock.assert_not_awaited() + + +@pytest.mark.parametrize( + "user_role", + [LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY], + ids=["admin", "admin-view-only"], +) +@pytest.mark.asyncio +async def test_admin_dashboard_search_tool_grants_with_deny_by_default(user_role: LitellmUserRoles) -> None: + caller: Final = _caller(virtual_key=False, team_id=UI_TEAM_ID, user_role=user_role) + load_team, load_team_mock = _missing_dashboard_team_loader() + assert await _denied_by(_DENY_ON, caller, load_team) is None + load_team_mock.assert_not_awaited() + + +@pytest.mark.parametrize( + "user_id, user_tools, expected", + [ + pytest.param("user-1", ["search-a"], None, id="user-grants-search-a"), + pytest.param("user-1", ["search-b"], _USER, id="user-grants-another-tool"), + pytest.param("user-1", None, _USER, id="user-grant-null"), + pytest.param("user-2", None, _USER, id="no-user-row"), + ], +) +@pytest.mark.asyncio +async def test_internal_dashboard_search_tool_requires_the_user_grant_with_deny_by_default( + cache: UserApiKeyCache, + user_id: str, + user_tools: list[str] | None, + expected: ProxyErrorTypes | None, +) -> None: + if user_id == "user-1": + _cache_user(cache, user_tools) + caller: Final = _caller(virtual_key=False, team_id=UI_TEAM_ID, user_id=user_id) + load_team, load_team_mock = _missing_dashboard_team_loader() + assert await _denied_by(_DENY_ON, caller, load_team) == expected + load_team_mock.assert_not_awaited() + + @pytest.mark.parametrize( "general_settings, caller, expected", [ @@ -257,6 +336,24 @@ async def test_search_tool_deny_by_default_does_not_load_the_user_when_off( pytest.param(_DENY_ON, _caller(virtual_key=False), _USER, id="keyless user"), pytest.param(_DENY_ON, _caller(user_role=LitellmUserRoles.PROXY_ADMIN), _KEY, id="proxy admin key"), pytest.param(_DENY_ON, _caller(api_key=LITELLM_PROXY_MASTER_KEY_ALIAS), None, id="master key"), + pytest.param( + _DENY_ON, + _caller(virtual_key=False, team_id=UI_TEAM_ID, user_role=LitellmUserRoles.INTERNAL_USER), + _USER, + id="internal dashboard user", + ), + pytest.param( + _DENY_ON, + _caller(virtual_key=False, team_id=UI_TEAM_ID, user_role=LitellmUserRoles.PROXY_ADMIN), + None, + id="admin dashboard", + ), + pytest.param( + _DENY_ON, + _caller(virtual_key=False, team_id=UI_TEAM_ID, user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), + None, + id="admin view-only dashboard", + ), ], ) def test_unregistered_search_fallback_follows_search_tool_deny_by_default( @@ -268,3 +365,12 @@ def test_unregistered_search_fallback_follows_search_tool_deny_by_default( with pytest.raises(ProxyException) as exc_info: check_unregistered_search_fallback(caller, general_settings) assert (exc_info.value.type, exc_info.value.code) == (expected, "403") + + +@pytest.mark.asyncio +async def test_search_preserves_missing_real_team_error_without_deny_by_default() -> None: + missing_team: Final = HTTPException(status_code=404, detail="Team doesn't exist in db. Team=team-1") + load_team: Final = AsyncMock(side_effect=missing_team) + with pytest.raises(HTTPException) as exc_info: + await can_caller_call_search_tool("search-a", _caller(team_id="team-1"), {}, load_team) + assert exc_info.value is missing_team From 9a38ff4cb59fd60a5ca2bed9b0410783149a3e68 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 18:27:13 -0400 Subject: [PATCH 25/73] fix(gemini): preserve tool media for older models (#48428) --- .../prompt_templates/factory.py | 5 +- .../llms/vertex_ai/gemini/transformation.py | 4 + .../vertex_and_google_ai_studio_gemini.py | 4 + .../test_vertex_ai_gemini_transformation.py | 73 +++++++++++++++---- 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 0aed2956b0a..3bc8dc4e23e 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -1490,6 +1490,7 @@ def convert_to_gemini_tool_call_result( message: ChatCompletionToolMessage | ChatCompletionFunctionMessage, last_message_with_tool_calls: dict | None, forward_function_call_id: bool = False, + supports_multimodal_function_responses: bool = True, ) -> VertexPartType | list[VertexPartType]: """ OpenAI message with a tool result looks like: @@ -1652,9 +1653,9 @@ def convert_to_gemini_tool_call_result( _part: Final[VertexPartType] = {"function_response": _function_response} - # For multimodal function responses, Gemini expects media parts nested - # inside functionResponse.parts instead of sibling content parts. if inline_data_list: + if not supports_multimodal_function_responses: + return [_part, *(VertexPartType(inline_data=inline_data) for inline_data in inline_data_list)] _function_response["parts"] = [{"inline_data": inline_data} for inline_data in inline_data_list] return [_part] diff --git a/litellm/llms/vertex_ai/gemini/transformation.py b/litellm/llms/vertex_ai/gemini/transformation.py index 71bb05ee155..ed86934db63 100644 --- a/litellm/llms/vertex_ai/gemini/transformation.py +++ b/litellm/llms/vertex_ai/gemini/transformation.py @@ -726,6 +726,9 @@ def gemini_convert_messages_with_history( from .vertex_and_google_ai_studio_gemini import VertexGeminiConfig forward_function_call_id: Final = VertexGeminiConfig.forward_gemini_function_call_id(model or "") + supports_multimodal_function_responses: Final = model is None or ( + VertexGeminiConfig.supports_multimodal_function_responses(model) + ) try: while msg_i < len(messages): @@ -1029,6 +1032,7 @@ def gemini_convert_messages_with_history( messages[msg_i], last_message_with_tool_calls, forward_function_call_id=forward_function_call_id, + supports_multimodal_function_responses=supports_multimodal_function_responses, ) msg_i += 1 # Handle both single part and list of parts (for Computer Use with images) diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index d788ccb0f1d..9acbc9c459c 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -689,6 +689,10 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): ) -> bool: return cls._forward_gemini_function_call_id(model) + @classmethod + def supports_multimodal_function_responses(cls, model: str) -> bool: + return cls.is_gemini_3_or_newer(model) + def _supports_penalty_parameters(self, model: str) -> bool: # Gemini 3 models do not support penalty parameters if VertexGeminiConfig._is_gemini_3_or_newer(model): diff --git a/tests/unit/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py b/tests/unit/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py index b97724319c7..75449eacfd3 100644 --- a/tests/unit/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py +++ b/tests/unit/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py @@ -2,7 +2,7 @@ import base64 from collections.abc import Sequence from itertools import chain from pathlib import Path -from typing import Final +from typing import Final, Literal import httpx import pytest @@ -1650,10 +1650,28 @@ def test_convert_tool_response_with_base64_image(): assert inline_data["data"] == test_image_base64 -def test_gemini_history_nests_multimodal_tool_response_parts(): - """Full history conversion should not emit sibling inline_data tool result parts.""" - test_image_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" - messages = [ +@pytest.mark.parametrize("provider", ["gemini", "vertex_ai", "vertex_ai_beta"]) +@pytest.mark.parametrize( + "model,nested", + [ + (None, True), + ("gemini-2.5-pro", False), + ("gemini/gemini-2.5-flash", False), + ("vertex_ai/gemini-2.5-flash-lite", False), + ("123456789", False), + ("gemini-3.8-flash", True), + ("gemini/gemini-3.8-flash", True), + ("vertex_ai/gemini-3.8-flash", True), + ("gemini-flash-latest", True), + ], +) +def test_gemini_history_places_tool_media_for_model( + model: str | None, + nested: bool, + provider: Literal["gemini", "vertex_ai", "vertex_ai_beta"], +) -> None: + test_image_base64: Final = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + messages: Final = [ {"role": "user", "content": "Get me an image"}, { "role": "assistant", @@ -1679,24 +1697,53 @@ def test_gemini_history_nests_multimodal_tool_response_parts(): "data": test_image_base64, }, }, + { + "type": "file", + "file": {"file_data": "data:application/pdf;base64,JVBERi0xLjQ="}, + }, ], }, + {"role": "user", "content": "Describe the image and file again"}, ] - contents = gemini_convert_messages_with_history(messages=messages) - - tool_response_parts = contents[-1]["parts"] - assert len(tool_response_parts) == 1 - assert "inline_data" not in tool_response_parts[0] - function_response = tool_response_parts[0]["function_response"] - assert function_response["parts"] == [ + contents: Final = ( + gemini_convert_messages_with_history(messages=messages) + if model is None + else transform_request_body( + messages=messages, + model=model, + custom_llm_provider=provider, + optional_params={}, + litellm_params={}, + cached_content=None, + )["contents"] + ) + media_parts: Final = [ { "inline_data": { "data": test_image_base64, "mime_type": "image/png", } - } + }, + {"inline_data": {"data": "JVBERi0xLjQ=", "mime_type": "application/pdf"}}, ] + function_response: Final = { + "name": "get_image", + "response": {"image_ref": "inline"}, + **({"id": "call_get_image"} if model is not None and nested else {}), + **({"parts": media_parts} if nested else {}), + } + assert contents[-2] == { + "role": "user", + "parts": [ + {"function_response": function_response}, + *([] if nested else media_parts), + ], + } + assert contents[-1] == { + "role": "user", + "parts": [{"text": "Describe the image and file again"}], + } def test_convert_tool_response_text_only(): From 57ac10731149fbc2a5612a811c1dc15f6780dac1 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:28:53 -0700 Subject: [PATCH 26/73] ci: remove the Together AI and weekly cost map sync workflows (#48473) Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...to_update_price_and_context_window_file.py | 319 ---------- .../auto_update_price_and_context_window.yml | 36 -- .github/workflows/sync-together-ai-models.yml | 65 --- scripts/sync_together_ai_models.py | 547 ------------------ ...to_update_price_and_context_window_file.py | 222 ------- tests/unit/test_sync_together_ai_models.py | 378 ------------ 6 files changed, 1567 deletions(-) delete mode 100644 .github/scripts/auto_update_price_and_context_window_file.py delete mode 100644 .github/workflows/auto_update_price_and_context_window.yml delete mode 100644 .github/workflows/sync-together-ai-models.yml delete mode 100644 scripts/sync_together_ai_models.py delete mode 100644 tests/unit/test_auto_update_price_and_context_window_file.py delete mode 100644 tests/unit/test_sync_together_ai_models.py diff --git a/.github/scripts/auto_update_price_and_context_window_file.py b/.github/scripts/auto_update_price_and_context_window_file.py deleted file mode 100644 index 4c9a05bd4f0..00000000000 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ /dev/null @@ -1,319 +0,0 @@ -import asyncio -import aiohttp -import json -import math -from typing import Any - -# Asynchronously fetch data from a given URL -async def fetch_data(url): - try: - # Create an asynchronous session - async with aiohttp.ClientSession() as session: - # Send a GET request to the URL - async with session.get(url) as resp: - # Raise an error if the response status is not OK - resp.raise_for_status() - # Parse the response JSON - resp_json = await resp.json() - print("Fetch the data from URL.") - # Return the 'data' field from the JSON response - return resp_json['data'] - except Exception as e: - # Print an error message if fetching data fails - print("Error fetching data from URL:", e) - return None - - -FRIENDLI_API_URL = "https://api.friendli.ai/serverless/v1/models" -FRIENDLI_PROVIDER = "friendliai" - -INHERITABLE_BASE_KEYS = ( - "supports_pdf_input", - "supports_assistant_prefill", - "supports_adaptive_thinking", - "supports_output_config", -) - -REASONING_EFFORT_LEVEL_ORDER = ("none", "minimal", "low", "medium", "high", "xhigh", "max") - - -def _find_base_model_entry(base_model: str, local_data: dict) -> str | None: - if not base_model: - return None - bm_tail = base_model.split("/")[-1].lower() - if base_model in local_data: - return base_model - for key in local_data: - if key.startswith("sample_spec") or key == "fallback_generalizations": - continue - if key.split("/")[-1].lower() == bm_tail: - return key - return None - - -def _reasoning_effort_levels(reasoning_options: list) -> list: - offered = { - val - for opt in reasoning_options or [] - if opt.get("type") == "effort" - for val in opt.get("values", []) - } - return [level for level in REASONING_EFFORT_LEVEL_ORDER if level in offered] - - -def _valid_token_price(value: object) -> bool: - try: - price = float(value) # pyright: ignore[reportArgumentType] # non-numeric values are rejected via the except - except (TypeError, ValueError): - return False - return math.isfinite(price) and price >= 0 - - -def _has_valid_token_prices(pricing: dict | None) -> bool: - prices = pricing or {} - return _valid_token_price(prices.get("input")) and _valid_token_price(prices.get("output")) - - -def _pricing(pricing: dict) -> dict: - out: dict[str, Any] = {} - if not pricing: - return out - if "input" in pricing: - out["input_cost_per_token"] = float(pricing["input"]) - if "output" in pricing: - out["output_cost_per_token"] = float(pricing["output"]) - if "input_cache_read" in pricing and pricing["input_cache_read"] is not None: - out["cache_read_input_token_cost"] = float(pricing["input_cache_read"]) - return out - - -def _modality_flags(input_mods: list) -> dict: - mods = input_mods or [] - has_image = "image" in mods - return { - "supports_vision": has_image, - "supports_image_input": has_image, - "supports_video_input": "video" in mods, - } - - -def transform_friendli_data(data: list, local_data: dict) -> dict: - transformed: dict[str, dict] = {} - if not data: - return transformed - for model in data: - # An unpriced row must never wholesale-replace an already priced local entry: - # missing prices cost-calculate as zero, silently zeroing tracked spend - if not _has_valid_token_prices(model.get("pricing")): - continue - model_id = model["id"] - base_model = model.get("base_model") or "" - entry: dict[str, Any] = { - "litellm_provider": FRIENDLI_PROVIDER, - } - - base_key = _find_base_model_entry(base_model, local_data) - if base_key: - base_entry = local_data[base_key] - for k in INHERITABLE_BASE_KEYS: - if k in base_entry: - entry[k] = base_entry[k] - - ctx = model.get("context_length") - if ctx is not None: - entry["max_input_tokens"] = int(ctx) - max_out = model.get("max_completion_tokens") - if max_out is not None: - entry["max_output_tokens"] = int(max_out) - entry["max_tokens"] = int(max_out) - - pricing = _pricing(model.get("pricing", {})) - entry.update(pricing) - entry["supports_prompt_caching"] = "cache_read_input_token_cost" in pricing - - reasoning = model.get("reasoning") is True - entry["supports_reasoning"] = reasoning - if reasoning: - entry["reasoning_effort_levels"] = _reasoning_effort_levels( - model.get("reasoning_options", []) - ) - - func = model.get("functionality", {}) - entry["supports_function_calling"] = func.get("tool_call") is True - entry["supports_parallel_function_calling"] = func.get("parallel_tool_call") is True - is_struct = func.get("structured_output") is True - entry["supports_response_schema"] = is_struct - entry["supports_native_structured_output"] = is_struct - entry["supports_system_messages"] = func.get("system_messages") is True - entry["supports_tool_choice"] = func.get("tool_choice") is True - - entry.update(_modality_flags(model.get("input_modalities", []))) - - entry["mode"] = model.get("mode", "chat") - - desc = model.get("description") - if desc: - entry["comment"] = desc - - dep = model.get("deprecation_date") - if dep: - entry["deprecation_date"] = dep.split("T")[0] - - entry["source"] = FRIENDLI_API_URL - - transformed[f"{FRIENDLI_PROVIDER}/{model_id}"] = entry - return transformed - -# Synchronize local data with remote data -def sync_local_data_with_remote(local_data, remote_data, replace_keys=frozenset()): - # Update existing keys in local_data with values from remote_data - # (replace_keys entries are swapped wholesale so a field the remote catalog - # dropped, e.g. cache pricing, cannot survive as a stale value) - for key in (set(local_data) & set(remote_data)): - if key in replace_keys: - local_data[key] = remote_data[key] - else: - local_data[key].update(remote_data[key]) - - # Add new keys from remote_data to local_data - for key in (set(remote_data) - set(local_data)): - local_data[key] = remote_data[key] - -# Write data to the json file -def write_to_file(file_path, data): - try: - # Open the file in write mode - with open(file_path, "w") as file: - # Dump the data as JSON into the file - json.dump(data, file, indent=4) - print("Values updated successfully.") - except Exception as e: - # Print an error message if writing to file fails - print("Error updating JSON file:", e) - -# Update the existing models and add the missing models for OpenRouter -def transform_openrouter_data(data): - transformed = {} - if not data: - return transformed - for row in data: - # Add the fields 'max_tokens' and 'input_cost_per_token' - obj = { - "max_tokens": row["context_length"], - "input_cost_per_token": float(row["pricing"]["prompt"]), - } - - # Add 'max_output_tokens' as a field if it is not None - if "top_provider" in row and "max_completion_tokens" in row["top_provider"] and row["top_provider"]["max_completion_tokens"] is not None: - obj['max_output_tokens'] = int(row["top_provider"]["max_completion_tokens"]) - - # Add the field 'output_cost_per_token' - obj.update({ - "output_cost_per_token": float(row["pricing"]["completion"]), - }) - - # Add field 'input_cost_per_image' if it exists and is non-zero - if "pricing" in row and "image" in row["pricing"] and float(row["pricing"]["image"]) != 0.0: - obj['input_cost_per_image'] = float(row["pricing"]["image"]) - - # Add the fields 'litellm_provider' and 'mode' - obj.update({ - "litellm_provider": "openrouter", - "mode": "chat" - }) - - # Add the 'supports_vision' field if the modality is 'multimodal' - if row.get('architecture', {}).get('modality') == 'multimodal': - obj['supports_vision'] = True - - # Use a composite key to store the transformed object - transformed[f'openrouter/{row["id"]}'] = obj - - return transformed - -# Update the existing models and add the missing models for Vercel AI Gateway -def transform_vercel_ai_gateway_data(data): - transformed = {} - if not data: - return transformed - for row in data: - # Rows without token pricing or token limits (video/embedding models) previously KeyError'd the whole sync - if any(row.get(k) is None for k in ("context_window", "max_tokens")) or any( - row.get("pricing", {}).get(k) is None for k in ("input", "output") - ): - continue - obj = { - "max_tokens": row["context_window"], - "input_cost_per_token": float(row["pricing"]["input"]), - "output_cost_per_token": float(row["pricing"]["output"]), - 'max_output_tokens': row['max_tokens'], - 'max_input_tokens': row["context_window"], - } - - # Handle cache pricing if available - if "pricing" in row: - if "input_cache_read" in row["pricing"] and row["pricing"]["input_cache_read"] is not None: - obj['cache_read_input_token_cost'] = float(f"{float(row['pricing']['input_cache_read']):e}") - - if "input_cache_write" in row["pricing"] and row["pricing"]["input_cache_write"] is not None: - obj['cache_creation_input_token_cost'] = float(f"{float(row['pricing']['input_cache_write']):e}") - - mode = "embedding" if "embedding" in row["id"].lower() else "chat" - - obj.update({"litellm_provider": "vercel_ai_gateway", "mode": mode}) - - transformed[f'vercel_ai_gateway/{row["id"]}'] = obj - - return transformed - - -# Load local data from a specified file -def load_local_data(file_path): - try: - # Open the file in read mode - with open(file_path, "r") as file: - # Load and return the JSON data - return json.load(file) - except FileNotFoundError: - # Print an error message if the file is not found - print("File not found:", file_path) - return None - except json.JSONDecodeError as e: - # Print an error message if JSON decoding fails - print("Error decoding JSON:", e) - return None - -def main(): - local_file_path = "model_prices_and_context_window.json" # Path to the local data file - openrouter_url = "https://openrouter.ai/api/v1/models" # URL to fetch OpenRouter data - vercel_ai_gateway_url = "https://ai-gateway.vercel.sh/v1/models" # URL to fetch Vercel AI Gateway data - - # Load local data from file - local_data = load_local_data(local_file_path) - - # Fetch OpenRouter data - openrouter_data = asyncio.run(fetch_data(openrouter_url)) - # Transform the fetched OpenRouter data - openrouter_data = transform_openrouter_data(openrouter_data) - - # Fetch Vercel AI Gateway data - vercel_data = asyncio.run(fetch_data(vercel_ai_gateway_url)) - # Transform the fetched Vercel AI Gateway data - vercel_data = transform_vercel_ai_gateway_data(vercel_data) - - friendli_data = asyncio.run(fetch_data(FRIENDLI_API_URL)) - friendli_data = transform_friendli_data(friendli_data, local_data) - - # Combine both datasets - all_remote_data = {**openrouter_data, **vercel_data, **friendli_data} - - # If both local and openrouter data are available, synchronize and save - if local_data and all_remote_data: - sync_local_data_with_remote(local_data, all_remote_data, replace_keys=frozenset(friendli_data)) - write_to_file(local_file_path, local_data) - else: - print("Failed to fetch model data from either local file or URL.") - -# Entry point of the script -if __name__ == "__main__": - main() diff --git a/.github/workflows/auto_update_price_and_context_window.yml b/.github/workflows/auto_update_price_and_context_window.yml deleted file mode 100644 index 838ba35f831..00000000000 --- a/.github/workflows/auto_update_price_and_context_window.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Updates model_prices_and_context_window.json and Create Pull Request - -on: - schedule: - - cron: "0 0 * * 0" # Run every Sundays at midnight - #- cron: "0 0 * * *" # Run daily at midnight - -permissions: - contents: write - pull-requests: write - -jobs: - auto_update_price_and_context_window: - if: github.repository == 'BerriAI/litellm' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - persist-credentials: false - - name: Set up uv - uses: ./.github/actions/setup-uv-with-retries - with: - version: "0.10.9" - - name: Update JSON Data - run: | - uv run --frozen --with 'aiohttp==3.13.3' python ".github/scripts/auto_update_price_and_context_window_file.py" - - name: Create Pull Request - run: | - git add model_prices_and_context_window.json - git commit -m "Update model_prices_and_context_window.json file: $(date +'%Y-%m-%d')" - gh pr create --title "Update model_prices_and_context_window.json file" \ - --body "Automated update for model_prices_and_context_window.json" \ - --head auto-update-price-and-context-window-$(date +'%Y-%m-%d') \ - --base main - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/sync-together-ai-models.yml b/.github/workflows/sync-together-ai-models.yml deleted file mode 100644 index 5325510e45e..00000000000 --- a/.github/workflows/sync-together-ai-models.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Sync Together AI model registry - -on: - schedule: - - cron: "30 6 * * *" - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - sync_together_ai_models: - if: github.repository == 'BerriAI/litellm' - runs-on: ubuntu-latest - env: - BASE_BRANCH: ${{ github.event.repository.default_branch }} - steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - ref: ${{ env.BASE_BRANCH }} - persist-credentials: false - - name: Set up uv - uses: ./.github/actions/setup-uv-with-retries - with: - version: "0.10.9" - - name: Look for an already-open sync PR - id: existing - run: | - open_pr="$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --limit 1000 --json headRefName \ - --jq '[.[].headRefName | select(startswith("litellm_together_registry_sync_"))] | first // empty')" - echo "open_pr=$open_pr" >> "$GITHUB_OUTPUT" - if [ -n "$open_pr" ]; then - echo "An open sync PR already exists on branch $open_pr; skipping this run." - fi - env: - GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} - - name: Run the sync - if: steps.existing.outputs.open_pr == '' - run: | - uv run --frozen python scripts/sync_together_ai_models.py --write --pr-body-file "$RUNNER_TEMP/pr_body.md" - env: - TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }} - - name: Create a pull request when the registry changed - if: steps.existing.outputs.open_pr == '' - run: | - if git diff --quiet; then - echo "Registry already in sync; no PR needed." - exit 0 - fi - branch="litellm_together_registry_sync_$(date +'%Y-%m-%d')" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git checkout -b "$branch" - git add model_prices_and_context_window.json \ - litellm/model_prices_and_context_window_backup.json - git commit -m "feat(models): sync together_ai model registry $(date +'%Y-%m-%d')" - gh auth setup-git - git push origin "$branch" - gh pr create --title "feat(models): sync together_ai model registry" \ - --body-file "$RUNNER_TEMP/pr_body.md" \ - --head "$branch" \ - --base "$BASE_BRANCH" - env: - GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} diff --git a/scripts/sync_together_ai_models.py b/scripts/sync_together_ai_models.py deleted file mode 100644 index 12b128890f1..00000000000 --- a/scripts/sync_together_ai_models.py +++ /dev/null @@ -1,547 +0,0 @@ -"""Sync the together_ai entries of model_prices_and_context_window.json with Together's live serverless catalog. - -Pulls ``GET https://api.together.ai/v1/models?serverless`` plus the deprecations doc, maps API fields onto -registry fields, merges the reviewed capability rules below for everything the API cannot express, and diffs -the result against the registry. Dry run (the default) prints the diff summary and the generated PR body; -``--write`` applies the changes to the root cost map and its ``litellm/`` backup copy. - -Policy highlights: -- Prices arrive per 1M tokens with float artifacts and are normalized to clean per-token values. -- A registry entry absent from the serverless catalog is marked with ``deprecation_date`` from the docs - deprecation table, never deleted; absences with no docs date are surfaced for a human call. -- Availability comes from the API: a model the docs list as removed but the API still serves stays live, - with the conflict surfaced as a warning. -- Manually curated values the API cannot express (``metadata.successor``, ``max_output_tokens`` on existing - entries, capability flags no rule covers) are never overwritten; conflicts are surfaced instead. -""" - -import argparse -import json -import os -import re -import sys -from collections.abc import Mapping, Sequence -from dataclasses import dataclass, field -from pathlib import Path -from types import MappingProxyType -from typing import Final - -import httpx -from pydantic import BaseModel, TypeAdapter, ValidationError - -MODELS_URL: Final = "https://api.together.ai/v1/models?serverless" -DEPRECATIONS_URL: Final = "https://docs.together.ai/docs/deprecations.md" -PROVIDER: Final = "together_ai" -PREFIX: Final = "together_ai/" -SOURCE_URL: Final = "https://docs.together.ai/docs/serverless-models" -COST_MAP_RELPATHS: Final = ( - "model_prices_and_context_window.json", - "litellm/model_prices_and_context_window_backup.json", -) - -TYPE_TO_MODE: Final = MappingProxyType({"chat": "chat", "embedding": "embedding", "moderation": "chat"}) - - -class SyncError(RuntimeError): - pass - - -class CatalogPricing(BaseModel): - input: float - output: float - cached_input: float | None = None - - -class CatalogModel(BaseModel): - id: str - type: str - context_length: int | None = None - pricing: CatalogPricing - - -CATALOG_ADAPTER: Final = TypeAdapter(list[CatalogModel]) - -RegistryEntry = dict[str, object] -CostMap = dict[str, object] - - -@dataclass(frozen=True, slots=True) -class CapabilityRule: - model_id: str - fields: Mapping[str, bool | int] - provenance: str - - -def _rule(model_id: str, provenance: str, **fields: bool | int) -> CapabilityRule: - return CapabilityRule(model_id=model_id, fields=MappingProxyType(dict(fields)), provenance=provenance) - - -_TOOLS: Final = MappingProxyType( - { - "supports_function_calling": True, - "supports_parallel_function_calling": True, - "supports_response_schema": True, - "supports_tool_choice": True, - } -) - -CAPABILITY_RULES: Final = ( - _rule( - "MiniMaxAI/MiniMax-M3", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/minimax-m3", - **_TOOLS, - supports_reasoning=True, - supports_vision=True, - ), - _rule("Prism-ML/Ternary-Bonsai-27B", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "Qwen/Qwen3.5-9B", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/qwen3-5-9b", - **_TOOLS, - supports_reasoning=True, - supports_vision=True, - ), - _rule( - "Qwen/Qwen3.6-Plus", - "reviewed for the LIT-5968 backfill; hybrid reasoning model without a documented tools contract", - supports_reasoning=True, - ), - _rule("Qwen/Qwen3.7-Max", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule("Qwen/Qwen3.7-Plus", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule("Qwen/Qwen3.8-2.4T-A95B", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule("arize-ai/qwen-2-1.5b-instruct", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "deepseek-ai/DeepSeek-V4-Flash-0731", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/deepseek-v4-flash", - **_TOOLS, - ), - _rule( - "deepseek-ai/DeepSeek-V4-Pro", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/deepseek-v4-pro", - **_TOOLS, - supports_reasoning=True, - ), - _rule( - "deepseek-ai/DeepSeek-V4-Pro-0813", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/deepseek-v4-pro", - **_TOOLS, - ), - _rule("google/gemma-3n-E4B-it", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "google/gemma-4-31B-it", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/gemma-4-31b-it", - **_TOOLS, - supports_vision=True, - ), - _rule( - "intfloat/multilingual-e5-large-instruct", - "embedding dims per https://huggingface.co/intfloat/multilingual-e5-large-instruct", - output_vector_size=1024, - ), - _rule( - "meta-llama/Llama-3.3-70B-Instruct-Turbo", - "reviewed for the LIT-5968 backfill against https://docs.together.ai/docs/function-calling", - **_TOOLS, - ), - _rule( - "meta-llama/Llama-Guard-4-12B", - "moderation classifier with a chat-shaped API; no tools per the LIT-5968 backfill review", - ), - _rule("meta-models/Muse-Glimmer-30B", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "moonshotai/Kimi-K2.7-Code", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/kimi-k2-7-code", - **_TOOLS, - supports_vision=True, - ), - _rule( - "moonshotai/Kimi-K3", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/kimi-k3", - **_TOOLS, - supports_reasoning=True, - supports_vision=True, - ), - _rule( - "nvidia/nemotron-3-ultra-550b-a55b", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/nemotron-3-ultra", - **_TOOLS, - supports_reasoning=True, - ), - _rule( - "openai/gpt-oss-120b", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/gpt-oss-120b", - **_TOOLS, - supports_reasoning=True, - ), - _rule( - "openai/gpt-oss-20b", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/gpt-oss-20b", - **_TOOLS, - ), - _rule("pearl-ai/gemma-4-31b-it", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "thinkingmachines/Inkling", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/inkling", - **_TOOLS, - ), - _rule("thinkingmachines/Inkling-Small", "reviewed for the LIT-5968 backfill; no tool or vision support documented"), - _rule( - "zai-org/GLM-5.2", - "reviewed for the LIT-5968 backfill against https://www.together.ai/models/glm-5-2;" - " 128K output ceiling per https://docs.z.ai/guides/llm/glm-5.2", - **_TOOLS, - supports_reasoning=True, - max_output_tokens=128000, - max_tokens=128000, - ), - _rule( - "zai-org/GLM-5.3-Flash", - "reviewed for LIT-6489 against https://www.together.ai/models/glm-5-3-flash;" - " 128K output ceiling per https://docs.z.ai/guides/llm/glm-5.3", - **_TOOLS, - supports_reasoning=True, - supports_vision=True, - max_output_tokens=128000, - max_tokens=128000, - ), -) - -RULES_BY_ID: Final = MappingProxyType({rule.model_id: rule for rule in CAPABILITY_RULES}) - - -@dataclass(frozen=True, slots=True) -class DeprecationDoc: - removal_dates: Mapping[str, str] - redirects: Mapping[str, str] - - -_REDIRECT_ROW: Final = re.compile(r"^\|\s*`([^`]+)`\s*\|\s*`([^`]+)`\s*\|") -_REMOVAL_ROW: Final = re.compile(r"^\|\s*(\d{4}-\d{2}-\d{2})\s*\|\s*`([^`]+)`\s*\|") - - -def _section(markdown: str, heading: str) -> str: - level: Final = heading.split(" ", 1)[0] - start: Final = markdown.find(f"\n{heading}\n") - if start < 0: - return "" - body: Final = markdown[start + 1 + len(heading) :] - next_heading: Final = re.search(rf"^{re.escape(level)} ", body, flags=re.MULTILINE) - return body[: next_heading.start()] if next_heading else body - - -def parse_deprecations(markdown: str) -> DeprecationDoc: - redirect_rows: Final = tuple( - m.groups() - for m in (_REDIRECT_ROW.match(line) for line in _section(markdown, "## Active model redirects").splitlines()) - if m - ) - inference: Final = _section(_section(markdown, "## Deprecation history"), "### Inference") - removal_rows: Final = tuple(m.groups() for m in (_REMOVAL_ROW.match(line) for line in inference.splitlines()) if m) - if not redirect_rows or not removal_rows: - raise SyncError( - "deprecations doc parsed to zero redirect or removal rows; the table format at " - f"{DEPRECATIONS_URL} changed and the parser needs updating" - ) - removal_dates: Final = {model: date for date, model in reversed(removal_rows)} - return DeprecationDoc( - removal_dates=MappingProxyType(dict(reversed(removal_dates.items()))), - redirects=MappingProxyType({original: target for original, target in redirect_rows}), - ) - - -def per_token(price_per_million: float) -> float: - return float(f"{price_per_million / 1e6:.6g}") - - -def _resolve_name(name: str, universe: frozenset[str]) -> str | None: - if name in universe: - return name - suffix_matches: Final = tuple(candidate for candidate in universe if candidate.endswith(f"/{name}")) - return suffix_matches[0] if len(suffix_matches) == 1 else None - - -def resolve_successor(model_id: str, doc: DeprecationDoc, live_ids: frozenset[str]) -> str | None: - canonical: Final = live_ids | frozenset(doc.removal_dates) - redirects: Final = { - (_resolve_name(raw_source, canonical) or raw_source): (_resolve_name(raw_target, canonical) or raw_target) - for raw_source, raw_target in doc.redirects.items() - } - seen: Final = set() - current = model_id # rebind-ok: walks the redirect chain - while current in redirects and current not in seen: - seen.add(current) - current = redirects[current] # rebind-ok: walks the redirect chain - return current if current != model_id and current in live_ids else None - - -@dataclass(frozen=True, slots=True) -class SyncOutcome: - cost_map: CostMap - added: tuple[str, ...] = () - updated: tuple[str, ...] = () - deprecated: tuple[str, ...] = () - reappeared: tuple[str, ...] = () - warnings: tuple[str, ...] = () - skipped_types: Mapping[str, int] = field(default_factory=dict) - - @property - def has_changes(self) -> bool: - return bool(self.added or self.updated or self.deprecated or self.reappeared) - - -def _api_fields(model: CatalogModel) -> RegistryEntry: - cached: Final = model.pricing.cached_input - return { - "input_cost_per_token": per_token(model.pricing.input), - "output_cost_per_token": per_token(model.pricing.output), - **({"cache_read_input_token_cost": per_token(cached), "supports_prompt_caching": True} if cached else {}), - **({"max_input_tokens": model.context_length} if model.context_length is not None else {}), - } - - -def _new_entry(model: CatalogModel, mode: str) -> RegistryEntry: - rule: Final = RULES_BY_ID.get(model.id) - legacy_ceiling: Final = {} if model.context_length is None else {"max_tokens": model.context_length} - merged: Final = { - **_api_fields(model), - **legacy_ceiling, - "litellm_provider": PROVIDER, - "mode": mode, - "source": SOURCE_URL, - **(dict(rule.fields) if rule else {}), - } - return dict(sorted(merged.items())) - - -def _updated_entry(entry: RegistryEntry, model: CatalogModel) -> tuple[RegistryEntry, tuple[str, ...]]: - rule: Final = RULES_BY_ID.get(model.id) - desired: Final = {**_api_fields(model), **(dict(rule.fields) if rule else {})} - dropped: Final = () if model.pricing.cached_input else ("cache_read_input_token_cost", "supports_prompt_caching") - changes: Final = tuple( - f"{name}: {entry.get(name)!r} -> {value!r}" for name, value in desired.items() if entry.get(name) != value - ) + tuple( - f"{name}: {entry[name]!r} removed (no longer in the catalog pricing)" for name in dropped if name in entry - ) - merged: Final = {name: value for name, value in {**entry, **desired}.items() if name not in dropped} - return dict(sorted(merged.items())), changes - - -def _with_new_keys_in_block(original: CostMap, result: CostMap, new_keys: Sequence[str]) -> CostMap: - provider_keys: Final = tuple(key for key in original if key.startswith(PREFIX)) - if not new_keys or not provider_keys: - return result - block_end: Final = provider_keys[-1] - return { - key: value - for existing in original - for key, value in ( - (existing, result[existing]), - *((new, result[new]) for new in sorted(new_keys) if existing == block_end), - ) - } - - -def compute_sync(cost_map: CostMap, catalog: Sequence[CatalogModel], doc: DeprecationDoc) -> SyncOutcome: - live_ids: Final = frozenset(model.id for model in catalog) - token_models: Final = {model.id: model for model in catalog if model.type in TYPE_TO_MODE} - skipped: Final = { - model.type: sum(1 for m in catalog if m.type == model.type) - for model in catalog - if model.type not in TYPE_TO_MODE - } - registry_ids: Final = {key.removeprefix(PREFIX): key for key in cost_map if key.startswith(PREFIX)} - - added: Final[list[str]] = [] - updated: Final[list[str]] = [] - deprecated: Final[list[str]] = [] - reappeared: Final[list[str]] = [] - warnings: Final[list[str]] = [] - result: Final[CostMap] = dict(cost_map) - - for model_id, model in sorted(token_models.items()): - mode: Final = TYPE_TO_MODE[model.type] - key: Final = f"{PREFIX}{model_id}" - if model_id in doc.removal_dates: - warnings.append( - f"`{key}` is listed as removed on {doc.removal_dates[model_id]} in the docs but the serverless " - "catalog still serves it; availability kept from the API" - ) - entry = result.get(key) - if not isinstance(entry, dict): - result[key] = _new_entry(model, mode) - added.append(key) - if model.type == "chat" and model_id not in RULES_BY_ID: - warnings.append( - f"`{key}` added without a capability rule; review its tools/vision/reasoning support and add one" - ) - continue - if entry.get("mode") != mode: - warnings.append( - f"`{key}` has curated mode {entry.get('mode')!r} but the catalog maps to {mode!r}; left unchanged" - ) - new_entry, changes = _updated_entry(entry, model) - if "deprecation_date" in new_entry: - new_entry.pop("deprecation_date") - reappeared.append(key) - if changes: - updated.append(f"{key}: " + "; ".join(changes)) - if changes or key in reappeared: - result[key] = new_entry - - for model_id, key in sorted(registry_ids.items()): - if model_id in token_models: - continue - entry = result.get(key) - if not isinstance(entry, dict): - continue - removal_date: Final = doc.removal_dates.get(model_id) - successor: Final = resolve_successor(model_id, doc, live_ids) - metadata = entry.get("metadata") - curated_successor: Final = metadata.get("successor") if isinstance(metadata, dict) else None - new_entry = dict(entry) - if removal_date is not None and entry.get("deprecation_date") != removal_date: - if "deprecation_date" in entry: - warnings.append( - f"`{key}` has curated deprecation_date {entry.get('deprecation_date')!r} but the docs list " - f"{removal_date!r}; left unchanged" - ) - else: - new_entry["deprecation_date"] = removal_date - if removal_date is None and "deprecation_date" not in entry: - warnings.append( - f"`{key}` is absent from the serverless catalog with no removal date in the docs; " - "needs a human deprecation call" - ) - if successor is not None: - desired_successor: Final = f"{PREFIX}{successor}" - if curated_successor is None: - new_entry["metadata"] = dict( - sorted({**(metadata if isinstance(metadata, dict) else {}), "successor": desired_successor}.items()) - ) - elif curated_successor != desired_successor: - warnings.append( - f"`{key}` has curated successor {curated_successor!r} but the docs redirects resolve to " - f"{desired_successor!r}; left unchanged" - ) - if new_entry != entry: - result[key] = dict(sorted(new_entry.items())) - deprecated.append(f"{key}: " + ", ".join(sorted(set(new_entry) - set(entry)) or ["updated"])) - - return SyncOutcome( - cost_map=_with_new_keys_in_block(cost_map, result, tuple(added)), - added=tuple(added), - updated=tuple(updated), - deprecated=tuple(deprecated), - reappeared=tuple(reappeared), - warnings=tuple(warnings), - skipped_types=MappingProxyType(skipped), - ) - - -def _section_block(title: str, lines: Sequence[str], backtick: bool) -> str: - bullets: Final = "\n".join(f"- `{line}`" if backtick else f"- {line}" for line in lines) or "- none" - return f"### {title} ({len(lines)})\n{bullets}\n" - - -def render_pr_body(outcome: SyncOutcome) -> str: - skipped: Final = ", ".join(f"{kind} ({count})" for kind, count in sorted(outcome.skipped_types.items())) or "none" - return ( - "Automated daily sync of the together_ai entries in model_prices_and_context_window.json against " - f"`GET {MODELS_URL}` and {DEPRECATIONS_URL} by scripts/sync_together_ai_models.py.\n" - "\n" - f"{_section_block('Added', outcome.added, backtick=True)}" - "\n" - f"{_section_block('Updated', outcome.updated, backtick=True)}" - "\n" - f"{_section_block('Marked deprecated', outcome.deprecated, backtick=True)}" - "\n" - f"{_section_block('Returned to the catalog', outcome.reappeared, backtick=True)}" - "\n" - f"{_section_block('Warnings needing a human call', outcome.warnings, backtick=False)}" - "\n" - f"Catalog model types outside the sync's token-pricing scope, skipped: {skipped}\n" - ) - - -def render_summary(outcome: SyncOutcome) -> str: - return ( - f"added={len(outcome.added)} updated={len(outcome.updated)} deprecated={len(outcome.deprecated)} " - f"reappeared={len(outcome.reappeared)} warnings={len(outcome.warnings)}" - ) - - -def load_catalog(raw: bytes) -> list[CatalogModel]: - parsed: Final = json.loads(raw) - entries: Final = parsed.get("data") if isinstance(parsed, dict) else parsed - try: - catalog: Final = CATALOG_ADAPTER.validate_python(entries) - except ValidationError as error: - raise SyncError(f"the catalog response no longer matches the expected shape: {error}") from error - if not any(model.type in TYPE_TO_MODE for model in catalog): - raise SyncError( - "the catalog response contains no token-priced models; refusing to mark the whole registry deprecated" - ) - return catalog - - -def _fetch(url: str, headers: Mapping[str, str]) -> bytes: - response: Final = httpx.get(url, headers=dict(headers), timeout=30, follow_redirects=True) - if response.status_code != 200: - raise SyncError(f"GET {url} returned {response.status_code}") - return response.content - - -def _serialize(cost_map: CostMap) -> str: - return json.dumps(cost_map, indent=4, ensure_ascii=False) + "\n" - - -def main(argv: Sequence[str]) -> int: - parser: Final = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--write", action="store_true", help="apply the sync to the cost map files (default: dry run)") - parser.add_argument("--models-json", type=Path, help="recorded catalog response to use instead of the live API") - parser.add_argument( - "--deprecations-md", type=Path, help="recorded deprecations doc to use instead of the live docs" - ) - parser.add_argument("--pr-body-file", type=Path, help="write the generated PR body to this path") - parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parent.parent) - args: Final = parser.parse_args(argv) - - if args.models_json is not None: - catalog_raw: Final = args.models_json.read_bytes() - else: - api_key: Final = os.environ.get("TOGETHER_API_KEY") - if not api_key: - raise SyncError("TOGETHER_API_KEY is not set and --models-json was not given") - catalog_raw = _fetch(MODELS_URL, {"Authorization": f"Bearer {api_key}"}) # rebind-ok: branch-dependent source - catalog: Final = load_catalog(catalog_raw) - markdown: Final = ( - args.deprecations_md.read_text() if args.deprecations_md is not None else _fetch(DEPRECATIONS_URL, {}).decode() - ) - doc: Final = parse_deprecations(markdown) - - cost_map_path: Final = args.repo_root / COST_MAP_RELPATHS[0] - cost_map: Final = json.loads(cost_map_path.read_text()) - outcome: Final = compute_sync(cost_map, catalog, doc) - body: Final = render_pr_body(outcome) - - if args.pr_body_file is not None: - args.pr_body_file.write_text(body) - if args.write and outcome.has_changes: - for relpath in COST_MAP_RELPATHS: - (args.repo_root / relpath).write_text(_serialize(outcome.cost_map)) - print(render_summary(outcome)) - print() - print(body) - if not args.write: - print("dry run: no files were touched") - elif not outcome.has_changes: - print("registry already in sync: no files were touched") - return 0 - - -if __name__ == "__main__": - try: - raise SystemExit(main(sys.argv[1:])) - except SyncError as error: - print(f"SYNC FAILED: {error}", file=sys.stderr) - raise SystemExit(1) from error diff --git a/tests/unit/test_auto_update_price_and_context_window_file.py b/tests/unit/test_auto_update_price_and_context_window_file.py deleted file mode 100644 index 435747e9a09..00000000000 --- a/tests/unit/test_auto_update_price_and_context_window_file.py +++ /dev/null @@ -1,222 +0,0 @@ -"""Unit tests for the Friendli transform in -`.github/scripts/auto_update_price_and_context_window_file.py`.""" - -from __future__ import annotations - -import importlib.util -import sys -from pathlib import Path - -import pytest - -SCRIPT_PATH = ( - Path(__file__).resolve().parents[2] - / ".github" - / "scripts" - / "auto_update_price_and_context_window_file.py" -) - - -@pytest.fixture(scope="module") -def sync_module(): - spec = importlib.util.spec_from_file_location( - "auto_update_price_and_context_window_file", SCRIPT_PATH - ) - assert spec and spec.loader, f"Could not load spec for {SCRIPT_PATH}" - module = importlib.util.module_from_spec(spec) - sys.modules["auto_update_price_and_context_window_file"] = module - spec.loader.exec_module(module) - return module - - -def _reasoning_model(**overrides: object) -> dict: - model = { - "id": "zai-org/GLM-Test", - "base_model": "zhipuai/glm-test", - "context_length": 1048576, - "max_completion_tokens": 131072, - "pricing": {"input": "0.00000015", "output": "0.0000005", "input_cache_read": "0.00000003"}, - "reasoning": True, - "reasoning_options": [{"type": "effort", "values": ["max", "high", "low"]}], - "functionality": { - "tool_call": True, - "parallel_tool_call": True, - "structured_output": True, - "system_messages": True, - "tool_choice": True, - }, - "input_modalities": ["text", "image", "video"], - "mode": "chat", - } - model.update(overrides) - return model - - -def test_transform_emits_declared_effort_levels_in_canonical_order(sync_module): - entry = sync_module.transform_friendli_data([_reasoning_model()], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert entry["supports_reasoning"] is True - assert entry["reasoning_effort_levels"] == ["low", "high", "max"] - assert not any(k.endswith("_reasoning_effort") for k in entry) - - -def test_transform_reasoning_model_without_effort_options_declares_empty_levels(sync_module): - model = _reasoning_model(reasoning_options=[{"type": "budget_tokens", "values": []}]) - entry = sync_module.transform_friendli_data([model], {})["friendliai/zai-org/GLM-Test"] - assert entry["reasoning_effort_levels"] == [] - - -def test_transform_non_reasoning_model_declares_no_levels(sync_module): - model = _reasoning_model(reasoning=False, reasoning_options=[]) - entry = sync_module.transform_friendli_data([model], {})["friendliai/zai-org/GLM-Test"] - assert entry["supports_reasoning"] is False - assert "reasoning_effort_levels" not in entry - - -def test_transform_max_tokens_mirrors_output_cap_not_context(sync_module): - entry = sync_module.transform_friendli_data([_reasoning_model()], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert entry["max_input_tokens"] == 1048576 - assert entry["max_output_tokens"] == 131072 - assert entry["max_tokens"] == entry["max_output_tokens"] - - -def test_transform_prompt_caching_follows_cache_pricing(sync_module): - cached = sync_module.transform_friendli_data([_reasoning_model()], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert cached["supports_prompt_caching"] is True - assert cached["cache_read_input_token_cost"] == 3e-08 - - uncached_model = _reasoning_model(pricing={"input": "0.00000014", "output": "0.0000004"}) - uncached = sync_module.transform_friendli_data([uncached_model], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert uncached["supports_prompt_caching"] is False - assert "cache_read_input_token_cost" not in uncached - - -def test_transform_modalities_set_vision_image_and_video_flags(sync_module): - entry = sync_module.transform_friendli_data([_reasoning_model()], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert entry["supports_vision"] is True - assert entry["supports_image_input"] is True - assert entry["supports_video_input"] is True - - text_only = _reasoning_model(input_modalities=["text"]) - entry_text = sync_module.transform_friendli_data([text_only], {})[ - "friendliai/zai-org/GLM-Test" - ] - assert entry_text["supports_vision"] is False - assert entry_text["supports_image_input"] is False - assert entry_text["supports_video_input"] is False - - -def test_transform_skips_rows_without_valid_token_prices_so_priced_local_entries_survive(sync_module): - local = { - "friendliai/zai-org/GLM-Test": { - "litellm_provider": "friendliai", - "input_cost_per_token": 1.5e-07, - "output_cost_per_token": 5e-07, - } - } - unpriced_rows = [ - _reasoning_model(pricing={}), - _reasoning_model(pricing=None), - _reasoning_model(pricing={"input": "0.00000015"}), - _reasoning_model(pricing={"output": "0.0000005"}), - _reasoning_model(pricing={"input": "not-a-number", "output": "0.0000005"}), - _reasoning_model(pricing={"input": "-0.00000015", "output": "0.0000005"}), - _reasoning_model(pricing={"input": "inf", "output": "0.0000005"}), - _reasoning_model(pricing={"input": "nan", "output": "0.0000005"}), - ] - remote = sync_module.transform_friendli_data(unpriced_rows, local) - assert remote == {} - sync_module.sync_local_data_with_remote(local, remote, replace_keys=frozenset(remote)) - assert local["friendliai/zai-org/GLM-Test"]["input_cost_per_token"] == 1.5e-07 - assert local["friendliai/zai-org/GLM-Test"]["output_cost_per_token"] == 5e-07 - - -def test_transform_keeps_zero_priced_rows(sync_module): - free_model = _reasoning_model(pricing={"input": "0", "output": "0"}) - entry = sync_module.transform_friendli_data([free_model], {})["friendliai/zai-org/GLM-Test"] - assert entry["input_cost_per_token"] == 0.0 - assert entry["output_cost_per_token"] == 0.0 - - -def test_transforms_survive_failed_fetch(sync_module): - assert sync_module.transform_friendli_data(None, {}) == {} - assert sync_module.transform_friendli_data([], {}) == {} - assert sync_module.transform_openrouter_data(None) == {} - assert sync_module.transform_vercel_ai_gateway_data(None) == {} - - -def test_vercel_transform_skips_rows_without_token_pricing_or_limits(sync_module): - rows = [ - { - "id": "wan-video", - "pricing": {"video_duration_pricing": [{"resolution": "720p", "cost_per_second": "0.1"}]}, - }, - { - "id": "qwen3-embedding", - "context_window": 32768, - "max_tokens": 32768, - "pricing": {"input": "0.00000001"}, - }, - { - "id": "no-limits-chat", - "pricing": {"input": "0.000001", "output": "0.000002"}, - }, - { - "id": "good-chat", - "context_window": 128000, - "max_tokens": 8192, - "pricing": {"input": "0.000001", "output": "0.000002"}, - }, - ] - transformed = sync_module.transform_vercel_ai_gateway_data(rows) - assert list(transformed) == ["vercel_ai_gateway/good-chat"] - assert transformed["vercel_ai_gateway/good-chat"]["input_cost_per_token"] == 1e-06 - assert transformed["vercel_ai_gateway/good-chat"]["output_cost_per_token"] == 2e-06 - - -def test_sync_replaces_friendli_entries_so_dropped_cache_pricing_does_not_survive(sync_module): - local = { - "friendliai/zai-org/GLM-Test": { - "litellm_provider": "friendliai", - "cache_read_input_token_cost": 3e-08, - "supports_prompt_caching": True, - } - } - uncached_model = _reasoning_model(pricing={"input": "0.00000014", "output": "0.0000004"}) - remote = sync_module.transform_friendli_data([uncached_model], local) - sync_module.sync_local_data_with_remote(local, remote, replace_keys=frozenset(remote)) - synced = local["friendliai/zai-org/GLM-Test"] - assert "cache_read_input_token_cost" not in synced - assert synced["supports_prompt_caching"] is False - - -def test_sync_still_merges_entries_outside_replace_keys(sync_module): - local = {"openrouter/some-model": {"input_cost_per_token": 1e-06, "supports_vision": True}} - remote = {"openrouter/some-model": {"input_cost_per_token": 2e-06}} - sync_module.sync_local_data_with_remote(local, remote) - assert local["openrouter/some-model"] == {"input_cost_per_token": 2e-06, "supports_vision": True} - - -def test_transform_inherits_allowlisted_keys_from_base_model_entry(sync_module): - local = { - "zhipuai/glm-test": { - "supports_pdf_input": True, - "supports_assistant_prefill": True, - "input_cost_per_token": 9e-06, - } - } - entry = sync_module.transform_friendli_data([_reasoning_model()], local)[ - "friendliai/zai-org/GLM-Test" - ] - assert entry["supports_pdf_input"] is True - assert entry["supports_assistant_prefill"] is True - assert entry["input_cost_per_token"] == 1.5e-07 diff --git a/tests/unit/test_sync_together_ai_models.py b/tests/unit/test_sync_together_ai_models.py deleted file mode 100644 index b8a85bcfbdc..00000000000 --- a/tests/unit/test_sync_together_ai_models.py +++ /dev/null @@ -1,378 +0,0 @@ -import importlib.util -import json -from pathlib import Path -from types import MappingProxyType - -import pytest - -ROOT = Path(__file__).resolve().parents[2] -SCRIPT = ROOT / "scripts" / "sync_together_ai_models.py" -FIXTURES = Path(__file__).resolve().parent / "fixtures" / "together_ai_sync" - -_spec = importlib.util.spec_from_file_location("sync_together_ai_models", SCRIPT) -assert _spec is not None and _spec.loader is not None -sync = importlib.util.module_from_spec(_spec) -_spec.loader.exec_module(sync) - -RECORDED_CATALOG = sync.load_catalog(FIXTURES.joinpath("models_serverless.json").read_bytes()) -RECORDED_DOC = sync.parse_deprecations(FIXTURES.joinpath("deprecations.md").read_text()) - - -def _doc(removal_dates: dict[str, str], redirects: dict[str, str] | None = None) -> object: - return sync.DeprecationDoc( - removal_dates=MappingProxyType(removal_dates), - redirects=MappingProxyType(redirects or {}), - ) - - -def _chat_model(model_id: str, ctx: int = 4096, price: float = 1.0, cached: float | None = None) -> object: - return sync.CatalogModel( - id=model_id, - type="chat", - context_length=ctx, - pricing=sync.CatalogPricing(input=price, output=price, cached_input=cached), - ) - - -@pytest.mark.parametrize( - ("per_million", "expected"), - [ - (3, 3e-06), - (15, 1.5e-05), - (1.4, 1.4e-06), - (0.25999999999999995, 2.6e-07), - (0.060000000000000005, 6e-08), - (1.0399999999999998, 1.04e-06), - (0, 0.0), - ], -) -def test_per_token_normalizes_float_artifacts(per_million: float, expected: float) -> None: - assert sync.per_token(per_million) == expected - - -def test_parse_deprecations_recorded_fixture() -> None: - assert dict(RECORDED_DOC.redirects) == { - "mistralai/Mistral-7B-Instruct-v0.3": "mistralai/Ministral-3-14B-Instruct-2512", - "Kimi-K2": "Kimi-K2-0905", - "DeepSeek-V3": "DeepSeek-V3.1", - "DeepSeek-V3-0324": "DeepSeek-V3.1", - "DeepSeek-R1": "DeepSeek-R1-0528", - } - assert len(RECORDED_DOC.removal_dates) == 208 - assert RECORDED_DOC.removal_dates["google/gemma-3n-E4B-it"] == "2026-08-04" - - -def test_parse_deprecations_duplicate_rows_keep_most_recent_date() -> None: - assert RECORDED_DOC.removal_dates["Qwen/Qwen3-235B-A22B-Thinking-2507"] == "2026-04-16" - - -@pytest.mark.parametrize( - "markdown", - [ - "# Deprecations\n\nNothing here anymore.\n", - "\n## Active model redirects\n\n| A | B |\n| --- | --- |\n| `x` | `y` |\n\n## Something else\n", - "\n## Deprecation history\n\n### Inference\n\n| Date | Model | R |\n| --- | --- | --- |\n| 2026-01-01 | `m` | No |\n", - ], -) -def test_parse_deprecations_raises_when_a_table_parses_empty(markdown: str) -> None: - with pytest.raises(sync.SyncError): - sync.parse_deprecations(markdown) - - -def test_load_catalog_raises_on_shape_change() -> None: - with pytest.raises(sync.SyncError): - sync.load_catalog(b'[{"id": "x", "type": "chat"}]') - - -def test_load_catalog_raises_when_no_token_models_remain() -> None: - only_video = json.dumps([{"id": "v", "type": "video", "pricing": {"input": 0, "output": 0}}]).encode() - with pytest.raises(sync.SyncError): - sync.load_catalog(only_video) - - -def test_recorded_catalog_counts() -> None: - assert len(RECORDED_CATALOG) == 102 - assert sum(1 for model in RECORDED_CATALOG if model.type in sync.TYPE_TO_MODE) == 26 - assert sum(1 for model in RECORDED_CATALOG if model.pricing.cached_input) == 13 - - -def test_added_chat_model_matches_reviewed_registry_shape() -> None: - outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) - assert len(outcome.added) == 26 - assert not outcome.deprecated - assert outcome.cost_map["together_ai/moonshotai/Kimi-K3"] == { - "cache_read_input_token_cost": 3e-07, - "input_cost_per_token": 3e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 1048576, - "max_tokens": 1048576, - "mode": "chat", - "output_cost_per_token": 1.5e-05, - "source": "https://docs.together.ai/docs/serverless-models", - "supports_function_calling": True, - "supports_parallel_function_calling": True, - "supports_prompt_caching": True, - "supports_reasoning": True, - "supports_response_schema": True, - "supports_tool_choice": True, - "supports_vision": True, - } - - -def test_added_embedding_model_has_no_output_token_cap() -> None: - outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) - assert outcome.cost_map["together_ai/intfloat/multilingual-e5-large-instruct"] == { - "input_cost_per_token": 2e-08, - "litellm_provider": "together_ai", - "max_input_tokens": 514, - "max_tokens": 514, - "mode": "embedding", - "output_cost_per_token": 2e-08, - "output_vector_size": 1024, - "source": "https://docs.together.ai/docs/serverless-models", - } - - -def test_moderation_type_maps_to_chat_mode() -> None: - outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) - guard = outcome.cost_map["together_ai/meta-llama/Llama-Guard-4-12B"] - assert guard["mode"] == "chat" - assert "max_output_tokens" not in guard - - -def test_output_ceiling_comes_from_the_rule_never_from_context_length() -> None: - glm = next(model for model in RECORDED_CATALOG if model.id == "zai-org/GLM-5.2") - fresh = sync.compute_sync({}, [_chat_model("acme/unreviewed", ctx=1048576), glm], _doc({"x": "2026-01-01"})) - unreviewed = fresh.cost_map["together_ai/acme/unreviewed"] - assert "max_output_tokens" not in unreviewed - assert (unreviewed["max_input_tokens"], unreviewed["max_tokens"]) == (1048576, 1048576) - reviewed = fresh.cost_map["together_ai/zai-org/GLM-5.2"] - assert (reviewed["max_input_tokens"], reviewed["max_output_tokens"], reviewed["max_tokens"]) == ( - 1048575, - 128000, - 128000, - ) - inflated = { - "together_ai/zai-org/GLM-5.2": { - "input_cost_per_token": 1.4e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 1048575, - "max_output_tokens": 1048575, - "max_tokens": 1048575, - "mode": "chat", - "output_cost_per_token": 4.4e-06, - } - } - corrected = sync.compute_sync(inflated, [glm], _doc({"x": "2026-01-01"})) - assert corrected.cost_map["together_ai/zai-org/GLM-5.2"]["max_output_tokens"] == 128000 - assert any("max_output_tokens: 1048575 -> 128000" in line for line in corrected.updated) - - -def test_docs_removed_but_live_model_stays_live_with_warning() -> None: - outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) - gemma = outcome.cost_map["together_ai/google/gemma-3n-E4B-it"] - assert "deprecation_date" not in gemma - assert any("gemma-3n-E4B-it" in warning and "2026-08-04" in warning for warning in outcome.warnings) - - -def test_price_change_updates_api_fields_and_keeps_curated_ones() -> None: - registry = { - "together_ai/acme/chat-1": { - "input_cost_per_token": 9e-07, - "litellm_provider": "together_ai", - "max_input_tokens": 4096, - "max_output_tokens": 2048, - "max_tokens": 2048, - "mode": "chat", - "output_cost_per_token": 9e-07, - "supports_audio_input": True, - } - } - outcome = sync.compute_sync(registry, [_chat_model("acme/chat-1", ctx=8192, price=2.0)], _doc({"x": "2026-01-01"})) - entry = outcome.cost_map["together_ai/acme/chat-1"] - assert entry["input_cost_per_token"] == 2e-06 - assert entry["max_input_tokens"] == 8192 - assert entry["max_output_tokens"] == 2048 - assert entry["supports_audio_input"] is True - assert len(outcome.updated) == 1 - assert "input_cost_per_token" in outcome.updated[0] - - -def test_cached_input_appearing_and_disappearing() -> None: - registry = { - "together_ai/acme/chat-1": { - "cache_read_input_token_cost": 1e-07, - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 4096, - "mode": "chat", - "output_cost_per_token": 1e-06, - "supports_prompt_caching": True, - }, - "together_ai/acme/chat-2": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 4096, - "mode": "chat", - "output_cost_per_token": 1e-06, - }, - } - catalog = [_chat_model("acme/chat-1"), _chat_model("acme/chat-2", cached=0.25999999999999995)] - outcome = sync.compute_sync(registry, catalog, _doc({"x": "2026-01-01"})) - assert "cache_read_input_token_cost" not in outcome.cost_map["together_ai/acme/chat-1"] - assert "supports_prompt_caching" not in outcome.cost_map["together_ai/acme/chat-1"] - assert outcome.cost_map["together_ai/acme/chat-2"]["cache_read_input_token_cost"] == 2.6e-07 - assert outcome.cost_map["together_ai/acme/chat-2"]["supports_prompt_caching"] is True - - -def test_capability_rule_backfills_existing_entry() -> None: - registry = { - "together_ai/moonshotai/Kimi-K3": { - "input_cost_per_token": 3e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 1048576, - "mode": "chat", - "output_cost_per_token": 1.5e-05, - } - } - kimi = next(model for model in RECORDED_CATALOG if model.id == "moonshotai/Kimi-K3") - outcome = sync.compute_sync(registry, [kimi], _doc({"x": "2026-01-01"})) - assert outcome.cost_map["together_ai/moonshotai/Kimi-K3"]["supports_reasoning"] is True - assert any("supports_reasoning" in line for line in outcome.updated) - - -def test_disappeared_model_gets_docs_date_and_is_never_deleted() -> None: - registry = { - "together_ai/acme/gone": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "mode": "chat", - "output_cost_per_token": 1e-06, - } - } - outcome = sync.compute_sync(registry, [_chat_model("acme/alive")], _doc({"acme/gone": "2026-07-01"})) - assert outcome.cost_map["together_ai/acme/gone"]["deprecation_date"] == "2026-07-01" - assert outcome.deprecated == ("together_ai/acme/gone: deprecation_date",) - - -def test_disappeared_model_without_docs_date_warns_instead() -> None: - registry = { - "together_ai/acme/gone": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "mode": "chat", - "output_cost_per_token": 1e-06, - } - } - outcome = sync.compute_sync(registry, [_chat_model("acme/alive")], _doc({"other": "2026-07-01"})) - assert "deprecation_date" not in outcome.cost_map["together_ai/acme/gone"] - assert not outcome.deprecated - assert any("acme/gone" in warning and "human" in warning for warning in outcome.warnings) - - -def test_curated_deprecation_date_is_never_overwritten() -> None: - registry = { - "together_ai/acme/gone": { - "deprecation_date": "2026-06-15", - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "mode": "chat", - "output_cost_per_token": 1e-06, - } - } - outcome = sync.compute_sync(registry, [_chat_model("acme/alive")], _doc({"acme/gone": "2026-07-01"})) - assert outcome.cost_map["together_ai/acme/gone"]["deprecation_date"] == "2026-06-15" - assert any("2026-06-15" in warning and "2026-07-01" in warning for warning in outcome.warnings) - - -def test_redirect_chain_resolves_to_final_live_model() -> None: - doc = _doc({"acme/a": "2026-01-01"}, redirects={"acme/a": "acme/b", "acme/b": "acme/c"}) - live = frozenset({"acme/c"}) - assert sync.resolve_successor("acme/a", doc, live) == "acme/c" - - -def test_redirect_dead_end_yields_no_successor() -> None: - doc = _doc({"acme/a": "2026-01-01"}, redirects={"acme/a": "acme/b"}) - assert sync.resolve_successor("acme/a", doc, frozenset({"acme/other"})) is None - - -def test_redirect_short_names_resolve_by_unique_suffix() -> None: - doc = _doc({"moonshotai/Kimi-K2": "2026-01-01"}, redirects={"Kimi-K2": "Kimi-K2-0905"}) - live = frozenset({"moonshotai/Kimi-K2-0905"}) - assert sync.resolve_successor("moonshotai/Kimi-K2", doc, live) == "moonshotai/Kimi-K2-0905" - - -def test_successor_written_only_when_not_curated() -> None: - registry = { - "together_ai/acme/a": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "mode": "chat", - "output_cost_per_token": 1e-06, - }, - "together_ai/acme/b": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "metadata": {"successor": "together_ai/acme/curated"}, - "mode": "chat", - "output_cost_per_token": 1e-06, - }, - } - doc = _doc({"acme/a": "2026-01-01", "acme/b": "2026-01-01"}, redirects={"acme/a": "acme/c", "acme/b": "acme/c"}) - outcome = sync.compute_sync(registry, [_chat_model("acme/c")], doc) - assert outcome.cost_map["together_ai/acme/a"]["metadata"] == {"successor": "together_ai/acme/c"} - assert outcome.cost_map["together_ai/acme/b"]["metadata"] == {"successor": "together_ai/acme/curated"} - assert any("acme/curated" in warning for warning in outcome.warnings) - - -def test_reappearance_clears_deprecation_date() -> None: - registry = { - "together_ai/acme/back": { - "deprecation_date": "2026-05-01", - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "max_input_tokens": 4096, - "mode": "chat", - "output_cost_per_token": 1e-06, - } - } - outcome = sync.compute_sync(registry, [_chat_model("acme/back")], _doc({"x": "2026-01-01"})) - assert "deprecation_date" not in outcome.cost_map["together_ai/acme/back"] - assert outcome.reappeared == ("together_ai/acme/back",) - - -def test_new_chat_model_without_rule_is_flagged() -> None: - outcome = sync.compute_sync({}, [_chat_model("acme/unreviewed")], _doc({"x": "2026-01-01"})) - assert any("acme/unreviewed" in warning and "capability rule" in warning for warning in outcome.warnings) - - -def test_new_keys_land_at_the_end_of_the_provider_block() -> None: - registry = { - "aaa": {"mode": "chat"}, - "together_ai/acme/old": { - "input_cost_per_token": 1e-06, - "litellm_provider": "together_ai", - "mode": "chat", - "output_cost_per_token": 1e-06, - }, - "zzz": {"mode": "chat"}, - } - outcome = sync.compute_sync(registry, [_chat_model("acme/old"), _chat_model("acme/new")], _doc({"x": "2026-01-01"})) - assert list(outcome.cost_map) == ["aaa", "together_ai/acme/old", "together_ai/acme/new", "zzz"] - - -def test_sync_is_idempotent_over_the_repo_cost_map() -> None: - cost_map = json.loads((ROOT / "model_prices_and_context_window.json").read_text()) - first = sync.compute_sync(cost_map, RECORDED_CATALOG, RECORDED_DOC) - second = sync.compute_sync(first.cost_map, RECORDED_CATALOG, RECORDED_DOC) - assert not second.has_changes - assert second.cost_map == first.cost_map - - -def test_pr_body_lists_every_section_and_the_skipped_types() -> None: - outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) - body = sync.render_pr_body(outcome) - assert "### Added (26)" in body - assert "### Warnings needing a human call" in body - assert "image (29)" in body - assert "video (38)" in body From 8b1910c95ae7f9c47135cb41c1e7bbb2700f36e5 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 18:30:05 -0400 Subject: [PATCH 27/73] fix(mcp): advertise valid OAuth resources for toolset URLs (#48474) --- .../mcp_server/auth/user_api_key_auth_mcp.py | 8 +- litellm/proxy/proxy_server.py | 3 +- tests/integration/mcp/test_mcp_oauth_flows.py | 92 ++++++++++++++++--- .../auth/test_user_api_key_auth_mcp.py | 41 +++++++++ 4 files changed, 129 insertions(+), 15 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index 824633bdf5a..f883031a529 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -27,6 +27,7 @@ from litellm.proxy._experimental.mcp_server.oauth_utils import ( get_passthrough_resource_metadata_url, get_passthrough_www_authenticate, get_request_base_url, + get_route_relative_request_path, well_known_root_suffix, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( @@ -387,7 +388,7 @@ def _gateway_dcr_challenge( """The RFC 9728 challenge pointing the client at the protected-resource metadata matching the scope it requested: the per-server document (same URL spelling the request arrived on) when the single target advertises gateway-owned sign-in, - else the gateway's aggregate document. Either way the client discovers the gateway + else a gateway document containing the public resource. Either way the client discovers the gateway as its authorization server and starts the same sign-in flow. ``invalid_token`` adds the RFC 6750 error code for a request that DID @@ -397,10 +398,13 @@ def _gateway_dcr_challenge( client_ip: Final = IPAddressUtils.get_mcp_client_ip(request) target: Final = _gateway_dcr_challenge_target(route, mcp_servers, client_ip) server_scope: Final = gateway_dcr_challenge_scope(route, mcp_servers, client_ip) if target is None else None + public_path: Final = get_route_relative_request_path(request.scope).rstrip("/") + aggregate_resource: Final = public_path == "/mcp" or public_path.startswith("/mcp/") + metadata_suffix: Final = f"{well_known_root_suffix()}/mcp" if aggregate_resource else "" resource_metadata_url: Final = ( get_passthrough_resource_metadata_url(request.scope, target) if target is not None - else f"{get_request_base_url(request)}/.well-known/oauth-protected-resource{well_known_root_suffix()}/mcp" + else f"{get_request_base_url(request)}/.well-known/oauth-protected-resource{metadata_suffix}" ) error_attr: Final = 'error="invalid_token", ' if invalid_token else "" scope_attr: Final = f', scope="{server_scope}"' if server_scope is not None else "" diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index df948b69a67..129cd7e1589 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -21795,8 +21795,7 @@ async def toolset_mcp_route(toolset_name: str, request: Request): detail=f"Toolset '{toolset_name}' not found", ) - scope: Final = dict(request.scope) - scope["path"] = "/mcp" + scope: Final = {**request.scope, "_original_path": request.scope.get("path", ""), "path": "/mcp"} token: Final = mcp_active_toolset_id.set(toolset.toolset_id) try: diff --git a/tests/integration/mcp/test_mcp_oauth_flows.py b/tests/integration/mcp/test_mcp_oauth_flows.py index 327b053d333..90a462472b2 100644 --- a/tests/integration/mcp/test_mcp_oauth_flows.py +++ b/tests/integration/mcp/test_mcp_oauth_flows.py @@ -792,17 +792,23 @@ def _ui_session_cookie(gateway: Gateway, user_id: str) -> dict[str, str]: def _gateway_session_sign_in( - gateway: Gateway, user_id: str, resource: str | None = None + gateway: Gateway, + user_id: str, + resource: str | None = None, + *, + registration_endpoint: str = "/register", + authorization_endpoint: str = "/authorize", + token_endpoint: str = "/token", ) -> tuple[dict[str, object], str]: registered: Final = gateway.client.post( - "/register", json={"redirect_uris": [CLIENT_REDIRECT], "client_name": "integration"} + registration_endpoint, json={"redirect_uris": [CLIENT_REDIRECT], "client_name": "integration"} ) assert registered.status_code in (200, 201), registered.text client_id: Final = str(_response_object(registered)["client_id"]) pkce: Final = _Pkce(secrets.token_urlsafe(48)) cookies: Final = _ui_session_cookie(gateway, user_id) started: Final = gateway.client.get( - "/authorize", + authorization_endpoint, params={ "client_id": client_id, "redirect_uri": CLIENT_REDIRECT, @@ -823,13 +829,14 @@ def _gateway_session_sign_in( callback: Final = parse_qs(urlsplit(completed.headers["location"]).query) assert "code" in callback, completed.headers["location"] issued: Final = gateway.client.post( - "/token", + token_endpoint, data={ "grant_type": "authorization_code", "code": callback["code"][0], "redirect_uri": CLIENT_REDIRECT, "client_id": client_id, "code_verifier": pkce.verifier, + **({} if resource is None else {"resource": resource}), }, ) assert issued.status_code == 200, issued.text @@ -1292,10 +1299,18 @@ def test_lite_logout_revokes_the_cli_refresh_token_sent_the_way_the_cli_sends_it assert completion.status_code == 200, completion.text -def _toolset_rpc(gateway: Gateway, bearer: str, name: str, method: str, params: dict[str, object]) -> Outcome: +def _toolset_rpc( + gateway: Gateway, + bearer: str, + name: str, + method: str, + params: dict[str, object], + *, + route: str = "/toolset/{name}/mcp", +) -> Outcome: def post(rpc_method: str, rpc_params: dict[str, object]) -> httpx.Response: return gateway.client.post( - f"/toolset/{name}/mcp", + route.format(name=name), json={"jsonrpc": "2.0", "id": 1, "method": rpc_method, "params": rpc_params}, headers={"Authorization": f"Bearer {bearer}", "Accept": "application/json, text/event-stream"}, ) @@ -1306,7 +1321,12 @@ def _toolset_rpc(gateway: Gateway, bearer: str, name: str, method: str, params: return _outcome_from_rpc(post(method, params)) -def test_gateway_session_bearer_of_a_team_member_is_served_the_team_toolset_on_its_route(gateway: Gateway) -> None: +@pytest.mark.parametrize("route", ["/toolset/{name}/mcp", "/{name}/mcp"]) +def test_gateway_session_bearer_of_a_team_member_is_served_the_team_toolset_on_its_route( + gateway: Gateway, route: str +) -> None: + from mcp.shared.auth_utils import check_resource_allowed + with mcp_peer() as peer, gateway.scenario() as scenario: alias: Final = "lit6029sess" + uuid.uuid4().hex[:6] server_id: Final = register_mcp(scenario, peer, alias) @@ -1315,19 +1335,69 @@ def test_gateway_session_bearer_of_a_team_member_is_served_the_team_toolset_on_i granted_id: Final = create_toolset(scenario, ((server_id, "add"),), toolset_name=granted_name) create_toolset(scenario, ((server_id, "multiply"),), toolset_name=withheld_name) member: Final = scenario.member(scenario.team(object_permission={"mcp_toolsets": [granted_id]})) - bearer: Final = _gateway_session_bearer(gateway, member) + path: Final = route.format(name=granted_name) + anonymous: Final = gateway.client.post( + path, + headers=ACCEPT, + json={"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": dict(INITIALIZE)}, + ) + assert anonymous.status_code == 401, anonymous.text + metadata_url: Final = anonymous.headers["www-authenticate"].split('resource_metadata="', 1)[1].split('"', 1)[0] + protected: Final = gateway.client.get(metadata_url) + assert protected.status_code == 200, protected.text + protected_body: Final = _response_object(protected) + resource: Final = str(protected_body["resource"]) + assert check_resource_allowed( + requested_resource=f"{_base(gateway)}{path}", configured_resource=resource + ), protected.text + issuers: Final = TypeAdapter(tuple[str, ...]).validate_python(protected_body["authorization_servers"]) + assert len(issuers) == 1, protected.text + issuer: Final = urlsplit(issuers[0]) + authorization: Final = gateway.client.get( + f"{issuer.scheme}://{issuer.netloc}/.well-known/oauth-authorization-server{issuer.path}" + ) + assert authorization.status_code == 200, authorization.text + authorization_body: Final = _response_object(authorization) + assert authorization_body["issuer"] == issuers[0], authorization.text + token_endpoint: Final = str(authorization_body["token_endpoint"]) + issued, client_id = _gateway_session_sign_in( + gateway, + member, + resource, + registration_endpoint=str(authorization_body["registration_endpoint"]), + authorization_endpoint=str(authorization_body["authorization_endpoint"]), + token_endpoint=token_endpoint, + ) + bearer: Final = _issued_token(issued) assert bearer.startswith("llm_session_"), bearer[:16] - listed: Final = _toolset_rpc(gateway, bearer, granted_name, "tools/list", {}) + listed: Final = _toolset_rpc(gateway, bearer, granted_name, "tools/list", {}, route=route) assert listed.tools == (f"{alias}-add",), listed.raw peer.drain() called: Final = _toolset_rpc( - gateway, bearer, granted_name, "tools/call", {"name": f"{alias}-add", "arguments": {"a": 4, "b": 5}} + gateway, bearer, granted_name, "tools/call", + {"name": f"{alias}-add", "arguments": {"a": 4, "b": 5}}, route=route ) assert called.ok and called.text == "9", called.raw assert len(tool_calls(peer.drain())) == 1 - denied: Final = _toolset_rpc(gateway, bearer, withheld_name, "tools/list", {}) + denied: Final = _toolset_rpc(gateway, bearer, withheld_name, "tools/list", {}, route=route) assert denied.status == 403, denied.raw assert tool_calls(peer.drain()) == () + rotated: Final = gateway.client.post( + token_endpoint, + data={ + "grant_type": "refresh_token", + "refresh_token": str(issued["refresh_token"]), + "client_id": client_id, + "resource": resource, + }, + ) + assert rotated.status_code == 200, rotated.text + rotated_body: Final = _response_object(rotated) + assert rotated_body["refresh_token"] != issued["refresh_token"], rotated.text + refreshed: Final = _toolset_rpc( + gateway, _issued_token(rotated_body), granted_name, "tools/list", {}, route=route + ) + assert refreshed.tools == listed.tools, refreshed.raw def test_resource_scoped_session_bearer_opens_a_team_toolset_inside_its_server_and_none_outside( diff --git a/tests/unit/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/unit/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index e8689b2ba89..16130364364 100644 --- a/tests/unit/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/unit/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -7693,6 +7693,47 @@ class TestMCPDcrBridgeDualCredential: assert "invalid_token" in str(exc_info.value.headers) +@pytest.mark.parametrize("public_path", ["/toolset/analysis/mcp", "/analysis/mcp", "/mcp", "/mcp/", "/mcp/proxy"]) +@pytest.mark.parametrize("root_path", ["", "/litellm"]) +@pytest.mark.parametrize("invalid_token", [False, True]) +def test_gateway_challenge_metadata_contains_the_public_resource( + public_path: str, root_path: str, invalid_token: bool, +) -> None: + from mcp.shared.auth_utils import check_resource_allowed + from starlette.requests import Request + from starlette.types import Scope + + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import _gateway_dcr_challenge + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_aggregate_protected_resource_response, + oauth_protected_resource_root, + ) + + scope: Final[Scope] = { + "type": "http", "method": "POST", "scheme": "https", "path": "/mcp", + "root_path": root_path, "_original_path": f"{root_path}{public_path}", + "headers": [(b"host", b"gateway.example.com")], + } + request: Final = Request(scope) + challenge: Final = _gateway_dcr_challenge(request, "/mcp", None, invalid_token) + header: Final = (challenge.headers or {})["WWW-Authenticate"] + metadata_url: Final = header.split('resource_metadata="', 1)[1].split('"', 1)[0] + toolset: Final = public_path in ("/toolset/analysis/mcp", "/analysis/mcp") + assert metadata_url == ( + f"https://gateway.example.com{root_path}/.well-known/oauth-protected-resource" + + ("" if toolset else "/mcp") + ) + metadata: Final = ( + oauth_protected_resource_root(request) if toolset else _build_aggregate_protected_resource_response(request) + ) + assert check_resource_allowed( + requested_resource=f"https://gateway.example.com{root_path}{public_path}", + configured_resource=metadata["resource"], + ) + assert challenge.status_code == 401 + assert ('error="invalid_token"' in header) is invalid_token + + @pytest.mark.asyncio class TestAggregateGatewayDcrChallenge: """The mcp_gateway_dcr front door: a 401 on the aggregate /mcp scope must From 48a893aefc1f4cee13f41d2b6f5bde8b382aa6b9 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 26 Oct 2026 15:38:17 -0700 Subject: [PATCH 28/73] build(deps): drop expired exclude-newer pins for certifi, mlflow and pypdf (#48461) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- pyproject.toml | 8 -------- uv.lock | 5 ----- 2 files changed, 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 88bb0b13d91..fc860a8fdf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -377,16 +377,8 @@ required-version = ">=0.10.9" exclude-newer = "3 days" [tool.uv.exclude-newer-package] -# TODO: ryan, remove after 2026-10-23, once certifi 2026.10.19 (drops the compromised eMudhra root) clears the 3-day window -certifi = "2026-10-20T03:00:00Z" # TODO: remove after 2026-10-25, once brotli 1.2.1 (CVE-2026-111497) clears the 3-day window brotli = "2026-10-22T14:01:00Z" -# TODO: ryan, remove after 2026-10-24, once mlflow 3.17.2 (GHSA-wqmh-qccw-pqwf) clears the 3-day window -mlflow = "2026-10-21T03:00:00Z" -mlflow-skinny = "2026-10-21T03:00:00Z" -mlflow-tracing = "2026-10-21T03:00:00Z" -# TODO: ryan, remove after 2026-10-25, once pypdf 6.21.2 (GHSA-6c7f-9265-g8fw) clears the 3-day window -pypdf = "2026-10-21T17:00:00Z" [tool.uv.sources] litellm-proxy-extras = { workspace = true } diff --git a/uv.lock b/uv.lock index fc4155524d7..f86614046e1 100644 --- a/uv.lock +++ b/uv.lock @@ -16,11 +16,6 @@ exclude-newer-span = "P3D" [options.exclude-newer-package] brotli = "2026-10-22T14:01:00Z" -certifi = "2026-10-20T03:00:00Z" -mlflow = "2026-10-21T03:00:00Z" -mlflow-skinny = "2026-10-21T03:00:00Z" -mlflow-tracing = "2026-10-21T03:00:00Z" -pypdf = "2026-10-21T17:00:00Z" [manifest] members = [ From 9a35ed93f9c9dbf7778c28fb3250a9df7cec4e70 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 15:40:03 -0700 Subject: [PATCH 29/73] feat(ui): simplify auto router setup with deployed model defaults (#48451) * feat(ui): simplify auto router setup with deployed model defaults * fix(ui): recognize catalog Opus deployment formats * fix(ui): address auto router setup review gaps * fix(ui): preserve Gemini auto router suggestions * fix(ui): prefer current aliases and wait for team selection * fix(ui): default auto routers to classify each user turn * fix(ui): parse Grok snapshot dates separately from releases * refactor(ui): use non-mutating auto router sorting * feat(ui): allow custom models in simple auto router setup --- .../app/(dashboard)/hooks/models/useModels.ts | 1 + .../AutoRouters/AutoRoutersPanel.tsx | 5 +- .../add_model/AutoRouterPresetSelect.tsx | 125 ++++ .../add_model/AutoRouterSimpleSetup.tsx | 190 +++++++ .../add_auto_router_tab.integration.test.tsx | 458 ++++++++++----- .../add_model/add_auto_router_tab.tsx | 432 +++++++------- .../components/add_model/auto_setup.test.ts | 537 ++++++++++-------- .../src/components/add_model/auto_setup.ts | 347 +++++++---- 8 files changed, 1413 insertions(+), 682 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/add_model/AutoRouterPresetSelect.tsx create mode 100644 ui/litellm-dashboard/src/components/add_model/AutoRouterSimpleSetup.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts index 24f76065f34..744db87ad11 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts @@ -121,6 +121,7 @@ export interface AutoRouterDeployment extends AutoRouterCandidateDeployment { /** False for config.yaml-defined deployments, which the update and delete routes refuse. */ db_model?: boolean | null; base_model?: string | null; + blocked?: boolean | null; mode?: string | null; created_at?: string | null; updated_at?: string | null; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AutoRouters/AutoRoutersPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AutoRouters/AutoRoutersPanel.tsx index 6f7e04f92c6..2d1c24685f0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AutoRouters/AutoRoutersPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AutoRouters/AutoRoutersPanel.tsx @@ -106,10 +106,7 @@ export function AutoRoutersPanel({ > Add Auto Router - - Choose a classifier to route each request to a model. Called like any other model, so clients keep using a - single model name. - + Choose models for your auto router. { + switch (availability.kind) { + case "available": + return null; + case "loading": + return "Checking model availability..."; + case "unverifiable": + return "Cannot verify these models are available"; + case "missing_models": + return `Missing: ${availability.models.join(", ")}`; + } +}; + +// "loading"/"unverifiable" are transient system states, not a gap specific to this preset; only a +// caller-specific missing-model reason gets the alarming red treatment. +const isPresetHintAlarming = (availability: PresetAvailability): boolean => availability.kind === "missing_models"; + +type Props = { + selectedPreset?: string; + presets: AutoRouterPreset[]; + presetAvailability: (preset: AutoRouterPreset) => PresetAvailability; + onPresetChange: (key: string | undefined) => void; + pending: boolean; + unavailable: boolean; + onRetry: () => void; +}; + +export default function AutoRouterPresetSelect({ + selectedPreset, + presets, + presetAvailability, + onPresetChange, + pending, + unavailable, + onRetry, +}: Props) { + const sortedPresetOptions = React.useMemo( + () => + presets + .map((preset) => ({ preset, availability: presetAvailability(preset) })) + .sort((a, b) => Number(b.availability.kind === "available") - Number(a.availability.kind === "available")), + [presets, presetAvailability], + ); + + const templateItems = React.useMemo( + () => [ + ...sortedPresetOptions.map(({ preset }) => ({ value: preset.key, label: preset.label })), + { value: "custom", label: "Start from scratch" }, + ], + [sortedPresetOptions], + ); + + return ( +
+
+ +

Replaces classifier and model choices

+ {pending &&
Loading templates...
} + {unavailable && ( +
+ Could not load templates, so only Start from scratch is shown.{" "} + +
+ )} +
+
+ ); +} diff --git a/ui/litellm-dashboard/src/components/add_model/AutoRouterSimpleSetup.tsx b/ui/litellm-dashboard/src/components/add_model/AutoRouterSimpleSetup.tsx new file mode 100644 index 00000000000..64c3d4de63f --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/AutoRouterSimpleSetup.tsx @@ -0,0 +1,190 @@ +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; +import { ROUTER_FAMILIES, ROUTER_GOALS, type FamilyPreference, type RouterGoal, type SolverModel } from "./auto_setup"; + +type Props = { + customized: boolean; + hasSuggestions: boolean; + onReset: () => void; + goal: RouterGoal; + family: FamilyPreference; + models: readonly SolverModel[]; + everyday?: SolverModel; + hard?: SolverModel; + waitingForTeam: boolean; + loading: boolean; + error: boolean; + notice: string | null; + onGoalChange: (goal: RouterGoal) => void; + onFamilyChange: (family: FamilyPreference) => void; + onModelChange: (role: "everyday" | "hard", model: string) => void; + onAdvanced: () => void; + onRetry: () => void; +}; + +const sharedFamily = (everyday?: SolverModel, hard?: SolverModel) => + everyday?.family && everyday.family === hard?.family ? ROUTER_FAMILIES[everyday.family] : null; + +const modelDescription = (model: SolverModel) => + [model.model !== model.name ? model.model : null, model.provider].filter(Boolean).join(" · "); + +export default function AutoRouterSimpleSetup({ + customized, + hasSuggestions, + onReset, + goal, + family, + models, + everyday, + hard, + waitingForTeam, + loading, + error, + notice, + onGoalChange, + onFamilyChange, + onModelChange, + onAdvanced, + onRetry, +}: Props) { + const inventoryReady = !waitingForTeam && !loading; + const showPreferences = !inventoryReady || error || hasSuggestions; + const sameFamily = sharedFamily(everyday, hard); + const familyItems = [ + { value: "auto", label: sameFamily ? `Auto (${sameFamily})` : "Auto" }, + ...Object.entries(ROUTER_FAMILIES).map(([value, label]) => ({ value, label })), + ]; + return ( +
+ {showPreferences && ( + <> +
+ +
+ {Object.entries(ROUTER_GOALS).map(([key, label]) => ( + + ))} +
+
+
+ + +
+ + )} + {waitingForTeam &&

Select a team to see available models.

} + {!waitingForTeam && loading && ( +

+ + Loading deployed models +

+ )} + {inventoryReady && error && ( +

+ Could not load deployed models.{" "} + +

+ )} + {inventoryReady && !error && ( + <> +
+
+ + {customized && hasSuggestions && ( + + )} +
+
+ {(["everyday", "hard"] as const).map((role) => { + const chosen = role === "everyday" ? everyday : hard; + const other = role === "everyday" ? hard : everyday; + const choices = models.filter((model) => model.name !== other?.name); + const items = choices.map((model) => ({ value: model.name, label: model.name })); + const label = role === "everyday" ? "Everyday" : "Hard tasks"; + return ( +
+
+ +

+ {role === "everyday" ? "Your cheaper model" : "Your stronger model"} +

+
+ + {chosen &&

{modelDescription(chosen)}

} +
+ ); + })} +
+
+ {(!everyday || !hard) && ( +

+ {models.length < 2 + ? "Two deployed models are needed. Add a model or use Advanced configuration." + : "Choose two models to get started."} +

+ )} + {notice && !customized && ( +

+ {notice} +

+ )} + + )} + +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.integration.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.integration.test.tsx index ee9cbe3c480..52c4e9093a4 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.integration.test.tsx @@ -1,3 +1,4 @@ +import { autoRouterListKey } from "@/app/(dashboard)/hooks/models/useModels"; import { normalizeTierModels } from "./complexity_router_tiers"; import { openAutoRouterAdvanced, @@ -174,6 +175,12 @@ vi.mock("../common_components/team_dropdown", () => ({ ), })); +const renderAdvanced: typeof renderWithProviders = (...args) => { + const result = renderWithProviders(...args); + fireEvent.click(screen.getByRole("button", { name: "Advanced configuration", exact: true })); + return result; +}; + const Harness = () => ; describe("AddAutoRouterTab", () => { @@ -192,12 +199,185 @@ describe("AddAutoRouterTab", () => { vi.mocked(modelCostMap).mockResolvedValue({}); }); + it("creates the goal-selected latest OpenAI pair using its Bedrock deployment aliases", async () => { + mockFetchAvailableModels.mockResolvedValue( + ["small", "middle-old", "middle", "large"].map((model_group) => ({ model_group, mode: "chat" })), + ); + mockFetchAllModelDeployments.mockResolvedValue([ + { model_name: "small", litellm_params: { model: "bedrock/us.openai.gpt-6-luna" } }, + { model_name: "middle-old", litellm_params: { model: "openai/gpt-6-sol" } }, + { model_name: "middle", litellm_params: { model: "bedrock/us.openai.gpt-6.1-sol" } }, + { model_name: "large", litellm_params: { model: "bedrock/global.openai.gpt-6-astra" } }, + ]); + renderWithProviders(); + expect(await screen.findByRole("combobox", { name: "Everyday model" })).toHaveTextContent("small"); + expect(screen.queryByText("Define your tiers")).not.toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: "Best quality", exact: true })); + expect(screen.getByRole("combobox", { name: "Everyday model" })).toHaveTextContent("middle"); + expect(screen.getAllByText("gpt-6.1-sol · Bedrock").some((element) => element.tagName === "P")).toBe(true); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + fireEvent.click(screen.getByRole("button", { name: "Add Auto Router" })); + await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalledOnce()); + expect(vi.mocked(handleAddAutoRouterSubmit).mock.calls[0][0]).toMatchObject({ + auto_router_name: "auto-router", + complexity_router_config: { + classifier_type: "heuristic", + classification_mode: "user_turn", + tiers: { SIMPLE: ["middle"], MEDIUM: ["middle"], COMPLEX: ["large"], REASONING: ["large"] }, + }, + }); + }); + + it("creates a router from two custom models without a goal or family decision", async () => { + const customModels = ["fast-custom", "quality-custom"]; + mockFetchAvailableModels.mockResolvedValue(customModels.map((model_group) => ({ model_group, mode: "chat" }))); + mockFetchAllModelDeployments.mockResolvedValue( + customModels.map((model_name) => ({ + model_name, + litellm_params: { model: `openai/${model_name}` }, + })), + ); + renderWithProviders(); + expect(await screen.findByText("Choose two models to get started.")).toBeVisible(); + expect(screen.queryByRole("group", { name: "Routing goal" })).not.toBeInTheDocument(); + expect(screen.queryByRole("combobox", { name: "Model family" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeDisabled(); + await chooseSelectOption( + userEvent.setup(), + screen.getByRole("combobox", { name: "Everyday model" }), + /fast-custom/, + ); + await chooseSelectOption( + userEvent.setup(), + screen.getByRole("combobox", { name: "Hard tasks model" }), + /quality-custom/, + ); + expect(screen.queryByRole("button", { name: "Use suggested models" })).not.toBeInTheDocument(); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + fireEvent.click(screen.getByRole("button", { name: "Add Auto Router" })); + await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalledOnce()); + expect(vi.mocked(handleAddAutoRouterSubmit).mock.calls[0][0].complexity_router_config).toMatchObject({ + classifier_type: "heuristic", + classification_mode: "user_turn", + tiers: { + SIMPLE: ["fast-custom"], + MEDIUM: ["fast-custom"], + COMPLEX: ["quality-custom"], + REASONING: ["quality-custom"], + }, + }); + }); + + it("saves concrete solver names expanded by the backend from an openai wildcard route", async () => { + mockFetchAvailableModels.mockResolvedValue( + ["openai/gpt-6-luna", "openai/gpt-6.1-sol", "openai/gpt-6-astra"].map((model_group) => ({ + model_group, + mode: "chat", + providers: ["openai"], + })), + ); + mockFetchAllModelDeployments.mockResolvedValue([{ model_name: "openai/*", litellm_params: { model: "openai/*" } }]); + renderWithProviders(); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + fireEvent.click(screen.getByRole("button", { name: "Add Auto Router" })); + await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalledOnce()); + expect(vi.mocked(handleAddAutoRouterSubmit).mock.calls[0][0]).toMatchObject({ + complexity_router_config: { + classifier_type: "heuristic", + classification_mode: "user_turn", + tiers: { + SIMPLE: ["openai/gpt-6-luna"], + MEDIUM: ["openai/gpt-6-luna"], + COMPLEX: ["openai/gpt-6-astra"], + REASONING: ["openai/gpt-6-astra"], + }, + }, + }); + }); + + it.each(["groups", "deployments"])( + "keeps chosen models and saving available after a failed %s background refresh", + async (source) => { + mockFetchAvailableModels.mockResolvedValue( + ["claude-haiku-5-5", "claude-opus-5-5"].map((model_group) => ({ model_group, mode: "chat" })), + ); + renderWithProviders(); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + const fetch = source === "groups" ? mockFetchAvailableModels : mockFetchAllModelDeployments; + fetch.mockRejectedValue(new Error("Background refresh unavailable")); + const queryKey = + source === "groups" ? ["availableModels", "autoRouter", "token"] : autoRouterListKey("", "Admin"); + await act(async () => { + await testQueryClient.refetchQueries({ queryKey }); + }); + await waitFor(() => expect(testQueryClient.getQueryState(queryKey)?.status).toBe("error")); + expect(screen.getByRole("combobox", { name: "Everyday model" })).toHaveTextContent("claude-haiku-5-5"); + expect(screen.queryByText("Could not load deployed models.")).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled(); + fireEvent.click(screen.getByRole("button", { name: "Add Auto Router" })); + await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalledOnce()); + }, + ); + + it("keeps manual solver choices on goal changes and carries them into Advanced", async () => { + mockFetchAvailableModels.mockResolvedValue( + ["claude-haiku-5-5", "custom-fast", "claude-opus-5-5"].map((model_group) => ({ + model_group, + mode: "chat", + })), + ); + renderWithProviders(); + const everyday = await screen.findByRole("combobox", { name: "Everyday model" }); + await userEvent.click(everyday); + await userEvent.click(await screen.findByRole("option", { name: /custom-fast/ })); + fireEvent.click(screen.getByRole("button", { name: "Cheapest", exact: true })); + expect(screen.getByRole("combobox", { name: "Everyday model" })).toHaveTextContent("custom-fast"); + expect(screen.getByRole("combobox", { name: "Hard tasks model" })).toHaveTextContent("claude-opus-5-5"); + fireEvent.click(screen.getByRole("button", { name: "Advanced configuration", exact: true })); + expectTierModel("Simple", "custom-fast"); + expectTierModel("Reasoning", "claude-opus-5-5"); + }); + + it("keeps standard setup usable when the Heuristic v2 allowance is exhausted", async () => { + mockFetchAvailableModels.mockResolvedValue( + ["claude-haiku-5-5", "claude-opus-5-5"].map((model_group) => ({ model_group, mode: "chat" })), + ); + vi.mocked(apiClient.post).mockResolvedValue({ + allowances: [{ key: "heuristic_v2", limit: 1, remaining: 0, available: true }], + error: null, + }); + renderWithProviders(); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + expect(screen.queryByText(/allowance/i)).not.toBeInTheDocument(); + }); + + it("blocks creation when deployment identity cannot be verified, then recovers on retry", async () => { + mockFetchAvailableModels.mockResolvedValue( + ["claude-haiku-5-5", "claude-opus-5-5"].map((model_group) => ({ model_group, mode: "chat" })), + ); + mockFetchAllModelDeployments.mockRejectedValueOnce(new Error("Unavailable")); + renderWithProviders(); + expect(await screen.findByRole("alert")).toHaveTextContent("Could not load deployed models"); + expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeDisabled(); + fireEvent.click(screen.getByRole("button", { name: "Retry", exact: true })); + await waitFor(() => expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeEnabled()); + }); + + it("offers Advanced instead of inventing a second solver for a one-model deployment", async () => { + mockFetchAvailableModels.mockResolvedValue([{ model_group: "claude-opus-5-5", mode: "chat" }]); + renderWithProviders(); + expect(await screen.findByText(/Two deployed models are needed/)).toBeVisible(); + expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeDisabled(); + fireEvent.click(screen.getByRole("button", { name: "Advanced configuration", exact: true })); + expect(screen.getByText("Define your tiers")).toBeVisible(); + }); + it("shows Jev essentials before tiers and saves tuning from its own disclosure", async () => { mockFetchAvailableModels.mockResolvedValue([ ...ALL_FAMILY_MODELS, { model_group: "my-decision", mode: "evaluation" }, ]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await userEvent.click(screen.getByRole("radio", { name: "Decisions Model" })); expect(screen.queryByLabelText("Classifier Model")).not.toBeInTheDocument(); @@ -250,7 +430,7 @@ describe("AddAutoRouterTab", () => { { model_name: "ordinary-chat", litellm_params: { model: "mixed-provider/chat-model" } }, { model_name: "unavailable-decider", litellm_params: { model: underlyingModel } }, ]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await userEvent.click(screen.getByRole("radio", { name: "Decisions Model" })); await userEvent.click(screen.getByRole("combobox", { name: "Decision model" })); @@ -269,7 +449,7 @@ describe("AddAutoRouterTab", () => { it("uses the judge picker empty state when no decision deployments exist", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await userEvent.click(screen.getByRole("radio", { name: "Decisions Model" })); await userEvent.click(screen.getByRole("combobox", { name: "Decision model" })); @@ -280,7 +460,7 @@ describe("AddAutoRouterTab", () => { it("does not refill a tier cleared by the user when models refresh", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await userEvent.click(within(tierChips("Simple")).getByRole("button", { name: "" })); await act(async () => { @@ -294,7 +474,7 @@ describe("AddAutoRouterTab", () => { allowances: [{ key: "heuristic_v2", limit: 1, remaining, available: true }], error: null, }); - renderWithProviders(); + renderAdvanced(); await waitFor(() => expect(screen.getByRole("button", { name: "Heuristic" })).toHaveTextContent("Rule-based")); }); @@ -305,21 +485,21 @@ describe("AddAutoRouterTab", () => { ? "This change needs an available rule-based tuning allowance" : null, })); - renderWithProviders(); + renderAdvanced(); await waitFor(() => expect(screen.getByRole("button", { name: "Heuristic" })).toHaveTextContent("Rule-based")); expect(screen.queryByRole("alert")).not.toBeInTheDocument(); expect(screen.getByRole("button", { name: "Add Auto Router" })).toBeDisabled(); }); it("keeps the Rule-based default when the form reopens with a cached allowance", async () => { - const first = renderWithProviders(); + const first = renderAdvanced(); await waitFor(() => expect(apiClient.post).toHaveBeenCalled()); first.unmount(); vi.mocked(apiClient.post).mockResolvedValueOnce({ allowances: [{ key: "heuristic_v2", limit: 1, remaining: 0, available: true }], error: null, }); - renderWithProviders(); + renderAdvanced(); await waitFor(() => expect(screen.getByRole("button", { name: "Heuristic" })).toHaveTextContent("Rule-based")); }); @@ -330,7 +510,7 @@ describe("AddAutoRouterTab", () => { allowances: [{ key: "heuristic_v2", limit: 1, remaining: 0, available: true }], error: null, }); - renderWithProviders(); + renderAdvanced(); fireEvent.click(screen.getByRole("button", { name: "Heuristic" })); const option = screen.getByRole("menuitemradio", { name: /^Heuristic v2/ }); await waitFor(() => expect(option).toHaveTextContent("0 of 1 available")); @@ -383,7 +563,7 @@ describe("AddAutoRouterTab", () => { ...ALL_FAMILY_MODELS, { model_group: "my-decision", mode: "evaluation" }, ]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await user.click(screen.getByRole("radio", { name: "Decisions Model" })); await userEvent.click(screen.getByRole("combobox", { name: "Decision model" })); @@ -431,7 +611,7 @@ describe("AddAutoRouterTab", () => { ...ALL_FAMILY_MODELS, { model_group: "my-decision", mode: "evaluation" }, ]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await user.click(screen.getByRole("radio", { name: "Decisions Model" })); await userEvent.click(screen.getByRole("combobox", { name: "Decision model" })); @@ -468,7 +648,7 @@ describe("AddAutoRouterTab", () => { complete = resolve; }), ); - renderWithProviders(); + renderAdvanced(); await userEvent.click(screen.getByRole("radio", { name: "LLM" })); complete?.({ allowances: [{ key: "heuristic_v2", limit: 1, remaining: 0, available: true }], error: null }); await waitFor(() => expect(screen.getByRole("radio", { name: "LLM" })).toBeChecked()); @@ -484,7 +664,7 @@ describe("AddAutoRouterTab", () => { complete = resolve; }), ); - renderWithProviders(); + renderAdvanced(); await userEvent.click(screen.getByRole("radio", { name: family })); await selectAutoRouterOption("How often to classify", "Every new user message"); await act(async () => { @@ -508,8 +688,8 @@ describe("AddAutoRouterTab", () => { { model_group: "capable", mode: "chat" }, { model_group: "judge", mode: "chat" }, ]); - renderWithProviders(); - await user.type(screen.getByLabelText("Auto Router Name"), "forecast-router"); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "forecast-router" } }); await selectAutoRouterApproach(label); expect(screen.getByLabelText("Auto Router Name")).toHaveValue("forecast-router"); expect(screen.queryByTestId("template-selector")).not.toBeInTheDocument(); @@ -575,7 +755,7 @@ describe("AddAutoRouterTab", () => { it("restores the automatic/template/detail flow on the Complexity tab", async () => { const user = userEvent.setup(); mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); await selectAutoRouterApproach("Capability"); expect(screen.queryByTestId("configure-automatically-button")).not.toBeInTheDocument(); @@ -603,9 +783,9 @@ describe("AddAutoRouterTab", () => { { model_group: "capable", mode: "chat" }, { model_group: "judge", mode: "chat" }, ]); - renderWithProviders(); + renderAdvanced(); await selectAutoRouterApproach(label); - await user.type(screen.getByLabelText("Auto Router Name"), "forecast-retry"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "forecast-retry" } }); const capability = label === "Capability"; const policyField = capability ? "Solve probability threshold" : "Efficient solver profile"; fireEvent.change(screen.getByLabelText(policyField), { @@ -627,7 +807,7 @@ describe("AddAutoRouterTab", () => { // Detailed Configuration starts collapsed so the modal opens onto just Name + Template; a caller // opts into the full tier/classifier form rather than always seeing it up front. it("shows models immediately and keeps advanced settings collapsed", async () => { - renderWithProviders(); + renderAdvanced(); expect(screen.getByRole("button", { name: "Advanced settings" })).toHaveAttribute("aria-expanded", "false"); @@ -641,14 +821,14 @@ describe("AddAutoRouterTab", () => { { model_group: "unknown-model-a", mode: "chat" }, { model_group: "unknown-model-b", mode: "chat" }, ]); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => expect(optionByLabel("Anthropic Family")).toHaveTextContent("Missing:")); expect(screen.queryByTestId("configure-automatically-button")).not.toBeInTheDocument(); }); - it("mixes preferred tier models even when one complete preset is available", async () => { + it("prefers a same-family pair when a complete family is available", async () => { const anthropicPreset = getPresetByKey("anthropic_family")!; mockFetchAvailableModels.mockResolvedValue( [...getRequiredModelsInPreset(anthropicPreset), "gpt-5.6-luna"].map((model_group) => ({ @@ -657,18 +837,18 @@ describe("AddAutoRouterTab", () => { })), ); mockFetchAllModelDeployments.mockResolvedValue([]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); - expectTierModel("Simple", "gpt-5.6-luna"); - expectTierModel("Medium", "claude-sonnet-5"); + expectTierModel("Simple", ANTHROPIC_TIERS.SIMPLE[0]); + expectTierModel("Medium", ANTHROPIC_TIERS.SIMPLE[0]); expectTierModel("Complex", "claude-opus-5-5"); expectTierModel("Reasoning", "claude-opus-5-5"); expect(toast.success).not.toHaveBeenCalledWith(expect.stringContaining("Configured with")); }); - it("mixes available models from the preferred tier catalog when no complete template fits", async () => { + it("uses two models within the available family without a complete template", async () => { mockFetchAvailableModels.mockResolvedValue( ["gpt-5.6-luna", "claude-sonnet-5", "gpt-5.6-sol"].map((model_group) => ({ model_group, @@ -676,12 +856,12 @@ describe("AddAutoRouterTab", () => { })), ); mockFetchAllModelDeployments.mockResolvedValue([]); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); expectTierModel("Simple", "gpt-5.6-luna"); - expectTierModel("Medium", "claude-sonnet-5"); + expectTierModel("Medium", "gpt-5.6-luna"); expectTierModel("Complex", "gpt-5.6-sol"); expectTierModel("Reasoning", "gpt-5.6-sol"); }); @@ -689,20 +869,20 @@ describe("AddAutoRouterTab", () => { it("opens Detailed Configuration on the tiers automatic setup just filled in", async () => { const simpleModel = "gpt-5.6-luna"; mockFetchAvailableModels.mockResolvedValue([...ALL_FAMILY_MODELS, { model_group: simpleModel, mode: "chat" }]); - renderWithProviders(); + renderAdvanced(); expect(screen.getByRole("button", { name: "Advanced settings" })).toHaveAttribute("aria-expanded", "false"); await waitForAutomaticTiers(); expect(screen.getByText("Define your tiers")).toBeInTheDocument(); - expectTierModel("Simple", simpleModel); + expectTierModel("Simple", ANTHROPIC_TIERS.SIMPLE[0]); }); // Nothing is filled in, so there is nothing to submit. The button reports that itself instead of // accepting a click and answering with a toast. it("offers no submit at all until every tier has a model", async () => { - renderWithProviders(); + renderAdvanced(); expect(screen.getByRole("button", { name: /add auto router/i })).toBeDisabled(); }); @@ -710,7 +890,8 @@ describe("AddAutoRouterTab", () => { it("still flags the router name once the config no longer blocks the submit", async () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -720,15 +901,27 @@ describe("AddAutoRouterTab", () => { }); it("offers no team selector to a proxy admin, who may create an unscoped router", async () => { - renderWithProviders(); + renderAdvanced(); expect(screen.queryByTestId("team-dropdown")).not.toBeInTheDocument(); }); - it("requires a team admin to pick a team", async () => { + it("waits for a team before showing model inventory in simple setup", async () => { renderWithProviders( , ); + expect(screen.getByText("Select a team to see available models.")).toBeInTheDocument(); + expect(screen.queryByText(/Two deployed models are needed/)).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: /add auto router/i })).toBeDisabled(); + await userEvent.selectOptions(screen.getByTestId("team-dropdown"), "team-1"); + expect(await screen.findByText("Chosen models")).toBeInTheDocument(); + expect(screen.queryByText("Select a team to see available models.")).not.toBeInTheDocument(); + }); + + it("requires a team admin to pick a team", async () => { + renderAdvanced( + , + ); expect(screen.getByTestId("team-dropdown")).toBeInTheDocument(); expect(screen.getByText("Select Team")).toBeInTheDocument(); @@ -741,11 +934,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders( + renderAdvanced( , ); - await user.type(screen.getByPlaceholderText(/smart_router/i), "team-scoped-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "team-scoped-router" } }); await user.selectOptions(screen.getByTestId("team-dropdown"), "team-1"); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -763,7 +956,7 @@ describe("AddAutoRouterTab", () => { ); vi.mocked(handleAddAutoRouterSubmit).mockImplementationOnce(actualSubmit.handleAddAutoRouterSubmit); const user = userEvent.setup(); - renderWithProviders( + renderAdvanced( { error: "session_affinity cannot be combined with tier_definitions", }); - renderWithProviders(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "rejected-router"); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "rejected-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -838,7 +1031,7 @@ describe("AddAutoRouterTab", () => { }), ); - const { container } = renderWithProviders(); + const { container } = renderAdvanced(); fireEvent.change(screen.getByPlaceholderText(/smart_router/i), { target: { value: "double-submit-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); @@ -858,8 +1051,8 @@ describe("AddAutoRouterTab", () => { vi.mocked(getMissingTiersError).mockReturnValue(null); validateAutoRouterConfig.mockResolvedValueOnce({ valid: true }); - renderWithProviders(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "accepted-router"); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "accepted-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -873,9 +1066,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "keyword-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "keyword-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -890,9 +1083,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "keyword-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "keyword-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -908,9 +1101,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "orphan-rule-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "orphan-rule-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -927,9 +1120,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "keyword-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "keyword-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -944,9 +1137,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "keyword-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "keyword-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -965,11 +1158,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders( + renderAdvanced( , ); - await user.type(screen.getByPlaceholderText(/smart_router/i), "team-scoped-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "team-scoped-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -983,11 +1176,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders( + renderAdvanced( , ); - await user.type(screen.getByPlaceholderText(/smart_router/i), "team-scoped-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "team-scoped-router" } }); await user.selectOptions(screen.getByTestId("team-dropdown"), "team-1"); await user.click(screen.getByTestId("team-dropdown-clear")); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); @@ -1001,9 +1194,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "affinity-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "affinity-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); expect(await screen.findByRole("combobox", { name: "How often to classify" })).not.toHaveTextContent( @@ -1022,7 +1215,8 @@ describe("AddAutoRouterTab", () => { it("blocks invalid success thresholds and creates a heuristic v2 router with explicit zero", async () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "" } }); fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "threshold-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); @@ -1059,7 +1253,7 @@ describe("AddAutoRouterTab", () => { "./handle_add_auto_router_submit", ); vi.mocked(handleAddAutoRouterSubmit).mockImplementationOnce(actualSubmit.handleAddAutoRouterSubmit); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "chained-router" } }); await userEvent.click(screen.getByRole("radio", { name: "LLM" })); @@ -1087,7 +1281,7 @@ describe("AddAutoRouterTab", () => { it("preserves classifier tuning when choosing models automatically", async () => { const user = userEvent.setup(); mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "reset-threshold-router" } }); openAutoRouterAdvanced("Classification Method"); @@ -1112,7 +1306,8 @@ describe("AddAutoRouterTab", () => { it("clears an invalid inactive threshold before creating the router", async () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "" } }); fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "clear-threshold-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); @@ -1133,13 +1328,15 @@ describe("AddAutoRouterTab", () => { it.each([false, true])("creates a router with cache routing only after an explicit opt-in: %s", async (enabled) => { const user = userEvent.setup(); mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForAutomaticTiers(); fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "cache-aware-router" } }); openAutoRouterAdvanced("Cache-aware routing"); const toggle = screen.getByRole("switch", { name: "Cache-aware routing" }); expect(toggle).not.toBeChecked(); if (enabled) { + expect(toggle).toHaveAttribute("aria-disabled", "true"); + await selectAutoRouterOption("How often to classify", "Every request"); await user.click(toggle); fireEvent.change(screen.getByLabelText("Expected output tokens"), { target: { value: "512" } }); fireEvent.change(screen.getByLabelText("Prediction timeout (ms)"), { target: { value: "750" } }); @@ -1158,9 +1355,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "ctx-window-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "ctx-window-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Context Window Escalation"); const toggle = await screen.findByRole("switch", { name: "Escalate oversized prompts to a tier that fits" }); @@ -1181,9 +1378,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "ctx-buffer-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "ctx-buffer-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Context Window Escalation"); await user.click(screen.getByRole("switch", { name: "Escalate oversized prompts to a tier that fits" })); @@ -1204,9 +1401,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "ctx-clear-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "ctx-clear-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Context Window Escalation"); await user.click(screen.getByRole("switch", { name: "Escalate oversized prompts to a tier that fits" })); @@ -1230,9 +1427,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "no-compression-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "no-compression-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1246,9 +1443,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "no-compression-explicit-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { + target: { value: "no-compression-explicit-router" }, + }); expandDetailedConfiguration(); openAutoRouterAdvanced("Compression"); await chooseSelectOption( @@ -1270,9 +1469,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "different-compression-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { + target: { value: "different-compression-router" }, + }); expandDetailedConfiguration(); openAutoRouterAdvanced("Compression"); await chooseSelectOption( @@ -1299,9 +1500,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "override-floor-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "override-floor-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); await selectAutoRouterOption("Heuristic", "Rule-based"); @@ -1321,9 +1522,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "affinity-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "affinity-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); await selectAutoRouterOption("How often to classify", "Once per session"); @@ -1346,9 +1547,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "user-turn-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "user-turn-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); await selectAutoRouterOption("How often to classify", "Every new user message"); @@ -1362,16 +1563,19 @@ describe("AddAutoRouterTab", () => { }); }); - it("writes every_request into the create payload when the default frequency stays selected", async () => { + it("allows every request in Advanced while defaulting to every new user message", async () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "default-timing-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "default-timing-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Classification Method"); - expect(await screen.findByRole("combobox", { name: "How often to classify" })).toHaveTextContent("Every request"); + expect(await screen.findByRole("combobox", { name: "How often to classify" })).toHaveTextContent( + "Every new user message", + ); + await selectAutoRouterOption("How often to classify", "Every request"); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1386,9 +1590,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "affinity-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "affinity-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Affinity"); expect(await screen.findByRole("switch", { name: "Pin one model deployment per tier" })).toBeChecked(); @@ -1406,9 +1610,9 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "affinity-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "affinity-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Affinity"); await user.click(await screen.findByRole("switch", { name: "Pin one model deployment per tier" })); @@ -1426,7 +1630,7 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); fireEvent.change(screen.getByPlaceholderText(/smart_router/i), { target: { value: "modality-router" } }); @@ -1444,7 +1648,7 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); vi.mocked(getMissingTiersError).mockReturnValue(null); - renderWithProviders(); + renderAdvanced(); fireEvent.change(screen.getByPlaceholderText(/smart_router/i), { target: { value: "modality-router" } }); expandDetailedConfiguration(); @@ -1465,7 +1669,7 @@ describe("AddAutoRouterTab", () => { // Custom is the escape hatch, not the headline choice, so it's listed after every bundled preset // rather than first. it("lists Start from scratch after the bundled presets", async () => { - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); const labels = visibleOptions().map((option) => option.querySelector(".font-medium")?.textContent); @@ -1487,7 +1691,7 @@ describe("AddAutoRouterTab", () => { ); vi.mocked(getMissingTiersError).mockImplementation(actual.getMissingTiersError); - renderWithProviders(); + renderAdvanced(); expect(screen.getByTestId("auto-router-test-routing-btn")).toBeDisabled(); }); @@ -1504,8 +1708,8 @@ describe("AddAutoRouterTab", () => { }, }); - renderWithProviders(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "keyword-router"); + renderAdvanced(); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "keyword-router" } }); expandDetailedConfiguration(); openAutoRouterAdvanced("Keyword/Semantic Matching"); await user.click(screen.getByRole("button", { name: /add keyword rule/i })); @@ -1540,7 +1744,7 @@ describe("AddAutoRouterTab", () => { }, }); - renderWithProviders(); + renderAdvanced(); await user.click(screen.getByTestId("auto-router-test-routing-btn")); await user.type(await screen.findByTestId("auto-router-routing-test-prompt"), "reconcile this invoice"); await user.click(screen.getByTestId("auto-router-routing-test-send")); @@ -1564,7 +1768,7 @@ describe("AddAutoRouterTab", () => { }), ); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); const anthropicOption = optionByLabel("Anthropic Family")!; @@ -1582,7 +1786,7 @@ describe("AddAutoRouterTab", () => { it("disables every preset and offers a retry when the model list fails to load", async () => { mockFetchAvailableModels.mockRejectedValue(new Error("network error")); - renderWithProviders(); + renderAdvanced(); expect(await screen.findByText("Could not load available models.")).toBeInTheDocument(); openTemplateDropdown(); @@ -1595,7 +1799,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); mockFetchAllModelDeployments.mockRejectedValue(new Error("network error")); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await waitForPresetEnabled("OpenAI Family"); @@ -1606,7 +1810,7 @@ describe("AddAutoRouterTab", () => { ALL_FAMILY_MODELS.filter((m) => m.model_group !== ANTHROPIC_ONLY_MODEL), ); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { @@ -1618,7 +1822,7 @@ describe("AddAutoRouterTab", () => { it("enables a preset once every model it needs is available", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await waitForPresetEnabled("OpenAI Family"); @@ -1626,7 +1830,7 @@ describe("AddAutoRouterTab", () => { it("shows the template's model choices while advanced settings stay collapsed", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); expect(screen.getByRole("button", { name: "Advanced settings" })).toHaveAttribute("aria-expanded", "false"); @@ -1635,7 +1839,7 @@ describe("AddAutoRouterTab", () => { }); it("expands detailed configuration when Start from scratch is chosen", async () => { - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await selectTemplate("Start from scratch"); @@ -1647,7 +1851,7 @@ describe("AddAutoRouterTab", () => { it("lets a caller manually re-expand a detailed configuration a preset just collapsed", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); expect(screen.queryByText("Keyword/Semantic Matching")).not.toBeInTheDocument(); @@ -1666,11 +1870,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); - await user.type(screen.getByPlaceholderText(/smart_router/i), "anthropic-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "anthropic-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1689,7 +1893,7 @@ describe("AddAutoRouterTab", () => { it("blocks a form submit when a referenced model disappears after the tiers are filled in", async () => { mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - const { container } = renderWithProviders(); + const { container } = renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); fireEvent.change(screen.getByPlaceholderText(/smart_router/i), { target: { value: "stale-model-router" } }); @@ -1713,11 +1917,11 @@ describe("AddAutoRouterTab", () => { const user = userEvent.setup(); mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); - await user.type(screen.getByPlaceholderText(/smart_router/i), "anthropic-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "anthropic-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1751,10 +1955,10 @@ describe("AddAutoRouterTab", () => { it("submits the pinned model in place of the one the tiers derive", async () => { const user = userEvent.setup(); - renderWithProviders(); + renderAdvanced(); await applyPresetAndPin(user); - await user.type(screen.getByPlaceholderText(/smart_router/i), "pinned-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "pinned-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1771,7 +1975,7 @@ describe("AddAutoRouterTab", () => { it("blocks a submit whose pinned model is no longer available", async () => { const user = userEvent.setup(); - const { container } = renderWithProviders(); + const { container } = renderAdvanced(); await applyPresetAndPin(user); fireEvent.change(screen.getByPlaceholderText(/smart_router/i), { target: { value: "stale-pin-router" } }); @@ -1796,11 +2000,11 @@ describe("AddAutoRouterTab", () => { it("omits plan_mode_min_tier from the payload when never touched", async () => { const user = userEvent.setup(); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); - await user.type(screen.getByPlaceholderText(/smart_router/i), "no-plan-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "no-plan-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1812,7 +2016,7 @@ describe("AddAutoRouterTab", () => { it("carries the enabled override through to the create payload", async () => { const user = userEvent.setup(); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Anthropic Family"); await selectTemplate("Anthropic Family"); @@ -1820,7 +2024,7 @@ describe("AddAutoRouterTab", () => { openAutoRouterAdvanced("Plan-Mode Override"); await user.click(await screen.findByRole("switch", { name: "Route plan-mode requests to a minimum tier" })); - await user.type(screen.getByPlaceholderText(/smart_router/i), "plan-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "plan-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1847,7 +2051,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(groupsFor(ALL_RENAMED_DEPLOYMENTS)); mockFetchAllModelDeployments.mockResolvedValue(ALL_RENAMED_DEPLOYMENTS); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { @@ -1874,7 +2078,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(groupsFor(deployments)); mockFetchAllModelDeployments.mockResolvedValue(deployments); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { expect(isOptionDisabled(optionByLabel("Anthropic Family")!)).toBe(false); @@ -1886,7 +2090,7 @@ describe("AddAutoRouterTab", () => { expect(screen.getByText("Keyword/Semantic Matching")).toBeInTheDocument(); - await user.type(screen.getByPlaceholderText(/smart_router/i), "renamed-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "renamed-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -1908,7 +2112,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(groupsFor(anthropicOnly)); mockFetchAllModelDeployments.mockResolvedValue(anthropicOnly); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { @@ -1933,7 +2137,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(groupsFor(wildcard)); mockFetchAllModelDeployments.mockResolvedValue(wildcard); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { @@ -1960,7 +2164,7 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(EXPANDED_HUB_GROUPS); mockFetchAllModelDeployments.mockResolvedValue(WILDCARD_DEPLOYMENTS); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { @@ -1974,14 +2178,14 @@ describe("AddAutoRouterTab", () => { mockFetchAvailableModels.mockResolvedValue(EXPANDED_HUB_GROUPS); mockFetchAllModelDeployments.mockResolvedValue(WILDCARD_DEPLOYMENTS); - renderWithProviders(); + renderAdvanced(); openTemplateDropdown(); await waitFor(() => { expect(isOptionDisabled(optionByLabel("Anthropic Family")!)).toBe(false); }); await selectTemplate("Anthropic Family"); - await user.type(screen.getByPlaceholderText(/smart_router/i), "wildcard-router"); + fireEvent.change(screen.getByLabelText("Auto Router Name"), { target: { value: "wildcard-router" } }); await waitFor(() => expect(screen.getByRole("button", { name: /add auto router/i })).toBeEnabled()); await user.click(screen.getByRole("button", { name: /add auto router/i })); @@ -2079,7 +2283,7 @@ describe("preset catalog fetch states", () => { }, ], }); - renderWithProviders(); + renderAdvanced(); await waitForPresetEnabled("Bounded JEV"); await selectTemplate("Bounded JEV"); await userEvent.click(screen.getByRole("combobox", { name: "Decision model" })); @@ -2100,7 +2304,7 @@ describe("preset catalog fetch states", () => { ...LOADED_PRESETS_QUERY, isError: true, } as never); - renderWithProviders(); + renderAdvanced(); expect(screen.queryByText(/Could not load templates/)).not.toBeInTheDocument(); @@ -2114,7 +2318,7 @@ describe("preset catalog fetch states", () => { data: undefined, isPending: true, } as never); - renderWithProviders(); + renderAdvanced(); expect(screen.getByText("Loading templates...")).toBeInTheDocument(); }); @@ -2127,7 +2331,7 @@ describe("preset catalog fetch states", () => { isError: true, refetch, } as never); - renderWithProviders(); + renderAdvanced(); expect(await screen.findByText(/Could not load templates/)).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index 82c30395755..8c3f402df1f 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -13,7 +13,7 @@ import { FormField } from "@/components/shared/form/FormField"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue } from "@/components/ui/select"; +import AutoRouterPresetSelect, { type PresetAvailability } from "./AutoRouterPresetSelect"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; import { useZodForm } from "@/lib/forms/useZodForm"; @@ -79,7 +79,16 @@ import { } from "@/lib/autorouter_presets"; import { useAutoRouterPresets } from "@/app/(dashboard)/hooks/autoRouter/useAutoRouterPresets"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { buildAutomaticRouterConfig, buildPreferredTierModels, prefillEmptyTiers } from "./auto_setup"; +import AutoRouterSimpleSetup from "./AutoRouterSimpleSetup"; +import { + deployedRouterModels, + nextRouterName, + simpleSetupError, + recommendSolverPair, + solverPairConfig, + type RouterGoal, + type FamilyPreference, +} from "./auto_setup"; interface AddAutoRouterTabProps { handleOk: () => void; @@ -95,31 +104,6 @@ interface AddAutoRouterTabProps { teams?: Team[] | null; } -type PresetAvailability = - | { kind: "available"; viaDeployments: boolean } - | { kind: "loading" } - | { kind: "unverifiable" } - | { kind: "missing_models"; models: readonly string[] }; - -// Every non-"available" state disables the option. Selection derives from this same function -// (see presetAvailability below), so an option a caller can click is always one that can be applied. -const presetDisabledHint = (availability: PresetAvailability): string | null => { - switch (availability.kind) { - case "available": - return null; - case "loading": - return "Checking model availability..."; - case "unverifiable": - return "Cannot verify these models are available"; - case "missing_models": - return `Missing: ${availability.models.join(", ")}`; - } -}; - -// "loading"/"unverifiable" are transient system states, not a gap specific to this preset; only a -// caller-specific missing-model reason gets the alarming red treatment. -const isPresetHintAlarming = (availability: PresetAvailability): boolean => availability.kind === "missing_models"; - const NO_PRESETS: AutoRouterPreset[] = []; // Why the submit is unavailable, or null when it is available. The button reads this to disable @@ -168,7 +152,7 @@ const autoRouterSchema = (requiresTeamScope: boolean) => type AddAutoRouterFormValues = z.infer>; const EMPTY_FORM_VALUES: AddAutoRouterFormValues = { - auto_router_name: "", + auto_router_name: "auto-router", team_id: null, model_access_group: undefined, }; @@ -210,11 +194,18 @@ const AddAutoRouterTab: React.FC = ({ }); const [modelAccessGroups, setModelAccessGroups] = useState([]); - const [complexityRouterConfig, setComplexityRouterConfig] = useState({ + const [advancedConfig, setComplexityRouterConfig] = useState({ tiers: { SIMPLE: [], MEDIUM: [], COMPLEX: [], REASONING: [] }, classifier_type: "heuristic", + classification_mode: "user_turn", }); + const [advanced, setAdvanced] = useState(false); + const [prefillAttempted, setPrefillAttempted] = useState(false); + const [goal, setGoal] = useState("balanced"); + const [family, setFamily] = useState("auto"); + const [solverOverrides, setSolverOverrides] = useState<{ everyday?: string; hard?: string }>({}); + const [customTechnicalKeywords, setCustomTechnicalKeywords] = useState([]); const [keywordTierRules, setKeywordTierRules] = useState([]); const [semanticMatchingEnabled, setSemanticMatchingEnabled] = useState(false); @@ -250,17 +241,30 @@ const AddAutoRouterTab: React.FC = ({ isError: modelsError, refetch: refetchModels, } = useQuery({ - queryKey: ["availableModels", "autoRouter", accessToken, ...(isMemberManaged ? [watchedTeamId] : [])], + queryKey: ["availableModels", "autoRouter", accessToken, ...(requiresTeamScope ? [watchedTeamId] : [])], queryFn: () => - isMemberManaged ? fetchAutoRouterModels(accessToken, watchedTeamId) : fetchAvailableModels(accessToken), - enabled: Boolean(accessToken && (!isMemberManaged || watchedTeamId)), + requiresTeamScope ? fetchAutoRouterModels(accessToken, watchedTeamId) : fetchAvailableModels(accessToken), + enabled: Boolean(accessToken && (!requiresTeamScope || watchedTeamId)), }); - const { data: deployments, isLoading: deploymentsLoading } = useQuery({ + const { + data: deployments, + isLoading: deploymentsLoading, + isError: deploymentsError, + refetch: refetchDeployments, + } = useQuery({ queryKey: autoRouterListKey(userId ?? "", userRole), queryFn: () => fetchAllModelDeployments(accessToken, userId ?? "", userRole), enabled: Boolean(accessToken), }); const modelsLoading = groupsLoading || deploymentsLoading; + useEffect(() => { + if (!deployments || form.getFieldState("auto_router_name").isDirty) return; + form.setValue( + "auto_router_name", + nextRouterName(deployments.flatMap((row) => (row.model_name ? [row.model_name] : []))), + ); + }, [deployments, form]); + const modelInfo = React.useMemo(() => data ?? [], [data]); const { data: presetsData, @@ -269,12 +273,12 @@ const AddAutoRouterTab: React.FC = ({ refetch: refetchPresets, } = useAutoRouterPresets(); const presets = presetsData ?? NO_PRESETS; - const automaticSetupLoading = modelsLoading || presetsPending; const presetsUnavailable = presetsError && presetsData === undefined; // react-query keeps the last successful list around when a later refetch fails, so isError alone // can't tell "never loaded" apart from "loaded, then a background refetch errored" - only the // former leaves us with nothing trustworthy to verify a preset's models against. const modelsUnverifiable = modelsError && data === undefined; + const inventoryUnavailable = modelsUnverifiable || (deploymentsError && deployments === undefined); const isAdmin = all_admin_roles.includes(userRole); @@ -294,14 +298,33 @@ const AddAutoRouterTab: React.FC = ({ ), [modelInfo], ); - const preferredTierModels = React.useMemo( - () => buildPreferredTierModels(presets, availability), - [presets, availability], + const solverModels = React.useMemo( + () => deployedRouterModels(modelInfo, deployments ?? []), + [modelInfo, deployments], ); - const automaticRouterConfig = React.useMemo( - () => buildAutomaticRouterConfig(modelInfo, deployments ?? [], preferredTierModels), - [modelInfo, deployments, preferredTierModels], + const recommended = React.useMemo( + () => recommendSolverPair(solverModels, goal, family), + [solverModels, goal, family], ); + const everyday = solverOverrides.everyday + ? solverModels.find((model) => model.name === solverOverrides.everyday) + : recommended?.everyday; + const hard = solverOverrides.hard + ? solverModels.find((model) => model.name === solverOverrides.hard) + : recommended?.hard; + const simpleConfig = everyday && hard ? solverPairConfig(everyday.name, hard.name) : null; + const complexityRouterConfig = advanced + ? advancedConfig + : simpleConfig ?? { + classifier_type: "heuristic" as const, + classification_mode: "user_turn" as const, + tiers: { SIMPLE: [], MEDIUM: [], COMPLEX: [], REASONING: [] }, + }; + const openAdvanced = () => { + setComplexityRouterConfig(complexityRouterConfig); + setPrefillAttempted(simpleConfig !== null); + setAdvanced(true); + }; // A preset's models can only be trusted against a successfully loaded list. Selection and the // greyed-out state derive from this one function, so a preset that cannot be selected can never @@ -322,22 +345,6 @@ const AddAutoRouterTab: React.FC = ({ [modelsLoading, modelsUnverifiable, availability, groupsOnlyAvailability], ); - const sortedPresetOptions = React.useMemo( - () => - presets - .map((preset) => ({ preset, availability: presetAvailability(preset) })) - .sort((a, b) => Number(b.availability.kind === "available") - Number(a.availability.kind === "available")), - [presets, presetAvailability], - ); - - const templateItems = React.useMemo( - () => [ - ...sortedPresetOptions.map(({ preset }) => ({ value: preset.key, label: preset.label })), - { value: "custom", label: "Start from scratch" }, - ], - [sortedPresetOptions], - ); - const applyPrefill = (prefill: PresetPrefill) => { setEditingTiers(false); setComplexityRouterConfig(prefill.complexityRouterConfig); @@ -349,16 +356,20 @@ const AddAutoRouterTab: React.FC = ({ setEscalationKeywords(prefill.escalationKeywords); }; - const [prefillAttempted, setPrefillAttempted] = useState(false); - const automaticPrefill = automaticSetupLoading || modelsUnverifiable ? null : automaticRouterConfig; - if (automaticPrefill && !prefillAttempted) { + if (advanced && !prefillAttempted && !modelsLoading && simpleConfig) { setPrefillAttempted(true); - setComplexityRouterConfig((current) => prefillEmptyTiers(current, automaticPrefill)); + setComplexityRouterConfig((current) => + !current.custom_tier_set && + !isForecastClassifier(current.classifier_type) && + activeTierRows(current).every((row) => row.models.length === 0) + ? { ...current, tiers: simpleConfig.tiers } + : current, + ); } const handlePresetChange = (presetKey: string | undefined) => { + setPrefillAttempted(true); if (!presetKey || presetKey === "custom") { - setPrefillAttempted(true); setSelectedPreset(presetKey); applyPrefill(buildEmptyPrefill()); @@ -372,7 +383,6 @@ const AddAutoRouterTab: React.FC = ({ const presetState = presetAvailability(preset); if (presetState.kind !== "available") return; - setPrefillAttempted(true); setSelectedPreset(presetKey); applyPrefill(buildPresetPrefill(preset.complexity_router_config, availability)); }; @@ -413,7 +423,11 @@ const AddAutoRouterTab: React.FC = ({ }, !requiresTeamScope || Boolean(watchedTeamId), ); - const saveBlockedReason = submitBlockedReason ?? routerAvailability.saveBlockedReason; + const simpleBlockedReason = advanced ? null : simpleSetupError(modelsLoading, inventoryUnavailable, everyday, hard); + const saveBlockedReason = + !advanced && requiresTeamScope && !watchedTeamId + ? "Select a team to see available models." + : simpleBlockedReason ?? submitBlockedReason ?? routerAvailability.saveBlockedReason; const handleClassifierChange = (config: ComplexityRouterConfigValue) => { setSelectedPreset(undefined); setComplexityRouterConfig(config); @@ -540,71 +554,20 @@ const AddAutoRouterTab: React.FC = ({ setIsTestModalVisible(true); }; - const tierSetup = ( -
-
- -

Replaces classifier and model choices

- {presetsPending &&
Loading templates...
} - {presetsUnavailable && ( -
- Could not load templates, so only Start from scratch is shown.{" "} - -
- )} -
-
- ); - const configurationForm = ( void refetchPresets()} + /> + } editingTiers={editingTiers} onEditingTiersChange={setEditingTiers} modelInfo={modelInfo} @@ -646,8 +609,8 @@ const AddAutoRouterTab: React.FC = ({ )}
-
- {isAdmin && ( +
+ {isAdmin && advanced && (
= ({ )} - - } - judgeModel={ - model.mode !== "embedding") - .map((model) => ({ value: model.model_group, label: model.model_group }))} - showValidationErrors={showValidationErrors} - /> - } - > - - {modelsUnverifiable && ( -

- Could not load available models.{" "} - -

- )} - {configurationForm} - -
- - - Need Help? - - } - /> - Get help on our github - -
- - - - +

+ )} + {configurationForm} + + + )} + {!advanced && routerAvailability.isError && ( +

+ Could not check availability.{" "} + +

+ )} + {!advanced && routerAvailability.data?.error && ( +

+ {routerAvailability.data.error} +

+ )} +
+ + - {isTestingConnection && } - Test Connection - - - - -
-
- - + Need Help? + + } + /> + Get help on our github + +
+ + + + + + + +
+
diff --git a/ui/litellm-dashboard/src/components/add_model/auto_setup.test.ts b/ui/litellm-dashboard/src/components/add_model/auto_setup.test.ts index 7b12a0d54fd..4097afc17fe 100644 --- a/ui/litellm-dashboard/src/components/add_model/auto_setup.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/auto_setup.test.ts @@ -1,274 +1,325 @@ -import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; +import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import type { AutoRouterDeployment } from "@/app/(dashboard)/hooks/models/useModels"; -import { buildModelAvailability, deploymentRefsFromModelInfo } from "@/lib/autorouter_presets"; import { - buildAutomaticRouterConfig, - buildPreferredTierModels, - prefillEmptyTiers, - type PreferredTierModels, + deployedRouterModels, + recommendSolverPair, + solverPairConfig, + nextRouterName, + simpleSetupError, } from "./auto_setup"; -const models = (...names: string[]) => names.map((model_group) => ({ model_group, mode: "chat" })); -const reasoningModel = (model_group: string, supported_reasoning_efforts: string[]) => ({ - model_group, - mode: "chat", - supports_reasoning: true, - supported_reasoning_efforts, -}); -const deployment = (model_name: string, model = model_name): AutoRouterDeployment => ({ - model_name, +const deployment = (name: string, model: string, extra: Partial = {}): AutoRouterDeployment => ({ + model_name: name, litellm_params: { model }, + ...extra, }); -const tierModels = (config: ReturnType) => - config && Object.values(config.tiers).map((tier) => (typeof tier === "string" ? tier : tier[0])); +const resolve = (...rows: AutoRouterDeployment[]) => + deployedRouterModels( + rows.map((row) => ({ model_group: row.model_name!, mode: "chat" })), + rows, + ); +const names = (pair: ReturnType) => pair && [pair.everyday.name, pair.hard.name]; +const ladder = () => + resolve( + deployment("small", "anthropic/claude-haiku-5-5"), + deployment("medium", "anthropic/claude-sonnet-5-5"), + deployment("large", "anthropic/claude-opus-5-5"), + ); -describe("buildPreferredTierModels", () => { - it("recognizes curated models that are not in a preset", () => { - const available = ["gpt-5.6-luna", "claude-sonnet-5", "grok-4.6", "deepseek-v4-pro"]; - const availability = buildModelAvailability(available, []); - const preferred = buildPreferredTierModels([], availability); - const expected: PreferredTierModels = { - SIMPLE: ["gpt-5.6-luna"], - MEDIUM: ["claude-sonnet-5"], - COMPLEX: ["deepseek-v4-pro", "grok-4.6"], - REASONING: ["deepseek-v4-pro", "grok-4.6"], - }; +const catalog: Record = JSON.parse( + readFileSync(new URL("../../../../../model_prices_and_context_window.json", import.meta.url), "utf8"), +); +const catalogOpusModels = Object.entries(catalog).filter( + ([id, metadata]) => metadata.mode === "chat" && /claude-opus-\d/.test(id) && !/-(?:fast|think)$/.test(id), +); - expect(preferred).toEqual(expected); - }); - - it("prefers the current model ladder over older preset entries", () => { - const availability = buildModelAvailability(["gpt-5.6-luna", "gpt-4o-mini"], []); - const preferred = buildPreferredTierModels( - [ - { - key: "old", - label: "Old", - description: "Old model", - complexity_router_config: { - tiers: { SIMPLE: ["gpt-4o-mini"], MEDIUM: [], COMPLEX: [], REASONING: [] }, - classifier_type: "heuristic_v2", - }, - }, - ], - availability, - ); - - expect(preferred.SIMPLE).toEqual(["gpt-5.6-luna", "gpt-4o-mini"]); - }); -}); - -describe("buildAutomaticRouterConfig", () => { - it("selects native Terra and Sol groups with their reasoning settings, retaining cloud fallback", () => { - const modelNames = ["gpt-5.6-terra", "gpt-5.6-sol"]; - const deployments = modelNames.flatMap((model) => [ - deployment(model, `azure/${model}`), - deployment(`z-native-${model}`, `openai/${model}`), - ]); - const available = deployments.map(({ model_name }) => reasoningModel(model_name!, ["none", "high"])); - const availability = buildModelAvailability( - available.map(({ model_group }) => model_group), - deploymentRefsFromModelInfo(deployments), - ); - const preferred = buildPreferredTierModels([], availability); - const config = buildAutomaticRouterConfig(available, deployments, preferred); - - expect(tierModels(config)).toEqual([ - "z-native-gpt-5.6-terra", - "z-native-gpt-5.6-terra", - "z-native-gpt-5.6-sol", - "z-native-gpt-5.6-sol", - ]); - expect(config?.tier_model_params).toEqual({ - REASONING: { "z-native-gpt-5.6-sol": { reasoning_effort: "high" } }, - }); - - const cloudOnly = models(...modelNames); - const cloudAvailability = buildModelAvailability(modelNames, deploymentRefsFromModelInfo(deployments)); - const cloudPreferred = buildPreferredTierModels([], cloudAvailability); - - expect(tierModels(buildAutomaticRouterConfig(cloudOnly, deployments, cloudPreferred))).toEqual([ - "gpt-5.6-terra", - "gpt-5.6-terra", - "gpt-5.6-sol", - "gpt-5.6-sol", - ]); - }); - - it("selects one preferred model for each tier", () => { - const preferred: PreferredTierModels = { - SIMPLE: ["simple"], - MEDIUM: ["medium"], - COMPLEX: ["complex"], - REASONING: ["reasoning"], - }; - - expect( - tierModels(buildAutomaticRouterConfig(models("simple", "medium", "complex", "reasoning"), [], preferred)), - ).toEqual(["simple", "medium", "complex", "reasoning"]); - }); - - it.each([ - { - provider: "OpenAI", - available: ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-6-astra"], - expected: ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-6-astra", "gpt-6-astra"], - supportedEfforts: ["low", "medium", "high", "xhigh", "max"], - effort: "max", - }, - { - provider: "Anthropic", - available: ["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5"], - expected: ["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5", "claude-opus-5"], - supportedEfforts: ["low", "medium", "high", "max"], - effort: "max", - }, - { - provider: "Google", - available: ["gemini-3.5-flash-lite", "gemini-3.8-flash", "gemini-3.1-pro-preview"], - expected: ["gemini-3.5-flash-lite", "gemini-3.8-flash", "gemini-3.1-pro-preview", "gemini-3.1-pro-preview"], - supportedEfforts: ["low", "medium", "high"], - effort: "high", - }, - { - provider: "DeepSeek", - available: ["deepseek-v4-flash", "deepseek-v4-pro"], - expected: ["deepseek-v4-flash", "deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-pro"], - supportedEfforts: ["none", "high"], - effort: "high", - }, - { - provider: "xAI", - available: ["grok-4.6"], - expected: ["grok-4.6", "grok-4.6", "grok-4.6", "grok-4.6"], - supportedEfforts: ["low", "medium", "high", "xhigh"], - effort: "xhigh", - }, - ])( - "uses the current $provider ladder and strongest advertised reasoning effort", - ({ available, expected, supportedEfforts, effort }) => { - const availability = buildModelAvailability(available, []); - const preferred = buildPreferredTierModels([], availability); - const modelInfo = models(...available).map((model) => - model.model_group === expected[3] ? reasoningModel(model.model_group, supportedEfforts) : model, - ); - - const config = buildAutomaticRouterConfig(modelInfo, [], preferred); - - expect(tierModels(config)).toEqual(expected); - expect(config?.tier_model_params).toEqual({ - REASONING: { [expected[3]]: { reasoning_effort: effort } }, - }); +describe("deployedRouterModels", () => { + it.each(catalogOpusModels)( + "recognizes the catalog's standard Opus deployment %s behind a custom alias", + (id, metadata) => { + const row = { + model_name: "custom-deployment", + litellm_params: { model: id, custom_llm_provider: metadata.litellm_provider }, + }; + const models = deployedRouterModels([{ model_group: "custom-deployment", mode: "chat" }], [row]); + expect(models).toEqual([expect.objectContaining({ name: "custom-deployment", family: "claude", line: "opus" })]); }, ); - it("never exceeds the selected model group's advertised reasoning efforts", () => { - const available = ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol"]; - const availability = buildModelAvailability(available, []); - const preferred = buildPreferredTierModels([], availability); - const modelInfo = [ - ...models("gpt-5.6-luna", "gpt-5.6-terra"), - reasoningModel("gpt-5.6-sol", ["none", "low", "medium", "high", "xhigh"]), - ]; - - const config = buildAutomaticRouterConfig(modelInfo, [], preferred); - - expect(config?.tier_model_params).toEqual({ - REASONING: { "gpt-5.6-sol": { reasoning_effort: "xhigh" } }, - }); + it("resolves underlying identities independently of deployment aliases and serving providers", () => { + const models = resolve( + deployment("everyday", "bedrock/us.openai.gpt-6.1-sol"), + deployment("hard", "bedrock_converse/global.openai.gpt-6-astra"), + ); + const expectedEveryday = { name: "everyday", family: "openai", line: "sol", version: [6, 1], provider: "Bedrock" }; + const expectedHard = { name: "hard", family: "openai", line: "astra", version: [6], provider: "Bedrock" }; + expect(models).toEqual( + expect.arrayContaining([expect.objectContaining(expectedEveryday), expect.objectContaining(expectedHard)]), + ); + expect(names(recommendSolverPair(models, "quality", "openai"))).toEqual(["everyday", "hard"]); }); - it("leaves reasoning effort unset when the proxy does not report supported values", () => { - const available = ["grok-4.6"]; - const availability = buildModelAvailability(available, []); - const preferred = buildPreferredTierModels([], availability); - - const config = buildAutomaticRouterConfig(models(...available), [], preferred); - - expect(config?.tier_model_params).toBeUndefined(); - }); - - it("reuses the closest available tier when a tier has no match", () => { - const preferred: PreferredTierModels = { - SIMPLE: ["simple"], - MEDIUM: [], - COMPLEX: ["complex"], - REASONING: [], - }; - - expect(tierModels(buildAutomaticRouterConfig(models("simple", "complex"), [], preferred))).toEqual([ - "simple", - "simple", - "complex", - "complex", + it("recognizes provider-specific Claude names and a declared base model for opaque deployments", () => { + const models = resolve( + deployment("small", "vertex_ai/claude-haiku-4-5@20251001"), + deployment("large", "bedrock/arn:example:inference-profile/us.anthropic.claude-opus-5-5-v1:0"), + deployment("middle", "azure/private-alias", { + litellm_params: { model: "azure/private-alias" }, + model_info: { base_model: "gpt-6.1-sol" }, + }), + ); + expect(models.map(({ name, family, provider }) => ({ name, family, provider }))).toEqual([ + { name: "large", family: "claude", provider: "Bedrock" }, + { name: "middle", family: "openai", provider: "Azure" }, + { name: "small", family: "claude", provider: "Vertex AI" }, ]); }); - it("returns null when none of the available models are recommended", () => { - const preferred: PreferredTierModels = { - SIMPLE: ["missing-simple"], - MEDIUM: ["missing-medium"], - COMPLEX: ["missing-complex"], - REASONING: ["missing-reasoning"], - }; - - expect(buildAutomaticRouterConfig(models("unknown-model"), [], preferred)).toBeNull(); + it("uses only caller-visible chat models and excludes blocked and recursive targets", () => { + const rows = [ + deployment("hidden", "anthropic/claude-opus-6"), + deployment("blocked", "anthropic/claude-opus-5", { model_info: { blocked: true } }), + deployment("embedding", "anthropic/claude-opus-5"), + deployment("recursive", "auto_router/complexity_router"), + deployment("ready", "anthropic/claude-sonnet-5"), + ]; + expect( + deployedRouterModels( + [ + { model_group: "blocked", mode: "chat" }, + { model_group: "embedding", mode: "embedding" }, + { model_group: "recursive" }, + { model_group: "ready", mode: "chat" }, + ], + rows, + ).map((model) => model.name), + ).toEqual(["ready"]); }); - it("ignores non-chat models and existing auto routers", () => { - const preferred: PreferredTierModels = { - SIMPLE: ["gpt-4o-mini", "smart-router"], - MEDIUM: [], - COMPLEX: [], - REASONING: [], - }; - const available = [ - { model_group: "gpt-4o-mini", mode: "chat" }, - { model_group: "image-model", mode: "image_generation" }, - { model_group: "smart-router", mode: "chat" }, + it("does not claim a mixed-version or unknown model group is a known version", () => { + const rows = [ + deployment("mixed", "anthropic/claude-opus-5"), + deployment("mixed", "anthropic/claude-opus-5-5"), + deployment("claude-sonnet-5", "openai/custom-model"), + deployment("wildcard", "openai/*"), ]; + const models = deployedRouterModels( + [ + { model_group: "mixed", mode: "chat" }, + { model_group: "claude-sonnet-5", mode: "chat" }, + { model_group: "wildcard", mode: "chat" }, + ], + rows, + ); + expect(models.map((model) => model.name)).toEqual(["claude-sonnet-5", "mixed"]); + expect(models.every((model) => model.family === undefined)).toBe(true); + expect(recommendSolverPair(models, "balanced", "auto")).toBeNull(); + }); + it("keeps homogeneous replicas and ignores blocked replicas when resolving identity", () => { + const rows = [ + deployment("group", "anthropic/claude-opus-5-5"), + deployment("group", "bedrock/anthropic.claude-opus-5-5-v1:0"), + deployment("group", "anthropic/claude-opus-5", { model_info: { blocked: true } }), + ]; + const models = deployedRouterModels([{ model_group: "group" }], rows); + expect(models).toHaveLength(1); + expect(models[0]).toMatchObject({ name: "group", version: [5, 5], provider: "Anthropic, Bedrock" }); + }); + + it("keeps arbitrary canonical names available without pretending an unversioned alias is latest", () => { expect( - tierModels( - buildAutomaticRouterConfig(available, [deployment("smart-router", "auto_router/complexity_router")], preferred), + deployedRouterModels([{ model_group: "claude-opus-8-12" }, { model_group: "claude-opus-latest" }], []).map( + (model) => model.name, ), - ).toEqual(["gpt-4o-mini", "gpt-4o-mini", "gpt-4o-mini", "gpt-4o-mini"]); + ).toEqual(["claude-opus-8-12", "claude-opus-latest"]); }); }); -describe("prefillEmptyTiers", () => { - const empty: ComplexityRouterConfigValue = { - classifier_type: "jev", - jev_classifier_config: { model: "jev-custom", timeout_ms: 4200 }, - classification_mode: "user_turn", - tiers: { SIMPLE: [], MEDIUM: [], COMPLEX: [], REASONING: [] }, - }; - const automatic: ComplexityRouterConfigValue = { - classifier_type: "heuristic_v2", - tiers: { SIMPLE: ["fast"], MEDIUM: ["mid"], COMPLEX: ["strong"], REASONING: ["strong"] }, - tier_model_params: { REASONING: { strong: { reasoning_effort: "high" } } }, - }; - it("fills tiers while preserving a classifier configured before models load", () => { - expect(prefillEmptyTiers(empty, automatic)).toEqual({ - ...empty, - tiers: automatic.tiers, - tier_model_params: automatic.tier_model_params, +describe("recommendSolverPair", () => { + it.each([ + ["cheapest", ["small", "medium"]], + ["balanced", ["small", "large"]], + ["quality", ["medium", "large"]], + ] as const)("uses the requested %s pair", (goal, expected) => { + expect(names(recommendSolverPair(ladder(), goal, "claude"))).toEqual(expected); + }); + + it("selects future deployed releases numerically within each line, without a version allowlist", () => { + const models = resolve( + deployment("old-small", "anthropic/claude-haiku-8-9"), + deployment("new-small", "bedrock/anthropic.claude-haiku-8-10-v1:0"), + deployment("old-large", "anthropic/claude-opus-8-9"), + deployment("new-large", "anthropic/claude-opus-9"), + ); + expect(names(recommendSolverPair(models, "balanced", "claude"))).toEqual(["new-small", "new-large"]); + }); + + it("orders snapshots within a version without confusing a snapshot with the next version", () => { + const models = resolve( + deployment("old", "anthropic/claude-haiku-4-5-20251001"), + deployment("snapshot", "anthropic/claude-haiku-4-5-20251101"), + deployment("next", "anthropic/claude-haiku-4-6"), + deployment("large", "anthropic/claude-opus-5"), + ); + expect(names(recommendSolverPair(models, "balanced", "claude"))).toEqual(["next", "large"]); + expect( + names( + recommendSolverPair( + models.filter((model) => model.name !== "next"), + "balanced", + "claude", + ), + ), + ).toEqual(["snapshot", "large"]); + }); + + it("prefers rolling aliases over snapshots of the same release", () => { + const models = resolve( + deployment("pin", "anthropic/claude-haiku-4-5-20251101"), + deployment("alias", "anthropic/claude-haiku-4-5"), + deployment("hard", "anthropic/claude-opus-5"), + deployment("luna-pin", "openai/gpt-6-luna-2026-01-01"), + deployment("luna", "openai/gpt-6-luna"), + deployment("astra", "openai/gpt-6-astra"), + ); + expect(names(recommendSolverPair(models, "balanced", "claude"))).toEqual(["alias", "hard"]); + expect(names(recommendSolverPair(models, "balanced", "openai"))).toEqual(["luna", "astra"]); + }); + + it.each([ + { models: ["grok-4.20", "grok-4.20-0309"], expected: "grok-4.20" }, + { models: ["grok-4.20-0309", "grok-4.20-0410"], expected: "grok-4.20-0410" }, + { models: ["grok-4.20-0309", "grok-4.21"], expected: "grok-4.21" }, + ])("orders Grok releases and short-date snapshots: $expected", ({ models, expected }) => { + const available = resolve( + deployment("everyday", "openai/gpt-6-luna"), + ...models.map((model) => deployment(model, `xai/${model}`)), + ); + expect(names(recommendSolverPair(available, "balanced", "grok"))).toEqual(["everyday", expected]); + }); + + it("does not rank a major-release snapshot above a newer minor release", () => { + const models = resolve( + deployment("old", "anthropic/claude-5-haiku-20260101"), + deployment("next", "anthropic/claude-haiku-5-1"), + deployment("hard", "anthropic/claude-opus-5"), + ); + expect(names(recommendSolverPair(models, "balanced", "claude"))).toEqual(["next", "hard"]); + }); + + it("prefers a same-family substitute over a matching rank from another family", () => { + const models = [ + ...ladder().filter((model) => model.line !== "haiku"), + ...resolve(deployment("luna", "openai/gpt-6-luna")), + ]; + const pair = recommendSolverPair(models, "balanced", "claude"); + expect(names(pair)).toEqual(["medium", "large"]); + expect(pair?.notice).toContain("closest available pair"); + }); + + it("prefers a complete family for Auto and explains a cross-family substitute when explicitly requested", () => { + const models = [ + ...ladder().filter((model) => model.line === "sonnet"), + ...resolve( + deployment("luna", "bedrock/us.openai.gpt-6-luna"), + deployment("astra", "bedrock/us.openai.gpt-6-astra"), + ), + ]; + expect(names(recommendSolverPair(models, "balanced", "auto"))).toEqual(["luna", "astra"]); + const selected = recommendSolverPair(models, "quality", "claude"); + expect(names(selected)).toEqual(["medium", "astra"]); + expect(selected?.notice).toContain("two model families"); + }); + + it.each([ + ["glm", "zai/glm-7.3-flash", "zai/glm-7.3"], + ["deepseek", "deepseek/deepseek-v9-flash", "deepseek/deepseek-v9-pro"], + ] as const)("handles the %s two-line family for every goal", (family, small, large) => { + const models = resolve(deployment("small", small), deployment("large", large)); + for (const goal of ["cheapest", "balanced", "quality"] as const) + expect(names(recommendSolverPair(models, goal, family))).toEqual(["small", "large"]); + }); + + it("does not fabricate a second solver or substitute a different family when none of the requested family exists", () => { + expect(recommendSolverPair(ladder().slice(0, 1), "balanced", "auto")).toBeNull(); + expect(recommendSolverPair(ladder(), "balanced", "openai")).toBeNull(); + expect(recommendSolverPair([], "balanced", "auto")).toBeNull(); + }); + + it("retains Gemini family suggestions and recognizes the existing Grok fallback", () => { + const models = resolve( + deployment("lite", "gemini/gemini-3.5-flash-lite"), + deployment("flash-old", "vertex_ai/gemini-3.5-flash"), + deployment("flash", "vertex_ai/gemini-3.8-flash"), + deployment("pro", "gemini/gemini-3.1-pro-preview"), + deployment("grok", "xai/grok-4.6"), + ); + expect(names(recommendSolverPair(models, "cheapest", "gemini"))).toEqual(["lite", "flash"]); + expect(names(recommendSolverPair(models, "balanced", "gemini"))).toEqual(["lite", "pro"]); + expect(names(recommendSolverPair(models, "quality", "gemini"))).toEqual(["flash", "pro"]); + expect(models.find((m) => m.name === "grok")).toMatchObject({ family: "grok", provider: "xAI" }); + expect(names(recommendSolverPair(models, "balanced", "grok"))).toEqual(["lite", "grok"]); + }); + + it("uses the latest deployed Terra as a fallback without replacing the preferred OpenAI pairs", () => { + const models = resolve( + deployment("terra-old", "openai/gpt-5.6-terra"), + deployment("terra", "openai/gpt-6.1-terra"), + deployment("sol", "openai/gpt-6.1-sol"), + ); + expect(names(recommendSolverPair(models, "balanced", "openai"))).toEqual(["terra", "sol"]); + const complete = [ + ...models, + ...resolve(deployment("luna", "openai/gpt-6-luna"), deployment("astra", "openai/gpt-6-astra")), + ]; + expect(names(recommendSolverPair(complete, "cheapest", "openai"))).toEqual(["luna", "sol"]); + expect(names(recommendSolverPair(complete, "balanced", "openai"))).toEqual(["luna", "astra"]); + expect(names(recommendSolverPair(complete, "quality", "openai"))).toEqual(["sol", "astra"]); + }); + + it("suggests a deterministic equal-rank cross-family fallback only when no ordered pair exists", () => { + const models = resolve(deployment("opus", "anthropic/claude-opus-5-5"), deployment("astra", "openai/gpt-6-astra")); + const pair = recommendSolverPair(models, "balanced", "auto"); + expect(names(pair)).toEqual(["astra", "opus"]); + expect(pair?.notice).toContain("two model families"); + expect(names(recommendSolverPair([...models].reverse(), "balanced", "auto"))).toEqual(names(pair)); + const withLuna = [...models, ...resolve(deployment("luna", "openai/gpt-6-luna"))]; + expect(names(recommendSolverPair(withLuna, "balanced", "auto"))).toEqual(["luna", "astra"]); + }); + + it("does not prefer native-provider deployments over a newer cloud deployment", () => { + const models = resolve( + deployment("native", "openai/gpt-6-luna"), + deployment("cloud", "bedrock/global.openai.gpt-6.2-luna"), + deployment("strong", "bedrock/global.openai.gpt-6-astra"), + ); + expect(names(recommendSolverPair(models, "balanced", "openai"))).toEqual(["cloud", "strong"]); + }); +}); + +describe("solverPairConfig", () => { + it("builds two solver references using built-in rules without paid classifiers, custom tiers or output overrides", () => { + expect(solverPairConfig("everyday", "hard")).toEqual({ + classifier_type: "heuristic", + classification_mode: "user_turn", + tiers: { SIMPLE: ["everyday"], MEDIUM: ["everyday"], COMPLEX: ["hard"], REASONING: ["hard"] }, }); }); - it("leaves the draft alone when no recommended models are available", () => { - expect(prefillEmptyTiers(empty, null)).toBe(empty); +}); + +describe("simple setup validation", () => { + it("avoids existing names without requiring a naming decision", () => { + expect(nextRouterName([])).toBe("auto-router"); + expect(nextRouterName(["auto-router", "auto-router-2", "auto-router-4"])).toBe("auto-router-3"); }); - it("preserves a partial manual assignment", () => { - const partial = { ...empty, tiers: { ...empty.tiers, SIMPLE: ["manual"] } }; - expect(prefillEmptyTiers(partial, automatic)).toBe(partial); - }); - it("preserves a custom tier set created before models load", () => { - const custom = { ...empty, custom_tier_set: { tiers: [], fallback_tier_id: "" } }; - expect(prefillEmptyTiers(custom, automatic)).toBe(custom); - }); - it.each(["capability", "llm_v2"] as const)("does not prefill %s solver pools", (classifier_type) => { - const solver = { ...empty, classifier_type }; - expect(prefillEmptyTiers(solver, automatic)).toBe(solver); + it("blocks stale or incomplete inventory and same-line selections", () => { + const [small, large] = [ladder()[0], ladder()[2]]; + expect(simpleSetupError(true, false, small, large)).not.toBeNull(); + expect(simpleSetupError(false, true, small, large)).not.toBeNull(); + expect(simpleSetupError(false, false, small)).not.toBeNull(); + expect(simpleSetupError(false, false, small, small)).not.toBeNull(); + expect(simpleSetupError(false, false, small, large)).toBeNull(); }); }); diff --git a/ui/litellm-dashboard/src/components/add_model/auto_setup.ts b/ui/litellm-dashboard/src/components/add_model/auto_setup.ts index 8b60336cd27..39476470928 100644 --- a/ui/litellm-dashboard/src/components/add_model/auto_setup.ts +++ b/ui/litellm-dashboard/src/components/add_model/auto_setup.ts @@ -1,110 +1,263 @@ -import { activeTierRows } from "./tier_rows"; -import { isForecastClassifier } from "./forecast_classifier_config"; -import { isAutoRouterDeployment, type AutoRouterDeployment } from "@/app/(dashboard)/hooks/models/useModels"; +import type { AutoRouterDeployment } from "@/app/(dashboard)/hooks/models/useModels"; +import { isAutoRouterDeployment } from "@/app/(dashboard)/hooks/models/useModels"; import type { ModelGroup } from "@/components/llm_calls/fetch_models"; -import { resolveAvailableModel, type AutoRouterPreset, type ModelAvailability } from "@/lib/autorouter_presets"; import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; -const TIER_NAMES = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] as const; -type TierName = (typeof TIER_NAMES)[number]; -export type PreferredTierModels = Record; +export const ROUTER_FAMILIES = { + claude: "Claude", + openai: "OpenAI", + gemini: "Gemini", + grok: "Grok", + glm: "GLM", + deepseek: "DeepSeek", +} as const; +export const ROUTER_GOALS = { cheapest: "Cheapest", balanced: "Balanced", quality: "Best quality" } as const; +export type RouterFamily = keyof typeof ROUTER_FAMILIES; +export type RouterGoal = keyof typeof ROUTER_GOALS; +export type FamilyPreference = RouterFamily | "auto"; -const REASONING_EFFORT_STRENGTH = ["max", "xhigh", "high", "medium", "low", "minimal", "none"] as const; +type ModelIdentity = { + family: RouterFamily; + line: string; + rank: number; + version: readonly number[]; + snapshot: number; + model: string; +}; +export type SolverModel = Partial & { name: string; provider: string; model: string }; +export type RouterModel = ModelIdentity & SolverModel; +export type SolverPair = { everyday: RouterModel; hard: RouterModel; notice: string | null }; -const CURRENT_TIER_MODELS: PreferredTierModels = { - SIMPLE: ["gpt-5.6-luna", "claude-haiku-4-5", "gemini-3.5-flash-lite", "deepseek-v4-flash"], - MEDIUM: ["gpt-5.6-terra", "claude-sonnet-5", "gemini-3.8-flash", "deepseek-v4-flash"], - COMPLEX: ["gpt-6-astra", "gpt-5.6-sol", "claude-opus-5", "gemini-3.1-pro-preview", "deepseek-v4-pro", "grok-4.6"], - REASONING: ["gpt-6-astra", "gpt-5.6-sol", "claude-opus-5", "gemini-3.1-pro-preview", "deepseek-v4-pro", "grok-4.6"], +const releaseVersion = (raw: string, date?: string) => { + const snapshot = raw.match(/[-.](\d{8})$/); + const release = snapshot ? raw.slice(0, -9) : raw; + return { + version: release.split(/[.-]/).map(Number), + // An undated alias follows the current snapshot of its release. + snapshot: date || snapshot ? Number((date ?? snapshot![1]).replaceAll("-", "")) : Number.MAX_SAFE_INTEGER, + }; }; -export const buildPreferredTierModels = ( - presets: AutoRouterPreset[], - availability: ModelAvailability, -): PreferredTierModels => - Object.fromEntries( - TIER_NAMES.map((tier) => [ - tier, - Array.from( - new Set( - [ - ...CURRENT_TIER_MODELS[tier], - ...presets.flatMap((preset) => preset.complexity_router_config.tiers[tier]), - ].flatMap((model) => { - const resolved = resolveAvailableModel(model, availability); - return resolved ? [resolved] : []; - }), - ), +const parseModel = (raw: string): ModelIdentity | null => { + const model = raw + .toLowerCase() + .split("/") + .at(-1)! + .replace(/^(?:us|eu|apac|global|au|jp|us-gov)\./, "") + .replace(/^(?:anthropic|openai|zai|deepseek)\./, "") + .replace(/^databricks-/, "") + .replace(/@default$/, "") + .replace(/-v\d+(?::\d+)?$/, ""); + const versionFirst = model.match(/^claude-(\d+(?:[.-]\d+)*)-(haiku|sonnet|opus)(?:[-@]([0-9]{8}))?$/); + const claude = + model.match(/^claude-(haiku|sonnet|opus)-(\d+(?:[.-]\d+)*)(?:@([0-9]+))?$/) ?? + (versionFirst ? [versionFirst[0], versionFirst[2], versionFirst[1], versionFirst[3]] : null); + if (claude) + return { + family: "claude", + line: claude[1], + rank: ["haiku", "sonnet", "opus"].indexOf(claude[1]), + ...releaseVersion(claude[2], claude[3]), + model, + }; + const openai = model.match(/^gpt-(\d+(?:[.-]\d+)*)-(luna|terra|sol|astra)(?:-(\d{4}-\d{2}-\d{2}))?$/); + if (openai) + return { + family: "openai", + line: openai[2], + rank: openai[2] === "terra" ? 0.5 : ["luna", "sol", "astra"].indexOf(openai[2]), + ...releaseVersion(openai[1], openai[3]), + model, + }; + const gemini = model.match(/^gemini-(\d+(?:[.-]\d+)*)-(flash-lite|flash|pro)(?:-preview)?$/); + if (gemini) + return { + family: "gemini", + line: gemini[2], + rank: ["flash-lite", "flash", "pro"].indexOf(gemini[2]), + ...releaseVersion(gemini[1]), + model, + }; + const grok = model.match(/^grok-(\d+(?:[.-]\d+)*?)(?:-(\d{4}))?$/); + if (grok) return { family: "grok", line: "grok", rank: 2, ...releaseVersion(grok[1], grok[2]), model }; + const glm = model.match(/^glm-(\d+(?:[.-]\d+)*)(-flash)?$/); + if (glm) + return { + family: "glm", + line: glm[2] ? "flash" : "glm", + rank: glm[2] ? 0 : 2, + ...releaseVersion(glm[1]), + model, + }; + const deepseek = model.match(/^deepseek-v(\d+(?:[.-]\d+)*)-(flash|pro)$/); + if (deepseek) + return { + family: "deepseek", + line: deepseek[2], + rank: deepseek[2] === "flash" ? 0 : 2, + ...releaseVersion(deepseek[1]), + model, + }; + return null; +}; + +const identityOf = (deployment: AutoRouterDeployment): ModelIdentity | null => + parseModel(deployment.litellm_params?.model ?? "") ?? + parseModel(deployment.model_info?.base_model ?? deployment.litellm_params?.base_model ?? ""); + +const providerOf = (deployment: AutoRouterDeployment, group: ModelGroup, family?: RouterFamily): string => { + const raw = deployment.litellm_params?.model ?? ""; + const provider = + deployment.litellm_params?.custom_llm_provider || + (raw.includes("/") ? raw.split("/")[0] : group.providers?.join(", ")) || + (family + ? { claude: "anthropic", glm: "zai", openai: "openai", gemini: "gemini", grok: "xai", deepseek: "deepseek" }[ + family + ] + : ""); + if (provider.startsWith("bedrock")) return "Bedrock"; + return ( + ( + { + anthropic: "Anthropic", + openai: "OpenAI", + gemini: "Google AI", + xai: "xAI", + azure: "Azure", + azure_ai: "Azure AI", + databricks: "Databricks", + github_copilot: "GitHub Copilot", + "vertex_ai-anthropic_models": "Vertex AI", + vertex_ai: "Vertex AI", + zai: "Z.ai", + deepseek: "DeepSeek", + } as Record + )[provider] ?? provider + ); +}; + +const compareVersion = (left: readonly number[], right: readonly number[]): number => { + const index = Array.from({ length: Math.max(left.length, right.length) }, (_, i) => i).find( + (i) => (left[i] ?? 0) !== (right[i] ?? 0), + ); + return index === undefined ? 0 : (left[index] ?? 0) - (right[index] ?? 0); +}; + +const compareRelease = (left: ModelIdentity, right: ModelIdentity): number => + compareVersion(left.version, right.version) || left.snapshot - right.snapshot; + +export const deployedRouterModels = ( + groups: readonly ModelGroup[], + deployments: readonly AutoRouterDeployment[], +): SolverModel[] => + groups + .flatMap((group) => { + if (group.mode && group.mode !== "chat") return []; + const rows = deployments.filter((row) => row.model_name === group.model_group); + if ( + rows.some(isAutoRouterDeployment) || + group.model_group.includes("*") || + group.model_group.startsWith("auto_router/") + ) + return []; + const active = rows.filter((row) => !row.model_info?.blocked); + if (rows.length > 0 && active.length === 0) return []; + if (active.some((row) => row.model_info?.mode && row.model_info.mode !== "chat")) return []; + if (active.some((row) => row.litellm_params?.model?.includes("*"))) return []; + const identities = active.length > 0 ? active.map(identityOf) : [parseModel(group.model_group)]; + const first = identities[0]; + const homogeneous = + first && + identities.every((identity) => { + if (!identity) return false; + const sameLine = identity.family === first.family && identity.line === first.line; + return sameLine && compareRelease(identity, first) === 0; + }); + const identity = homogeneous ? first : undefined; + const providers = + active.length > 0 + ? active.map((row) => providerOf(row, group, identity?.family)) + : [providerOf({}, group, identity?.family)]; + return [ + { + ...identity, + name: group.model_group, + model: identity?.model ?? group.model_group, + provider: [...new Set(providers)].toSorted().join(", "), + }, + ]; + }) + .toSorted((a, b) => a.name.localeCompare(b.name)); + +const latestLines = (models: readonly RouterModel[]): RouterModel[] => + models.filter( + (model) => + !models.some( + (other) => other.family === model.family && other.line === model.line && compareRelease(other, model) > 0, ), - ]), - ) as PreferredTierModels; - -const selectPreferredTierModels = ( - preferredByTier: PreferredTierModels, - usableNames: ReadonlySet, -): [string, string, string, string] | null => { - const preferred = TIER_NAMES.map((tier) => preferredByTier[tier].find((name) => usableNames.has(name))); - const candidates = preferred.flatMap((model, tier) => (model ? [{ model, tier }] : [])); - if (candidates.length === 0) return null; - - const nearest = (tier: number): string => - [...candidates].sort( - (left, right) => Math.abs(left.tier - tier) - Math.abs(right.tier - tier) || left.tier - right.tier, - )[0].model; - return preferred.map((model, tier) => model ?? nearest(tier)) as [string, string, string, string]; -}; - -export const buildAutomaticRouterConfig = ( - models: ModelGroup[], - deployments: AutoRouterDeployment[], - preferredByTier: PreferredTierModels, -): ComplexityRouterConfigValue | null => { - const autoRouterNames: ReadonlySet = new Set( - deployments - .filter(isAutoRouterDeployment) - .flatMap((deployment) => (deployment.model_name ? [deployment.model_name] : [])), ); - const names = Array.from( - new Set( - models - .filter((model) => model.mode === undefined || model.mode === "chat") - .map((model) => model.model_group) - .filter((name) => name && !name.startsWith("auto_router/") && !autoRouterNames.has(name)), - ), + +const requestedRanks = (goal: RouterGoal): readonly [number, number] => + (({ cheapest: [0, 1], quality: [1, 2], balanced: [0, 2] }) as const)[goal]; + +export const recommendSolverPair = ( + models: readonly SolverModel[], + goal: RouterGoal, + family: FamilyPreference, +): SolverPair | null => { + const recognized = models.filter((model): model is RouterModel => model.family !== undefined); + const latest = latestLines(recognized); + const [low, high] = requestedRanks(goal); + const pairs = latest.flatMap((everyday) => + latest + .filter((hard) => { + const matchesFamily = family === "auto" || everyday.family === family || hard.family === family; + const ordered = hard.rank > everyday.rank; + const equalRankFallback = hard.rank === everyday.rank && hard.family !== everyday.family; + return (ordered || equalRankFallback) && matchesFamily; + }) + .map((hard) => ({ everyday, hard })), ); - if (names.length === 0) return null; - const usableNames: ReadonlySet = new Set(names); - const selected = selectPreferredTierModels(preferredByTier, usableNames); - if (selected === null) return null; - - const supportedReasoningEfforts = models.find( - (model) => model.model_group === selected[3], - )?.supported_reasoning_efforts; - const reasoningEffort = REASONING_EFFORT_STRENGTH.find((effort) => supportedReasoningEfforts?.includes(effort)); - - return { - tiers: { - SIMPLE: [selected[0]], - MEDIUM: [selected[1]], - COMPLEX: [selected[2]], - REASONING: [selected[3]], - }, - classifier_type: "heuristic_v2", - ...(reasoningEffort && { - tier_model_params: { REASONING: { [selected[3]]: { reasoning_effort: reasoningEffort } } }, - }), - }; + const score = ({ everyday, hard }: { everyday: RouterModel; hard: RouterModel }): number => + (everyday.family === hard.family ? 0 : 100) + + (everyday.rank === hard.rank ? 10 : 0) + + Math.abs(everyday.rank - low) + + Math.abs(hard.rank - high); + const chosen = pairs.toSorted( + (a, b) => + score(a) - score(b) || a.everyday.name.localeCompare(b.everyday.name) || a.hard.name.localeCompare(b.hard.name), + )[0]; + if (!chosen) return null; + const mixed = chosen.everyday.family !== chosen.hard.family; + const substituted = chosen.everyday.rank !== low || chosen.hard.rank !== high; + const mixedNotice = mixed ? "A complete pair is unavailable in one family. Using two model families." : null; + const substituteNotice = substituted ? "Using the closest available pair in this family." : null; + return { ...chosen, notice: mixedNotice ?? substituteNotice }; }; -export const prefillEmptyTiers = ( - current: ComplexityRouterConfigValue, - automatic: ComplexityRouterConfigValue | null, -): ComplexityRouterConfigValue => { - if (!automatic || current.custom_tier_set || isForecastClassifier(current.classifier_type)) return current; - if (activeTierRows(current).some((row) => row.models.length > 0)) return current; - return { - ...current, - tiers: { ...current.tiers, ...automatic.tiers }, - tier_model_params: { ...current.tier_model_params, ...automatic.tier_model_params }, - }; +export const solverPairConfig = (everyday: string, hard: string): ComplexityRouterConfigValue => ({ + classifier_type: "heuristic", + classification_mode: "user_turn", + tiers: { SIMPLE: [everyday], MEDIUM: [everyday], COMPLEX: [hard], REASONING: [hard] }, +}); + +export const simpleSetupError = ( + loading: boolean, + failed: boolean, + everyday?: SolverModel, + hard?: SolverModel, +): string | null => { + if (loading) return "Loading deployed models"; + if (failed) return "Could not verify deployed models"; + if (!everyday || !hard) return "Select two different deployed models"; + if (everyday.name === hard.name) return "Select two different deployed models"; + return null; +}; + +export const nextRouterName = (names: readonly string[]): string => { + const candidates = [ + "auto-router", + ...Array.from({ length: names.length + 1 }, (_, index) => `auto-router-${index + 2}`), + ]; + return candidates.find((name) => !names.includes(name))!; }; From 7045139c3a6fe81f80895cb3887882056b5755fe Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 26 Oct 2026 15:40:19 -0700 Subject: [PATCH 30/73] fix(router): stop concurrent deployment deletes from crashing deployment lookups (#47680) * fix(router): stop concurrent deployment deletes from crashing deployment lookups delete_deployment and upsert_deployment shrink model_list before they repair the index maps, so a lookup on another thread in that window read a stale index: IndexError, or the neighbouring deployment and its credentials. An add racing a delete could corrupt the index for good. One RLock now covers model_list and the three index maps. Delete, upsert, add and the set_model_list reset change them under it, and every lookup that turns an index into a deployment reads under it. Fixes #45242 * test(router): release the held delete when the read reaches the index lock The race tests released the held write after a fixed 0.2 s, so a reader the runner stalled past it read the repaired list and passed on unfixed code. The read now runs on its own thread and the write stays held until the read returns or reaches the router's index lock; the 10 s bound only fails a broken synchronization. The integration test gains the audit cells: chat, streaming, Responses and Anthropic messages calls (sync and async, by model name and team public name) during a held delete, the same for upsert, add and set_model_list, a request for the deployment being deleted, a provider error, a double delete, and thread and async bursts during upsert churn. * test(router): cover the early return of a delete and the team filter of get_model_ids delete_deployment now returns None from inside the index lock, and get_model_ids by name reads its deployments through _deployments_named; pin both: deleting an unknown id returns None and leaves the list alone, and exclude_team_models drops only the team deployment. * fix(router): read deployments by name under the index lock outside the Router The response polling provider check, the complexity router's adaptive pool and the savings baseline turned the name index into list positions themselves, so a concurrent delete could still hand them a stale slot. They now read through Router.deployments_named, which takes the deployment index lock. * fix(router): type the locked deployment lookups as DeploymentTypedDict deployments_named and _team_deployments_named returned tuple[Any, ...]. They now return DeploymentTypedDict rows, which is what model_list holds. The adaptive pool setup in the Router and the complexity router drop their branches for pydantic Deployment rows, which these lookups never return, and should_include_deployment accepts the typed row. --------- Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com> --- .../proxy/response_polling/polling_handler.py | 5 +- litellm/router.py | 243 ++++----- .../complexity_router/complexity_router.py | 15 +- litellm/router_strategy/savings_baseline.py | 11 +- .../router_code_coverage.py | 1 + ...router_concurrent_deployment_delete_sdk.py | 474 ++++++++++++++++++ .../test_response_polling_handler.py | 37 +- .../router_strategy/test_complexity_router.py | 21 +- .../router_strategy/test_savings_baseline.py | 3 +- tests/unit/test_router/test_router.py | 321 +++++++++++- 10 files changed, 964 insertions(+), 167 deletions(-) create mode 100644 tests/integration/sdk/test_router_concurrent_deployment_delete_sdk.py diff --git a/litellm/proxy/response_polling/polling_handler.py b/litellm/proxy/response_polling/polling_handler.py index 787f27f56c8..e50587b1a0e 100644 --- a/litellm/proxy/response_polling/polling_handler.py +++ b/litellm/proxy/response_polling/polling_handler.py @@ -308,10 +308,7 @@ def should_use_polling_for_request( # Otherwise, check ALL deployments for this model_name in router elif llm_router is not None: try: - # Get all deployment indices for this model name - indices: Final = llm_router.model_name_to_deployment_indices.get(model, []) - for idx in indices: - deployment_dict = llm_router.model_list[idx] + for deployment_dict in llm_router.deployments_named(model) or (): litellm_params = deployment_dict.get("litellm_params", {}) # Check custom_llm_provider first diff --git a/litellm/router.py b/litellm/router.py index 7993b5b921c..3af39736e25 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1440,6 +1440,9 @@ class Router: # Maps (team_id, team_public_model_name) -> list of indices in model_list self.team_model_to_deployment_indices: dict[tuple[str, str], list[int]] = {} self.team_public_model_names: frozenset[str] = frozenset() + # Guards model_list and the three index maps above: writers change them, and readers turn an + # index into a deployment, under it, so a lookup never lands between a removal and its re-index + self._deployment_index_lock = threading.RLock() # Initialize cache attributes that ``_invalidate_model_group_info_cache`` # and ``_invalidate_access_groups_cache`` touch *before* the first @@ -2180,8 +2183,8 @@ class Router: public_model_name: Final = deployment.model_info.team_public_model_name if team_id is None or public_model_name is None: return False - sibling_indices: Final = self.team_model_to_deployment_indices.get((team_id, public_model_name)) or () - routable_siblings: Final = self._filter_blocked_deployments([self.model_list[idx] for idx in sibling_indices]) + siblings: Final = self._team_deployments_named(team_id, public_model_name) or () + routable_siblings: Final = self._filter_blocked_deployments([dict(sibling) for sibling in siblings]) return len(routable_siblings) > 1 _OVERRIDABLE_ROUTING_STRATEGIES: frozenset[str] = frozenset({"simple-shuffle", *_DEFAULT_SELECTOR_ATTR_BY_STRATEGY}) @@ -10827,20 +10830,17 @@ class Router: # O(k) via the name→indices map: only touch deployments whose name # is listed in `available_models`, instead of scanning model_list. for name in config.available_models: - indices = self.model_name_to_deployment_indices.get(name, []) - if not indices: + deployments = self.deployments_named(name) + if not deployments: continue - d = (self.model_list or [])[indices[0]] - mi = d.get("model_info") if isinstance(d, dict) else d.model_info - mi_dict: dict[str, Any] = mi if isinstance(mi, dict) else (mi.model_dump() if mi else {}) + d = deployments[0] + mi_dict: dict[str, Any] = d.get("model_info") or {} prefs_raw = mi_dict.get("adaptive_router_preferences") if prefs_raw is not None: model_to_prefs[name] = AdaptiveRouterPreferences(**prefs_raw) # model_info is the conventional pricing location elsewhere in LiteLLM; litellm_params wins if set. - lp = d.get("litellm_params") if isinstance(d, dict) else d.litellm_params - lp_dict: dict[str, Any] = lp if isinstance(lp, dict) else (lp.model_dump() if lp else {}) - cost = lp_dict.get("input_cost_per_token") + cost = d.get("litellm_params", {}).get("input_cost_per_token") if cost is None: cost = mi_dict.get("input_cost_per_token") if cost is not None: @@ -10932,10 +10932,11 @@ class Router: def set_model_list(self, model_list: list): original_model_list: Final = copy.deepcopy(model_list) self._discovered_model_info_cache.flush_cache() - self.model_list = [] - self.model_id_to_deployment_index_map = {} # Reset the index - self.model_name_to_deployment_indices = {} # Reset the model_name index - self.team_model_to_deployment_indices = {} # Reset the team_model index + with self._deployment_index_lock: + self.model_list = [] + self.model_id_to_deployment_index_map = {} # Reset the index + self.model_name_to_deployment_indices = {} # Reset the model_name index + self.team_model_to_deployment_indices = {} # Reset the team_model index self.team_pattern_routers = {} self.team_public_model_names = frozenset() # Reset per-strategy router registries so hot-reload doesn't leave @@ -11326,26 +11327,27 @@ class Router: - model: dict - the model to add to the list - model_id: Optional[str] - the model ID to use for indexing. If None, will try to get from model["model_info"]["id"] """ - idx: Final = len(self.model_list) - self.model_list.append(model) - self._invalidate_model_group_info_cache() - self._invalidate_access_groups_cache() + with self._deployment_index_lock: + idx: Final = len(self.model_list) + self.model_list.append(model) + self._invalidate_model_group_info_cache() + self._invalidate_access_groups_cache() - # Update model_id index for O(1) lookup - if model_id is not None: - self.model_id_to_deployment_index_map[model_id] = idx - elif model.get("model_info", {}).get("id") is not None: - self.model_id_to_deployment_index_map[model["model_info"]["id"]] = idx + # Update model_id index for O(1) lookup + if model_id is not None: + self.model_id_to_deployment_index_map[model_id] = idx + elif model.get("model_info", {}).get("id") is not None: + self.model_id_to_deployment_index_map[model["model_info"]["id"]] = idx - # Update model_name index for O(1) lookup - model_name: Final = model.get("model_name") - if model_name: - if model_name not in self.model_name_to_deployment_indices: - self.model_name_to_deployment_indices[model_name] = [] - self.model_name_to_deployment_indices[model_name].append(idx) + # Update model_name index for O(1) lookup + model_name: Final = model.get("model_name") + if model_name: + if model_name not in self.model_name_to_deployment_indices: + self.model_name_to_deployment_indices[model_name] = [] + self.model_name_to_deployment_indices[model_name].append(idx) - # Update team_model index for O(1) team-scoped lookup - self._update_team_model_index(model, idx) + # Update team_model index for O(1) team-scoped lookup + self._update_team_model_index(model, idx) def upsert_deployment(self, deployment: Deployment) -> Deployment | None: """ @@ -11372,18 +11374,15 @@ class Router: # if there is a new litellm param -> then update the deployment # remove the previous deployment - removal_idx: int | None = None - deployment_id: Final = deployment.model_info.id - deployment_fast_mapping: Final = self.model_id_to_deployment_index_map - - if deployment_id in deployment_fast_mapping: - removal_idx = deployment_fast_mapping[deployment_id] - + with self._deployment_index_lock: + removal_idx: Final = self.model_id_to_deployment_index_map.get(_deployment_model_id) if removal_idx is not None: self.model_list.pop(removal_idx) self._invalidate_model_group_info_cache() self._invalidate_access_groups_cache() - self._update_deployment_indices_after_removal(model_id=deployment_id, removal_idx=removal_idx) + self._update_deployment_indices_after_removal( + model_id=_deployment_model_id, removal_idx=removal_idx + ) # Free the outgoing deployment's pre-routing strategy slot (keyed by the # OLD model_name/tags) before the re-add below re-registers it. @@ -11617,35 +11616,32 @@ class Router: - The deleted deployment - OR None (if deleted deployment not found) """ - deployment_idx = None - if id in self.model_id_to_deployment_index_map: - deployment_idx = self.model_id_to_deployment_index_map[id] - try: - if deployment_idx is not None: + with self._deployment_index_lock: + deployment_idx: Final = self.model_id_to_deployment_index_map.get(id) + if deployment_idx is None: + return None # Pop the item from the list first item: Final = self.model_list.pop(deployment_idx) self._invalidate_model_group_info_cache() self._invalidate_access_groups_cache() self._update_deployment_indices_after_removal(model_id=id, removal_idx=deployment_idx) - _budget_limiter: Final = self.get_router_deployment_budget_limiter() - if _budget_limiter is not None: - _budget_limiter.unregister_deployment_budget(model_id=id) - try: - self._unregister_pre_routing_strategy_for_deployment( - deployment=item if isinstance(item, Deployment) else Deployment(**item) - ) - except Exception: - verbose_router_logger.exception( - "delete_deployment: could not release pre-routing strategies for model_id=%s; " - "the deployment is out of the model_list and its indices are repaired", - id, - ) - return item - else: - return None + _budget_limiter: Final = self.get_router_deployment_budget_limiter() + if _budget_limiter is not None: + _budget_limiter.unregister_deployment_budget(model_id=id) + try: + self._unregister_pre_routing_strategy_for_deployment( + deployment=item if isinstance(item, Deployment) else Deployment(**item) + ) + except Exception: + verbose_router_logger.exception( + "delete_deployment: could not release pre-routing strategies for model_id=%s; " + "the deployment is out of the model_list and its indices are repaired", + id, + ) except Exception: return None + return item def get_router_deployment_budget_limiter( self, @@ -11694,6 +11690,18 @@ class Router: if _budget_limiter is not None: _budget_limiter.register_deployment_budget(deployment=deployment.to_json(exclude_none=True)) + def deployments_named(self, model_name: str) -> tuple[DeploymentTypedDict, ...] | None: + """The deployments indexed under `model_name`, read under the index lock, or None when the name is not indexed.""" + with self._deployment_index_lock: + indices: Final = self.model_name_to_deployment_indices.get(model_name) + return None if indices is None else tuple(self.model_list[idx] for idx in indices) + + def _team_deployments_named(self, team_id: str, public_model_name: str) -> tuple[DeploymentTypedDict, ...] | None: + """The deployments `team_id` serves under `public_model_name`, or None when that pair is not indexed.""" + with self._deployment_index_lock: + indices: Final = self.team_model_to_deployment_indices.get((team_id, public_model_name)) + return None if indices is None else tuple(self.model_list[idx] for idx in indices) + def get_deployment(self, model_id: str) -> Deployment | None: """ Returns -> Deployment or None @@ -11701,9 +11709,10 @@ class Router: Raise Exception -> if model found in invalid format """ # Use O(1) lookup via model_id_to_deployment_index_map only - if model_id in self.model_id_to_deployment_index_map: - idx: Final = self.model_id_to_deployment_index_map[model_id] - model: Final = self.model_list[idx] + with self._deployment_index_lock: + idx: Final = self.model_id_to_deployment_index_map.get(model_id) + model: Final = None if idx is None else self.model_list[idx] + if model is not None: if isinstance(model, dict): return Deployment(**model) elif isinstance(model, Deployment): @@ -11737,17 +11746,17 @@ class Router: Optimized with O(1) index lookup instead of O(n) linear scan. """ # O(1) lookup in model_name index - if model_group_name in self.model_name_to_deployment_indices: - indices: Final = self.model_name_to_deployment_indices[model_group_name] - if indices: - # Return first deployment for this model_name - model: Final = self.model_list[indices[0]] - if isinstance(model, dict): - return Deployment(**model) - elif isinstance(model, Deployment): - return model - else: - raise Exception(f"Model Name invalid - {type(model)}") + with self._deployment_index_lock: + indices: Final = self.model_name_to_deployment_indices.get(model_group_name) + # Return first deployment for this model_name + model: Final = self.model_list[indices[0]] if indices else None + if model is not None: + if isinstance(model, dict): + return Deployment(**model) + elif isinstance(model, Deployment): + return model + else: + raise Exception(f"Model Name invalid - {type(model)}") return None @staticmethod @@ -11770,11 +11779,12 @@ class Router: by other teams so a shared model name never resolves another team's credentials. """ - indices: Final = self.model_name_to_deployment_indices.get(model_group_name) or () - usable: Final = ( - self.model_list[idx] for idx in indices if self.deployment_usable_by_team(self.model_list[idx], team_id) - ) - first_usable: Final = next(usable, None) + with self._deployment_index_lock: + indices: Final = self.model_name_to_deployment_indices.get(model_group_name) or () + usable: Final = ( + self.model_list[idx] for idx in indices if self.deployment_usable_by_team(self.model_list[idx], team_id) + ) + first_usable: Final = next(usable, None) if first_usable is None: return None return Deployment(**first_usable) if isinstance(first_usable, dict) else first_usable @@ -11877,11 +11887,10 @@ class Router: this never triggers pattern matching or deep copies, so it is safe to call per listed model on the /v1/models hot path. """ - indices: Final = self.model_name_to_deployment_indices.get(model_name) - if not indices: + deployments: Final = self.deployments_named(model_name) + if not deployments: return None - deployments: Final = tuple(self.model_list[index] for index in indices) model_infos: Final = tuple( MappingProxyType( { @@ -12054,10 +12063,11 @@ class Router: def _get_team_public_name_deployment(self, model_id: str, team_id: str | None) -> Deployment | None: if team_id is None: return None - team_indices: Final = self.team_model_to_deployment_indices.get((team_id, model_id)) - if not team_indices: + with self._deployment_index_lock: + team_indices: Final = self.team_model_to_deployment_indices.get((team_id, model_id)) + team_model: Final = self.model_list[team_indices[0]] if team_indices else None + if team_model is None: return None - team_model: Final = self.model_list[team_indices[0]] return Deployment(**team_model) if isinstance(team_model, dict) else team_model def _get_wildcard_deployment_usable_by_team(self, model_id: str, team_id: str | None) -> Deployment | None: @@ -12296,10 +12306,9 @@ class Router: Optimized with O(1) index lookup instead of O(n) linear scan. """ # O(1) lookup via model_id_to_deployment_index_map - if id in self.model_id_to_deployment_index_map: - idx: Final = self.model_id_to_deployment_index_map[id] - return self.model_list[idx] - return None + with self._deployment_index_lock: + idx: Final = self.model_id_to_deployment_index_map.get(id) + return None if idx is None else self.model_list[idx] def get_model_group(self, id: str) -> list | None: """ @@ -12969,20 +12978,19 @@ class Router: if model_name is not None: # O(1) lookup in model_name index, then O(k) iteration where k = deployments for this model_name - if model_name in self.model_name_to_deployment_indices: - indices: Final = self.model_name_to_deployment_indices[model_name] - for idx in indices: - model = self.model_list[idx] - if "model_info" in model and "id" in model["model_info"]: - if exclude_team_models and model["model_info"].get("team_id"): - continue - ids.append(model["model_info"]["id"]) + for model in self.deployments_named(model_name) or (): + if "model_info" in model and "id" in model["model_info"]: + if exclude_team_models and model["model_info"].get("team_id"): + continue + ids.append(model["model_info"]["id"]) else: # When model_name is None, return all model IDs # Use the index map keys for O(n) where n = total deployments - for model_id in self.model_id_to_deployment_index_map: - idx = self.model_id_to_deployment_index_map[model_id] - model = self.model_list[idx] + with self._deployment_index_lock: + id_indices: Final = tuple(self.model_id_to_deployment_index_map.items()) + models: Final = tuple(self.model_list) + for model_id, idx in id_indices: + model = models[idx] if "model_info" in model and "id" in model["model_info"]: if exclude_team_models and model["model_info"].get("team_id"): continue @@ -13260,7 +13268,9 @@ class Router: # handled downstream by the pattern_router in _common_checks_available_deployment. return None - def should_include_deployment(self, model_name: str, model: dict, team_id: str | None = None) -> bool: + def should_include_deployment( + self, model_name: str, model: DeploymentTypedDict | dict, team_id: str | None = None + ) -> bool: """ Get the team-specific model name if team_id matches the deployment. """ @@ -13308,12 +13318,10 @@ class Router: # O(1) lookup in team_model index when team_id is provided if team_id is not None: - key: Final = (team_id, model_name) - if key in self.team_model_to_deployment_indices: - indices = self.team_model_to_deployment_indices[key] + team_models: Final = self._team_deployments_named(team_id, model_name) + if team_models is not None: # O(k) where k = team deployments for this model_name (typically 1-10) - for idx in indices: - model = self.model_list[idx] + for model in team_models: if not self.should_include_deployment(model_name=model_name, model=model, team_id=team_id): continue if model_alias is not None: @@ -13326,12 +13334,10 @@ class Router: return returned_models # O(1) lookup in model_name index - if model_name in self.model_name_to_deployment_indices: - indices = self.model_name_to_deployment_indices[model_name] - + named_models: Final = self.deployments_named(model_name) + if named_models is not None: # O(k) where k = deployments for this model_name (typically 1-10) - for idx in indices: - model = self.model_list[idx] + for model in named_models: if self.should_include_deployment(model_name=model_name, model=model, team_id=team_id): if model_alias is not None: # Optimized: Use shallow copy since we only modify top-level model_name @@ -13345,7 +13351,7 @@ class Router: # Fallback: if team_id is provided and model_name not in index, # check if model_name matches any team_public_model_name # O(n) scan but only when team_id lookup fails - for idx, model in enumerate(self.model_list): + for _idx, model in enumerate(self.model_list): if self.should_include_deployment(model_name=model_name, model=model, team_id=team_id): if model_alias is not None: # Optimized: Use shallow copy since we only modify top-level model_name @@ -14610,12 +14616,13 @@ class Router: def _team_deployments_across_teams(self, model: str) -> list[DeploymentTypedDict]: """Every team's deployments under public name `model`, for a proxy admin calling without a team.""" - team_deployments: Final = [ - self.model_list[index] - for (_, public_model_name), indices in self.team_model_to_deployment_indices.items() - if public_model_name == model - for index in indices - ] + with self._deployment_index_lock: + team_deployments: Final = [ + self.model_list[index] + for (_, public_model_name), indices in self.team_model_to_deployment_indices.items() + if public_model_name == model + for index in indices + ] team_ids: Final = { team_id for deployment in team_deployments diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index a475358e1f0..cc592bbcbc5 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -3126,17 +3126,14 @@ class ComplexityRouter(CustomLogger): model_to_prefs: Final[dict[str, AdaptiveRouterPreferences]] = {} model_to_cost: Final[dict[str, float]] = {} - model_list: Final = getattr(self.litellm_router_instance, "model_list", None) or [] - name_to_indices: Final = getattr(self.litellm_router_instance, "model_name_to_deployment_indices", {}) or {} for name in available_models: - indices = name_to_indices.get(name, []) - if not indices: + deployments = self.litellm_router_instance.deployments_named(name) + if not deployments: model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) model_to_cost[name] = 0.0 continue - deployment = model_list[indices[0]] - mi = deployment.get("model_info") if isinstance(deployment, dict) else deployment.model_info - mi_dict: dict[str, Any] = mi if isinstance(mi, dict) else (mi.model_dump() if mi else {}) + deployment = deployments[0] + mi_dict: dict[str, Any] = deployment.get("model_info") or {} prefs_raw = mi_dict.get("adaptive_router_preferences") if prefs_raw is not None: model_to_prefs[name] = AdaptiveRouterPreferences(**prefs_raw) @@ -3144,9 +3141,7 @@ class ComplexityRouter(CustomLogger): model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) # model_info is the conventional pricing location elsewhere in LiteLLM; litellm_params wins if set. - lp = deployment.get("litellm_params") if isinstance(deployment, dict) else deployment.litellm_params - lp_dict: dict[str, Any] = lp if isinstance(lp, dict) else (lp.model_dump() if lp else {}) - cost = lp_dict.get("input_cost_per_token") + cost = deployment.get("litellm_params", {}).get("input_cost_per_token") if cost is None: cost = mi_dict.get("input_cost_per_token") model_to_cost[name] = float(cost) if cost is not None else 0.0 diff --git a/litellm/router_strategy/savings_baseline.py b/litellm/router_strategy/savings_baseline.py index b5b80ddae6d..add0f78fd3a 100644 --- a/litellm/router_strategy/savings_baseline.py +++ b/litellm/router_strategy/savings_baseline.py @@ -14,7 +14,7 @@ bare string with no provider beside them; an operator who writes ``deepseek-r1`` Azure would otherwise be priced against whoever else owns that name. """ -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Final, NamedTuple from litellm._logging import verbose_router_logger @@ -80,12 +80,11 @@ def _models_in(router: "Router", group_name: str) -> tuple[Baseline, ...]: straight at a provider model rather than at a configured group, and prices under its own name because there is no deployment to override it. """ - indices: Final = router.model_name_to_deployment_indices.get(group_name) - if not indices: + deployments: Final = router.deployments_named(group_name) + if not deployments: return (Baseline(qualified),) if (qualified := canonical_model(group_name)) else () - def candidate(index: int) -> Baseline | None: - deployment: Final = router.model_list[index] + def candidate(deployment: Mapping[str, object]) -> Baseline | None: params: Final = deployment.get("litellm_params") if not isinstance(params, dict): return None @@ -98,7 +97,7 @@ def _models_in(router: "Router", group_name: str) -> tuple[Baseline, ...]: deployment_id: Final = info.get("id") if isinstance(info, dict) else None return Baseline(qualified, str(deployment_id) if deployment_id else None) - return tuple(c for index in indices if (c := candidate(index)) is not None) + return tuple(c for deployment in deployments if (c := candidate(deployment)) is not None) def _priced(router: "Router", candidate: Baseline) -> tuple[float, Baseline] | None: diff --git a/tests/code_coverage_tests/router_code_coverage.py b/tests/code_coverage_tests/router_code_coverage.py index b5e36055354..853b10ad97b 100644 --- a/tests/code_coverage_tests/router_code_coverage.py +++ b/tests/code_coverage_tests/router_code_coverage.py @@ -171,6 +171,7 @@ ignored_function_names = [ "_deployment_field", # Read by is_batch_capable, tested through the mixed-group tests in test_router.py "_deployment_provider", # Read by without_deployments_that_cannot_batch, tested through the mixed-group tests in test_router.py "_cannot_run_batches", # Tested through the Azure and OpenAI mixed-group acreate_batch tests in test_router.py + "_team_deployments_named", # Tested through the team lookups in the concurrent delete tests in test_router.py ] diff --git a/tests/integration/sdk/test_router_concurrent_deployment_delete_sdk.py b/tests/integration/sdk/test_router_concurrent_deployment_delete_sdk.py new file mode 100644 index 00000000000..dc94c96da05 --- /dev/null +++ b/tests/integration/sdk/test_router_concurrent_deployment_delete_sdk.py @@ -0,0 +1,474 @@ +from __future__ import annotations + +import asyncio +import json +import linecache +import sys +import threading +import uuid +from collections.abc import AsyncIterable, Awaitable, Callable, Iterable +from concurrent.futures import ThreadPoolExecutor +from types import FrameType +from typing import Final, TypeVar + +import pytest +from integration._support.client import object_value +from integration._support.openai_wire import chat_reply, openai_error, responses_reply +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue + +import litellm +from litellm import Router + +_DEPLOYMENTS: Final = 4 +_TARGET: Final = _DEPLOYMENTS - 1 +_SYNC_SECONDS: Final = 10 +_RATE_LIMITED_KEY: Final = "key-rate-limited" +_BURST_THREADS: Final = 8 +_BURST_CALLS: Final = 24 +_CHURN_UPSERTS: Final = 120 +_UNREPORTED_MODEL_ID: Final = frozenset({"aresponses-stream", "aanthropic-messages"}) +_ROUTER_FILE: Final = Router.__init__.__code__.co_filename +_INDEX_LOCK_LINES: Final = frozenset( + number + for number, line in enumerate(linecache.getlines(_ROUTER_FILE), start=1) + if "with self._deployment_index_lock" in line +) + +T = TypeVar("T") + + +def _peer(request: Request) -> Reply: + if request.headers["authorization"] == f"Bearer {_RATE_LIMITED_KEY}": + return openai_error(429) + body: Final = object_value(json.loads(request.body)) + stream: Final = body.get("stream") is True + if request.target == "/v1/responses": + return responses_reply(f"resp_{uuid.uuid4().hex}", "gpt-4o-mini", "ok", stream=stream) + return chat_reply(f"chatcmpl-{uuid.uuid4().hex}", "gpt-4o-mini", "ok", stream=stream) + + +def _deployment( + index: int, + api_base: str, + team: bool, + *, + model: int | None = None, + public_name: str | None = None, + timeout: float = 10, +) -> dict[str, JsonValue]: + team_info: Final[dict[str, JsonValue]] = ( + {"team_id": "team-1", "team_public_model_name": public_name or f"public-{index}"} if team else {} + ) + return { + "model_name": f"model-{index if model is None else model}", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "api_base": api_base, + "api_key": f"key-{index}", + "timeout": timeout, + }, + "model_info": {"id": f"deployment-{index}", **team_info}, + } + + +def _router(wire: Wire, team: bool = False) -> Router: + return Router(model_list=[_deployment(index, f"{wire.url}/v1", team) for index in range(_DEPLOYMENTS)]) + + +def _read_during_held_write(read: Callable[[], T], write: Callable[[], object]) -> T: + """Run `read` on its own thread while `write` waits where the list has shrunk but the index maps have not caught + up. The write resumes once `read` returns or reaches the router's index lock, so a read that skips the lock meets + the half-updated list and a read that takes the lock waits for the write""" + reindex: Final = Router._update_deployment_indices_after_removal.__code__ + held: Final = threading.Event() + release: Final = threading.Event() + settled: Final = threading.Event() + released_in_time: Final[list[bool]] = [] + outcome: Final[list[T]] = [] + failure: Final[list[BaseException]] = [] + + def hold_at_reindex(frame: FrameType, event: str, _arg: object) -> None: + if event == "call" and frame.f_code is reindex and not held.is_set(): + held.set() + released_in_time.append(release.wait(timeout=_SYNC_SECONDS)) + + def run_write() -> None: + previous: Final = sys.getprofile() + sys.setprofile(hold_at_reindex) + try: + write() + finally: + sys.setprofile(previous) + + def watch_read(frame: FrameType, event: str, _arg: object) -> Callable[..., object] | None: + if frame.f_code.co_filename != _ROUTER_FILE: + return None + if event == "line" and frame.f_lineno in _INDEX_LOCK_LINES: + settled.set() + return watch_read + + def run_read() -> None: + previous: Final = sys.gettrace() + sys.settrace(watch_read) + try: + outcome.append(read()) + except BaseException as error: + failure.append(error) + finally: + sys.settrace(previous) + settled.set() + + writer: Final = threading.Thread(target=run_write) + reader: Final = threading.Thread(target=run_read) + writer.start() + try: + assert held.wait(timeout=_SYNC_SECONDS), "the write never reached re-indexing" + reader.start() + assert settled.wait(timeout=_SYNC_SECONDS), "the read neither returned nor reached the index lock" + finally: + release.set() + if reader.ident is not None: + reader.join() + writer.join() + assert released_in_time == [True], "the write was not held for the whole read" + if failure: + raise failure[0] + return outcome[0] + + +def _read_during_held_delete(router: Router, deployment_id: str, read: Callable[[], T]) -> T: + return _read_during_held_write(read, lambda: router.delete_deployment(deployment_id)) + + +def _keys(wire: Wire) -> tuple[str, ...]: + return tuple(request.headers["authorization"] for request in wire.drain()) + + +def _team_metadata(team: bool) -> dict[str, JsonValue]: + return {"user_api_key_team_id": "team-1"} if team else {} + + +def _completion(router: Router, model: str, team: bool) -> str | None: + response: Final = router.completion( + model=model, messages=[{"role": "user", "content": "hi"}], metadata=_team_metadata(team) + ) + return response._hidden_params["model_id"] + + +def _completion_stream(router: Router, model: str, team: bool) -> str | None: + stream: Final = router.completion( + model=model, messages=[{"role": "user", "content": "hi"}], metadata=_team_metadata(team), stream=True + ) + assert isinstance(stream, Iterable), stream + chunks: Final = list(stream) + assert "".join(chunk.choices[0].delta.content or "" for chunk in chunks) == "ok", chunks + return stream._hidden_params["model_id"] + + +def _acompletion(router: Router, model: str, team: bool) -> str | None: + async def call() -> str | None: + response: Final = await router.acompletion( + model=model, messages=[{"role": "user", "content": "hi"}], metadata=_team_metadata(team) + ) + return response._hidden_params["model_id"] + + return asyncio.run(call()) + + +def _acompletion_stream(router: Router, model: str, team: bool) -> str | None: + async def call() -> str | None: + stream: Final = await router.acompletion( + model=model, messages=[{"role": "user", "content": "hi"}], metadata=_team_metadata(team), stream=True + ) + assert isinstance(stream, AsyncIterable), stream + chunks: Final = [chunk async for chunk in stream] + assert "".join(chunk.choices[0].delta.content or "" for chunk in chunks) == "ok", chunks + return stream._hidden_params["model_id"] + + return asyncio.run(call()) + + +def _responses(router: Router, model: str, team: bool) -> str | None: + response: Final = router.responses(model=model, input="hi", metadata=_team_metadata(team)) + assert response.output_text == "ok", response + return response._hidden_params["model_id"] + + +def _aresponses(router: Router, model: str, team: bool) -> str | None: + async def call() -> str | None: + response: Final = await router.aresponses(model=model, input="hi", metadata=_team_metadata(team)) + assert response.output_text == "ok", response + return response._hidden_params["model_id"] + + return asyncio.run(call()) + + +def _aresponses_stream(router: Router, model: str, team: bool) -> str | None: + async def call() -> None: + stream: Final = await router.aresponses(model=model, input="hi", metadata=_team_metadata(team), stream=True) + assert isinstance(stream, AsyncIterable), stream + events: Final = [event.type async for event in stream] + assert events[-1] == "response.completed", events + + asyncio.run(call()) + return None + + +def _aanthropic_messages(router: Router, model: str, team: bool) -> str | None: + async def call() -> None: + response: Final = await router.aanthropic_messages( + model=model, messages=[{"role": "user", "content": "hi"}], max_tokens=16, metadata=_team_metadata(team) + ) + assert response["content"][0]["text"] == "ok", response + + asyncio.run(call()) + return None + + +_CALLS: Final[dict[str, Callable[[Router, str, bool], str | None]]] = { + "completion": _completion, + "completion-stream": _completion_stream, + "acompletion": _acompletion, + "acompletion-stream": _acompletion_stream, + "responses": _responses, + "aresponses": _aresponses, + "aresponses-stream": _aresponses_stream, + "aanthropic-messages": _aanthropic_messages, +} + + +@pytest.mark.parametrize("call", list(_CALLS)) +@pytest.mark.parametrize("team", [False, True], ids=["model-name", "team-public-name"]) +def test_call_reaches_its_deployment_while_another_thread_deletes_one(call: str, team: bool) -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire, team) + served_by: Final = _read_during_held_delete( + router, + "deployment-0", + lambda: _CALLS[call](router, f"public-{_TARGET}" if team else f"model-{_TARGET}", team), + ) + keys: Final = _keys(wire) + + assert served_by == (None if call in _UNREPORTED_MODEL_ID else f"deployment-{_TARGET}") + assert keys == (f"Bearer key-{_TARGET}",) + assert router.get_model_ids() == [f"deployment-{index}" for index in range(1, _DEPLOYMENTS)] + + +@pytest.mark.parametrize("deleted", [0, _TARGET - 1], ids=["first", "next-to-target"]) +def test_completion_reaches_its_deployment_whichever_earlier_deployment_is_deleted(deleted: int) -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + served_by: Final = _read_during_held_delete( + router, f"deployment-{deleted}", lambda: _completion(router, f"model-{_TARGET}", team=False) + ) + keys: Final = _keys(wire) + + assert served_by == f"deployment-{_TARGET}" + assert keys == (f"Bearer key-{_TARGET}",) + + +def test_team_completion_reaches_the_remaining_deployment_of_a_shared_public_name_while_its_sibling_is_deleted() -> ( + None +): + with wire_server(_peer) as wire: + api_base: Final = f"{wire.url}/v1" + router: Final = Router( + model_list=[ + _deployment(0, api_base, team=False), + _deployment(1, api_base, team=False), + _deployment(2, api_base, team=True, public_name="public-shared"), + _deployment(3, api_base, team=True, public_name="public-shared"), + ] + ) + served_by: Final = _read_during_held_delete( + router, "deployment-2", lambda: _completion(router, "public-shared", team=True) + ) + keys: Final = _keys(wire) + + assert served_by == "deployment-3" + assert keys == ("Bearer key-3",) + + +def test_completion_for_the_deployment_being_deleted_fails_cleanly_and_sends_nothing() -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + with pytest.raises(litellm.BadRequestError): + _read_during_held_delete( + router, + "deployment-0", + lambda: router.completion(model="model-0", messages=[{"role": "user", "content": "hi"}]), + ) + keys: Final = _keys(wire) + + assert keys == () + assert router.get_model_ids() == [f"deployment-{index}" for index in range(1, _DEPLOYMENTS)] + + +def test_provider_error_during_a_concurrent_delete_comes_from_the_requested_deployment() -> None: + with wire_server(_peer) as wire: + api_base: Final = f"{wire.url}/v1" + rate_limited: Final = _deployment(_TARGET, api_base, team=False) + rate_limited["litellm_params"] = {**object_value(rate_limited["litellm_params"]), "api_key": _RATE_LIMITED_KEY} + router: Final = Router( + model_list=[*(_deployment(index, api_base, team=False) for index in range(_TARGET)), rate_limited], + num_retries=0, + ) + with pytest.raises(litellm.RateLimitError): + _read_during_held_delete( + router, + "deployment-0", + lambda: router.completion(model=f"model-{_TARGET}", messages=[{"role": "user", "content": "hi"}]), + ) + served_by: Final = _completion(router, "model-1", team=False) + keys: Final = _keys(wire) + + assert served_by == "deployment-1" + assert keys == (f"Bearer {_RATE_LIMITED_KEY}", "Bearer key-1") + + +def test_two_threads_deleting_the_same_deployment_remove_it_once() -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + second: Final = _read_during_held_delete( + router, "deployment-0", lambda: router.delete_deployment("deployment-0") + ) + served_by: Final = _completion(router, f"model-{_TARGET}", team=False) + keys: Final = _keys(wire) + + assert second is None + assert served_by == f"deployment-{_TARGET}" + assert keys == (f"Bearer key-{_TARGET}",) + assert router.get_model_ids() == [f"deployment-{index}" for index in range(1, _DEPLOYMENTS)] + + +def test_completion_reaches_its_deployment_while_another_thread_upserts_an_earlier_one() -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + changed: Final = litellm.Deployment(**_deployment(0, f"{wire.url}/v1", team=False, timeout=11)) + served_by: Final = _read_during_held_write( + lambda: _completion(router, f"model-{_TARGET}", team=False), lambda: router.upsert_deployment(changed) + ) + keys: Final = _keys(wire) + + assert served_by == f"deployment-{_TARGET}" + assert keys == (f"Bearer key-{_TARGET}",) + assert sorted(router.get_model_ids()) == [f"deployment-{index}" for index in range(_DEPLOYMENTS)] + + +def test_deployment_added_during_a_concurrent_delete_serves_its_own_requests() -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + added: Final = litellm.Deployment(**_deployment(9, f"{wire.url}/v1", team=False)) + _read_during_held_delete(router, "deployment-0", lambda: router.add_deployment(added)) + served: Final = tuple(_completion(router, f"model-{index}", team=False) for index in (9, _TARGET)) + keys: Final = _keys(wire) + + assert served == ("deployment-9", f"deployment-{_TARGET}") + assert keys == ("Bearer key-9", f"Bearer key-{_TARGET}") + + +def test_model_list_set_during_a_concurrent_delete_serves_its_own_requests() -> None: + with wire_server(_peer) as wire: + router: Final = _router(wire) + replacement: Final = [_deployment(index, f"{wire.url}/v1", team=False) for index in range(10, 14)] + _read_during_held_delete(router, "deployment-0", lambda: router.set_model_list(replacement)) + served: Final = tuple(_completion(router, f"model-{index}", team=False) for index in range(10, 14)) + keys: Final = _keys(wire) + + assert served == tuple(f"deployment-{index}" for index in range(10, 14)) + assert keys == tuple(f"Bearer key-{index}" for index in range(10, 14)) + assert router.get_model_ids() == [f"deployment-{index}" for index in range(10, 14)] + + +def _burst_router(api_base: str) -> Router: + """Two deployments per model name, so an upsert of one leaves its model group served.""" + return Router( + model_list=[ + _deployment(index, api_base, team=False, model=index % _DEPLOYMENTS) for index in range(2 * _DEPLOYMENTS) + ] + ) + + +def _churn(router: Router, api_base: str, stop: threading.Event) -> int: + """Upsert the deployments in turn with a changed timeout; each upsert moves one to the end of the list.""" + upserts = 0 + while upserts < _CHURN_UPSERTS and not stop.is_set(): + index, round_number = upserts % (2 * _DEPLOYMENTS), upserts // (2 * _DEPLOYMENTS) + router.upsert_deployment( + litellm.Deployment( + **_deployment(index, api_base, team=False, model=index % _DEPLOYMENTS, timeout=11 + round_number % 2) + ) + ) + upserts += 1 + return upserts + + +def _served_keys(wire: Wire) -> dict[str, str]: + """Which key reached the upstream for each request marker.""" + return { + object_value(json.loads(request.body))["messages"][0]["content"]: request.headers["authorization"] + for request in wire.drain() + } + + +def _assert_each_request_used_its_models_deployment(served: list[tuple[str, str | None]], keys: dict[str, str]) -> None: + for marker, model_id in served: + model: Final = int(marker.split(":")[0]) + assert model_id in (f"deployment-{model}", f"deployment-{model + _DEPLOYMENTS}"), (marker, model_id) + assert keys[marker] == f"Bearer key-{str(model_id).removeprefix('deployment-')}", (marker, keys[marker]) + assert len(keys) == len(served) + + +def test_thread_burst_during_upsert_churn_sends_every_request_with_its_deployments_key() -> None: + with wire_server(_peer) as wire: + api_base: Final = f"{wire.url}/v1" + router: Final = _burst_router(api_base) + stop: Final = threading.Event() + + def call(number: int) -> tuple[str, str | None]: + marker: Final = f"{number % _DEPLOYMENTS}:{number}" + response: Final = router.completion( + model=f"model-{number % _DEPLOYMENTS}", messages=[{"role": "user", "content": marker}] + ) + return marker, response._hidden_params["model_id"] + + with ThreadPoolExecutor(max_workers=_BURST_THREADS + 1) as pool: + churn: Final = pool.submit(_churn, router, api_base, stop) + served: Final = list(pool.map(call, range(_BURST_THREADS * _BURST_CALLS))) + stop.set() + upserts: Final = churn.result() + keys: Final = _served_keys(wire) + + assert upserts > 0 + _assert_each_request_used_its_models_deployment(served, keys) + assert sorted(router.get_model_ids()) == sorted(f"deployment-{index}" for index in range(2 * _DEPLOYMENTS)) + + +async def _gathered(calls: Iterable[Awaitable[tuple[str, str | None]]]) -> list[tuple[str, str | None]]: + return list(await asyncio.gather(*calls)) + + +def test_async_burst_during_upsert_churn_sends_every_request_with_its_deployments_key() -> None: + with wire_server(_peer) as wire: + api_base: Final = f"{wire.url}/v1" + router: Final = _burst_router(api_base) + stop: Final = threading.Event() + + async def call(number: int) -> tuple[str, str | None]: + marker: Final = f"{number % _DEPLOYMENTS}:{number}" + response: Final = await router.acompletion( + model=f"model-{number % _DEPLOYMENTS}", messages=[{"role": "user", "content": marker}] + ) + return marker, response._hidden_params["model_id"] + + with ThreadPoolExecutor(max_workers=1) as pool: + churn: Final = pool.submit(_churn, router, api_base, stop) + served: Final = asyncio.run(_gathered(call(number) for number in range(_BURST_THREADS * _BURST_CALLS))) + stop.set() + upserts: Final = churn.result() + keys: Final = _served_keys(wire) + + assert upserts > 0 + _assert_each_request_used_its_models_deployment(served, keys) + assert sorted(router.get_model_ids()) == sorted(f"deployment-{index}" for index in range(2 * _DEPLOYMENTS)) diff --git a/tests/unit/proxy/response_polling/test_response_polling_handler.py b/tests/unit/proxy/response_polling/test_response_polling_handler.py index 979ab499f3b..63e38ce533a 100644 --- a/tests/unit/proxy/response_polling/test_response_polling_handler.py +++ b/tests/unit/proxy/response_polling/test_response_polling_handler.py @@ -1011,10 +1011,9 @@ class TestPollingConditionChecks: # Create mock router mock_router = Mock() - mock_router.model_name_to_deployment_indices = {"gpt-5": [0]} - mock_router.model_list = [ - {"model_name": "gpt-5", "litellm_params": {"model": "openai/gpt-5"}} - ] + mock_router.deployments_named.side_effect = { + "gpt-5": ({"model_name": "gpt-5", "litellm_params": {"model": "openai/gpt-5"}},) + }.get result = should_use_polling_for_request( background_mode=True, @@ -1033,13 +1032,14 @@ class TestPollingConditionChecks: ) mock_router = Mock() - mock_router.model_name_to_deployment_indices = {"claude-3": [0]} - mock_router.model_list = [ - { - "model_name": "claude-3", - "litellm_params": {"model": "anthropic/claude-3-sonnet"}, - } - ] + mock_router.deployments_named.side_effect = { + "claude-3": ( + { + "model_name": "claude-3", + "litellm_params": {"model": "anthropic/claude-3-sonnet"}, + }, + ) + }.get result = should_use_polling_for_request( background_mode=True, @@ -1183,13 +1183,14 @@ class TestPollingConditionChecks: ) mock_router = Mock() - mock_router.model_name_to_deployment_indices = {"deep-research": [0]} - mock_router.model_list = [ - { - "model_name": "deep-research", - "litellm_params": {"model": "openai/o4-mini-deep-research"}, - } - ] + mock_router.deployments_named.side_effect = { + "deep-research": ( + { + "model_name": "deep-research", + "litellm_params": {"model": "openai/o4-mini-deep-research"}, + }, + ) + }.get # Model alias "deep-research" is in native_background_mode result = should_use_polling_for_request( diff --git a/tests/unit/router_strategy/test_complexity_router.py b/tests/unit/router_strategy/test_complexity_router.py index 9b4019bead6..a46f415e10a 100644 --- a/tests/unit/router_strategy/test_complexity_router.py +++ b/tests/unit/router_strategy/test_complexity_router.py @@ -112,6 +112,13 @@ def _heuristic_v2_artifact() -> TrainedTierArtifact: ) +def _answer_deployments_named(router: MagicMock) -> None: + """Make a mock router answer `deployments_named` from its own `model_list`, the way Router answers from its index.""" + router.deployments_named.side_effect = lambda name: ( + tuple(deployment for deployment in router.model_list if deployment["model_name"] == name) or None + ) + + @pytest.fixture def mock_router_instance(): """Create a mock LiteLLM Router instance.""" @@ -3181,7 +3188,7 @@ class TestCapabilityClassifier: {"model_name": "efficient-model", "litellm_params": {"input_cost_per_token": 0.000001}}, {"model_name": "capable-model", "litellm_params": {"input_cost_per_token": 0.00001}}, ] - mock_router_instance.model_name_to_deployment_indices = {"efficient-model": [0], "capable-model": [1]} + _answer_deployments_named(mock_router_instance) router: Final = self._router( mock_router_instance, adaptive=True, @@ -5670,7 +5677,7 @@ class TestAdaptiveSoftFloors: "model_info": {"adaptive_router_preferences": {"quality_tier": 3, "strengths": []}}, }, ] - router.model_name_to_deployment_indices = {"cheap": [0], "premium": [1]} + _answer_deployments_named(router) return router @pytest.fixture @@ -6829,7 +6836,7 @@ class TestTierModelAffinity: ) -> ComplexityRouter: mock_router_instance.cache = DualCache() mock_router_instance.model_list = [] - mock_router_instance.model_name_to_deployment_indices = {} + _answer_deployments_named(mock_router_instance) return ComplexityRouter( model_name="affinity-router", litellm_router_instance=mock_router_instance, @@ -7547,7 +7554,7 @@ class TestSessionAffinity: "model_info": {}, }, ] - mock_router_instance.model_name_to_deployment_indices = {"cheap": [0]} + _answer_deployments_named(mock_router_instance) router = ComplexityRouter( model_name="hybrid", litellm_router_instance=mock_router_instance, @@ -11351,7 +11358,7 @@ class TestClientHousekeepingCalls: "model_info": {"adaptive_router_preferences": {"quality_tier": 3, "strengths": []}}, }, ] - adaptive_instance.model_name_to_deployment_indices = {"cheap": [0], "premium": [1]} + _answer_deployments_named(adaptive_instance) router = ComplexityRouter( model_name="hybrid", litellm_router_instance=adaptive_instance, @@ -13442,7 +13449,7 @@ class TestPlanModeTierFloor: "model_info": {"adaptive_router_preferences": {"quality_tier": 3, "strengths": []}}, }, ] - adaptive_instance.model_name_to_deployment_indices = {"cheap": [0], "premium": [1]} + _answer_deployments_named(adaptive_instance) router = ComplexityRouter( model_name="hybrid", litellm_router_instance=adaptive_instance, @@ -14941,7 +14948,7 @@ class TestModalityRouting: elif path == "adaptive_pick_rewritten": config["adaptive"] = True mock_router_instance.model_list = [] - mock_router_instance.model_name_to_deployment_indices = {} + _answer_deployments_named(mock_router_instance) router = self._router(mock_router_instance, config, vision) result = await router.async_pre_routing_hook(model="m", request_kwargs=request_kwargs, messages=messages) assert result.model == expected_model diff --git a/tests/unit/router_strategy/test_savings_baseline.py b/tests/unit/router_strategy/test_savings_baseline.py index 5efc73d2dcb..4241a541202 100644 --- a/tests/unit/router_strategy/test_savings_baseline.py +++ b/tests/unit/router_strategy/test_savings_baseline.py @@ -107,8 +107,7 @@ class TestResolveBaseline: served; a dashboard counterfactual must not be able to take routing down.""" class Exploding: - @property - def model_name_to_deployment_indices(self): + def deployments_named(self, model_name): raise RuntimeError("router is mid-reload") assert resolve_baseline(Exploding(), ["anything"]) is None diff --git a/tests/unit/test_router/test_router.py b/tests/unit/test_router/test_router.py index 568a05e8ff4..4814d36ee39 100644 --- a/tests/unit/test_router/test_router.py +++ b/tests/unit/test_router/test_router.py @@ -5,6 +5,7 @@ import functools import gc import itertools import json +import linecache import logging import os import sys @@ -13,8 +14,8 @@ import warnings from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Iterator, Mapping from datetime import datetime, timedelta, timezone from pathlib import Path -from types import MappingProxyType, SimpleNamespace -from typing import TYPE_CHECKING, Final, Literal, cast +from types import CodeType, FrameType, MappingProxyType, SimpleNamespace +from typing import TYPE_CHECKING, Final, Literal, TypeVar, cast from unittest.mock import AsyncMock, MagicMock, Mock, patch import httpx @@ -60,7 +61,10 @@ from litellm.router import ( _without_line_breaks, Span, ) +from litellm.proxy.response_polling.polling_handler import should_use_polling_for_request from litellm.router_strategy import simple_shuffle +from litellm.router_strategy.complexity_router.complexity_router import ComplexityRouter +from litellm.router_strategy.savings_baseline import Baseline, _models_in from litellm.router_utils.client_initalization_utils import MaxParallelRequestsLimit from litellm.router_utils.cooldown_handlers import ( _async_get_cooldown_deployments, @@ -30004,6 +30008,319 @@ async def test_responses_fallback_wrapper_disconnect_billing_keeps_the_primary_s assert wrapped.partial_response_on_disconnect().response.usage.output_tokens == primary_tokens + fallback_tokens +_SYNC_SECONDS: Final = 10 +_ROUTER_FILE: Final = Router.__init__.__code__.co_filename +_INDEX_LOCK_LINES: Final = frozenset( + number + for number, line in enumerate(linecache.getlines(_ROUTER_FILE), start=1) + if "with self._deployment_index_lock" in line +) + +_Read = TypeVar("_Read") + + +def _concurrent_delete_deployment(index: int) -> dict[str, object]: + return { + "model_name": f"model-{index}", + "litellm_params": {"model": f"openai/gpt-{index}", "api_key": "k"}, + "model_info": { + "id": f"deployment-{index}", + "team_id": "team-1", + "team_public_model_name": "public-shared" if index >= 2 else f"public-{index}", + }, + } + + +def _concurrent_delete_router() -> Router: + return Router(model_list=[_concurrent_delete_deployment(index) for index in range(4)]) + + +def _read_during_held_write( + read: Callable[[], _Read], + write: Callable[[], object], + event: Literal["call", "c_call"] = "call", + write_after: CodeType | None = None, +) -> _Read: + """Run `read` on its own thread while `write` waits at its first `event` in the re-index, after the list shrank and + before the maps caught up. With `write_after`, the write starts when `read` returns from that function. The write + resumes once `read` returns or reaches the router's index lock, so a read that skips the lock meets the half-updated + list and a read that takes the lock waits for the write""" + reindex: Final = Router._update_deployment_indices_after_removal.__code__ + held: Final = threading.Event() + release: Final = threading.Event() + settled: Final = threading.Event() + released_in_time: Final[list[bool]] = [] + outcome: Final[list[_Read]] = [] + failure: Final[list[BaseException]] = [] + + def hold_in_reindex(frame: FrameType, observed: str, _arg: object) -> None: + if observed == event and frame.f_code is reindex and not held.is_set(): + held.set() + released_in_time.append(release.wait(timeout=_SYNC_SECONDS)) + + def run_write() -> None: + previous: Final = sys.getprofile() + sys.setprofile(hold_in_reindex) + try: + write() + finally: + sys.setprofile(previous) + + writer: Final = threading.Thread(target=run_write) + + def start_write() -> None: + writer.start() + assert held.wait(timeout=_SYNC_SECONDS), "the write never reached re-indexing" + + def watch_read(frame: FrameType, observed: str, _arg: object) -> Callable[..., object] | None: + if frame.f_code.co_filename != _ROUTER_FILE: + return None + if observed == "return" and frame.f_code is write_after and writer.ident is None: + start_write() + elif observed == "line" and held.is_set() and frame.f_lineno in _INDEX_LOCK_LINES: + settled.set() + return watch_read + + def run_read() -> None: + previous: Final = sys.gettrace() + sys.settrace(watch_read) + try: + outcome.append(read()) + except BaseException as error: + failure.append(error) + finally: + sys.settrace(previous) + settled.set() + + reader: Final = threading.Thread(target=run_read) + try: + if write_after is None: + start_write() + reader.start() + assert settled.wait(timeout=_SYNC_SECONDS), "the read neither returned nor reached the index lock" + finally: + release.set() + if reader.ident is not None: + reader.join() + if writer.ident is not None: + writer.join() + assert released_in_time == [True], "the write was not held for the whole read" + if failure: + raise failure[0] + return outcome[0] + + +def _deployment_ids(deployments: list[DeploymentTypedDict]) -> tuple[str, ...]: + return tuple(deployment["model_info"]["id"] for deployment in deployments) + + +@pytest.mark.parametrize( + ("read", "expected"), + [ + pytest.param( + lambda router: router.get_deployment("deployment-3").model_info.id, "deployment-3", id="get_deployment" + ), + pytest.param( + lambda router: router.get_model_info("deployment-3")["model_info"]["id"], + "deployment-3", + id="get_model_info", + ), + pytest.param( + lambda router: _deployment_ids(router.get_all_deployments("model-3")), + ("deployment-3",), + id="get_all_deployments", + ), + pytest.param( + lambda router: _deployment_ids(router.get_all_deployments("public-shared", team_id="team-1")), + ("deployment-2", "deployment-3"), + id="get_all_deployments-team", + ), + pytest.param( + lambda router: router.map_team_model("public-shared", "team-1"), "public-shared", id="map_team_model" + ), + pytest.param(lambda router: router.get_model_ids("model-3"), ["deployment-3"], id="get_model_ids-by-name"), + pytest.param( + lambda router: router.get_model_ids(), ["deployment-1", "deployment-2", "deployment-3"], id="get_model_ids" + ), + pytest.param( + lambda router: router.get_deployment_by_model_group_name("model-3").model_info.id, + "deployment-3", + id="get_deployment_by_model_group_name", + ), + pytest.param( + lambda router: router.get_model_listing_info("model-3").cost_map_keys, + ("openai/gpt-3",), + id="get_model_listing_info", + ), + pytest.param( + lambda router: router.team_model_has_alternatives("deployment-3"), True, id="team_model_has_alternatives" + ), + pytest.param( + lambda router: ( + router._get_model_group_deployment_usable_by_team( + model_group_name="model-3", team_id="team-1" + ).model_info.id + ), + "deployment-3", + id="model_group_deployment_usable_by_team", + ), + pytest.param( + lambda router: ( + router._get_team_public_name_deployment(model_id="public-shared", team_id="team-1").model_info.id + ), + "deployment-2", + id="team_public_name_deployment", + ), + pytest.param( + lambda router: _deployment_ids(router._team_deployments_across_teams("public-shared")), + ("deployment-2", "deployment-3"), + id="team_deployments_across_teams", + ), + pytest.param( + lambda router: _deployment_ids(list(router.deployments_named("model-3") or ())), + ("deployment-3",), + id="deployments_named", + ), + pytest.param( + lambda router: should_use_polling_for_request( + background_mode=True, + polling_via_cache_enabled=["openai"], + redis_cache=MagicMock(), + model="model-3", + llm_router=router, + ), + True, + id="response_polling_provider_lookup", + ), + pytest.param( + lambda router: _models_in(router, "model-3"), + (Baseline("openai/gpt-3", "deployment-3"),), + id="savings_baseline_candidates", + ), + ], +) +def test_lookup_during_a_concurrent_delete_returns_the_requested_deployment( + read: Callable[[Router], object], expected: object +) -> None: + router: Final = _concurrent_delete_router() + + observed: Final = _read_during_held_write(lambda: read(router), lambda: router.delete_deployment("deployment-0")) + + assert observed == expected + + +def test_lookup_during_an_upsert_returns_the_requested_deployment() -> None: + router: Final = _concurrent_delete_router() + rotated: Final = Deployment( + **{**_concurrent_delete_deployment(0), "litellm_params": {"model": "openai/gpt-0", "api_key": "rotated"}} + ) + + observed: Final = _read_during_held_write( + lambda: router.get_deployment("deployment-3").model_info.id, lambda: router.upsert_deployment(rotated) + ) + + assert observed == "deployment-3" + + +def test_add_during_a_concurrent_delete_indexes_the_added_deployment() -> None: + router: Final = _concurrent_delete_router() + + _read_during_held_write( + lambda: router.add_deployment(Deployment(**_concurrent_delete_deployment(4))), + lambda: router.delete_deployment("deployment-0"), + ) + + assert [router.get_deployment(f"deployment-{index}").model_info.id for index in range(1, 5)] == [ + f"deployment-{index}" for index in range(1, 5) + ] + + +def test_set_model_list_during_a_concurrent_delete_indexes_the_new_list() -> None: + router: Final = _concurrent_delete_router() + + _read_during_held_write( + lambda: router.set_model_list([_concurrent_delete_deployment(index) for index in range(10, 14)]), + lambda: router.delete_deployment("deployment-0"), + event="c_call", + ) + + assert router.get_model_ids() == [f"deployment-{index}" for index in range(10, 14)] + + +def test_team_model_has_alternatives_when_a_delete_starts_after_its_deployment_lookup() -> None: + router: Final = _concurrent_delete_router() + + has_alternatives: Final = _read_during_held_write( + lambda: router.team_model_has_alternatives("deployment-3"), + lambda: router.delete_deployment("deployment-0"), + write_after=Router.get_deployment.__code__, + ) + + assert has_alternatives is True + + +def test_init_adaptive_router_deployment_during_a_concurrent_delete_prices_the_listed_model() -> None: + priced: Final = { + **_concurrent_delete_deployment(3), + "litellm_params": {"model": "openai/gpt-3", "api_key": "k", "input_cost_per_token": 3e-06}, + } + router: Final = Router(model_list=[*(_concurrent_delete_deployment(index) for index in range(3)), priced]) + adaptive: Final = Deployment( + model_name="adaptive", + litellm_params=LiteLLM_Params( + model="auto_router/adaptive_router", adaptive_router_config={"available_models": ["model-3"]} + ), + model_info=ModelInfo(id="adaptive"), + ) + + _read_during_held_write( + lambda: router.init_adaptive_router_deployment(deployment=adaptive), + lambda: router.delete_deployment("deployment-0"), + ) + + assert router.adaptive_routers["adaptive"][0].strategy.model_to_cost == {"model-3": 3e-06} + + +def test_complexity_router_adaptive_pool_during_a_concurrent_delete_prices_the_listed_model() -> None: + priced: Final = { + **_concurrent_delete_deployment(3), + "litellm_params": {"model": "openai/gpt-3", "api_key": "k", "input_cost_per_token": 3e-06}, + } + router: Final = Router(model_list=[*(_concurrent_delete_deployment(index) for index in range(3)), priced]) + complexity: Final = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=router, + complexity_router_config={"adaptive": True, "tiers": {"SIMPLE": ["model-3"]}}, + ) + + adaptive: Final = _read_during_held_write( + complexity._ensure_adaptive_router, lambda: router.delete_deployment("deployment-0") + ) + + assert adaptive is not None + assert adaptive.model_to_cost == {"model-3": 3e-06} + + +def test_delete_of_an_unknown_deployment_returns_none_and_keeps_the_list() -> None: + router: Final = _concurrent_delete_router() + + assert router.delete_deployment("deployment-9") is None + assert router.get_model_ids() == [f"deployment-{index}" for index in range(4)] + + +def test_get_model_ids_by_name_skips_team_deployments_only_when_asked() -> None: + shared: Final = {"model_name": "shared", "litellm_params": {"model": "openai/gpt-shared", "api_key": "k"}} + router: Final = Router( + model_list=[ + {**shared, "model_info": {"id": "global"}}, + {**shared, "model_info": {"id": "team", "team_id": "team-1", "team_public_model_name": "public"}}, + ] + ) + + assert router.get_model_ids("shared", exclude_team_models=True) == ["global"] + assert router.get_model_ids("shared") == ["global", "team"] + + _IMAGE_PART = {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo="}} _IMAGE_CHAT = [{"role": "user", "content": [{"type": "text", "text": "What is in this image?"}, _IMAGE_PART]}] _IMAGE_RESPONSES_INPUT = [ From 4da24fa01e2248a44c3a62e2022b43f5a38794b8 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 15:42:17 -0700 Subject: [PATCH 31/73] fix(ui): show search tool errors instead of empty results (#48478) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../_components/SearchToolTester.test.tsx | 32 +++++++++++++++++-- .../_components/SearchToolTester.tsx | 2 +- .../src/components/networking.test.ts | 26 +++++++++++++++ .../src/components/networking.tsx | 2 +- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx index 54be24d2401..08a0ab7d02b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { SearchToolTester } from "./SearchToolTester"; @@ -271,7 +271,15 @@ describe("SearchToolTester", () => { }); it("should handle search errors and show notification", async () => { - const error = new Error("Search failed"); + const error = new Error( + JSON.stringify({ + error: { + message: "User not allowed to access search tool: exa-search-2", + type: "user_search_tool_access_denied", + code: "403", + }, + }), + ); vi.mocked(networking.searchToolQueryCall).mockRejectedValue(error); const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); const user = userEvent.setup(); @@ -281,11 +289,29 @@ describe("SearchToolTester", () => { const searchButton = screen.getByRole("button", { name: /search/i }); await user.click(searchButton); await waitFor(() => { - expect(toast.fromError).toHaveBeenCalledWith("Failed to query search tool"); + expect(toast.fromError).toHaveBeenCalledWith(error); }); consoleSpy.mockRestore(); }); + it("should not show empty results or add history when search fails", async () => { + vi.mocked(networking.searchToolQueryCall).mockRejectedValue(new Error("Search failed")); + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + const user = userEvent.setup(); + render(); + const input = screen.getByPlaceholderText("Enter your search query..."); + fireEvent.change(input, { target: { value: "test query" } }); + await user.click(screen.getByRole("button", { name: /search/i })); + + await waitFor(() => { + expect(toast.fromError).toHaveBeenCalled(); + }); + expect(screen.queryByText("No results found")).not.toBeInTheDocument(); + expect(screen.queryByText("test query")).not.toBeInTheDocument(); + expect(screen.getByText("Test your search tool")).toBeInTheDocument(); + consoleSpy.mockRestore(); + }); + it("should maintain search history", async () => { const user = userEvent.setup(); render(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx index 7dcef3fc6ba..646120443c7 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx @@ -60,7 +60,7 @@ export const SearchToolTester: React.FC = ({ searchToolNa setSearchHistory((prev) => [historyEntry, ...prev]); } catch (error) { console.error("Error querying search tool:", error); - toast.fromError("Failed to query search tool"); + toast.fromError(error); } finally { setIsLoading(false); } diff --git a/ui/litellm-dashboard/src/components/networking.test.ts b/ui/litellm-dashboard/src/components/networking.test.ts index e7a55870345..8b0f0389772 100644 --- a/ui/litellm-dashboard/src/components/networking.test.ts +++ b/ui/litellm-dashboard/src/components/networking.test.ts @@ -1178,3 +1178,29 @@ describe("getUiConfig runtime backend discovery", () => { }); }); }); + +describe("searchToolQueryCall", () => { + const originalFetch = global.fetch; + + afterEach(() => { + global.fetch = originalFetch; + }); + + it("rejects with the backend error message for a denied search", async () => { + const errorMessage = "User not allowed to access search tool: exa-search-2"; + global.fetch = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ + error: { + message: errorMessage, + type: "user_search_tool_access_denied", + code: "403", + }, + }), + { status: 403 }, + ), + ); + + await expect(Networking.searchToolQueryCall("token", "exa-search-2", "litellm")).rejects.toThrow(errorMessage); + }); +}); diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index fd25242ec34..9aade717566 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -6860,7 +6860,7 @@ export const searchToolQueryCall = async ( if (!response.ok) { const errorData = await response.text(); await handleError(errorData); - return null; + throw new Error(errorData); } const data = await response.json(); From 2e27a02a9fce9bdc6ece13ba3cb8222dc5b6ecfa Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:44:05 -0700 Subject: [PATCH 32/73] feat(chatgpt): forward client subscriptions through the gateway (#41522) * feat(chatgpt): forward each Codex user's ChatGPT OAuth login through the proxy Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(router): cover _model_group_llm_provider metadata resolution Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(chatgpt): keep the gateway ChatGPT login bound to the authenticator api_base Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(chatgpt): move client credential header selection into the chatgpt provider Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(chatgpt): cover credential recognition and stored account id fallbacks Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(chatgpt): support per-model chatgpt_auth_file for multiple ChatGPT accounts Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(chatgpt): resolve bare chatgpt_auth_file paths and drop redundant test docstring Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(chatgpt): pass per-model auth file to early provider detection Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(chatgpt): normalize chatgpt_auth_file before caching the authenticator Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(chatgpt): type account selection parameters * fix(chatgpt): preserve account selection through saved prompts * fix(chatgpt): allow administrator account configuration through the proxy * test(chatgpt): construct immutable request parameters * fix(router): preserve typed deployment credential resolution * fix(chatgpt): allow admins to probe unsaved account configurations * fix(chatgpt): retain SDK login headers during provider resolution * fix(chatgpt): allow admins to probe unsaved account configurations * fix(chatgpt): normalize subscription health probe input * fix(chatgpt): normalize health probe input and cover credential destinations * test(chatgpt): keep destination rejection assertions focused * feat(proxy): forward client ChatGPT subscriptions on native Codex routes * docs: lead ChatGPT forwarding with standard model setup * fix(chatgpt): preserve selected credentials in health provider lookup * fix(chatgpt): preserve gateway credentials and workspace selection * refactor(chatgpt): avoid rebinding streaming metadata inputs * fix(chatgpt): keep account lookup token argument immutable * fix(chatgpt): scope forwarded credential routing to ChatGPT * refactor: keep forwarded credential isolation opt-in by provider --------- Co-authored-by: Moe Khalil Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Urbanonymous Co-authored-by: mateo --- .../chatgpt_subscription_passthrough.md | 76 +++ .../litellm_core_utils/get_litellm_params.py | 1 + .../get_llm_provider_logic.py | 12 +- .../get_provider_specific_headers.py | 13 + litellm/llms/chatgpt/authenticator.py | 85 +-- litellm/llms/chatgpt/chat/transformation.py | 83 ++- litellm/llms/chatgpt/common_utils.py | 169 ++++- .../llms/chatgpt/responses/transformation.py | 38 +- litellm/llms/custom_httpx/llm_http_handler.py | 48 +- litellm/main.py | 13 +- litellm/proxy/_lazy_features.py | 5 + litellm/proxy/_lazy_openapi_snapshot.json | 178 +++++ litellm/proxy/_types.py | 1 + litellm/proxy/auth/auth_utils.py | 4 +- .../health_endpoints/_health_endpoints.py | 10 +- litellm/proxy/litellm_pre_call_utils.py | 15 +- .../codex_passthrough_endpoints.py | 87 +++ .../openai_passthrough_logging_handler.py | 9 +- .../pass_through_endpoints.py | 15 +- litellm/proxy/spend_tracking/savings.py | 5 +- litellm/responses/main.py | 53 +- litellm/responses/utils.py | 20 + litellm/router.py | 62 +- litellm/types/litellm_params.py | 1 + litellm/types/router.py | 4 + litellm/types/utils.py | 1 + litellm/utils.py | 6 +- .../test_get_llm_provider_logic.py | 49 ++ .../chat/test_chatgpt_chat_transformation.py | 638 ++++++++++++++++++ .../test_chatgpt_responses_transformation.py | 165 +++++ .../chatgpt/test_chatgpt_authenticator.py | 148 ++++ .../llms/chatgpt/test_chatgpt_common_utils.py | 90 +++ .../custom_httpx/test_llm_http_handler.py | 41 ++ tests/unit/proxy/auth/test_auth_utils.py | 36 + .../health_endpoints/test_health_endpoints.py | 61 ++ .../test_model_management_endpoints.py | 24 +- .../test_codex_passthrough_endpoints.py | 185 +++++ .../unit/proxy/test_litellm_pre_call_utils.py | 92 +++ tests/unit/responses/test_responses_utils.py | 36 + tests/unit/test_router/test_router.py | 9 + tests/unit/types/test_litellm_params.py | 1 + tests/unit/types/test_router.py | 3 +- ui/litellm-dashboard/src/lib/http/schema.d.ts | 105 +++ 43 files changed, 2547 insertions(+), 150 deletions(-) create mode 100644 cookbook/ai_coding_tool_guides/chatgpt_subscription_passthrough.md create mode 100644 litellm/proxy/pass_through_endpoints/codex_passthrough_endpoints.py create mode 100644 tests/unit/llms/chatgpt/chat/test_chatgpt_chat_transformation.py create mode 100644 tests/unit/llms/chatgpt/test_chatgpt_common_utils.py create mode 100644 tests/unit/proxy/pass_through_endpoints/test_codex_passthrough_endpoints.py diff --git a/cookbook/ai_coding_tool_guides/chatgpt_subscription_passthrough.md b/cookbook/ai_coding_tool_guides/chatgpt_subscription_passthrough.md new file mode 100644 index 00000000000..c79632e9195 --- /dev/null +++ b/cookbook/ai_coding_tool_guides/chatgpt_subscription_passthrough.md @@ -0,0 +1,76 @@ +# Use your ChatGPT subscription through LiteLLM + +Sign into ChatGPT in Codex or Pi and send model requests through your existing LiteLLM proxy. The client keeps your login and refreshes it. Each request uses that client's ChatGPT account, while a separate LiteLLM virtual key controls gateway access and usage attribution. + +An administrator adds a `chatgpt/` model using the usual model configuration. No ChatGPT login needs to be added to the proxy. This uses the same separate-key convention as provider passthrough, including Claude Code subscription forwarding. + +## Add a model to the proxy + +Reuse your existing proxy. If you need one, follow the [proxy quickstart](https://docs.litellm.ai/docs/proxy/docker_quick_start). Merge this entry into its model list and restart the proxy: + +```yaml +model_list: + - model_name: gpt-6-luna + litellm_params: + model: chatgpt/gpt-6-luna + model_info: + mode: responses +``` + +Use a model available to the caller's subscription. The virtual key must allow the exposed model name. Leave saved ChatGPT logins and API credentials unset for a client-only deployment. Explicit cross-provider fallbacks can incur separate API charges; do not configure them for subscription-only traffic. + +## Configure Codex + +Use your existing LiteLLM URL and a virtual key assigned to your gateway user. Export that key as `LITELLM_API_KEY` in the environment that launches Codex. Keep the value out of committed configuration. + +Add this provider to `~/.codex/config.toml`, preserving your other settings: + +```toml +model = "gpt-6-luna" +model_provider = "litellm_subscription" + +[model_providers.litellm_subscription] +name = "LiteLLM subscription" +base_url = "https://litellm.example.com/v1" +wire_api = "responses" +supports_websockets = false +requires_openai_auth = true +env_http_headers = { "x-litellm-api-key" = "LITELLM_API_KEY" } +``` + +Replace `https://litellm.example.com` with your proxy URL. A local proxy normally uses `http://localhost:4000/v1`. Launch Codex, sign into ChatGPT if needed, and send a prompt with a model available to your subscription. The selected model must match the name exposed by the gateway. + +For a managed workspace rollout, use OpenAI's [gateway configuration guide](https://learn.chatgpt.com/docs/enterprise/sign-in-with-chatgpt-through-a-gateway). + +## Native compatibility and Pi + +For a native Codex model catalog and request format, change Codex's `base_url` to `https://litellm.example.com/backend-api/codex`. This route uses the same credentials but does not need a model deployment or resolve model aliases. Restricted keys must allow the upstream model name. + +### Configure Pi + +Sign into the built-in `openai-codex` provider in Pi. Merge this override into `~/.pi/agent/models.json` and export your LiteLLM virtual key as `LITELLM_API_KEY` before launching Pi: + +```json +{ + "providers": { + "openai-codex": { + "baseUrl": "https://litellm.example.com/backend-api/codex", + "headers": { + "x-litellm-api-key": "LITELLM_API_KEY" + } + } + } +} +``` + +Keep Pi's subscription login as the provider credential. The header value names an environment variable, not a key to paste into the file. + +## Verify account usage + +Send a prompt and check the completed request in LiteLLM's Logs page. Its gateway user/key and token counts should match the caller. Give each person their own LiteLLM virtual key for separate attribution. The gateway credential and ChatGPT login are independently authenticated; LiteLLM does not establish that both identities belong to the same person. + +The native routes are `GET /backend-api/codex/models` and `POST /backend-api/codex/responses`. Model requests use the upstream model name, so a restricted LiteLLM key must allow that name. OpenAI's model catalog is returned in its native format; seeing a model there does not override gateway permissions. + +The proxy forwards the ChatGPT bearer and account header to the fixed OpenAI Codex endpoint. It removes gateway credentials and cookies from the upstream request and redacts credential headers in logging callbacks. The client remains responsible for refresh and reauthentication. Native passthrough returns authentication and quota errors without switching subscriptions. Standard model routes retain the caller login across ChatGPT retries; explicitly configured cross-provider fallbacks use their own credentials. An explicit workspace header is preserved for OpenAI to validate. + +This flow does not connect accounts through the dashboard or pool subscription allowances. Use a Codex build that supports ChatGPT authentication with a custom model provider. The integration forwards HTTP and SSE requests; it does not add native WebSocket or other Codex backend routes. diff --git a/litellm/litellm_core_utils/get_litellm_params.py b/litellm/litellm_core_utils/get_litellm_params.py index 85557090ae9..ca8e2af18e6 100644 --- a/litellm/litellm_core_utils/get_litellm_params.py +++ b/litellm/litellm_core_utils/get_litellm_params.py @@ -76,6 +76,7 @@ OPTIONAL_KWARGS_KEYS: Final = ( "itpm", "otpm", "use_xai_oauth", + "chatgpt_auth_file", "github_copilot_auth_type", "github_copilot_host", "github_copilot_user_session", diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index de51ecd0b21..19c1d5c414c 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -158,6 +158,14 @@ def declared_authenticating_provider(model: str | None, custom_llm_provider: str return declared if declared in PROVIDERS_THAT_AUTHENTICATE_ON_PROVIDER_INFO else None +def resolve_model_and_provider_for_metadata(model: str, custom_llm_provider: str | None = None) -> tuple[str, str]: + declared: Final = declared_authenticating_provider(model, custom_llm_provider) + if declared is not None: + return model.removeprefix(f"{declared}/"), declared + resolved_model, resolved_provider, _, _ = get_llm_provider(model=model, custom_llm_provider=custom_llm_provider) + return resolved_model, resolved_provider + + def inferred_provider(model: str | None) -> str | None: if not model: return None @@ -842,7 +850,9 @@ def _get_openai_compatible_provider_info( api_base, dynamic_api_key, custom_llm_provider, - ) = litellm.ChatGPTConfig().get_openai_compatible_provider_info(model, api_base, api_key, custom_llm_provider) + ) = litellm.ChatGPTConfig().get_openai_compatible_provider_info( + model, api_base, api_key, custom_llm_provider, litellm_params + ) elif custom_llm_provider == "novita": api_base = api_base or get_secret("NOVITA_API_BASE") or "https://api.novita.ai/v3/openai" dynamic_api_key = api_key or get_secret_str("NOVITA_API_KEY") diff --git a/litellm/litellm_core_utils/get_provider_specific_headers.py b/litellm/litellm_core_utils/get_provider_specific_headers.py index bc325c3c702..62dfcb073e5 100644 --- a/litellm/litellm_core_utils/get_provider_specific_headers.py +++ b/litellm/litellm_core_utils/get_provider_specific_headers.py @@ -1,10 +1,23 @@ from collections.abc import Mapping, Sequence from typing import Final +from pydantic import TypeAdapter + from litellm.types.utils import ProviderSpecificHeader +_PROVIDER_HEADERS: Final[TypeAdapter[ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None]] = TypeAdapter( + ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None +) + class ProviderSpecificHeaderUtils: + @staticmethod + def forwarded_authorization(provider_specific_header: object, custom_llm_provider: str | None) -> str | None: + headers: Final = ProviderSpecificHeaderUtils.get_provider_specific_headers( + _PROVIDER_HEADERS.validate_python(provider_specific_header), custom_llm_provider + ) + return next((value for key, value in headers.items() if key.lower() == "authorization"), None) + @staticmethod def get_provider_specific_headers( provider_specific_header: ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None, diff --git a/litellm/llms/chatgpt/authenticator.py b/litellm/llms/chatgpt/authenticator.py index ff9c6476cba..a5fb1c9c6b9 100644 --- a/litellm/llms/chatgpt/authenticator.py +++ b/litellm/llms/chatgpt/authenticator.py @@ -1,9 +1,9 @@ -import base64 import json import os import time from collections.abc import Mapping -from typing import Final, TypeAlias +from functools import lru_cache +from typing import TYPE_CHECKING, Final import httpx from pydantic import JsonValue, TypeAdapter, ValidationError @@ -24,19 +24,21 @@ from .common_utils import ( CHATGPT_OAUTH_TOKEN_URL, GetAccessTokenError, GetDeviceCodeError, + JsonObject, RefreshAccessTokenError, + decode_jwt_claims, + extract_chatgpt_account_id, ) +if TYPE_CHECKING: + from litellm.types.router import GenericLiteLLMParams + TOKEN_EXPIRY_SKEW_SECONDS: Final = 60 TOKEN_REFRESH_TIMEOUT_SECONDS: Final = 30 DEVICE_CODE_TIMEOUT_SECONDS: Final = 15 * 60 DEVICE_CODE_COOLDOWN_SECONDS: Final = 5 * 60 DEVICE_CODE_POLL_SLEEP_SECONDS: Final = 5 -OPENAI_AUTH_CLAIM_KEY: Final = "https://api.openai.com/auth" - -JsonObject: TypeAlias = Mapping[str, JsonValue] - _JSON_OBJECT_ADAPTER: Final = TypeAdapter(JsonObject) @@ -44,6 +46,26 @@ def _optional_str(value: JsonValue | None) -> str | None: return value if isinstance(value, str) else None +def get_chatgpt_auth_file( + litellm_params: "Mapping[str, object] | GenericLiteLLMParams | None", +) -> str | None: + if litellm_params is None: + return None + value: Final = ( + litellm_params.get("chatgpt_auth_file") + if isinstance(litellm_params, Mapping) + else litellm_params.chatgpt_auth_file + ) + if not isinstance(value, str) or not value: + return None + return os.path.abspath(os.path.expanduser(value)) + + +@lru_cache(maxsize=128) +def get_cached_authenticator(auth_file: str) -> "Authenticator": + return Authenticator(auth_file=auth_file) + + def _token_refresh_timeout() -> httpx.Timeout: configured: Final = get_configured_request_timeout() seconds: Final = ( @@ -53,13 +75,13 @@ def _token_refresh_timeout() -> httpx.Timeout: class Authenticator: - def __init__(self) -> None: - self.token_dir = os.getenv( - "CHATGPT_TOKEN_DIR", - os.path.expanduser("~/.config/litellm/chatgpt"), + def __init__(self, auth_file: str | None = None) -> None: + default_auth_file: Final = os.path.join( + os.getenv("CHATGPT_TOKEN_DIR", os.path.expanduser("~/.config/litellm/chatgpt")), + os.getenv("CHATGPT_AUTH_FILE", "auth.json"), ) - self.auth_file = os.path.join(self.token_dir, os.getenv("CHATGPT_AUTH_FILE", "auth.json")) - self._ensure_token_dir() + self.auth_file = os.path.abspath(os.path.expanduser(auth_file or default_auth_file)) + self.token_dir = os.path.dirname(self.auth_file) def get_api_base(self) -> str: return os.getenv("CHATGPT_API_BASE") or os.getenv("OPENAI_CHATGPT_API_BASE") or CHATGPT_API_BASE @@ -99,16 +121,18 @@ class Authenticator: tokens: Final = self._login_device_code() return tokens["access_token"] - def get_account_id(self) -> str | None: + def get_account_id(self, access_token: str | None = None) -> str | None: auth_data: Final = self._read_auth_file() + if access_token and (not auth_data or auth_data.get("access_token") != access_token): + return extract_chatgpt_account_id(access_token) if not auth_data: return None account_id: Final = _optional_str(auth_data.get("account_id")) if account_id: return account_id id_token: Final = auth_data.get("id_token") - access_token: Final = auth_data.get("access_token") - derived: Final = self._extract_account_id(_optional_str(id_token or access_token)) + saved_access_token: Final = auth_data.get("access_token") + derived: Final = extract_chatgpt_account_id(_optional_str(id_token or saved_access_token)) if derived: self._write_auth_file({**auth_data, "account_id": derived}) return derived @@ -129,6 +153,7 @@ class Authenticator: def _write_auth_file(self, data: JsonObject) -> None: try: + self._ensure_token_dir() with open(self.auth_file, "w") as f: json.dump(data, f) except OSError as exc: @@ -146,34 +171,11 @@ class Authenticator: return time.time() >= float(derived_expires_at) - TOKEN_EXPIRY_SKEW_SECONDS def _get_expires_at(self, token: str) -> int | None: - claims: Final = self._decode_jwt_claims(token) - exp: Final = claims.get("exp") + exp: Final = decode_jwt_claims(token).get("exp") if isinstance(exp, (int, float)): return int(exp) return None - def _decode_jwt_claims(self, token: str) -> JsonObject: - try: - parts: Final = token.split(".") - if len(parts) < 2: - return {} - payload_b64: Final = parts[1] + "=" * (-len(parts[1]) % 4) - payload_bytes: Final = base64.urlsafe_b64decode(payload_b64) - return _JSON_OBJECT_ADAPTER.validate_python(json.loads(payload_bytes.decode("utf-8"))) - except Exception: - return {} - - def _extract_account_id(self, token: str | None) -> str | None: - if not token: - return None - claims: Final = self._decode_jwt_claims(token) - auth_claims: Final = claims.get(OPENAI_AUTH_CLAIM_KEY) - if isinstance(auth_claims, dict): - account_id: Final = auth_claims.get("chatgpt_account_id") - if isinstance(account_id, str) and account_id: - return account_id - return None - def _login_device_code(self) -> dict[str, str]: cooldown_remaining: Final = self._get_device_code_cooldown_remaining(self._read_auth_file()) if cooldown_remaining > 0: @@ -361,15 +363,16 @@ class Authenticator: "refresh_token": _optional_str(data.get("refresh_token")) or refresh_token, "id_token": id_token, } + account_id: Final = self.get_account_id() auth_data: Final = self._build_auth_record(refreshed) - self._write_auth_file(auth_data) + self._write_auth_file({**auth_data, "account_id": account_id} if account_id else auth_data) return refreshed def _build_auth_record(self, tokens: dict[str, str]) -> JsonObject: access_token: Final = tokens.get("access_token") id_token: Final = tokens.get("id_token") expires_at: Final = self._get_expires_at(access_token) if access_token else None - account_id: Final = self._extract_account_id(id_token or access_token) + account_id: Final = extract_chatgpt_account_id(id_token or access_token) return { "access_token": access_token, "refresh_token": tokens.get("refresh_token"), diff --git a/litellm/llms/chatgpt/chat/transformation.py b/litellm/llms/chatgpt/chat/transformation.py index a1aac7f315b..9005185931f 100644 --- a/litellm/llms/chatgpt/chat/transformation.py +++ b/litellm/llms/chatgpt/chat/transformation.py @@ -1,14 +1,20 @@ +from collections.abc import Mapping from typing import Any, Final from litellm.exceptions import AuthenticationError from litellm.llms.openai.openai import OpenAIConfig from litellm.types.llms.openai import AllMessageValues +from litellm.types.router import GenericLiteLLMParams -from ..authenticator import Authenticator +from ..authenticator import Authenticator, get_cached_authenticator, get_chatgpt_auth_file from ..common_utils import ( GetAccessTokenError, + chatgpt_client_api_base, ensure_chatgpt_session_id, + get_chatgpt_client_credential, get_chatgpt_default_headers, + get_chatgpt_request_credential, + strip_chatgpt_client_credential_headers, ) from .streaming_utils import ChatGPTToolCallNormalizer @@ -19,9 +25,27 @@ class ChatGPTConfig(OpenAIConfig): api_key: str | None = None, api_base: str | None = None, custom_llm_provider: str = "openai", + authenticator: Authenticator | None = None, ) -> None: super().__init__() - self.authenticator = Authenticator() + self.authenticator = authenticator if authenticator is not None else Authenticator() + + def _resolve_authenticator( + self, litellm_params: Mapping[str, object] | GenericLiteLLMParams | None + ) -> Authenticator: + auth_file: Final = get_chatgpt_auth_file(litellm_params) + return get_cached_authenticator(auth_file) if auth_file else self.authenticator + + @staticmethod + def _get_access_token_or_raise(authenticator: Authenticator, model: str, llm_provider: str) -> str: + try: + return authenticator.get_access_token() + except GetAccessTokenError as e: + raise AuthenticationError( + model=model, + llm_provider=llm_provider, + message=str(e), + ) def api_base_without_login(self) -> str: return self.authenticator.get_api_base() @@ -32,16 +56,16 @@ class ChatGPTConfig(OpenAIConfig): api_base: str | None, api_key: str | None, custom_llm_provider: str, + litellm_params: Mapping[str, object] | GenericLiteLLMParams | None = None, ) -> tuple[str | None, str | None, str]: - dynamic_api_base: Final = self.api_base_without_login() - try: - dynamic_api_key: Final = self.authenticator.get_access_token() - except GetAccessTokenError as e: - raise AuthenticationError( - model=model, - llm_provider=custom_llm_provider, - message=str(e), - ) + client_credential: Final = get_chatgpt_request_credential(litellm_params) + if client_credential is not None: + return chatgpt_client_api_base(api_base), client_credential.access_token, custom_llm_provider + if api_key: + return api_base or self.api_base_without_login(), api_key, custom_llm_provider + authenticator: Final = self._resolve_authenticator(litellm_params) + dynamic_api_base: Final = authenticator.get_api_base() + dynamic_api_key: Final = self._get_access_token_or_raise(authenticator, model, custom_llm_provider) return dynamic_api_base, dynamic_api_key, custom_llm_provider def get_openai_compatible_provider_info( @@ -50,26 +74,49 @@ class ChatGPTConfig(OpenAIConfig): api_base: str | None, api_key: str | None, custom_llm_provider: str, + litellm_params: Mapping[str, object] | GenericLiteLLMParams | None = None, ) -> tuple[str | None, str | None, str]: - return self._get_openai_compatible_provider_info(model, api_base, api_key, custom_llm_provider) + return self._get_openai_compatible_provider_info(model, api_base, api_key, custom_llm_provider, litellm_params) def validate_environment( self, - headers: dict, + headers: dict[str, str], model: str, messages: list[AllMessageValues], optional_params: dict, - litellm_params: dict, + litellm_params: dict[str, object], api_key: str | None = None, api_base: str | None = None, - ) -> dict: + ) -> dict[str, str]: + client_credential: Final = get_chatgpt_client_credential(headers) + if client_credential is not None: + return dict( + get_chatgpt_default_headers( + client_credential.access_token, + client_credential.account_id, + ensure_chatgpt_session_id(litellm_params), + ), + **strip_chatgpt_client_credential_headers(headers), + ) + authenticator: Final = self._resolve_authenticator(litellm_params) + resolved_api_key: Final = ( + self._get_access_token_or_raise(authenticator, model, "chatgpt") + if get_chatgpt_auth_file(litellm_params) + else api_key + ) validated_headers: Final = super().validate_environment( - headers, model, messages, optional_params, litellm_params, api_key, api_base + dict(strip_chatgpt_client_credential_headers(headers)), + model, + messages, + optional_params, + litellm_params, + resolved_api_key, + api_base, ) - account_id: Final = self.authenticator.get_account_id() + account_id: Final = authenticator.get_account_id(resolved_api_key) session_id: Final = ensure_chatgpt_session_id(litellm_params) - default_headers: Final = get_chatgpt_default_headers(api_key or "", account_id, session_id) + default_headers: Final = get_chatgpt_default_headers(resolved_api_key or "", account_id, session_id) return {**default_headers, **validated_headers} def post_stream_processing(self, stream: Any) -> ChatGPTToolCallNormalizer: diff --git a/litellm/llms/chatgpt/common_utils.py b/litellm/llms/chatgpt/common_utils.py index fe33219f110..ec7fbfa92da 100644 --- a/litellm/llms/chatgpt/common_utils.py +++ b/litellm/llms/chatgpt/common_utils.py @@ -2,14 +2,32 @@ Constants and helpers for ChatGPT subscription OAuth. """ +import base64 +import json import os import platform -from typing import Any, Final +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from types import MappingProxyType +from typing import TYPE_CHECKING, Final, TypeAlias +from urllib.parse import urlsplit from uuid import uuid4 import httpx +from pydantic import JsonValue, TypeAdapter +from litellm.litellm_core_utils.get_provider_specific_headers import ProviderSpecificHeaderUtils from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.types.utils import ProviderSpecificHeader + +if TYPE_CHECKING: + from litellm.types.router import GenericLiteLLMParams + +_SCOPED_HEADERS: Final[TypeAdapter[ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None]] = TypeAdapter( + ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None +) +_STRING_HEADERS: Final = TypeAdapter(Mapping[str, str]) +_PARAMS: Final = TypeAdapter(Mapping[str, object]) # OAuth + API constants (derived from openai/codex) CHATGPT_AUTH_BASE: Final = "https://auth.openai.com" @@ -19,6 +37,12 @@ CHATGPT_OAUTH_TOKEN_URL: Final = f"{CHATGPT_AUTH_BASE}/oauth/token" CHATGPT_DEVICE_VERIFY_URL: Final = f"{CHATGPT_AUTH_BASE}/codex/device" CHATGPT_API_BASE: Final = "https://chatgpt.com/backend-api/codex" CHATGPT_CLIENT_ID: Final = "app_EMoamEEZ73f0CkXaXp7hrann" +CHATGPT_ACCOUNT_ID_HEADER: Final = "ChatGPT-Account-Id" +OPENAI_AUTH_CLAIM_KEY: Final = "https://api.openai.com/auth" + +JsonObject: TypeAlias = Mapping[str, JsonValue] +_JSON_OBJECT_ADAPTER: Final = TypeAdapter(JsonObject) +_EMPTY_CLAIMS: Final[JsonObject] = MappingProxyType({}) DEFAULT_ORIGINATOR: Final = "codex_cli_rs" DEFAULT_USER_AGENT: Final = "codex_cli_rs/0.0.0 (Unknown 0; unknown) unknown" @@ -229,7 +253,7 @@ def get_chatgpt_default_headers( access_token: str, account_id: str | None, session_id: str | None = None, -) -> dict: +) -> dict[str, str]: originator: Final = get_chatgpt_originator() user_agent: Final = get_chatgpt_user_agent(originator) headers: Final = { @@ -242,30 +266,133 @@ def get_chatgpt_default_headers( if session_id: headers["session_id"] = session_id if account_id: - headers["ChatGPT-Account-Id"] = account_id + headers[CHATGPT_ACCOUNT_ID_HEADER] = account_id return headers +def decode_jwt_claims(token: str) -> JsonObject: + try: + parts: Final = token.split(".") + if len(parts) < 2: + return _EMPTY_CLAIMS + payload_b64: Final = parts[1] + "=" * (-len(parts[1]) % 4) + payload_bytes: Final = base64.urlsafe_b64decode(payload_b64) + return _JSON_OBJECT_ADAPTER.validate_python(json.loads(payload_bytes.decode("utf-8"))) + except Exception: + return _EMPTY_CLAIMS + + +def extract_chatgpt_account_id(token: str | None) -> str | None: + if not token: + return None + auth_claims: Final = decode_jwt_claims(token).get(OPENAI_AUTH_CLAIM_KEY) + if not isinstance(auth_claims, dict): + return None + account_id: Final = auth_claims.get("chatgpt_account_id") + return account_id if isinstance(account_id, str) and account_id else None + + +def _bearer_token(value: str) -> str: + scheme, separator, token = value.strip().partition(" ") + if not separator: + return scheme + return token.strip() if scheme.lower() == "bearer" else "" + + +def is_chatgpt_oauth_key(value: str | None) -> bool: + if value is None: + return False + return OPENAI_AUTH_CLAIM_KEY in decode_jwt_claims(_bearer_token(value)) + + +@dataclass(frozen=True, slots=True) +class ChatGPTClientCredential: + access_token: str + account_id: str | None + + +def _header_value(headers: Mapping[str, str], name: str) -> str | None: + return next((value for key, value in headers.items() if key.lower() == name), None) + + +def get_chatgpt_client_credential(headers: Mapping[str, str]) -> ChatGPTClientCredential | None: + auth_header: Final = _header_value(headers, "authorization") + if auth_header is None or not is_chatgpt_oauth_key(auth_header): + return None + access_token: Final = _bearer_token(auth_header) + account_id: Final = _header_value(headers, CHATGPT_ACCOUNT_ID_HEADER.lower()) or extract_chatgpt_account_id( + access_token + ) + return ChatGPTClientCredential(access_token=access_token, account_id=account_id) + + +def select_chatgpt_client_credential_headers(headers: Mapping[str, str]) -> Mapping[str, str]: + if get_chatgpt_client_credential(headers) is None: + return MappingProxyType({}) + return MappingProxyType( + { + key: value + for key, value in headers.items() + if (key.lower() == "authorization" and is_chatgpt_oauth_key(value)) + or key.lower() == CHATGPT_ACCOUNT_ID_HEADER.lower() + } + ) + + +def strip_chatgpt_client_credential_headers(headers: Mapping[str, str]) -> Mapping[str, str]: + return MappingProxyType( + { + key: value + for key, value in headers.items() + if key.lower() not in ("authorization", CHATGPT_ACCOUNT_ID_HEADER.lower()) + } + ) + + +def get_chatgpt_request_credential( + params: "Mapping[str, object] | GenericLiteLLMParams | None", +) -> ChatGPTClientCredential | None: + normalized: Final = _normalize_litellm_params(params) + scoped: Final = normalized.get("provider_specific_header") + extra: Final = normalized.get("extra_headers") + headers: Final = ProviderSpecificHeaderUtils.get_provider_specific_headers( + provider_specific_header=_SCOPED_HEADERS.validate_python(scoped), custom_llm_provider="chatgpt" + ) + return get_chatgpt_client_credential(headers) or get_chatgpt_client_credential( + _STRING_HEADERS.validate_python(extra) if extra is not None else MappingProxyType({}) + ) + + +def chatgpt_client_api_base(api_base: str | None) -> str: + base: Final = api_base or CHATGPT_API_BASE + parsed: Final = urlsplit(base) + if ( + parsed.scheme != "https" + or parsed.hostname != "chatgpt.com" + or parsed.port not in (None, 443) + or parsed.username + or parsed.password + ): + raise ChatGPTAuthError( + status_code=400, message="Client ChatGPT credentials require the official HTTPS ChatGPT endpoint" + ) + return base + + def get_chatgpt_default_instructions() -> str: return os.getenv("CHATGPT_DEFAULT_INSTRUCTIONS") or CHATGPT_DEFAULT_INSTRUCTIONS -def _normalize_litellm_params(litellm_params: Any | None) -> dict: - if litellm_params is None: - return {} - if isinstance(litellm_params, dict): - return litellm_params - if hasattr(litellm_params, "model_dump"): - try: - return litellm_params.model_dump() - except Exception: - return {} - if hasattr(litellm_params, "dict"): - try: - return litellm_params.dict() - except Exception: - return {} - return {} +def _normalize_litellm_params(litellm_params: object) -> Mapping[str, object]: + if isinstance(litellm_params, Mapping): + return _PARAMS.validate_python(litellm_params) + serializer: Final = getattr(litellm_params, "model_dump", None) or getattr(litellm_params, "dict", None) + if not callable(serializer): + return MappingProxyType({}) + try: + return _PARAMS.validate_python(serializer()) + except Exception: + return MappingProxyType({}) def get_chatgpt_session_id(litellm_params: object) -> str | None: @@ -274,8 +401,8 @@ def get_chatgpt_session_id(litellm_params: object) -> str | None: value = params.get(key) if value: return str(value) - metadata: Final = params.get("metadata") - if isinstance(metadata, dict): + metadata: Final = _normalize_litellm_params(params.get("metadata")) + if metadata: value = metadata.get("session_id") if value: return str(value) diff --git a/litellm/llms/chatgpt/responses/transformation.py b/litellm/llms/chatgpt/responses/transformation.py index 34953a79c60..2352de35b5b 100644 --- a/litellm/llms/chatgpt/responses/transformation.py +++ b/litellm/llms/chatgpt/responses/transformation.py @@ -25,13 +25,15 @@ from litellm.types.llms.openai import ( from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import LlmProviders -from ..authenticator import Authenticator +from ..authenticator import Authenticator, get_cached_authenticator, get_chatgpt_auth_file from ..common_utils import ( CHATGPT_API_BASE, GetAccessTokenError, ensure_chatgpt_session_id, + get_chatgpt_client_credential, get_chatgpt_default_headers, get_chatgpt_default_instructions, + strip_chatgpt_client_credential_headers, ) if TYPE_CHECKING: @@ -49,14 +51,35 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig): def custom_llm_provider(self) -> LlmProviders: return LlmProviders.CHATGPT + def _resolve_authenticator(self, litellm_params: GenericLiteLLMParams | None) -> Authenticator: + auth_file: Final = get_chatgpt_auth_file(litellm_params) + return get_cached_authenticator(auth_file) if auth_file else self.authenticator + def validate_environment( self, - headers: dict, + headers: dict[str, str], model: str, litellm_params: GenericLiteLLMParams | None, - ) -> dict: + ) -> dict[str, str]: + session_id: Final = ensure_chatgpt_session_id(litellm_params) + client_credential: Final = get_chatgpt_client_credential(headers) + if client_credential is not None: + return { + **get_chatgpt_default_headers(client_credential.access_token, client_credential.account_id, session_id), + **strip_chatgpt_client_credential_headers(headers), + } + authenticator: Final = self._resolve_authenticator(litellm_params) + safe_headers: Final = strip_chatgpt_client_credential_headers(headers) + configured_api_key: Final = litellm_params.api_key if litellm_params is not None else None + if configured_api_key and not get_chatgpt_auth_file(litellm_params): + return { + **get_chatgpt_default_headers( + configured_api_key, authenticator.get_account_id(configured_api_key), session_id + ), + **safe_headers, + } try: - access_token: Final = self.authenticator.get_access_token() + access_token: Final = authenticator.get_access_token() except GetAccessTokenError as e: raise AuthenticationError( model=model, @@ -64,10 +87,9 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig): message=str(e), ) - account_id: Final = self.authenticator.get_account_id() - session_id: Final = ensure_chatgpt_session_id(litellm_params) + account_id: Final = authenticator.get_account_id() default_headers: Final = get_chatgpt_default_headers(access_token, account_id, session_id) - return {**default_headers, **headers} + return {**default_headers, **safe_headers} def transform_responses_api_request( self, @@ -79,7 +101,7 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig): ) -> dict: request: Final = super().transform_responses_api_request( model, - input, + [{"role": "user", "content": input}] if isinstance(input, str) else input, response_api_optional_request_params, litellm_params, headers, diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 1b491f1614d..7b808cf2be2 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -198,6 +198,26 @@ from litellm.utils import ( ) +def _merge_headers_case_insensitive( + headers: Mapping[str, object], extra_headers: Mapping[str, object] | None +) -> dict[str, str]: # mutable-ok: legacy HTTP handler methods require a dict of headers + overridden: Final = frozenset(name.lower() for name in extra_headers or {}) + return { + **{name: str(value) for name, value in headers.items() if name.lower() not in overridden}, + **{name: str(value) for name, value in (extra_headers or {}).items()}, + } + + +def _client_request_headers( + response_api_optional_request_params: Mapping[str, object], + extra_headers: Mapping[str, object] | None, +) -> dict[str, str]: # mutable-ok: provider validate_environment methods require a dict + from litellm.litellm_core_utils.core_helpers import as_str_mapping + + optional_headers: Final = as_str_mapping(response_api_optional_request_params.get("extra_headers")) + return _merge_headers_case_insensitive(optional_headers or {}, extra_headers) + + def _rust_responses_websocket_enabled( custom_llm_provider: str | None, ) -> bool: @@ -2906,14 +2926,18 @@ class BaseLLMHTTPHandler: else: sync_httpx_client = client - headers = responses_api_provider_config.validate_environment( - headers=response_api_optional_request_params.get("extra_headers", {}) or {}, - model=model, - litellm_params=litellm_params, - ) - - if extra_headers: - headers.update(extra_headers) + headers = { # rebind-ok: validation replaces the method's incoming headers + **responses_api_provider_config.validate_environment( + headers=( + _client_request_headers(response_api_optional_request_params, extra_headers) + if custom_llm_provider == "chatgpt" + else response_api_optional_request_params.get("extra_headers", {}) or {} + ), + model=model, + litellm_params=litellm_params, + ), + **((extra_headers or {}) if custom_llm_provider != "chatgpt" else {}), + } # Check if streaming is requested stream = response_api_optional_request_params.get("stream", False) @@ -3104,11 +3128,15 @@ class BaseLLMHTTPHandler: validated_headers: Final = { **await asyncio.to_thread( responses_api_provider_config.validate_environment, - headers=response_api_optional_request_params.get("extra_headers", {}) or {}, + headers=( + _client_request_headers(response_api_optional_request_params, extra_headers) + if custom_llm_provider == "chatgpt" + else response_api_optional_request_params.get("extra_headers", {}) or {} + ), model=model, litellm_params=litellm_params, ), - **(extra_headers or {}), + **((extra_headers or {}) if custom_llm_provider != "chatgpt" else {}), } # Check if streaming is requested diff --git a/litellm/main.py b/litellm/main.py index f30d93361e5..b3d62daa4d8 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -677,7 +677,11 @@ async def acompletion( } if custom_llm_provider is None: _supplemental: Final = cast( # cast-ok: kwargs values are request-scoped objects - "dict[str, object]", {k: kwargs[k] for k in OPTIONAL_KWARGS_KEYS if k in kwargs} + "dict[str, object]", + { + **{k: kwargs[k] for k in OPTIONAL_KWARGS_KEYS | {"provider_specific_header"} if k in kwargs}, + "extra_headers": extra_headers, + }, ) _, custom_llm_provider, _, _ = get_llm_provider( model=model, @@ -5678,7 +5682,10 @@ def completion( if deployment_id is not None: # azure llms model = deployment_id custom_llm_provider = "azure" - _supplemental_provider_params: Final = {k: kwargs[k] for k in OPTIONAL_KWARGS_KEYS if k in kwargs} + _supplemental_provider_params: Final = { + **{k: kwargs[k] for k in OPTIONAL_KWARGS_KEYS | {"provider_specific_header"} if k in kwargs}, + "extra_headers": extra_headers, + } model, custom_llm_provider, dynamic_api_key, api_base = get_llm_provider( model=model, custom_llm_provider=custom_llm_provider, @@ -5998,6 +6005,7 @@ def completion( tpm=kwargs.get("tpm"), rpm=kwargs.get("rpm"), use_xai_oauth=kwargs.get("use_xai_oauth", False), + chatgpt_auth_file=routing_kwargs.get("chatgpt_auth_file"), gigachat_scope=kwargs.get("gigachat_scope"), gigachat_auth_url=kwargs.get("gigachat_auth_url"), gigachat_access_token=kwargs.get("gigachat_access_token"), @@ -9140,6 +9148,7 @@ async def ahealth_check( custom_llm_provider=custom_llm_provider_from_params, api_base=api_base_from_params, api_key=api_key_from_params, + litellm_params=GenericLiteLLMParams(**model_params), ) model_params["cache"] = {"no-cache": True} # don't used cached responses for making health check calls diff --git a/litellm/proxy/_lazy_features.py b/litellm/proxy/_lazy_features.py index aa67d36af4f..4e54d6ac733 100644 --- a/litellm/proxy/_lazy_features.py +++ b/litellm/proxy/_lazy_features.py @@ -207,6 +207,11 @@ LAZY_FEATURES: Final[tuple[LazyFeature, ...]] = ( module_path="litellm.proxy.management_endpoints.config_override_endpoints", path_prefixes=("/config_overrides",), ), + LazyFeature( + name="codex_passthrough", + module_path="litellm.proxy.pass_through_endpoints.codex_passthrough_endpoints", + path_prefixes=("/backend-api/codex/",), + ), LazyFeature( name="llm_passthrough", module_path="litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints", diff --git a/litellm/proxy/_lazy_openapi_snapshot.json b/litellm/proxy/_lazy_openapi_snapshot.json index 4e183bf9d5c..166072e9613 100644 --- a/litellm/proxy/_lazy_openapi_snapshot.json +++ b/litellm/proxy/_lazy_openapi_snapshot.json @@ -8673,6 +8673,184 @@ } } }, + "codex_passthrough": { + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "title": "Detail", + "type": "array" + } + }, + "title": "HTTPValidationError", + "type": "object" + }, + "ValidationError": { + "properties": { + "ctx": { + "title": "Context", + "type": "object" + }, + "input": { + "title": "Input" + }, + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "title": "Location", + "type": "array" + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + }, + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError", + "type": "object" + } + } + }, + "paths": { + "/backend-api/codex/{endpoint}": { + "get": { + "description": "Forward a client's ChatGPT subscription with separate LiteLLM authentication.", + "operationId": "codex_passthrough_route_backend_api_codex__endpoint__get", + "parameters": [ + { + "in": "path", + "name": "endpoint", + "required": true, + "schema": { + "enum": [ + "models", + "responses" + ], + "title": "Endpoint", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": {} + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "security": [ + { + "APIKeyHeader": [] + } + ], + "summary": "Codex Passthrough Route", + "tags": [ + "codex_passthrough" + ] + }, + "post": { + "description": "Forward a client's ChatGPT subscription with separate LiteLLM authentication.", + "operationId": "codex_passthrough_route_backend_api_codex__endpoint__post", + "parameters": [ + { + "in": "path", + "name": "endpoint", + "required": true, + "schema": { + "enum": [ + "models", + "responses" + ], + "title": "Endpoint", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object" + } + }, + "text/event-stream": { + "schema": { + "type": "string" + } + } + }, + "description": "Unmodified Codex response or server-sent events" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "security": [ + { + "APIKeyHeader": [] + } + ], + "summary": "Codex Passthrough Route", + "tags": [ + "codex_passthrough" + ] + } + } + } + }, "compliance": { "components": { "schemas": { diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index db433f07f3b..6280e2caf87 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -391,6 +391,7 @@ class LiteLLMRoutes(enum.Enum): ] mapped_pass_through_routes = [ + "/backend-api/codex", "/bedrock", "/comprehendmedical", "/azure_speech", diff --git a/litellm/proxy/auth/auth_utils.py b/litellm/proxy/auth/auth_utils.py index 25f900a5f9f..30ede935cfa 100644 --- a/litellm/proxy/auth/auth_utils.py +++ b/litellm/proxy/auth/auth_utils.py @@ -736,7 +736,9 @@ def is_request_body_safe( def _reject_request_credential_params(litellm_params: Mapping[str, object], *, manages_deployments: bool) -> None: if manages_deployments: - reject_server_owned_wif_fields(litellm_params) + reject_server_owned_wif_fields( + MappingProxyType({key: value for key, value in litellm_params.items() if key != "chatgpt_auth_file"}) + ) else: reject_server_owned_wif_params(litellm_params) reject_federated_credential_reference(litellm_params) diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 684f1619512..53c54fe15bf 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -2304,7 +2304,15 @@ async def test_model_connection( ) selected_by_id: Final = deployment_by_id is not None - reject_server_owned_wif_fields(request_litellm_params) + reject_server_owned_wif_fields( + MappingProxyType( + { + key: value + for key, value in _OBJECT_MAPPING.validate_python(request_litellm_params).items() + if key != "chatgpt_auth_file" + } + ) + ) # Merge: config params (from proxy config) as base, request params override litellm_params = { **_config_base_for_health_check( diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 8dc7501bc3b..b51eea7a876 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -1417,13 +1417,14 @@ def clean_headers( Cleaned headers dict """ from litellm.llms.anthropic.common_utils import is_anthropic_oauth_key + from litellm.llms.chatgpt.common_utils import is_chatgpt_oauth_key clean_headers: Final = {} litellm_key_lower: Final = litellm_key_header_name.lower() if litellm_key_header_name is not None else None for header, value in headers.items(): header_lower = header.lower() - if header_lower == "authorization" and is_anthropic_oauth_key(value): + if header_lower == "authorization" and (is_anthropic_oauth_key(value) or is_chatgpt_oauth_key(value)): if authenticated_with_header is None or authenticated_with_header.lower() != "authorization": clean_headers[header] = value continue @@ -1452,7 +1453,7 @@ def clean_headers( def _is_credential_header(header: str) -> bool: """Whether `header` carries a caller credential rather than request context.""" - return header.lower() in _CREDENTIAL_HEADER_NAMES + return header.lower().removeprefix("x-pass-") in _CREDENTIAL_HEADER_NAMES def redact_credential_headers(headers: Mapping[str, str]) -> Mapping[str, str]: @@ -4282,6 +4283,7 @@ _NATIVE_MESSAGES_ANTHROPIC_API_HEADER_PROVIDERS: Final = ",".join( (LlmProviders.AZURE_AI.value, LlmProviders.DATABRICKS.value) ) _ANTHROPIC_OAUTH_CREDENTIAL_PROVIDERS: Final = ",".join(sorted(ANTHROPIC_OAUTH_FORWARD_PROVIDERS)) +_CHATGPT_OAUTH_CREDENTIAL_PROVIDERS: Final = LlmProviders.CHATGPT.value def add_provider_specific_headers_to_request( @@ -4289,6 +4291,7 @@ def add_provider_specific_headers_to_request( headers: Mapping[str, str], ) -> bool: from litellm.llms.anthropic.common_utils import is_anthropic_oauth_key + from litellm.llms.chatgpt.common_utils import select_chatgpt_client_credential_headers anthropic_api_headers: Final = {header: headers[header] for header in ANTHROPIC_API_HEADERS if header in headers} anthropic_oauth_credential_headers: Final = { @@ -4296,6 +4299,8 @@ def add_provider_specific_headers_to_request( for header, value in headers.items() if header.lower() == "authorization" and is_anthropic_oauth_key(value) } + chatgpt_oauth_credential_headers: Final = dict(select_chatgpt_client_credential_headers(headers)) + typesafe_danger_zone_headers: Final = { header: value for header, value in headers.items() @@ -4320,13 +4325,17 @@ def add_provider_specific_headers_to_request( ProviderSpecificHeader( custom_llm_provider=LlmProviders.TYPESAFE.value, extra_headers=typesafe_danger_zone_headers ), + ProviderSpecificHeader( + custom_llm_provider=_CHATGPT_OAUTH_CREDENTIAL_PROVIDERS, + extra_headers=chatgpt_oauth_credential_headers, + ), ) if scoped_header["extra_headers"] ] if scoped_headers: data["provider_specific_header"] = scoped_headers[0] if len(scoped_headers) == 1 else scoped_headers - return bool(anthropic_oauth_credential_headers) + return bool(anthropic_oauth_credential_headers or chatgpt_oauth_credential_headers) def _add_otel_traceparent_to_data(data: dict, request: Request): diff --git a/litellm/proxy/pass_through_endpoints/codex_passthrough_endpoints.py b/litellm/proxy/pass_through_endpoints/codex_passthrough_endpoints.py new file mode 100644 index 00000000000..3087faa9f67 --- /dev/null +++ b/litellm/proxy/pass_through_endpoints/codex_passthrough_endpoints.py @@ -0,0 +1,87 @@ +import hmac +from types import MappingProxyType +from typing import Annotated, Final, Literal + +from fastapi import APIRouter, Depends, HTTPException, Request, Response + +from litellm.llms.chatgpt.common_utils import CHATGPT_API_BASE +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.pass_through_endpoints.pass_through_endpoints import pass_through_request + +router: Final = APIRouter() +_HEADERS: Final = frozenset( + { + "authorization", + "chatgpt-account-id", + "content-type", + "accept", + "user-agent", + "originator", + "openai-beta", + "session-id", + "thread-id", + "session_id", + "conversation_id", + "x-codex-turn-metadata", + "x-codex-turn-state", + } +) + + +@router.get("/backend-api/codex/{endpoint}", tags=["ChatGPT Pass-through", "pass-through"]) +@router.post( + "/backend-api/codex/{endpoint}", + tags=["ChatGPT Pass-through", "pass-through"], + openapi_extra={ + "requestBody": { + "required": True, + "content": {"application/json": {"schema": {"type": "object", "additionalProperties": True}}}, + } + }, + responses={ + 200: { + "description": "Unmodified Codex response or server-sent events", + "content": { + "application/json": {"schema": {"type": "object", "additionalProperties": True}}, + "text/event-stream": {"schema": {"type": "string"}}, + }, + } + }, +) +async def codex_passthrough_route( + endpoint: Literal["models", "responses"], + request: Request, + user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], +) -> Response: + """Forward a client's ChatGPT subscription with separate LiteLLM authentication.""" + expected_method: Final = "GET" if endpoint == "models" else "POST" + if request.method != expected_method: + raise HTTPException(status_code=405, detail="Method not allowed", headers={"Allow": expected_method}) + + authorization: Final = request.headers.get("authorization", "") + scheme, _, token = authorization.partition(" ") + gateway_key: Final = request.headers.get("x-litellm-api-key", "") + gateway_parts: Final = gateway_key.split() + gateway_token: Final = ( + gateway_parts[-1] + if len(gateway_parts) == 1 or (len(gateway_parts) == 2 and gateway_parts[0].lower() == "bearer") + else "" + ) + if scheme.lower() != "bearer" or not token.strip() or not gateway_token: + raise HTTPException( + status_code=401, + detail="Send the ChatGPT bearer in Authorization and the LiteLLM key in x-litellm-api-key", + ) + if hmac.compare_digest(token.strip().encode(), gateway_token.encode()): + raise HTTPException(status_code=401, detail="ChatGPT and LiteLLM credentials must be separate") + + headers: Final = MappingProxyType({name: value for name, value in request.headers.items() if name in _HEADERS}) + return await pass_through_request( + request=request, + target=f"{CHATGPT_API_BASE}/{endpoint}", + custom_headers=dict(headers), + user_api_key_dict=user_api_key_dict, + custom_llm_provider="chatgpt", + upstream_headers_filter=lambda _: headers, + ) diff --git a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/openai_passthrough_logging_handler.py b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/openai_passthrough_logging_handler.py index c763b5dc26e..c410150df70 100644 --- a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/openai_passthrough_logging_handler.py +++ b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/openai_passthrough_logging_handler.py @@ -10,6 +10,7 @@ from typing import Final, Literal, TypeAlias from urllib.parse import urlparse import httpx +from pydantic import TypeAdapter import litellm from litellm._logging import verbose_proxy_logger @@ -95,6 +96,8 @@ def is_openai_compatible_url(url_route: str | None) -> bool: hostname: Final = parsed_url.hostname if not hostname: return False + if hostname == "chatgpt.com" and parsed_url.path.startswith("/backend-api/codex/"): + return True if _hostname_matches(hostname, _OPENAI_HOSTNAMES): return True if _hostname_matches(hostname, _AZURE_OPENAI_HOSTNAMES): @@ -198,7 +201,7 @@ class OpenAIPassthroughLoggingHandler(BasePassthroughLoggingHandler): if not url_route: return False parsed_url: Final = urlparse(url_route) - return _is_openai_compatible_host(parsed_url.hostname) and ( + return is_openai_compatible_url(url_route) and ( "/v1/responses" in parsed_url.path or "/responses" in parsed_url.path ) @@ -424,7 +427,9 @@ class OpenAIPassthroughLoggingHandler(BasePassthroughLoggingHandler): ) = None handler_instance: Final = OpenAIPassthroughLoggingHandler() - custom_llm_provider: Final = kwargs.get("custom_llm_provider", "openai") + custom_llm_provider: Final = TypeAdapter(str).validate_python( + kwargs.get("custom_llm_provider", logging_obj.model_call_details.get("custom_llm_provider", "openai")) + ) if is_chat_completions: # Handle chat completions with existing logic diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index e1e8864678e..fc0e5be3f4c 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -118,6 +118,7 @@ from litellm.proxy.litellm_pre_call_utils import ( # noqa: F401 # legacy modul _key_or_team_allows_client_pricing_override, # pyright: ignore[reportPrivateUsage] # reuse the proxy's pricing trust policy _strip_client_pricing_overrides, # pyright: ignore[reportPrivateUsage] # sanitize before trusted hooks add guardrail costs get_dynamic_logging_metadata, # pyright: ignore[reportPrivateUsage] # shared proxy helper, same import style as _read_request_body above + redact_credential_headers, ) from litellm.proxy.route_llm_request import ProxyModelNotFoundError from litellm.proxy.route_priority import ( @@ -409,6 +410,8 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): @staticmethod def get_endpoint_type(url: str, custom_llm_provider: str | None = None) -> EndpointType: parsed_url: Final = urlparse(url) + if custom_llm_provider == "chatgpt": + return EndpointType.OPENAI if custom_llm_provider in ("typesafe", "laya", "bespoke") and parsed_url.path.removesuffix("/").endswith( "/v1/systemone" ): @@ -723,7 +726,7 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): "url": str(request.url), "method": request.method, "body": copy.copy(_parsed_body), # use copy instead of deepcopy - "headers": request.headers, + "headers": redact_credential_headers(request.headers), }, }, "call_type": "pass_through_endpoint", @@ -1490,7 +1493,7 @@ async def pass_through_request( additional_args={ "complete_input_dict": _parsed_body, "api_base": str(logging_url), - "headers": upstream_headers, + "headers": redact_credential_headers(upstream_headers), }, ) stream = HttpPassThroughEndpointHelpers.update_stream_param_based_on_request_body( @@ -1561,7 +1564,7 @@ async def pass_through_request( data=_parsed_body or {}, user_api_key_dict=user_api_key_dict, response=relay_response, - request_headers=dict(request.headers), + request_headers=dict(redact_credential_headers(request.headers)), ) if callback_headers: _response_headers.update(callback_headers) @@ -1653,7 +1656,7 @@ async def pass_through_request( data=_parsed_body or {}, user_api_key_dict=user_api_key_dict, response=detected_relay_response, - request_headers=dict(request.headers), + request_headers=dict(redact_credential_headers(request.headers)), ) if callback_headers: _response_headers.update(callback_headers) @@ -1703,7 +1706,7 @@ async def pass_through_request( data=_parsed_body or {}, user_api_key_dict=user_api_key_dict, response=response, - request_headers=dict(request.headers), + request_headers=dict(redact_credential_headers(request.headers)), ) if relay_callback_headers: relay_custom_headers.update(relay_callback_headers) @@ -1878,7 +1881,7 @@ async def pass_through_request( data=_parsed_body or {}, user_api_key_dict=user_api_key_dict, response=response, - request_headers=dict(request.headers), + request_headers=dict(redact_credential_headers(request.headers)), ) if callback_headers: custom_headers.update(callback_headers) diff --git a/litellm/proxy/spend_tracking/savings.py b/litellm/proxy/spend_tracking/savings.py index 2aab8deb034..604a7a389e6 100644 --- a/litellm/proxy/spend_tracking/savings.py +++ b/litellm/proxy/spend_tracking/savings.py @@ -19,6 +19,7 @@ from pydantic import ConfigDict, Field import litellm from litellm._logging import verbose_proxy_logger from litellm.constants import INTERNAL_CALL_ORIGIN_METADATA_KEY +from litellm.litellm_core_utils.get_llm_provider_logic import resolve_model_and_provider_for_metadata from litellm.litellm_core_utils.llm_cost_calc.utils import ( calculate_prompt_caching_savings, generic_cost_per_token, @@ -69,7 +70,9 @@ def _resolve_model(model: str | None, custom_llm_provider: str | None) -> _Model if not model: return None try: - resolved_model, provider, _, _ = litellm.get_llm_provider(model=model, custom_llm_provider=custom_llm_provider) + resolved_model, provider = resolve_model_and_provider_for_metadata( + model=model, custom_llm_provider=custom_llm_provider + ) except Exception as e: # noqa: BLE001 # get_llm_provider raises for unroutable names; degrade to an unavailable estimate verbose_proxy_logger.debug( "savings: cannot resolve provider for model=%s custom_llm_provider=%s (%s)", model, custom_llm_provider, e diff --git a/litellm/responses/main.py b/litellm/responses/main.py index 1ff80b0d71d..d993b05f5b7 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -705,10 +705,12 @@ def _api_base_kwarg(kwargs: Mapping[str, object]) -> str | None: return api_base if isinstance(api_base, str) else None -def _dispatched_model_name(model: str, custom_llm_provider: str, api_base: str | None) -> str: - provider_model, _, _, _ = litellm.get_llm_provider( - model=model, custom_llm_provider=custom_llm_provider, api_base=api_base - ) +def _dispatched_model_name( + model: str, custom_llm_provider: str, api_base: str | None, litellm_params: GenericLiteLLMParams | None = None +) -> str: + from litellm.litellm_core_utils.get_llm_provider_logic import resolve_model_and_provider_for_metadata + + provider_model, _ = resolve_model_and_provider_for_metadata(model=model, custom_llm_provider=custom_llm_provider) return _strip_responses_routing_prefix(provider_model) @@ -718,6 +720,7 @@ def _will_bridge_to_chat_completions( use_chat_completions_api: bool, model_info: object, api_base: str | None, + litellm_params: GenericLiteLLMParams | None = None, ) -> bool: """``_bridges_to_chat_completions`` for callers running before the provider config is resolved. @@ -734,7 +737,7 @@ def _will_bridge_to_chat_completions( return True return _bridges_to_chat_completions( _resolve_responses_api_provider_config( - _dispatched_model_name(normalized_model[0], custom_llm_provider, api_base), + _dispatched_model_name(normalized_model[0], custom_llm_provider, api_base, litellm_params), custom_llm_provider, model_info, api_base, @@ -750,12 +753,15 @@ def provider_stores_the_conversation(model: str, custom_llm_provider: str | None first MCP round runs as an internal call that writes no spend log at all. """ api_base: Final = _api_base_kwarg(kwargs) + litellm_params: Final = GenericLiteLLMParams(**kwargs) return not _will_bridge_to_chat_completions( model, - custom_llm_provider or litellm.get_llm_provider(model=model, api_base=api_base)[1], + custom_llm_provider + or litellm.get_llm_provider(model=model, api_base=api_base, litellm_params=litellm_params)[1], bool(kwargs.get("use_chat_completions_api")), kwargs.get("model_info"), api_base, + litellm_params, ) @@ -905,7 +911,8 @@ async def aresponses( model=model, api_base=local_vars.get("base_url", None), litellm_params=GenericLiteLLMParams( - **cast("dict[str, object]", kwargs) # cast-ok: kwargs is the untyped request dict + extra_headers=extra_headers, + **cast("dict[str, object]", kwargs), # cast-ok: kwargs is the untyped request dict ), ) # Update local_vars with detected provider (fixes #19782) @@ -936,6 +943,7 @@ async def aresponses( bool(kwargs.get("use_chat_completions_api")), kwargs.get("model_info"), _api_base_kwarg(kwargs), + GenericLiteLLMParams(**kwargs), ), ): _stamp_injection_points_for_the_hook(kwargs, model, custom_llm_provider, input, tools) @@ -967,7 +975,7 @@ async def aresponses( original_model=original_model, swapped_model=model, custom_llm_provider=custom_llm_provider, - kwargs=kwargs, + kwargs={**kwargs, "extra_headers": extra_headers}, prompt_id=prompt_id, ) kwargs.pop("prompt_id", None) @@ -1051,11 +1059,14 @@ def _resolve_prompt_swapped_provider( kwargs: Mapping[str, object], prompt_id: str | None, ) -> str: - swapped_provider: Final = litellm.get_llm_provider(model=swapped_model)[1] + litellm_params: Final = GenericLiteLLMParams(**kwargs) + swapped_provider: Final = litellm.get_llm_provider(model=swapped_model, litellm_params=litellm_params)[1] if kwargs.get("api_key") is None and kwargs.get("api_base") is None: return swapped_provider try: - original_provider: Final = custom_llm_provider or litellm.get_llm_provider(model=original_model)[1] + original_provider: Final = ( + custom_llm_provider or litellm.get_llm_provider(model=original_model, litellm_params=litellm_params)[1] + ) except litellm.BadRequestError: return swapped_provider if swapped_provider == original_provider: @@ -1108,6 +1119,7 @@ def _apply_prompt_management_to_responses_call( use_chat_completions_api, kwargs.get("model_info"), _api_base_kwarg(kwargs), + GenericLiteLLMParams(**kwargs), ), ): _stamp_injection_points_for_the_hook(kwargs, model, custom_llm_provider, input, tools) @@ -1142,7 +1154,7 @@ def _apply_prompt_management_to_responses_call( original_model=original_model, swapped_model=model, custom_llm_provider=custom_llm_provider, - kwargs=kwargs, + kwargs={**kwargs, "extra_headers": local_vars.get("extra_headers")}, prompt_id=prompt_id, ) ) @@ -1498,7 +1510,8 @@ def responses( model=model, api_base=local_vars.get("base_url", None), litellm_params=GenericLiteLLMParams( - **cast("dict[str, object]", kwargs) # cast-ok: kwargs is the untyped request dict + extra_headers=extra_headers, + **cast("dict[str, object]", kwargs), # cast-ok: kwargs is the untyped request dict ), ) local_vars["custom_llm_provider"] = custom_llm_provider @@ -1514,8 +1527,16 @@ def responses( use_chat_completions_api=use_chat_completions_api, ) - # get llm provider logic - litellm_params: Final = GenericLiteLLMParams(**kwargs) + from litellm.litellm_core_utils.core_helpers import attribute_or_none + + litellm_params: Final = GenericLiteLLMParams(extra_headers=extra_headers, **kwargs) + provider_headers: Final = ResponsesAPIRequestUtils.merge_provider_specific_headers( + extra_headers=extra_headers, + provider_specific_header=attribute_or_none(litellm_params, "provider_specific_header"), + custom_llm_provider=custom_llm_provider, + ) + local_vars["extra_headers"] = provider_headers + affinity_api_base: Final = ( get_openai_credentials(api_base=litellm_params.api_base).api_base if custom_llm_provider == "openai" @@ -1525,7 +1546,7 @@ def responses( try: effective_extra_headers: Final = ( add_provider_affinity_header( - headers=extra_headers or MappingProxyType({}), + headers=provider_headers or MappingProxyType({}), litellm_params=MappingProxyType( { "provider_affinity_header": litellm_params.provider_affinity_header, @@ -1538,7 +1559,7 @@ def responses( default_header=default_affinity_header, ) if litellm_params.provider_affinity_header is not None or default_affinity_header is not None - else extra_headers + else provider_headers ) except ValueError as affinity_error: raise litellm.BadRequestError( diff --git a/litellm/responses/utils.py b/litellm/responses/utils.py index dc75e77d685..2d0873a218a 100644 --- a/litellm/responses/utils.py +++ b/litellm/responses/utils.py @@ -12,6 +12,7 @@ import litellm from litellm._logging import verbose_logger from litellm.litellm_core_utils.core_helpers import as_str_mapping, attribute_or_none, iter_items from litellm.litellm_core_utils.dot_notation_indexing import delete_nested_value, is_nested_path +from litellm.litellm_core_utils.get_provider_specific_headers import ProviderSpecificHeaderUtils from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig, ReasoningSummaryLevel from litellm.types.llms.openai import ( AllMessageValues, @@ -29,11 +30,16 @@ from litellm.types.utils import ( CacheCreationTokenDetails, CompletionTokensDetailsWrapper, PromptTokensDetailsWrapper, + ProviderSpecificHeader, SpecialEnums, Usage, text_tokens_without_nested_reasoning, ) +_PROVIDER_SPECIFIC_HEADER_ADAPTER: Final[ + TypeAdapter[ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None] +] = TypeAdapter(ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None) + class _UsageExtraFields(ExtraItemsTypedDict, total=False, extra_items=object): pass @@ -233,6 +239,20 @@ class ResponsesAPIRequestUtils: **extra_headers, } + @staticmethod + def merge_provider_specific_headers( + extra_headers: Mapping[str, object] | None, + provider_specific_header: object, + custom_llm_provider: str | None, + ) -> dict[str, object] | None: # mutable-ok: legacy Responses request fields require a dict + scoped_headers: Final = ProviderSpecificHeaderUtils.get_provider_specific_headers( + provider_specific_header=_PROVIDER_SPECIFIC_HEADER_ADAPTER.validate_python(provider_specific_header), + custom_llm_provider=custom_llm_provider, + ) + if extra_headers is None and not scoped_headers: + return None + return {**(extra_headers or {}), **scoped_headers} + @staticmethod def _check_valid_arg( supported_params: list[str] | None, diff --git a/litellm/router.py b/litellm/router.py index 3af39736e25..c76e4ec843f 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -98,7 +98,9 @@ from litellm.litellm_core_utils.credential_accessor import CredentialAccessor, r from litellm.litellm_core_utils.dd_tracing import tracer from litellm.litellm_core_utils.get_llm_provider_logic import ( declared_authenticating_provider, + inferred_provider, is_registered_custom_provider, + resolve_model_and_provider_for_metadata, ) from litellm.litellm_core_utils.hidden_params import ( HIDDEN_PARAMS_ATTR as _HIDDEN_PARAMS_ATTR, @@ -561,6 +563,7 @@ _ALIAS_PARAMS_NEVER_FORWARDED: Final = frozenset( {"model", "api_base", "api_key", "api_version", "responses_api_version"} ) _ALIAS_MARKER_FORWARDED_PARAMS_KWARG: Final = "_alias_marker_forwarded_params" +_FORWARDED_CREDENTIAL_ISOLATION_PROVIDERS: Final = frozenset({"chatgpt"}) _CLAUDE_CODE_SESSION_ID_RE: Final = re.compile(r"^[a-zA-Z0-9_\-]{8,}$") _CLAUDE_CODE_SESSION_ROUTER_TTL_SECONDS: Final = 3600 CLAUDE_CODE_SESSION_ROUTER_BINDING_TARGET: Final = "claude_code_session_router_binding" @@ -4801,9 +4804,39 @@ class Router: deployment_litellm_model_name = deployment["litellm_params"]["model"] deployment_api_base = deployment["litellm_params"].get("api_base") deployment_model_name: Final = deployment["model_name"] - if is_clientside_credential(request_kwargs=kwargs): + from litellm.litellm_core_utils.get_provider_specific_headers import ProviderSpecificHeaderUtils + + request_context: Final = cast( # cast-ok: the router's legacy request dict has string keys + Mapping[str, object], kwargs + ) + raw_deployment_params: Final[object] = deployment["litellm_params"] + deployment_context: Final = _LITELLM_PARAMS_ADAPTER.validate_python( + raw_deployment_params.model_dump(exclude_none=True) + if isinstance(raw_deployment_params, LiteLLM_Params) + else raw_deployment_params + ) + declared_provider: Final = deployment_context.get("custom_llm_provider") + declared_model: Final = deployment_context.get("model") + deployment_provider: Final = ( + declared_provider + if isinstance(declared_provider, str) + else inferred_provider(declared_model if isinstance(declared_model, str) else None) + ) + forwarded_authorization: Final = ( + ProviderSpecificHeaderUtils.forwarded_authorization( + request_context.get("provider_specific_header"), deployment_provider + ) + if deployment_provider in _FORWARDED_CREDENTIAL_ISOLATION_PROVIDERS + else None + ) + if is_clientside_credential(request_kwargs=kwargs) or forwarded_authorization is not None: deployment_pydantic_obj: Final = self._handle_clientside_credential( - deployment=deployment, kwargs=kwargs, function_name=function_name + deployment=deployment, + kwargs={ + **kwargs, + **({"api_key": forwarded_authorization} if forwarded_authorization is not None else {}), + }, + function_name=function_name, ) model_info = deployment_pydantic_obj.model_info.model_dump() deployment_litellm_model_name = deployment_pydantic_obj.litellm_params.model @@ -11048,6 +11081,10 @@ class Router: custom_llm_provider = None dynamic_api_key = None api_base = None + elif declared_authenticating_provider(litellm_model, deployment.litellm_params.custom_llm_provider) is not None: + _model, custom_llm_provider = ( # rebind-ok: each branch resolves the deployment provider + self._model_group_llm_provider(deployment.litellm_params) + ) else: # check if model provider in supported providers ( @@ -12401,6 +12438,17 @@ class Router: ) return model_info + @staticmethod + def _model_group_llm_provider(litellm_params: LiteLLM_Params) -> tuple[str, str]: + try: + return resolve_model_and_provider_for_metadata( + model=litellm_params.model, + custom_llm_provider=litellm_params.custom_llm_provider, + ) + except litellm.exceptions.BadRequestError as e: + verbose_router_logger.error("litellm.router.py::get_model_group_info() - %s", e) + return "", "" + def _set_model_group_info( self, model_group: str, user_facing_model_group_name: str, team_id: str | None = None ) -> ModelGroupInfo | None: @@ -12489,15 +12537,7 @@ class Router: deployment_is_mapped = deployment_is_catalog_mapped(model_info, model_info_dict) # get llm provider - litellm_model, llm_provider = "", "" - try: - litellm_model, llm_provider, _, _ = litellm.get_llm_provider( - model=litellm_params.model, - custom_llm_provider=litellm_params.custom_llm_provider, - litellm_params=litellm_params, - ) - except litellm.exceptions.BadRequestError as e: - verbose_router_logger.error("litellm.router.py::get_model_group_info() - %s", e) + litellm_model, llm_provider = self._model_group_llm_provider(litellm_params) if model_info is None: supported_openai_params = litellm.get_supported_openai_params( diff --git a/litellm/types/litellm_params.py b/litellm/types/litellm_params.py index 089f21a33c1..717b74e5741 100644 --- a/litellm/types/litellm_params.py +++ b/litellm/types/litellm_params.py @@ -91,6 +91,7 @@ class ProviderConnection: litellm_credential_name: str | None = None configurable_clientside_auth_params: "Sequence[str | ConfigurableClientsideParamsCustomAuth] | None" = None use_xai_oauth: bool | None = None + chatgpt_auth_file: str | None = None github_copilot_auth_type: str | None = None github_copilot_host: str | None = None github_copilot_user_session: object | None = None diff --git a/litellm/types/router.py b/litellm/types/router.py index cd1389aea9d..ba9b82e2a57 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -572,6 +572,10 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): default=False, description="Use stored xAI OAuth credentials when no xAI API key is configured.", ) + chatgpt_auth_file: str | None = Field( + default=None, + description="Path to a ChatGPT OAuth auth.json for this deployment.", + ) fireworks_forward_user_id: bool | None = Field( default=None, description="Send the LiteLLM user id of the calling key as the `user` field on Fireworks AI chat, responses and messages requests.", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index cc7ef851111..4f39741a079 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4360,6 +4360,7 @@ server_owned_wif_litellm_params: Final = ( + openai_wif_litellm_params + oauth_token_exchange_litellm_params + github_copilot_oauth_litellm_params + + ("chatgpt_auth_file",) ) secret_bearing_wif_litellm_params: Final = tuple(sorted(WIF_SECRET_BEARING_KEYS)) diff --git a/litellm/utils.py b/litellm/utils.py index 4315ae426cf..1cc7c63e6a5 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2964,12 +2964,14 @@ def supports_native_streaming(model: str, custom_llm_provider: str | None) -> bo Raises: Exception: If the given model is not found in model_prices_and_context_window.json. """ + from litellm.litellm_core_utils.get_llm_provider_logic import resolve_model_and_provider_for_metadata + try: - model, custom_llm_provider, _, _ = litellm.get_llm_provider( + resolved_model, resolved_provider = resolve_model_and_provider_for_metadata( model=model, custom_llm_provider=custom_llm_provider ) - model_info: Final = get_model_info_helper(model=model, custom_llm_provider=custom_llm_provider) + model_info: Final = get_model_info_helper(model=resolved_model, custom_llm_provider=resolved_provider) supports_native_streaming = model_info.get("supports_native_streaming", True) if supports_native_streaming is None: supports_native_streaming = True diff --git a/tests/unit/litellm_core_utils/test_get_llm_provider_logic.py b/tests/unit/litellm_core_utils/test_get_llm_provider_logic.py index 57e66805678..e9e3f7b8174 100644 --- a/tests/unit/litellm_core_utils/test_get_llm_provider_logic.py +++ b/tests/unit/litellm_core_utils/test_get_llm_provider_logic.py @@ -1,5 +1,9 @@ +from collections.abc import Iterator +from pathlib import Path from typing import Final +import respx + import asyncio, httpx, importlib, os import pytest @@ -8,6 +12,7 @@ from litellm import CustomLLM from litellm.litellm_core_utils import get_llm_provider_logic from litellm.litellm_core_utils.get_llm_provider_logic import ( get_llm_provider, + resolve_model_and_provider_for_metadata, get_provider_affinity_header, inferred_provider, is_registered_custom_provider, @@ -715,3 +720,47 @@ def test_opencode_affinity_resolution_preserves_existing_transport(prefix: str) model, provider, _, destination = get_llm_provider(model=f"{prefix}/test-model", api_base=api_base) assert (model, provider, destination) == ("test-model", prefix, api_base) assert get_provider_affinity_header(destination) == "x-opencode-session" + + +@pytest.fixture +def chatgpt_login_tripwire(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Iterator[None]: + blocked: Final = tmp_path / "blocked" + blocked.write_text("ordinary file") + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(blocked / "unused")) + monkeypatch.delenv("CHATGPT_AUTH_FILE", raising=False) + with respx.mock as transport: + yield + assert len(transport.calls) == 0 + + +@pytest.mark.parametrize( + ("model", "custom_llm_provider", "expected"), + [ + ("chatgpt/gpt-5.4", None, ("gpt-5.4", "chatgpt")), + ("gpt-5.4", "chatgpt", ("gpt-5.4", "chatgpt")), + ("github_copilot/gpt-4o", None, ("gpt-4o", "github_copilot")), + ("openai/gpt-5.4", None, ("gpt-5.4", "openai")), + ], +) +def test_resolve_model_and_provider_for_metadata_never_logs_in( + chatgpt_login_tripwire: None, model: str, custom_llm_provider: str | None, expected: tuple[str, str] +) -> None: + assert resolve_model_and_provider_for_metadata(model, custom_llm_provider) == expected + + +def test_metadata_callers_never_log_in_for_a_declared_chatgpt_deployment( + chatgpt_login_tripwire: None, monkeypatch: pytest.MonkeyPatch +) -> None: + from litellm.litellm_core_utils.llm_response_utils.get_api_base import get_api_base + from litellm.proxy.spend_tracking.savings import _resolve_model + from litellm.router import Router + from litellm.types.router import LiteLLM_Params + from litellm.utils import supports_native_streaming + + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + + assert supports_native_streaming("chatgpt/gpt-5.4", None) is True + assert get_api_base("chatgpt/gpt-5.4", {"model": "chatgpt/gpt-5.4"}) == "https://chatgpt.com/backend-api/codex" + identity = _resolve_model("chatgpt/gpt-5.4", None) + assert identity is not None and identity.provider == "chatgpt" + assert Router._model_group_llm_provider(LiteLLM_Params(model="chatgpt/gpt-5.4")) == ("gpt-5.4", "chatgpt") diff --git a/tests/unit/llms/chatgpt/chat/test_chatgpt_chat_transformation.py b/tests/unit/llms/chatgpt/chat/test_chatgpt_chat_transformation.py new file mode 100644 index 00000000000..f9d1dc28d12 --- /dev/null +++ b/tests/unit/llms/chatgpt/chat/test_chatgpt_chat_transformation.py @@ -0,0 +1,638 @@ +from unittest.mock import MagicMock + +import pytest + +from litellm.exceptions import AuthenticationError +from litellm.llms.chatgpt.chat.transformation import ChatGPTConfig +from litellm.llms.chatgpt.common_utils import GetAccessTokenError + +GATEWAY_BASE = "https://chatgpt.example/backend-api/codex" + + +def _gateway_authenticator() -> MagicMock: + authenticator = MagicMock() + authenticator.get_api_base.return_value = GATEWAY_BASE + authenticator.get_access_token.return_value = "gateway-access-token" + return authenticator + + +def _provider_info( + config: ChatGPTConfig, api_base: str | None, api_key: str | None +) -> tuple[str | None, str | None, str]: + return config._get_openai_compatible_provider_info( + model="gpt-5.4", api_base=api_base, api_key=api_key, custom_llm_provider="chatgpt" + ) + + +class TestChatGPTChatProviderInfo: + def test_configured_key_uses_configured_base_without_gateway_login(self): + authenticator = _gateway_authenticator() + config = ChatGPTConfig(authenticator=authenticator) + + assert _provider_info(config, "http://127.0.0.1:9999", "configured-key") == ( + "http://127.0.0.1:9999", + "configured-key", + "chatgpt", + ) + authenticator.get_access_token.assert_not_called() + + def test_configured_key_without_base_uses_gateway_base(self): + config = ChatGPTConfig(authenticator=_gateway_authenticator()) + + assert _provider_info(config, None, "configured-key") == (GATEWAY_BASE, "configured-key", "chatgpt") + + def test_gateway_login_is_never_sent_to_a_custom_base(self): + config = ChatGPTConfig(authenticator=_gateway_authenticator()) + + assert _provider_info(config, "http://attacker.example", None) == ( + GATEWAY_BASE, + "gateway-access-token", + "chatgpt", + ) + + def test_missing_gateway_login_raises_authentication_error(self): + authenticator = _gateway_authenticator() + authenticator.get_access_token.side_effect = GetAccessTokenError(status_code=401, message="not logged in") + config = ChatGPTConfig(authenticator=authenticator) + + with pytest.raises(AuthenticationError, match="not logged in"): + _provider_info(config, None, None) + + +import asyncio +import json +from pathlib import Path +from types import MappingProxyType +from typing import Final + +import httpx +import respx + +import litellm +from litellm.integrations.dotprompt.dotprompt_manager import DotpromptManager +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.llms.chatgpt.chat.transformation import ChatGPTConfig +from litellm.types.router import GenericLiteLLMParams + + +def _write_auth_record(path: Path, token: str, account_id: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text( + json.dumps( + { + "access_token": token, + "account_id": account_id, + "expires_at": 4102444800, + } + ) + ) + + +@pytest.fixture +def auth_files(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> tuple[Path, Path]: + monkeypatch.setattr("time.time", lambda: 1700000000) + default_dir: Final = tmp_path / "default" + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(default_dir)) + monkeypatch.delenv("CHATGPT_AUTH_FILE", raising=False) + _write_auth_record(default_dir / "auth.json", "token-default", "acct-default") + file_a: Final = tmp_path / "account-a" / "auth.json" + file_b: Final = tmp_path / "account-b" / "auth.json" + _write_auth_record(file_a, "token-a", "acct-a") + _write_auth_record(file_b, "token-b", "acct-b") + return file_a, file_b + + +class TestChatGPTMultiAccountChat: + def test_validate_environment_uses_per_model_auth_file(self, auth_files: tuple[Path, Path]) -> None: + file_a, file_b = auth_files + config: Final = ChatGPTConfig() + + headers_a: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + messages=[], + optional_params={}, + litellm_params={"chatgpt_auth_file": str(file_a)}, + api_key="token-default", + ) + headers_b: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + messages=[], + optional_params={}, + litellm_params={"chatgpt_auth_file": str(file_b)}, + api_key="token-default", + ) + + assert headers_a["Authorization"] == "Bearer token-a" + assert headers_a["ChatGPT-Account-Id"] == "acct-a" + assert headers_b["Authorization"] == "Bearer token-b" + assert headers_b["ChatGPT-Account-Id"] == "acct-b" + + def test_validate_environment_defaults_to_global_auth_file(self, auth_files: tuple[Path, Path]) -> None: + config: Final = ChatGPTConfig() + + headers: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + messages=[], + optional_params={}, + litellm_params={}, + api_key="token-default", + ) + + assert headers["Authorization"] == "Bearer token-default" + assert headers["ChatGPT-Account-Id"] == "acct-default" + + def test_provider_info_uses_per_model_auth_file(self, auth_files: tuple[Path, Path]) -> None: + file_a, _ = auth_files + config: Final = ChatGPTConfig() + + _, per_model_key, _ = config._get_openai_compatible_provider_info( + "gpt-6.1-sol", None, None, "chatgpt", {"chatgpt_auth_file": str(file_a)} + ) + _, default_key, _ = config._get_openai_compatible_provider_info("gpt-6.1-sol", None, None, "chatgpt") + + assert per_model_key == "token-a" + assert default_key == "token-default" + + def test_get_llm_provider_threads_auth_file(self, auth_files: tuple[Path, Path]) -> None: + file_a, _ = auth_files + + model, provider, dynamic_api_key, _ = get_llm_provider( + model="chatgpt/gpt-6.1-sol", + litellm_params=GenericLiteLLMParams(chatgpt_auth_file=str(file_a)), + ) + + assert model == "gpt-6.1-sol" + assert provider == "chatgpt" + assert dynamic_api_key == "token-a" + + +def _subscription_response(request: httpx.Request) -> httpx.Response: + account: Final = request.headers["ChatGPT-Account-Id"] + assert request.headers["Authorization"] in ( + f"Bearer token-{account.removeprefix('acct-')}", + f"Bearer {_client_token(account)}", + ) + assert "chatgpt_auth_file" not in request.content.decode() + message: Final = { + "id": "msg_test", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": account, "annotations": []}], + } + response: Final = { + "id": "resp_test", + "object": "response", + "created_at": 1700000000, + "status": "completed", + "model": "gpt-6.1-sol", + "output": [message], + "usage": {"input_tokens": 1, "output_tokens": 1, "total_tokens": 2}, + } + events: Final = ( + {"type": "response.created", "response": {**response, "status": "in_progress", "output": []}}, + { + "type": "response.output_text.delta", + "item_id": "msg_test", + "output_index": 0, + "content_index": 0, + "delta": account, + }, + {"type": "response.output_item.done", "output_index": 0, "item": message}, + {"type": "response.completed", "response": response}, + ) + return httpx.Response( + 200, + text="".join(f"data: {json.dumps(event)}\n\n" for event in events), + headers={"content-type": "text/event-stream"}, + ) + + +@pytest.mark.parametrize("blocked_default", [False, True]) +@pytest.mark.parametrize("with_prompt", [False, True]) +@pytest.mark.parametrize("endpoint", ["chat", "responses"]) +@pytest.mark.parametrize("through_router", [False, True]) +@pytest.mark.asyncio +async def test_requests_use_selected_subscription( + auth_files: tuple[Path, Path], + endpoint: str, + through_router: bool, + with_prompt: bool, + blocked_default: bool, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + default_dir: Final = auth_files[0].parent / "missing-default" + if blocked_default: + default_dir.write_text("ordinary file") + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(default_dir / "unused")) + if with_prompt: + monkeypatch.setattr( + litellm, + "callbacks", + [ + DotpromptManager( + prompt_id="account-check", + prompt_data={"content": "Saved account prompt", "metadata": {"model": "chatgpt/gpt-6.1-sol"}}, + ) + ], + ) + router: Final = ( + litellm.Router( + model_list=[ + { + "model_name": f"account-{index}", + "litellm_params": {"model": "chatgpt/gpt-6.1-sol", "chatgpt_auth_file": str(path)}, + } + for index, path in enumerate(auth_files) + ] + ) + if through_router + else None + ) + + async def request(index: int, path: Path) -> str: + params: Final = MappingProxyType( + { + "model": f"account-{index}" if router else "chatgpt/gpt-6.1-sol", + **({"chatgpt_auth_file": str(path)} if router is None else {}), + **({"prompt_id": "account-check"} if with_prompt else {}), + } + ) + if endpoint == "chat": + result: Final = await (router.acompletion if router else litellm.acompletion)( + **params, messages=[{"role": "user", "content": "Hello"}] + ) + return result.choices[0].message.content + response: Final = await (router.aresponses if router else litellm.aresponses)( + **params, input=[{"role": "user", "content": "Hello"}] + ) + return response.output[0].content[0].text + + with respx.mock as transport: + transport.post("https://chatgpt.com/backend-api/codex/responses").mock(side_effect=_subscription_response) + results: Final = await asyncio.gather(*(request(index, path) for index, path in enumerate(auth_files))) + assert results == ["acct-a", "acct-b"] + if with_prompt: + assert all("Saved account prompt" in call.request.content.decode() for call in transport.calls) + + +def _client_token(account: str) -> str: + import base64 + + payload: Final = ( + base64.urlsafe_b64encode(json.dumps({"https://api.openai.com/auth": {"chatgpt_account_id": account}}).encode()) + .rstrip(b"=") + .decode() + ) + return f"header.{payload}.signature" + + +@pytest.mark.parametrize("endpoint", ["chat", "responses"]) +@pytest.mark.parametrize("configured_login", [False, True]) +@pytest.mark.parametrize("with_prompt", [False, True]) +@pytest.mark.parametrize("stream", [False, True]) +@pytest.mark.asyncio +async def test_client_login_keeps_its_account_across_gateway_deployments( + auth_files: tuple[Path, Path], + endpoint: str, + configured_login: bool, + with_prompt: bool, + stream: bool, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(auth_files[0].parent / "no-gateway-login")) + if with_prompt: + monkeypatch.setattr( + litellm, + "callbacks", + [ + DotpromptManager( + prompt_id="client-account-check", + prompt_data={"content": "Saved client prompt", "metadata": {"model": "chatgpt/gpt-6.1-sol"}}, + ) + ], + ) + router: Final = litellm.Router( + model_list=[ + { + "model_name": "subscriptions", + "litellm_params": { + "model": "chatgpt/gpt-6.1-sol", + **({"chatgpt_auth_file": str(path)} if configured_login else {}), + }, + } + for path in auth_files + ], + num_retries=0, + ) + + def reply(request: httpx.Request) -> httpx.Response: + account: Final = request.headers["ChatGPT-Account-Id"] + assert request.headers["Authorization"] == f"Bearer {_client_token(account)}" + return _subscription_response( + httpx.Request( + "POST", + request.url, + content=request.content, + headers={ + **{key: value for key, value in request.headers.items() if key.lower() != "authorization"}, + "Authorization": f"Bearer token-{account.removeprefix('acct-')}", + }, + ) + ) + + async def call(account: str) -> str: + params: Final = MappingProxyType( + { + "model": "subscriptions", + "stream": stream, + **({"prompt_id": "client-account-check"} if with_prompt else {}), + "provider_specific_header": { + "custom_llm_provider": "chatgpt", + "extra_headers": {"Authorization": f"Bearer {_client_token(account)}"}, + }, + } + ) + response: Final = await ( + router.acompletion(**params, messages=[{"role": "user", "content": "Hello"}]) + if endpoint == "chat" + else router.aresponses(**params, input=[{"role": "user", "content": "Hello"}]) + ) + if stream: + chunks: Final = [part async for part in response] + return ( + "".join(part.choices[0].delta.content or "" for part in chunks) + if endpoint == "chat" + else "".join( + getattr(part, "delta", "") + for part in chunks + if getattr(part, "type", "") == "response.output_text.delta" + ) + ) + return response.choices[0].message.content if endpoint == "chat" else response.output[0].content[0].text + + with respx.mock as transport: + transport.post("https://chatgpt.com/backend-api/codex/responses").mock(side_effect=reply) + results: Final = await asyncio.gather(call("acct-client-a"), call("acct-client-b")) + assert results == ["acct-client-a", "acct-client-b"] + assert len(transport.calls) == 2 + + +@pytest.mark.asyncio +async def test_one_client_limit_does_not_cool_down_other_subscriptions( + auth_files: tuple[Path, Path], monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + router: Final = litellm.Router( + model_list=[ + {"model_name": "pool", "litellm_params": {"model": "chatgpt/gpt-6.1-sol", "chatgpt_auth_file": str(path)}} + for path in auth_files + ], + num_retries=0, + allowed_fails=0, + cooldown_time=60, + ) + headers: Final = { + "custom_llm_provider": "chatgpt", + "extra_headers": {"Authorization": f"Bearer {_client_token('acct-limited')}"}, + } + with respx.mock as transport: + route: Final = transport.post("https://chatgpt.com/backend-api/codex/responses") + route.respond( + 429, + headers={"retry-after": "60"}, + json={"error": {"message": "Usage limit reached", "type": "rate_limit_error"}}, + ) + for _ in range(2): + with pytest.raises(litellm.RateLimitError): + await router.aresponses(model="pool", input="Hello", provider_specific_header=headers) + await asyncio.sleep(0) + route.mock(side_effect=_subscription_response) + response: Final = await router.aresponses(model="pool", input="Hello") + assert response.output[0].content[0].text in ("acct-a", "acct-b") + + +@pytest.mark.parametrize("endpoint", ["chat", "responses"]) +@pytest.mark.parametrize("async_call", [False, True]) +@pytest.mark.parametrize("direct_headers", [False, True]) +@pytest.mark.parametrize("configured_key", [False, True]) +@pytest.mark.parametrize( + "target", ["https://example.com/v1", "http://chatgpt.com/backend-api/codex", "https://chatgpt.com.example.com/v1"] +) +@pytest.mark.asyncio +async def test_client_login_cannot_be_redirected_to_another_host( + endpoint: str, target: str, async_call: bool, direct_headers: bool, configured_key: bool +) -> None: + headers: Final = {"Authorization": f"Bearer {_client_token('acct-client')}"} + credential: Final = ( + {"extra_headers": headers} + if direct_headers + else {"provider_specific_header": {"custom_llm_provider": "chatgpt", "extra_headers": headers}} + ) + params: Final = MappingProxyType( + { + "model": "chatgpt/gpt-6.1-sol", + "api_base": target, + **({"api_key": "configured-test-key"} if configured_key else {}), + **credential, + } + ) + + async def request() -> None: + if endpoint == "chat": + if async_call: + await litellm.acompletion(**params, messages=[{"role": "user", "content": "Hello"}]) + else: + litellm.completion(**params, messages=[{"role": "user", "content": "Hello"}]) + elif async_call: + await litellm.aresponses(**params, input="Hello") + else: + litellm.responses(**params, input="Hello") + + with respx.mock as transport: + with pytest.raises(litellm.BadRequestError, match="official HTTPS ChatGPT endpoint"): + await request() + assert len(transport.calls) == 0 + + +@pytest.mark.asyncio +async def test_cross_provider_fallback_does_not_receive_the_chatgpt_login( + auth_files: tuple[Path, Path], monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + router: Final = litellm.Router( + model_list=[ + { + "model_name": "subscription", + "litellm_params": {"model": "chatgpt/gpt-6.1-sol", "chatgpt_auth_file": str(auth_files[0])}, + }, + { + "model_name": "api-fallback", + "litellm_params": {"model": "openai/gpt-6.1-sol", "api_key": "sk-example-fallback"}, + }, + ], + fallbacks=[{"subscription": ["api-fallback"]}], + num_retries=0, + ) + with respx.mock as transport: + transport.post("https://chatgpt.com/backend-api/codex/responses").respond( + 429, json={"error": {"message": "Usage limit reached", "type": "rate_limit_error"}} + ) + fallback: Final = transport.post("https://api.openai.com/v1/responses").respond( + 200, + json={ + "id": "resp_fallback", + "object": "response", + "created_at": 1700000000, + "status": "completed", + "model": "gpt-6.1-sol", + "output": [ + { + "id": "msg_fallback", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "fallback", "annotations": []}], + } + ], + "usage": {"input_tokens": 1, "output_tokens": 1, "total_tokens": 2}, + }, + ) + response: Final = await router.aresponses( + model="subscription", + input="Hello", + provider_specific_header={ + "custom_llm_provider": "chatgpt", + "extra_headers": { + "Authorization": f"Bearer {_client_token('acct-client')}", + "ChatGPT-Account-Id": "acct-client", + }, + }, + ) + assert response.output[0].content[0].text == "fallback" + assert fallback.calls.last.request.headers["Authorization"] == "Bearer sk-example-fallback" + assert "ChatGPT-Account-Id" not in fallback.calls.last.request.headers + + +@pytest.mark.parametrize("endpoint", ["chat", "responses"]) +@pytest.mark.parametrize("async_call", [False, True]) +@pytest.mark.parametrize("with_prompt", [False, True]) +@pytest.mark.asyncio +async def test_sdk_extra_headers_use_client_login_without_gateway_auth( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, endpoint: str, async_call: bool, with_prompt: bool +) -> None: + blocked: Final = tmp_path / "blocked" + blocked.write_text("ordinary file") + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(blocked / "unused")) + monkeypatch.delenv("CHATGPT_AUTH_FILE", raising=False) + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + headers: Final = {"Authorization": f"Bearer {_client_token('acct-client-a')}"} + prompt_params: Final = {"prompt_id": "sdk-client"} if with_prompt else {} + if with_prompt: + monkeypatch.setattr( + litellm, + "callbacks", + [ + DotpromptManager( + prompt_id="sdk-client", + prompt_data={"content": "Hello", "metadata": {"model": "chatgpt/gpt-6-luna"}}, + ) + ], + ) + with respx.mock as transport: + transport.post("https://chatgpt.com/backend-api/codex/responses").mock(side_effect=_subscription_response) + if endpoint == "chat": + params: Final = { + "model": "chatgpt/gpt-6.1-sol", + "messages": [{"role": "user", "content": "Hello"}], + "extra_headers": headers, + **prompt_params, + } + result: Final = await litellm.acompletion(**params) if async_call else litellm.completion(**params) + assert result.choices[0].message.content == "acct-client-a" + else: + response: Final = ( + await litellm.aresponses( + model="chatgpt/gpt-6.1-sol", input="Hello", extra_headers=headers, **prompt_params + ) + if async_call + else litellm.responses( + model="chatgpt/gpt-6.1-sol", input="Hello", extra_headers=headers, **prompt_params + ) + ) + assert response.output[0].content[0].text == "acct-client-a" + assert transport.calls.last.request.headers["Authorization"] == headers["Authorization"] + assert len(transport.calls) == 1 + + +@pytest.mark.parametrize("endpoint", ["chat", "responses"]) +@pytest.mark.parametrize("sync", [False, True]) +@pytest.mark.parametrize("credential_source", ["default", "file", "api_key"]) +@pytest.mark.parametrize("authorization_header", [None, "Authorization", "authorization"]) +@pytest.mark.asyncio +async def test_requests_preserve_gateway_token_and_selected_workspace( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + endpoint: str, + sync: bool, + credential_source: str, + authorization_header: str | None, +) -> None: + monkeypatch.setattr("time.time", lambda: 1700000000) + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + monkeypatch.setenv("CHATGPT_AUTH_FILE", str(tmp_path / "default.json")) + token: Final = _client_token("acct-token-default") + auth_file: Final = tmp_path / ("selected.json" if credential_source == "file" else "default.json") + _write_auth_record(auth_file, token, "acct-saved-workspace") + configured_token: Final = _client_token("acct-configured") + expected_token: Final = configured_token if credential_source == "api_key" else token + expected_account: Final = "acct-configured" if credential_source == "api_key" else "acct-saved-workspace" + + def reply(request: httpx.Request) -> httpx.Response: + assert request.headers.get_list("authorization") == [f"Bearer {expected_token}"] + assert request.headers.get_list("chatgpt-account-id") == [expected_account] + assert request.headers["originator"] == "codex_cli_rs" + return _subscription_response( + httpx.Request( + "POST", + request.url, + content=request.content, + headers={ + "Authorization": f"Bearer token-{expected_account.removeprefix('acct-')}", + "ChatGPT-Account-Id": expected_account, + }, + ) + ) + + params: Final = MappingProxyType( + { + "model": "chatgpt/gpt-6.1-sol", + **({"chatgpt_auth_file": str(auth_file)} if credential_source == "file" else {}), + **({"api_key": configured_token} if credential_source == "api_key" else {}), + "extra_headers": { + "originator": "codex_cli_rs", + **({authorization_header: "Bearer sk-unrelated-example"} if authorization_header else {}), + }, + } + ) + with respx.mock as transport: + transport.post("https://chatgpt.com/backend-api/codex/responses").mock(side_effect=reply) + if endpoint == "chat": + result: Final = ( + await asyncio.to_thread(litellm.completion, **params, messages=[{"role": "user", "content": "Hello"}]) + if sync + else await litellm.acompletion(**params, messages=[{"role": "user", "content": "Hello"}]) + ) + assert result.choices[0].message.content == expected_account + else: + response: Final = ( + await asyncio.to_thread(litellm.responses, **params, input=[{"role": "user", "content": "Hello"}]) + if sync + else await litellm.aresponses(**params, input=[{"role": "user", "content": "Hello"}]) + ) + assert response.output[0].content[0].text == expected_account diff --git a/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py b/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py index e47d7fc702c..d88bdfc0b41 100644 --- a/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py +++ b/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py @@ -4,8 +4,10 @@ Tests for ChatGPT subscription Responses API transformation Source: litellm/llms/chatgpt/responses/transformation.py """ +import base64 import json from collections.abc import Generator +from pathlib import Path from typing import Final from unittest.mock import MagicMock, patch @@ -13,6 +15,7 @@ import httpx import pytest import litellm +from litellm.llms.chatgpt.authenticator import Authenticator from litellm.llms.chatgpt.responses.transformation import ChatGPTResponsesAPIConfig from litellm.llms.openai.common_utils import OpenAIError from litellm.main import responses_api_bridge_check @@ -395,3 +398,165 @@ class TestChatGPTResponsesAPITransformation: assert exc_info.value.message == "ChatGPT upstream failed" assert exc_info.value.status_code == 502 + + +def _fake_chatgpt_oauth_token(account_id: str) -> str: + header = base64.urlsafe_b64encode(b'{"alg":"RS256"}').rstrip(b"=").decode() + payload = ( + base64.urlsafe_b64encode( + json.dumps({"https://api.openai.com/auth": {"chatgpt_account_id": account_id}}).encode() + ) + .rstrip(b"=") + .decode() + ) + return f"{header}.{payload}.fake-signature" + + +def _gateway_authenticator() -> MagicMock: + authenticator = MagicMock() + authenticator.get_access_token.return_value = "gateway-access-token" + authenticator.get_account_id.return_value = "acct-gateway" + return authenticator + + +def _config_with(authenticator: MagicMock) -> ChatGPTResponsesAPIConfig: + return ChatGPTResponsesAPIConfig(authenticator=authenticator) + + +class TestChatGPTClientCredentialForwarding: + def test_client_chatgpt_oauth_wins_over_configured_key_and_gateway_login(self): + authenticator = _gateway_authenticator() + token = _fake_chatgpt_oauth_token("acct-alice") + + headers = _config_with(authenticator).validate_environment( + headers={"Authorization": f"Bearer {token}", "originator": "codex_cli_rs"}, + model="gpt-5.4", + litellm_params=GenericLiteLLMParams(api_key="configured-gateway-key", litellm_session_id="session-1"), + ) + + assert headers["Authorization"] == f"Bearer {token}" + assert headers["ChatGPT-Account-Id"] == "acct-alice" + assert headers["originator"] == "codex_cli_rs" + assert headers["accept"] == "text/event-stream" + assert [name for name in headers if name.lower() == "authorization"] == ["Authorization"] + authenticator.get_access_token.assert_not_called() + + def test_explicit_account_header_wins_over_jwt_claim(self): + token = _fake_chatgpt_oauth_token("acct-from-jwt") + + headers = _config_with(_gateway_authenticator()).validate_environment( + headers={"authorization": f"Bearer {token}", "chatgpt-account-id": "acct-from-header"}, + model="gpt-5.4", + litellm_params=GenericLiteLLMParams(), + ) + + assert headers["ChatGPT-Account-Id"] == "acct-from-header" + assert [name for name in headers if name.lower() == "chatgpt-account-id"] == ["ChatGPT-Account-Id"] + + @pytest.mark.parametrize( + "bearer", ["Bearer sk-proj-fake-platform-key", "Bearer not-a-jwt", "Bearer a.bm90LWpzb24.c"] + ) + def test_non_chatgpt_bearer_falls_back_to_configured_key(self, bearer, tmp_path): + authenticator = Authenticator(auth_file=str(tmp_path / "missing-default.json")) + + headers = ChatGPTResponsesAPIConfig(authenticator=authenticator).validate_environment( + headers={"Authorization": bearer}, + model="gpt-5.4", + litellm_params=GenericLiteLLMParams(api_key="configured-gateway-key"), + ) + + assert "ChatGPT-Account-Id" not in headers + assert headers["Authorization"] == "Bearer configured-gateway-key" + + def test_configured_key_is_used_without_client_credential(self): + authenticator = _gateway_authenticator() + + headers = _config_with(authenticator).validate_environment( + headers={}, + model="gpt-5.4", + litellm_params=GenericLiteLLMParams(api_key="configured-gateway-key"), + ) + + assert headers["Authorization"] == "Bearer configured-gateway-key" + authenticator.get_access_token.assert_not_called() + + def test_gateway_login_is_used_without_client_credential_or_configured_key(self): + headers = _config_with(_gateway_authenticator()).validate_environment( + headers={}, + model="gpt-5.4", + litellm_params=GenericLiteLLMParams(), + ) + + assert headers["Authorization"] == "Bearer gateway-access-token" + assert headers["ChatGPT-Account-Id"] == "acct-gateway" + + def test_two_clients_never_share_a_credential(self): + config = _config_with(_gateway_authenticator()) + alice = _fake_chatgpt_oauth_token("acct-alice") + bob = _fake_chatgpt_oauth_token("acct-bob") + + forwarded = [ + config.validate_environment( + headers={"Authorization": f"Bearer {token}"}, model="gpt-5.4", litellm_params=GenericLiteLLMParams() + ) + for token in (alice, bob, alice) + ] + + assert [h["Authorization"] for h in forwarded] == [f"Bearer {alice}", f"Bearer {bob}", f"Bearer {alice}"] + assert [h["ChatGPT-Account-Id"] for h in forwarded] == ["acct-alice", "acct-bob", "acct-alice"] + + +class TestChatGPTResponsesMultiAccount: + @pytest.fixture(autouse=True) + def fixed_clock(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr("time.time", lambda: 1700000000) + + @staticmethod + def _write_auth_record(path: Path, token: str, account_id: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text( + json.dumps( + { + "access_token": token, + "account_id": account_id, + "expires_at": 4102444800, + } + ) + ) + + def test_validate_environment_uses_per_model_auth_file( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + default_dir: Final = tmp_path / "default" + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(default_dir)) + monkeypatch.delenv("CHATGPT_AUTH_FILE", raising=False) + self._write_auth_record(default_dir / "auth.json", "token-default", "acct-default") + file_a: Final = tmp_path / "account-a" / "auth.json" + file_b: Final = tmp_path / "account-b" / "auth.json" + self._write_auth_record(file_a, "token-a", "acct-a") + self._write_auth_record(file_b, "token-b", "acct-b") + + config: Final = ChatGPTResponsesAPIConfig() + + headers_a: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + litellm_params=GenericLiteLLMParams(chatgpt_auth_file=str(file_a)), + ) + headers_b: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + litellm_params=GenericLiteLLMParams(chatgpt_auth_file=str(file_b)), + ) + headers_default: Final = config.validate_environment( + headers={}, + model="gpt-6.1-sol", + litellm_params=GenericLiteLLMParams(), + ) + + assert headers_a["Authorization"] == "Bearer token-a" + assert headers_a["ChatGPT-Account-Id"] == "acct-a" + assert headers_b["Authorization"] == "Bearer token-b" + assert headers_b["ChatGPT-Account-Id"] == "acct-b" + assert headers_default["Authorization"] == "Bearer token-default" + assert headers_default["ChatGPT-Account-Id"] == "acct-default" diff --git a/tests/unit/llms/chatgpt/test_chatgpt_authenticator.py b/tests/unit/llms/chatgpt/test_chatgpt_authenticator.py index 43a4c26b6d2..2e9e3462bee 100644 --- a/tests/unit/llms/chatgpt/test_chatgpt_authenticator.py +++ b/tests/unit/llms/chatgpt/test_chatgpt_authenticator.py @@ -2,17 +2,24 @@ import base64 import json import time from concurrent.futures import ThreadPoolExecutor +from pathlib import Path +from typing import Final from unittest.mock import MagicMock, mock_open, patch import pytest +import httpx +import respx import litellm from litellm.constants import DEFAULT_REQUEST_TIMEOUT_SECONDS, HTTP_HANDLER_CONNECT_TIMEOUT_SECONDS from litellm.llms.chatgpt.authenticator import ( TOKEN_REFRESH_TIMEOUT_SECONDS, Authenticator, + get_cached_authenticator, + get_chatgpt_auth_file, ) from litellm.llms.chatgpt.common_utils import GetAccessTokenError +from litellm.types.router import GenericLiteLLMParams def _make_jwt(payload: dict) -> str: @@ -161,3 +168,144 @@ class TestChatGPTAuthenticator: assert account_id == "acct-123" mock_write.assert_called_once() assert mock_write.call_args[0][0]["account_id"] == "acct-123" + + def test_get_account_id_prefers_stored_value_without_rewriting_file(self, authenticator): + id_token = _make_jwt({"https://api.openai.com/auth": {"chatgpt_account_id": "acct-from-token"}}) + auth_data = json.dumps({"account_id": "acct-stored", "id_token": id_token}) + + with ( + patch("builtins.open", mock_open(read_data=auth_data)), + patch.object(authenticator, "_write_auth_file") as mock_write, + ): + assert authenticator.get_account_id() == "acct-stored" + mock_write.assert_not_called() + + def test_get_account_id_without_login_returns_none(self, authenticator): + with ( + patch("builtins.open", mock_open(read_data="{}")), + patch.object(authenticator, "_write_auth_file") as mock_write, + ): + assert authenticator.get_account_id() is None + mock_write.assert_not_called() + + +def _write_auth_record(path: Path, token: str, account_id: str) -> None: + path.write_text( + json.dumps( + { + "access_token": token, + "account_id": account_id, + "expires_at": 4102444800, + } + ) + ) + + +class TestChatGPTMultiAccountAuthenticator: + @pytest.fixture(autouse=True) + def fixed_clock(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr("time.time", lambda: 1700000000) + + def test_explicit_auth_file_overrides_env(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(tmp_path / "env-dir")) + monkeypatch.setenv("CHATGPT_AUTH_FILE", "env.json") + custom_file: Final = tmp_path / "account-a" / "auth.json" + + authenticator: Final = Authenticator(auth_file=str(custom_file)) + + assert authenticator.auth_file == str(custom_file) + assert authenticator.token_dir == str(custom_file.parent) + assert not custom_file.parent.exists() + authenticator._write_auth_file({"access_token": "token-a"}) + assert json.loads(custom_file.read_text())["access_token"] == "token-a" + + def test_default_auth_file_from_env(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(tmp_path / "env-dir")) + monkeypatch.setenv("CHATGPT_AUTH_FILE", "env.json") + + authenticator: Final = Authenticator() + + assert authenticator.auth_file == str(tmp_path / "env-dir" / "env.json") + + def test_bare_filename_auth_file_resolves_against_cwd( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + monkeypatch.chdir(tmp_path) + + authenticator: Final = Authenticator(auth_file="auth.json") + + assert authenticator.auth_file == str(tmp_path / "auth.json") + assert authenticator.token_dir == str(tmp_path) + + def test_two_auth_files_are_isolated(self, tmp_path: Path) -> None: + file_a: Final = tmp_path / "account-a.json" + file_b: Final = tmp_path / "account-b.json" + _write_auth_record(file_a, "token-a", "acct-a") + _write_auth_record(file_b, "token-b", "acct-b") + + authenticator_a: Final = Authenticator(auth_file=str(file_a)) + authenticator_b: Final = Authenticator(auth_file=str(file_b)) + + assert authenticator_a.get_access_token() == "token-a" + assert authenticator_a.get_account_id() == "acct-a" + assert authenticator_b.get_access_token() == "token-b" + assert authenticator_b.get_account_id() == "acct-b" + + def test_get_cached_authenticator_reuses_instance_per_path(self, tmp_path: Path) -> None: + file_a: Final = tmp_path / "account-a.json" + file_b: Final = tmp_path / "account-b.json" + + authenticator_a: Final = get_cached_authenticator(str(file_a)) + + assert get_cached_authenticator(str(file_a)) is authenticator_a + assert get_cached_authenticator(str(file_b)) is not authenticator_a + + def test_get_chatgpt_auth_file(self) -> None: + assert get_chatgpt_auth_file(None) is None + assert get_chatgpt_auth_file({}) is None + assert get_chatgpt_auth_file({"chatgpt_auth_file": ""}) is None + assert get_chatgpt_auth_file({"chatgpt_auth_file": "/a/auth.json"}) == "/a/auth.json" + assert get_chatgpt_auth_file(GenericLiteLLMParams()) is None + assert get_chatgpt_auth_file(GenericLiteLLMParams(chatgpt_auth_file="/b/auth.json")) == "/b/auth.json" + + def test_relative_and_absolute_auth_file_share_cached_authenticator( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + monkeypatch.chdir(tmp_path) + get_cached_authenticator.cache_clear() + auth_file: Final = tmp_path / "x" / "auth.json" + auth_file.parent.mkdir(parents=True) + auth_file.write_text("{}") + + relative_key: Final = get_chatgpt_auth_file({"chatgpt_auth_file": "x/auth.json"}) + absolute_key: Final = get_chatgpt_auth_file({"chatgpt_auth_file": str(auth_file)}) + assert relative_key is not None and absolute_key is not None + + assert get_cached_authenticator(relative_key) is get_cached_authenticator(absolute_key) + + +def test_refresh_keeps_saved_workspace(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr("time.time", lambda: 1700000000) + auth_file: Final = tmp_path / "selected.json" + auth_file.write_text( + json.dumps( + { + "access_token": "expired-example-token", + "refresh_token": "example-refresh-token", + "expires_at": 1, + "account_id": "acct-selected-workspace", + } + ) + ) + token: Final = _make_jwt({"exp": 4102444800, "https://api.openai.com/auth": {"chatgpt_account_id": "acct-default"}}) + authenticator: Final = Authenticator(auth_file=str(auth_file)) + with respx.mock as transport: + transport.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 200, + json={"access_token": token, "id_token": token, "refresh_token": "example-rotated-refresh"}, + ) + ) + assert authenticator.get_access_token() == token + assert authenticator.get_account_id(token) == "acct-selected-workspace" + assert json.loads(auth_file.read_text())["refresh_token"] == "example-rotated-refresh" diff --git a/tests/unit/llms/chatgpt/test_chatgpt_common_utils.py b/tests/unit/llms/chatgpt/test_chatgpt_common_utils.py new file mode 100644 index 00000000000..6b280d97089 --- /dev/null +++ b/tests/unit/llms/chatgpt/test_chatgpt_common_utils.py @@ -0,0 +1,90 @@ +import base64 +import json + +import pytest + +from litellm.llms.chatgpt.common_utils import ( + extract_chatgpt_account_id, + get_chatgpt_client_credential, + is_chatgpt_oauth_key, + select_chatgpt_client_credential_headers, +) + + +def _make_jwt(payload: dict) -> str: + def _b64(obj: dict) -> str: + raw = json.dumps(obj, separators=(",", ":")).encode("utf-8") + return base64.urlsafe_b64encode(raw).decode("utf-8").rstrip("=") + + return f"{_b64({'alg': 'none', 'typ': 'JWT'})}.{_b64(payload)}." + + +CHATGPT_JWT = _make_jwt({"https://api.openai.com/auth": {"chatgpt_account_id": "acct-alice"}}) +CHATGPT_JWT_WITHOUT_ACCOUNT = _make_jwt({"https://api.openai.com/auth": {"user_id": "user-1"}}) +NON_CHATGPT_JWT = _make_jwt({"sub": "someone", "https://api.openai.com/auth": "not-an-object"}) + + +class TestExtractChatGPTAccountId: + def test_reads_account_id_from_auth_claim(self): + assert extract_chatgpt_account_id(CHATGPT_JWT) == "acct-alice" + + @pytest.mark.parametrize( + "token", + [None, "", "sk-proj-not-a-jwt", "header.only", NON_CHATGPT_JWT, CHATGPT_JWT_WITHOUT_ACCOUNT], + ) + def test_returns_none_without_a_usable_account_claim(self, token): + assert extract_chatgpt_account_id(token) is None + + +class TestIsChatGPTOAuthKey: + def test_recognizes_bearer_and_raw_chatgpt_jwt(self): + assert is_chatgpt_oauth_key(CHATGPT_JWT) + assert is_chatgpt_oauth_key(f"Bearer {CHATGPT_JWT}") + assert is_chatgpt_oauth_key(f"Bearer {CHATGPT_JWT_WITHOUT_ACCOUNT}") + + @pytest.mark.parametrize( + "value", [None, "", "Bearer sk-proj-platform-key", "Bearer not.a.jwt", _make_jwt({"sub": "x"})] + ) + def test_rejects_non_chatgpt_credentials(self, value): + assert not is_chatgpt_oauth_key(value) + + +class TestSelectChatGPTClientCredentialHeaders: + def test_account_header_alone_is_not_forwarded(self): + selected = select_chatgpt_client_credential_headers( + {"chatgpt-account-id": "acct-alice", "authorization": "Bearer sk-proj-platform-key"} + ) + assert dict(selected) == {} + assert get_chatgpt_client_credential({"chatgpt-account-id": "acct-alice"}) is None + + def test_forwards_only_bearer_and_account_context(self): + selected = select_chatgpt_client_credential_headers( + { + "Authorization": f"Bearer {CHATGPT_JWT}", + "ChatGPT-Account-Id": "acct-explicit", + "x-litellm-api-key": "sk-gateway", + "user-agent": "codex", + } + ) + assert dict(selected) == {"Authorization": f"Bearer {CHATGPT_JWT}", "ChatGPT-Account-Id": "acct-explicit"} + credential = get_chatgpt_client_credential(selected) + assert credential is not None + assert credential.access_token == CHATGPT_JWT + assert credential.account_id == "acct-explicit" + + def test_account_id_falls_back_to_jwt_claim(self): + credential = get_chatgpt_client_credential({"authorization": f"Bearer {CHATGPT_JWT}"}) + assert credential is not None + assert credential.account_id == "acct-alice" + + +@pytest.mark.parametrize("scheme", ["Bearer", "bearer", "BEARER"]) +def test_bearer_scheme_is_case_insensitive(scheme): + credential = get_chatgpt_client_credential({"authorization": f"{scheme} {CHATGPT_JWT}"}) + assert credential is not None + assert credential.access_token == CHATGPT_JWT + assert credential.account_id == "acct-alice" + + +def test_other_authentication_schemes_are_not_chatgpt_logins(): + assert not is_chatgpt_oauth_key(f"Basic {CHATGPT_JWT}") diff --git a/tests/unit/llms/custom_httpx/test_llm_http_handler.py b/tests/unit/llms/custom_httpx/test_llm_http_handler.py index bbcd7f70e63..f7850aaf9a1 100644 --- a/tests/unit/llms/custom_httpx/test_llm_http_handler.py +++ b/tests/unit/llms/custom_httpx/test_llm_http_handler.py @@ -5654,3 +5654,44 @@ def test_vector_store_retrieve_handler_sends_id_before_azure_api_version_query() sent: Final = received[0] assert str(sent.url) == "https://x.openai.azure.com/openai/vector_stores/vs_abc?api-version=2025-04-01-preview" assert len(received) == 1 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("is_async", (False, True)) +@pytest.mark.parametrize("provider", ("openai", "azure")) +async def test_non_chatgpt_responses_preserves_caller_headers_after_validation( + is_async: bool, provider: str, respx_mock: respx.MockRouter, +) -> None: + route: Final = respx_mock.post(url__regex=r"https://responses\.example\.com/.*").respond( + 200, + json={ + "id": "resp_headers", "object": "response", "created_at": 1, + "model": "gpt-6-luna", "status": "completed", "output": [], + "parallel_tool_calls": False, "tool_choice": "auto", "tools": [], + }, + ) + client: Final = AsyncHTTPHandler(transport=httpx.AsyncHTTPTransport()) if is_async else HTTPHandler(client=httpx.Client()) + kwargs: Final = { + "model": f"{provider}/gpt-6-luna", + "input": "hello", + "api_key": "fake-configured-key", + "api_base": "https://responses.example.com/v1", + "api_version": "2025-04-01-preview", + "extra_headers": {"Authorization": "Bearer fake-caller-key", "content-type": "application/vnd.example+json"}, + "client": client, + } + try: + response: Final = await litellm.aresponses(**kwargs) if is_async else litellm.responses(**kwargs) + assert response.status == "completed" + assert route.calls[0].request.headers["authorization"] == "Bearer fake-caller-key" + expected_content_types: Final = ( + ["application/json", "application/vnd.example+json"] + if provider == "openai" + else ["application/vnd.example+json"] + ) + assert route.calls[0].request.headers.get_list("content-type") == expected_content_types + finally: + if isinstance(client, AsyncHTTPHandler): + await client.close() + else: + client.client.close() diff --git a/tests/unit/proxy/auth/test_auth_utils.py b/tests/unit/proxy/auth/test_auth_utils.py index ee1ae43fd3a..10a0f974a42 100644 --- a/tests/unit/proxy/auth/test_auth_utils.py +++ b/tests/unit/proxy/auth/test_auth_utils.py @@ -3626,6 +3626,18 @@ class TestIsRequestBodySafeBlocksEndpointTargetingFields: ) +@pytest.mark.parametrize("allow_client_side_credentials", [False, True]) +@pytest.mark.parametrize("nested", [False, True]) +def test_request_cannot_select_chatgpt_auth_file(allow_client_side_credentials: bool, nested: bool) -> None: + selector: Final = {"chatgpt_auth_file": "/tokens/account-b/auth.json"} + with pytest.raises(ValueError, match="chatgpt_auth_file"): + is_request_body_safe( + request_body={"model": "chatgpt-account-a", **({"extra_body": selector} if nested else selector)}, + general_settings={"allow_client_side_credentials": allow_client_side_credentials}, + llm_router=None, + model="chatgpt-account-a", + ) + class TestIsRequestBodySafeBlocksBedrockProjectOverride: """``aws_bedrock_project_id`` pins a deployment to a Bedrock project so that project's data-retention policy applies to its requests. A @@ -5304,3 +5316,27 @@ async def test_a_non_numeric_max_request_size_mb_is_ignored_without_premium(): patch("litellm.proxy.proxy_server.premium_user", False), ): assert await check_if_request_size_is_safe(request=_request_with_content_length(1024)) is True + + +@pytest.mark.parametrize("allow_client_side_credentials", [False, True]) +@pytest.mark.parametrize("route", ["/model/new", "/model/update", "/model/account-a/update", "/health/test_connection"]) +def test_chatgpt_model_configuration_reaches_role_authorization( + allow_client_side_credentials: bool, route: str +) -> None: + body: Final = {"litellm_params": {"model": "chatgpt/gpt-6-luna", "chatgpt_auth_file": "/tokens/account-a/auth.json"}} + assert is_request_body_safe( + request_body=body, + general_settings={"allow_client_side_credentials": allow_client_side_credentials}, + llm_router=None, + model="", + route=route, + ) + for inference_route in ("/v1/chat/completions", "/v1/responses"): + with pytest.raises(ValueError, match="chatgpt_auth_file"): + is_request_body_safe( + request_body=body, + general_settings={"allow_client_side_credentials": allow_client_side_credentials}, + llm_router=None, + model="", + route=inference_route, + ) diff --git a/tests/unit/proxy/health_endpoints/test_health_endpoints.py b/tests/unit/proxy/health_endpoints/test_health_endpoints.py index 950d1742d49..67b280eb379 100644 --- a/tests/unit/proxy/health_endpoints/test_health_endpoints.py +++ b/tests/unit/proxy/health_endpoints/test_health_endpoints.py @@ -5437,3 +5437,64 @@ async def test_health_services_endpoint_langfuse_missing_keys_errors(monkeypatch with pytest.raises(ProxyException, match="auth_check failed") as raised: await health_services_endpoint(service="langfuse") assert "connection refused by lf.internal.example" in str(raised.value.message) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("admin", [False, True]) +async def test_unsaved_chatgpt_account_probe_requires_proxy_admin( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, admin: bool +) -> None: + from starlette.requests import Request + from litellm.proxy import proxy_server + + monkeypatch.setenv("CHATGPT_AUTH_FILE", str(tmp_path / "missing-default-auth.json")) + auth_file: Final = tmp_path / "auth.json" + auth_file.write_text( + json.dumps({"access_token": "token-probe", "account_id": "acct-probe", "expires_at": 4102444800}) + ) + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + monkeypatch.setattr(proxy_server, "prisma_client", MagicMock()) + monkeypatch.setattr(proxy_server, "llm_router", None) + monkeypatch.setattr(proxy_server, "general_settings", {}) + output: Final = { + "id": "msg_probe", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "ok", "annotations": []}], + } + response: Final = { + "id": "resp_probe", + "object": "response", + "created_at": 1700000000, + "status": "completed", + "model": "gpt-6.1-sol", + "output": [output], + "usage": {"input_tokens": 1, "output_tokens": 1, "total_tokens": 2}, + } + with respx.mock(assert_all_called=False) as upstream: + upstream.post("https://chatgpt.com/backend-api/codex/responses").respond( + 200, + text="data: " + json.dumps({"type": "response.completed", "response": response}) + "\n\n", + headers={"content-type": "text/event-stream"}, + ) + call: Final = health_test_model_connection( + request=Request({"type": "http", "headers": []}), + mode="responses", + litellm_params={"model": "chatgpt/gpt-6.1-sol", "chatgpt_auth_file": str(auth_file)}, + model_info={}, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN if admin else LitellmUserRoles.INTERNAL_USER, token="test-key" + ), + ) + if admin: + result: Final = await call + assert result["status"] == "success" + assert upstream.calls.last.request.headers["Authorization"] == "Bearer token-probe" + assert upstream.calls.last.request.headers["ChatGPT-Account-Id"] == "acct-probe" + assert isinstance(json.loads(upstream.calls.last.request.content)["input"], list) + else: + with pytest.raises(ProxyException) as error: + await call + assert error.value.code == "403" + assert len(upstream.calls) == 0 diff --git a/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py b/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py index 303b8e060ed..a718426eea6 100644 --- a/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py @@ -8933,7 +8933,14 @@ class TestNonAdminCannotPersistWifFieldsOnModel: mock_prisma.db.litellm_proxymodeltable.update.assert_not_called() @pytest.mark.asyncio - async def test_patch_model_non_admin_cannot_set_wif_field(self): + @pytest.mark.parametrize( + "field,value", + [ + ("anthropic_keycloak_token_url", "https://identity.example.com/token"), + ("chatgpt_auth_file", "/tokens/account-b/auth.json"), + ], + ) + async def test_patch_model_non_admin_cannot_set_wif_field(self, field: str, value: str) -> None: from litellm.proxy.management_endpoints.model_management_endpoints import ( patch_model, ) @@ -8979,13 +8986,13 @@ class TestNonAdminCannotPersistWifFieldsOnModel: model_id="m1", patch_data=updateDeployment( litellm_params=updateLiteLLMParams( - anthropic_keycloak_token_url="https://attacker.example/token", + **{field: value}, ) ), user_api_key_dict=non_admin, ) err = exc_info.value - assert getattr(err, "param", "") == "anthropic_keycloak_token_url" + assert getattr(err, "param", "") == field mock_prisma.db.litellm_proxymodeltable.update.assert_not_called() @pytest.mark.asyncio @@ -9044,7 +9051,14 @@ class TestNonAdminCannotPersistWifFieldsOnModel: mock_prisma.db.litellm_proxymodeltable.update.assert_not_called() @pytest.mark.asyncio - async def test_patch_model_admin_can_set_wif_field(self): + @pytest.mark.parametrize( + "field,value", + [ + ("anthropic_keycloak_token_url", "https://identity.example.com/token"), + ("chatgpt_auth_file", "/tokens/account-b/auth.json"), + ], + ) + async def test_patch_model_admin_can_set_wif_field(self, field: str, value: str) -> None: from litellm.proxy.management_endpoints.model_management_endpoints import ( patch_model, ) @@ -9095,7 +9109,7 @@ class TestNonAdminCannotPersistWifFieldsOnModel: model_id="m1", patch_data=updateDeployment( litellm_params=updateLiteLLMParams( - anthropic_keycloak_token_url="https://keycloak.internal/token", + **{field: value}, ) ), user_api_key_dict=admin, diff --git a/tests/unit/proxy/pass_through_endpoints/test_codex_passthrough_endpoints.py b/tests/unit/proxy/pass_through_endpoints/test_codex_passthrough_endpoints.py new file mode 100644 index 00000000000..354c65776dc --- /dev/null +++ b/tests/unit/proxy/pass_through_endpoints/test_codex_passthrough_endpoints.py @@ -0,0 +1,185 @@ +import asyncio +import json +from collections.abc import Iterator +from datetime import datetime +from typing import Final + +import httpx +import pytest +import respx +from fastapi.testclient import TestClient + +import litellm +from litellm.integrations.custom_logger import CustomLogger +from litellm.proxy import proxy_server +from tests._master_key import MASTER_KEY + +_BASE: Final = "https://chatgpt.com/backend-api/codex" +_TOKEN: Final = "test-subscription-token" +_HEADERS: Final = {"authorization": f"Bearer {_TOKEN}", "x-litellm-api-key": MASTER_KEY} + + +@pytest.fixture +def client(monkeypatch: pytest.MonkeyPatch, httpx_transport: None) -> Iterator[TestClient]: + monkeypatch.setattr(proxy_server, "master_key", MASTER_KEY) + yield TestClient(proxy_server.app) + + +def test_native_catalog_preserves_account_and_filters_gateway_credentials( + client: TestClient, respx_mock: respx.MockRouter +) -> None: + catalog: Final = {"models": [{"slug": "subscription-model", "supported_reasoning_levels": ["high"]}]} + upstream: Final = respx_mock.get(f"{_BASE}/models", params={"client_version": "1.0.0"}).respond(json=catalog) + response: Final = client.get( + "/backend-api/codex/models?client_version=1.0.0", + headers={ + **_HEADERS, + "chatgpt-account-id": "account-a", + "cookie": "session=test-cookie", + "x-pass-authorization": "Bearer test-injected-token", + "x-pass-x-litellm-api-key": "test-injected-key", + }, + ) + assert response.status_code == 200 + assert response.json() == catalog + assert upstream.calls.last.request.headers["authorization"] == f"Bearer {_TOKEN}" + assert upstream.calls.last.request.headers["chatgpt-account-id"] == "account-a" + assert "x-litellm-api-key" not in upstream.calls.last.request.headers + assert "cookie" not in upstream.calls.last.request.headers + + +@pytest.mark.parametrize("account", ["account-a", "account-b"]) +def test_native_responses_preserves_body_and_stream( + client: TestClient, respx_mock: respx.MockRouter, account: str +) -> None: + body: Final = { + "model": "subscription-model", + "input": [{"role": "user", "content": "Hello"}], + "reasoning": {"effort": "high"}, + "store": False, + "stream": True, + } + stream: Final = ( + b'event: response.output_text.delta\ndata: {"type":"response.output_text.delta","delta":"Hello"}\n\n' + ) + upstream: Final = respx_mock.post(f"{_BASE}/responses").respond( + content=stream, headers={"content-type": "text/event-stream"} + ) + response: Final = client.post( + "/backend-api/codex/responses", + json=body, + headers={**_HEADERS, "authorization": f"Bearer test-{account}", "chatgpt-account-id": account}, + ) + assert response.status_code == 200 + assert response.content == stream + assert response.headers["content-type"].startswith("text/event-stream") + assert json.loads(upstream.calls.last.request.content) == body + assert upstream.calls.last.request.headers["authorization"] == f"Bearer test-{account}" + assert upstream.calls.last.request.headers["chatgpt-account-id"] == account + + +@pytest.mark.parametrize("gateway_key", [None, "sk-invalid-test-key"]) +def test_gateway_auth_rejects_before_upstream( + client: TestClient, respx_mock: respx.MockRouter, gateway_key: str | None +) -> None: + headers: Final = { + "authorization": f"Bearer {_TOKEN}", + **({"x-litellm-api-key": gateway_key} if gateway_key else {}), + } + response: Final = client.get("/backend-api/codex/models", headers=headers) + assert response.status_code in (400, 401, 403) + assert not respx_mock.calls + + +@pytest.mark.parametrize("authorization", ["", "Basic test", "Bearer ", f"Bearer {MASTER_KEY}"]) +def test_subscription_credential_required(client: TestClient, respx_mock: respx.MockRouter, authorization: str) -> None: + response: Final = client.get("/backend-api/codex/models", headers={**_HEADERS, "authorization": authorization}) + assert response.status_code == 401 + assert not respx_mock.calls + + +@pytest.mark.parametrize("status", [401, 403, 429]) +def test_provider_errors_do_not_fall_back(client: TestClient, respx_mock: respx.MockRouter, status: int) -> None: + upstream: Final = respx_mock.post(f"{_BASE}/responses").respond( + status, json={"error": {"message": "subscription unavailable"}}, headers={"retry-after": "60"} + ) + response: Final = client.post( + "/backend-api/codex/responses", headers=_HEADERS, json={"model": "subscription-model", "stream": True} + ) + assert response.status_code == status + assert upstream.call_count == 1 + + +@pytest.mark.parametrize("method,path", [("POST", "models"), ("GET", "responses"), ("GET", "deviceauth")]) +def test_only_native_inference_routes_are_forwarded( + client: TestClient, respx_mock: respx.MockRouter, method: str, path: str +) -> None: + response: Final = client.request(method, f"/backend-api/codex/{path}", headers=_HEADERS) + assert response.status_code in (405, 422) + assert not respx_mock.calls + + +class UsageRecorder(CustomLogger): + def __init__(self) -> None: + super().__init__() + self.events: Final[asyncio.Queue[dict[str, object]]] = asyncio.Queue() + + async def async_log_success_event( + self, kwargs: dict[str, object], response_obj: object, start_time: datetime, end_time: datetime + ) -> None: + await self.events.put(kwargs) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("stream", [False, True]) +async def test_usage_logged_without_credentials( + monkeypatch: pytest.MonkeyPatch, httpx_transport: None, respx_mock: respx.MockRouter, stream: bool +) -> None: + monkeypatch.setattr(proxy_server, "master_key", MASTER_KEY) + recorder: Final = UsageRecorder() + monkeypatch.setattr(litellm, "_async_success_callback", [recorder]) + litellm.register_model( + { + "chatgpt/subscription-model": { + "litellm_provider": "chatgpt", + "mode": "responses", + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, + } + } + ) + result: Final = { + "id": "resp_test", + "object": "response", + "created_at": 1700000000, + "status": "completed", + "model": "subscription-model", + "output": [], + "usage": {"input_tokens": 12, "output_tokens": 3, "total_tokens": 15}, + } + event: Final = {"type": "response.completed", "response": result} + response_body: Final = ( + httpx.Response( + 200, + content=f"event: response.completed\ndata: {json.dumps(event)}\n\n", + headers={"content-type": "text/event-stream"}, + ) + if stream + else httpx.Response(200, json=result) + ) + respx_mock.post(f"{_BASE}/responses").mock(return_value=response_body) + async with httpx.AsyncClient(transport=httpx.ASGITransport(app=proxy_server.app), base_url="http://test") as caller: + response: Final = await caller.post( + "/backend-api/codex/responses", + json={"model": "subscription-model", "stream": stream, "input": "Hello"}, + headers={**_HEADERS, "x-pass-authorization": "Bearer test-hidden-token", "cookie": "test-hidden-cookie"}, + ) + assert response.status_code == 200 + logged: Final = await asyncio.wait_for(recorder.events.get(), timeout=5) + payload: Final = logged["standard_logging_object"] + assert isinstance(payload, dict) + assert payload["prompt_tokens"] == 12 + assert payload["completion_tokens"] == 3 + serialized: Final = json.dumps(logged, default=str) + for secret in (_TOKEN, MASTER_KEY, "test-hidden-token", "test-hidden-cookie"): + assert secret not in serialized diff --git a/tests/unit/proxy/test_litellm_pre_call_utils.py b/tests/unit/proxy/test_litellm_pre_call_utils.py index 64ff8392c02..ed7817bd660 100644 --- a/tests/unit/proxy/test_litellm_pre_call_utils.py +++ b/tests/unit/proxy/test_litellm_pre_call_utils.py @@ -1,4 +1,5 @@ import asyncio +import base64 import copy import json import os @@ -9208,6 +9209,97 @@ def test_default_team_settings_bool_turn_off_message_logging_redacts(): ) +def _fake_chatgpt_oauth_token(account_id: str) -> str: + header = base64.urlsafe_b64encode(b'{"alg":"RS256"}').rstrip(b"=").decode() + payload = ( + base64.urlsafe_b64encode( + json.dumps({"https://api.openai.com/auth": {"chatgpt_account_id": account_id}, "exp": 4102444800}).encode() + ) + .rstrip(b"=") + .decode() + ) + return f"{header}.{payload}.fake-signature" + + +CHATGPT_OAUTH_TOKEN = f"Bearer {_fake_chatgpt_oauth_token('acct-alice')}" +CHATGPT_ACCOUNT_ID_HEADER_NAME = "ChatGPT-Account-Id" +PLATFORM_API_KEY_BEARER = "Bearer sk-proj-fake-platform-key-0123456789" +CHATGPT_LEAK_TARGET_PROVIDERS = ["openai", "azure", "anthropic", "bedrock", "vertex_ai"] + + +def _codex_client_headers( + authorization_header_name: str = "authorization", account_id: str | None = "acct-alice" +) -> dict: + return { + "content-type": "application/json", + "originator": "codex_cli_rs", + authorization_header_name: CHATGPT_OAUTH_TOKEN, + **({CHATGPT_ACCOUNT_ID_HEADER_NAME: account_id} if account_id is not None else {}), + } + + +def test_clean_headers_keeps_chatgpt_oauth_bearer_when_gateway_key_is_separate(): + result = clean_headers( + headers=Headers({**_codex_client_headers(), "x-litellm-api-key": "sk-proxy-virtual-key"}), + litellm_key_header_name="x-litellm-api-key", + forward_llm_provider_auth_headers=False, + authenticated_with_header="x-litellm-api-key", + ) + + assert result["authorization"] == CHATGPT_OAUTH_TOKEN + assert "x-litellm-api-key" not in result + + +def test_clean_headers_drops_bearer_that_authenticated_to_the_gateway(): + result = clean_headers( + headers=Headers({"authorization": CHATGPT_OAUTH_TOKEN, "content-type": "application/json"}), + litellm_key_header_name=None, + forward_llm_provider_auth_headers=False, + authenticated_with_header="authorization", + ) + + assert _authorization_values(result) == [] + + +@pytest.mark.parametrize("bearer", [PLATFORM_API_KEY_BEARER, "Bearer not.a-jwt", "Bearer a.bm90LWpzb24.c"]) +def test_clean_headers_drops_non_chatgpt_bearers(bearer): + result = clean_headers( + headers=Headers({"authorization": bearer, "x-litellm-api-key": "sk-proxy-virtual-key"}), + litellm_key_header_name="x-litellm-api-key", + forward_llm_provider_auth_headers=False, + authenticated_with_header="x-litellm-api-key", + ) + + assert _authorization_values(result) == [] + + +@pytest.mark.parametrize("authorization_header_name", AUTHORIZATION_HEADER_CASINGS) +def test_chatgpt_oauth_credential_and_account_reach_chatgpt_provider(authorization_header_name): + forwarded = _headers_forwarded_to(_codex_client_headers(authorization_header_name), "chatgpt") + + assert _authorization_values(forwarded) == [CHATGPT_OAUTH_TOKEN] + assert forwarded[CHATGPT_ACCOUNT_ID_HEADER_NAME] == "acct-alice" + + +@pytest.mark.parametrize("custom_llm_provider", CHATGPT_LEAK_TARGET_PROVIDERS) +def test_chatgpt_oauth_credential_is_never_forwarded_to_other_providers(custom_llm_provider): + forwarded = _headers_forwarded_to(_codex_client_headers(), custom_llm_provider) + + assert _authorization_values(forwarded) == [] + assert CHATGPT_OAUTH_TOKEN not in forwarded.values() + assert CHATGPT_ACCOUNT_ID_HEADER_NAME not in forwarded + + +def test_chatgpt_account_header_is_not_forwarded_without_chatgpt_oauth_bearer(): + headers = {**_codex_client_headers(), "authorization": PLATFORM_API_KEY_BEARER} + + assert _headers_forwarded_to(headers, "chatgpt") == {} + + +def test_anthropic_oauth_bearer_does_not_reach_chatgpt_provider(): + assert _authorization_values(_headers_forwarded_to(_client_headers(), "chatgpt")) == [] + + def test_add_user_api_key_auth_to_request_metadata_attributes_a_cli_session_to_its_alias(): data = {"model": "gpt-5.4-nano", "messages": [{"role": "user", "content": "hi"}], "litellm_metadata": {}} session = UserAPIKeyAuth( diff --git a/tests/unit/responses/test_responses_utils.py b/tests/unit/responses/test_responses_utils.py index 97a39f64001..0863abd1f1e 100644 --- a/tests/unit/responses/test_responses_utils.py +++ b/tests/unit/responses/test_responses_utils.py @@ -933,6 +933,42 @@ class TestResponsesInputToChatMessages: ) == [user_message] +class TestMergeProviderSpecificHeaders: + def test_scoped_headers_merge_only_for_matching_provider(self): + scoped = {"custom_llm_provider": "chatgpt", "extra_headers": {"Authorization": "Bearer client-token"}} + + merged = ResponsesAPIRequestUtils.merge_provider_specific_headers( + extra_headers={"originator": "codex_cli_rs"}, + provider_specific_header=scoped, + custom_llm_provider="chatgpt", + ) + untouched = ResponsesAPIRequestUtils.merge_provider_specific_headers( + extra_headers={"originator": "codex_cli_rs"}, + provider_specific_header=scoped, + custom_llm_provider="openai", + ) + + assert merged == {"originator": "codex_cli_rs", "Authorization": "Bearer client-token"} + assert untouched == {"originator": "codex_cli_rs"} + + def test_sequence_of_scoped_entries_each_keep_their_own_scope(self): + entries = [ + {"custom_llm_provider": "anthropic", "extra_headers": {"Authorization": "Bearer anthropic-token"}}, + {"custom_llm_provider": "chatgpt", "extra_headers": {"Authorization": "Bearer chatgpt-token"}}, + ] + + merged = ResponsesAPIRequestUtils.merge_provider_specific_headers( + extra_headers=None, provider_specific_header=entries, custom_llm_provider="chatgpt" + ) + + assert merged == {"Authorization": "Bearer chatgpt-token"} + + def test_no_scoped_headers_returns_extra_headers_unchanged(self): + assert ResponsesAPIRequestUtils.merge_provider_specific_headers( + extra_headers=None, provider_specific_header=None, custom_llm_provider="chatgpt" + ) is None + + def test_responses_object_bills_an_explicit_cache_hit_at_the_explicit_rate(): model = "dashscope/qwen-responses-explicit-cache-test" previous_row = litellm.model_cost.get(model) diff --git a/tests/unit/test_router/test_router.py b/tests/unit/test_router/test_router.py index 4814d36ee39..6aaa3379f47 100644 --- a/tests/unit/test_router/test_router.py +++ b/tests/unit/test_router/test_router.py @@ -283,6 +283,15 @@ def test_update_kwargs_does_not_mutate_defaults_and_merges_metadata(): assert kwargs["litellm_metadata"] == {"baz": 123} +def test_router_model_group_llm_provider_resolves_chatgpt_without_login(tmp_path, monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("CHATGPT_TOKEN_DIR", str(tmp_path)) + params: Final = LiteLLM_Params(model="chatgpt/gpt-5.4") + + assert Router._model_group_llm_provider(params) == ("gpt-5.4", "chatgpt") + assert Router._model_group_llm_provider(LiteLLM_Params(model="no-such-provider-model-xyz")) == ("", "") + assert not any(tmp_path.iterdir()) + + def test_router_with_model_info_and_model_group(): """ Test edge case where user specifies model_group in model_info diff --git a/tests/unit/types/test_litellm_params.py b/tests/unit/types/test_litellm_params.py index b0086662f1c..740342f54d9 100644 --- a/tests/unit/types/test_litellm_params.py +++ b/tests/unit/types/test_litellm_params.py @@ -94,6 +94,7 @@ CONNECTION_NAMES: Final = ( "github_copilot_host", "configurable_clientside_auth_params", "use_xai_oauth", + "chatgpt_auth_file", "fireworks_forward_user_id", "aws_batch_role_arn", "aws_bedrock_control_plane_endpoint", diff --git a/tests/unit/types/test_router.py b/tests/unit/types/test_router.py index 2892a829eab..7855eba0042 100644 --- a/tests/unit/types/test_router.py +++ b/tests/unit/types/test_router.py @@ -16,8 +16,8 @@ from litellm.types.router import ( holds_secret_pointer, reject_server_owned_wif_params, server_owned_wif_fields_named, - server_owned_wif_fields_written, server_owned_wif_fields_present, + server_owned_wif_fields_written, ) from litellm.types.utils import ( CustomPricingLiteLLMParams, @@ -367,6 +367,7 @@ def test_server_owned_registry_includes_anthropic_openai_and_oauth_token_exchang + openai_wif_litellm_params + oauth_token_exchange_litellm_params + github_copilot_oauth_litellm_params + + ("chatgpt_auth_file",) ) assert set(openai_wif_litellm_params) == { "openai_identity_provider_id", diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 262e8b6deae..d3c1e0422df 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -1792,6 +1792,30 @@ export interface paths { patch: operations["azure_speech_proxy_route_azure_speech__endpoint__patch"]; trace?: never; }; + "/backend-api/codex/{endpoint}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Codex Passthrough Route + * @description Forward a client's ChatGPT subscription with separate LiteLLM authentication. + */ + get: operations["codex_passthrough_route_backend_api_codex__endpoint__get"]; + put?: never; + /** + * Codex Passthrough Route + * @description Forward a client's ChatGPT subscription with separate LiteLLM authentication. + */ + post: operations["codex_passthrough_route_backend_api_codex__endpoint__post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/batches": { parameters: { query?: never; @@ -35309,6 +35333,11 @@ export interface components { cache_read_input_token_cost_priority?: number | null; /** Cache Read Input Token Cost Ultrafast */ cache_read_input_token_cost_ultrafast?: number | null; + /** + * Chatgpt Auth File + * @description Path to a ChatGPT OAuth auth.json for this deployment. + */ + chatgpt_auth_file?: string | null; /** Citation Cost Per Token */ citation_cost_per_token?: number | null; /** Client Id */ @@ -50660,6 +50689,11 @@ export interface components { cache_read_input_token_cost_priority?: number | null; /** Cache Read Input Token Cost Ultrafast */ cache_read_input_token_cost_ultrafast?: number | null; + /** + * Chatgpt Auth File + * @description Path to a ChatGPT OAuth auth.json for this deployment. + */ + chatgpt_auth_file?: string | null; /** Citation Cost Per Token */ citation_cost_per_token?: number | null; /** Client Id */ @@ -53737,6 +53771,77 @@ export interface operations { }; }; }; + codex_passthrough_route_backend_api_codex__endpoint__get: { + parameters: { + query?: never; + header?: never; + path: { + endpoint: "models" | "responses"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + codex_passthrough_route_backend_api_codex__endpoint__post: { + parameters: { + query?: never; + header?: never; + path: { + endpoint: "models" | "responses"; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + responses: { + /** @description Unmodified Codex response or server-sent events */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: unknown; + }; + "text/event-stream": string; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; list_batches_batches_get: { parameters: { query?: { From ffb79b10ee706ebd5a7f4a5784deb0ad127fc2a3 Mon Sep 17 00:00:00 2001 From: Zap777 <124182405+devanshc777@users.noreply.github.com> Date: Tue, 27 Oct 2026 04:30:02 +0530 Subject: [PATCH 33/73] fix(mcp): send tool-call stream headers once the call is authorized (#47897) OAuthScopeResponse holds a tools/call response's http.response.start so a managed OAuth scope challenge can replace it with a 403. Once the call was allowed, the held start still waited for the next body message, which for a slow tool is the 15s SSE keep-alive ping. Send it as soon as the call is allowed instead. Fixes #46659 --- .../mcp_server/scope_challenge.py | 51 +++++---- .../test_mcp_oauth_passthrough_tools.py | 108 ++++++++++++++++-- .../mcp_server/test_mcp_server_manager.py | 4 +- .../test_mcp_server_tool_calls_and_headers.py | 3 +- 4 files changed, 127 insertions(+), 39 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/scope_challenge.py b/litellm/proxy/_experimental/mcp_server/scope_challenge.py index c326418bee7..121234e305d 100644 --- a/litellm/proxy/_experimental/mcp_server/scope_challenge.py +++ b/litellm/proxy/_experimental/mcp_server/scope_challenge.py @@ -33,9 +33,8 @@ class OAuthScopeResponse: self._request_path = request_path self._ready = asyncio.Event() self._lock = asyncio.Lock() - self._start: Message | None = None self._challenge: MCPUpstreamAuthError | None = None - self._refused = False + self._closed = False @property def pending(self) -> bool: @@ -50,31 +49,33 @@ class OAuthScopeResponse: self._ready.set() async def __call__(self, message: Message) -> None: - if message["type"] == "http.response.start": - self._start = message - if message["status"] != 200: - self.allow() - return - await self._ready.wait() async with self._lock: - if self._refused: + if self._closed: return - if self._start is not None: - start: Final = self._start - self._start = None - if self._challenge is not None: - error: Final = self._challenge.to_http_exception(self._base_url, self._request_path) - response: Final = JSONResponse( - status_code=error.status_code, content={"detail": error.detail}, headers=error.headers - ) - self._refused = True - await self._send( - {"type": "http.response.start", "status": response.status_code, "headers": response.raw_headers} - ) - await self._send({"type": "http.response.body", "body": bytes(response.body)}) - return - await self._send(start) - await self._send(message) + try: + if message["type"] == "http.response.start": + if message["status"] != 200: + self.allow() + await self._ready.wait() + if self._challenge is not None: + error: Final = self._challenge.to_http_exception(self._base_url, self._request_path) + response: Final = JSONResponse( + status_code=error.status_code, content={"detail": error.detail}, headers=error.headers + ) + self._closed = True + await self._send( + { + "type": "http.response.start", + "status": response.status_code, + "headers": response.raw_headers, + } + ) + await self._send({"type": "http.response.body", "body": bytes(response.body)}) + return + await self._send(message) + except BaseException: + self._closed = True + raise def scope_response_from_request(request: object) -> OAuthScopeResponse | None: diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py index b0d53f9c93b..34b021df2b9 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py @@ -5,6 +5,7 @@ from litellm.proxy._experimental.mcp_server import operations as mcp_operations import asyncio import logging import sys +from typing import Final from unittest.mock import AsyncMock, MagicMock import httpx @@ -531,12 +532,12 @@ async def test_scope_challenge_replaces_pending_stream_headers_once(): send = AsyncMock() response = OAuthScopeResponse(send, "https://gateway.example", "/docs/mcp") - await response({"type": "http.response.start", "status": 200, "headers": []}) + headers: Final = asyncio.create_task(response({"type": "http.response.start", "status": 200, "headers": []})) body = asyncio.create_task(response({"type": "http.response.body", "body": b"event: message\n"})) await asyncio.sleep(0) send.assert_not_awaited() response.deny(MCPUpstreamAuthError(403, None, "docs", required_scope="tools.write")) - await asyncio.wait_for(body, 1) + await asyncio.wait_for(asyncio.gather(headers, body), 1) await response({"type": "http.response.body", "body": b"private tool error"}) assert send.await_count == 2 start, result = [call.args[0] for call in send.await_args_list] @@ -567,7 +568,7 @@ async def test_scope_authorization_releases_progress_without_waiting_for_tool_re context = ServerRequestContext(session=MagicMock(), lifespan_context={}, protocol_version="2025-11-25", method="tools/call", request=request) token = active_mcp_request_ctx_var.set(context) try: - await gate({"type": "http.response.start", "status": 200, "headers": []}) + headers: Final = asyncio.create_task(gate({"type": "http.response.start", "status": 200, "headers": []})) progress = asyncio.create_task(gate({"type": "http.response.body", "body": b"progress", "more_body": True})) await asyncio.sleep(0) for method, status in [("initialize", 200), ("tools/list", 200), ("tools/call", 403)]: @@ -582,7 +583,7 @@ async def test_scope_authorization_releases_progress_without_waiting_for_tool_re upstream = MCPClient(server_url="https://upstream.example/mcp") async with upstream._create_httpx_client_factory(transport=httpx2.MockTransport(lambda request: httpx2.Response(200, content=b"tool result")))() as client: async with client.stream("POST", "https://upstream.example/mcp", json={"method": "tools/call"}): - await asyncio.wait_for(progress, 1) + await asyncio.wait_for(asyncio.gather(headers, progress), 1) assert send.await_args_list[-1].args[0]["body"] == b"progress" gate.deny(MCPUpstreamAuthError(403, None, "docs", required_scope="tools.write")) await gate({"type": "http.response.body", "body": b"result"}) @@ -594,21 +595,105 @@ async def test_scope_authorization_releases_progress_without_waiting_for_tool_re @pytest.mark.asyncio @pytest.mark.parametrize("failure", [ValueError, asyncio.CancelledError]) -async def test_scope_middleware_releases_waiting_response_on_validation_or_cancellation(failure): +@pytest.mark.parametrize("fail_send", [False, True]) +async def test_scope_middleware_releases_waiting_response_on_validation_or_cancellation( + failure: type[BaseException], fail_send: bool, +) -> None: from mcp.server.context import ServerRequestContext from starlette.requests import Request from litellm.proxy._experimental.mcp_server.scope_challenge import SCOPE_RESPONSE_KEY, OAuthScopeResponse, finish_scope_response - send = AsyncMock() - gate = OAuthScopeResponse(send, "https://gateway.example", "/mcp/docs") - context = ServerRequestContext(session=MagicMock(), lifespan_context={}, protocol_version="2025-11-25", method="tools/call", request=Request({"type": "http", SCOPE_RESPONSE_KEY: gate})) + send: Final = AsyncMock(side_effect=BrokenPipeError("peer disconnected") if fail_send else None) + gate: Final = OAuthScopeResponse(send, "https://gateway.example", "/mcp/docs") + context: Final = ServerRequestContext(session=MagicMock(), lifespan_context={}, protocol_version="2025-11-25", method="tools/call", request=Request({"type": "http", SCOPE_RESPONSE_KEY: gate})) + headers: Final = asyncio.create_task(gate({"type": "http.response.start", "status": 200, "headers": []})) + await asyncio.sleep(0) with pytest.raises(failure): await finish_scope_response(context, AsyncMock(side_effect=failure)) - await gate({"type": "http.response.start", "status": 200, "headers": []}) + if fail_send: + with pytest.raises(BrokenPipeError, match="peer disconnected"): + await headers + else: + await headers await gate({"type": "http.response.body", "body": b"error"}) assert not gate.pending - assert send.await_args_list[-1].args[0]["body"] == b"error" + assert send.await_args_list[-1].args[0]["type"] == ("http.response.start" if fail_send else "http.response.body") + + +@pytest.mark.asyncio +@pytest.mark.parametrize("fail_send", [False, True]) +async def test_scope_transport_owns_header_send_cancellation_and_errors(fail_send: bool) -> None: + import httpx2 + from starlette.types import Message + + from litellm.proxy._experimental.mcp_server.legacy_callbacks import record_upstream_tool_authorization + from litellm.proxy._experimental.mcp_server.scope_challenge import OAuthScopeResponse + + send_started: Final = asyncio.Event() + send_stopped: Final = asyncio.Event() + writable: Final = asyncio.Event() + sent: Final[list[Message]] = [] + + async def send(message: Message) -> None: + sent.append(message) + assert message["type"] == "http.response.start" + send_started.set() + try: + if fail_send: + raise BrokenPipeError("peer disconnected") + await writable.wait() + finally: + send_stopped.set() + + gate: Final = OAuthScopeResponse(send, "https://gateway.example", "/mcp/docs") + headers: Final = asyncio.create_task(gate({"type": "http.response.start", "status": 200, "headers": []})) + body: Final = asyncio.create_task(gate({"type": "http.response.body", "body": b"result"})) + await asyncio.sleep(0) + assert not send_started.is_set() + response: Final = httpx2.Response(200, request=httpx2.Request( + "POST", "https://upstream.example/mcp", json={"method": "tools/call"}, + )) + try: + await record_upstream_tool_authorization(response, pending=gate) + await send_started.wait() + if fail_send: + with pytest.raises(BrokenPipeError, match="peer disconnected"): + await headers + else: + headers.cancel() + with pytest.raises(asyncio.CancelledError): + await headers + await body + assert len(sent) == 1 + assert send_stopped.is_set() + finally: + writable.set() + await send_stopped.wait() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("allowed_before_start", [False, True]) +async def test_scope_authorization_sends_stream_headers_before_the_first_body_chunk(allowed_before_start: bool) -> None: + from starlette.types import Message + + from litellm.proxy._experimental.mcp_server.scope_challenge import OAuthScopeResponse + + send: Final = AsyncMock() + gate: Final = OAuthScopeResponse(send, "https://gateway.example", "/mcp/docs") + start: Final[Message] = {"type": "http.response.start", "status": 200, "headers": [(b"content-type", b"text/event-stream")]} + if allowed_before_start: + gate.allow() + headers: Final = asyncio.create_task(gate(start)) + await asyncio.sleep(0) + if not allowed_before_start: + send.assert_not_awaited() + gate.allow() + await headers + send.assert_awaited_once_with(start) + await gate({"type": "http.response.body", "body": b"event: message\n", "more_body": True}) + assert send.await_args_list[-1].args[0]["body"] == b"event: message\n" + assert send.await_count == 2 @pytest.mark.asyncio @@ -697,13 +782,14 @@ async def test_scope_challenges_are_isolated_between_concurrent_requests(): context = ServerRequestContext(session=MagicMock(), lifespan_context={}, protocol_version="2025-11-25", method="tools/call", request=Request({"type": "http", SCOPE_RESPONSE_KEY: gate})) token = active_mcp_request_ctx_var.set(context) try: - await gate({"type": "http.response.start", "status": 200, "headers": []}) + headers: Final = asyncio.create_task(gate({"type": "http.response.start", "status": 200, "headers": []})) await asyncio.sleep(0) assert get_scope_response() is gate if denied: gate.deny(MCPUpstreamAuthError(403, None, "docs", required_scope="tools.write")) else: await record_upstream_tool_authorization(httpx2.Response(200, request=httpx2.Request("POST", "https://upstream.example/mcp", json={"method": "tools/call"})), pending=get_scope_response()) + await headers await gate({"type": "http.response.body", "body": b"done"}) return send.await_args_list[0].args[0]["status"] finally: diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 7315315a619..ecd08e17cda 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -7681,7 +7681,7 @@ class TestMCPServerManager: request=Request({"type": "http", SCOPE_RESPONSE_KEY: gate}), ) token = active_mcp_request_ctx_var.set(context) - await gate({"type": "http.response.start", "status": 200, "headers": []}) + headers: Final = asyncio.create_task(gate({"type": "http.response.start", "status": 200, "headers": []})) progress = asyncio.create_task(gate({"type": "http.response.body", "body": b"progress", "more_body": True})) async def upstream_call(*args, **kwargs): @@ -7701,7 +7701,7 @@ class TestMCPServerManager: assert not result.is_error finally: gate.allow() - await asyncio.wait_for(progress, 1) + await asyncio.wait_for(asyncio.gather(headers, progress), 1) active_mcp_request_ctx_var.reset(token) @pytest.mark.asyncio diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py index b8e28c3f9b8..a873e2ab1ae 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py @@ -280,7 +280,7 @@ async def test_mcp_server_tool_call_relays_upstream_auth_error_as_iserror( send = AsyncMock() response = OAuthScopeResponse(send, "https://gateway.example", "/mcp/pt") request = Request({"type": "http", "headers": [], SCOPE_RESPONSE_KEY: response}) - await response({"type": "http.response.start", "status": 200, "headers": []}) + headers: Final = asyncio.create_task(response({"type": "http.response.start", "status": 200, "headers": []})) try: from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError from litellm.proxy._experimental.mcp_server.server import ( @@ -328,6 +328,7 @@ async def test_mcp_server_tool_call_relays_upstream_auth_error_as_iserror( info_calls = [str(c.args[0]) for c in mock_logger.info.call_args_list if c.args] assert any("Upstream auth failure" in m for m in info_calls) response.allow() + await headers await response({"type": "http.response.body", "body": b"tool error"}) start = send.await_args_list[0].args[0] assert start["status"] == (403 if required_scope is not None else 200) From 593c640971728851d5dbf56941a26ef73e8095c6 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 19:00:38 -0400 Subject: [PATCH 34/73] fix(models): declare Azure audio snapshot capabilities (#48475) --- .../model_prices_and_context_window_backup.json | 4 ++++ model_prices_and_context_window.json | 4 ++++ .../test_model_prices_and_context_window.py | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index b7e0c881455..d9df9d97d7b 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -6206,6 +6206,8 @@ "text", "audio" ], + "supports_audio_input": true, + "supports_audio_output": true, "supports_function_calling": true, "supports_native_streaming": true, "supports_parallel_function_calling": true, @@ -6270,6 +6272,8 @@ "text", "audio" ], + "supports_audio_input": true, + "supports_audio_output": true, "supports_function_calling": true, "supports_native_streaming": true, "supports_parallel_function_calling": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index b7e0c881455..d9df9d97d7b 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -6206,6 +6206,8 @@ "text", "audio" ], + "supports_audio_input": true, + "supports_audio_output": true, "supports_function_calling": true, "supports_native_streaming": true, "supports_parallel_function_calling": true, @@ -6270,6 +6272,8 @@ "text", "audio" ], + "supports_audio_input": true, + "supports_audio_output": true, "supports_function_calling": true, "supports_native_streaming": true, "supports_parallel_function_calling": true, diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index c0abb18ca8c..45222b2c9ab 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -27,6 +27,23 @@ def prices() -> dict: return json.loads(PRICES_PATH.read_text()) +@pytest.mark.parametrize("model", ("azure/gpt-audio-1.5-2026-02-23", "azure/gpt-audio-mini-2025-10-06")) +def test_azure_audio_snapshot_capabilities_match_declared_modalities(model: str, local_model_cost_map: None) -> None: + catalog: Final = TypeAdapter(dict[str, JsonValue]).validate_json(PRICES_PATH.read_bytes()) + backup: Final = TypeAdapter(dict[str, JsonValue]).validate_json(BACKUP_PRICES_PATH.read_bytes()) + entry: Final = catalog[model] + assert isinstance(entry, dict) + assert entry == backup[model] + input_modalities: Final = entry["supported_modalities"] + output_modalities: Final = entry["supported_output_modalities"] + assert isinstance(input_modalities, list) + assert isinstance(output_modalities, list) + expected: Final = ("audio" in input_modalities, "audio" in output_modalities) + info: Final = litellm.get_model_info(model) + assert (info["supports_audio_input"], info["supports_audio_output"]) == expected + assert (litellm.supports_audio_input(model), litellm.supports_audio_output(model)) == expected + + class _CopilotEndpointRow(BaseModel): model_config = ConfigDict(frozen=True, extra="ignore") From db9eda19b4bb7a8c30cd93a037b45efd0b83b6d3 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 26 Oct 2026 16:04:33 -0700 Subject: [PATCH 35/73] fix(http_handler): name the timeout phase and limit that fired instead of "after None seconds" (#48250) * fix(http_handler): name the timeout phase and the limit that fired instead of 'after None seconds' Every HTTP verb on both handlers raised litellm.Timeout with the per-call timeout argument interpolated into the message, which is None whenever the caller relied on the client default, and always said "Connection" even for a read timeout. One helper now reads the limit httpx actually enforced from the failed request and names the phase that fired Fixes #14635 * fix(rust_bridge): name the timeout phase in the vault timeout message to match the httpx handler * fix(http_handler): drop the unreachable timeout fallbacks httpx already rules out * refactor(rust_bridge): drop a doc comment that restated the timeout helper --- .../src/exception_mapping_utils/mod.rs | 65 +++++------ .../src/exception_mapping_utils/original.rs | 14 +++ .../python-bridge/src/secrets/provider.rs | 19 +-- .../crates/python-bridge/src/secrets/vault.rs | 18 ++- litellm/llms/custom_httpx/http_handler.py | 108 ++++++++---------- .../llms/custom_httpx/test_http_handler.py | 83 ++++++++++++++ 6 files changed, 195 insertions(+), 112 deletions(-) diff --git a/litellm-rust/crates/core-utils/src/exception_mapping_utils/mod.rs b/litellm-rust/crates/core-utils/src/exception_mapping_utils/mod.rs index 162d325e4f4..8b63fadf5a6 100644 --- a/litellm-rust/crates/core-utils/src/exception_mapping_utils/mod.rs +++ b/litellm-rust/crates/core-utils/src/exception_mapping_utils/mod.rs @@ -51,7 +51,7 @@ mod rules; mod status; mod vertex_ai; -pub use original::{ExceptionFamily, OriginalException}; +pub use original::{ExceptionFamily, OriginalException, TimeoutPhase}; pub use public::{MappedFailure, PublicError, UpstreamResponse}; use rules::{Rule, contains_any, first_match}; @@ -67,7 +67,6 @@ const TIMEOUT_MARKERS: &[&str] = &[ pub struct ExceptionContext { pub model: String, pub custom_llm_provider: String, - pub asynchronous: bool, pub vertex_project: Option, pub vertex_location: Option, pub model_group: Option, @@ -105,11 +104,12 @@ pub fn exception_type( (None, message.clone(), None) } OriginalException::Timeout { + phase, timeout_seconds, elapsed_seconds, } => ( None, - timeout_message(context.asynchronous, *timeout_seconds, *elapsed_seconds), + timeout_message(*phase, *timeout_seconds, *elapsed_seconds), None, ), }; @@ -161,21 +161,16 @@ fn family_rules(family: ExceptionFamily) -> &'static [Rule] { } } -/// The text the Python HTTP handler's timeout carries: the sync and async handlers word it -/// differently. -fn timeout_message( - asynchronous: bool, +/// The text the Python HTTP handler's timeout carries: the phase that expired and the limit +/// httpx enforced for it, both rendered as Python would print them. +pub fn timeout_message( + phase: TimeoutPhase, timeout_seconds: Option, elapsed_seconds: Option, ) -> String { let timeout = python_float(timeout_seconds); - if asynchronous { - let elapsed = - python_float(elapsed_seconds.map(|seconds| (seconds * 1000.0).round() / 1000.0)); - format!("Connection timed out. Timeout passed={timeout}, time taken={elapsed} seconds") - } else { - format!("Connection timed out after {timeout} seconds.") - } + let elapsed = python_float(elapsed_seconds.map(|seconds| (seconds * 1000.0).round() / 1000.0)); + format!("{phase} timed out. Timeout passed={timeout}, time taken={elapsed} seconds") } fn python_float(value: Option) -> String { @@ -356,6 +351,7 @@ mod tests { #[test] fn a_handler_timeout_is_a_408_without_a_response() { let original = OriginalException::Timeout { + phase: TimeoutPhase::Read, timeout_seconds: Some(0.5), elapsed_seconds: Some(0.5031), }; @@ -363,7 +359,7 @@ mod tests { mapped("mistral", &original), MappedFailure { error: PublicError::Timeout { status: 408 }, - message: "MistralException - Connection timed out after 0.5 seconds.".into(), + message: "MistralException - Read timed out. Timeout passed=0.5, time taken=0.503 seconds".into(), upstream: None, debug_info: DEBUG.into(), } @@ -440,39 +436,44 @@ mod tests { } #[rstest::rstest] - #[case::sync( - false, + #[case::connect(TimeoutPhase::Connect, "Connection timed out.")] + #[case::read(TimeoutPhase::Read, "Read timed out.")] + #[case::write(TimeoutPhase::Write, "Write timed out.")] + #[case::pool(TimeoutPhase::Pool, "Connection pool timed out.")] + fn timeout_text_names_the_phase_that_expired( + #[case] phase: TimeoutPhase, + #[case] expected: &str, + ) { + assert_eq!( + timeout_message(phase, Some(0.5), Some(0.5031)), + format!("{expected} Timeout passed=0.5, time taken=0.503 seconds") + ); + } + + #[rstest::rstest] + #[case::rounds_the_elapsed_time( Some(0.5), Some(0.5031), - "Connection timed out after 0.5 seconds." - )] - #[case::async_rounds_the_elapsed_time( - true, - Some(0.5), - Some(0.5031), - "Connection timed out. Timeout passed=0.5, time taken=0.503 seconds" + "Timeout passed=0.5, time taken=0.503 seconds" )] #[case::whole_seconds_keep_a_decimal( - true, Some(600.0), Some(2.0), - "Connection timed out. Timeout passed=600.0, time taken=2.0 seconds" + "Timeout passed=600.0, time taken=2.0 seconds" )] #[case::unknown_values_render_as_none( - true, None, None, - "Connection timed out. Timeout passed=None, time taken=None seconds" + "Timeout passed=None, time taken=None seconds" )] - fn timeout_text_follows_the_delivery_mode( - #[case] asynchronous: bool, + fn timeout_text_renders_durations_as_python_floats( #[case] timeout_seconds: Option, #[case] elapsed_seconds: Option, #[case] expected: &str, ) { assert_eq!( - timeout_message(asynchronous, timeout_seconds, elapsed_seconds), - expected + timeout_message(TimeoutPhase::Read, timeout_seconds, elapsed_seconds), + format!("Read timed out. {expected}") ); } diff --git a/litellm-rust/crates/core-utils/src/exception_mapping_utils/original.rs b/litellm-rust/crates/core-utils/src/exception_mapping_utils/original.rs index 4de67e6e7b5..6517b093bbd 100644 --- a/litellm-rust/crates/core-utils/src/exception_mapping_utils/original.rs +++ b/litellm-rust/crates/core-utils/src/exception_mapping_utils/original.rs @@ -10,6 +10,7 @@ pub enum OriginalException { message: String, }, Timeout { + phase: TimeoutPhase, timeout_seconds: Option, elapsed_seconds: Option, }, @@ -20,6 +21,19 @@ pub enum OriginalException { }, } +/// The httpx timeout that expired, spelled the way the Python HTTP handler names it. +#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display, strum::VariantArray)] +pub enum TimeoutPhase { + #[strum(serialize = "Connection")] + Connect, + #[strum(serialize = "Read")] + Read, + #[strum(serialize = "Write")] + Write, + #[strum(serialize = "Connection pool")] + Pool, +} + /// Which provider-specific text rules apply before the shared status table. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ExceptionFamily { diff --git a/litellm-rust/crates/python-bridge/src/secrets/provider.rs b/litellm-rust/crates/python-bridge/src/secrets/provider.rs index 568a0cd2228..5f4cd193fcf 100644 --- a/litellm-rust/crates/python-bridge/src/secrets/provider.rs +++ b/litellm-rust/crates/python-bridge/src/secrets/provider.rs @@ -88,20 +88,23 @@ fn aws_context( } fn read_timeout(value: Option<&Bound<'_, PyAny>>) -> PyResult> { - let Some(value) = value.filter(|value| !value.is_none()) else { - return Ok(None); - }; - let seconds = match value.extract::() { - Ok(value) => Some(value), - Err(_) => value.getattr("read")?.extract::>()?, - }; - seconds + read_timeout_seconds(value)? .map(|value| { Duration::try_from_secs_f64(value).map_err(|_| PyValueError::new_err("invalid timeout")) }) .transpose() } +pub(super) fn read_timeout_seconds(value: Option<&Bound<'_, PyAny>>) -> PyResult> { + let Some(value) = value.filter(|value| !value.is_none()) else { + return Ok(None); + }; + match value.extract::() { + Ok(value) => Ok(Some(value)), + Err(_) => value.getattr("read")?.extract::>(), + } +} + fn vault_context(params: Option<&Bound<'_, PyAny>>) -> PyResult { let params = params.and_then(|value| value.cast::().ok()); let nested = params diff --git a/litellm-rust/crates/python-bridge/src/secrets/vault.rs b/litellm-rust/crates/python-bridge/src/secrets/vault.rs index 46b29839fe4..79281662848 100644 --- a/litellm-rust/crates/python-bridge/src/secrets/vault.rs +++ b/litellm-rust/crates/python-bridge/src/secrets/vault.rs @@ -2,10 +2,12 @@ mod operation; pub(super) use operation::{Failure, FailureKind, FailureStage, delete, rotate, write}; +use litellm_core_utils::exception_mapping_utils::{TimeoutPhase, timeout_message}; use litellm_secrets::hashicorp::{Error, RawOperationError}; use pyo3::prelude::*; use super::mutation::{error_value, http_message, json_value}; +use super::provider::read_timeout_seconds; const HTTP_TRANSPORT_MODULE: &str = "litellm.rust_bridge.host.http_transport"; @@ -69,21 +71,17 @@ fn cause_message(py: Python<'_>, kind: FailureKind, context: &ErrorContext) -> P } FailureKind::Native(RawOperationError::Timeout { method, elapsed }) => { if method == "POST" { - let elapsed = py - .import("builtins")? - .call_method1("round", (elapsed.as_secs_f64(), 3))?; let kwargs = pyo3::types::PyDict::new(py); kwargs.set_item( "message", - format!( - "Connection timed out. Timeout passed={}, time taken={} seconds", - context.timeout.as_deref().unwrap_or("None"), - elapsed.str()? + timeout_message( + TimeoutPhase::Read, + context.timeout, + Some(elapsed.as_secs_f64()), ), )?; kwargs.set_item("model", "default-model-name")?; kwargs.set_item("llm_provider", "litellm-httpx-handler")?; - kwargs.set_item("headers", pyo3::types::PyDict::new(py))?; py.import("litellm.exceptions")? .getattr("Timeout")? .call((), Some(&kwargs))? @@ -159,7 +157,7 @@ fn response_text(py: Python<'_>, body: &[u8]) -> PyResult { #[derive(Default)] pub(super) struct ErrorContext { - timeout: Option, + timeout: Option, aiohttp: bool, } @@ -173,7 +171,7 @@ impl ErrorContext { return Ok(Self::default()); } Ok(Self { - timeout: timeout.map(|value| value.str()?.extract()).transpose()?, + timeout: read_timeout_seconds(timeout)?, aiohttp: py .import(HTTP_TRANSPORT_MODULE)? .getattr("uses_aiohttp_transport")? diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index cffeb44f12c..0b64885df45 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -20,6 +20,7 @@ from aiohttp import ClientSession, DummyCookieJar, TCPConnector from httpx import USE_CLIENT_DEFAULT, AsyncHTTPTransport, HTTPTransport from httpx._types import CertTypes, RequestFiles from httpx._utils import get_environment_proxies +from pydantic import TypeAdapter import litellm from litellm._logging import verbose_logger @@ -566,6 +567,34 @@ async def _raise_masked_async_error(e: httpx.HTTPStatusError, stream: bool) -> N raise MaskedHTTPStatusError(e, message=_text, text=_text) from None +_TIMEOUT_PHASES: Final[Mapping[type[httpx.TimeoutException], tuple[str, str]]] = MappingProxyType( + { + httpx.ConnectTimeout: ("Connection", "connect"), + httpx.ReadTimeout: ("Read", "read"), + httpx.WriteTimeout: ("Write", "write"), + httpx.PoolTimeout: ("Connection pool", "pool"), + } +) +_UNKNOWN_TIMEOUT_PHASE: Final = ("Request", "read") +_TIMEOUT_EXTENSION: Final = TypeAdapter(dict[str, float | None]) + + +def _timeout_phase_seconds(error: httpx.TimeoutException, phase: str) -> float | None: + """The limit httpx enforced for ``phase`` on the request that raised ``error``, from the request itself.""" + return _TIMEOUT_EXTENSION.validate_python(error.request.extensions["timeout"]).get(phase) + + +def _raise_timeout(error: httpx.TimeoutException, started_at: float) -> NoReturn: + label, phase = _TIMEOUT_PHASES.get(type(error), _UNKNOWN_TIMEOUT_PHASE) + seconds: Final = _timeout_phase_seconds(error, phase) + time_taken: Final = round(time.time() - started_at, 3) + raise litellm.Timeout( + message=f"{label} timed out. Timeout passed={seconds}, time taken={time_taken} seconds", + model="default-model-name", + llm_provider="litellm-httpx-handler", + ) + + class HTTPResponseLimitError(ValueError): pass @@ -870,20 +899,7 @@ class AsyncHTTPHandler: finally: await new_client.aclose() except httpx.TimeoutException as e: - end_time: Final = time.time() - time_delta: Final = round(end_time - start_time, 3) - headers = {} - error_response: Final[httpx.Response | None] = getattr(e, "response", None) - if error_response is not None: - for key, value in error_response.headers.items(): - headers[f"response_headers-{key}"] = value - - raise litellm.Timeout( - message=f"Connection timed out. Timeout passed={timeout}, time taken={time_delta} seconds", - model="default-model-name", - llm_provider="litellm-httpx-handler", - headers=headers, - ) + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: await _raise_masked_async_error(e, stream) except Exception as e: @@ -902,6 +918,7 @@ class AsyncHTTPHandler: follow_redirects: bool | None = None, ): _follow_redirects: Final = follow_redirects if follow_redirects is not None else USE_CLIENT_DEFAULT + start_time: Final = time.time() try: if timeout is None: timeout = self.timeout @@ -946,18 +963,7 @@ class AsyncHTTPHandler: finally: await new_client.aclose() except httpx.TimeoutException as e: - headers = {} - error_response: Final[httpx.Response | None] = getattr(e, "response", None) - if error_response is not None: - for key, value in error_response.headers.items(): - headers[f"response_headers-{key}"] = value - - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - headers=headers, - ) + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: await _raise_masked_async_error(e, stream) except Exception as e: @@ -974,6 +980,7 @@ class AsyncHTTPHandler: stream: bool = False, content: _RequestContent | None = None, ): + start_time: Final = time.time() try: if timeout is None: timeout = self.timeout @@ -1012,18 +1019,7 @@ class AsyncHTTPHandler: finally: await new_client.aclose() except httpx.TimeoutException as e: - headers = {} - error_response: Final[httpx.Response | None] = getattr(e, "response", None) - if error_response is not None: - for key, value in error_response.headers.items(): - headers[f"response_headers-{key}"] = value - - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - headers=headers, - ) + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: await _raise_masked_async_error(e, stream) except Exception as e: @@ -1514,6 +1510,7 @@ class HTTPHandler: content: _RequestContent | None = None, logging_obj: LiteLLMLoggingObject | None = None, ): + start_time: Final = time.time() try: # Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix) request_data, request_content = _prepare_request_data_and_content(data, content) @@ -1546,12 +1543,8 @@ class HTTPHandler: _anchor_handler_to(response, self) response.raise_for_status() return response - except httpx.TimeoutException: - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - ) + except httpx.TimeoutException as e: + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: _raise_masked_sync_error(e, stream) except Exception as e: @@ -1568,6 +1561,7 @@ class HTTPHandler: timeout: float | httpx.Timeout | None = None, content: _RequestContent | None = None, ): + start_time: Final = time.time() try: # Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix) request_data, request_content = _prepare_request_data_and_content(data, content) @@ -1598,12 +1592,8 @@ class HTTPHandler: _anchor_handler_to(response, self) response.raise_for_status() return response - except httpx.TimeoutException: - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - ) + except httpx.TimeoutException as e: + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: _raise_masked_sync_error(e, stream) except Exception as e: @@ -1620,6 +1610,7 @@ class HTTPHandler: timeout: float | httpx.Timeout | None = None, content: _RequestContent | None = None, ): + start_time: Final = time.time() try: # Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix) request_data, request_content = _prepare_request_data_and_content(data, content) @@ -1649,12 +1640,8 @@ class HTTPHandler: if stream: _anchor_handler_to(response, self) return response - except httpx.TimeoutException: - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - ) + except httpx.TimeoutException as e: + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: _raise_masked_sync_error(e, stream) except Exception as e: @@ -1671,6 +1658,7 @@ class HTTPHandler: stream: bool = False, content: _RequestContent | None = None, ): + start_time: Final = time.time() try: # Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix) request_data, request_content = _prepare_request_data_and_content(data, content) @@ -1701,12 +1689,8 @@ class HTTPHandler: _anchor_handler_to(response, self) response.raise_for_status() return response - except httpx.TimeoutException: - raise litellm.Timeout( - message=f"Connection timed out after {timeout} seconds.", - model="default-model-name", - llm_provider="litellm-httpx-handler", - ) + except httpx.TimeoutException as e: + _raise_timeout(e, start_time) except httpx.HTTPStatusError as e: _raise_masked_sync_error(e, stream) except Exception as e: diff --git a/tests/unit/llms/custom_httpx/test_http_handler.py b/tests/unit/llms/custom_httpx/test_http_handler.py index bd4ed4c99cc..1607447f7bf 100644 --- a/tests/unit/llms/custom_httpx/test_http_handler.py +++ b/tests/unit/llms/custom_httpx/test_http_handler.py @@ -2172,3 +2172,86 @@ async def test_async_ollama_ssl_verify_false_builds_a_client_that_skips_certific assert route.call_count == 1 assert text == "Sunny" assert _cached_clients_verify_modes(client_cache, AsyncHTTPHandler) == (ssl.CERT_NONE,) + + +_TIMEOUT_CLIENT_LIMITS: Final = httpx.Timeout(42.0, connect=7.0) +_TIMEOUT_PHASES: Final = ( + (httpx.ConnectTimeout, "Connection timed out", "Timeout passed=7.0"), + (httpx.ReadTimeout, "Read timed out", "Timeout passed=42.0"), + (httpx.WriteTimeout, "Write timed out", "Timeout passed=42.0"), + (httpx.PoolTimeout, "Connection pool timed out", "Timeout passed=42.0"), +) + + +def _timing_out(kind: type[httpx.TimeoutException]) -> httpx.MockTransport: + def _raise(request: httpx.Request) -> httpx.Response: + raise kind("simulated", request=request) + + return httpx.MockTransport(_raise) + + +@pytest.mark.parametrize("verb", ["post", "put", "patch", "delete"]) +@pytest.mark.parametrize(("kind", "label", "limit"), _TIMEOUT_PHASES) +def test_sync_timeout_without_a_per_call_timeout_names_the_phase_and_the_limit_that_fired( + verb: str, kind: type[httpx.TimeoutException], label: str, limit: str +) -> None: + """Issue #14635: the handler used to print the caller's absent argument as 'after None seconds'.""" + handler: Final = HTTPHandler() + handler.client.close() + handler.client = httpx.Client(transport=_timing_out(kind), timeout=_TIMEOUT_CLIENT_LIMITS) + try: + with pytest.raises(LitellmTimeout) as raised: + getattr(handler, verb)("https://example.com/v1/embeddings", json={"input": "hi"}) + finally: + handler.close() + + message: Final = str(raised.value) + assert label in message, message + assert limit in message, message + assert "None" not in message, message + + +@pytest.mark.parametrize("verb", ["post", "put", "patch"]) +@pytest.mark.parametrize(("kind", "label", "limit"), _TIMEOUT_PHASES) +async def test_async_timeout_without_a_per_call_timeout_names_the_phase_and_the_limit_that_fired( + verb: str, kind: type[httpx.TimeoutException], label: str, limit: str +) -> None: + handler: Final = AsyncHTTPHandler(timeout=_TIMEOUT_CLIENT_LIMITS) + await handler.client.aclose() + handler.client = httpx.AsyncClient(transport=_timing_out(kind), timeout=_TIMEOUT_CLIENT_LIMITS) + try: + with pytest.raises(LitellmTimeout) as raised: + await getattr(handler, verb)("https://example.com/v1/embeddings", json={"input": "hi"}) + finally: + await handler.close() + + message: Final = str(raised.value) + assert label in message, message + assert limit in message, message + assert "None" not in message, message + + +def test_sync_timeout_reports_the_per_call_timeout_as_a_float_when_one_is_given() -> None: + handler: Final = HTTPHandler() + handler.client.close() + handler.client = httpx.Client(transport=_timing_out(httpx.ReadTimeout), timeout=_TIMEOUT_CLIENT_LIMITS) + try: + with pytest.raises(LitellmTimeout) as raised: + handler.post("https://example.com/v1/embeddings", json={"input": "hi"}, timeout=30) + finally: + handler.close() + + assert "Read timed out. Timeout passed=30.0, time taken=" in str(raised.value), str(raised.value) + + +async def test_async_timeout_reports_the_per_call_timeout_as_a_float_when_one_is_given() -> None: + handler: Final = AsyncHTTPHandler(timeout=_TIMEOUT_CLIENT_LIMITS) + await handler.client.aclose() + handler.client = httpx.AsyncClient(transport=_timing_out(httpx.ReadTimeout), timeout=_TIMEOUT_CLIENT_LIMITS) + try: + with pytest.raises(LitellmTimeout) as raised: + await handler.post("https://example.com/v1/embeddings", json={"input": "hi"}, timeout=30) + finally: + await handler.close() + + assert "Read timed out. Timeout passed=30.0, time taken=" in str(raised.value), str(raised.value) From bc0f598db5866d1d3fdb5c99ce627fb5ea19a788 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 19:11:38 -0400 Subject: [PATCH 36/73] fix(proxy): validate JSON object bodies before request handling (#48438) --- litellm/proxy/auth/auth_exception_handler.py | 12 +- .../proxy/common_utils/http_parsing_utils.py | 43 +++++-- .../proxy/auth/test_auth_exception_handler.py | 62 ++++++++++ .../test_user_api_key_auth_request_flow.py | 6 +- .../common_utils/test_http_parsing_utils.py | 112 +++++++++++++++++- tests/unit/proxy/lens/test_buffering.py | 17 ++- .../test_llm_pass_through_endpoints.py | 1 + 7 files changed, 226 insertions(+), 27 deletions(-) diff --git a/litellm/proxy/auth/auth_exception_handler.py b/litellm/proxy/auth/auth_exception_handler.py index f6637a1fdf3..2eb25cc6105 100644 --- a/litellm/proxy/auth/auth_exception_handler.py +++ b/litellm/proxy/auth/auth_exception_handler.py @@ -12,7 +12,7 @@ import litellm from litellm._logging import verbose_proxy_logger, verbose_proxy_stdout_logger from litellm.constants import EMPTY_MAPPING from litellm.integrations.otel.runtime import seed_request_identity -from litellm.litellm_core_utils.core_helpers import is_expected_client_error +from litellm.litellm_core_utils.core_helpers import as_str_mapping, is_expected_client_error from litellm.proxy._types import ( LitellmUserRoles, ModelAccessDeniedProxyException, @@ -139,7 +139,7 @@ class UserAPIKeyAuthExceptionHandler: async def handle_authentication_error( e: Exception, request: Request, - request_data: dict[str, object], + request_data: object, route: str, parent_otel_span: Span | None, api_key: str, @@ -190,6 +190,8 @@ class UserAPIKeyAuthExceptionHandler: request_route=route, ) else: + request_mapping: Final = as_str_mapping(request_data) + request_context: Final[dict[str, object]] = dict(request_mapping) if request_mapping is not None else {} # raise the exception to the caller requester_ip: Final = get_request_ip_address( request=request, @@ -229,7 +231,7 @@ class UserAPIKeyAuthExceptionHandler: # without this the failed trace would carry no team/key attributes. seed_request_identity( user_api_key_dict, - model=request_data.get("model"), + model=request_context.get("model"), ) # Budget checks live in tenant-scoped helpers (key / team / org / tag) @@ -242,14 +244,14 @@ class UserAPIKeyAuthExceptionHandler: resolve_llm_provider_for_rate_limit, ) - budget_model: Final = request_data.get("model") + budget_model: Final = request_context.get("model") _, e.llm_provider = resolve_llm_provider_for_rate_limit( budget_model if isinstance(budget_model, str) else None ) # Allow callbacks to transform the error response transformed_exception: Final = await proxy_logging_obj.post_call_failure_hook( - request_data=_with_client_context(request_data, requester_ip, user_agent), + request_data=_with_client_context(request_context, requester_ip, user_agent), original_exception=e, user_api_key_dict=user_api_key_dict, error_type=ProxyErrorTypes.auth_error, diff --git a/litellm/proxy/common_utils/http_parsing_utils.py b/litellm/proxy/common_utils/http_parsing_utils.py index a38769ac41b..9f7fed40ab3 100644 --- a/litellm/proxy/common_utils/http_parsing_utils.py +++ b/litellm/proxy/common_utils/http_parsing_utils.py @@ -6,7 +6,7 @@ from typing import Annotated, Any, Final, Literal, Union, get_args, get_origin import orjson from fastapi import Request, UploadFile, status -from pydantic import TypeAdapter +from pydantic import TypeAdapter, ValidationError from starlette._utils import get_route_path from starlette.requests import HTTPConnection from typing_extensions import NotRequired, ReadOnly, Required, assert_never @@ -162,13 +162,11 @@ def coerce_numeric_form_fields( } -def _parse_binary_body(body: bytes) -> dict: +def _parse_binary_body(body: bytes) -> dict[str, object]: """JSON sent under a binary content type still parses; real binary (protobuf) carries no params -> {}.""" try: - parsed: Final = _DECODED_JSON.validate_python(orjson.loads(body)) - if isinstance(parsed, dict): - return parsed - except orjson.JSONDecodeError: + return dict(_JSON_REQUEST_BODY.validate_python(orjson.loads(body))) + except (orjson.JSONDecodeError, ValidationError): pass return {} @@ -299,8 +297,9 @@ async def read_request_body(request: Request | None) -> dict: body_str = re.sub(r"(? dict: safe_set_request_parsed_body(request=request, parsed_body=parsed_body) return parsed_body + except ValidationError as e: + from fastapi.routing import APIRoute + + from litellm.proxy.auth.auth_utils import ( + request_dispatched_to_pass_through_endpoint, + request_dispatched_to_provider_pass_through, + ) + from litellm.proxy.lens.buffering import LensRoute + + route: Final[object] = request.scope.get("route") if request is not None else None + if request is not None and ( + request_dispatched_to_pass_through_endpoint(request) + or request_dispatched_to_provider_pass_through(request) + or isinstance(route, LensRoute) + or ( + isinstance(route, APIRoute) + and route.body_field is not None + and not route.body_field.field_info.is_required() + and (await request.body()).strip() == b"null" + ) + ): + return {} + raise ProxyException( + message="Invalid JSON payload: expected an object", + type="invalid_request_error", + param="request_body", + code=status.HTTP_400_BAD_REQUEST, + ) from e except (json.JSONDecodeError, orjson.JSONDecodeError, ProxyException) as e: # Re-raise ProxyException as-is verbose_proxy_logger.error("Invalid JSON payload received: %s", e) diff --git a/tests/unit/proxy/auth/test_auth_exception_handler.py b/tests/unit/proxy/auth/test_auth_exception_handler.py index d83a8aa2fcd..e21491fd996 100644 --- a/tests/unit/proxy/auth/test_auth_exception_handler.py +++ b/tests/unit/proxy/auth/test_auth_exception_handler.py @@ -1,5 +1,7 @@ import asyncio import json +from collections.abc import Mapping +from typing import Final from unittest.mock import AsyncMock, MagicMock, patch import httpx @@ -29,6 +31,7 @@ import litellm from litellm._logging import verbose_proxy_logger from litellm.constants import INVALID_VIRTUAL_KEY_ERROR_MARKER from litellm.exceptions import BudgetExceededError +from litellm.integrations.custom_logger import CustomLogger from litellm.proxy._types import ( ModelAccessDeniedProxyException, ProxyErrorTypes, @@ -812,6 +815,65 @@ def _http_request(client_host: str | None = "10.1.2.3", headers: dict[str, str] ) +class _AuthFailureRecorder(CustomLogger): + def __init__(self) -> None: + super().__init__() + self.data: dict[str, object] | None = None + self.error: Exception | None = None + + async def async_post_call_failure_hook( + self, + request_data: dict[str, object], + original_exception: Exception, + user_api_key_dict: UserAPIKeyAuth, + traceback_str: str | None = None, + ) -> HTTPException | None: + self.data = dict(request_data) + self.error = original_exception + return None + + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_data", [[], None, "metadata", 1, True]) +@pytest.mark.parametrize("error_kind", ["auth", "budget", "database"]) +async def test_auth_error_preserves_classification_with_non_object_context( + monkeypatch: pytest.MonkeyPatch, request_data: object, error_kind: str +) -> None: + import litellm.proxy.proxy_server as proxy_server + + monkeypatch.setattr(litellm, "budget_exceeded_status_code", 429) + errors: Final = { + "auth": HTTPException(status_code=401, detail="Invalid API key"), + "budget": BudgetExceededError(current_cost=2, max_budget=1), + "database": httpx.ConnectError("Database unavailable"), + } + error: Final = errors[error_kind] + expected: Final = _as_proxy_exception(error) + recorder: Final = _AuthFailureRecorder() + monkeypatch.setattr(litellm, "callbacks", [recorder]) + monkeypatch.setattr(proxy_server, "general_settings", {"allow_requests_on_db_unavailable": False}) + + with pytest.raises(ProxyException) as exc_info: + await UserAPIKeyAuthExceptionHandler.handle_authentication_error( + error, + _http_request(headers={"user-agent": "shape-regression"}), + request_data, + "/key/info", + None, + "sk-test", + ) + + assert exc_info.value.code == expected.code + assert exc_info.value.type == expected.type + assert exc_info.value.message == expected.message + assert recorder.error is error + assert recorder.data is not None + metadata: Final = recorder.data["metadata"] + assert isinstance(metadata, Mapping) + assert metadata["requester_ip_address"] == "10.1.2.3" + assert metadata["user_agent"] == "shape-regression" + + @pytest.mark.asyncio @pytest.mark.parametrize( "auth_error, general_settings, request_kwargs, expected_ip", diff --git a/tests/unit/proxy/auth/test_user_api_key_auth_request_flow.py b/tests/unit/proxy/auth/test_user_api_key_auth_request_flow.py index 108f7ce2983..2d9c781b0d1 100644 --- a/tests/unit/proxy/auth/test_user_api_key_auth_request_flow.py +++ b/tests/unit/proxy/auth/test_user_api_key_auth_request_flow.py @@ -7724,7 +7724,8 @@ async def test_user_api_key_auth_does_not_overwrite_end_user_id_set_by_builder() @pytest.mark.asyncio -async def test_user_api_key_auth_authenticates_before_raising_malformed_body_error(): +@pytest.mark.parametrize("body", [b'{}{"model": "gpt-4o"}', b"[]", b"null", b"[1e400]"]) +async def test_user_api_key_auth_authenticates_before_raising_malformed_body_error(body: bytes) -> None: """Regression (LIT-4780): a body that fails to parse must still be authenticated first, so the rejected request's trace carries the caller's key / team / user identity instead of an anonymous root span. The parse error is re-raised @@ -7742,10 +7743,11 @@ async def test_user_api_key_auth_authenticates_before_raising_malformed_body_err "headers": [(b"content-type", b"application/json")], "method": "POST", "path": "/chat/completions", + "query_string": b"probe=1", } ) request._url = URL(url="/chat/completions") - request._body = b'{}{"model": "gpt-4o"}' + request._body = body attrs = _proxy_attrs_for_centralized_checks(user_custom_auth=None) originals = {a: getattr(_proxy_server_mod, a, None) for a in attrs} diff --git a/tests/unit/proxy/common_utils/test_http_parsing_utils.py b/tests/unit/proxy/common_utils/test_http_parsing_utils.py index cd5820fa2fd..354feb0bab1 100644 --- a/tests/unit/proxy/common_utils/test_http_parsing_utils.py +++ b/tests/unit/proxy/common_utils/test_http_parsing_utils.py @@ -64,6 +64,100 @@ def _starlette_request( return Request(scope, receive) +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body", + [b"[]", b'[["model", "test"]]', b"null", b'"s"', b"1", b"true", b"false"], +) +@pytest.mark.parametrize("path", ["/v1/messages", "/vertex_ai/v1/messages", "/lens/datasets"]) +async def test_read_request_body_rejects_non_object_json(body: bytes, path: str) -> None: + request: Final = _starlette_request(body, "application/json", path) + + for _ in range(2): + with pytest.raises(ProxyException) as exc_info: + await read_request_body(request) + assert exc_info.value.code == "400" + assert exc_info.value.type == "invalid_request_error" + assert exc_info.value.param == "request_body" + assert exc_info.value.message == "Invalid JSON payload: expected an object" + assert _safe_get_request_parsed_body(request) is None + assert await request.body() == body + + +@pytest.mark.asyncio +@pytest.mark.parametrize("body", [b"[1e400]", b"1e400", b"[NaN]"]) +async def test_read_request_body_rejects_non_objects_from_json_fallback(body: bytes) -> None: + with pytest.raises(ProxyException) as exc_info: + await read_request_body(_starlette_request(body, "application/json")) + assert exc_info.value.code == "400" + assert exc_info.value.message == "Invalid JSON payload: expected an object" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body, invalid_unicode", + [ + (b"[]", False), + (b"null", False), + (b'"value"', False), + (b"[1e400]", False), + (b'"\\ud800"', True), + (b'["\\ud800"]', True), + (b'{"value":"\\ud800"}', True), + ], +) +@pytest.mark.parametrize("dispatch", ["custom", "provider", "lens"]) +async def test_pass_through_body_validation_preserves_bytes(dispatch: str, body: bytes, invalid_unicode: bool) -> None: + from fastapi.routing import APIRoute + from litellm.proxy.lens.adapter import lens_request + from litellm.proxy.lens.buffering import LensRoute + from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import vertex_proxy_route + from litellm.proxy.pass_through_endpoints.pass_through_endpoints import create_pass_through_route + + path: Final = {"custom": "/v1/messages", "provider": "/vertex_ai/predict", "lens": "/lens/datasets"}[dispatch] + route: Final = { + "custom": APIRoute(path, create_pass_through_route(path, "https://upstream.test"), methods=["POST"]), + "provider": APIRoute("/vertex_ai/{endpoint:path}", vertex_proxy_route, methods=["POST"]), + "lens": LensRoute("/lens/{path:path}", lens_request, methods=["POST"]), + }[dispatch] + incoming: Final = _starlette_request(body, "application/json", path) + _, dispatched_scope = route.matches(incoming.scope) + request: Final = Request({**incoming.scope, **dispatched_scope}, incoming.receive) + + for _ in range(2): + if invalid_unicode: + with pytest.raises(ProxyException) as exc_info: + await read_request_body(request) + assert exc_info.value.code == "400" + else: + assert await read_request_body(request) == {} + assert await request.body() == body + + +@pytest.mark.asyncio +@pytest.mark.parametrize("optional", [False, True]) +@pytest.mark.parametrize("body", [b"null", b" \nnull\t", b"[]", b'"value"']) +async def test_null_body_respects_the_dispatched_body_requirement(optional: bool, body: bytes) -> None: + from fastapi.routing import APIRoute + from litellm.proxy.management_endpoints.key_management_endpoints import generate_key_fn + from litellm.proxy.spend_tracking.spend_management_endpoints import global_spend_end_users + + endpoint: Final = global_spend_end_users if optional else generate_key_fn + route: Final = APIRoute("/audit", endpoint, methods=["POST"]) + incoming: Final = _starlette_request(body, "application/json", "/audit") + _, dispatched_scope = route.matches(incoming.scope) + request: Final = Request({**incoming.scope, **dispatched_scope}, incoming.receive) + + if optional and body.strip() == b"null": + assert await read_request_body(request) == {} + assert await request.json() is None + else: + with pytest.raises(ProxyException) as exc_info: + await read_request_body(request) + assert exc_info.value.code == "400" + assert await request.body() == body + + @pytest.mark.asyncio async def test_read_request_body_marks_body_received_once_with_its_size(monkeypatch: pytest.MonkeyPatch): events: list[tuple[str, dict[str, str | int]]] = [] # mutable-ok: recorder for the injected phase_event double @@ -131,14 +225,20 @@ async def test_read_raw_json_body_is_none_for_form_bodies(): @pytest.mark.asyncio @pytest.mark.parametrize("content_type", ["application/x-protobuf", "application/protobuf; charset=binary"]) -async def test_protobuf_body_is_not_parsed_as_json(content_type): - # OTLP trace exports (POST /v1/traces) are binary protobuf; arbitrary bytes like these - # used to hit the JSON surrogate-repair path and fail auth with a 400. - body = b"\n\xa2\x01\n\x1c\n\x0cservice.name\x12\x0c\n\nswarm\xed\xa0\x80\xff" - request = _starlette_request(body, content_type) +@pytest.mark.parametrize( + "body", + [ + b"\n\xa2\x01\n\x1c\n\x0cservice.name\x12\x0c\n\nswarm\xed\xa0\x80\xff", + pytest.param(b"100000000", id="protobuf-also-json-number"), + ], +) +async def test_protobuf_body_is_not_parsed_as_json(content_type: str, body: bytes) -> None: + request: Final = _starlette_request(body, content_type) assert await read_request_body(request) == {} - assert await request.body() == body # body is still readable by the endpoint + assert await read_request_body(request) == {} + assert await read_raw_json_body(request) == body + assert await request.body() == body @pytest.mark.asyncio diff --git a/tests/unit/proxy/lens/test_buffering.py b/tests/unit/proxy/lens/test_buffering.py index b544b70d36b..2b868e72096 100644 --- a/tests/unit/proxy/lens/test_buffering.py +++ b/tests/unit/proxy/lens/test_buffering.py @@ -249,7 +249,10 @@ async def test_admission_releases_upload_on_disconnect_or_cancellation(cancel: b @pytest.mark.asyncio @pytest.mark.parametrize("reject_auth", [False, True]) -async def test_admitted_bytes_survive_auth_parsing_and_remain_reserved_until_sent(reject_auth: bool) -> None: +@pytest.mark.parametrize("body", [b" {} ", b"[]", b"null", b'"s"']) +async def test_admitted_bytes_survive_auth_parsing_and_remain_reserved_until_sent( + reject_auth: bool, body: bytes +) -> None: app: Final = FastAPI() router: Final = APIRouter(route_class=LensRoute) @@ -264,9 +267,11 @@ async def test_admitted_bytes_survive_auth_parsing_and_remain_reserved_until_sen if reject_auth: raise HTTPException(401, "Invalid key") - async with httpx.AsyncClient( - transport=httpx.MockTransport(lambda _: httpx.Response(200, content=b"result")) - ) as upstream: + def upstream_response(incoming: httpx.Request) -> httpx.Response: + assert incoming.content == body + return httpx.Response(200, content=b"result") + + async with httpx.AsyncClient(transport=httpx.MockTransport(upstream_response)) as upstream: @router.post("/lens/datasets") async def endpoint(request: Request, auth: Annotated[None, Depends(auth_reader)]) -> Response: @@ -284,14 +289,14 @@ async def test_admitted_bytes_survive_auth_parsing_and_remain_reserved_until_sen sending.set() await finish.wait() - incoming: Final = request(b" {} ") + incoming: Final = request(body) task: Final = asyncio.create_task(app(incoming.scope, incoming.receive, send)) try: await asyncio.wait_for(sending.wait(), 2) async with httpx.AsyncClient( transport=httpx.ASGITransport(app=app), base_url="http://gateway.test" ) as client: - blocked: Final = await client.post("/lens/datasets", content=b" {} " * 2) + blocked: Final = await client.post("/lens/datasets", content=b" " * (11 - len(body))) unrelated: Final = await client.post("/ordinary", content=b"ordinary body") assert blocked.status_code == 503 assert unrelated.content == b"ordinary body" diff --git a/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py b/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py index 0186a33dbc4..60043daa402 100644 --- a/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py +++ b/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py @@ -4357,6 +4357,7 @@ class TestVertexRawPredictStreamingClassification: "type": "http", "method": "POST", "path": f"/vertex_ai/{endpoint}", + "path_params": {"endpoint": endpoint}, "headers": [ (b"content-type", b"application/json"), (b"x-litellm-api-key", b"test-key"), From a4ee9524441381c012c5f4b1bc2c3f7827a2a77b Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 26 Oct 2026 16:45:35 -0700 Subject: [PATCH 37/73] fix(proxy)!: gate key budget_duration and budget_id updates like max_budget (#47681) * fix(proxy): gate key budget_duration and budget_id updates like max_budget A key owner or team member could shorten budget_duration or swap budget_id on /key/update without the budget authorization max_budget gets, so the reset job cleared spend on a capped key or a bigger tier lifted its cap Fixes #46916 * test(proxy): cover tier keys and self-serve budget policies for key budget updates --------- Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com> --- litellm/litellm_core_utils/duration_parser.py | 10 +- .../management_endpoints/key_budget_change.py | 44 +++- .../test_key_budget_owner_updates.py | 195 ++++++++++++++++++ .../test_key_budget_change.py | 109 ++++++++++ 4 files changed, 349 insertions(+), 9 deletions(-) create mode 100644 tests/integration/authorization/test_key_budget_owner_updates.py diff --git a/litellm/litellm_core_utils/duration_parser.py b/litellm/litellm_core_utils/duration_parser.py index d52716951df..95bc77e3eba 100644 --- a/litellm/litellm_core_utils/duration_parser.py +++ b/litellm/litellm_core_utils/duration_parser.py @@ -22,7 +22,7 @@ _BUDGET_DURATION_WORD_ALIASES: Final[dict[str, str]] = { } -def _normalize_duration(duration: str) -> str: +def normalize_duration(duration: str) -> str: return _BUDGET_DURATION_WORD_ALIASES.get(duration.strip().lower(), duration) @@ -61,7 +61,7 @@ def duration_in_seconds(duration: str) -> int: Returns time in seconds till when budget needs to be reset """ - value, unit = _extract_from_regex(duration=_normalize_duration(duration)) + value, unit = _extract_from_regex(duration=normalize_duration(duration)) if unit == "s": return value @@ -89,7 +89,7 @@ def duration_in_seconds_from(start: datetime, duration: str) -> int: month of a different length needs its own start to know when it ends. Every other unit is a fixed length and ignores `start`. """ - value, unit = _extract_from_regex(duration=_normalize_duration(duration)) + value, unit = _extract_from_regex(duration=normalize_duration(duration)) if unit == "mo": return _months_in_seconds(start=start, months=value) return duration_in_seconds(duration) @@ -129,7 +129,7 @@ def get_next_standardized_reset_time( current_time, _ = _setup_timezone(current_time, timezone_str) # Parse duration - value, unit = _parse_duration(_normalize_duration(duration)) + value, unit = _parse_duration(normalize_duration(duration)) if value is None: verbose_logger.warning( "Unrecognized budget_duration %r; falling back to a next-midnight reset. " @@ -171,7 +171,7 @@ def get_budget_window_start(duration: str, reset_at: datetime) -> datetime: """Start of the budget period that ends at `reset_at`, under the same rules `get_next_standardized_reset_time` used to pick it: `30d` and `Nmo` span calendar months and a duration it does not recognize resets at the next midnight.""" - value, unit = _parse_duration(_normalize_duration(duration)) + value, unit = _parse_duration(normalize_duration(duration)) if value is None: return reset_at - timedelta(days=1) match unit: diff --git a/litellm/proxy/management_endpoints/key_budget_change.py b/litellm/proxy/management_endpoints/key_budget_change.py index 3143ef53d76..197a13cdb13 100644 --- a/litellm/proxy/management_endpoints/key_budget_change.py +++ b/litellm/proxy/management_endpoints/key_budget_change.py @@ -9,6 +9,7 @@ from typing import Final, Literal, TypeAlias from pydantic import TypeAdapter, ValidationError from litellm._logging import verbose_proxy_logger +from litellm.litellm_core_utils.duration_parser import normalize_duration from litellm.models.team import BudgetLimitEntry from litellm.models.verification_token import LiteLLM_VerificationToken from litellm.proxy._types import UpdateKeyRequest @@ -99,6 +100,30 @@ def _budget_limits_change(data: UpdateKeyRequest, existing: LiteLLM_Verification return "tightened" if kept_or_lowered else "loosened" +def _reset_schedule(budget_duration: str | None) -> str | None: + return None if budget_duration is None else normalize_duration(budget_duration) + + +def _changes_budget_duration(data: UpdateKeyRequest, existing: LiteLLM_VerificationToken) -> bool: + return "budget_duration" in data.model_fields_set and _reset_schedule(data.budget_duration) != _reset_schedule( + existing.budget_duration + ) + + +def _changes_budget_id(data: UpdateKeyRequest, existing: LiteLLM_VerificationToken) -> bool: + return "budget_id" in data.model_fields_set and data.budget_id != existing.budget_id + + +def _budget_duration_change(data: UpdateKeyRequest, existing: LiteLLM_VerificationToken) -> _FieldChange: + """ + Any new `budget_duration` restarts the reset schedule from now, so only clearing it tightens the + stored `max_budget`. A key with no `max_budget` has no cap for the schedule to reset. + """ + if existing.max_budget is None or not _changes_budget_duration(data, existing): + return "unchanged" + return "tightened" if data.budget_duration is None else "loosened" + + @dataclass(frozen=True, slots=True) class EffectiveKeyBudget: max_budget: float | None @@ -122,12 +147,23 @@ def effective_key_budget(data: UpdateKeyRequest, existing: LiteLLM_VerificationT def classify_key_budget_change(data: UpdateKeyRequest, existing: LiteLLM_VerificationToken) -> KeyBudgetChange: - overrides_linked_budget: Final = ( - existing.budget_id is not None and _max_budget_change(data, existing) != "unchanged" + overrides_linked_budget: Final = existing.budget_id is not None and ( + _max_budget_change(data, existing) != "unchanged" or _changes_budget_duration(data, existing) ) - if data.spend is not None or "soft_budget" in data.model_fields_set or overrides_linked_budget: + if ( + data.spend is not None + or "soft_budget" in data.model_fields_set + or overrides_linked_budget + or _changes_budget_id(data, existing) + ): return KeyBudgetAdminOnly() - changes: Final = frozenset((_max_budget_change(data, existing), _budget_limits_change(data, existing))) + changes: Final = frozenset( + ( + _max_budget_change(data, existing), + _budget_limits_change(data, existing), + _budget_duration_change(data, existing), + ) + ) if "loosened" in changes: return KeyBudgetLoosened() if "tightened" in changes: diff --git a/tests/integration/authorization/test_key_budget_owner_updates.py b/tests/integration/authorization/test_key_budget_owner_updates.py new file mode 100644 index 00000000000..8415c044b6f --- /dev/null +++ b/tests/integration/authorization/test_key_budget_owner_updates.py @@ -0,0 +1,195 @@ +from collections.abc import Iterator +from hashlib import sha256 +from pathlib import Path +from types import MappingProxyType +from typing import Final + +import pytest +import yaml +from integration._support.client import ( + JSON_OBJECT, + Gateway, + Scenario, + eventually, + gateway_from_environment, + string_value, +) +from integration._support.process import owned_proxy +from pydantic import JsonValue + +pytestmark: Final = pytest.mark.shares_worker_state + +CAP: Final = 0.1 +BUDGET_EXCEEDED: Final = 422 +FAST_RESET_TICK: Final = MappingProxyType( + {"PROXY_BUDGET_RESCHEDULER_MIN_TIME": "2", "PROXY_BUDGET_RESCHEDULER_MAX_TIME": "2"} +) + + +@pytest.fixture(scope="module") +def resetting(tmp_path_factory: pytest.TempPathFactory) -> Iterator[Gateway]: + with ( + gateway_from_environment() as shared, + owned_proxy(shared, tmp_path_factory.mktemp("fast-reset"), FAST_RESET_TICK) as replica, + ): + yield replica + + +def _policy_config(directory: Path, policy: str) -> Path: + shared: Final = JSON_OBJECT.validate_python(yaml.safe_load(Path("tests/integration/proxy_config.yaml").read_text())) + general_settings: Final = { + **JSON_OBJECT.validate_python(shared["general_settings"]), + "self_serve_budget_policy": policy, + } + path: Final = directory / f"{policy}.yaml" + path.write_text(yaml.safe_dump({**shared, "general_settings": general_settings})) + return path + + +@pytest.fixture(scope="module", params=("lower_only", "ceiling")) +def self_serve( + request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory +) -> Iterator[tuple[str, Gateway]]: + policy: Final = string_value(request.param) + directory: Final = tmp_path_factory.mktemp(policy) + with ( + gateway_from_environment() as shared, + owned_proxy(shared, directory, {}, config=_policy_config(directory, policy)) as replica, + ): + yield policy, replica + + +def _owner_credential(scenario: Scenario) -> str: + return scenario.key(user_id=scenario.user(user_role="internal_user")) + + +def _owned_key(gateway: Gateway, scenario: Scenario, owner: str, **fields: JsonValue) -> str: + response: Final = gateway.request("POST", "/key/generate", fields, key=owner) + assert response.status_code == 200, response.text + token: Final = string_value(JSON_OBJECT.validate_json(response.content)["key"]) + scenario.cleanups.callback(scenario.delete_key, token) + return token + + +def _key_row(gateway: Gateway, key: str) -> dict[str, JsonValue]: + rows: Final = gateway.rows( + "SELECT spend, max_budget, budget_duration, budget_id, budget_reset_at::text AS reset_at" + ' FROM "LiteLLM_VerificationToken" WHERE token = %s', + (sha256(key.encode()).hexdigest(),), + ) + assert len(rows) == 1, rows + return rows[0] + + +def _exhaust(gateway: Gateway, key: str) -> None: + gateway.post("/key/update", {"key": key, "spend": CAP}) + assert _key_row(gateway, key)["spend"] == CAP + + +def _chat_status(gateway: Gateway, model: str, key: str) -> int: + return gateway.request( + "POST", "/v1/chat/completions", {"model": model, "messages": [{"role": "user", "content": "budget"}]}, key=key + ).status_code + + +def test_key_owner_cannot_shorten_budget_duration_to_restart_an_exhausted_budget(resetting: Gateway) -> None: + with resetting.scenario() as scenario: + model: Final = scenario.model() + owner: Final = _owner_credential(scenario) + target: Final = _owned_key(resetting, scenario, owner, models=[model], max_budget=CAP, budget_duration="30d") + control: Final = _owned_key(resetting, scenario, owner, models=[model], max_budget=CAP, budget_duration="30d") + _exhaust(resetting, target) + _exhaust(resetting, control) + assert _chat_status(resetting, model, target) == BUDGET_EXCEEDED + before: Final = _key_row(resetting, target) + + denied: Final = resetting.request("POST", "/key/update", {"key": target, "budget_duration": "1s"}, key=owner) + + resetting.post("/key/update", {"key": control, "budget_duration": "1s"}) + eventually(lambda: _key_row(resetting, control)["spend"], lambda spend: spend == 0.0, seconds=60) + assert denied.status_code == 403, denied.text + assert _key_row(resetting, target) == before + assert _chat_status(resetting, model, target) == BUDGET_EXCEEDED + + +def test_key_owner_cannot_swap_or_reschedule_a_tier_key(gateway: Gateway) -> None: + with gateway.scenario() as scenario: + model: Final = scenario.model() + low: Final = scenario.budget(max_budget=CAP) + high: Final = scenario.budget(max_budget=100.0) + owner: Final = _owner_credential(scenario) + target: Final = _owned_key(gateway, scenario, owner, models=[model], budget_id=low) + _exhaust(gateway, target) + assert _chat_status(gateway, model, target) == BUDGET_EXCEEDED + before: Final = _key_row(gateway, target) + + swapped: Final = gateway.request("POST", "/key/update", {"key": target, "budget_id": high}, key=owner) + rescheduled: Final = gateway.request("POST", "/key/update", {"key": target, "budget_duration": "1s"}, key=owner) + + assert swapped.status_code == 403, swapped.text + assert rescheduled.status_code == 403, rescheduled.text + assert _key_row(gateway, target) == before + assert _chat_status(gateway, model, target) == BUDGET_EXCEEDED + + +def test_team_member_cannot_shorten_budget_duration_on_a_team_key(gateway: Gateway) -> None: + with gateway.scenario() as scenario: + team: Final = scenario.team(team_member_permissions=["/key/update", "/key/info"]) + member: Final = scenario.key(user_id=scenario.member(team), team_id=team) + target: Final = scenario.key(team_id=team, max_budget=CAP, budget_duration="30d") + before: Final = _key_row(gateway, target) + + denied: Final = gateway.request("POST", "/key/update", {"key": target, "budget_duration": "1s"}, key=member) + + assert denied.status_code == 403, denied.text + assert _key_row(gateway, target) == before + + +def test_key_owner_keeps_edits_that_cannot_restart_a_budget(gateway: Gateway) -> None: + with gateway.scenario() as scenario: + owner: Final = _owner_credential(scenario) + capped: Final = _owned_key(gateway, scenario, owner, max_budget=CAP, budget_duration="monthly") + uncapped: Final = _owned_key(gateway, scenario, owner) + + renamed: Final = gateway.request("POST", "/key/update", {"key": capped, "key_alias": "renamed"}, key=owner) + resent: Final = gateway.request("POST", "/key/update", {"key": capped, "budget_duration": "30d"}, key=owner) + scheduled: Final = gateway.request("POST", "/key/update", {"key": uncapped, "budget_duration": "1d"}, key=owner) + + assert renamed.status_code == 200, renamed.text + assert resent.status_code == 200, resent.text + assert scheduled.status_code == 200, scheduled.text + assert _key_row(gateway, uncapped)["budget_duration"] == "1d" + + +def test_self_serve_policy_keeps_tier_keys_admin_only(self_serve: tuple[str, Gateway]) -> None: + _, proxy = self_serve + with proxy.scenario() as scenario: + low: Final = scenario.budget(max_budget=CAP) + high: Final = scenario.budget(max_budget=100.0) + owner: Final = _owner_credential(scenario) + target: Final = _owned_key(proxy, scenario, owner, budget_id=low) + before: Final = _key_row(proxy, target) + + swapped: Final = proxy.request("POST", "/key/update", {"key": target, "budget_id": high}, key=owner) + rescheduled: Final = proxy.request("POST", "/key/update", {"key": target, "budget_duration": "1d"}, key=owner) + + assert swapped.status_code == 403, swapped.text + assert rescheduled.status_code == 403, rescheduled.text + assert _key_row(proxy, target) == before + + +def test_self_serve_policy_gates_a_new_reset_period_like_a_raised_cap(self_serve: tuple[str, Gateway]) -> None: + policy, proxy = self_serve + with proxy.scenario() as scenario: + owner: Final = _owner_credential(scenario) + shortened: Final = _owned_key(proxy, scenario, owner, max_budget=CAP, budget_duration="30d") + cleared: Final = _owned_key(proxy, scenario, owner, max_budget=CAP, budget_duration="30d") + + shorten: Final = proxy.request("POST", "/key/update", {"key": shortened, "budget_duration": "1s"}, key=owner) + clear: Final = proxy.request("POST", "/key/update", {"key": cleared, "budget_duration": None}, key=owner) + + assert (shorten.status_code, _key_row(proxy, shortened)["budget_duration"]) == ( + (403, "30d") if policy == "lower_only" else (200, "1s") + ), shorten.text + assert clear.status_code == 200, clear.text + assert _key_row(proxy, cleared)["budget_duration"] is None diff --git a/tests/unit/proxy/management_endpoints/test_key_budget_change.py b/tests/unit/proxy/management_endpoints/test_key_budget_change.py index baf8d870117..484f8638513 100644 --- a/tests/unit/proxy/management_endpoints/test_key_budget_change.py +++ b/tests/unit/proxy/management_endpoints/test_key_budget_change.py @@ -45,6 +45,7 @@ def _key( max_budget: float | None = None, budget_limits: Sequence[Mapping[str, object]] | None = None, budget_id: str | None = None, + budget_duration: str | None = None, ) -> LiteLLM_VerificationToken: return LiteLLM_VerificationToken( token="hashed-owner-key", @@ -54,6 +55,7 @@ def _key( max_budget=max_budget, budget_limits=None if budget_limits is None else [dict(window) for window in budget_limits], budget_id=budget_id, + budget_duration=budget_duration, metadata={}, ) @@ -127,6 +129,56 @@ def _window(duration: str, cap: float) -> Mapping[str, object]: (UpdateKeyRequest(key="sk-1", spend=0.0), _key(), KeyBudgetAdminOnly()), (UpdateKeyRequest(key="sk-1", soft_budget=1.0), _key(), KeyBudgetAdminOnly()), (UpdateKeyRequest(key="sk-1", soft_budget=None, max_budget=5.0), _key(max_budget=10.0), KeyBudgetAdminOnly()), + ( + UpdateKeyRequest(key="sk-1", budget_duration="1s"), + _key(max_budget=10.0, budget_duration="30d"), + KeyBudgetLoosened(), + ), + ( + UpdateKeyRequest(key="sk-1", budget_duration="60d"), + _key(max_budget=10.0, budget_duration="30d"), + KeyBudgetLoosened(), + ), + (UpdateKeyRequest(key="sk-1", budget_duration="1d"), _key(max_budget=10.0), KeyBudgetLoosened()), + ( + UpdateKeyRequest(key="sk-1", budget_duration=None), + _key(max_budget=10.0, budget_duration="30d"), + KeyBudgetTightened(), + ), + ( + UpdateKeyRequest(key="sk-1", budget_duration="30d"), + _key(max_budget=10.0, budget_duration="30d"), + KeyBudgetUnchanged(), + ), + ( + UpdateKeyRequest(key="sk-1", budget_duration="30d"), + _key(max_budget=10.0, budget_duration="Monthly"), + KeyBudgetUnchanged(), + ), + (UpdateKeyRequest(key="sk-1", budget_duration="1s"), _key(budget_duration="30d"), KeyBudgetUnchanged()), + (UpdateKeyRequest(key="sk-1", max_budget=5.0, budget_duration="1s"), _key(), KeyBudgetTightened()), + ( + UpdateKeyRequest(key="sk-1", budget_duration="1s"), + _key(budget_id="shared-tier-10", budget_duration="30d"), + KeyBudgetAdminOnly(), + ), + ( + UpdateKeyRequest(key="sk-1", budget_duration=None), + _key(budget_id="shared-tier-10", budget_duration="30d"), + KeyBudgetAdminOnly(), + ), + ( + UpdateKeyRequest(key="sk-1", budget_id="shared-tier-100"), + _key(budget_id="shared-tier-10"), + KeyBudgetAdminOnly(), + ), + (UpdateKeyRequest(key="sk-1", budget_id=None), _key(budget_id="shared-tier-10"), KeyBudgetAdminOnly()), + (UpdateKeyRequest(key="sk-1", budget_id="shared-tier-10"), _key(), KeyBudgetAdminOnly()), + ( + UpdateKeyRequest(key="sk-1", budget_id="shared-tier-10"), + _key(budget_id="shared-tier-10"), + KeyBudgetUnchanged(), + ), ], ) def test_classify_key_budget_change( @@ -465,3 +517,60 @@ async def test_owner_cannot_clear_max_budget_by_default(monkeypatch: pytest.Monk existing=_key(max_budget=10.0), ) assert exc.value.status_code == 403 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("policy", "data", "existing"), + [ + (None, UpdateKeyRequest(key="sk-1", budget_duration="1s"), _key(max_budget=10.0, budget_duration="30d")), + ( + "lower_only", + UpdateKeyRequest(key="sk-1", budget_duration="1s"), + _key(max_budget=10.0, budget_duration="30d"), + ), + ("ceiling", UpdateKeyRequest(key="sk-1", budget_id="shared-tier-100"), _key(budget_id="shared-tier-10")), + ("ceiling", UpdateKeyRequest(key="sk-1", budget_duration="1s"), _key(budget_id="shared-tier-10")), + ], +) +async def test_owner_cannot_restart_or_relink_a_key_budget_without_admin( + monkeypatch: pytest.MonkeyPatch, policy: str | None, data: UpdateKeyRequest, existing: LiteLLM_VerificationToken +) -> None: + with pytest.raises(HTTPException) as exc: + await _update(monkeypatch, policy=policy, data=data, existing=existing) + assert exc.value.status_code == 403 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("policy", "data", "existing"), + [ + (None, UpdateKeyRequest(key="sk-1", budget_duration="1d"), _key(budget_duration="30d")), + (None, UpdateKeyRequest(key="sk-1", budget_duration="24h"), _key(max_budget=10.0, budget_duration="daily")), + ( + "lower_only", + UpdateKeyRequest(key="sk-1", budget_duration=None), + _key(max_budget=10.0, budget_duration="30d"), + ), + ("ceiling", UpdateKeyRequest(key="sk-1", budget_duration="1s"), _key(max_budget=10.0, budget_duration="30d")), + ], +) +async def test_owner_changes_budget_duration_when_it_cannot_restart_a_cap_or_policy_allows( + monkeypatch: pytest.MonkeyPatch, policy: str | None, data: UpdateKeyRequest, existing: LiteLLM_VerificationToken +) -> None: + assert await _denial(monkeypatch, policy=policy, data=data, existing=existing) is None + + +@pytest.mark.asyncio +async def test_team_member_cannot_restart_a_team_key_budget_created_by_someone_else( + monkeypatch: pytest.MonkeyPatch, +) -> None: + with pytest.raises(HTTPException) as exc: + await _update( + monkeypatch, + policy=None, + data=UpdateKeyRequest(key="sk-1", budget_duration="1s"), + existing=_key(created_by=_TEAM_ADMIN, team_id=_TEAM, max_budget=10.0, budget_duration="30d"), + team=_team(["/key/update"]), + ) + assert exc.value.status_code == 403 From df06b2d4fbce65f67dbd714d41d27258b62c8123 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 26 Oct 2026 16:52:43 -0700 Subject: [PATCH 38/73] chore(lint): add google-convention docstring rules to strict ruff gate (#48245) * chore(lint): add google-convention docstring rules (D202, D403, D417) to strict ruff gate * fix(lint): mark docstring rules external in base ruff config so RUF100 keeps their noqa --- ruff-strict.toml | 5 ++++- ruff.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ruff-strict.toml b/ruff-strict.toml index 28102da3ccd..398cca15df2 100644 --- a/ruff-strict.toml +++ b/ruff-strict.toml @@ -2,7 +2,7 @@ extend = "ruff.toml" [lint] preview = true -select = ["ANN", "ASYNC230", "B004", "B006", "B008", "B009", "B010", "B018", "B019", "B021", "B026", "B033", "BLE", "C401", "C404", "C405", "C408", "C414", "C419", "C901", "D419", "DTZ001", "DTZ003", "DTZ005", "DTZ006", "DTZ007", "DTZ011", "EXE001", "EXE002", "F401", "FURB136", "FURB168", "FURB188", "I001", "LOG015", "N999", "PERF102", "PERF401", "PERF402", "PERF403", "PIE790", "PIE800", "PIE804", "PIE810", "PLC0206", "PLC0208", "PLC0414", "PLR0124", "PLR0206", "PLR0402", "PLR1704", "PLR1711", "PLR1714", "PLR1730", "PLR2044", "PLW0127", "PLW0133", "PLW0602", "PLW0603", "PLW1508", "PLW1510", "PYI030", "PYI036", "PYI041", "PYI064", "RET501", "RET504", "RUF010", "RUF012", "RUF015", "RUF019", "RUF022", "RUF023", "RUF046", "RUF051", "RUF059", "RUF100", "S110", "S112", "SIM101", "SIM102", "SIM103", "SIM113", "SIM114", "SIM115", "SIM117", "SIM118", "SIM201", "SIM210", "SIM211", "SIM222", "SIM401", "TC004", "TC005", "TID251", "TRY002", "TRY004", "TRY201", "TRY203", "TRY300", "UP006", "UP007", "UP008", "UP012", "UP018", "UP024", "UP028", "UP031", "UP032", "UP034", "UP035", "UP036", "UP037", "UP045"] +select = ["ANN", "ASYNC230", "B004", "B006", "B008", "B009", "B010", "B018", "B019", "B021", "B026", "B033", "BLE", "C401", "C404", "C405", "C408", "C414", "C419", "C901", "D202", "D403", "D417", "D419", "DTZ001", "DTZ003", "DTZ005", "DTZ006", "DTZ007", "DTZ011", "EXE001", "EXE002", "F401", "FURB136", "FURB168", "FURB188", "I001", "LOG015", "N999", "PERF102", "PERF401", "PERF402", "PERF403", "PIE790", "PIE800", "PIE804", "PIE810", "PLC0206", "PLC0208", "PLC0414", "PLR0124", "PLR0206", "PLR0402", "PLR1704", "PLR1711", "PLR1714", "PLR1730", "PLR2044", "PLW0127", "PLW0133", "PLW0602", "PLW0603", "PLW1508", "PLW1510", "PYI030", "PYI036", "PYI041", "PYI064", "RET501", "RET504", "RUF010", "RUF012", "RUF015", "RUF019", "RUF022", "RUF023", "RUF046", "RUF051", "RUF059", "RUF100", "S110", "S112", "SIM101", "SIM102", "SIM103", "SIM113", "SIM114", "SIM115", "SIM117", "SIM118", "SIM201", "SIM210", "SIM211", "SIM222", "SIM401", "TC004", "TC005", "TID251", "TRY002", "TRY004", "TRY201", "TRY203", "TRY300", "UP006", "UP007", "UP008", "UP012", "UP018", "UP024", "UP028", "UP031", "UP032", "UP034", "UP035", "UP036", "UP037", "UP045"] extend-select = [] # Overrides the inherited list: rules this gate enforces itself must NOT be external here, # so this config's RUF100 flags their stale `# noqa` directives. What remains external is @@ -39,6 +39,9 @@ external = [ [lint.mccabe] max-complexity = 15 +[lint.pydocstyle] +convention = "google" + [lint.pylint] max-args = 5 diff --git a/ruff.toml b/ruff.toml index 93396a723e7..715e5551fad 100644 --- a/ruff.toml +++ b/ruff.toml @@ -20,7 +20,7 @@ lint.external = [ # Family entries whose every strict rule graduated into extend-select above (FURB), and # standalone graduated codes (I001, RUF010, RUF022, RUF023, RUF051), are dropped so this # config's RUF100 polices their directives itself. - "ANN", "ASYNC230", "B", "C", "D419", "DTZ", "EXE", "LOG015", "N999", "PERF", + "ANN", "ASYNC230", "B", "C", "D202", "D403", "D417", "D419", "DTZ", "EXE", "LOG015", "N999", "PERF", "PIE", "PL", "PYI", "RET", "RUF012", "RUF015", "RUF019", "RUF046", "RUF059", "S110", "S112", "SIM", "TC", "TID251", "TRY", "UP", # Enforced by upstream litellm's ruff config, but not run in this repo's CI From 7fd8740ad5cc39c47ba433ef7444b52d301a3084 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Mon, 26 Oct 2026 16:54:38 -0700 Subject: [PATCH 39/73] fix(router): isolate forwarded Anthropic subscription cooldowns (#48485) * fix: isolate forwarded Anthropic subscription cooldowns * test: fix the clock for Anthropic cooldown regression --- litellm/router.py | 2 +- tests/unit/test_router/test_router.py | 81 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/litellm/router.py b/litellm/router.py index c76e4ec843f..d5093384a17 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -563,7 +563,7 @@ _ALIAS_PARAMS_NEVER_FORWARDED: Final = frozenset( {"model", "api_base", "api_key", "api_version", "responses_api_version"} ) _ALIAS_MARKER_FORWARDED_PARAMS_KWARG: Final = "_alias_marker_forwarded_params" -_FORWARDED_CREDENTIAL_ISOLATION_PROVIDERS: Final = frozenset({"chatgpt"}) +_FORWARDED_CREDENTIAL_ISOLATION_PROVIDERS: Final = frozenset({"chatgpt", "anthropic"}) _CLAUDE_CODE_SESSION_ID_RE: Final = re.compile(r"^[a-zA-Z0-9_\-]{8,}$") _CLAUDE_CODE_SESSION_ROUTER_TTL_SECONDS: Final = 3600 CLAUDE_CODE_SESSION_ROUTER_BINDING_TARGET: Final = "claude_code_session_router_binding" diff --git a/tests/unit/test_router/test_router.py b/tests/unit/test_router/test_router.py index 6aaa3379f47..55628cb0c34 100644 --- a/tests/unit/test_router/test_router.py +++ b/tests/unit/test_router/test_router.py @@ -30475,3 +30475,84 @@ def test_request_carries_image_sees_chat_parts_and_responses_input_items(): assert litellm.Router._request_carries_image(None, {"input": _IMAGE_RESPONSES_INPUT}) is True assert litellm.Router._request_carries_image([{"role": "user", "content": "Say hi"}], {}) is False assert litellm.Router._request_carries_image(None, {"input": "Say hi"}) is False + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("isolate_litellm_state") +@pytest.mark.parametrize("surface", ["chat", "messages"]) +@pytest.mark.parametrize("credential_kind", ["oauth", "api_key"]) +async def test_anthropic_caller_rate_limit_does_not_block_other_credentials( + respx_mock: respx.MockRouter, monkeypatch: pytest.MonkeyPatch, surface: str, credential_kind: str +) -> None: + from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler + from litellm.types.router import AllowedFailsPolicy + + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + monkeypatch.setattr("time.time", lambda: 1767225600.0) + + def upstream(request: httpx.Request) -> httpx.Response: + credential: Final = request.headers.get("authorization", request.headers.get("x-api-key", "")) + if credential.endswith("limited") and route.call_count < 2: + return httpx.Response( + 429, + headers={"retry-after": "168307"}, + json={"type": "error", "error": {"type": "rate_limit_error", "message": "usage limit"}}, + ) + return httpx.Response(200, json={ + "id": "msg-healthy", "type": "message", "role": "assistant", "model": "claude-sonnet-5-5", + "content": [{"type": "text", "text": "healthy account"}], "stop_reason": "end_turn", + "usage": {"input_tokens": 2, "output_tokens": 2}, + }) + + route: Final = respx_mock.post("https://api.anthropic.com/v1/messages").mock(side_effect=upstream) + router: Final = Router( + model_list=[{ + "model_name": "claude", "litellm_params": { + "model": "anthropic/claude-sonnet-5-5", "api_key": "fake-gateway-key", + }, "model_info": {"id": "shared-anthropic"}, + }], + num_retries=0, + allowed_fails_policy=AllowedFailsPolicy(RateLimitErrorAllowedFails=1), + ) + client: Final = AsyncHTTPHandler(transport=httpx.AsyncHTTPTransport()) + + async def call(account: str | None) -> object: + credentials: Final = ( + {"provider_specific_header": { + "custom_llm_provider": "anthropic", "extra_headers": { + "Authorization": f"Bearer sk-ant-oat-fake-{account}", + }, + }} if credential_kind == "oauth" else {"api_key": f"fake-{account}"} + ) if account is not None else {} + if surface == "messages": + max_tokens: Final = litellm.get_max_tokens("anthropic/claude-sonnet-5-5") + assert max_tokens is not None + return await router.aanthropic_messages( + model="claude", messages=[{"role": "user", "content": "hello"}], + client=client, max_tokens=max_tokens, **credentials, + ) + return await router.acompletion( + model="claude", messages=[{"role": "user", "content": "hello"}], + client=client, **credentials, + ) + + try: + for _ in range(2): + with pytest.raises(litellm.RateLimitError): + await call("limited") + await GLOBAL_LOGGING_WORKER.flush() + limited_calls: Final = len(route.calls) + assert limited_calls == 2 + await call("healthy") + assert len(route.calls) == limited_calls + 1 + assert route.calls[-1].request.headers.get( + "authorization", route.calls[-1].request.headers.get("x-api-key", "") + ) == ("Bearer sk-ant-oat-fake-healthy" if credential_kind == "oauth" else "fake-healthy") + await call(None) + assert len(route.calls) == limited_calls + 2 + assert route.calls[-1].request.headers["x-api-key"] == "fake-gateway-key" + await call("limited") + assert len(route.calls) == limited_calls + 3 + finally: + await GLOBAL_LOGGING_WORKER.flush() + await client.close() From db1c871c12c2add5242f300031012d324acd4e65 Mon Sep 17 00:00:00 2001 From: Kerry Lu Date: Mon, 26 Oct 2026 17:03:05 -0700 Subject: [PATCH 40/73] refactor(cost-map): drop the legacy max_tokens field in favor of max_input_tokens and max_output_tokens (#48471) * refactor(cost-map): drop the legacy max_tokens field in favor of max_input_tokens and max_output_tokens Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: drop assertions that pinned the retired cost-map max_tokens field Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 3573 ++--------------- .../mcp_server/sampling_handler.py | 2 +- litellm/router.py | 1 - .../io_token_rate_limit_check.py | 2 +- litellm/utils.py | 15 +- model_prices_and_context_window.json | 3573 ++--------------- .../test_fallback_generalizations.py | 8 +- .../openai_like/test_openinfer_provider.py | 9 +- .../test_model_prices_and_context_window.py | 23 +- tests/unit/test_together_ai_model_metadata.py | 14 - tests/unit/test_utils.py | 41 +- 11 files changed, 543 insertions(+), 6718 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index d9df9d97d7b..c913c879ccf 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -9,9 +9,8 @@ "input_cost_per_audio_token": 0.0, "input_cost_per_token": 0.0, "litellm_provider": "one of https://docs.litellm.ai/docs/providers", - "max_input_tokens": "max input tokens, if the provider specifies it. if not default to max_tokens", - "max_output_tokens": "max output tokens, if the provider specifies it. if not default to max_tokens", - "max_tokens": "LEGACY parameter. set to max_output_tokens if provider specifies it. IF not set to max_input_tokens, if provider specifies it.", + "max_input_tokens": "max input tokens, if the provider specifies it", + "max_output_tokens": "max output tokens, if the provider specifies it", "mode": "one of: chat, embedding, completion, image_generation, audio_transcription, audio_speech, image_generation, moderation, rerank, search", "output_cost_per_reasoning_token": 0.0, "output_cost_per_token": 0.0, @@ -49,28 +48,24 @@ "1024-x-1024/50-steps/stability.stable-diffusion-xl-v1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, "1024-x-1024/max-steps/stability.stable-diffusion-xl-v1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "512-x-512/50-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.018 }, "512-x-512/max-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.036 }, @@ -79,7 +74,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8191, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.25e-05, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -89,7 +83,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8191, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.88e-05, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -100,7 +93,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-ai21-labs-jamba-1-5-large.md" @@ -111,7 +103,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-ai21-labs-jamba-1-5-mini.md" @@ -121,7 +112,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 70000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -298,7 +288,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x4.md", @@ -311,7 +300,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x5.md", @@ -324,7 +312,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x4.md", @@ -337,7 +324,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x5.md", @@ -352,7 +338,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -368,7 +353,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -389,7 +373,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -414,7 +397,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -435,7 +417,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.311e-06, "supports_function_calling": true, @@ -455,7 +436,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://aws.amazon.com/nova/pricing/", @@ -474,7 +454,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.597e-06, "supports_function_calling": true, @@ -494,7 +473,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://aws.amazon.com/nova/pricing/", @@ -518,7 +496,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-06, "output_cost_per_token_batches": 1.375e-06, @@ -544,7 +521,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -562,7 +538,6 @@ "amazon.nova-2-multimodal-embeddings-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 8172, - "max_tokens": 8172, "mode": "embedding", "input_cost_per_token": 1.35e-07, "input_cost_per_image": 6e-05, @@ -583,7 +558,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "output_cost_per_token_batches": 7e-08, @@ -602,7 +576,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.25e-05, "output_cost_per_token_batches": 6.25e-06, @@ -626,7 +599,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -646,7 +618,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2.75e-06, @@ -660,7 +631,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -669,7 +639,6 @@ "input_cost_per_token": 8e-07, "litellm_provider": "bedrock", "max_input_tokens": 128, - "max_tokens": 128, "metadata": { "notes": "'supports_image_input' is a deprecated field. Use 'supports_embedding_image_input' instead." }, @@ -684,7 +653,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -693,7 +661,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -702,7 +669,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -740,7 +706,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -755,7 +720,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -770,7 +734,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -784,7 +747,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -798,7 +760,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -812,7 +773,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -860,7 +820,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 6e-07 }, @@ -869,7 +828,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-07 }, @@ -878,7 +836,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -889,7 +846,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -909,7 +865,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/index.json", @@ -941,7 +896,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -964,7 +918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -982,7 +935,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -1001,7 +953,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -1021,7 +972,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -1039,7 +989,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -1054,7 +1003,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -1067,7 +1015,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -1097,7 +1044,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -1125,7 +1071,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1164,7 +1109,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1204,7 +1148,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1244,7 +1187,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1284,7 +1226,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1324,7 +1265,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1364,7 +1304,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1405,7 +1344,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -1427,7 +1365,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1470,7 +1407,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1513,7 +1449,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1556,7 +1491,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1596,7 +1530,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1639,7 +1572,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1678,7 +1610,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1721,7 +1652,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1760,7 +1690,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1803,7 +1732,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1842,7 +1770,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1880,7 +1807,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1922,7 +1848,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1965,7 +1890,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -2010,7 +1934,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2053,7 +1976,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -2098,7 +2020,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2141,7 +2062,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2186,7 +2106,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2229,7 +2148,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2269,7 +2187,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2312,7 +2229,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2357,7 +2273,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2395,7 +2310,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2435,7 +2349,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2480,7 +2393,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2525,7 +2437,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2570,7 +2481,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2615,7 +2525,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2660,7 +2569,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2703,7 +2611,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2745,7 +2652,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -2790,7 +2696,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -2835,7 +2740,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2880,7 +2784,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2925,7 +2828,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2970,7 +2872,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -3011,7 +2912,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3050,7 +2950,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3089,7 +2988,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3128,7 +3026,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3167,7 +3064,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3206,7 +3102,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3246,7 +3141,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3286,7 +3180,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3319,7 +3212,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05 }, @@ -3328,7 +3220,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -3338,7 +3229,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07 }, @@ -3347,7 +3237,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -3356,7 +3245,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/codellama-CodeLlama-70b-Instruct-hf" @@ -3366,7 +3254,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/google-gemma-7b-it" @@ -3376,7 +3263,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07 }, @@ -3385,7 +3271,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -3394,7 +3279,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07 }, @@ -3403,7 +3287,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/meta-llama-Meta-Llama-3-70B-Instruct" @@ -3413,7 +3296,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/meta-llama-Meta-Llama-3-8B-Instruct" @@ -3423,7 +3305,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mistral-7B-Instruct-v0.1", @@ -3434,7 +3315,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mixtral-8x22B-Instruct-v0.1", @@ -3445,7 +3325,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mixtral-8x7B-Instruct-v0.1", @@ -3457,7 +3336,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.52e-07, "supports_function_calling": true, @@ -3473,7 +3351,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.48e-07, "supports_function_calling": true, @@ -3489,7 +3366,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.36e-06, "output_cost_per_token_flex": 1.68e-06, @@ -3508,7 +3384,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -3543,7 +3418,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3595,7 +3469,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3628,7 +3501,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -3640,7 +3512,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 6e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -3670,7 +3541,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -3685,7 +3555,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -3709,7 +3578,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, @@ -3734,7 +3602,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3765,7 +3632,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3797,7 +3663,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3830,7 +3695,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3868,7 +3732,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3899,7 +3762,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3938,7 +3800,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3971,7 +3832,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -3996,7 +3856,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -4030,7 +3889,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": false, @@ -4052,7 +3910,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -4087,7 +3944,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -4105,7 +3961,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -4154,7 +4009,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -4203,7 +4057,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -4252,7 +4105,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -4304,7 +4156,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4345,7 +4196,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", @@ -4395,7 +4245,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 6e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", @@ -4439,7 +4288,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4486,7 +4334,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4533,7 +4380,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -4581,7 +4427,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -4629,7 +4474,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -4675,7 +4519,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -4717,7 +4560,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_priority": 9e-06, @@ -4759,7 +4601,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_priority": 9e-06, @@ -4801,7 +4642,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_priority": 2.5e-06, @@ -4843,7 +4683,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_priority": 2.5e-06, @@ -4883,7 +4722,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" @@ -4895,7 +4733,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Catalog-name twin of azure_ai/model_router: the flat $0.14 per M input tokens is the router's own fee, the routed model is priced on top of it" @@ -4908,7 +4745,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4929,7 +4765,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4949,7 +4784,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4968,7 +4802,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -4987,7 +4820,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5006,7 +4838,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2.2e-05, "output_cost_per_token": 2.64e-06, @@ -5026,7 +4857,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.00022, "output_cost_per_token": 2.2e-05, @@ -5046,7 +4876,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8.8e-05, "output_cost_per_token": 2.2e-05, @@ -5076,7 +4905,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -5116,7 +4944,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -5155,7 +4982,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "supported_endpoints": [ @@ -5196,7 +5022,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5228,7 +5053,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5261,7 +5085,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -5298,7 +5121,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -5317,7 +5139,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -5335,7 +5156,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5353,7 +5173,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5371,7 +5190,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5389,7 +5207,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5409,7 +5226,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5426,7 +5242,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -5443,7 +5258,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5463,7 +5277,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5484,7 +5297,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -5525,7 +5337,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5557,7 +5368,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5587,7 +5397,6 @@ "litellm_provider": "azure", "max_input_tokens": 4097, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -5597,7 +5406,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5607,7 +5416,6 @@ "litellm_provider": "azure", "max_input_tokens": 4097, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -5619,7 +5427,6 @@ "litellm_provider": "azure", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5631,7 +5438,6 @@ "litellm_provider": "azure", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5642,7 +5448,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5651,7 +5457,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5662,7 +5468,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -5674,7 +5479,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -5687,7 +5491,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5699,7 +5502,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -5712,7 +5514,6 @@ "litellm_provider": "azure", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.00012, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5724,7 +5525,6 @@ "litellm_provider": "azure", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.00012, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5735,7 +5535,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5748,7 +5547,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5762,7 +5560,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5779,7 +5576,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -5817,7 +5613,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -5855,7 +5650,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -5893,7 +5687,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -5929,7 +5722,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -5963,7 +5755,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -5997,7 +5788,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 0.00015, "output_cost_per_token_batches": 7.5e-05, @@ -6017,7 +5807,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -6034,7 +5823,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -6053,7 +5841,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6073,7 +5860,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6092,7 +5878,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6125,7 +5910,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6157,7 +5941,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -6190,7 +5973,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -6225,7 +6007,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6257,7 +6038,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6290,7 +6070,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6322,7 +6101,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -6340,7 +6118,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -6358,7 +6135,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 6e-07, @@ -6392,7 +6168,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6414,7 +6189,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6449,7 +6223,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6483,7 +6256,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6518,7 +6290,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6552,7 +6323,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -6587,7 +6357,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6622,7 +6391,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6656,7 +6424,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6720,7 +6487,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.0002, "output_cost_per_token": 2e-05, @@ -6739,7 +6505,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 2e-05, @@ -6793,7 +6558,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "audio_transcription", "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/gpt-realtime-whisper", "supported_endpoints": [ @@ -6817,7 +6581,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -6860,7 +6623,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -6895,7 +6657,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "output_cost_per_token_priority": 3.6e-06, @@ -6932,7 +6693,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6971,7 +6731,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -7008,7 +6767,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7044,7 +6802,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -7083,7 +6840,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -7122,7 +6878,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -7158,7 +6913,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -7194,7 +6948,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -7228,7 +6981,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -7269,7 +7021,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7294,7 +7045,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7316,7 +7066,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7337,7 +7086,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7358,7 +7106,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7380,7 +7127,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7401,7 +7147,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7422,7 +7167,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7444,7 +7188,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7465,7 +7208,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7486,7 +7228,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.6e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7502,7 +7243,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.6e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7518,7 +7258,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7550,7 +7289,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7582,7 +7320,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7618,7 +7355,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_batches": 7e-06, @@ -7658,7 +7394,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_batches": 7e-06, @@ -7696,7 +7431,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7730,7 +7464,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -7762,7 +7495,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -7801,7 +7533,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -7850,7 +7581,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -7906,7 +7636,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -7956,7 +7685,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8006,7 +7734,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -8062,7 +7789,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8112,7 +7838,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8163,7 +7888,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -8212,7 +7936,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -8266,7 +7989,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8327,7 +8049,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8391,7 +8112,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8455,7 +8175,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -8519,7 +8238,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -8583,7 +8301,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -8647,7 +8364,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -8699,7 +8415,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -8748,7 +8463,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -8797,7 +8511,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -8846,7 +8559,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -8901,7 +8613,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -8954,7 +8665,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9001,7 +8711,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9050,7 +8759,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9099,7 +8807,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -9148,7 +8855,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -9197,7 +8903,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -9242,7 +8947,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -9279,7 +8983,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -9325,7 +9028,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9380,7 +9082,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9436,7 +9137,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-05, "output_cost_per_token_above_272k_tokens": 1.98e-05, @@ -9492,7 +9192,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-06, "output_cost_per_token_above_272k_tokens": 1.98e-06, @@ -9542,7 +9241,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "output_cost_per_token_above_272k_tokens": 8.25e-05, @@ -9591,7 +9289,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "output_cost_per_token_above_272k_tokens": 8.25e-07, @@ -9646,7 +9343,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -9689,7 +9385,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.3e-05, "reasoning_effort_levels": [ @@ -9735,7 +9430,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9790,7 +9484,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9846,7 +9539,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-05, "output_cost_per_token_above_272k_tokens": 1.98e-05, @@ -9902,7 +9594,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-06, "output_cost_per_token_above_272k_tokens": 1.98e-06, @@ -9961,7 +9652,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -10020,7 +9710,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_batches": 2.475e-05, "output_cost_per_token_batches": 1.65e-05, @@ -10075,7 +9764,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_batches": 2.475e-05, "output_cost_per_token_batches": 1.65e-05, @@ -10127,7 +9815,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -10187,7 +9874,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_batches": 1.5e-05, @@ -10246,7 +9932,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10299,7 +9984,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10353,7 +10037,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10406,7 +10089,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10453,7 +10135,6 @@ "litellm_provider": "azure", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -10497,7 +10178,6 @@ "litellm_provider": "azure", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -10539,7 +10219,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "search_context_cost_per_query": { @@ -10590,7 +10269,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "search_context_cost_per_query": { @@ -10642,7 +10320,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "search_context_cost_per_query": { @@ -10690,7 +10367,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "search_context_cost_per_query": { @@ -11066,7 +10742,7 @@ "input_cost_per_token": 8e-06, "litellm_provider": "azure", "max_input_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_function_calling": true @@ -11075,7 +10751,7 @@ "input_cost_per_token": 8e-06, "litellm_provider": "azure", "max_input_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_function_calling": true @@ -11087,7 +10763,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_function_calling": true, @@ -11105,7 +10780,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -11124,7 +10798,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11142,7 +10815,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11160,7 +10832,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supported_endpoints": [ @@ -11191,7 +10862,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -11223,7 +10893,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "search_context_cost_per_query": { @@ -11261,7 +10930,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_prompt_caching": true, @@ -11278,7 +10946,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11295,7 +10962,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -11326,7 +10992,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -11357,7 +11022,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supported_endpoints": [ @@ -11388,7 +11052,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11433,7 +11096,6 @@ "input_cost_per_token": 1.43e-07, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11443,7 +11105,6 @@ "input_cost_per_token": 2.2e-08, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11453,7 +11114,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11504,7 +11164,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -11542,7 +11201,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.76e-06, "output_cost_per_token_batches": 8.8e-07, @@ -11578,7 +11236,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -11612,7 +11269,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11633,7 +11289,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11653,7 +11308,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11672,7 +11326,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11691,7 +11344,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11710,7 +11362,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2.2e-05, "output_cost_per_token": 2.64e-06, @@ -11730,7 +11381,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.00022, "output_cost_per_token": 2.2e-05, @@ -11750,7 +11400,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8.8e-05, "output_cost_per_token": 2.2e-05, @@ -11780,7 +11429,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11820,7 +11468,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -11858,7 +11505,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -11896,7 +11542,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "supported_endpoints": [ @@ -11937,7 +11582,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11969,7 +11613,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12002,7 +11645,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -12021,7 +11663,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -12039,7 +11680,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12057,7 +11697,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -12090,7 +11729,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -12123,7 +11761,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12141,7 +11778,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12159,7 +11795,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12180,7 +11815,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12204,7 +11838,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "azure_ai", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -12215,7 +11848,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "azure_ai", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -12227,7 +11859,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12245,7 +11876,6 @@ "azure_ai/FLUX-1.1-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 5000, - "max_tokens": 5000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/black-forest-labs-flux-1-kontext-pro-and-flux1-1-pro-now-available-in-azure-ai-f/4434659", @@ -12256,7 +11886,6 @@ "azure_ai/FLUX.1-Kontext-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 5000, - "max_tokens": 5000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/black-forest-labs/", @@ -12267,7 +11896,6 @@ "azure_ai/flux.2-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -12279,7 +11907,6 @@ "input_cost_per_pixel": 5e-08, "litellm_provider": "azure_ai", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "image_generation", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/black-forest-labs/", "supported_endpoints": [ @@ -12301,7 +11928,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.85e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12316,7 +11942,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.828e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12332,7 +11957,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.52e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12348,7 +11972,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.84e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12363,7 +11986,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.84e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12378,7 +12000,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 7.26e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12393,7 +12014,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.46e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12415,7 +12035,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.3e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12438,7 +12057,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12461,7 +12079,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12484,7 +12101,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -12513,7 +12129,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12528,7 +12143,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12571,7 +12185,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.64e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12637,7 +12250,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12660,7 +12272,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.1e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -12672,7 +12283,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12685,7 +12295,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 10000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://azure.microsoft.com/en-us/blog/introducing-the-llama-4-herd-in-azure-ai-foundry-and-azure-databricks/", @@ -12699,7 +12308,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.7e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12711,7 +12319,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.54e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12723,7 +12330,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12736,7 +12342,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12749,7 +12354,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12762,7 +12366,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12775,7 +12378,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12788,7 +12390,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12801,7 +12402,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12814,7 +12414,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12827,7 +12426,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12840,7 +12438,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12854,7 +12451,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12867,7 +12463,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.2e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12881,7 +12476,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12893,7 +12487,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12935,7 +12528,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 8182, - "max_tokens": 8182, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12975,7 +12567,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12989,7 +12580,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13001,7 +12591,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13012,7 +12601,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/introducing-cohere-rerank-4-0-in-microsoft-foundry/4477076" @@ -13023,7 +12611,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/introducing-cohere-rerank-4-0-in-microsoft-foundry/4477076" @@ -13033,7 +12620,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13048,7 +12634,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13064,7 +12649,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.56e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13076,7 +12660,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13092,7 +12675,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 5.1e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13109,7 +12691,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13122,7 +12703,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 3072, @@ -13142,7 +12722,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13159,7 +12738,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -13180,7 +12758,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -13200,7 +12777,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13219,7 +12795,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13236,7 +12811,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -13250,7 +12824,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -13264,7 +12837,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13279,7 +12851,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.00971, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13290,7 +12861,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 70000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13302,7 +12872,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13319,7 +12888,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13343,7 +12911,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-08, "source": "https://marketplace.microsoft.com/en/marketplace/apps/000-000.ministral-3b-2410-offer?tab=Overview", @@ -13356,7 +12923,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13369,7 +12935,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13381,7 +12946,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://marketplace.microsoft.com/en/marketplace/apps/000-000.mistral-ai-large-2407-offer?tab=Overview", @@ -13393,7 +12957,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13407,7 +12970,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "supports_assistant_prefill": true, @@ -13420,7 +12982,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13432,7 +12993,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13445,7 +13005,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -13459,7 +13018,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -13471,7 +13029,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13481,7 +13038,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13491,7 +13047,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13501,7 +13056,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13526,7 +13080,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13536,7 +13089,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v2:1": { @@ -13545,7 +13097,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13555,7 +13106,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13565,7 +13115,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v2:1": { @@ -13574,7 +13123,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13583,7 +13131,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 7.55e-06, "supports_tool_choice": true @@ -13593,7 +13140,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -13603,7 +13149,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -13614,7 +13159,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13634,7 +13178,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13652,7 +13195,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13672,7 +13214,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.03e-06, "output_cost_per_token_flex": 1.52e-06, @@ -13686,7 +13227,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13704,7 +13244,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13723,7 +13262,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -13737,7 +13275,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -13757,7 +13294,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -13768,7 +13304,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -13779,7 +13314,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13799,7 +13333,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13817,7 +13350,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13837,7 +13369,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.94e-06, "output_cost_per_token_flex": 1.47e-06, @@ -13851,7 +13382,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13869,7 +13399,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13887,7 +13416,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13906,7 +13434,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13926,7 +13453,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13944,7 +13470,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13963,7 +13488,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13981,7 +13505,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13999,7 +13522,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.03e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14010,7 +13532,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.9e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14021,7 +13542,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -14041,7 +13561,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14059,7 +13578,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14078,7 +13596,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -14096,7 +13613,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14106,7 +13622,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v2:1": { @@ -14115,7 +13630,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14125,7 +13639,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14135,7 +13648,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v2:1": { @@ -14144,7 +13656,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14153,7 +13664,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 8.38e-06, "supports_tool_choice": true @@ -14163,7 +13673,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05 }, @@ -14172,7 +13681,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14184,7 +13692,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14202,7 +13709,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14221,7 +13727,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14239,7 +13744,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.78e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14250,7 +13754,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14262,7 +13765,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14280,7 +13782,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14299,7 +13800,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14317,7 +13817,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.55e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14328,7 +13827,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14340,7 +13838,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.86e-06, "output_cost_per_token_flex": 9.3e-07, @@ -14358,7 +13855,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14377,7 +13873,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.01e-06, "output_cost_per_token_flex": 5.05e-07, @@ -14396,7 +13891,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.86e-06, "output_cost_per_token_flex": 9.3e-07, @@ -14414,7 +13908,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -14425,7 +13918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.56e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -14437,7 +13929,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9.1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -14450,7 +13941,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14468,7 +13958,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14487,7 +13976,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14504,7 +13992,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Anthropic via Invoke route does not currently support pdf input." }, @@ -14523,7 +14010,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.88e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14534,7 +14020,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.01e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14545,7 +14030,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -14565,7 +14049,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14583,7 +14066,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14603,7 +14085,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.03e-06, "output_cost_per_token_flex": 1.52e-06, @@ -14617,7 +14098,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -14635,7 +14115,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14653,7 +14132,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14663,7 +14141,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-east-1/1-month-commitment/anthropic.claude-v2:1": { @@ -14672,7 +14149,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14682,7 +14158,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14692,7 +14167,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-east-1/6-month-commitment/anthropic.claude-v2:1": { @@ -14701,7 +14175,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14710,7 +14183,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -14720,7 +14192,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14730,7 +14201,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14741,7 +14211,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14752,7 +14221,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14763,7 +14231,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -14774,7 +14241,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -14786,7 +14252,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -14798,7 +14263,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -14818,7 +14282,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14838,7 +14301,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -14855,7 +14317,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -14869,7 +14330,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -14887,7 +14347,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14905,7 +14364,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -14925,7 +14383,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14943,7 +14400,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14963,7 +14419,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -14977,7 +14432,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -14995,7 +14449,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15013,7 +14466,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.84e-06, "supports_function_calling": true, @@ -15027,7 +14479,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -15036,7 +14487,6 @@ "input_cost_per_token": 2e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -15046,7 +14496,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.7e-06 }, @@ -15055,7 +14504,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-07 }, @@ -15064,7 +14512,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -15077,7 +14524,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15106,7 +14552,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15127,7 +14572,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.5e-06, "supports_pdf_input": true @@ -15137,7 +14581,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 2.65e-06, "supports_pdf_input": true @@ -15145,7 +14588,6 @@ "bedrock/us-gov-west-1/amazon.nova-2-multimodal-embeddings-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 8172, - "max_tokens": 8172, "mode": "embedding", "input_cost_per_token": 1.62e-07, "input_cost_per_image": 7.2e-05, @@ -15164,7 +14606,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.88e-07, "supports_function_calling": true, @@ -15180,7 +14621,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.68e-07, "supports_function_calling": true, @@ -15194,7 +14634,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.84e-06, "supports_function_calling": true, @@ -15208,7 +14647,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -15217,7 +14655,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -15227,7 +14664,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.6e-06 }, @@ -15236,7 +14672,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-07 }, @@ -15245,7 +14680,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -15258,7 +14692,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15287,7 +14720,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15308,7 +14740,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.5e-06, "supports_pdf_input": true @@ -15318,7 +14749,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 6e-07, "supports_pdf_input": true @@ -15329,7 +14759,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -15340,7 +14769,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -15351,7 +14779,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15361,7 +14788,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-west-2/1-month-commitment/anthropic.claude-v2:1": { @@ -15370,7 +14796,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15380,7 +14805,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15390,7 +14814,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-west-2/6-month-commitment/anthropic.claude-v2:1": { @@ -15399,7 +14822,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15408,7 +14830,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -15418,7 +14839,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -15428,7 +14848,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -15439,7 +14858,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -15450,7 +14868,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -15462,7 +14879,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -15474,7 +14890,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -15494,7 +14909,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15514,7 +14928,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -15531,7 +14944,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -15545,7 +14957,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -15563,7 +14974,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15582,7 +14992,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -15672,7 +15081,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -15683,7 +15091,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -15694,7 +15101,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true, @@ -15705,7 +15111,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -15716,7 +15121,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -15727,7 +15131,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://www.cerebras.ai/blog/openai-gpt-oss-120b-runs-fastest-on-cerebras", @@ -15742,7 +15145,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inference-docs.cerebras.ai/support/pricing", @@ -15755,7 +15157,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.49e-06, "source": "https://api.cerebras.ai/public/v1/models/qwen-3.8-27b", @@ -15771,7 +15172,6 @@ "litellm_provider": "nlp_cloud", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5e-07 }, @@ -15802,7 +15202,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -15829,7 +15228,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -15858,7 +15256,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -15892,7 +15289,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -15928,7 +15324,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -15972,7 +15367,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -16019,7 +15413,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -16063,7 +15456,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -16092,7 +15484,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16126,7 +15517,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16163,7 +15553,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16206,7 +15595,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16249,7 +15637,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16293,7 +15680,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16337,7 +15723,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -16382,7 +15767,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -16428,7 +15812,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1e-05, @@ -16474,7 +15857,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16522,7 +15904,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16638,7 +16019,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 3072, "max_output_tokens": 3072, - "max_tokens": 3072, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16648,7 +16028,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 2048, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16658,7 +16037,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16668,7 +16046,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16678,7 +16055,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-07, "rpm": 300, @@ -16690,7 +16066,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16700,7 +16075,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 80000, "max_output_tokens": 80000, - "max_tokens": 80000, "mode": "chat", "output_cost_per_token": 3.35e-07, "rpm": 300 @@ -16710,7 +16084,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-08, "rpm": 300 @@ -16720,7 +16093,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 15000, "max_output_tokens": 15000, - "max_tokens": 15000, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16731,7 +16103,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "rpm": 20, @@ -16743,7 +16114,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 80000, "max_output_tokens": 80000, - "max_tokens": 80000, "mode": "chat", "output_cost_per_token": 4.881e-06, "rpm": 300, @@ -16754,7 +16124,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.87e-07, "rpm": 300 @@ -16764,7 +16133,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 60000, "max_output_tokens": 60000, - "max_tokens": 60000, "mode": "chat", "output_cost_per_token": 2.01e-07, "rpm": 300 @@ -16775,7 +16143,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "rpm": 20, @@ -16787,7 +16154,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-07, "rpm": 300, @@ -16799,7 +16165,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16809,7 +16174,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 24000, "max_output_tokens": 24000, - "max_tokens": 24000, "mode": "chat", "output_cost_per_token": 2.253e-06, "rpm": 300, @@ -16820,7 +16184,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1.12e-07, "rpm": 300, @@ -16831,7 +16194,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-06, "rpm": 300 @@ -16842,7 +16204,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "rpm": 20, @@ -16854,7 +16215,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.5e-06, "rpm": 300, @@ -16866,7 +16226,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.55e-07, "rpm": 300 @@ -16876,7 +16235,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.35e-07, "rpm": 300, @@ -16888,7 +16246,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 3500, "max_output_tokens": 3500, - "max_tokens": 3500, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16898,7 +16255,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3e-07, "rpm": 300, @@ -16910,7 +16266,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.55e-07, "rpm": 300, @@ -16921,7 +16276,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.76e-07, "rpm": 300, @@ -16932,7 +16286,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "rpm": 300, @@ -16944,7 +16297,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 8.5e-07, "rpm": 300, @@ -16955,7 +16307,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 24000, "max_output_tokens": 24000, - "max_tokens": 24000, "mode": "chat", "output_cost_per_token": 1e-06, "rpm": 300, @@ -16966,7 +16317,6 @@ "litellm_provider": "codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/", @@ -16978,7 +16328,6 @@ "litellm_provider": "codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/", @@ -16990,7 +16339,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17001,7 +16349,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17011,7 +16358,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17021,7 +16367,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17031,7 +16376,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17042,7 +16386,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17053,7 +16396,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17064,7 +16406,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17076,7 +16417,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048, @@ -17087,7 +16427,6 @@ "input_cost_per_token": 8e-08, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048, @@ -17109,7 +16448,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-cohere-rerank-3-5.md" @@ -17119,7 +16457,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -17131,7 +16468,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "source": "https://docs.cohere.com/docs/models" }, @@ -17140,7 +16476,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.cohere.com/docs/command-a-plus", @@ -17155,7 +16490,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06 }, @@ -17164,7 +16498,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -17175,7 +16508,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -17186,7 +16518,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.cohere.com/v2/docs/command-r7b", @@ -17199,7 +16530,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17222,7 +16553,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17245,7 +16576,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17268,7 +16599,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17291,7 +16622,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17314,7 +16645,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17337,7 +16668,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "supported_endpoints": [ @@ -17367,7 +16697,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "supported_endpoints": [ @@ -17398,7 +16727,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -17420,7 +16748,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -17442,7 +16769,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17458,7 +16784,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17474,7 +16799,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17490,7 +16814,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17506,7 +16829,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17522,7 +16844,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17538,7 +16859,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17550,7 +16870,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17579,7 +16898,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17609,7 +16927,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17622,7 +16939,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17635,7 +16951,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17648,7 +16963,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -17662,7 +16976,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -17675,7 +16988,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17706,7 +17018,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17737,7 +17048,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17769,7 +17079,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17783,7 +17092,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17796,7 +17104,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17810,7 +17117,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17823,7 +17129,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17834,7 +17139,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17883,7 +17187,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17928,7 +17231,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17977,7 +17279,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18022,7 +17323,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18059,7 +17359,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18096,7 +17395,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18134,7 +17432,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18146,7 +17443,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18159,7 +17455,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18172,7 +17467,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18186,7 +17480,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18199,7 +17492,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18212,7 +17504,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18250,7 +17541,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18284,7 +17574,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18298,7 +17587,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18334,7 +17622,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18352,7 +17639,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -18371,7 +17657,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -18390,7 +17675,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18711,7 +17995,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.qwencloud.com/models", @@ -18727,7 +18010,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.qwencloud.com/models", @@ -18743,7 +18025,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.qwencloud.com/models", @@ -18759,7 +18040,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.qwencloud.com/models", @@ -18775,7 +18055,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.qwencloud.com/models", @@ -18791,7 +18070,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.qwencloud.com/models", @@ -18807,7 +18085,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.qwencloud.com/models", @@ -18819,7 +18096,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18848,7 +18124,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18878,7 +18153,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.qwencloud.com/models", @@ -18891,7 +18165,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -18904,7 +18177,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -18917,7 +18189,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -18931,7 +18202,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -18944,7 +18214,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18975,7 +18244,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19006,7 +18274,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19038,7 +18305,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19052,7 +18318,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.qwencloud.com/models", @@ -19065,7 +18330,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19079,7 +18343,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19092,7 +18355,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19103,7 +18365,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19152,7 +18413,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19197,7 +18457,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19246,7 +18505,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19291,7 +18549,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19328,7 +18585,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19365,7 +18621,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19403,7 +18658,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -19415,7 +18669,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -19428,7 +18681,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.qwencloud.com/models", @@ -19441,7 +18693,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.qwencloud.com/models", @@ -19455,7 +18706,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.qwencloud.com/models", @@ -19468,7 +18718,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.qwencloud.com/models", @@ -19481,7 +18730,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19519,7 +18767,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19553,7 +18800,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.qwencloud.com/models", @@ -19567,7 +18813,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19603,7 +18848,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.qwencloud.com/models", @@ -19619,7 +18863,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.qwencloud.com/models", @@ -19940,7 +19183,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19956,7 +19198,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19972,7 +19213,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19988,7 +19228,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20004,7 +19243,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20020,7 +19258,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20036,7 +19273,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20048,7 +19284,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20077,7 +19312,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20107,7 +19341,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20120,7 +19353,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20133,7 +19365,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20146,7 +19377,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -20160,7 +19390,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -20173,7 +19402,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20204,7 +19432,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20235,7 +19462,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20267,7 +19493,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20281,7 +19506,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20294,7 +19518,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20308,7 +19531,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20321,7 +19543,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20332,7 +19553,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20381,7 +19601,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20426,7 +19645,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20475,7 +19693,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20520,7 +19737,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20557,7 +19773,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20594,7 +19809,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20632,7 +19846,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20644,7 +19857,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20657,7 +19869,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20670,7 +19881,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20684,7 +19894,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20697,7 +19906,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20710,7 +19918,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20748,7 +19955,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20782,7 +19988,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20796,7 +20001,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20832,7 +20036,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20850,7 +20053,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -20869,7 +20071,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -20888,7 +20089,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -21210,7 +20410,6 @@ "input_dbu_cost_per_token": 1.429e-06, "litellm_provider": "databricks", "max_input_tokens": 512, - "max_tokens": 512, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21228,7 +20427,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21257,7 +20455,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21287,7 +20484,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21311,7 +20507,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21335,7 +20530,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21359,7 +20553,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21384,7 +20577,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21409,7 +20601,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21436,7 +20627,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21464,7 +20654,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21493,7 +20682,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21528,7 +20716,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21552,7 +20739,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21575,7 +20761,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21599,7 +20784,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21624,7 +20808,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields. Introductory launch rates of 28.571 input / 142.857 output / 35.714 cache write / 2.857 cache read DBU run through 2026-08-31; the standard rates are listed here because entries carry no expiry date." }, @@ -21653,7 +20836,6 @@ "token_count_framing": 97, "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference. Context/max output are the DeepSeek-published model limits (1M context, 384K max output)." }, @@ -21686,7 +20868,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference. Context/max output are the DeepSeek-published model limits (1M context, 384K max output)." }, @@ -21715,7 +20896,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21749,7 +20929,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21784,7 +20963,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21812,7 +20990,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21833,7 +21010,6 @@ "litellm_provider": "databricks", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21858,7 +21034,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21891,7 +21066,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21923,7 +21097,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21939,7 +21112,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21964,7 +21136,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21993,7 +21164,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22025,7 +21195,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22056,7 +21225,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22089,7 +21257,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22106,7 +21273,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference." }, @@ -22134,7 +21300,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22165,7 +21330,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22195,7 +21359,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22215,7 +21378,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22235,7 +21397,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22255,7 +21416,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22275,7 +21435,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22304,7 +21463,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22333,7 +21491,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22362,7 +21519,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields. Rates reflect OpenAI's promotional pricing in effect through November 21, 2026; afterwards input, cache and Batch rates are 25% higher and output rates 50% higher." }, @@ -22391,7 +21547,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22420,7 +21575,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22449,7 +21603,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22478,7 +21631,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22507,7 +21659,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22527,7 +21678,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22547,7 +21697,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22564,7 +21713,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22606,7 +21754,6 @@ "input_dbu_cost_per_token": 1.857e-06, "litellm_provider": "databricks", "max_input_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22653,7 +21800,6 @@ "token_count_framing": 100, "max_input_tokens": 1000000, "max_output_tokens": 1048576, - "max_tokens": 1048576, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference." }, @@ -22687,7 +21833,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Databricks documentation now provides both DBU costs (_dbu_cost_per_token) and dollar costs(_cost_per_token)." }, @@ -22705,7 +21850,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22722,7 +21866,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22740,7 +21883,6 @@ "litellm_provider": "databricks", "max_input_tokens": 262144, "max_output_tokens": 25000, - "max_tokens": 25000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22790,7 +21932,6 @@ "input_dbu_cost_per_token": 2.86e-07, "litellm_provider": "databricks", "max_input_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22836,7 +21977,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -23431,7 +22571,6 @@ ] }, "deepinfra/Gryphe/MythoMax-L2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 4e-07, @@ -23443,7 +22582,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/NousResearch/Hermes-3-Llama-3.1-405B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-06, @@ -23454,7 +22592,6 @@ "supports_function_calling": true }, "deepinfra/NousResearch/Hermes-3-Llama-3.1-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 7e-07, @@ -23465,7 +22602,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/QwQ-32B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -23476,7 +22612,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen2.5-72B-Instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 3.6e-07, @@ -23488,7 +22623,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen2.5-7B-Instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 4e-08, @@ -23498,7 +22632,6 @@ "supports_tool_choice": false }, "deepinfra/Qwen/Qwen2.5-VL-32B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -23510,7 +22643,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-14B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.2e-07, @@ -23522,7 +22654,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-235B-A22B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.8e-07, @@ -23533,7 +22664,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-235B-A22B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-08, @@ -23545,7 +22675,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-235B-A22B-Thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -23556,7 +22685,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-30B-A3B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.2e-07, @@ -23568,7 +22696,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-32B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 8e-08, @@ -23580,7 +22707,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 4e-07, @@ -23591,7 +22717,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -23605,7 +22730,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Next-80B-A3B-Instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-08, @@ -23617,7 +22741,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Next-80B-A3B-Thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.4e-07, @@ -23628,7 +22751,6 @@ "supports_function_calling": true }, "deepinfra/Sao10K/L3-8B-Lunaris-v1-Turbo": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -23638,7 +22760,6 @@ "supports_tool_choice": false }, "deepinfra/Sao10K/L3.1-70B-Euryale-v2.2": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 8.5e-07, @@ -23649,7 +22770,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Sao10K/L3.3-70B-Euryale-v2.3": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6.5e-07, @@ -23659,7 +22779,6 @@ "supports_tool_choice": false }, "deepinfra/allenai/olmOCR-7B-0725-FP8": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2.7e-07, @@ -23669,7 +22788,6 @@ "supports_tool_choice": false }, "deepinfra/anthropic/claude-3-7-sonnet-latest": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 3.3e-06, @@ -23681,7 +22799,6 @@ "supports_function_calling": true }, "deepinfra/anthropic/claude-4-opus": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 1.65e-05, @@ -23692,7 +22809,6 @@ "supports_function_calling": true }, "deepinfra/anthropic/claude-4-sonnet": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 3.3e-06, @@ -23703,7 +22819,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 7e-07, @@ -23714,7 +22829,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-0528": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -23726,7 +22840,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-0528-Turbo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-06, @@ -23737,7 +22850,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -23747,7 +22859,6 @@ "supports_tool_choice": false }, "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.7e-07, @@ -23758,7 +22869,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-Turbo": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-06, @@ -23769,7 +22879,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-V3": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 3.2e-07, @@ -23781,7 +22890,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3-0324": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.4e-07, @@ -23795,7 +22903,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.5e-07, @@ -23809,7 +22916,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.1-Terminus": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.7e-07, @@ -23821,7 +22927,6 @@ "supports_function_calling": true }, "deepinfra/google/gemini-2.5-flash": { - "max_tokens": 1000000, "max_input_tokens": 1000000, "max_output_tokens": 1000000, "input_cost_per_token": 3e-07, @@ -23837,7 +22942,6 @@ "supports_audio_input": true }, "deepinfra/google/gemini-2.5-pro": { - "max_tokens": 1000000, "max_input_tokens": 1000000, "max_output_tokens": 1000000, "input_cost_per_token": 1.25e-06, @@ -23852,7 +22956,6 @@ "supports_audio_input": true }, "deepinfra/google/gemma-3-12b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-08, @@ -23864,7 +22967,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-3-27b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 8e-08, @@ -23876,7 +22978,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-3-4b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-08, @@ -23888,7 +22989,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-3.2-11B-Vision-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4.9e-08, @@ -23898,7 +22998,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Llama-3.2-3B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -23909,7 +23008,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.3e-07, @@ -23920,7 +23018,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Llama-3.3-70B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -23932,7 +23029,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 2e-07, @@ -23944,7 +23040,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-4-Scout-17B-16E-Instruct": { - "max_tokens": 327680, "max_input_tokens": 327680, "max_output_tokens": 327680, "input_cost_per_token": 1e-07, @@ -23956,7 +23051,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-Guard-3-8B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5.5e-08, @@ -23966,7 +23060,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Llama-Guard-4-12B": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.8e-07, @@ -23976,7 +23069,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -23987,7 +23079,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -23998,7 +23089,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -24010,7 +23100,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -24021,7 +23110,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -24033,7 +23121,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/microsoft/WizardLM-2-8x22B": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 4.8e-07, @@ -24043,7 +23130,6 @@ "supports_tool_choice": false }, "deepinfra/microsoft/phi-4": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 7e-08, @@ -24054,7 +23140,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mistral-Nemo-Instruct-2407": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.9e-08, @@ -24066,7 +23151,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/mistralai/Mistral-Small-24B-Instruct-2501": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-08, @@ -24077,7 +23161,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mistral-Small-3.2-24B-Instruct-2506": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 7.5e-08, @@ -24088,7 +23171,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mixtral-8x7B-Instruct-v0.1": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 4e-07, @@ -24099,7 +23181,6 @@ "supports_function_calling": true }, "deepinfra/moonshotai/Kimi-K2-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-07, @@ -24110,7 +23191,6 @@ "supports_function_calling": true }, "deepinfra/moonshotai/Kimi-K2-Instruct-0905": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-07, @@ -24122,7 +23202,6 @@ "supports_function_calling": true }, "deepinfra/nvidia/Llama-3.1-Nemotron-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-07, @@ -24133,7 +23212,6 @@ "supports_function_calling": true }, "deepinfra/nvidia/Llama-3.3-Nemotron-Super-49B-v1.5": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -24157,7 +23235,6 @@ "supports_prompt_caching": true }, "deepinfra/nvidia/NVIDIA-Nemotron-Nano-9B-v2": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-08, @@ -24168,7 +23245,6 @@ "supports_function_calling": true }, "deepinfra/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3.7e-08, @@ -24180,7 +23256,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/openai/gpt-oss-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -24192,7 +23267,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.5": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -24211,7 +23285,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -24234,7 +23307,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07, "supports_assistant_prefill": true, @@ -24249,7 +23321,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.19e-06, "supports_assistant_prefill": true, @@ -24266,7 +23337,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -24291,7 +23361,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.1e-06, "supports_assistant_prefill": true, @@ -24306,7 +23375,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 4e-07, "supports_assistant_prefill": true, @@ -24321,7 +23389,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-v3-1.html", @@ -24338,7 +23405,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 164000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_batches": 9.25e-07, @@ -24358,7 +23424,6 @@ "litellm_provider": "nlp_cloud", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "completion", "output_cost_per_token": 5e-07 }, @@ -24367,7 +23432,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 98304, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24381,7 +23445,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24395,7 +23458,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 229376, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24408,7 +23470,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - standard version with 2560 dimensions" }, @@ -24420,7 +23481,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - large version with 2048 dimensions" }, @@ -24432,7 +23492,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-240915 version with 4096 dimensions" }, @@ -24444,7 +23503,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-250515 version with 2048 dimensions" }, @@ -24456,7 +23514,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-240715 version with 2560 dimensions" }, @@ -24569,7 +23626,6 @@ "mode": "search" }, "sail/moonshotai/Kimi-K3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 2.5e-06, @@ -24591,7 +23647,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/zai-org/GLM-5.3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9.8e-07, @@ -24613,7 +23668,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/zai-org/GLM-5.3-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.1e-07, @@ -24635,7 +23689,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4-Pro-0813": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9.2e-07, @@ -24657,7 +23710,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9e-08, @@ -24679,7 +23731,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4.1-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9e-08, @@ -24701,7 +23752,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-06, @@ -24724,7 +23774,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/google/gemma-4-31B-it": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 4e-07, @@ -24747,7 +23796,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/nvidia/Gemma-4-31B-IT-NVFP4": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.4e-07, @@ -24770,7 +23818,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/google/gemma-4-12B-it": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 3e-07, @@ -24792,7 +23839,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-08, @@ -24808,7 +23854,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/Qwen/Qwen3.6-35B-A3B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-08, @@ -25047,7 +24092,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -25056,7 +24100,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "metadata": { "notes": "'supports_image_input' is a deprecated field. Use 'supports_embedding_image_input' instead." }, @@ -25069,7 +24112,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "supports_embedding_image_input": true @@ -25078,7 +24120,6 @@ "input_cost_per_token": 0.0001, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "supports_embedding_image_input": true @@ -25089,7 +24130,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.12e-07, "supports_function_calling": true, @@ -25105,7 +24145,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.84e-07, "supports_function_calling": true, @@ -25121,7 +24160,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 4.2e-06, "output_cost_per_token_flex": 2.1e-06, @@ -25138,7 +24176,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -25161,7 +24198,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -25189,7 +24225,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -25205,7 +24240,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -25224,7 +24258,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -25244,7 +24277,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -25261,7 +24293,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -25292,7 +24323,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -25323,7 +24353,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -25363,7 +24392,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -25397,7 +24425,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.3e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -25410,7 +24437,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -25422,7 +24448,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-pixtral-large.md", @@ -27688,14 +26713,12 @@ "litellm_provider": "featherless_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat" }, "featherless_ai/featherless-ai/Qwerky-QwQ-32B": { "litellm_provider": "featherless_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat" }, "fireworks-ai-4.1b-to-16b": { @@ -27743,7 +26766,6 @@ "input_cost_per_token": 1.6e-08, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -27753,7 +26775,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/pricing", @@ -27766,7 +26787,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 20480, - "max_tokens": 20480, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://fireworks.ai/pricing", @@ -27778,7 +26798,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 160000, - "max_tokens": 160000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://fireworks.ai/pricing", @@ -27790,7 +26809,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 20480, - "max_tokens": 20480, "mode": "chat", "output_cost_per_token": 2.19e-06, "source": "https://fireworks.ai/pricing", @@ -27802,7 +26820,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -27814,7 +26831,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/models/fireworks/deepseek-v3-0324", @@ -27826,7 +26842,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/pricing", @@ -27839,7 +26854,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/pricing", @@ -27852,7 +26866,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/models/fireworks/deepseek-v3p2", @@ -27867,7 +26880,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -27886,7 +26898,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.96e-06, "output_cost_per_token_priority": 4.95e-06, @@ -27906,7 +26917,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.96e-06, "output_cost_per_token_priority": 4.95e-06, @@ -27922,7 +26932,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -27935,7 +26944,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 2.19e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p5", @@ -27949,7 +26957,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://artificialanalysis.ai/models/glm-4-5-air", @@ -27964,7 +26971,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "source": "https://fireworks.ai/pricing", "supports_function_calling": true, @@ -27978,7 +26984,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p7", @@ -27993,7 +26998,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28012,7 +27016,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -28032,7 +27035,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 7.2e-07, @@ -28049,7 +27051,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28064,7 +27065,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://fireworks.ai/models/fireworks/kimi-k2-instruct", @@ -28077,7 +27077,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://app.fireworks.ai/models/fireworks/kimi-k2-instruct-0905", @@ -28090,7 +27089,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://fireworks.ai/pricing", @@ -28105,7 +27103,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28122,7 +27119,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28142,7 +27138,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28158,7 +27153,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28171,7 +27165,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28184,7 +27177,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://fireworks.ai/pricing", @@ -28198,7 +27190,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28211,7 +27202,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28224,7 +27214,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28237,7 +27226,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://fireworks.ai/pricing", @@ -28249,7 +27237,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://fireworks.ai/pricing", @@ -28262,7 +27249,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/models/fireworks/minimax-m2p1", @@ -28278,7 +27264,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.8e-06, @@ -28294,7 +27279,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/pricing", @@ -28307,7 +27291,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28320,7 +27303,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28333,7 +27315,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28347,7 +27328,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28363,7 +27343,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p7", @@ -28378,7 +27357,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28394,7 +27372,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28413,7 +27390,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -28433,7 +27409,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 7.2e-07, @@ -28450,7 +27425,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28466,7 +27440,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28483,7 +27456,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28501,7 +27473,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28520,7 +27491,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28538,7 +27508,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28554,7 +27523,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/models/fireworks/minimax-m2p1", @@ -28570,7 +27538,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.8e-06, @@ -28587,7 +27554,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -28601,7 +27567,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28610,7 +27575,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28619,7 +27583,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28628,7 +27591,6 @@ "input_cost_per_token": 1.6e-08, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28637,7 +27599,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 3e-08, @@ -28665,7 +27626,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.26e-06, "output_cost_per_token": 3.96e-06, "cache_read_input_token_cost": 2.34e-07, @@ -28693,7 +27653,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 4e-07, "supports_prompt_caching": false, @@ -28716,7 +27675,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, @@ -28743,7 +27701,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "input_cost_per_token": 5e-07, "output_cost_per_token": 1.5e-06, "cache_read_input_token_cost": 2.5e-07, @@ -28767,7 +27724,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-08, @@ -28791,7 +27747,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 202752, "max_output_tokens": 202752, - "max_tokens": 202752, "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, @@ -28818,7 +27773,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 9e-07, @@ -28831,7 +27785,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -28844,7 +27797,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_batches": 3e-06, @@ -28858,7 +27810,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28870,7 +27821,6 @@ "litellm_provider": "openai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28882,7 +27832,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28894,7 +27843,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "OpenAI needs to add pricing for this ft model, will be updated when added by OpenAI. Defaulting to base model pricing", @@ -28911,7 +27859,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -28932,7 +27879,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -28952,7 +27898,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -28974,7 +27919,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -28995,7 +27939,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -29016,7 +27959,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "output_cost_per_token_batches": 4e-07, @@ -29037,7 +27979,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 1.6e-05, "output_cost_per_token_batches": 8e-06, @@ -29060,7 +28001,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29117,7 +28057,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -29175,7 +28114,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29229,7 +28167,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29281,7 +28218,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -29327,7 +28263,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -29376,7 +28311,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -29416,7 +28350,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -29475,7 +28408,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -29535,7 +28467,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29591,7 +28522,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29629,7 +28559,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -29684,7 +28613,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -29731,7 +28659,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29778,7 +28705,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29824,7 +28750,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29868,7 +28793,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29920,7 +28844,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -29982,7 +28905,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_image": 0.00012, "output_cost_per_token": 1.2e-05, @@ -30035,7 +28957,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30088,7 +29009,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30149,7 +29069,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -30196,7 +29115,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -30259,7 +29177,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30320,7 +29237,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30384,7 +29300,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30440,7 +29355,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 7.5e-06, "output_cost_per_token": 7.5e-06, @@ -30488,7 +29402,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -30531,7 +29444,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_image": 0.00012, "output_cost_per_token": 1.2e-05, @@ -30584,7 +29496,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30641,7 +29552,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -30674,7 +29584,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_reasoning_token": 2.5e-06, @@ -30712,7 +29621,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -30759,7 +29667,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -30783,7 +29690,6 @@ "input_cost_per_token_batches": 1.2e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30800,7 +29706,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30818,7 +29723,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30837,7 +29741,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30856,7 +29759,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30870,7 +29772,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30882,7 +29783,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "gemini", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30901,7 +29801,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "gemini", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30922,7 +29821,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -30968,7 +29866,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "image_generation", "output_cost_per_image": 0.045, "output_cost_per_image_token": 6e-05, @@ -31020,7 +29917,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -31080,7 +29976,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "gemini", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -31097,7 +29992,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "rpm": 1000, @@ -31134,7 +30028,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "rpm": 1000, @@ -31180,7 +30073,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31243,7 +30135,6 @@ "supports_reasoning": false, "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -31295,7 +30186,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -31344,7 +30234,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -31387,7 +30276,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.045, "output_cost_per_image_token": 6e-05, @@ -31429,7 +30317,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -31468,7 +30355,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -31513,7 +30399,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -31570,7 +30455,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -31629,7 +30513,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31688,7 +30571,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 1e-05, "output_cost_per_token": 1e-05, @@ -31718,7 +30600,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -31772,7 +30653,6 @@ "litellm_provider": "gemini", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -31815,7 +30695,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -31877,7 +30756,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31940,7 +30818,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 3e-06, @@ -32002,7 +30879,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32067,7 +30943,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32129,7 +31004,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32192,7 +31066,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32250,7 +31123,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32288,7 +31160,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -32352,7 +31223,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -32421,7 +31291,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 3e-06, @@ -32466,7 +31335,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32501,7 +31369,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32564,7 +31431,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32625,7 +31491,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32689,7 +31554,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32745,7 +31609,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 7.5e-06, "output_cost_per_token": 7.5e-06, @@ -32792,7 +31655,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -32827,7 +31689,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Rate limits not documented for gemini-exp-1114. Assuming same as gemini-1.5-pro.", "supports_tool_choice": true @@ -32850,7 +31711,6 @@ "litellm_provider": "gemini", "max_input_tokens": 2097152, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Rate limits not documented for gemini-exp-1206. Assuming same as gemini-1.5-pro.", "supports_tool_choice": true @@ -32871,7 +31731,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "gemini", "max_output_tokens": 8192, - "max_tokens": 8192, + "max_input_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.05e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -32885,7 +31745,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "gemini", "max_output_tokens": 8192, - "max_tokens": 8192, + "max_input_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.05e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -32909,7 +31769,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 0, "output_cost_per_character_above_128k_tokens": 0, @@ -32929,7 +31788,6 @@ "litellm_provider": "gemini", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -32945,7 +31803,6 @@ "litellm_provider": "gemini", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -32981,7 +31838,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32767, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 0, "output_cost_per_character_above_128k_tokens": 0, @@ -33000,7 +31856,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -33025,7 +31880,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -33049,7 +31903,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -33066,7 +31919,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -33082,7 +31934,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.05, "output_cost_per_second_1080p": 0.08, @@ -33097,7 +31948,6 @@ "gemini/veo-3.1-fast-generate-001": { "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -33113,7 +31963,6 @@ "gemini/veo-3.1-generate-001": { "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -33132,7 +31981,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33155,7 +32003,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33178,7 +32025,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 5e-06, "supported_endpoints": [ @@ -33199,7 +32045,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_100k_tokens": 2.5e-06, @@ -33224,7 +32069,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33247,7 +32091,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33270,7 +32113,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33292,7 +32134,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33315,7 +32156,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33337,7 +32177,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33360,7 +32199,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33379,7 +32217,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33396,7 +32233,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33404,7 +32240,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33412,7 +32247,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33420,7 +32254,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33428,7 +32261,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33438,7 +32270,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/model-deprecation-history.yml", "supports_function_calling": true, @@ -33451,7 +32282,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/model-deprecation-history.yml", "supports_function_calling": true, @@ -33467,7 +32297,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33477,7 +32306,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33487,7 +32315,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33496,7 +32323,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33506,7 +32332,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33515,7 +32340,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33527,7 +32351,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://github.blog/changelog/2026-10-19-github-copilot-models-deprecated-on-october-19-2026", @@ -33542,7 +32365,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "supported_endpoints": [ @@ -33563,7 +32385,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -33585,7 +32406,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 4.5e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33607,7 +32427,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -33631,7 +32450,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_200k_tokens": 1.8e-06, @@ -33655,7 +32473,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -33680,7 +32497,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -33704,7 +32520,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -33728,7 +32543,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -33752,7 +32566,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -33776,7 +32589,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -33796,7 +32608,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 917504, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33814,7 +32625,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33829,19 +32639,16 @@ "github_copilot/text-embedding-3-small": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "github_copilot/text-embedding-3-small-inference": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "github_copilot/text-embedding-ada-002": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "chatgpt/gpt-6-sol": { @@ -33853,7 +32660,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33874,7 +32680,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33895,7 +32700,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33916,7 +32720,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33933,7 +32736,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.5", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33953,7 +32755,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-luna", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33973,7 +32774,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-sol", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33993,7 +32793,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-terra", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34012,7 +32811,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34032,7 +32830,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34050,7 +32847,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34068,7 +32864,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34082,7 +32877,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34097,7 +32891,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34113,7 +32906,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34128,7 +32920,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34148,7 +32939,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34163,7 +32953,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34179,7 +32968,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34190,7 +32978,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34202,7 +32989,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34213,7 +32999,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -34222,7 +33007,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -34231,7 +33015,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560 @@ -34240,7 +33023,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048 @@ -34250,7 +33032,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_function_calling": true, @@ -34262,7 +33043,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -34273,7 +33053,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -34284,7 +33063,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -34295,7 +33073,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.4e-05, "supports_function_calling": true @@ -34305,7 +33082,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true @@ -34315,7 +33091,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true @@ -34325,7 +33100,6 @@ "litellm_provider": "gmi", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -34336,7 +33110,6 @@ "litellm_provider": "gmi", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -34347,7 +33120,6 @@ "litellm_provider": "gmi", "max_input_tokens": 163840, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true @@ -34357,7 +33129,6 @@ "litellm_provider": "gmi", "max_input_tokens": 163840, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 8.8e-07, "supports_function_calling": true @@ -34367,7 +33138,6 @@ "litellm_provider": "gmi", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -34379,7 +33149,6 @@ "litellm_provider": "gmi", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -34388,7 +33157,6 @@ "litellm_provider": "gmi", "max_input_tokens": 196608, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -34420,7 +33188,6 @@ "litellm_provider": "baseten", "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -34465,7 +33232,6 @@ "litellm_provider": "baseten", "max_input_tokens": 128072, "max_output_tokens": 128072, - "max_tokens": 128072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -34495,7 +33261,6 @@ "litellm_provider": "gmi", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-06, "supports_vision": true @@ -34505,7 +33270,6 @@ "litellm_provider": "gmi", "max_input_tokens": 202752, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2e-06 }, @@ -34518,7 +33282,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.9e-07, "output_cost_per_token_batches": 1.5e-07, @@ -34540,7 +33303,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 3.8e-07, "output_cost_per_token_batches": 1.9e-07, @@ -34562,7 +33324,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8e-08, "output_cost_per_token_batches": 4e-08, @@ -34594,7 +33355,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -34634,7 +33394,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -34668,7 +33427,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -34700,7 +33458,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -34721,7 +33478,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://developers.openai.com/api/docs/pricing", @@ -34737,7 +33493,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -34755,7 +33510,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 2e-06, @@ -34772,7 +33526,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "supports_prompt_caching": true, @@ -34785,7 +33538,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06, "source": "https://developers.openai.com/api/docs/pricing" @@ -34795,7 +33547,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 8192, "max_output_tokens": 4097, - "max_tokens": 4097, "mode": "completion", "output_cost_per_token": 2e-06 }, @@ -34805,7 +33556,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "supports_function_calling": true, @@ -34820,7 +33570,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -34836,7 +33585,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -34851,7 +33599,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -34869,7 +33616,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_batches": 1.5e-05, @@ -34891,7 +33637,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -34935,7 +33680,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_priority": 1.4e-05, @@ -34978,7 +33722,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -35022,7 +33765,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_priority": 2.8e-06, @@ -35066,7 +33808,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -35104,7 +33845,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_priority": 8e-07, @@ -35141,7 +33881,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -35164,7 +33903,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -35188,7 +33926,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 1.7e-05, @@ -35213,7 +33950,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 1.7e-05, @@ -35235,7 +33971,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -35253,7 +33988,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -35271,7 +34005,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35308,7 +34041,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35343,7 +34075,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35381,7 +34112,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35419,7 +34149,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35456,7 +34185,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35497,7 +34225,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35527,7 +34254,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 1e-06, @@ -35554,7 +34280,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 6e-07, @@ -35574,7 +34299,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35592,7 +34316,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35619,7 +34342,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35700,7 +34422,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -35727,7 +34448,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -36281,7 +35001,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -36327,7 +35046,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5-codex", @@ -36362,7 +35080,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -36412,7 +35129,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex-mini", @@ -36443,7 +35159,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex-max", @@ -36475,7 +35190,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex", @@ -36507,7 +35221,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-chat-latest", @@ -36542,7 +35255,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -36594,7 +35306,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -36645,7 +35356,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.2-codex", @@ -36677,7 +35387,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.2-chat-latest", @@ -36712,7 +35421,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -36762,7 +35470,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -36804,7 +35511,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -36884,7 +35590,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -36971,7 +35676,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -37062,7 +35766,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_decisions": 0.0, @@ -37148,7 +35851,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37230,7 +35932,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37314,7 +36015,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -37397,7 +36097,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -37455,7 +36154,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37494,7 +36192,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37533,7 +36230,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37571,7 +36267,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37610,7 +36305,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37644,7 +36338,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -37690,7 +36383,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -37753,7 +36445,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -37809,7 +36500,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -37861,7 +36551,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -37920,7 +36609,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -37978,7 +36666,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -38029,7 +36716,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -38082,7 +36768,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -38138,7 +36823,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_flex": 2.25e-06, @@ -38193,7 +36877,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_flex": 2.25e-06, @@ -38246,7 +36929,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, @@ -38298,7 +36980,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, @@ -38345,7 +37026,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 272000, - "max_tokens": 272000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -38388,7 +37068,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 272000, - "max_tokens": 272000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -38437,7 +37116,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -38483,7 +37161,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -38519,7 +37196,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5-chat-latest", @@ -38552,7 +37228,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -38596,7 +37271,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.3-chat-latest", @@ -38633,7 +37307,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -38685,7 +37358,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -38735,7 +37407,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -38785,7 +37456,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -38871,7 +37541,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -38906,7 +37575,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -38941,7 +37609,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -38976,7 +37643,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -39013,7 +37679,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -39051,7 +37716,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -39087,7 +37751,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -39113,7 +37776,6 @@ }, "gradient_ai/alibaba-qwen3-32b": { "litellm_provider": "gradient_ai", - "max_tokens": 40960, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39128,7 +37790,6 @@ "gradient_ai/anthropic-claude-3-opus": { "input_cost_per_token": 1.5e-05, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 7.5e-05, "supported_endpoints": [ @@ -39144,7 +37805,6 @@ "gradient_ai/anthropic-claude-3.5-haiku": { "input_cost_per_token": 8e-07, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 4e-06, "supported_endpoints": [ @@ -39160,7 +37820,6 @@ "gradient_ai/anthropic-claude-3.5-sonnet": { "input_cost_per_token": 3e-06, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.5e-05, "supported_endpoints": [ @@ -39176,7 +37835,6 @@ "gradient_ai/anthropic-claude-3.7-sonnet": { "input_cost_per_token": 3e-06, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.5e-05, "supported_endpoints": [ @@ -39192,7 +37850,6 @@ "gradient_ai/deepseek-r1-distill-llama-70b": { "input_cost_per_token": 9.9e-07, "litellm_provider": "gradient_ai", - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 9.9e-07, "supported_endpoints": [ @@ -39208,7 +37865,6 @@ "gradient_ai/llama3-8b-instruct": { "input_cost_per_token": 2e-07, "litellm_provider": "gradient_ai", - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 2e-07, "supported_endpoints": [ @@ -39224,7 +37880,6 @@ "gradient_ai/llama3.3-70b-instruct": { "input_cost_per_token": 6.5e-07, "litellm_provider": "gradient_ai", - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 6.5e-07, "supported_endpoints": [ @@ -39240,7 +37895,6 @@ "gradient_ai/mistral-nemo-instruct-2407": { "input_cost_per_token": 3e-07, "litellm_provider": "gradient_ai", - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 3e-07, "supported_endpoints": [ @@ -39255,7 +37909,6 @@ }, "gradient_ai/openai-gpt-4o": { "litellm_provider": "gradient_ai", - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39269,7 +37922,6 @@ }, "gradient_ai/openai-gpt-4o-mini": { "litellm_provider": "gradient_ai", - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39284,7 +37936,6 @@ "gradient_ai/openai-o3": { "input_cost_per_token": 2e-06, "litellm_provider": "gradient_ai", - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supported_endpoints": [ @@ -39300,7 +37951,6 @@ "gradient_ai/openai-o3-mini": { "input_cost_per_token": 1.1e-06, "litellm_provider": "gradient_ai", - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supported_endpoints": [ @@ -39316,7 +37966,6 @@ "lemonade/Qwen3-Coder-30B-A3B-Instruct-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 262144, "max_output_tokens": 32768, "mode": "chat", @@ -39328,7 +37977,6 @@ "lemonade/gpt-oss-20b-mxfp4-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 131072, "max_output_tokens": 32768, "mode": "chat", @@ -39340,7 +37988,6 @@ "lemonade/gpt-oss-120b-mxfp-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 131072, "max_output_tokens": 32768, "mode": "chat", @@ -39352,7 +37999,6 @@ "lemonade/Gemma-3-4b-it-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "mode": "chat", @@ -39364,7 +38010,6 @@ "lemonade/Qwen3-4B-Instruct-2507-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 262144, "max_output_tokens": 32768, "mode": "chat", @@ -39378,7 +38023,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "supports_function_calling": true, @@ -39390,7 +38034,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "supports_function_calling": true, @@ -39404,7 +38047,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 1000000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.25e-05, "supports_function_calling": true, @@ -39418,7 +38060,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "supports_function_calling": true, @@ -39432,7 +38073,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "groq", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://console.groq.com/docs/model/llama-guard-3-8b" @@ -39442,7 +38083,6 @@ "litellm_provider": "groq", "max_input_tokens": 512, "max_output_tokens": 512, - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 3e-08, "source": "https://console.groq.com/docs/models" @@ -39452,7 +38092,6 @@ "litellm_provider": "groq", "max_input_tokens": 512, "max_output_tokens": 512, - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 4e-08, "source": "https://console.groq.com/docs/model/meta-llama/llama-prompt-guard-2-86m" @@ -39463,7 +38102,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 6e-07, "search_context_cost_per_query": { @@ -39484,7 +38122,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "search_context_cost_per_query": { @@ -39505,7 +38142,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "search_context_cost_per_query": { @@ -39524,7 +38160,6 @@ "litellm_provider": "groq", "max_input_tokens": 4000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "audio_speech", "source": "https://console.groq.com/docs/model/canopylabs/orpheus-v1-english" }, @@ -39533,7 +38168,6 @@ "litellm_provider": "groq", "max_input_tokens": 4000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "audio_speech", "source": "https://console.groq.com/docs/models" }, @@ -39551,7 +38185,6 @@ }, "heroku/claude-3-5-haiku": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39561,7 +38194,6 @@ }, "heroku/claude-3-5-sonnet-latest": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39571,7 +38203,6 @@ }, "heroku/claude-3-7-sonnet": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39581,7 +38212,6 @@ }, "heroku/claude-4-sonnet": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39627,7 +38257,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39640,7 +38269,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -39653,7 +38281,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39666,7 +38293,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39679,7 +38305,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "supports_function_calling": true, @@ -39692,7 +38317,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true, @@ -39705,7 +38329,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_function_calling": true, @@ -39718,7 +38341,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -39731,7 +38353,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true, @@ -39744,7 +38365,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39757,7 +38377,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39770,7 +38389,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39783,7 +38401,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39796,7 +38413,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39809,7 +38425,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39822,7 +38437,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "supports_function_calling": true, @@ -39835,7 +38449,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 3e-06 }, @@ -39844,7 +38457,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 1e-05 }, @@ -39853,7 +38465,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 1.5e-05 }, @@ -39862,7 +38473,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39872,7 +38482,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39882,7 +38491,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39892,7 +38500,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39902,7 +38509,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39912,7 +38518,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39922,7 +38527,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39932,7 +38536,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39942,7 +38545,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39952,7 +38554,6 @@ "litellm_provider": "jina_ai", "max_input_tokens": 1024, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://api.jina.ai/v1/models" @@ -39972,7 +38573,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -40009,7 +38609,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -40037,7 +38636,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 7e-06, "supports_function_calling": false, @@ -40050,7 +38648,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -40063,7 +38660,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -40077,7 +38673,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -40090,7 +38685,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "supports_function_calling": false, @@ -40103,7 +38697,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40116,7 +38709,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -40130,7 +38722,6 @@ "litellm_provider": "inception", "max_input_tokens": 128000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "chat", "output_cost_per_token": 7.5e-07, "supports_function_calling": true, @@ -40145,7 +38736,6 @@ "litellm_provider": "inception", "max_input_tokens": 260000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://docs.inceptionlabs.ai/get-started/models", @@ -40160,7 +38750,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40181,7 +38771,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40202,7 +38792,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40223,7 +38813,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40244,7 +38834,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40265,7 +38855,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40286,7 +38876,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40307,7 +38897,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40328,7 +38918,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40349,7 +38939,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40370,7 +38960,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40391,7 +38981,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40412,7 +39002,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40433,7 +39023,7 @@ "input_cost_per_token": 1.82e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 5.72e-06, "source": "https://inferenceapis.com/pricing", @@ -40454,7 +39044,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://inferenceapis.com/pricing", @@ -40474,7 +39064,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inferenceapis.com/pricing", @@ -40493,7 +39083,7 @@ "input_cost_per_token": 4e-08, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://inferenceapis.com/pricing", @@ -40513,7 +39103,7 @@ "input_cost_per_token": 8.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.42e-06, "source": "https://inferenceapis.com/pricing", @@ -40533,7 +39123,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40552,7 +39142,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40571,7 +39161,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40590,7 +39180,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40609,7 +39199,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40628,7 +39218,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40648,7 +39238,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://inferenceapis.com/pricing", @@ -40669,7 +39259,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://inferenceapis.com/pricing", @@ -40690,7 +39280,7 @@ "input_cost_per_token": 8.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.42e-06, "source": "https://inferenceapis.com/pricing", @@ -40710,7 +39300,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inferenceapis.com/pricing", @@ -40729,7 +39319,7 @@ "input_cost_per_token": 4e-08, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://inferenceapis.com/pricing", @@ -40748,7 +39338,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40768,7 +39358,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40788,7 +39378,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40807,7 +39397,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40827,7 +39417,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40848,7 +39438,7 @@ "input_cost_per_token": 1.82e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 5.72e-06, "source": "https://inferenceapis.com/pricing", @@ -40869,7 +39459,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://inferenceapis.com/pricing", @@ -40891,7 +39481,6 @@ "litellm_provider": "text-completion-inception", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 7.5e-07 }, @@ -40900,7 +39489,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40914,7 +39502,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40928,7 +39515,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40942,7 +39528,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40955,7 +39540,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40968,7 +39552,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -40981,7 +39564,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -40994,7 +39576,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -41007,7 +39588,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -41020,7 +39600,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41033,7 +39612,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 16384, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41046,7 +39624,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -41059,7 +39636,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41072,7 +39648,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -41085,7 +39660,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41098,7 +39672,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-08, "supports_function_calling": true, @@ -41112,7 +39685,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-08, "supports_function_calling": true, @@ -41125,7 +39697,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41138,7 +39709,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41151,7 +39721,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41196,14 +39765,12 @@ "max-x-max/50-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.036 }, "max-x-max/max-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.072 }, @@ -41299,7 +39866,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_character": 1.5e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -41310,7 +39876,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 1e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -41321,7 +39886,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -41331,7 +39895,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.56e-06, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -41342,7 +39905,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41356,7 +39918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -41371,7 +39932,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.2e-07, "output_cost_per_token_batches": 1.1e-07, @@ -41385,7 +39945,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41399,7 +39958,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -41412,7 +39970,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -41425,7 +39982,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41440,7 +39996,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -41454,7 +40009,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -41465,7 +40019,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -41477,7 +40030,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9.7e-07, "output_cost_per_token_batches": 4.85e-07, @@ -41500,7 +40052,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 10000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_batches": 3.3e-07, @@ -41522,7 +40073,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41563,7 +40113,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41604,7 +40153,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "search_context_cost_per_query": { @@ -41645,7 +40193,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41686,7 +40233,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "search_context_cost_per_query": { @@ -41743,7 +40289,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -41784,7 +40329,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 128000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41800,7 +40344,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 128000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41816,7 +40359,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 1000000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41833,7 +40375,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 10000000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41855,7 +40396,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -41878,7 +40418,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 196000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -41902,7 +40441,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 196000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, "output_cost_per_token_priority": 2.1e-06, @@ -42100,7 +40638,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -42122,7 +40659,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 40000, - "max_tokens": 40000, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -42144,7 +40680,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_batches": 1e-07, @@ -42166,7 +40701,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-07, "output_cost_per_token_batches": 5e-08, @@ -42188,7 +40722,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-07, "output_cost_per_token_batches": 7e-08, @@ -42208,7 +40741,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -42219,7 +40751,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -42230,7 +40761,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -42245,7 +40775,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -42265,7 +40794,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -42278,7 +40806,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -42293,7 +40820,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-08, "output_cost_per_token_batches": 2e-08, @@ -42313,7 +40839,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-07, "output_cost_per_token_batches": 1.5e-07, @@ -42330,7 +40855,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -42345,7 +40869,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -42360,7 +40883,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://mistral.ai/technology/", @@ -42373,7 +40895,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.mistral.ai/models/devstral-small-2-25-12", @@ -42388,7 +40909,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -42403,7 +40923,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -42417,7 +40936,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.mistral.ai/models/ministral-3-14b-25-12", @@ -42433,7 +40951,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.mistral.ai/models/ministral-3-14b-25-12", @@ -42449,7 +40966,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.mistral.ai/models/ministral-3-3b-25-12", @@ -42465,7 +40981,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.mistral.ai/models/ministral-3-3b-25-12", @@ -42480,7 +40995,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "source": "https://docs.mistral.ai/models/mistral-embed-23-12" }, @@ -42490,7 +41004,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -42671,7 +41184,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.mistral.ai/models/voxtral-small-25-07", @@ -42686,7 +41198,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.mistral.ai/models/voxtral-small-25-07", @@ -42702,7 +41213,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42728,7 +41238,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42750,7 +41259,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "reasoning_effort_levels": [ @@ -42773,7 +41281,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42795,7 +41302,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42817,7 +41323,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42896,7 +41401,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.mistral.ai/models/model-cards/mistral-medium-3-5-26-04", @@ -42913,7 +41417,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.mistral.ai/models/model-cards/mistral-small-4-0-26-03", @@ -42928,7 +41431,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/codestral-embed": { @@ -42936,7 +41438,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/codestral-embed-2505": { @@ -42944,7 +41445,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/mistral-large-4": { @@ -42952,7 +41452,7 @@ "input_cost_per_token": 6.8e-07, "litellm_provider": "mistral", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.09e-06, "reasoning_effort_levels": [ @@ -42973,7 +41473,7 @@ "input_cost_per_token": 6.8e-07, "litellm_provider": "mistral", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.09e-06, "reasoning_effort_levels": [ @@ -42995,7 +41495,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43011,7 +41510,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43027,7 +41525,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43043,7 +41540,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43064,7 +41560,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43085,7 +41580,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43106,7 +41600,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43127,7 +41620,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-07, "supports_assistant_prefill": true, @@ -43141,7 +41633,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -43162,7 +41653,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://mistral.ai/pricing", @@ -43178,7 +41668,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43194,7 +41683,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://mistral.ai/pricing", @@ -43210,7 +41698,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43226,7 +41713,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43242,7 +41728,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_assistant_prefill": true, @@ -43255,7 +41740,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://mistral.ai/technology/", @@ -43269,7 +41753,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_assistant_prefill": true, @@ -43286,7 +41769,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -43308,7 +41790,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -43328,7 +41809,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://platform.kimi.ai/docs/pricing/chat-k27-code", @@ -43346,7 +41826,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/guide/kimi-k2-5-quickstart", @@ -43363,7 +41842,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://platform.kimi.ai/docs/pricing/chat-k26", @@ -43380,7 +41858,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -43401,7 +41878,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43414,7 +41890,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43428,7 +41903,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43441,7 +41915,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43455,7 +41928,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43468,7 +41940,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43482,7 +41953,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43495,7 +41965,6 @@ "litellm_provider": "morph", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": false, @@ -43509,7 +41978,6 @@ "litellm_provider": "morph", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.9e-06, "supports_function_calling": false, @@ -43527,7 +41995,6 @@ "input_cost_per_video_per_second_above_8s_interval": 0.001, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -43551,7 +42018,6 @@ "input_cost_per_video_per_second_above_8s_interval": 0.001, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -43711,7 +42177,6 @@ ] }, "nebius/deepseek-ai/DeepSeek-R1": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 8e-07, @@ -43723,7 +42188,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-R1-0528": { - "max_tokens": 164000, "max_input_tokens": 164000, "max_output_tokens": 164000, "input_cost_per_token": 8e-07, @@ -43735,7 +42199,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2.5e-07, @@ -43746,7 +42209,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V3": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 5e-07, @@ -43757,7 +42219,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V3-0324": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 5e-07, @@ -43768,7 +42229,6 @@ "source": "https://nebius.com/prices" }, "nebius/google/gemma-3-27b-it": { - "max_tokens": 110000, "max_input_tokens": 110000, "max_output_tokens": 110000, "input_cost_per_token": 1e-07, @@ -43780,7 +42240,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/google%2Fgemma-3-27b-it" }, "nebius/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -43791,7 +42250,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Llama-Guard-3-8B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-08, @@ -43801,7 +42259,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-8B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-08, @@ -43812,7 +42269,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-70B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.3e-07, @@ -43823,7 +42279,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-405B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-06, @@ -43834,7 +42289,6 @@ "source": "https://nebius.com/prices" }, "nebius/mistralai/Mistral-Nemo-Instruct-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 4e-08, @@ -43845,7 +42299,6 @@ "source": "https://nebius.com/prices" }, "nebius/NousResearch/Hermes-3-Llama-3.1-405B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-06, @@ -43856,7 +42309,6 @@ "source": "https://nebius.com/prices" }, "nebius/nvidia/Llama-3.1-Nemotron-Ultra-253B-v1": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 6e-07, @@ -43867,7 +42319,6 @@ "source": "https://nebius.com/prices" }, "nebius/nvidia/Llama-3.3-Nemotron-Super-49B-v1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -43878,7 +42329,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-235B-A22B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -43889,7 +42339,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-32B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -43900,7 +42349,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-32B" }, "nebius/Qwen/Qwen3-30B-A3B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -43911,7 +42359,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-14B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 8e-08, @@ -43922,7 +42369,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-4B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 8e-08, @@ -43933,7 +42379,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/QwQ-32B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.5e-07, @@ -43945,7 +42390,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-72B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.3e-07, @@ -43956,7 +42400,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-32B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 6e-08, @@ -43967,7 +42410,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-Coder-7B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-08, @@ -43978,7 +42420,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-VL-72B-Instruct": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 2.5e-07, @@ -43990,7 +42431,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/Qwen%2FQwen2.5-VL-72B-Instruct" }, "nebius/Qwen/Qwen2-VL-72B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -44002,7 +42442,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2-VL-7B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -44013,7 +42452,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V4-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.4e-07, @@ -44025,7 +42463,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/deepseek-ai%2FDeepSeek-V4-Flash" }, "nebius/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 1.4e-07, @@ -44037,7 +42474,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/deepseek-ai%2FDeepSeek-V4-Flash-0731" }, "nebius/deepseek-ai/DeepSeek-V4-Pro": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.75e-06, @@ -44063,7 +42499,6 @@ "litellm_provider": "nebius", "max_input_tokens": 1048000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://tokenfactory.nebius.com/endpoints?modals=endpoint-details&model-id=deepseek-ai/DeepSeek-V4.1-Flash", @@ -44072,7 +42507,6 @@ "supports_vision": true }, "nebius/MiniMaxAI/MiniMax-M2.5": { - "max_tokens": 196608, "max_input_tokens": 196608, "max_output_tokens": 196608, "input_cost_per_token": 3e-07, @@ -44084,7 +42518,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/MiniMaxAI%2FMiniMax-M2.5" }, "nebius/MiniMaxAI/MiniMax-M3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 3e-07, @@ -44097,7 +42530,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/MiniMaxAI%2FMiniMax-M3" }, "nebius/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9.5e-07, @@ -44110,7 +42542,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/moonshotai%2FKimi-K2.6" }, "nebius/moonshotai/Kimi-K2.7-Code": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9.5e-07, @@ -44123,7 +42554,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/moonshotai%2FKimi-K2.7-Code" }, "nebius/moonshotai/Kimi-K3": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 3e-06, @@ -44136,7 +42566,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/moonshotai%2FKimi-K3" }, "nebius/NousResearch/Hermes-4-405B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-06, @@ -44148,7 +42577,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/NousResearch%2FHermes-4-405B" }, "nebius/NousResearch/Hermes-4-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -44160,7 +42588,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/NousResearch%2FHermes-4-70B" }, "nebius/nvidia/Cosmos3-Super-Reasoner": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -44173,7 +42600,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/nvidia%2FCosmos3-Super-Reasoner" }, "nebius/nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-07, @@ -44184,7 +42610,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FLlama-3_1-Nemotron-Ultra-253B-v1" }, "nebius/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-08, @@ -44196,7 +42621,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNVIDIA-Nemotron-3-Nano-30B-A3B" }, "nebius/nvidia/Nemotron-3-Nano-Omni": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-08, @@ -44209,7 +42633,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3-Nano-Omni" }, "nebius/nvidia/nemotron-3-super-120b-a12b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -44221,7 +42644,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2Fnemotron-3-super-120b-a12b" }, "nebius/nvidia/Nemotron-3-Ultra-550b-a55b": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1e-06, @@ -44233,7 +42655,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3-Ultra-550b-a55b" }, "nebius/nvidia/Nemotron-3_5-Lightning": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 6e-08, @@ -44245,7 +42666,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3_5-Lightning" }, "nebius/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -44257,7 +42677,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/openai%2Fgpt-oss-120b" }, "nebius/openbmb/MiniCPM-V-4_5": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 6.58e-07, @@ -44268,7 +42687,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/openbmb%2FMiniCPM-V-4_5" }, "nebius/Qwen/Qwen3-235B-A22B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -44279,7 +42697,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-235B-A22B-Instruct-2507" }, "nebius/Qwen/Qwen3-30B-A3B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -44290,7 +42707,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-30B-A3B-Instruct-2507" }, "nebius/Qwen/Qwen3-Next-80B-A3B-Thinking": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.5e-07, @@ -44302,7 +42718,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-Next-80B-A3B-Thinking" }, "nebius/Qwen/Qwen3.5-397B-A17B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-07, @@ -44324,7 +42739,6 @@ "supports_reasoning": true }, "nebius/zai-org/GLM-5.1": { - "max_tokens": 202752, "max_input_tokens": 202752, "max_output_tokens": 202752, "input_cost_per_token": 1.4e-06, @@ -44336,7 +42750,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/zai-org%2FGLM-5.1" }, "nebius/zai-org/GLM-5.2": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.4e-06, @@ -44351,7 +42764,7 @@ "input_cost_per_token": 1.4e-06, "litellm_provider": "nebius", "max_input_tokens": 1024000, - "max_tokens": 1024000, + "max_output_tokens": 1024000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://tokenfactory.nebius.com/models/catalog/text2text/zai-org%2FGLM-5.3", @@ -44359,7 +42772,6 @@ "supports_reasoning": true }, "nebius/zai-org/GLM-5.3-Flash": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 1.5e-07, @@ -44372,7 +42784,6 @@ "supports_vision": true }, "nebius/BAAI/bge-en-icl": { - "max_tokens": 32768, "max_input_tokens": 32768, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44381,7 +42792,6 @@ "source": "https://nebius.com/prices" }, "nebius/BAAI/bge-multilingual-gemma2": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44390,7 +42800,6 @@ "source": "https://nebius.com/prices" }, "nebius/intfloat/e5-mistral-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44399,7 +42808,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-Embedding-8B": { - "max_tokens": 40960, "max_input_tokens": 40960, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44416,7 +42824,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -44438,7 +42845,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.3e-07, "output_cost_per_token_batches": 1.2e-07, @@ -44459,7 +42865,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -44482,7 +42887,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.5e-07, "output_cost_per_token_batches": 3.25e-07, @@ -44505,7 +42909,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -44528,7 +42931,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -44550,7 +42952,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 0.0006, "output_cost_per_token_batches": 0.0003, @@ -44584,7 +42985,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 0.0006, "output_cost_per_token_batches": 0.0003, @@ -44622,7 +43022,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -44663,7 +43062,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "source": "https://developers.openai.com/api/docs/models/o3-deep-research", @@ -44696,7 +43094,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "source": "https://developers.openai.com/api/docs/models/o3-deep-research", @@ -44733,7 +43130,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -44776,7 +43172,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44797,7 +43192,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44816,7 +43210,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -44854,7 +43247,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -44897,7 +43289,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44925,7 +43316,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-06, "source": "https://developers.openai.com/api/docs/models/o4-mini-deep-research", @@ -44958,7 +43348,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-06, "source": "https://developers.openai.com/api/docs/models/o4-mini-deep-research", @@ -44995,7 +43384,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -45022,7 +43410,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45035,7 +43422,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45048,7 +43434,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.068e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45061,7 +43446,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45075,7 +43459,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45088,7 +43471,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45102,7 +43484,6 @@ "litellm_provider": "oci", "max_input_tokens": 10485760, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45115,7 +43496,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45128,7 +43508,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45141,7 +43520,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45154,7 +43532,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45167,7 +43544,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45180,7 +43556,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45193,7 +43568,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45206,7 +43580,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45219,7 +43592,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45239,7 +43611,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45258,7 +43629,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45278,7 +43648,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45292,7 +43661,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45314,7 +43682,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45326,7 +43693,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45339,7 +43705,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 9e-08, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45351,7 +43716,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45363,7 +43727,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45375,7 +43738,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45388,7 +43750,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45400,7 +43761,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45412,7 +43772,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45424,7 +43783,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [], @@ -45437,7 +43795,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45452,7 +43809,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45468,7 +43824,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45485,7 +43840,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45501,7 +43855,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45515,7 +43868,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45527,7 +43879,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45542,7 +43893,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45557,7 +43907,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45572,7 +43921,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45585,7 +43933,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45597,7 +43944,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45607,7 +43953,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45617,7 +43962,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45627,7 +43971,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45637,7 +43980,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45648,7 +43990,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45659,7 +44000,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45670,7 +44010,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "oci", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -45682,7 +44021,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": false @@ -45692,7 +44030,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45701,7 +44038,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45710,7 +44046,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45720,7 +44055,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45730,7 +44064,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45740,7 +44073,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45750,7 +44082,6 @@ "litellm_provider": "ollama", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45760,7 +44091,6 @@ "litellm_provider": "ollama", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45770,7 +44100,6 @@ "litellm_provider": "ollama", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45780,7 +44109,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45790,7 +44118,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45799,7 +44126,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45808,7 +44134,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45817,7 +44142,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45826,7 +44150,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45835,7 +44158,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45844,7 +44166,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45854,7 +44175,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45863,7 +44183,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45872,7 +44191,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45882,7 +44200,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45892,7 +44209,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45902,7 +44218,6 @@ "litellm_provider": "ollama", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45912,7 +44227,6 @@ "litellm_provider": "ollama", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45922,7 +44236,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45932,7 +44245,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45941,7 +44253,6 @@ "litellm_provider": "ollama", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45951,7 +44262,6 @@ "litellm_provider": "ollama", "max_input_tokens": 2048, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45960,7 +44270,6 @@ "litellm_provider": "openai", "max_input_tokens": 32768, "max_output_tokens": 0, - "max_tokens": 0, "mode": "moderation", "output_cost_per_token": 0.0 }, @@ -45969,7 +44278,6 @@ "litellm_provider": "openai", "max_input_tokens": 32768, "max_output_tokens": 0, - "max_tokens": 0, "mode": "moderation", "output_cost_per_token": 0.0 }, @@ -45987,7 +44295,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46013,7 +44320,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07, "output_cost_per_token_batches": 1.5e-07, @@ -46033,7 +44339,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46055,7 +44360,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_batches": 1e-07, @@ -46074,7 +44378,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46088,7 +44391,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46101,7 +44403,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46114,7 +44415,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46127,7 +44427,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 127000, "max_output_tokens": 127000, - "max_tokens": 127000, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46140,7 +44439,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 118000, "max_output_tokens": 118000, - "max_tokens": 118000, "mode": "chat", "output_cost_per_token": 1.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46153,7 +44451,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46167,7 +44464,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46180,7 +44476,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 8.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46193,7 +44488,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 9.1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46207,7 +44501,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46221,7 +44514,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46235,7 +44527,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46249,7 +44540,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.9e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46263,7 +44553,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46296,7 +44585,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-06 }, @@ -46305,7 +44593,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46314,7 +44601,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46323,7 +44609,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -46332,7 +44617,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07 }, @@ -46341,7 +44625,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46350,7 +44633,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46359,7 +44641,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46369,7 +44650,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46378,7 +44658,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46388,7 +44667,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46444,7 +44722,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-06, "search_context_cost_per_query": { @@ -46459,7 +44737,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 8e-06, @@ -46476,7 +44754,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.8e-06 }, @@ -46486,7 +44763,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 12000, "max_output_tokens": 12000, - "max_tokens": 12000, "mode": "chat", "output_cost_per_token": 1.8e-06 }, @@ -46495,7 +44771,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 200000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -46509,7 +44784,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "search_context_cost_per_query": { @@ -46524,7 +44799,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 8e-06, "search_context_cost_per_query": { @@ -46540,7 +44815,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46550,7 +44824,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 12000, "max_output_tokens": 12000, - "max_tokens": 12000, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46559,7 +44832,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46571,7 +44843,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46583,7 +44854,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46595,7 +44865,6 @@ "litellm_provider": "publicai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46607,7 +44876,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46619,7 +44887,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46859,7 +45126,6 @@ "input_cost_per_token": 4e-09, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -46869,7 +45135,6 @@ "input_cost_per_token": 3e-08, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560, @@ -46880,7 +45145,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46892,7 +45156,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46905,7 +45168,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46919,7 +45181,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.8e-06, "supports_function_calling": true, @@ -46934,7 +45195,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 8.8e-07, "output_cost_per_token_flex": 4.4e-07, @@ -46954,7 +45214,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46973,7 +45232,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46990,7 +45248,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -47006,7 +45263,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.45e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47022,7 +45278,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.41e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47038,7 +45293,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.236e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47054,7 +45308,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.41e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47070,7 +45323,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.86e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47086,7 +45338,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.45e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47104,7 +45355,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.66e-06, "output_cost_per_token_batches": 1.33e-06, @@ -47125,7 +45375,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -47180,7 +45429,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "supports_tool_choice": true @@ -47190,7 +45438,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "supports_tool_choice": true @@ -47200,7 +45447,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47210,7 +45456,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47220,7 +45465,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47230,7 +45474,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47240,7 +45483,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47250,7 +45492,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47260,7 +45501,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8086, "max_output_tokens": 8086, - "max_tokens": 8086, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47270,7 +45510,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8086, "max_output_tokens": 8086, - "max_tokens": 8086, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47280,7 +45519,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47290,7 +45528,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47300,7 +45537,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "supports_tool_choice": true @@ -47429,7 +45665,6 @@ "mode": "chat", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_system_messages": true }, @@ -47584,7 +45819,6 @@ "mode": "chat", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "supports_function_calling": true, "supports_reasoning": true, "supports_system_messages": true @@ -47605,7 +45839,6 @@ "mode": "chat", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_reasoning": true, "supports_system_messages": true }, @@ -47615,7 +45848,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47625,7 +45857,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47635,7 +45866,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47645,7 +45875,6 @@ "litellm_provider": "cohere", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://cohere.com/pricing" @@ -47656,7 +45885,6 @@ "litellm_provider": "cohere", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://cohere.com/pricing" @@ -47687,7 +45915,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47696,7 +45923,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47705,7 +45931,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47714,7 +45939,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47723,7 +45947,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47732,7 +45955,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47741,7 +45963,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 196608, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://cloud.sambanova.ai/plans/pricing", @@ -47754,7 +45975,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7e-06, "source": "https://cloud.sambanova.ai/plans/pricing" @@ -47764,7 +45984,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "For vision models, images are converted to 6432 input tokens and are billed at that amount" }, @@ -47781,7 +46000,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.sambanova.ai/plans/pricing", @@ -47790,7 +46008,6 @@ "supports_tool_choice": true }, "sambanova/DeepSeek-V3.1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-06, @@ -47803,7 +46020,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.2e-07, @@ -47816,7 +46032,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/DeepSeek-V3.2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 3e-06, @@ -47828,7 +46043,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/gemma-4-31B-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3.8e-07, @@ -47844,7 +46058,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 375000, - "max_tokens": 375000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://scx.ai/models/deepseek-v4-1-flash", @@ -47859,7 +46072,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.08e-06, "source": "https://scx.ai/models/glm-5-3", @@ -47875,7 +46087,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.413e-05, "source": "https://scx.ai/models/kimi-k3", @@ -47891,7 +46102,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.98e-06, "source": "https://scx.ai/pricing", @@ -47908,7 +46118,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.99e-06, "source": "https://scx.ai/pricing", @@ -47923,7 +46132,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 200000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 3e-06, "output_cost_per_token": 1.5e-05, @@ -47939,7 +46147,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 1.35e-06, "output_cost_per_token": 5.4e-06, @@ -47950,56 +46157,48 @@ "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-1.5-large": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-1.5-mini": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-instruct": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama2-70b-chat": { "litellm_provider": "snowflake", "max_input_tokens": 4096, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3-70b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3-8b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.1-405b": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 1.2e-06, "output_cost_per_token": 1.2e-06, @@ -48010,7 +46209,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 7.2e-07, "output_cost_per_token": 7.2e-07, @@ -48021,7 +46219,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 2.4e-07, "output_cost_per_token": 2.4e-07, @@ -48031,18 +46228,15 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.2-3b": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.3-70b": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 7.2e-07, @@ -48056,21 +46250,18 @@ "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/mistral-large": { "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/mistral-large2": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -48082,39 +46273,33 @@ "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/reka-core": { "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/reka-flash": { "litellm_provider": "snowflake", "max_input_tokens": 100000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-arctic": { "litellm_provider": "snowflake", "max_input_tokens": 4096, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-llama-3.1-405b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-llama-3.3-70b": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 7.2e-07, @@ -48311,21 +46496,18 @@ "stability.sd3-5-large-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "stability.sd3-large-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "stability.stable-image-core-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, @@ -48501,21 +46683,18 @@ "stability.stable-image-core-v1:1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, "stability.stable-image-ultra-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.14 }, "stability.stable-image-ultra-v1:1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.14 }, @@ -48549,7 +46728,6 @@ "litellm_provider": "text-completion-codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "completion", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/" @@ -48559,7 +46737,6 @@ "litellm_provider": "text-completion-codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "completion", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/" @@ -48570,7 +46747,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48582,7 +46758,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48593,7 +46768,6 @@ "input_cost_per_token_batches": 6.5e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0, @@ -48605,7 +46779,6 @@ "input_cost_per_token_batches": 1e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0, @@ -48616,7 +46789,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -48627,7 +46799,6 @@ "input_cost_per_token_batches": 5e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0 @@ -48637,7 +46808,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -48647,7 +46817,6 @@ "input_cost_per_token": 6.25e-09, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 3072, - "max_tokens": 3072, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48659,7 +46828,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48670,7 +46838,6 @@ "litellm_provider": "vertex_ai-text-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "completion", "output_cost_per_token": 2.8e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models" @@ -48680,7 +46847,6 @@ "litellm_provider": "vertex_ai-text-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "completion", "output_cost_per_token": 2.8e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models" @@ -48706,7 +46872,8 @@ "together-ai-8.1b-21b": { "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", - "max_tokens": 1000, + "max_output_tokens": 1000, + "max_input_tokens": 1000, "mode": "chat", "output_cost_per_token": 3e-07 }, @@ -48759,7 +46926,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "successor": "together_ai/deepseek-ai/DeepSeek-V4-Pro-0813" }, @@ -48775,7 +46941,7 @@ "input_cost_per_token": 1.04e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.04e-06, "source": "https://api.together.ai/v1/models", @@ -48802,7 +46968,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models", @@ -48823,7 +46989,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "metadata": { "successor": "together_ai/zai-org/GLM-5.2" }, @@ -48840,7 +47006,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -48857,7 +47023,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.together.ai/pricing", @@ -48867,7 +47033,7 @@ "input_cost_per_token": 0.0, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.together.ai/docs/serverless-models" @@ -48876,7 +47042,7 @@ "input_cost_per_token": 1.7e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://api.together.ai/v1/models", @@ -48891,7 +47057,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://api.together.ai/v1/models", @@ -48902,7 +47068,7 @@ "input_cost_per_token": 1.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://www.together.ai/pricing", @@ -48912,7 +47078,7 @@ "input_cost_per_token": 3.2e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.28e-06, "source": "https://api.together.ai/v1/models" @@ -48922,7 +47088,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 1010000, - "max_tokens": 1010000, + "max_output_tokens": 1010000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://api.together.ai/v1/models", @@ -48932,7 +47098,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://api.together.ai/v1/models" @@ -48943,7 +47109,7 @@ "input_cost_per_token": 1.4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://api.together.ai/v1/models", @@ -48958,7 +47124,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -48972,7 +47138,7 @@ "input_cost_per_token": 1.32e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://api.together.ai/v1/models", @@ -48987,7 +47153,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models", @@ -48998,7 +47164,7 @@ "input_cost_per_token": 1.25e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.25e-06, "source": "https://docs.together.ai/docs/serverless-models", @@ -49011,7 +47177,7 @@ "input_cost_per_token": 3e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -49033,7 +47199,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://api.together.ai/v1/models", @@ -49049,7 +47215,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models", @@ -49066,7 +47231,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models", @@ -49083,7 +47247,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://api.together.ai/v1/models", @@ -49168,7 +47331,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -49187,7 +47349,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "output_cost_per_token_batches": 7e-08, @@ -49208,7 +47369,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -49229,7 +47389,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -49249,7 +47408,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49277,7 +47435,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -49295,7 +47452,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -49314,7 +47470,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -49332,7 +47487,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -49350,7 +47504,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -49389,7 +47542,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -49431,7 +47583,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -49463,7 +47614,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -49503,7 +47653,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -49542,7 +47691,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -49580,7 +47728,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -49618,7 +47765,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -49654,7 +47800,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49672,7 +47817,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -49688,7 +47832,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -49703,7 +47846,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49721,7 +47863,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -49741,7 +47882,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -49768,7 +47908,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supports_function_calling": true, "supports_prompt_caching": false, @@ -49785,7 +47924,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "supports_assistant_prefill": true, @@ -49815,7 +47953,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -49843,7 +47980,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -49880,7 +48016,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -49917,7 +48052,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -49957,7 +48091,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -49988,7 +48121,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-r1.html", @@ -50004,7 +48136,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_batches": 9.25e-07, @@ -50025,7 +48156,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -50043,7 +48173,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50057,7 +48186,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -50072,7 +48200,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.2e-07, "output_cost_per_token_batches": 1.1e-07, @@ -50086,7 +48213,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50100,7 +48226,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -50113,7 +48238,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -50126,7 +48250,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50141,7 +48264,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -50156,7 +48278,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9.7e-07, "output_cost_per_token_batches": 4.85e-07, @@ -50179,7 +48300,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 10000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_batches": 3.3e-07, @@ -50200,7 +48320,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-pixtral-large.md", @@ -50212,7 +48331,6 @@ "litellm_provider": "v0", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50226,7 +48344,6 @@ "litellm_provider": "v0", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -50240,7 +48357,6 @@ "litellm_provider": "v0", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50254,7 +48370,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.4e-07 }, @@ -50263,7 +48378,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07 }, @@ -50272,7 +48386,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07 }, @@ -50281,7 +48394,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -50292,7 +48404,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 262144, "max_output_tokens": 66536, - "max_tokens": 66536, "mode": "chat", "output_cost_per_token": 1.6e-06, "supports_function_calling": true, @@ -50303,7 +48414,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 300000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-07, "supports_vision": true, @@ -50315,7 +48425,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.4e-07, "supports_function_calling": true, @@ -50326,7 +48435,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 300000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.2e-06, "supports_vision": true, @@ -50338,7 +48446,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50349,7 +48456,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_vision": true, @@ -50364,7 +48470,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_vision": true, @@ -50379,7 +48484,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_vision": true, @@ -50394,7 +48498,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -50409,7 +48512,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -50424,7 +48526,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_vision": true, @@ -50439,7 +48540,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50452,7 +48552,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50470,7 +48569,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50488,7 +48586,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50507,7 +48604,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -50527,7 +48623,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -50547,7 +48642,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -50567,7 +48661,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, @@ -50590,7 +48683,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": false, @@ -50611,7 +48703,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50631,7 +48722,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50648,7 +48738,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -50660,7 +48749,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -50671,7 +48759,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -50682,7 +48769,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50691,7 +48777,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.19e-06, "supports_tool_choice": true @@ -50701,7 +48786,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 9.9e-07, "supports_function_calling": true, @@ -50713,7 +48797,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "supports_tool_choice": true @@ -50723,7 +48806,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.5e-06, "supports_vision": true, @@ -50742,7 +48824,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -50763,7 +48844,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50772,7 +48852,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "supports_vision": true, @@ -50784,7 +48863,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50793,7 +48871,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50802,7 +48879,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -50811,7 +48887,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.9e-07, "supports_tool_choice": true @@ -50821,7 +48896,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 8e-08, "supports_tool_choice": true @@ -50831,7 +48905,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_tool_choice": true @@ -50841,7 +48914,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-08, "supports_function_calling": true, @@ -50852,7 +48924,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.6e-07, "supports_vision": true, @@ -50864,7 +48935,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07 }, @@ -50873,7 +48943,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_function_calling": true, @@ -50884,7 +48953,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_vision": true, @@ -50896,7 +48964,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_function_calling": true, @@ -50907,7 +48974,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "supports_tool_choice": true @@ -50917,7 +48983,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-07, "supports_vision": true, @@ -50929,7 +48994,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 9e-07, "supports_function_calling": true, @@ -50940,7 +49004,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50949,7 +49012,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.8e-07, "supports_function_calling": true, @@ -50961,7 +49023,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_function_calling": true, @@ -50973,7 +49034,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true @@ -50983,7 +49043,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -50994,7 +49053,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_vision": true, @@ -51006,7 +49064,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -51015,7 +49072,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -51026,7 +49082,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.9e-07 }, @@ -51035,7 +49090,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -51047,7 +49101,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 65536, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true @@ -51057,7 +49110,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_vision": true, @@ -51070,7 +49122,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_vision": true, @@ -51083,7 +49134,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.2e-06, "supports_function_calling": true, @@ -51094,7 +49144,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -51103,7 +49152,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.9e-06 }, @@ -51112,7 +49160,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -51123,7 +49170,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06 }, @@ -51132,7 +49178,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_vision": true, @@ -51146,7 +49191,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "supports_vision": true, @@ -51161,7 +49205,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "supports_vision": true, @@ -51176,7 +49219,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_vision": true, @@ -51191,7 +49233,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -51206,7 +49247,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_vision": true, @@ -51221,7 +49261,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_vision": true, @@ -51236,7 +49275,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_vision": true, @@ -51251,7 +49289,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_function_calling": true, @@ -51265,7 +49302,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_vision": true, @@ -51278,7 +49314,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51287,7 +49322,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51296,7 +49330,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51305,7 +49338,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -51314,7 +49346,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-05 }, @@ -51323,7 +49354,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 5e-06 }, @@ -51332,7 +49362,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8e-06 }, @@ -51341,7 +49370,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -51353,7 +49381,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -51365,7 +49392,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -51376,7 +49402,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -51388,7 +49413,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -51399,7 +49423,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_function_calling": true @@ -51409,7 +49432,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "supports_function_calling": true, @@ -51420,7 +49442,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "supports_function_calling": true, @@ -51431,7 +49452,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -51442,7 +49462,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-06, "supports_function_calling": true, @@ -51453,7 +49472,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 1.1e-06, "supports_function_calling": true, @@ -51465,7 +49483,6 @@ "input_cost_per_token": 4.5e-07, "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 1.8e-06, "source": "https://vercel.com/ai-gateway/models/glm-4.6", @@ -51516,7 +49533,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -51530,7 +49546,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -51550,7 +49565,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -51579,7 +49593,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -51602,7 +49615,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51618,7 +49630,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51633,7 +49644,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_assistant_prefill": true, @@ -51647,7 +49657,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_assistant_prefill": true, @@ -51661,7 +49670,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -51675,7 +49683,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -51688,7 +49695,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51701,7 +49707,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51721,7 +49726,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51756,7 +49760,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51795,7 +49798,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51833,7 +49835,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51870,7 +49871,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51908,7 +49908,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51946,7 +49945,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -51984,7 +49982,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -52028,7 +50025,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -52066,7 +50062,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -52111,7 +50106,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52150,7 +50144,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52187,7 +50180,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52228,7 +50220,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52271,7 +50262,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52310,7 +50300,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52350,7 +50339,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52380,7 +50368,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -52419,7 +50406,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52458,7 +50444,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52482,7 +50467,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52494,7 +50478,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52506,7 +50489,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52518,7 +50500,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52530,7 +50511,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52541,7 +50521,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52552,7 +50531,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52566,7 +50544,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.7e-06, "output_cost_per_token_batches": 8.5e-07, @@ -52588,7 +50565,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.68e-06, "output_cost_per_token_batches": 8.4e-07, @@ -52609,7 +50585,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 65336, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "output_cost_per_token_batches": 2.7e-06, @@ -52632,7 +50607,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -52691,7 +50665,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -52726,7 +50699,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -52759,7 +50731,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -52787,7 +50758,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -52817,7 +50787,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -52857,7 +50826,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -52916,7 +50884,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -52977,7 +50944,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -53034,7 +51000,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -53048,7 +51013,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53059,7 +51023,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -53070,7 +51033,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -53081,7 +51043,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53092,7 +51053,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53122,7 +51082,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "audio_speech", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -53158,7 +51117,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "audio_speech", "output_cost_per_image": 0.08, "output_cost_per_token": 0, @@ -53195,7 +51153,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.6e-05, "source": "https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama-3.2-90b-vision-instruct-maas", @@ -53208,7 +51165,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama-3.2-90b-vision-instruct-maas", @@ -53221,7 +51177,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "metadata": { "notes": "VertexAI states that The Llama 3.1 API service for llama-3.1-70b-instruct-maas and llama-3.1-8b-instruct-maas are in public preview and at no cost." }, @@ -53237,7 +51192,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "metadata": { "notes": "VertexAI states that The Llama 3.2 API service is at no cost during public preview, and will be priced as per dollar-per-1M-tokens at GA." }, @@ -53254,7 +51208,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.15e-06, "output_cost_per_token_batches": 5.75e-07, @@ -53276,7 +51229,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.15e-06, "output_cost_per_token_batches": 5.75e-07, @@ -53298,7 +51250,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 10000000, "max_output_tokens": 10000000, - "max_tokens": 10000000, "mode": "chat", "output_cost_per_token": 7e-07, "output_cost_per_token_batches": 3.5e-07, @@ -53320,7 +51271,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 10000000, "max_output_tokens": 10000000, - "max_tokens": 10000000, "mode": "chat", "output_cost_per_token": 7e-07, "output_cost_per_token_batches": 3.5e-07, @@ -53341,7 +51291,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53352,7 +51301,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53363,7 +51311,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53376,7 +51323,6 @@ "litellm_provider": "vertex_ai-minimax_models", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53390,7 +51336,6 @@ "litellm_provider": "vertex_ai-moonshot_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53405,7 +51350,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53423,7 +51367,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53440,7 +51383,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53452,7 +51394,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53464,7 +51405,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53476,7 +51416,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53488,7 +51427,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53499,7 +51437,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53510,7 +51447,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53521,7 +51457,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53532,7 +51467,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -53543,7 +51477,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_function_calling": true, @@ -53554,7 +51487,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -53567,7 +51499,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -53601,7 +51532,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 262144, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/maas/google/gemma-4-26b-a4b-it", @@ -53618,7 +51548,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.6e-07, "output_cost_per_token_batches": 1.8e-07, @@ -53631,7 +51560,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53646,7 +51574,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53663,7 +51590,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53683,7 +51609,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, - "max_tokens": 2000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53703,7 +51628,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, - "max_tokens": 2000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53724,7 +51648,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53744,7 +51667,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -53763,7 +51685,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 8.8e-07, "output_cost_per_token_batches": 4.4e-07, @@ -53783,7 +51704,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_batches": 9e-07, @@ -53800,7 +51720,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53816,7 +51735,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53829,7 +51747,6 @@ "vertex_ai/veo-3.1-generate-preview": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -53844,7 +51761,6 @@ "vertex_ai/veo-3.1-fast-generate-preview": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -53861,7 +51777,6 @@ "deprecation_date": "2026-11-17", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -53877,7 +51792,6 @@ "deprecation_date": "2026-11-17", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -53893,7 +51807,6 @@ "vertex_ai/veo-3.1-lite-generate-001": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.05, "output_cost_per_second_1080p": 0.08, @@ -53911,7 +51824,6 @@ "litellm_provider": "voyage", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53920,7 +51832,6 @@ "litellm_provider": "voyage", "max_input_tokens": 8000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53929,7 +51840,6 @@ "litellm_provider": "voyage", "max_input_tokens": 8000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53939,7 +51849,6 @@ "litellm_provider": "voyage", "max_input_tokens": 4000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53949,7 +51858,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53958,7 +51866,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53967,7 +51874,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53977,7 +51883,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53986,7 +51891,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4000, - "max_tokens": 4000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -53994,7 +51898,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54002,7 +51905,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -54012,7 +51914,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54020,7 +51921,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54028,7 +51928,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54036,7 +51935,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54044,7 +51942,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54052,7 +51949,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54060,7 +51956,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 120000, - "max_tokens": 120000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54068,7 +51963,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54076,7 +51970,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54084,7 +51977,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -54094,7 +51986,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54102,7 +51993,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54110,7 +52000,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4000, - "max_tokens": 4000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54118,13 +52007,11 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, "wandb/openai/gpt-oss-120b": { "supports_reasoning": true, - "max_tokens": 131072, "max_input_tokens": 131000, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -54135,7 +52022,6 @@ }, "wandb/openai/gpt-oss-20b": { "supports_reasoning": true, - "max_tokens": 131072, "max_input_tokens": 131000, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -54145,7 +52031,6 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "wandb/moonshotai/Kimi-K2.5": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "cache_read_input_token_cost": 1e-07, @@ -54160,7 +52045,6 @@ "supports_vision": true }, "wandb/meta-llama/Llama-3.1-8B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 131000, "max_output_tokens": 128000, "input_cost_per_token": 2.2e-07, @@ -54171,7 +52055,6 @@ }, "wandb/deepseek-ai/DeepSeek-V3.1": { "supports_reasoning": true, - "max_tokens": 128000, "max_input_tokens": 161000, "max_output_tokens": 128000, "input_cost_per_token": 5.5e-07, @@ -54181,7 +52064,6 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "wandb/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 7.1e-07, @@ -54195,7 +52077,6 @@ "litellm_provider": "watsonx", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 2e-07, "supports_audio_input": false, @@ -54213,7 +52094,6 @@ "litellm_provider": "watsonx", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_audio_input": false, @@ -54227,7 +52107,6 @@ "supports_vision": false }, "watsonx/bigscience/mt0-xxl-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.908e-06, @@ -54240,7 +52119,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/bigscience/mt0-xxl": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.908e-06, @@ -54253,7 +52131,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/core42/jais-13b-chat": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 0.0005, @@ -54265,7 +52142,6 @@ "supports_vision": false }, "watsonx/google/flan-t5-xl-3b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54277,7 +52153,6 @@ "supports_vision": false }, "watsonx/ibm/granite-13b-chat-v2": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54289,7 +52164,6 @@ "supports_vision": false }, "watsonx/ibm/granite-13b-instruct-v2": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54301,7 +52175,6 @@ "supports_vision": false }, "watsonx/ibm/granite-3-3-8b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -54313,7 +52186,6 @@ "supports_vision": false }, "watsonx/ibm/granite-4-h-small": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6.36e-08, @@ -54326,7 +52198,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/ibm/granite-guardian-3-2-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -54338,7 +52209,6 @@ "supports_vision": false }, "watsonx/ibm/granite-guardian-3-3-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -54350,7 +52220,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-1024-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54362,7 +52231,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-1536-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54374,7 +52242,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-512-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54386,7 +52253,6 @@ "supports_vision": false }, "watsonx/ibm/granite-vision-3-2-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -54398,7 +52264,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-2-11b-vision-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54410,7 +52275,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-2-1b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-07, @@ -54422,7 +52286,6 @@ "supports_vision": false }, "watsonx/meta-llama/llama-3-2-3b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.5e-07, @@ -54434,7 +52297,6 @@ "supports_vision": false }, "watsonx/meta-llama/llama-3-2-90b-vision-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-06, @@ -54446,7 +52308,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-3-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 7.526e-07, @@ -54459,7 +52320,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-4-maverick-17b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3.71e-07, @@ -54472,7 +52332,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3.71e-07, @@ -54485,7 +52344,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-guard-3-11b-vision": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54497,7 +52355,6 @@ "supports_vision": true }, "watsonx/mistralai/mistral-medium-2505": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3e-06, @@ -54509,7 +52366,6 @@ "supports_vision": false }, "watsonx/mistralai/mistral-small-2503": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 1e-07, @@ -54521,7 +52377,6 @@ "supports_vision": false }, "watsonx/mistralai/mistral-small-3-1-24b-instruct-2503": { - "max_tokens": 16384, "max_input_tokens": 131072, "max_output_tokens": 16384, "input_cost_per_token": 1.06e-07, @@ -54534,7 +52389,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/mistralai/pixtral-12b-2409": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54546,7 +52400,6 @@ "supports_vision": true }, "watsonx/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.59e-07, @@ -54559,7 +52412,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/sdaia/allam-1-13b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1.8e-06, @@ -54596,7 +52448,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54621,7 +52472,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54645,7 +52495,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -54671,7 +52520,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54699,7 +52547,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -54727,7 +52574,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -54751,7 +52597,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54773,7 +52618,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54795,7 +52639,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54817,7 +52660,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54839,7 +52681,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54861,7 +52702,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 203000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -54884,7 +52724,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_batches": 1.6e-06, "output_cost_per_token_priority": 5.6e-06, @@ -54907,7 +52746,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 203000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -55505,7 +53343,6 @@ } }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 4.5e-07, @@ -55515,7 +53352,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/flux-kontext-pro": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 4e-08, @@ -55524,7 +53360,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/SSD-1B": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -55533,7 +53368,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/chronos-hermes-13b-v2": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55542,7 +53376,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55551,7 +53384,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55560,7 +53392,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55569,7 +53400,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55578,7 +53408,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55587,7 +53416,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55596,7 +53424,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55605,7 +53432,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55614,7 +53440,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b-python": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55623,7 +53448,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55632,7 +53456,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55641,7 +53464,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55650,7 +53472,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-qwen-1p5-7b": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 2e-07, @@ -55659,7 +53480,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/codegemma-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -55668,7 +53488,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/codegemma-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -55677,7 +53496,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-671b-v2-p1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.2e-06, @@ -55686,7 +53504,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -55695,7 +53512,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-70b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55704,7 +53520,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55713,7 +53528,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55722,7 +53536,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55731,7 +53544,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-kontext-max": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 8e-08, @@ -55740,7 +53552,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/dbrx-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.2e-06, @@ -55749,7 +53560,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-1b-base": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -55758,7 +53568,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-33b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55767,7 +53576,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55776,7 +53584,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base-v1p5": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55785,7 +53592,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-instruct-v1p5": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55794,7 +53600,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-base": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55803,7 +53608,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-instruct": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55812,7 +53616,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-prover-v2": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.2e-06, @@ -55821,7 +53624,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528-distill-qwen3-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55830,7 +53632,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-70b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55839,7 +53640,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55848,7 +53648,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55857,7 +53656,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-1p5b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -55866,7 +53664,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55875,7 +53672,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55884,7 +53680,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-v2-lite-chat": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55893,7 +53688,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-v2p5": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.2e-06, @@ -55902,7 +53696,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/devstral-small-2505": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55911,7 +53704,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dobby-mini-unhinged-plus-llama-3-1-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55920,7 +53712,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dobby-unhinged-llama-3-3-70b-new": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55929,7 +53720,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dolphin-2-9-2-qwen2-72b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55938,7 +53728,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dolphin-2p6-mixtral-8x7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -55947,7 +53736,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ernie-4p5-21b-a3b-pt": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -55956,7 +53744,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ernie-4p5-300b-a47b-pt": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -55965,7 +53752,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/fare-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55974,7 +53760,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firefunction-v1": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -55983,7 +53768,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firellava-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55992,7 +53776,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firesearch-ocr-v6": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56001,7 +53784,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/fireworks-asr-large": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 0.0, @@ -56010,7 +53792,6 @@ "mode": "audio_transcription" }, "fireworks_ai/accounts/fireworks/models/fireworks-asr-v2": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 0.0, @@ -56019,7 +53800,6 @@ "mode": "audio_transcription" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56028,7 +53808,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev-controlnet-union": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-09, @@ -56037,7 +53816,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev-fp8": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 5e-10, @@ -56046,7 +53824,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/flux-1-schnell": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56055,7 +53832,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-schnell-fp8": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 3.5e-10, @@ -56064,7 +53840,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/gemma-2b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -56073,7 +53848,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-3-27b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56082,7 +53856,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56091,7 +53864,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-7b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56100,7 +53872,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma2-9b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56109,7 +53880,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/glm-4p5v": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.2e-06, @@ -56119,7 +53889,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.2e-06, @@ -56128,7 +53897,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-07, @@ -56137,7 +53905,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/hermes-2-pro-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56146,7 +53913,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-38b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56155,7 +53921,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-78b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56164,7 +53929,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-8b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -56173,7 +53937,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/japanese-stable-diffusion-xl": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56182,7 +53945,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/kat-coder": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-07, @@ -56191,7 +53953,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/kat-dev-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56200,7 +53961,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/kat-dev-72b-exp": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56209,7 +53969,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-2-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56218,7 +53977,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-3-1b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56227,7 +53985,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-3-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56236,7 +53993,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56245,7 +54001,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-13b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56254,7 +54009,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56263,7 +54017,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-70b-chat": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 9e-07, @@ -56272,7 +54025,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56281,7 +54033,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-7b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56290,7 +54041,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 9e-07, @@ -56299,7 +54049,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct-hf": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 9e-07, @@ -56308,7 +54057,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56317,7 +54065,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-8b-instruct-hf": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56326,7 +54073,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct-long": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56335,7 +54081,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56344,7 +54089,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct-1b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56353,7 +54097,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-nemotron-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56362,7 +54105,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56371,7 +54113,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56380,7 +54121,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56389,7 +54129,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llamaguard-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56398,7 +54137,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llava-yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56407,7 +54145,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/minimax-m1-80k": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56416,7 +54153,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/minimax-m2": { - "max_tokens": 4096, "max_input_tokens": 196608, "max_output_tokens": 4096, "input_cost_per_token": 3e-07, @@ -56425,7 +54161,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-14b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 2e-07, @@ -56434,7 +54169,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-3b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 1e-07, @@ -56443,7 +54177,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-8b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 2e-07, @@ -56452,7 +54185,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56461,7 +54193,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-4k": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56470,7 +54201,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v0p2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56479,7 +54209,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v3": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56488,7 +54217,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-v0p2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56497,7 +54225,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-large-3-fp8": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 1.2e-06, @@ -56506,7 +54233,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-nemo-base-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -56515,7 +54241,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-nemo-instruct-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -56524,7 +54249,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-small-24b-instruct-2501": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56533,7 +54257,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x22b": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 1.2e-06, @@ -56542,7 +54265,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 1.2e-06, @@ -56551,7 +54273,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56560,7 +54281,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56569,7 +54289,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct-hf": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56578,7 +54297,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mythomax-l2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56587,7 +54305,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nemotron-nano-v2-12b-vl": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56596,7 +54313,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-capybara-7b-v1p9": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56605,7 +54321,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-2-mixtral-8x7b-dpo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56614,7 +54329,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-2-yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56623,7 +54337,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56632,7 +54345,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56641,7 +54353,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56650,7 +54361,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-12b-v2": { - "max_tokens": 131072, "max_input_tokens": 128000, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56659,7 +54369,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-9b-v2": { - "max_tokens": 131072, "max_input_tokens": 128000, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56668,7 +54377,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openchat-3p5-0106-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56677,7 +54385,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openhermes-2-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56686,7 +54393,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openhermes-2p5-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56695,7 +54401,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openorca-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56704,7 +54409,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-2-3b": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 1e-07, @@ -56713,7 +54417,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-3-mini-128k-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56722,7 +54425,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-3-vision-128k-instruct": { - "max_tokens": 32064, "max_input_tokens": 32064, "max_output_tokens": 32064, "input_cost_per_token": 2e-07, @@ -56731,7 +54433,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-python-v1": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56740,7 +54441,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v1": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56749,7 +54449,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v2": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56758,7 +54457,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/playground-v2-1024px-aesthetic": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56767,7 +54465,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/playground-v2-5-1024px-aesthetic": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56776,7 +54473,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/pythia-12b": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 2e-07, @@ -56785,7 +54481,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-qwq-32b-preview": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56794,7 +54489,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-v2p5-14b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56803,7 +54497,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-v2p5-7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56812,7 +54505,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen1p5-72b-chat": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56821,7 +54513,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56830,7 +54521,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-2b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56839,7 +54529,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-72b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56848,7 +54537,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56857,7 +54545,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-0p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56866,7 +54553,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56875,7 +54561,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-1p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56884,7 +54569,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56893,7 +54577,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-32b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56902,7 +54585,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-72b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56911,7 +54593,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-72b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56920,7 +54601,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56929,7 +54609,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56938,7 +54617,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56947,7 +54625,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56956,7 +54633,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56965,7 +54641,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56974,7 +54649,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56983,7 +54657,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56992,7 +54665,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-128k": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57001,7 +54673,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-32k-rope": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -57010,7 +54681,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-64k": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 9e-07, @@ -57019,7 +54689,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -57028,7 +54697,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -57037,7 +54705,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57046,7 +54713,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57055,7 +54721,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-math-72b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57064,7 +54729,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-32b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 9e-07, @@ -57073,7 +54737,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-3b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57082,7 +54745,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-72b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 9e-07, @@ -57091,7 +54753,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-7b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57100,7 +54761,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-0p6b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -57109,7 +54769,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-14b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57118,7 +54777,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -57127,7 +54785,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -57136,7 +54793,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-131072": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -57145,7 +54801,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-40960": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -57154,7 +54809,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.2e-07, @@ -57163,7 +54817,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57172,7 +54825,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57181,7 +54833,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -57190,7 +54841,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-07, @@ -57199,7 +54849,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-07, @@ -57208,7 +54857,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57218,7 +54866,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/qwen3-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57227,7 +54874,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-4b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -57236,7 +54882,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57246,7 +54891,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-30b-a3b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57255,7 +54899,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-instruct-bf16": { - "max_tokens": 4096, "max_input_tokens": 262144, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57264,7 +54907,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-embedding-0p6b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 0.0, @@ -57273,7 +54915,6 @@ "mode": "embedding" }, "fireworks_ai/accounts/fireworks/models/qwen3-embedding-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57282,7 +54923,6 @@ "mode": "embedding" }, "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57291,7 +54931,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-thinking": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57300,7 +54939,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-0p6b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57309,7 +54947,6 @@ "mode": "rerank" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57318,7 +54955,6 @@ "mode": "rerank" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57328,7 +54964,6 @@ "source": "https://api.fireworks.ai/v1/serverless/models" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57337,7 +54972,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57346,7 +54980,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57355,7 +54988,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57364,7 +54996,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-32b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57373,7 +55004,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-8b-instruct": { - "max_tokens": 4096, "max_input_tokens": 262144, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -57387,7 +55017,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -57398,7 +55027,6 @@ "supports_vision": true }, "fireworks_ai/accounts/fireworks/models/qwq-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57407,7 +55035,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/rolm-ocr": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57416,7 +55043,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/snorkel-mistral-7b-pairrm-dpo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57425,7 +55051,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/stable-diffusion-xl-1024-v1-0": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -57434,7 +55059,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/stablecode-3b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -57443,7 +55067,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder-16b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -57452,7 +55075,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -57461,7 +55083,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-15b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -57470,7 +55091,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-3b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -57479,7 +55099,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-7b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -57488,7 +55107,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/toppy-m-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57497,7 +55115,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57506,7 +55123,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b-200k-capybara": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 9e-07, @@ -57515,7 +55131,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57524,7 +55139,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-6b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -57533,7 +55147,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/zephyr-7b-beta": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57547,7 +55160,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57564,7 +55176,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57581,7 +55192,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57596,7 +55206,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-06, "supports_function_calling": true, @@ -57608,7 +55217,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -57620,7 +55228,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.25e-06, "supports_function_calling": true @@ -57636,7 +55243,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -57647,7 +55253,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true @@ -57663,7 +55268,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 5e-07, "supports_function_calling": true, @@ -57675,7 +55279,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.5e-06, "supports_reasoning": true, @@ -57688,7 +55291,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.5e-07, "supports_function_calling": true, @@ -57699,7 +55301,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "supports_vision": true, @@ -57717,7 +55318,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 9e-07, "supports_function_calling": true @@ -57729,7 +55329,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57746,7 +55345,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57762,7 +55360,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57779,7 +55376,6 @@ "output_cost_per_token": 3.3e-07, "max_input_tokens": 262144, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57796,7 +55392,6 @@ "output_cost_per_token": 1.38e-07, "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_vision": true, "supports_system_messages": true }, @@ -57807,7 +55402,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 262144, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57824,7 +55418,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57841,7 +55434,6 @@ "output_cost_per_token": 2e-08, "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_vision": true, "supports_system_messages": true }, @@ -57852,7 +55444,6 @@ "output_cost_per_token": 4.1e-07, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57867,7 +55458,6 @@ "output_cost_per_token": 3.95e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_vision": true, "supports_system_messages": true, "supports_reasoning": true, @@ -57881,7 +55471,6 @@ "output_cost_per_token": 9e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57899,7 +55488,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57916,7 +55504,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57932,7 +55519,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57947,7 +55533,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57962,7 +55547,6 @@ "output_cost_per_token": 3e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -57975,7 +55559,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57992,7 +55575,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58007,7 +55589,6 @@ "output_cost_per_token": 8.45e-06, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58021,7 +55602,6 @@ "output_cost_per_token": 6e-07, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -58034,7 +55614,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58051,7 +55630,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 262144, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58065,7 +55643,6 @@ "output_cost_per_token": 1.55e-06, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58079,7 +55656,6 @@ "output_cost_per_token": 2.7e-07, "max_input_tokens": 160000, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58093,7 +55669,6 @@ "output_cost_per_token": 2.5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58109,7 +55684,6 @@ "output_cost_per_token": 2.3e-06, "max_input_tokens": 131072, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58123,7 +55697,6 @@ "output_cost_per_token": 1.12e-06, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58139,7 +55712,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 131072, "max_output_tokens": 98304, - "max_tokens": 98304, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58155,7 +55727,6 @@ "output_cost_per_token": 3e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58169,7 +55740,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_system_messages": true, "supports_response_schema": true }, @@ -58180,7 +55750,6 @@ "output_cost_per_token": 1e-07, "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -58193,7 +55762,6 @@ "output_cost_per_token": 1.8e-06, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58211,7 +55779,6 @@ "output_cost_per_token": 1.5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_tool_choice": true, "supports_vision": false, "supports_system_messages": true, @@ -58225,7 +55792,6 @@ "output_cost_per_token": 5.8e-07, "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58239,7 +55805,6 @@ "output_cost_per_token": 1.5e-07, "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58252,7 +55817,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 12288, "max_output_tokens": 12288, - "max_tokens": 12288, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58266,7 +55830,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58280,7 +55843,6 @@ "output_cost_per_token": 1.7e-07, "max_input_tokens": 60288, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58292,7 +55854,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 1000000, "max_output_tokens": 40000, - "max_tokens": 40000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58307,7 +55868,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58324,7 +55884,6 @@ "output_cost_per_token": 3e-07, "max_input_tokens": 64000, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58337,7 +55896,6 @@ "output_cost_per_token": 4e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true, "supports_response_schema": true }, @@ -58348,7 +55906,6 @@ "output_cost_per_token": 6.2e-07, "max_input_tokens": 65535, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_system_messages": true, "supports_response_schema": true }, @@ -58359,7 +55916,6 @@ "output_cost_per_token": 9e-08, "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58370,7 +55926,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58383,7 +55938,6 @@ "output_cost_per_token": 7.4e-07, "max_input_tokens": 8192, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58395,7 +55949,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true, "supports_response_schema": true @@ -58407,7 +55960,6 @@ "output_cost_per_token": 8.5e-07, "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_vision": true, "supports_system_messages": true, "supports_response_schema": true @@ -58419,7 +55971,6 @@ "output_cost_per_token": 5.9e-07, "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_vision": true, "supports_system_messages": true }, @@ -58430,7 +55981,6 @@ "output_cost_per_token": 1.4e-07, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58442,7 +55992,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_vision": true, "supports_system_messages": true }, @@ -58453,7 +56002,6 @@ "output_cost_per_token": 1.48e-06, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58466,7 +56014,6 @@ "output_cost_per_token": 2.8e-07, "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_system_messages": true, "supports_reasoning": true }, @@ -58477,7 +56024,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58489,7 +56035,6 @@ "output_cost_per_token": 7e-08, "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-424b-a47b": { @@ -58499,7 +56044,6 @@ "output_cost_per_token": 1.25e-06, "max_input_tokens": 123000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_vision": true, "supports_system_messages": true, "supports_reasoning": true @@ -58511,7 +56055,6 @@ "output_cost_per_token": 1.1e-06, "max_input_tokens": 123000, "max_output_tokens": 12000, - "max_tokens": 12000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58523,7 +56066,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 160000, "max_output_tokens": 160000, - "max_tokens": 160000, "supports_system_messages": true }, "novita/qwen/qwen3-32b-fp8": { @@ -58533,7 +56075,6 @@ "output_cost_per_token": 4.5e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58544,7 +56085,6 @@ "output_cost_per_token": 4.5e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true, "supports_function_calling": true, @@ -58557,7 +56097,6 @@ "output_cost_per_token": 2e-07, "max_input_tokens": 98304, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_vision": true, "supports_system_messages": true }, @@ -58568,7 +56107,6 @@ "output_cost_per_token": 1.3e-06, "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58581,7 +56119,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58596,7 +56133,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 8192, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58609,7 +56145,6 @@ "output_cost_per_token": 9e-08, "max_input_tokens": 4096, "max_output_tokens": 3200, - "max_tokens": 3200, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-28b-a3b-thinking": { @@ -58619,7 +56154,6 @@ "output_cost_per_token": 3.9e-07, "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58635,7 +56169,6 @@ "output_cost_per_token": 5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58650,7 +56183,6 @@ "output_cost_per_token": 8.5e-07, "max_input_tokens": 131072, "max_output_tokens": 98304, - "max_tokens": 98304, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58666,7 +56198,6 @@ "output_cost_per_token": 7e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58681,7 +56212,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58696,7 +56226,6 @@ "output_cost_per_token": 9.7e-07, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58713,7 +56242,6 @@ "output_cost_per_token": 9.7e-07, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58730,7 +56258,6 @@ "output_cost_per_token": 7.5e-07, "max_input_tokens": 16384, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-28b-a3b": { @@ -58740,7 +56267,6 @@ "output_cost_per_token": 5.6e-07, "max_input_tokens": 30000, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58755,7 +56281,6 @@ "output_cost_per_token": 2.8e-07, "max_input_tokens": 120000, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58768,7 +56293,6 @@ "output_cost_per_token": 1.38e-07, "max_input_tokens": 128000, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58779,7 +56303,6 @@ "output_cost_per_token": 3e-08, "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true, "supports_reasoning": true, "supports_function_calling": true, @@ -58792,7 +56315,6 @@ "output_cost_per_token": 7e-08, "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58806,7 +56328,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 32768, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58819,7 +56340,6 @@ "output_cost_per_token": 1.48e-06, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58831,8 +56351,7 @@ "input_cost_per_token": 7e-08, "output_cost_per_token": 0, "max_input_tokens": 32768, - "max_output_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "novita/qwen/qwen3-embedding-8b": { "litellm_provider": "novita", @@ -58840,8 +56359,7 @@ "input_cost_per_token": 7e-08, "output_cost_per_token": 0, "max_input_tokens": 32768, - "max_output_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "novita/baai/bge-m3": { "litellm_provider": "novita", @@ -58849,8 +56367,7 @@ "input_cost_per_token": 1e-08, "output_cost_per_token": 1e-08, "max_input_tokens": 8192, - "max_output_tokens": 96000, - "max_tokens": 96000 + "max_output_tokens": 96000 }, "novita/qwen/qwen3-reranker-8b": { "litellm_provider": "novita", @@ -58858,8 +56375,7 @@ "input_cost_per_token": 5e-08, "output_cost_per_token": 5e-08, "max_input_tokens": 32768, - "max_output_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "novita/baai/bge-reranker-v2-m3": { "litellm_provider": "novita", @@ -58867,11 +56383,9 @@ "input_cost_per_token": 1e-08, "output_cost_per_token": 1e-08, "max_input_tokens": 8000, - "max_output_tokens": 8000, - "max_tokens": 8000 + "max_output_tokens": 8000 }, "llamagate/llama-3.1-8b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -58882,7 +56396,6 @@ "supports_response_schema": true }, "llamagate/llama-3.2-3b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -58893,7 +56406,6 @@ "supports_response_schema": true }, "llamagate/mistral-7b-v0.3": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -58904,7 +56416,6 @@ "supports_response_schema": true }, "llamagate/qwen3-8b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -58915,7 +56426,6 @@ "supports_response_schema": true }, "llamagate/dolphin3-8b": { - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "input_cost_per_token": 8e-08, @@ -58926,7 +56436,6 @@ "supports_response_schema": true }, "llamagate/deepseek-r1-8b": { - "max_tokens": 16384, "max_input_tokens": 65536, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -58938,7 +56447,6 @@ "supports_reasoning": true }, "llamagate/deepseek-r1-7b-qwen": { - "max_tokens": 16384, "max_input_tokens": 131072, "max_output_tokens": 16384, "input_cost_per_token": 8e-08, @@ -58950,7 +56458,6 @@ "supports_reasoning": true }, "llamagate/openthinker-7b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 8e-08, @@ -58962,7 +56469,6 @@ "supports_reasoning": true }, "llamagate/qwen2.5-coder-7b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 6e-08, @@ -58973,7 +56479,6 @@ "supports_response_schema": true }, "llamagate/deepseek-coder-6.7b": { - "max_tokens": 4096, "max_input_tokens": 16384, "max_output_tokens": 4096, "input_cost_per_token": 6e-08, @@ -58984,7 +56489,6 @@ "supports_response_schema": true }, "llamagate/codellama-7b": { - "max_tokens": 4096, "max_input_tokens": 16384, "max_output_tokens": 4096, "input_cost_per_token": 6e-08, @@ -58995,7 +56499,6 @@ "supports_response_schema": true }, "llamagate/qwen3-vl-8b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 1.5e-07, @@ -59007,7 +56510,6 @@ "supports_vision": true }, "llamagate/llava-7b": { - "max_tokens": 2048, "max_input_tokens": 4096, "max_output_tokens": 2048, "input_cost_per_token": 1e-07, @@ -59018,7 +56520,6 @@ "supports_vision": true }, "llamagate/gemma3-4b": { - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -59030,7 +56531,6 @@ "supports_vision": true }, "llamagate/nomic-embed-text": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 2e-08, "output_cost_per_token": 0, @@ -59038,7 +56538,6 @@ "mode": "embedding" }, "llamagate/qwen3-embedding-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "input_cost_per_token": 2e-08, "output_cost_per_token": 0, @@ -59046,7 +56545,6 @@ "mode": "embedding" }, "libertai/hermes-3-8b-tee": { - "max_tokens": 16000, "max_input_tokens": 16000, "max_output_tokens": 16000, "input_cost_per_token": 1.5e-07, @@ -59060,7 +56558,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/gemma-4-31b-it": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59074,7 +56571,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/gemma-4-31b-it-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59089,7 +56585,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-27b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59103,7 +56598,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-27b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59118,7 +56612,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-35b-a3b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59132,7 +56625,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-35b-a3b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59147,7 +56639,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.5-122b-a10b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.5e-07, @@ -59161,7 +56652,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.5-122b-a10b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.5e-07, @@ -59176,7 +56666,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/deepseek-v4-flash": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 2.5e-07, @@ -59190,7 +56679,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/deepseek-v4-flash-thinking": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 2.5e-07, @@ -59205,7 +56693,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/bge-m3": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -59221,7 +56708,6 @@ "litellm_provider": "sarvam", "max_input_tokens": 8192, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0, "supports_reasoning": true @@ -59297,7 +56783,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -59325,7 +56810,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -59357,7 +56841,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -59467,7 +56950,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_token": 0.0, @@ -59488,7 +56970,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_token": 0.0, @@ -59511,7 +56992,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59537,7 +57017,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59564,7 +57043,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59599,7 +57077,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59634,7 +57111,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59668,7 +57144,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59701,7 +57176,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59729,7 +57203,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59758,7 +57231,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59796,7 +57268,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59833,7 +57304,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -59860,7 +57330,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 9e-06, "output_cost_per_token": 9e-06, @@ -59885,7 +57354,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 6e-06, "output_cost_per_token": 6e-06, @@ -59905,7 +57373,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 1e-05, "output_cost_per_token": 1e-05, @@ -59925,7 +57392,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -59984,7 +57450,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -60044,7 +57509,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -60106,7 +57570,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -60167,7 +57630,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -60218,7 +57680,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -60257,7 +57718,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -60299,7 +57759,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -60325,7 +57784,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 1.5e-07, "output_cost_per_token_priority": 5.25e-07, @@ -60351,7 +57809,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -60375,7 +57832,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1e-07, "output_cost_per_token_priority": 3.5e-07, @@ -60405,7 +57861,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60449,7 +57904,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60484,7 +57938,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-cyber.html", "use_openai_responses_path": true, @@ -60520,7 +57973,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60560,7 +58012,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60601,7 +58052,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html", "supported_modalities": [ @@ -60638,7 +58088,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html", "supported_modalities": [ @@ -60675,7 +58124,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html", "supported_modalities": [ @@ -60712,7 +58160,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html", "supported_modalities": [ @@ -60749,7 +58196,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html", "supported_modalities": [ @@ -60783,7 +58229,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-54.html", "supported_modalities": [ @@ -60815,7 +58260,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-54.html", "supported_modalities": [ @@ -60847,7 +58291,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-55.html", "supported_modalities": [ @@ -60879,7 +58322,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-55.html", "supported_modalities": [ @@ -60914,7 +58356,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html", "supported_modalities": [ @@ -60955,7 +58396,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token_above_272k_tokens_ultrafast": 0.000495, "output_cost_per_token_ultrafast": 0.00033, @@ -60996,7 +58436,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61035,7 +58474,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61082,7 +58520,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_ultrafast": 0.000495, "output_cost_per_token_ultrafast": 0.00033, @@ -61123,7 +58560,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61162,7 +58598,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61207,7 +58642,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_ultrafast": 0.00045, "output_cost_per_token_ultrafast": 0.0003, @@ -61247,7 +58681,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61282,7 +58715,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61320,7 +58752,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61355,7 +58786,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61395,7 +58825,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61435,7 +58864,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61470,7 +58898,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token_batches": 2e-07, "output_cost_per_token_priority": 7e-07, @@ -61496,7 +58923,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token_batches": 2e-07, "output_cost_per_token_priority": 7e-07, @@ -61522,7 +58948,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_batches": 4e-08, "output_cost_per_token_priority": 1.4e-07, @@ -61552,7 +58977,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1048576, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, "output_cost_per_token_flex": 1.25e-06, @@ -61580,7 +59004,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3.3e-06, "output_cost_per_token_priority": 1.155e-05, @@ -61605,7 +59028,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -61636,7 +59058,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -61674,7 +59095,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -61722,7 +59142,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3.3e-06, "output_cost_per_token_priority": 1.155e-05, @@ -61750,7 +59169,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3e-06, "output_cost_per_token_priority": 1.05e-05, @@ -61765,7 +59183,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61805,7 +59222,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4.3125e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61821,7 +59237,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 1024000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4.3125e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61839,7 +59254,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 1024000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3.88126e-07, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61856,7 +59270,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.15625e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61870,7 +59283,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61908,7 +59320,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61946,7 +59357,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -62041,7 +59451,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -62059,7 +59468,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -62080,7 +59488,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -62110,7 +59517,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -62132,7 +59538,6 @@ } }, "snowflake/claude-sonnet-4-5": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62150,7 +59555,6 @@ "snowflake/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "supports_legacy_thinking": true, - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62166,7 +59570,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-4-sonnet": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62182,7 +59585,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-4-opus": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 5e-06, @@ -62199,7 +59601,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-haiku-4-5": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 1e-06, @@ -62215,7 +59616,6 @@ "prompt_cache_min_tokens": 4096 }, "snowflake/claude-3-7-sonnet": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62231,7 +59631,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-4.1": { - "max_tokens": 16384, "max_input_tokens": 300000, "max_output_tokens": 16384, "input_cost_per_token": 2e-06, @@ -62246,7 +59645,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5": { - "max_tokens": 16384, "max_input_tokens": 300000, "max_output_tokens": 16384, "input_cost_per_token": 1.25e-06, @@ -62262,7 +59660,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5-mini": { - "max_tokens": 16384, "max_input_tokens": 1000000, "max_output_tokens": 16384, "input_cost_per_token": 3e-07, @@ -62274,7 +59671,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5-nano": { - "max_tokens": 16384, "max_input_tokens": 5000000, "max_output_tokens": 16384, "input_cost_per_token": 1.5e-07, @@ -62286,7 +59682,6 @@ "supports_response_schema": true }, "snowflake/llama4-maverick": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 2.4e-07, @@ -62297,7 +59692,6 @@ "supports_system_messages": true }, "snowflake/snowflake-arctic-embed-l-v2.0": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 7e-08, "output_cost_per_token": 0.0, @@ -62305,7 +59699,6 @@ "mode": "embedding" }, "snowflake/snowflake-arctic-embed-m-v2.0": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 7e-08, "output_cost_per_token": 0.0, @@ -62315,7 +59708,7 @@ "soniox/stt-async-v4": { "litellm_provider": "soniox", "max_output_tokens": 8000, - "max_tokens": 8000, + "max_input_tokens": 8000, "input_cost_per_second": 0.0, "output_cost_per_second": 2.77778e-05, "mode": "audio_transcription", @@ -62328,7 +59721,7 @@ "soniox/stt-async-v5": { "litellm_provider": "soniox", "max_output_tokens": 8000, - "max_tokens": 8000, + "max_input_tokens": 8000, "input_cost_per_second": 0.0, "output_cost_per_second": 2.77778e-05, "mode": "audio_transcription", @@ -62505,7 +59898,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62559,7 +59951,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62613,7 +60004,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62667,7 +60057,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -62721,7 +60110,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62775,7 +60163,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62829,7 +60216,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62883,7 +60269,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -62937,7 +60322,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -62963,7 +60347,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -62989,7 +60372,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -63044,7 +60426,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -63099,7 +60480,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63124,7 +60504,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63149,7 +60528,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63173,7 +60551,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63197,7 +60574,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63221,7 +60597,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63245,7 +60620,6 @@ "litellm_provider": "tencent", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63269,7 +60643,6 @@ "litellm_provider": "tencent", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63336,7 +60709,6 @@ "source": "https://docs.devin.ai/desktop/models" }, "pinstripes/ps/glm-4.5-air": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.25e-07, @@ -63349,7 +60721,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3.6-35b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -63362,7 +60733,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-08, @@ -63375,7 +60745,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3-coder-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-07, @@ -63388,7 +60757,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/deepseek-v4-flash": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1e-07, @@ -63401,7 +60769,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/minimax-m2.7": { - "max_tokens": 1000192, "max_input_tokens": 1000192, "max_output_tokens": 1000192, "input_cost_per_token": 2.55e-07, @@ -63418,7 +60785,6 @@ "litellm_provider": "darkbloom", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.65e-07, "source": "https://www.darkbloom.dev/", @@ -63435,7 +60801,6 @@ "litellm_provider": "darkbloom", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7e-08, "source": "https://www.darkbloom.dev/", @@ -63455,7 +60820,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -63482,7 +60846,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -63511,7 +60874,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.x.ai/v1/language-models", @@ -63584,7 +60946,6 @@ "litellm_provider": "openai", "max_input_tokens": 16000, "max_output_tokens": 2000, - "max_tokens": 2000, "mode": "realtime", "source": "https://developers.openai.com/api/docs/pricing", "supported_modalities": [ @@ -63609,7 +60970,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -63653,7 +61013,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -63696,7 +61055,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "prompt_cache_min_tokens": 512, @@ -63732,7 +61090,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 5e-06, "search_context_cost_per_query": { @@ -63767,7 +61124,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -63788,7 +61144,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/models/model-cards/leanstral-1-5", @@ -63878,7 +61233,6 @@ "mode": "chat", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_vision": true, @@ -63977,7 +61331,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 2.1e-05, "output_cost_per_token": 2.1e-05, @@ -64006,7 +61359,6 @@ "litellm_provider": "gemini", "max_input_tokens": 98304, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "audio_transcription", "output_cost_per_token": 1.2e-05, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -64031,7 +61383,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "audio_transcription", "output_cost_per_token": 2.1e-05, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -64112,7 +61463,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64122,7 +61472,6 @@ "input_cost_per_token": 5e-08, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560, @@ -64132,7 +61481,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64142,7 +61490,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64152,7 +61499,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64162,7 +61508,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64172,7 +61517,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 120000, - "max_tokens": 120000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64182,7 +61526,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64198,7 +61541,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64217,7 +61559,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -64237,7 +61578,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64257,7 +61597,7 @@ "input_cost_per_token_priority": 2.75e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64275,7 +61615,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_priority": 1.875e-05, @@ -64300,7 +61639,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64319,7 +61657,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -64339,7 +61676,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64359,7 +61695,7 @@ "input_cost_per_token_priority": 2.75e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64376,7 +61712,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64393,7 +61728,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64411,7 +61745,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_priority": 1.875e-05, @@ -64433,7 +61766,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "reasoning_effort_levels": [ @@ -64456,7 +61788,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "output_cost_per_token_priority": 2.8125e-05, @@ -64498,7 +61829,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64518,7 +61848,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_priority": 2.5e-07, @@ -64538,7 +61867,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.4e-06, "output_cost_per_token_priority": 3e-06, @@ -64558,7 +61886,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64593,7 +61920,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_priority": 2.5e-07, @@ -64613,7 +61939,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.4e-06, "output_cost_per_token_priority": 3e-06, @@ -64648,7 +61973,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -64665,7 +61989,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64681,7 +62004,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "reasoning_effort_levels": [ @@ -64704,7 +62026,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "output_cost_per_token_priority": 2.8125e-05, @@ -64726,7 +62047,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -64743,7 +62063,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://novita.ai/pricing", @@ -64760,7 +62079,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://novita.ai/pricing", @@ -64777,7 +62095,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 5.8e-07, "source": "https://novita.ai/pricing", @@ -64794,7 +62111,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://novita.ai/pricing", @@ -64811,7 +62127,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://novita.ai/pricing", @@ -64828,7 +62143,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -64845,7 +62159,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://novita.ai/pricing", @@ -64862,7 +62175,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://novita.ai/pricing", @@ -64878,7 +62190,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -64895,7 +62206,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://novita.ai/pricing", @@ -64912,7 +62222,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -64929,7 +62238,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://novita.ai/pricing", @@ -64945,7 +62253,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://novita.ai/pricing", @@ -64962,7 +62269,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://novita.ai/pricing", @@ -64978,7 +62284,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.6e-06, "source": "https://novita.ai/pricing", @@ -64994,7 +62299,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://novita.ai/pricing", @@ -65010,7 +62314,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.0000000000000002e-07, "source": "https://novita.ai/pricing", @@ -65026,7 +62329,6 @@ "litellm_provider": "novita", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.13e-06, "source": "https://novita.ai/pricing", @@ -65042,7 +62344,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.3600000000000004e-07, "source": "https://novita.ai/pricing", @@ -65059,7 +62360,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://novita.ai/pricing", @@ -65076,7 +62376,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.044e-06, "source": "https://novita.ai/pricing", @@ -65092,7 +62391,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.6000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65108,7 +62406,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.4e-06, "source": "https://novita.ai/pricing", @@ -65125,7 +62422,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://novita.ai/pricing", @@ -65142,7 +62438,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65159,7 +62454,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65175,7 +62469,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65190,7 +62483,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65206,7 +62498,6 @@ "litellm_provider": "novita", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65223,7 +62514,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -65240,7 +62530,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://novita.ai/pricing", @@ -65256,7 +62545,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://novita.ai/pricing", @@ -65271,7 +62559,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65286,7 +62573,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://novita.ai/pricing", @@ -65301,7 +62587,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.6000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65317,7 +62602,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -65334,7 +62618,6 @@ "litellm_provider": "novita", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65350,7 +62633,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://novita.ai/pricing", @@ -65364,7 +62646,6 @@ "litellm_provider": "novita", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-08, "source": "https://novita.ai/pricing", @@ -65376,7 +62657,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://novita.ai/pricing", @@ -65393,7 +62673,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65410,7 +62689,6 @@ "litellm_provider": "novita", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65426,7 +62704,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.4850000000000002e-06, "source": "https://novita.ai/pricing", @@ -65441,7 +62718,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 8.900000000000001e-07, "source": "https://api.novita.ai/v3/openai/models", @@ -65454,7 +62730,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65468,7 +62743,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8.900000000000001e-07, "source": "https://api.novita.ai/v3/openai/models", @@ -65481,7 +62755,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65495,7 +62768,6 @@ "litellm_provider": "novita", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.66e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65509,7 +62781,6 @@ "litellm_provider": "novita", "max_input_tokens": 131000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2e-08, "source": "https://api.novita.ai/v3/openai/models", @@ -65518,7 +62789,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Flash": { "supports_reasoning": true, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1049000, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, @@ -65532,7 +62803,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Flash-0731": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1.3e-07, "output_cost_per_token": 2.8e-07, @@ -65545,7 +62816,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Pro": { "supports_reasoning": true, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1049000, "input_cost_per_token": 1.15e-06, "output_cost_per_token": 2.55e-06, @@ -65559,7 +62830,7 @@ }, "wandb/google/gemma-4-31B-it": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1e-07, "output_cost_per_token": 3.4e-07, @@ -65570,7 +62841,7 @@ }, "wandb/ibm-granite/granite-4.1-8b": { "deprecation_date": "2026-10-05", - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131000, "input_cost_per_token": 5e-08, "output_cost_per_token": 1e-07, @@ -65581,7 +62852,7 @@ }, "wandb/JetBrains/Mellum2-12B-A2.5B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131000, "input_cost_per_token": 5e-08, "output_cost_per_token": 1e-07, @@ -65592,7 +62863,7 @@ }, "wandb/meta-llama/Llama-3.1-70B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 128000, + "max_output_tokens": 128000, "max_input_tokens": 131000, "input_cost_per_token": 8e-07, "output_cost_per_token": 8e-07, @@ -65603,7 +62874,7 @@ }, "wandb/MiniMaxAI/MiniMax-M3": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.3e-07, "output_cost_per_token": 9.6e-07, @@ -65616,7 +62887,7 @@ }, "wandb/moonshotai/Kimi-K2.7-Code": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 7.1e-07, "output_cost_per_token": 3.5e-06, @@ -65629,7 +62900,7 @@ }, "wandb/moonshotai/Kimi-K2.6": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 6.5e-07, "output_cost_per_token": 3.41e-06, @@ -65642,7 +62913,7 @@ }, "wandb/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 7e-08, "output_cost_per_token": 2e-07, @@ -65655,7 +62926,7 @@ }, "wandb/nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 5e-07, "output_cost_per_token": 2.15e-06, @@ -65668,7 +62939,7 @@ }, "wandb/OpenPipe/Qwen3-14B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 32768, + "max_output_tokens": 32768, "max_input_tokens": 32800, "input_cost_per_token": 5e-08, "output_cost_per_token": 2.2e-07, @@ -65679,7 +62950,7 @@ }, "wandb/Qwen/Qwen3.8-27B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 4e-07, "output_cost_per_token": 3e-06, @@ -65692,7 +62963,7 @@ }, "wandb/Qwen/Qwen3.6-35B-A3B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.25e-06, @@ -65703,7 +62974,7 @@ }, "wandb/Qwen/Qwen3.6-27B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 6e-07, "output_cost_per_token": 3.6e-06, @@ -65718,7 +62989,7 @@ "wandb/Qwen/Qwen3.5-35B-A3B": { "deprecation_date": "2026-10-05", "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.25e-06, @@ -65729,7 +63000,7 @@ }, "wandb/Qwen/Qwen3-30B-A3B-Instruct-2507": { "deprecation_date": "2026-10-05", - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1e-07, "output_cost_per_token": 3e-07, @@ -65762,7 +63033,7 @@ }, "wandb/zai-org/GLM-5.2": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 1049000, "input_cost_per_token": 7.6e-07, "output_cost_per_token": 2.42e-06, @@ -65774,7 +63045,7 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "deepinfra/openai/gpt-oss-120b-Turbo": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, @@ -65788,7 +63059,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M2.7": { - "max_tokens": 196608, + "max_output_tokens": 196608, "max_input_tokens": 196608, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1e-06, @@ -65804,7 +63075,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4e-07, "output_cost_per_token": 3e-06, @@ -65820,7 +63091,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it-Ultra": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2.7e-07, "output_cost_per_token": 7.6e-07, @@ -65834,7 +63105,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.5": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 2.25e-06, @@ -65850,7 +63121,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.7-Flash": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 6e-08, "output_cost_per_token": 4e-07, @@ -65866,7 +63137,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.6": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 5e-07, "output_cost_per_token": 2e-06, @@ -65885,7 +63156,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 1024, @@ -65899,7 +63170,7 @@ "supports_vision": true }, "deepinfra/anthropic/claude-sonnet-4-6": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 3e-06, "output_cost_per_token": 1.5e-05, @@ -65915,7 +63186,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.5-flash": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 1.5e-06, "output_cost_per_token": 9e-06, @@ -65930,7 +63201,7 @@ "supports_audio_input": true }, "deepinfra/XiaomiMiMo/MiMo-V2.5": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4e-07, "output_cost_per_token": 2e-06, @@ -65946,7 +63217,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.2e-06, "output_cost_per_token": 6e-06, @@ -65961,7 +63232,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it-turbo": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 9e-08, "output_cost_per_token": 3.4e-07, @@ -65976,7 +63247,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/thinkingmachines/Inkling-Small": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 1.2e-06, @@ -65992,7 +63263,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-models/Muse-Glimmer-30B": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, @@ -66008,7 +63279,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Max-Thinking": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.2e-06, "output_cost_per_token": 6e-06, @@ -66023,7 +63294,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-VL-235B-A22B-Instruct": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-07, "output_cost_per_token": 8.8e-07, @@ -66038,7 +63309,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-VL-30B-A3B-Instruct": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, @@ -66051,7 +63322,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2.6e-07, "output_cost_per_token": 2.6e-06, @@ -66065,7 +63336,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.6-35B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1e-07, "output_cost_per_token": 9.5e-07, @@ -66079,7 +63350,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/Nemotron-Content-Safety-3.5": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-07, "output_cost_per_token": 2e-07, @@ -66092,7 +63363,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 512, @@ -66106,7 +63377,7 @@ "supports_vision": true }, "deepinfra/thinkingmachines/Inkling": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 9.5e-07, "output_cost_per_token": 4.05e-06, @@ -66122,7 +63393,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 3.5e-06, @@ -66138,7 +63409,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Pro-0813": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1.3e-06, "output_cost_per_token": 2.6e-06, @@ -66153,7 +63424,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.7-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 2.5e-06, "output_cost_per_token": 7.5e-06, @@ -66168,7 +63439,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-mini": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1e-07, "output_cost_per_token": 4e-07, @@ -66184,7 +63455,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-2.4T-A95B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -66200,7 +63471,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M3": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 2.8e-07, "output_cost_per_token": 1.1e-06, @@ -66216,7 +63487,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.1-flash-lite": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.5e-06, @@ -66231,7 +63502,7 @@ "supports_audio_input": true }, "deepinfra/google/gemini-3.7-flash": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 3.75e-06, @@ -66247,7 +63518,7 @@ "supports_audio_input": true }, "deepinfra/inclusionAI/Ling-3.0-flash": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 6e-08, "output_cost_per_token": 1.8e-07, @@ -66263,7 +63534,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/stepfun-ai/Step-3.7-Flash": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-07, "output_cost_per_token": 1.15e-06, @@ -66277,7 +63548,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-35B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 1e-06, @@ -66293,7 +63564,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-1.8": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 2e-06, @@ -66309,7 +63580,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/tencent/Hy3": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 5.8e-07, @@ -66325,7 +63596,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-code": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 5e-07, "output_cost_per_token": 3e-06, @@ -66341,7 +63612,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-pro": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 5e-07, "output_cost_per_token": 3e-06, @@ -66357,7 +63628,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 6e-07, "output_cost_per_token": 2.08e-06, @@ -66373,7 +63644,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/Nemotron-3-Nano-30B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 5e-08, "output_cost_per_token": 2e-07, @@ -66388,7 +63659,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.7-Code": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 6.8e-07, "output_cost_per_token": 3.4e-06, @@ -66407,7 +63678,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1e-05, "prompt_cache_min_tokens": 1024, @@ -66421,7 +63692,7 @@ "supports_vision": true }, "deepinfra/Qwen/Qwen3.5-397B-A17B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 3e-06, @@ -66437,7 +63708,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 8e-08, "output_cost_per_token": 1.8e-07, @@ -66453,7 +63724,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-E4B-it": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-08, "output_cost_per_token": 1e-07, @@ -66466,7 +63737,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.2": { - "max_tokens": 163840, + "max_output_tokens": 163840, "max_input_tokens": 163840, "input_cost_per_token": 2.6e-07, "output_cost_per_token": 3.8e-07, @@ -66481,7 +63752,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.65e-06, "output_cost_per_token": 4.951e-06, @@ -66499,7 +63770,7 @@ "input_cost_per_token": 1e-05, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-05, "prompt_cache_min_tokens": 512, @@ -66514,7 +63785,7 @@ "thinking_always_on": true }, "deepinfra/nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 5e-07, "output_cost_per_token": 2.2e-06, @@ -66530,7 +63801,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-122B-A10B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2.9e-07, "output_cost_per_token": 2.4e-06, @@ -66544,7 +63815,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5.1": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 1.05e-06, "output_cost_per_token": 3.5e-06, @@ -66560,7 +63831,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Pro": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1.3e-06, "output_cost_per_token": 2.6e-06, @@ -66575,7 +63846,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 8.5e-08, "output_cost_per_token": 4e-07, @@ -66589,7 +63860,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5.2": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 2.4e-06, @@ -66605,7 +63876,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K3": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 2.85e-06, "output_cost_per_token": 1.425e-05, @@ -66623,7 +63894,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 2048, @@ -66637,7 +63908,7 @@ "supports_vision": true }, "deepinfra/Qwen/Qwen3.6-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 3.2e-07, "output_cost_per_token": 3.2e-06, @@ -66651,7 +63922,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-26B-A4B-it": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 7e-08, "output_cost_per_token": 3.4e-07, @@ -66665,7 +63936,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.1-pro": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 2e-06, "output_cost_per_token": 1.2e-05, @@ -66680,7 +63951,7 @@ "supports_audio_input": true }, "deepinfra/XiaomiMiMo/MiMo-V2.5-Pro": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1e-06, "output_cost_per_token": 3e-06, @@ -66696,7 +63967,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/anthropic/claude-haiku-4-5": { - "max_tokens": 200000, + "max_output_tokens": 200000, "max_input_tokens": 200000, "input_cost_per_token": 1e-06, "output_cost_per_token": 5e-06, @@ -66711,7 +63982,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Flash": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 9e-08, "output_cost_per_token": 1.8e-07, @@ -66727,7 +63998,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4.1-Flash": { - "max_tokens": 131072, "max_input_tokens": 1048576, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -66744,7 +64014,6 @@ "source": "https://deepinfra.com/deepseek-ai/DeepSeek-V4.1-Flash" }, "deepinfra/deepseek-ai/DeepSeek-V4.1-Flash-1017": { - "max_tokens": 131072, "max_input_tokens": 1048576, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -66761,7 +64030,7 @@ "source": "https://deepinfra.com/deepseek-ai/DeepSeek-V4.1-Flash-1017" }, "deepinfra/openai/gpt-oss-120b-Ultra": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-07, "output_cost_per_token": 9.5e-07, @@ -66775,7 +64044,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-9B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1e-07, "output_cost_per_token": 1.5e-07, @@ -66788,7 +64057,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M2.7-Turbo": { - "max_tokens": 196608, + "max_output_tokens": 196608, "max_input_tokens": 196608, "input_cost_per_token": 3.8e-07, "output_cost_per_token": 1.7e-06, @@ -66804,7 +64073,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.7": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 4e-07, "output_cost_per_token": 1.75e-06, @@ -66820,7 +64089,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.3e-07, "output_cost_per_token": 3.8e-07, @@ -66839,7 +64108,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -66874,7 +64142,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -66902,7 +64169,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -66930,7 +64196,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67206,7 +64471,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67233,7 +64497,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67260,7 +64523,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67291,7 +64553,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67319,7 +64580,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://console.groq.com/docs/model/qwen/qwen3.8-27b", @@ -67335,7 +64595,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67356,7 +64615,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67377,7 +64635,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67398,7 +64655,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -67419,7 +64675,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -67434,7 +64689,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -67449,7 +64703,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -67550,7 +64803,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/models/model-cards/leanstral-1-5", @@ -67566,7 +64818,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -67585,7 +64836,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_priority": 8.25e-06, @@ -67604,7 +64854,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -67623,7 +64872,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_priority": 8.25e-06, @@ -67639,7 +64887,7 @@ "input_cost_per_token": 2.1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -67681,7 +64929,7 @@ "input_cost_per_token": 2.1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -67698,7 +64946,7 @@ "input_cost_per_token_priority": 1.875e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_priority": 6.25e-07, @@ -67716,7 +64964,7 @@ "input_cost_per_token_priority": 2.8125e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 7.5e-07, "output_cost_per_token_priority": 9.375e-07, @@ -67733,7 +64981,7 @@ "input_cost_per_token_priority": 1.875e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_priority": 6.25e-07, @@ -67751,7 +64999,7 @@ "input_cost_per_token_priority": 2.8125e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 7.5e-07, "output_cost_per_token_priority": 9.375e-07, @@ -67769,7 +65017,7 @@ "input_cost_per_token_priority": 1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.05e-06, "output_cost_per_token_priority": 4.05e-06, @@ -67785,7 +65033,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 40960, "max_output_tokens": 40960, - "max_tokens": 40960, "mode": "embedding", "source": "https://api.fireworks.ai/v1/serverless/models" }, @@ -67808,7 +65055,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://api.together.ai/v1/models" @@ -67868,7 +65115,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 131072, "max_output_tokens": 40960, - "max_tokens": 40960, "mode": "chat", "output_cost_per_token": 1.49e-06, "source": "https://api.cerebras.ai/public/v1/models/gemma-4-31b", @@ -67894,7 +65140,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://www.scaleway.com/en/pricing/model-as-a-service/", @@ -67908,7 +65153,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://www.scaleway.com/en/pricing/model-as-a-service/", @@ -67924,7 +65168,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -67947,7 +65190,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true, @@ -67989,7 +65231,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68004,7 +65245,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.1e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68019,7 +65259,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68034,7 +65273,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.25e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68050,7 +65288,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68067,7 +65304,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68080,7 +65316,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68155,7 +65390,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68173,7 +65407,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -68189,7 +65422,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -68204,7 +65436,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68222,7 +65453,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -68242,7 +65472,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -68267,7 +65496,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -68306,7 +65534,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -68344,7 +65571,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -68381,7 +65607,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -68419,7 +65644,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -68454,7 +65678,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68472,7 +65695,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -68488,7 +65710,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -68503,7 +65724,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68521,7 +65741,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -68541,7 +65760,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -68566,7 +65784,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -68600,7 +65817,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -68633,7 +65849,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -68665,7 +65880,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -68698,7 +65912,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -68727,7 +65940,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68763,7 +65975,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68799,7 +66010,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68830,7 +66040,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1048576, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -68854,7 +66063,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -68873,7 +66081,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68892,7 +66099,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68911,7 +66117,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68931,7 +66136,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", "supported_endpoints": [ @@ -68951,7 +66155,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", "supported_endpoints": [ @@ -68974,7 +66177,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -69010,7 +66212,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -69039,7 +66240,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -69072,7 +66272,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -69092,7 +66291,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", "supported_endpoints": [ @@ -69112,7 +66310,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", "supported_endpoints": [ @@ -69136,7 +66333,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 8.4e-07, "output_cost_per_token_priority": 2.94e-06, @@ -69158,7 +66354,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, "output_cost_per_token_priority": 4.38e-06, @@ -69180,7 +66375,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 4.4e-07, "output_cost_per_token_priority": 1.54e-06, @@ -69202,7 +66396,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 32000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -69225,7 +66418,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -69247,7 +66439,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 9e-07, "output_cost_per_token_priority": 3.15e-06, @@ -69268,7 +66459,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, "output_cost_per_token_priority": 2.1e-06, @@ -69290,7 +66480,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 1.33e-06, "output_cost_per_token_priority": 4.66e-06, @@ -69309,7 +66498,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.375e-05, "supported_endpoints": [ @@ -69343,7 +66531,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.05e-06, "supports_prompt_caching": true, @@ -69356,7 +66543,6 @@ "input_cost_per_token": 1.63e-07, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -69364,7 +66550,6 @@ "input_cost_per_token": 2.5e-08, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -69395,7 +66580,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -69420,7 +66604,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -69445,7 +66628,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -69470,7 +66652,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/models/lyria-realtime-exp", @@ -69843,7 +67024,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -69867,7 +67047,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://inference.baseten.co/v1/models", @@ -69892,7 +67071,7 @@ "output_cost_per_token": 1.5e-07, "source": "https://api.together.ai/v1/models", "max_input_tokens": 128000, - "max_tokens": 128000 + "max_output_tokens": 128000 }, "vertex_ai/gemini-2.5-flash-native-audio": { "deprecation_date": "2026-12-13", @@ -69953,7 +67132,7 @@ "input_cost_per_token": 1.5e-06, "litellm_provider": "vertex_ai", "max_output_tokens": 57920, - "max_tokens": 57920, + "max_input_tokens": 57920, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -70016,7 +67195,7 @@ "output_cost_per_token": 3.5e-06, "source": "https://api.together.ai/v1/models", "max_input_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "together_ai/meta-llama/Llama-3.2-1B-Instruct": { "input_cost_per_token": 6e-08, @@ -70025,7 +67204,7 @@ "output_cost_per_token": 6e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 131072, - "max_tokens": 131072 + "max_output_tokens": 131072 }, "together_ai/meta-llama/Llama-3.2-3B-Instruct": { "input_cost_per_token": 6e-08, @@ -70034,7 +67213,7 @@ "output_cost_per_token": 6e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 131072, - "max_tokens": 131072 + "max_output_tokens": 131072 }, "together_ai/Qwen/Qwen2-1.5B-Instruct": { "input_cost_per_token": 2e-08, @@ -70043,7 +67222,7 @@ "output_cost_per_token": 2e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Qwen/Qwen2.5-14B-Instruct": { "input_cost_per_token": 8e-07, @@ -70052,7 +67231,7 @@ "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Qwen/Qwen2.5-72B-Instruct": { "input_cost_per_token": 1.2e-06, @@ -70061,14 +67240,13 @@ "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Salesforce/Llama-Rank-V1": { "deprecation_date": "2026-02-06", "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://api.together.ai/v1/models" @@ -70077,7 +67255,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "completion", "output_cost_per_token": 2e-07, "source": "https://api.together.xyz/v1/models" @@ -70095,7 +67273,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models" @@ -70105,7 +67283,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70115,7 +67293,7 @@ "input_cost_per_token": 9e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://api.together.ai/v1/models" @@ -70125,7 +67303,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70135,7 +67313,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70145,7 +67323,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70155,7 +67333,7 @@ "input_cost_per_token": 1.95e-06, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://api.together.ai/v1/models" @@ -70165,7 +67343,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70175,7 +67353,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70185,7 +67363,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70195,7 +67373,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70205,7 +67383,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70215,7 +67393,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://api.together.ai/v1/models" @@ -70226,7 +67404,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.6e-06, "source": "https://api.together.ai/v1/models" @@ -70236,7 +67414,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70246,7 +67424,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://api.together.ai/v1/models" @@ -70256,7 +67434,7 @@ "input_cost_per_token": 1.6e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.together.ai/v1/models" @@ -70266,7 +67444,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.7e-06, "source": "https://api.together.ai/v1/models" @@ -70276,7 +67454,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70286,7 +67464,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70296,7 +67474,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 9.7e-07, "source": "https://api.together.ai/v1/models" @@ -70306,7 +67484,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70316,7 +67494,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.9e-07, "source": "https://api.together.ai/v1/models" @@ -70326,7 +67504,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70336,7 +67514,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70346,7 +67524,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70356,7 +67534,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://api.together.ai/v1/models" @@ -70366,7 +67544,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70376,7 +67554,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://api.together.ai/v1/models" @@ -70386,7 +67564,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models" @@ -70397,7 +67575,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://api.together.ai/v1/models" @@ -70408,7 +67586,7 @@ "input_cost_per_token": 9.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.together.ai/v1/models" @@ -70418,7 +67596,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70429,7 +67607,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 512288, - "max_tokens": 512288, + "max_output_tokens": 512288, "mode": "chat", "output_cost_per_token": 3.6e-06, "source": "https://api.together.ai/v1/models" @@ -70439,7 +67617,7 @@ "input_cost_per_token": 5e-08, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70449,7 +67627,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.1e-06, "source": "https://api.together.ai/v1/models" @@ -70459,7 +67637,7 @@ "input_cost_per_token": 4.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70469,7 +67647,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://api.together.ai/v1/models" @@ -70480,7 +67658,7 @@ "input_cost_per_token": 1.4e-06, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models" @@ -70547,7 +67725,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_batches": 8.25e-06, @@ -70720,7 +67897,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_above_272k_tokens": 9e-05, @@ -70764,7 +67940,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_above_272k_tokens": 9e-07, @@ -70813,7 +67988,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -70869,7 +68043,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -70883,7 +68056,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -70906,7 +68078,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -70920,7 +68091,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -70955,7 +68125,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -70977,7 +68146,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -71053,7 +68221,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_batches": 8.25e-06, @@ -71266,7 +68433,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -71314,7 +68480,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "audio_transcription", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supported_endpoints": [ @@ -71355,7 +68520,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supported_modalities": [ @@ -71373,7 +68537,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 512000, "max_output_tokens": 65500, - "max_tokens": 65500, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71386,7 +68549,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71399,7 +68561,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71414,7 +68575,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71435,7 +68595,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71453,7 +68612,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71473,7 +68631,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71494,7 +68651,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71513,7 +68669,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71528,7 +68683,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.61333e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71557,7 +68711,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71573,7 +68726,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71587,7 +68739,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71602,7 +68753,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.84e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71617,7 +68767,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.38e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71632,7 +68781,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.411e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71648,7 +68796,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.4246e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71665,7 +68812,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.0136e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71682,7 +68828,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.411e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71699,7 +68844,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.32375e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71716,7 +68860,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 119000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.5353e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71729,7 +68872,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71746,7 +68888,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71763,7 +68904,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71780,7 +68920,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71797,7 +68936,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.499999e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71815,7 +68953,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71832,7 +68969,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 3.9998e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71844,7 +68980,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 3.9998e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71857,7 +68992,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 7.889e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71872,7 +69006,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.9438e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71887,7 +69020,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.9438e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71903,7 +69035,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.09848e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71917,7 +69048,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71933,7 +69063,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71950,7 +69079,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71967,7 +69095,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71984,7 +69111,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72001,7 +69127,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72017,7 +69142,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 0.00018, "source": "https://aihubmix.com/api/v1/models", @@ -72034,7 +69158,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72051,7 +69174,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72068,7 +69190,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72084,7 +69205,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72129,7 +69249,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72145,7 +69264,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72161,7 +69279,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.248e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72177,7 +69294,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.535e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72193,7 +69309,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.9995e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72209,7 +69324,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.999e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72225,7 +69339,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72241,7 +69354,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.0984e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72254,7 +69366,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72290,7 +69401,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 204800, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.1832e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72304,7 +69414,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.152e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72329,7 +69438,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 5.48e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72341,7 +69449,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9.008e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72356,7 +69463,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9.864e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72371,7 +69477,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.532e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72386,7 +69491,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.524e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72403,7 +69507,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.608e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72420,7 +69523,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.128e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72437,7 +69539,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72454,7 +69555,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.80025e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72472,7 +69572,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5.07e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72657,7 +69756,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "wandb", "max_input_tokens": 1049000, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.5e-07, "source": "https://wandb.ai/site/pricing/tokens/", @@ -72670,7 +69769,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "wandb", "max_input_tokens": 262000, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://wandb.ai/site/pricing/tokens/", @@ -72711,7 +69810,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -72743,7 +69841,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_flex": 7.5e-06, @@ -72775,7 +69872,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -72797,7 +69893,6 @@ "litellm_provider": "xiaomi_mimo", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.7e-07, "source": "https://platform.xiaomimimo.com/static/docs/price/pay-as-you-go.md", @@ -72820,7 +69915,6 @@ "litellm_provider": "xiaomi_mimo", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://platform.xiaomimimo.com/static/docs/price/pay-as-you-go.md", @@ -72845,7 +69939,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -72871,7 +69964,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72893,7 +69985,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72915,7 +70006,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72937,7 +70027,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72958,7 +70047,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72980,7 +70068,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73001,7 +70088,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73023,7 +70109,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73045,7 +70130,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73066,7 +70150,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73088,7 +70171,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73110,7 +70192,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73131,7 +70212,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73153,7 +70233,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73178,7 +70257,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73203,7 +70281,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73228,7 +70305,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73249,7 +70325,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73271,7 +70346,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -73289,7 +70363,6 @@ "litellm_provider": "baseten", "max_input_tokens": 262000, "max_output_tokens": 262000, - "max_tokens": 262000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73307,7 +70380,6 @@ "litellm_provider": "baseten", "max_input_tokens": 262000, "max_output_tokens": 262000, - "max_tokens": 262000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73324,7 +70396,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://inference.baseten.co/v1/models", @@ -73341,7 +70412,6 @@ "litellm_provider": "baseten", "max_input_tokens": 202800, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73359,7 +70429,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://inference.baseten.co/v1/models", @@ -73377,7 +70446,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -73394,7 +70462,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73411,7 +70478,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -73428,7 +70494,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.6e-07, "source": "https://inference.baseten.co/v1/models", @@ -73446,7 +70511,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://inference.baseten.co/v1/models", @@ -73463,7 +70527,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://inference.baseten.co/v1/models", @@ -73480,7 +70543,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://inference.baseten.co/v1/models", @@ -73500,7 +70562,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73521,7 +70582,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73542,7 +70602,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73563,7 +70622,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.2e-06, @@ -73581,7 +70639,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "mode": "chat", "output_cost_per_token_priority": 1.2e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -73647,7 +70704,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -73665,7 +70721,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -73684,7 +70739,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#veo", @@ -73700,7 +70754,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -73717,7 +70770,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.5, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#veo", @@ -73764,7 +70816,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73797,7 +70848,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73830,7 +70880,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73863,7 +70912,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73901,7 +70949,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73934,7 +70981,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73970,7 +71016,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74009,7 +71054,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74049,7 +71093,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74087,7 +71130,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74126,7 +71168,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74159,7 +71200,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -74197,7 +71237,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74217,7 +71256,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74237,7 +71275,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74259,7 +71296,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-r1.html", @@ -74272,7 +71308,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -74285,7 +71320,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/microsoft/", @@ -74321,7 +71355,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74359,7 +71392,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -74405,7 +71437,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74447,7 +71478,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74484,7 +71514,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74521,7 +71550,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74562,7 +71590,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74609,7 +71636,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74651,7 +71677,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74783,7 +71808,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74829,7 +71853,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74874,7 +71897,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -74914,7 +71936,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -74947,7 +71968,6 @@ "litellm_provider": "prism", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 6.3e-07, "source": "https://prisminference.com/pricing", @@ -74972,7 +71992,6 @@ "litellm_provider": "prism", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.1e-07, "source": "https://prisminference.com/pricing", @@ -75005,7 +72024,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_flex": 3e-06, @@ -75031,7 +72049,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_flex": 3.3e-06, @@ -75049,7 +72066,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -75104,7 +72120,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -75172,7 +72187,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -75218,7 +72232,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "responses", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -75266,7 +72279,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -75301,7 +72313,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 9e-06, "output_cost_per_token": 9e-06, @@ -75315,7 +72326,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 6e-06, "output_cost_per_token": 6e-06, @@ -75332,7 +72342,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -75350,7 +72359,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/kimi/", @@ -75383,7 +72391,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.28e-06, "output_cost_per_token_batches": 2.64e-06, @@ -75416,7 +72423,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.808e-06, "output_cost_per_token_batches": 2.904e-06, @@ -75439,7 +72445,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2.75e-06, @@ -75454,7 +72459,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/model-router/", @@ -75474,7 +72478,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75519,7 +72522,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75565,7 +72567,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75609,7 +72610,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -75637,7 +72637,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -75670,7 +72669,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -75724,7 +72722,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -75771,7 +72768,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -75816,7 +72812,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -75859,7 +72854,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -75910,7 +72904,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -75957,7 +72950,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -75999,7 +72991,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76041,7 +73032,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76087,7 +73077,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76139,7 +73128,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -76186,7 +73174,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76245,7 +73232,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76296,7 +73282,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76346,7 +73331,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -76391,7 +73375,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -76430,7 +73413,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76441,7 +73423,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76452,7 +73433,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76488,7 +73468,6 @@ "input_cost_per_token": 8e-08, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048.0, @@ -76508,7 +73487,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048.0, @@ -76577,7 +73555,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -76623,7 +73600,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -76673,7 +73649,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 1e-05, "output_cost_per_token": 1e-05, @@ -76732,7 +73707,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 1e-05, "output_cost_per_token": 1e-05, @@ -76790,7 +73764,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 2e-05, "output_cost_per_token": 2e-05, @@ -76838,7 +73811,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -76851,7 +73823,6 @@ "supports_vision": true }, "openinfer/@oi/GLM-5.3-Flash": { - "max_tokens": 393216, "max_input_tokens": 393216, "max_output_tokens": 393216, "input_cost_per_token": 1.95e-07, @@ -76863,7 +73834,6 @@ "source": "https://api.openinfer.ai/v1/models" }, "openinfer/@oi/Qwen3.5-27B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.95e-07, @@ -76875,7 +73845,6 @@ "source": "https://api.openinfer.ai/v1/models" }, "openinfer/@oi/Qwen3.5-9B": { - "max_tokens": 100000, "max_input_tokens": 100000, "max_output_tokens": 100000, "input_cost_per_token": 5e-06, @@ -76954,7 +73923,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://api.together.ai/v1/models", @@ -76977,7 +73946,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.311e-06, "output_cost_per_token_batches": 1.6555e-06, @@ -76999,7 +73967,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -77023,7 +73990,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-05, "prompt_cache_min_tokens": 512.0, @@ -77053,7 +74019,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.25e-06, "source": "https://inference.baseten.co/v1/models", @@ -77071,7 +74036,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -77087,7 +74051,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "wandb", "max_input_tokens": 1049000, - "max_tokens": 943718, + "max_output_tokens": 943718, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://openrouter.ai/api/v1/models/nvidia/nemotron-3.5-super-vl/endpoints", @@ -77103,7 +74067,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -77117,7 +74081,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -77131,7 +74094,7 @@ "input_cost_per_token": 2.5e-07, "litellm_provider": "nebius", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://tokenfactory.nebius.com/models/catalog/text2text/reflection%2Fbeam", @@ -77144,7 +74107,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -77161,7 +74123,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -77180,7 +74141,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -77200,7 +74160,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -77222,7 +74181,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.25e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -77244,7 +74202,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77266,7 +74223,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77288,7 +74244,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77307,7 +74262,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -77344,7 +74299,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 9e-07, @@ -77380,7 +74334,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -77429,7 +74382,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -77459,7 +74411,6 @@ "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, "cache_read_input_token_cost": 5e-07, - "max_tokens": 500000, "max_output_tokens": 500000, "max_input_tokens": 500000, "supports_function_calling": true, @@ -77497,7 +74448,6 @@ "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 1e-08, "cache_creation_input_token_cost": 1.25e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 922000, "supports_function_calling": true, @@ -77537,7 +74487,6 @@ "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 2e-08, "cache_creation_input_token_cost": 2.5e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 922000, "supports_function_calling": true, @@ -77577,7 +74526,6 @@ "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 1e-08, "cache_creation_input_token_cost": 1.25e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77616,7 +74564,6 @@ "input_cost_per_token": 2e-06, "output_cost_per_token": 1e-05, "cache_read_input_token_cost": 1e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77633,7 +74580,6 @@ "input_cost_per_token": 1.5e-07, "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 3e-08, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77650,7 +74596,6 @@ "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77667,7 +74612,6 @@ "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77685,7 +74629,6 @@ "output_cost_per_token": 1e-05, "cache_read_input_token_cost": 2.0000000000000002e-07, "cache_creation_input_token_cost": 2e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77703,7 +74646,6 @@ "output_cost_per_token": 1.5e-05, "cache_read_input_token_cost": 3e-07, "cache_creation_input_token_cost": 3e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77720,7 +74662,6 @@ "input_cost_per_token": 9.499999999999999e-07, "output_cost_per_token": 4e-06, "cache_read_input_token_cost": 1.9e-07, - "max_tokens": 262144, "max_output_tokens": 262144, "max_input_tokens": 262144, "supports_function_calling": true, @@ -77737,7 +74678,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77754,7 +74694,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-09, - "max_tokens": 384000, "max_output_tokens": 384000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77771,7 +74710,6 @@ "input_cost_per_token": 1.32e-06, "output_cost_per_token": 3.96e-06, "cache_read_input_token_cost": 4.4e-08, - "max_tokens": 384000, "max_output_tokens": 384000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77788,7 +74726,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, "cache_read_input_token_cost": 2.8e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77805,7 +74742,6 @@ "input_cost_per_token": 4.35e-07, "output_cost_per_token": 8.7e-07, "cache_read_input_token_cost": 3.625e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77822,7 +74758,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, "cache_read_input_token_cost": 2.8e-09, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77839,7 +74774,6 @@ "input_cost_per_token": 4.35e-07, "output_cost_per_token": 8.7e-07, "cache_read_input_token_cost": 3.625e-09, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77856,7 +74790,6 @@ "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, "cache_read_input_token_cost": 1.5e-08, - "max_tokens": 512000, "max_output_tokens": 512000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77873,7 +74806,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-08, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77890,7 +74822,6 @@ "input_cost_per_token": 1.0000000000000001e-07, "output_cost_per_token": 2.0000000000000002e-07, "cache_read_input_token_cost": 2e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77907,7 +74838,6 @@ "input_cost_per_token": 1.0000000000000001e-07, "output_cost_per_token": 2.0000000000000002e-07, "cache_read_input_token_cost": 2e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77925,7 +74855,6 @@ "output_cost_per_token": 6e-06, "cache_read_input_token_cost": 2.5e-07, "cache_creation_input_token_cost": 2.5e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77943,7 +74872,6 @@ "output_cost_per_token": 4.6999999999999995e-07, "cache_read_input_token_cost": 1.6e-08, "cache_creation_input_token_cost": 2.0000000000000002e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77961,7 +74889,6 @@ "output_cost_per_token": 1.6000000000000001e-06, "cache_read_input_token_cost": 4e-08, "cache_creation_input_token_cost": 5e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78000,7 +74927,6 @@ "input_cost_per_token": 8.34e-07, "output_cost_per_token": 2.501e-06, "cache_read_input_token_cost": 4.2000000000000006e-08, - "max_tokens": 64000, "max_output_tokens": 64000, "max_input_tokens": 1024000, "supports_function_calling": true, @@ -78017,7 +74943,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 5.8e-07, "cache_read_input_token_cost": 3.5e-08, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 192000, "supports_function_calling": true, @@ -78034,7 +74959,6 @@ "input_cost_per_token": 0, "output_cost_per_token": 0, "cache_read_input_token_cost": 0, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78051,7 +74975,6 @@ "input_cost_per_token": 1e-06, "output_cost_per_token": 2.7e-06, "cache_read_input_token_cost": 1.0000000000000001e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78068,7 +74991,6 @@ "input_cost_per_token": 0, "output_cost_per_token": 0, "cache_read_input_token_cost": 0, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -78089,7 +75011,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -78134,7 +75055,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -78178,7 +75098,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -78211,7 +75130,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -78261,7 +75179,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78298,7 +75215,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78341,7 +75257,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78385,7 +75300,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78430,7 +75344,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78475,7 +75388,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1e-05, @@ -78520,7 +75432,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -78555,7 +75466,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -78598,7 +75508,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -78642,7 +75551,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -78712,7 +75620,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, @@ -78761,7 +75668,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "source": "https://developers.openai.com/api/docs/pricing", "supported_endpoints": [ @@ -78788,7 +75694,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, @@ -78827,7 +75732,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-06, "output_cost_per_token_above_272k_tokens": 8.25e-06, @@ -78884,7 +75788,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, diff --git a/litellm/proxy/_experimental/mcp_server/sampling_handler.py b/litellm/proxy/_experimental/mcp_server/sampling_handler.py index e1469f2fff7..d049ada4dfd 100644 --- a/litellm/proxy/_experimental/mcp_server/sampling_handler.py +++ b/litellm/proxy/_experimental/mcp_server/sampling_handler.py @@ -222,7 +222,7 @@ def _select_model_by_priority( input_cost = info.get("input_cost_per_token") or 0.0 output_cost = info.get("output_cost_per_token") or 0.0 total_cost = input_cost + output_cost - max_output = info.get("max_output_tokens") or info.get("max_tokens") or 0 + max_output = info.get("max_output_tokens") or 0 output_tps = info.get("output_tokens_per_second") or 0.0 scored.append( _ScoredModel( diff --git a/litellm/router.py b/litellm/router.py index d5093384a17..548df8e6721 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -12264,7 +12264,6 @@ class Router: _fallback_entry = litellm.model_cost.get(_azure_fallback_key) _fallback_resolves = _fallback_entry is not None and ( (_fallback_entry.get("max_input_tokens") or 0) > 0 - or (_fallback_entry.get("max_tokens") or 0) > 0 or (_fallback_entry.get("input_cost_per_token") or 0) > 0 ) if _fallback_resolves: diff --git a/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py b/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py index 086f5517c29..2e350a2f87b 100644 --- a/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py +++ b/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py @@ -148,7 +148,7 @@ def _model_max_output_tokens(model_name: str) -> int | None: # this lookup is a fallback default and must never fail the request. with contextlib.suppress(Exception): info: Final = litellm.get_model_info(model=model_name) - model_max: Final = info.get("max_output_tokens") or info.get("max_tokens") + model_max: Final = info.get("max_output_tokens") if model_max is not None: return max(0, int(model_max)) return None diff --git a/litellm/utils.py b/litellm/utils.py index 1cc7c63e6a5..1aed15ba83e 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6079,11 +6079,8 @@ def get_max_tokens(model: str) -> int | None: return None try: - if model in litellm.model_cost: - if "max_output_tokens" in litellm.model_cost[model]: - return litellm.model_cost[model]["max_output_tokens"] - elif "max_tokens" in litellm.model_cost[model]: - return litellm.model_cost[model]["max_tokens"] + if model in litellm.model_cost and "max_output_tokens" in litellm.model_cost[model]: + return litellm.model_cost[model]["max_output_tokens"] get_llm_provider: Final = litellm_utils.get_llm_provider model, custom_llm_provider, _, _ = get_llm_provider(model=model) if custom_llm_provider == "huggingface": @@ -6092,8 +6089,6 @@ def get_max_tokens(model: str) -> int | None: if model in litellm.model_cost: # check if extracted model is in model_list if "max_output_tokens" in litellm.model_cost[model]: return litellm.model_cost[model]["max_output_tokens"] - elif "max_tokens" in litellm.model_cost[model]: - return litellm.model_cost[model]["max_tokens"] else: raise Exception() return None @@ -6742,7 +6737,7 @@ def get_model_info_helper( returned_model_info: Final = ModelInfoBase( key=key, - max_tokens=_model_info.get("max_tokens", None), + max_tokens=_model_info.get("max_output_tokens") or _model_info.get("max_input_tokens"), max_input_tokens=_model_info.get("max_input_tokens", None), max_output_tokens=_model_info.get("max_output_tokens", None), input_cost_per_token=_input_cost_per_token, @@ -8242,9 +8237,7 @@ def trim_messages( if max_tokens is None: # Check if model is valid if model in litellm.model_cost: - max_tokens_for_model: Final = litellm.model_cost[model].get( - "max_input_tokens", litellm.model_cost[model]["max_tokens"] - ) + max_tokens_for_model: Final = litellm.model_cost[model]["max_input_tokens"] max_tokens = int(max_tokens_for_model * trim_ratio) else: # if user did not specify max (input) tokens diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index d9df9d97d7b..c913c879ccf 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -9,9 +9,8 @@ "input_cost_per_audio_token": 0.0, "input_cost_per_token": 0.0, "litellm_provider": "one of https://docs.litellm.ai/docs/providers", - "max_input_tokens": "max input tokens, if the provider specifies it. if not default to max_tokens", - "max_output_tokens": "max output tokens, if the provider specifies it. if not default to max_tokens", - "max_tokens": "LEGACY parameter. set to max_output_tokens if provider specifies it. IF not set to max_input_tokens, if provider specifies it.", + "max_input_tokens": "max input tokens, if the provider specifies it", + "max_output_tokens": "max output tokens, if the provider specifies it", "mode": "one of: chat, embedding, completion, image_generation, audio_transcription, audio_speech, image_generation, moderation, rerank, search", "output_cost_per_reasoning_token": 0.0, "output_cost_per_token": 0.0, @@ -49,28 +48,24 @@ "1024-x-1024/50-steps/stability.stable-diffusion-xl-v1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, "1024-x-1024/max-steps/stability.stable-diffusion-xl-v1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "512-x-512/50-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.018 }, "512-x-512/max-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.036 }, @@ -79,7 +74,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8191, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.25e-05, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -89,7 +83,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8191, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.88e-05, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -100,7 +93,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-ai21-labs-jamba-1-5-large.md" @@ -111,7 +103,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-ai21-labs-jamba-1-5-mini.md" @@ -121,7 +112,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 70000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -298,7 +288,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x4.md", @@ -311,7 +300,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x5.md", @@ -324,7 +312,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x4.md", @@ -337,7 +324,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-writer-palmyra-x5.md", @@ -352,7 +338,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -368,7 +353,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -389,7 +373,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -414,7 +397,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -435,7 +417,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.311e-06, "supports_function_calling": true, @@ -455,7 +436,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://aws.amazon.com/nova/pricing/", @@ -474,7 +454,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.597e-06, "supports_function_calling": true, @@ -494,7 +473,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://aws.amazon.com/nova/pricing/", @@ -518,7 +496,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-06, "output_cost_per_token_batches": 1.375e-06, @@ -544,7 +521,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -562,7 +538,6 @@ "amazon.nova-2-multimodal-embeddings-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 8172, - "max_tokens": 8172, "mode": "embedding", "input_cost_per_token": 1.35e-07, "input_cost_per_image": 6e-05, @@ -583,7 +558,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "output_cost_per_token_batches": 7e-08, @@ -602,7 +576,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.25e-05, "output_cost_per_token_batches": 6.25e-06, @@ -626,7 +599,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -646,7 +618,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2.75e-06, @@ -660,7 +631,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -669,7 +639,6 @@ "input_cost_per_token": 8e-07, "litellm_provider": "bedrock", "max_input_tokens": 128, - "max_tokens": 128, "metadata": { "notes": "'supports_image_input' is a deprecated field. Use 'supports_embedding_image_input' instead." }, @@ -684,7 +653,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -693,7 +661,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -702,7 +669,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -740,7 +706,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -755,7 +720,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -770,7 +734,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -784,7 +747,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -798,7 +760,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -812,7 +773,6 @@ "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, - "max_tokens": 500, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 512, @@ -860,7 +820,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 6e-07 }, @@ -869,7 +828,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-07 }, @@ -878,7 +836,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -889,7 +846,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -909,7 +865,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/index.json", @@ -941,7 +896,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -964,7 +918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -982,7 +935,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -1001,7 +953,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -1021,7 +972,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -1039,7 +989,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -1054,7 +1003,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -1067,7 +1015,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -1097,7 +1044,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -1125,7 +1071,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1164,7 +1109,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1204,7 +1148,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1244,7 +1187,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1284,7 +1226,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1324,7 +1265,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1364,7 +1304,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1405,7 +1344,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -1427,7 +1365,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1470,7 +1407,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1513,7 +1449,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1556,7 +1491,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -1596,7 +1530,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1639,7 +1572,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1678,7 +1610,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1721,7 +1652,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -1760,7 +1690,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1803,7 +1732,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1842,7 +1770,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1880,7 +1807,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "search_context_cost_per_query": { @@ -1922,7 +1848,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -1965,7 +1890,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -2010,7 +1934,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2053,7 +1976,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -2098,7 +2020,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2141,7 +2062,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2186,7 +2106,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2229,7 +2148,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2269,7 +2187,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2312,7 +2229,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2357,7 +2273,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2395,7 +2310,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -2435,7 +2349,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2480,7 +2393,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -2525,7 +2437,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2570,7 +2481,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2615,7 +2525,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2660,7 +2569,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2703,7 +2611,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -2745,7 +2652,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -2790,7 +2696,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -2835,7 +2740,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2880,7 +2784,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2925,7 +2828,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -2970,7 +2872,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -3011,7 +2912,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3050,7 +2950,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3089,7 +2988,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3128,7 +3026,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3167,7 +3064,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3206,7 +3102,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3246,7 +3141,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3286,7 +3180,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3319,7 +3212,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05 }, @@ -3328,7 +3220,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -3338,7 +3229,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07 }, @@ -3347,7 +3237,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -3356,7 +3245,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/codellama-CodeLlama-70b-Instruct-hf" @@ -3366,7 +3254,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/google-gemma-7b-it" @@ -3376,7 +3263,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07 }, @@ -3385,7 +3271,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -3394,7 +3279,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07 }, @@ -3403,7 +3287,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/meta-llama-Meta-Llama-3-70B-Instruct" @@ -3413,7 +3296,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/meta-llama-Meta-Llama-3-8B-Instruct" @@ -3423,7 +3305,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mistral-7B-Instruct-v0.1", @@ -3434,7 +3315,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mixtral-8x22B-Instruct-v0.1", @@ -3445,7 +3325,6 @@ "litellm_provider": "anyscale", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.anyscale.com/preview/endpoints/text-generation/supported-models/mistralai-Mixtral-8x7B-Instruct-v0.1", @@ -3457,7 +3336,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.52e-07, "supports_function_calling": true, @@ -3473,7 +3351,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.48e-07, "supports_function_calling": true, @@ -3489,7 +3366,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.36e-06, "output_cost_per_token_flex": 1.68e-06, @@ -3508,7 +3384,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -3543,7 +3418,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -3595,7 +3469,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -3628,7 +3501,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -3640,7 +3512,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 6e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -3670,7 +3541,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -3685,7 +3555,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -3709,7 +3578,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, @@ -3734,7 +3602,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3765,7 +3632,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3797,7 +3663,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3830,7 +3695,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3868,7 +3732,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3899,7 +3762,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3938,7 +3800,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -3971,7 +3832,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -3996,7 +3856,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -4030,7 +3889,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": false, @@ -4052,7 +3910,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -4087,7 +3944,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -4105,7 +3961,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -4154,7 +4009,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -4203,7 +4057,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -4252,7 +4105,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -4304,7 +4156,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4345,7 +4196,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", @@ -4395,7 +4245,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 6e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", @@ -4439,7 +4288,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4486,7 +4334,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -4533,7 +4380,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -4581,7 +4427,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -4629,7 +4474,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -4675,7 +4519,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -4717,7 +4560,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_priority": 9e-06, @@ -4759,7 +4601,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_priority": 9e-06, @@ -4801,7 +4642,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_priority": 2.5e-06, @@ -4843,7 +4683,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_priority": 2.5e-06, @@ -4883,7 +4722,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" @@ -4895,7 +4733,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Catalog-name twin of azure_ai/model_router: the flat $0.14 per M input tokens is the router's own fee, the routed model is priced on top of it" @@ -4908,7 +4745,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4929,7 +4765,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4949,7 +4784,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -4968,7 +4802,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -4987,7 +4820,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5006,7 +4838,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2.2e-05, "output_cost_per_token": 2.64e-06, @@ -5026,7 +4857,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.00022, "output_cost_per_token": 2.2e-05, @@ -5046,7 +4876,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8.8e-05, "output_cost_per_token": 2.2e-05, @@ -5076,7 +4905,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -5116,7 +4944,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -5155,7 +4982,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "supported_endpoints": [ @@ -5196,7 +5022,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5228,7 +5053,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5261,7 +5085,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -5298,7 +5121,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -5317,7 +5139,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -5335,7 +5156,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5353,7 +5173,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5371,7 +5190,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -5389,7 +5207,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5409,7 +5226,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5426,7 +5242,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -5443,7 +5258,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5463,7 +5277,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -5484,7 +5297,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -5525,7 +5337,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5557,7 +5368,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -5587,7 +5397,6 @@ "litellm_provider": "azure", "max_input_tokens": 4097, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -5597,7 +5406,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5607,7 +5416,6 @@ "litellm_provider": "azure", "max_input_tokens": 4097, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -5619,7 +5427,6 @@ "litellm_provider": "azure", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5631,7 +5438,6 @@ "litellm_provider": "azure", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5642,7 +5448,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5651,7 +5457,7 @@ "input_cost_per_token": 1.65e-06, "litellm_provider": "azure_text", "max_input_tokens": 4097, - "max_tokens": 4097, + "max_output_tokens": 4097, "mode": "completion", "output_cost_per_token": 2.2e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -5662,7 +5468,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -5674,7 +5479,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -5687,7 +5491,6 @@ "litellm_provider": "azure", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5699,7 +5502,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -5712,7 +5514,6 @@ "litellm_provider": "azure", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.00012, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5724,7 +5525,6 @@ "litellm_provider": "azure", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.00012, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/legacy-models", @@ -5735,7 +5535,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5748,7 +5547,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5762,7 +5560,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.3e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/", @@ -5779,7 +5576,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -5817,7 +5613,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -5855,7 +5650,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -5893,7 +5687,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -5929,7 +5722,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -5963,7 +5755,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -5997,7 +5788,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 0.00015, "output_cost_per_token_batches": 7.5e-05, @@ -6017,7 +5807,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -6034,7 +5823,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -6053,7 +5841,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6073,7 +5860,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6092,7 +5878,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6125,7 +5910,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6157,7 +5941,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -6190,7 +5973,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -6225,7 +6007,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6257,7 +6038,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6290,7 +6070,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -6322,7 +6101,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -6340,7 +6118,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -6358,7 +6135,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 6e-07, @@ -6392,7 +6168,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6414,7 +6189,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6449,7 +6223,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6483,7 +6256,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6518,7 +6290,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -6552,7 +6323,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -6587,7 +6357,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6622,7 +6391,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6656,7 +6424,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -6720,7 +6487,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.0002, "output_cost_per_token": 2e-05, @@ -6739,7 +6505,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 2e-05, @@ -6793,7 +6558,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "audio_transcription", "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/gpt-realtime-whisper", "supported_endpoints": [ @@ -6817,7 +6581,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -6860,7 +6623,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -6895,7 +6657,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "output_cost_per_token_priority": 3.6e-06, @@ -6932,7 +6693,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -6971,7 +6731,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -7008,7 +6767,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7044,7 +6802,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -7083,7 +6840,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -7122,7 +6878,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -7158,7 +6913,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -7194,7 +6948,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -7228,7 +6981,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -7269,7 +7021,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7294,7 +7045,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7316,7 +7066,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7337,7 +7086,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7358,7 +7106,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7380,7 +7127,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7401,7 +7147,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7422,7 +7167,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7444,7 +7188,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7465,7 +7208,6 @@ "litellm_provider": "azure", "max_input_tokens": 111616, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.54e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7486,7 +7228,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.6e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7502,7 +7243,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.6e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7518,7 +7258,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7550,7 +7289,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7582,7 +7320,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7618,7 +7355,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_batches": 7e-06, @@ -7658,7 +7394,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_batches": 7e-06, @@ -7696,7 +7431,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -7730,7 +7464,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -7762,7 +7495,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -7801,7 +7533,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -7850,7 +7581,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -7906,7 +7636,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -7956,7 +7685,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8006,7 +7734,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -8062,7 +7789,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8112,7 +7838,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -8163,7 +7888,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -8212,7 +7936,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -8266,7 +7989,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8327,7 +8049,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8391,7 +8112,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -8455,7 +8175,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -8519,7 +8238,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -8583,7 +8301,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -8647,7 +8364,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -8699,7 +8415,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -8748,7 +8463,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -8797,7 +8511,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -8846,7 +8559,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -8901,7 +8613,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -8954,7 +8665,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9001,7 +8711,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9050,7 +8759,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -9099,7 +8807,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -9148,7 +8855,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -9197,7 +8903,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -9242,7 +8947,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -9279,7 +8983,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -9325,7 +9028,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9380,7 +9082,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9436,7 +9137,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-05, "output_cost_per_token_above_272k_tokens": 1.98e-05, @@ -9492,7 +9192,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-06, "output_cost_per_token_above_272k_tokens": 1.98e-06, @@ -9542,7 +9241,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "output_cost_per_token_above_272k_tokens": 8.25e-05, @@ -9591,7 +9289,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "output_cost_per_token_above_272k_tokens": 8.25e-07, @@ -9646,7 +9343,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -9689,7 +9385,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.3e-05, "reasoning_effort_levels": [ @@ -9735,7 +9430,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9790,7 +9484,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "output_cost_per_token_above_272k_tokens": 3.3e-05, @@ -9846,7 +9539,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-05, "output_cost_per_token_above_272k_tokens": 1.98e-05, @@ -9902,7 +9594,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.32e-06, "output_cost_per_token_above_272k_tokens": 1.98e-06, @@ -9961,7 +9652,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -10020,7 +9710,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_batches": 2.475e-05, "output_cost_per_token_batches": 1.65e-05, @@ -10075,7 +9764,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_batches": 2.475e-05, "output_cost_per_token_batches": 1.65e-05, @@ -10127,7 +9815,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -10187,7 +9874,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_batches": 1.5e-05, @@ -10246,7 +9932,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10299,7 +9984,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10353,7 +10037,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10406,7 +10089,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "search_context_cost_per_query": { "search_context_size_high": 0.01, @@ -10453,7 +10135,6 @@ "litellm_provider": "azure", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -10497,7 +10178,6 @@ "litellm_provider": "azure", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -10539,7 +10219,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "search_context_cost_per_query": { @@ -10590,7 +10269,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "search_context_cost_per_query": { @@ -10642,7 +10320,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "search_context_cost_per_query": { @@ -10690,7 +10367,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "search_context_cost_per_query": { @@ -11066,7 +10742,7 @@ "input_cost_per_token": 8e-06, "litellm_provider": "azure", "max_input_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_function_calling": true @@ -11075,7 +10751,7 @@ "input_cost_per_token": 8e-06, "litellm_provider": "azure", "max_input_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_function_calling": true @@ -11087,7 +10763,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_function_calling": true, @@ -11105,7 +10780,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -11124,7 +10798,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11142,7 +10815,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11160,7 +10832,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supported_endpoints": [ @@ -11191,7 +10862,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -11223,7 +10893,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "search_context_cost_per_query": { @@ -11261,7 +10930,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_prompt_caching": true, @@ -11278,7 +10946,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11295,7 +10962,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -11326,7 +10992,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -11357,7 +11022,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supported_endpoints": [ @@ -11388,7 +11052,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -11433,7 +11096,6 @@ "input_cost_per_token": 1.43e-07, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11443,7 +11105,6 @@ "input_cost_per_token": 2.2e-08, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11453,7 +11114,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "azure", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/azure-openai/" @@ -11504,7 +11164,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -11542,7 +11201,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.76e-06, "output_cost_per_token_batches": 8.8e-07, @@ -11578,7 +11236,6 @@ "litellm_provider": "azure", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -11612,7 +11269,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11633,7 +11289,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11653,7 +11308,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11672,7 +11326,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11691,7 +11344,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11710,7 +11362,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2.2e-05, "output_cost_per_token": 2.64e-06, @@ -11730,7 +11381,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 0.00022, "output_cost_per_token": 2.2e-05, @@ -11750,7 +11400,6 @@ "litellm_provider": "azure", "max_input_tokens": 16000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 8.8e-05, "output_cost_per_token": 2.2e-05, @@ -11780,7 +11429,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_batches": 5.5e-06, @@ -11820,7 +11468,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -11858,7 +11505,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-07, "output_cost_per_token_batches": 2.2e-07, @@ -11896,7 +11542,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "supported_endpoints": [ @@ -11937,7 +11582,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -11969,7 +11613,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12002,7 +11645,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -12021,7 +11663,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.6e-05, "output_cost_per_token_batches": 3.3e-05, @@ -12039,7 +11680,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12057,7 +11697,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -12090,7 +11729,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -12123,7 +11761,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12141,7 +11778,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12159,7 +11795,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12180,7 +11815,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -12204,7 +11838,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "azure_ai", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -12215,7 +11848,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "azure_ai", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -12227,7 +11859,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12245,7 +11876,6 @@ "azure_ai/FLUX-1.1-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 5000, - "max_tokens": 5000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/black-forest-labs-flux-1-kontext-pro-and-flux1-1-pro-now-available-in-azure-ai-f/4434659", @@ -12256,7 +11886,6 @@ "azure_ai/FLUX.1-Kontext-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 5000, - "max_tokens": 5000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/black-forest-labs/", @@ -12267,7 +11896,6 @@ "azure_ai/flux.2-pro": { "litellm_provider": "azure_ai", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "image_generation", "output_cost_per_image": 0.04, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -12279,7 +11907,6 @@ "input_cost_per_pixel": 5e-08, "litellm_provider": "azure_ai", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "image_generation", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/black-forest-labs/", "supported_endpoints": [ @@ -12301,7 +11928,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.85e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12316,7 +11942,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.828e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12332,7 +11957,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.52e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12348,7 +11972,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.84e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12363,7 +11986,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.84e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12378,7 +12000,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 7.26e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12393,7 +12014,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.46e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12415,7 +12035,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.3e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12438,7 +12057,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12461,7 +12079,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12484,7 +12101,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -12513,7 +12129,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12528,7 +12143,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12571,7 +12185,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.64e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12637,7 +12250,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12660,7 +12272,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.1e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -12672,7 +12283,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12685,7 +12295,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 10000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://azure.microsoft.com/en-us/blog/introducing-the-llama-4-herd-in-azure-ai-foundry-and-azure-databricks/", @@ -12699,7 +12308,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.7e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12711,7 +12319,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.54e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12723,7 +12330,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12736,7 +12342,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12749,7 +12354,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12762,7 +12366,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12775,7 +12378,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12788,7 +12390,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12801,7 +12402,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12814,7 +12414,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12827,7 +12426,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5.2e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12840,7 +12438,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12854,7 +12451,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12867,7 +12463,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3.2e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12881,7 +12476,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12893,7 +12487,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12935,7 +12528,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 8182, - "max_tokens": 8182, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -12975,7 +12567,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -12989,7 +12580,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13001,7 +12591,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13012,7 +12601,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/introducing-cohere-rerank-4-0-in-microsoft-foundry/4477076" @@ -13023,7 +12611,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/introducing-cohere-rerank-4-0-in-microsoft-foundry/4477076" @@ -13033,7 +12620,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13048,7 +12634,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13064,7 +12649,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.56e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13076,7 +12660,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13092,7 +12675,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 5.1e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13109,7 +12691,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13122,7 +12703,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 3072, @@ -13142,7 +12722,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13159,7 +12738,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -13180,7 +12758,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -13200,7 +12777,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13219,7 +12795,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13236,7 +12811,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -13250,7 +12824,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supports_function_calling": true, @@ -13264,7 +12837,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure", @@ -13279,7 +12851,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.00971, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models" @@ -13290,7 +12861,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 70000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13302,7 +12872,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13319,7 +12888,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13343,7 +12911,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-08, "source": "https://marketplace.microsoft.com/en/marketplace/apps/000-000.ministral-3b-2410-offer?tab=Overview", @@ -13356,7 +12923,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13369,7 +12935,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13381,7 +12946,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://marketplace.microsoft.com/en/marketplace/apps/000-000.mistral-ai-large-2407-offer?tab=Overview", @@ -13393,7 +12957,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -13407,7 +12970,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "supports_assistant_prefill": true, @@ -13420,7 +12982,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13432,7 +12993,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models", @@ -13445,7 +13005,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -13459,7 +13018,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -13471,7 +13029,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13481,7 +13038,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13491,7 +13047,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13501,7 +13056,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_tool_choice": true }, @@ -13526,7 +13080,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13536,7 +13089,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v2:1": { @@ -13545,7 +13097,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13555,7 +13106,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13565,7 +13115,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v2:1": { @@ -13574,7 +13123,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -13583,7 +13131,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 7.55e-06, "supports_tool_choice": true @@ -13593,7 +13140,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -13603,7 +13149,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -13614,7 +13159,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13634,7 +13178,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13652,7 +13195,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13672,7 +13214,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.03e-06, "output_cost_per_token_flex": 1.52e-06, @@ -13686,7 +13227,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13704,7 +13244,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13723,7 +13262,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -13737,7 +13275,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -13757,7 +13294,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -13768,7 +13304,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -13779,7 +13314,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13799,7 +13333,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13817,7 +13350,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13837,7 +13369,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.94e-06, "output_cost_per_token_flex": 1.47e-06, @@ -13851,7 +13382,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13869,7 +13399,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13887,7 +13416,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13906,7 +13434,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -13926,7 +13453,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13944,7 +13470,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -13963,7 +13488,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -13981,7 +13505,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -13999,7 +13522,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.03e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14010,7 +13532,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.9e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14021,7 +13542,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -14041,7 +13561,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14059,7 +13578,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14078,7 +13596,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -14096,7 +13613,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14106,7 +13622,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v2:1": { @@ -14115,7 +13630,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14125,7 +13639,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14135,7 +13648,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v2:1": { @@ -14144,7 +13656,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14153,7 +13664,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 8.38e-06, "supports_tool_choice": true @@ -14163,7 +13673,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05 }, @@ -14172,7 +13681,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14184,7 +13692,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14202,7 +13709,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14221,7 +13727,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14239,7 +13744,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.78e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14250,7 +13754,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14262,7 +13765,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14280,7 +13782,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14299,7 +13800,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14317,7 +13817,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.55e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14328,7 +13827,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14340,7 +13838,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.86e-06, "output_cost_per_token_flex": 9.3e-07, @@ -14358,7 +13855,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14377,7 +13873,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.01e-06, "output_cost_per_token_flex": 5.05e-07, @@ -14396,7 +13891,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.86e-06, "output_cost_per_token_flex": 9.3e-07, @@ -14414,7 +13908,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -14425,7 +13918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.56e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -14437,7 +13929,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9.1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -14450,7 +13941,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14468,7 +13958,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14487,7 +13976,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14504,7 +13992,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Anthropic via Invoke route does not currently support pdf input." }, @@ -14523,7 +14010,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.88e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14534,7 +14020,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.01e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14545,7 +14030,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -14565,7 +14049,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14583,7 +14066,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14603,7 +14085,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.03e-06, "output_cost_per_token_flex": 1.52e-06, @@ -14617,7 +14098,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3.6e-06, "output_cost_per_token_flex": 1.8e-06, @@ -14635,7 +14115,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.44e-06, "output_cost_per_token_flex": 7.2e-07, @@ -14653,7 +14132,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14663,7 +14141,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-east-1/1-month-commitment/anthropic.claude-v2:1": { @@ -14672,7 +14149,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14682,7 +14158,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14692,7 +14167,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-east-1/6-month-commitment/anthropic.claude-v2:1": { @@ -14701,7 +14175,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -14710,7 +14183,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -14720,7 +14192,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14730,7 +14201,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -14741,7 +14211,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -14752,7 +14221,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -14763,7 +14231,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -14774,7 +14241,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -14786,7 +14252,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -14798,7 +14263,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -14818,7 +14282,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14838,7 +14301,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -14855,7 +14317,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -14869,7 +14330,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -14887,7 +14347,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14905,7 +14364,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -14925,7 +14383,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -14943,7 +14400,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-minimax-minimax-m2-5.html", "supports_function_calling": true, @@ -14963,7 +14419,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -14977,7 +14432,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -14995,7 +14449,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15013,7 +14466,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.84e-06, "supports_function_calling": true, @@ -15027,7 +14479,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -15036,7 +14487,6 @@ "input_cost_per_token": 2e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -15046,7 +14496,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.7e-06 }, @@ -15055,7 +14504,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-07 }, @@ -15064,7 +14512,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -15077,7 +14524,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15106,7 +14552,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15127,7 +14572,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.5e-06, "supports_pdf_input": true @@ -15137,7 +14581,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 2.65e-06, "supports_pdf_input": true @@ -15145,7 +14588,6 @@ "bedrock/us-gov-west-1/amazon.nova-2-multimodal-embeddings-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 8172, - "max_tokens": 8172, "mode": "embedding", "input_cost_per_token": 1.62e-07, "input_cost_per_image": 7.2e-05, @@ -15164,7 +14606,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.88e-07, "supports_function_calling": true, @@ -15180,7 +14621,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.68e-07, "supports_function_calling": true, @@ -15194,7 +14634,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.84e-06, "supports_function_calling": true, @@ -15208,7 +14647,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536 @@ -15217,7 +14655,6 @@ "input_cost_per_token": 1.1e-07, "litellm_provider": "bedrock", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -15227,7 +14664,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.6e-06 }, @@ -15236,7 +14672,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-07 }, @@ -15245,7 +14680,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 42000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06 }, @@ -15258,7 +14692,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15287,7 +14720,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -15308,7 +14740,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 3.5e-06, "supports_pdf_input": true @@ -15318,7 +14749,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 6e-07, "supports_pdf_input": true @@ -15329,7 +14759,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -15340,7 +14769,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -15351,7 +14779,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15361,7 +14788,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-west-2/1-month-commitment/anthropic.claude-v2:1": { @@ -15370,7 +14796,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15380,7 +14805,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15390,7 +14814,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat" }, "bedrock/us-west-2/6-month-commitment/anthropic.claude-v2:1": { @@ -15399,7 +14822,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "supports_tool_choice": true }, @@ -15408,7 +14830,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-06, "supports_tool_choice": true @@ -15418,7 +14839,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -15428,7 +14848,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 100000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.4e-05, "supports_tool_choice": true @@ -15439,7 +14858,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -15450,7 +14868,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -15462,7 +14879,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -15474,7 +14890,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 163840, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_flex": 9.25e-07, @@ -15494,7 +14909,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15514,7 +14928,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 196000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -15531,7 +14944,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, @@ -15545,7 +14957,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_flex": 1.5e-06, @@ -15563,7 +14974,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, @@ -15582,7 +14992,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -15672,7 +15081,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -15683,7 +15091,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -15694,7 +15101,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true, @@ -15705,7 +15111,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -15716,7 +15121,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -15727,7 +15131,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://www.cerebras.ai/blog/openai-gpt-oss-120b-runs-fastest-on-cerebras", @@ -15742,7 +15145,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inference-docs.cerebras.ai/support/pricing", @@ -15755,7 +15157,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.49e-06, "source": "https://api.cerebras.ai/public/v1/models/qwen-3.8-27b", @@ -15771,7 +15172,6 @@ "litellm_provider": "nlp_cloud", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5e-07 }, @@ -15802,7 +15202,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -15829,7 +15228,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -15858,7 +15256,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -15892,7 +15289,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -15928,7 +15324,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -15972,7 +15367,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -16019,7 +15413,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -16063,7 +15456,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -16092,7 +15484,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16126,7 +15517,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16163,7 +15553,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16206,7 +15595,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16249,7 +15637,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16293,7 +15680,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16337,7 +15723,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -16382,7 +15767,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -16428,7 +15812,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1e-05, @@ -16474,7 +15857,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16522,7 +15904,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -16638,7 +16019,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 3072, "max_output_tokens": 3072, - "max_tokens": 3072, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16648,7 +16028,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 2048, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16658,7 +16037,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16668,7 +16046,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.923e-06, "rpm": 300 @@ -16678,7 +16055,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-07, "rpm": 300, @@ -16690,7 +16066,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16700,7 +16075,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 80000, "max_output_tokens": 80000, - "max_tokens": 80000, "mode": "chat", "output_cost_per_token": 3.35e-07, "rpm": 300 @@ -16710,7 +16084,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-08, "rpm": 300 @@ -16720,7 +16093,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 15000, "max_output_tokens": 15000, - "max_tokens": 15000, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16731,7 +16103,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "rpm": 20, @@ -16743,7 +16114,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 80000, "max_output_tokens": 80000, - "max_tokens": 80000, "mode": "chat", "output_cost_per_token": 4.881e-06, "rpm": 300, @@ -16754,7 +16124,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.87e-07, "rpm": 300 @@ -16764,7 +16133,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 60000, "max_output_tokens": 60000, - "max_tokens": 60000, "mode": "chat", "output_cost_per_token": 2.01e-07, "rpm": 300 @@ -16775,7 +16143,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "rpm": 20, @@ -16787,7 +16154,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-07, "rpm": 300, @@ -16799,7 +16165,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16809,7 +16174,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 24000, "max_output_tokens": 24000, - "max_tokens": 24000, "mode": "chat", "output_cost_per_token": 2.253e-06, "rpm": 300, @@ -16820,7 +16184,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1.12e-07, "rpm": 300, @@ -16831,7 +16194,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-06, "rpm": 300 @@ -16842,7 +16204,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "rpm": 20, @@ -16854,7 +16215,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.5e-06, "rpm": 300, @@ -16866,7 +16226,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.55e-07, "rpm": 300 @@ -16876,7 +16235,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.35e-07, "rpm": 300, @@ -16888,7 +16246,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 3500, "max_output_tokens": 3500, - "max_tokens": 3500, "mode": "chat", "output_cost_per_token": 0.0, "rpm": 300 @@ -16898,7 +16255,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3e-07, "rpm": 300, @@ -16910,7 +16266,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.55e-07, "rpm": 300, @@ -16921,7 +16276,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.76e-07, "rpm": 300, @@ -16932,7 +16286,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "rpm": 300, @@ -16944,7 +16297,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 8.5e-07, "rpm": 300, @@ -16955,7 +16307,6 @@ "litellm_provider": "cloudflare", "max_input_tokens": 24000, "max_output_tokens": 24000, - "max_tokens": 24000, "mode": "chat", "output_cost_per_token": 1e-06, "rpm": 300, @@ -16966,7 +16317,6 @@ "litellm_provider": "codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/", @@ -16978,7 +16328,6 @@ "litellm_provider": "codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/", @@ -16990,7 +16339,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17001,7 +16349,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17011,7 +16358,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17021,7 +16367,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "bedrock", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -17031,7 +16376,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17042,7 +16386,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17053,7 +16396,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "bedrock", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17064,7 +16406,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -17076,7 +16417,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048, @@ -17087,7 +16427,6 @@ "input_cost_per_token": 8e-08, "litellm_provider": "cohere", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048, @@ -17109,7 +16448,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-cohere-rerank-3-5.md" @@ -17119,7 +16457,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -17131,7 +16468,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "source": "https://docs.cohere.com/docs/models" }, @@ -17140,7 +16476,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.cohere.com/docs/command-a-plus", @@ -17155,7 +16490,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06 }, @@ -17164,7 +16498,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -17175,7 +16508,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -17186,7 +16518,6 @@ "litellm_provider": "cohere_chat", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.cohere.com/v2/docs/command-r7b", @@ -17199,7 +16530,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17222,7 +16553,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17245,7 +16576,7 @@ "input_cost_per_token": 1.12e-06, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17268,7 +16599,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17291,7 +16622,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17314,7 +16645,7 @@ "input_cost_per_token": 1e-08, "litellm_provider": "coralbricks", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.coralbricks.ai/pricing", @@ -17337,7 +16668,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "supported_endpoints": [ @@ -17367,7 +16697,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.2e-05, "supported_endpoints": [ @@ -17398,7 +16727,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -17420,7 +16748,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -17442,7 +16769,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17458,7 +16784,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17474,7 +16799,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17490,7 +16814,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17506,7 +16829,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17522,7 +16844,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17538,7 +16859,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17550,7 +16870,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17579,7 +16898,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17609,7 +16927,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17622,7 +16939,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17635,7 +16951,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17648,7 +16963,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -17662,7 +16976,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -17675,7 +16988,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17706,7 +17018,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17737,7 +17048,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17769,7 +17079,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17783,7 +17092,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -17796,7 +17104,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17810,7 +17117,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -17823,7 +17129,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17834,7 +17139,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17883,7 +17187,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17928,7 +17231,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -17977,7 +17279,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18022,7 +17323,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18059,7 +17359,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18096,7 +17395,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18134,7 +17432,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18146,7 +17443,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18159,7 +17455,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18172,7 +17467,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18186,7 +17480,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18199,7 +17492,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -18212,7 +17504,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18250,7 +17541,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18284,7 +17574,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18298,7 +17587,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -18334,7 +17622,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18352,7 +17639,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -18371,7 +17657,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -18390,7 +17675,6 @@ "litellm_provider": "dashscope", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -18711,7 +17995,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.qwencloud.com/models", @@ -18727,7 +18010,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.qwencloud.com/models", @@ -18743,7 +18025,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.qwencloud.com/models", @@ -18759,7 +18040,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.qwencloud.com/models", @@ -18775,7 +18055,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.qwencloud.com/models", @@ -18791,7 +18070,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.qwencloud.com/models", @@ -18807,7 +18085,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.qwencloud.com/models", @@ -18819,7 +18096,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18848,7 +18124,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18878,7 +18153,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.qwencloud.com/models", @@ -18891,7 +18165,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -18904,7 +18177,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -18917,7 +18189,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -18931,7 +18202,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -18944,7 +18214,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -18975,7 +18244,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19006,7 +18274,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19038,7 +18305,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19052,7 +18318,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.qwencloud.com/models", @@ -19065,7 +18330,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19079,7 +18343,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -19092,7 +18355,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19103,7 +18365,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19152,7 +18413,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19197,7 +18457,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19246,7 +18505,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19291,7 +18549,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19328,7 +18585,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19365,7 +18621,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19403,7 +18658,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -19415,7 +18669,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.qwencloud.com/models", @@ -19428,7 +18681,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.qwencloud.com/models", @@ -19441,7 +18693,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.qwencloud.com/models", @@ -19455,7 +18706,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.qwencloud.com/models", @@ -19468,7 +18718,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.qwencloud.com/models", @@ -19481,7 +18730,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19519,7 +18767,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19553,7 +18800,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.qwencloud.com/models", @@ -19567,7 +18813,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.qwencloud.com/models", "supports_function_calling": true, @@ -19603,7 +18848,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.qwencloud.com/models", @@ -19619,7 +18863,6 @@ "litellm_provider": "qwencloud", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.qwencloud.com/models", @@ -19940,7 +19183,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19956,7 +19198,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19972,7 +19213,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 4.8e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -19988,7 +19228,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 202745, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20004,7 +19243,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20020,7 +19258,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 229376, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20036,7 +19273,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20048,7 +19284,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20077,7 +19312,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20107,7 +19341,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 30720, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20120,7 +19353,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20133,7 +19365,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20146,7 +19377,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -20160,7 +19390,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 4e-06, "output_cost_per_token": 1.2e-06, @@ -20173,7 +19402,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20204,7 +19432,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20235,7 +19462,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20267,7 +19493,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20281,7 +19506,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20294,7 +19518,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20308,7 +19531,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 1000000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_reasoning_token": 5e-07, "output_cost_per_token": 2e-07, @@ -20321,7 +19543,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 129024, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20332,7 +19553,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20381,7 +19601,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20426,7 +19645,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20475,7 +19693,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 997952, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20520,7 +19737,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20557,7 +19773,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20594,7 +19809,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 258048, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20632,7 +19846,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20644,7 +19857,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20657,7 +19869,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20670,7 +19881,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20684,7 +19894,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6.4e-07, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20697,7 +19906,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.87e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/model-pricing", @@ -20710,7 +19918,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 260096, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20748,7 +19955,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20782,7 +19988,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20796,7 +20001,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "source": "https://www.alibabacloud.com/help/en/model-studio/models", "supports_function_calling": true, @@ -20832,7 +20036,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -20850,7 +20053,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -20869,7 +20071,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 991808, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://docs.modelstudio.console.alibabacloud.com/en/model-studio/model-pricing", @@ -20888,7 +20089,6 @@ "litellm_provider": "qwen_ai_platform", "max_input_tokens": 98304, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://www.alibabacloud.com/help/en/model-studio/models", @@ -21210,7 +20410,6 @@ "input_dbu_cost_per_token": 1.429e-06, "litellm_provider": "databricks", "max_input_tokens": 512, - "max_tokens": 512, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21228,7 +20427,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21257,7 +20455,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21287,7 +20484,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21311,7 +20507,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21335,7 +20530,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21359,7 +20553,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21384,7 +20577,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21409,7 +20601,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21436,7 +20627,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21464,7 +20654,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21493,7 +20682,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -21528,7 +20716,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21552,7 +20739,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21575,7 +20761,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21599,7 +20784,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21624,7 +20808,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields. Introductory launch rates of 28.571 input / 142.857 output / 35.714 cache write / 2.857 cache read DBU run through 2026-08-31; the standard rates are listed here because entries carry no expiry date." }, @@ -21653,7 +20836,6 @@ "token_count_framing": 97, "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference. Context/max output are the DeepSeek-published model limits (1M context, 384K max output)." }, @@ -21686,7 +20868,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference. Context/max output are the DeepSeek-published model limits (1M context, 384K max output)." }, @@ -21715,7 +20896,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21749,7 +20929,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21784,7 +20963,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21812,7 +20990,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21833,7 +21010,6 @@ "litellm_provider": "databricks", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21858,7 +21034,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21891,7 +21066,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21923,7 +21097,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -21939,7 +21112,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21964,7 +21136,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Databricks DBU rates not yet published for this model; endpoint metadata only." }, @@ -21993,7 +21164,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22025,7 +21195,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22056,7 +21225,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22089,7 +21257,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22106,7 +21273,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference." }, @@ -22134,7 +21300,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22165,7 +21330,6 @@ "litellm_provider": "databricks", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22195,7 +21359,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22215,7 +21378,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22235,7 +21397,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22255,7 +21416,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22275,7 +21435,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22304,7 +21463,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22333,7 +21491,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22362,7 +21519,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields. Rates reflect OpenAI's promotional pricing in effect through November 21, 2026; afterwards input, cache and Batch rates are 25% higher and output rates 50% higher." }, @@ -22391,7 +21547,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22420,7 +21575,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22449,7 +21603,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22478,7 +21631,6 @@ "litellm_provider": "databricks", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22507,7 +21659,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22527,7 +21678,6 @@ "litellm_provider": "databricks", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22547,7 +21697,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22564,7 +21713,6 @@ "litellm_provider": "databricks", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22606,7 +21754,6 @@ "input_dbu_cost_per_token": 1.857e-06, "litellm_provider": "databricks", "max_input_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22653,7 +21800,6 @@ "token_count_framing": 100, "max_input_tokens": 1000000, "max_output_tokens": 1048576, - "max_tokens": 1048576, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Billing reads the per-token dollar fields; the '*_dbu_cost_per_token' fields are the published Databricks rates, kept for reference." }, @@ -22687,7 +21833,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Databricks documentation now provides both DBU costs (_dbu_cost_per_token) and dollar costs(_cost_per_token)." }, @@ -22705,7 +21850,6 @@ "litellm_provider": "databricks", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22722,7 +21866,6 @@ "litellm_provider": "databricks", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "metadata": { "notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation." }, @@ -22740,7 +21883,6 @@ "litellm_provider": "databricks", "max_input_tokens": 262144, "max_output_tokens": 25000, - "max_tokens": 25000, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22790,7 +21932,6 @@ "input_dbu_cost_per_token": 2.86e-07, "litellm_provider": "databricks", "max_input_tokens": 32768, - "max_tokens": 32768, "metadata": { "notes": "Costs per token are the published Global DBU rates times $0.070 per DBU. The '*_dbu_cost_per_token' fields are provided for reference; cost calculation reads the dollar '*_cost_per_token' fields." }, @@ -22836,7 +21977,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -23431,7 +22571,6 @@ ] }, "deepinfra/Gryphe/MythoMax-L2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 4e-07, @@ -23443,7 +22582,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/NousResearch/Hermes-3-Llama-3.1-405B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-06, @@ -23454,7 +22592,6 @@ "supports_function_calling": true }, "deepinfra/NousResearch/Hermes-3-Llama-3.1-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 7e-07, @@ -23465,7 +22602,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/QwQ-32B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -23476,7 +22612,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen2.5-72B-Instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 3.6e-07, @@ -23488,7 +22623,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen2.5-7B-Instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 4e-08, @@ -23498,7 +22632,6 @@ "supports_tool_choice": false }, "deepinfra/Qwen/Qwen2.5-VL-32B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -23510,7 +22643,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-14B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.2e-07, @@ -23522,7 +22654,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-235B-A22B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.8e-07, @@ -23533,7 +22664,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-235B-A22B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-08, @@ -23545,7 +22675,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-235B-A22B-Thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -23556,7 +22685,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-30B-A3B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1.2e-07, @@ -23568,7 +22696,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-32B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 8e-08, @@ -23580,7 +22707,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 4e-07, @@ -23591,7 +22717,6 @@ "supports_function_calling": true }, "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -23605,7 +22730,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Next-80B-A3B-Instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-08, @@ -23617,7 +22741,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Next-80B-A3B-Thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.4e-07, @@ -23628,7 +22751,6 @@ "supports_function_calling": true }, "deepinfra/Sao10K/L3-8B-Lunaris-v1-Turbo": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -23638,7 +22760,6 @@ "supports_tool_choice": false }, "deepinfra/Sao10K/L3.1-70B-Euryale-v2.2": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 8.5e-07, @@ -23649,7 +22770,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Sao10K/L3.3-70B-Euryale-v2.3": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6.5e-07, @@ -23659,7 +22779,6 @@ "supports_tool_choice": false }, "deepinfra/allenai/olmOCR-7B-0725-FP8": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2.7e-07, @@ -23669,7 +22788,6 @@ "supports_tool_choice": false }, "deepinfra/anthropic/claude-3-7-sonnet-latest": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 3.3e-06, @@ -23681,7 +22799,6 @@ "supports_function_calling": true }, "deepinfra/anthropic/claude-4-opus": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 1.65e-05, @@ -23692,7 +22809,6 @@ "supports_function_calling": true }, "deepinfra/anthropic/claude-4-sonnet": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 3.3e-06, @@ -23703,7 +22819,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 7e-07, @@ -23714,7 +22829,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-0528": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -23726,7 +22840,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-0528-Turbo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-06, @@ -23737,7 +22850,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -23747,7 +22859,6 @@ "supports_tool_choice": false }, "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.7e-07, @@ -23758,7 +22869,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-R1-Turbo": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-06, @@ -23769,7 +22879,6 @@ "supports_function_calling": true }, "deepinfra/deepseek-ai/DeepSeek-V3": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 3.2e-07, @@ -23781,7 +22890,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3-0324": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.4e-07, @@ -23795,7 +22903,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.5e-07, @@ -23809,7 +22916,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.1-Terminus": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 2.7e-07, @@ -23821,7 +22927,6 @@ "supports_function_calling": true }, "deepinfra/google/gemini-2.5-flash": { - "max_tokens": 1000000, "max_input_tokens": 1000000, "max_output_tokens": 1000000, "input_cost_per_token": 3e-07, @@ -23837,7 +22942,6 @@ "supports_audio_input": true }, "deepinfra/google/gemini-2.5-pro": { - "max_tokens": 1000000, "max_input_tokens": 1000000, "max_output_tokens": 1000000, "input_cost_per_token": 1.25e-06, @@ -23852,7 +22956,6 @@ "supports_audio_input": true }, "deepinfra/google/gemma-3-12b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-08, @@ -23864,7 +22967,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-3-27b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 8e-08, @@ -23876,7 +22978,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-3-4b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-08, @@ -23888,7 +22989,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-3.2-11B-Vision-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4.9e-08, @@ -23898,7 +22998,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Llama-3.2-3B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -23909,7 +23008,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.3e-07, @@ -23920,7 +23018,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Llama-3.3-70B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -23932,7 +23029,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 2e-07, @@ -23944,7 +23040,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-4-Scout-17B-16E-Instruct": { - "max_tokens": 327680, "max_input_tokens": 327680, "max_output_tokens": 327680, "input_cost_per_token": 1e-07, @@ -23956,7 +23051,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Llama-Guard-3-8B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5.5e-08, @@ -23966,7 +23060,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Llama-Guard-4-12B": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.8e-07, @@ -23976,7 +23069,6 @@ "supports_tool_choice": false }, "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -23987,7 +23079,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -23998,7 +23089,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -24010,7 +23100,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -24021,7 +23110,6 @@ "supports_function_calling": true }, "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -24033,7 +23121,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/microsoft/WizardLM-2-8x22B": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 4.8e-07, @@ -24043,7 +23130,6 @@ "supports_tool_choice": false }, "deepinfra/microsoft/phi-4": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 7e-08, @@ -24054,7 +23140,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mistral-Nemo-Instruct-2407": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.9e-08, @@ -24066,7 +23151,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/mistralai/Mistral-Small-24B-Instruct-2501": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-08, @@ -24077,7 +23161,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mistral-Small-3.2-24B-Instruct-2506": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 7.5e-08, @@ -24088,7 +23171,6 @@ "supports_function_calling": true }, "deepinfra/mistralai/Mixtral-8x7B-Instruct-v0.1": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 4e-07, @@ -24099,7 +23181,6 @@ "supports_function_calling": true }, "deepinfra/moonshotai/Kimi-K2-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-07, @@ -24110,7 +23191,6 @@ "supports_function_calling": true }, "deepinfra/moonshotai/Kimi-K2-Instruct-0905": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-07, @@ -24122,7 +23202,6 @@ "supports_function_calling": true }, "deepinfra/nvidia/Llama-3.1-Nemotron-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-07, @@ -24133,7 +23212,6 @@ "supports_function_calling": true }, "deepinfra/nvidia/Llama-3.3-Nemotron-Super-49B-v1.5": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -24157,7 +23235,6 @@ "supports_prompt_caching": true }, "deepinfra/nvidia/NVIDIA-Nemotron-Nano-9B-v2": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-08, @@ -24168,7 +23245,6 @@ "supports_function_calling": true }, "deepinfra/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3.7e-08, @@ -24180,7 +23256,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/openai/gpt-oss-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -24192,7 +23267,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.5": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 4e-07, @@ -24211,7 +23285,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -24234,7 +23307,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07, "supports_assistant_prefill": true, @@ -24249,7 +23321,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.19e-06, "supports_assistant_prefill": true, @@ -24266,7 +23337,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 4.2e-07, "source": "https://api-docs.deepseek.com/quick_start/pricing", @@ -24291,7 +23361,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.1e-06, "supports_assistant_prefill": true, @@ -24306,7 +23375,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 4e-07, "supports_assistant_prefill": true, @@ -24321,7 +23389,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-v3-1.html", @@ -24338,7 +23405,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 164000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_batches": 9.25e-07, @@ -24358,7 +23424,6 @@ "litellm_provider": "nlp_cloud", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "completion", "output_cost_per_token": 5e-07 }, @@ -24367,7 +23432,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 98304, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24381,7 +23445,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24395,7 +23458,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 229376, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_assistant_prefill": true, @@ -24408,7 +23470,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - standard version with 2560 dimensions" }, @@ -24420,7 +23481,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - large version with 2048 dimensions" }, @@ -24432,7 +23492,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-240915 version with 4096 dimensions" }, @@ -24444,7 +23503,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-250515 version with 2048 dimensions" }, @@ -24456,7 +23514,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "volcengine", "max_input_tokens": 4096, - "max_tokens": 4096, "metadata": { "notes": "Volcengine Doubao embedding model - text-240715 version with 2560 dimensions" }, @@ -24569,7 +23626,6 @@ "mode": "search" }, "sail/moonshotai/Kimi-K3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 2.5e-06, @@ -24591,7 +23647,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/zai-org/GLM-5.3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9.8e-07, @@ -24613,7 +23668,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/zai-org/GLM-5.3-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.1e-07, @@ -24635,7 +23689,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4-Pro-0813": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9.2e-07, @@ -24657,7 +23710,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9e-08, @@ -24679,7 +23731,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/deepseek-ai/DeepSeek-V4.1-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 9e-08, @@ -24701,7 +23752,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-06, @@ -24724,7 +23774,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/google/gemma-4-31B-it": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 4e-07, @@ -24747,7 +23796,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/nvidia/Gemma-4-31B-IT-NVFP4": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.4e-07, @@ -24770,7 +23818,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/google/gemma-4-12B-it": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 3e-07, @@ -24792,7 +23839,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-08, @@ -24808,7 +23854,6 @@ "source": "https://docs.sailresearch.com/models" }, "sail/Qwen/Qwen3.6-35B-A3B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-08, @@ -25047,7 +24092,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -25056,7 +24100,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "metadata": { "notes": "'supports_image_input' is a deprecated field. Use 'supports_embedding_image_input' instead." }, @@ -25069,7 +24112,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "supports_embedding_image_input": true @@ -25078,7 +24120,6 @@ "input_cost_per_token": 0.0001, "litellm_provider": "cohere", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "supports_embedding_image_input": true @@ -25089,7 +24130,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.12e-07, "supports_function_calling": true, @@ -25105,7 +24145,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.84e-07, "supports_function_calling": true, @@ -25121,7 +24160,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 4.2e-06, "output_cost_per_token_flex": 2.1e-06, @@ -25138,7 +24176,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -25161,7 +24198,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -25189,7 +24225,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -25205,7 +24240,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -25224,7 +24258,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -25244,7 +24277,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -25261,7 +24293,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -25292,7 +24323,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -25323,7 +24353,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -25363,7 +24392,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -25397,7 +24425,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.3e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -25410,7 +24437,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -25422,7 +24448,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-pixtral-large.md", @@ -27688,14 +26713,12 @@ "litellm_provider": "featherless_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat" }, "featherless_ai/featherless-ai/Qwerky-QwQ-32B": { "litellm_provider": "featherless_ai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat" }, "fireworks-ai-4.1b-to-16b": { @@ -27743,7 +26766,6 @@ "input_cost_per_token": 1.6e-08, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -27753,7 +26775,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/pricing", @@ -27766,7 +26787,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 20480, - "max_tokens": 20480, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://fireworks.ai/pricing", @@ -27778,7 +26798,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 160000, - "max_tokens": 160000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://fireworks.ai/pricing", @@ -27790,7 +26809,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 20480, - "max_tokens": 20480, "mode": "chat", "output_cost_per_token": 2.19e-06, "source": "https://fireworks.ai/pricing", @@ -27802,7 +26820,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -27814,7 +26831,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/models/fireworks/deepseek-v3-0324", @@ -27826,7 +26842,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/pricing", @@ -27839,7 +26854,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/pricing", @@ -27852,7 +26866,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.68e-06, "source": "https://fireworks.ai/models/fireworks/deepseek-v3p2", @@ -27867,7 +26880,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -27886,7 +26898,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.96e-06, "output_cost_per_token_priority": 4.95e-06, @@ -27906,7 +26917,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.96e-06, "output_cost_per_token_priority": 4.95e-06, @@ -27922,7 +26932,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -27935,7 +26944,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 2.19e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p5", @@ -27949,7 +26957,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://artificialanalysis.ai/models/glm-4-5-air", @@ -27964,7 +26971,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "source": "https://fireworks.ai/pricing", "supports_function_calling": true, @@ -27978,7 +26984,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p7", @@ -27993,7 +26998,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28012,7 +27016,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -28032,7 +27035,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 7.2e-07, @@ -28049,7 +27051,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28064,7 +27065,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://fireworks.ai/models/fireworks/kimi-k2-instruct", @@ -28077,7 +27077,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://app.fireworks.ai/models/fireworks/kimi-k2-instruct-0905", @@ -28090,7 +27089,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://fireworks.ai/pricing", @@ -28105,7 +27103,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28122,7 +27119,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28142,7 +27138,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28158,7 +27153,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28171,7 +27165,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28184,7 +27177,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://fireworks.ai/pricing", @@ -28198,7 +27190,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28211,7 +27202,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://fireworks.ai/pricing", @@ -28224,7 +27214,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28237,7 +27226,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://fireworks.ai/pricing", @@ -28249,7 +27237,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://fireworks.ai/pricing", @@ -28262,7 +27249,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/models/fireworks/minimax-m2p1", @@ -28278,7 +27264,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.8e-06, @@ -28294,7 +27279,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/pricing", @@ -28307,7 +27291,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28320,7 +27303,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://fireworks.ai/pricing", @@ -28333,7 +27315,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28347,7 +27328,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28363,7 +27343,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://fireworks.ai/models/fireworks/glm-4p7", @@ -28378,7 +27357,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202752, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28394,7 +27372,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28413,7 +27390,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -28433,7 +27409,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 7.2e-07, @@ -28450,7 +27425,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28466,7 +27440,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://fireworks.ai/pricing", @@ -28483,7 +27456,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28501,7 +27473,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28520,7 +27491,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-06, "output_cost_per_token_priority": 6e-06, @@ -28538,7 +27508,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -28554,7 +27523,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://fireworks.ai/models/fireworks/minimax-m2p1", @@ -28570,7 +27538,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.8e-06, @@ -28587,7 +27554,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -28601,7 +27567,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28610,7 +27575,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28619,7 +27583,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28628,7 +27591,6 @@ "input_cost_per_token": 1.6e-08, "litellm_provider": "fireworks_ai-embedding-models", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "source": "https://fireworks.ai/pricing" @@ -28637,7 +27599,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 3e-08, @@ -28665,7 +27626,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.26e-06, "output_cost_per_token": 3.96e-06, "cache_read_input_token_cost": 2.34e-07, @@ -28693,7 +27653,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 4e-07, "supports_prompt_caching": false, @@ -28716,7 +27675,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, @@ -28743,7 +27701,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "input_cost_per_token": 5e-07, "output_cost_per_token": 1.5e-06, "cache_read_input_token_cost": 2.5e-07, @@ -28767,7 +27724,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-08, @@ -28791,7 +27747,6 @@ "litellm_provider": "friendliai", "max_input_tokens": 202752, "max_output_tokens": 202752, - "max_tokens": 202752, "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, @@ -28818,7 +27773,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 9e-07, @@ -28831,7 +27785,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -28844,7 +27797,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_batches": 3e-06, @@ -28858,7 +27810,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28870,7 +27821,6 @@ "litellm_provider": "openai", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28882,7 +27832,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-06, "supports_system_messages": true, @@ -28894,7 +27843,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "source": "OpenAI needs to add pricing for this ft model, will be updated when added by OpenAI. Defaulting to base model pricing", @@ -28911,7 +27859,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -28932,7 +27879,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -28952,7 +27898,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -28974,7 +27919,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -28995,7 +27939,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -29016,7 +27959,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "output_cost_per_token_batches": 4e-07, @@ -29037,7 +27979,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 1.6e-05, "output_cost_per_token_batches": 8e-06, @@ -29060,7 +28001,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29117,7 +28057,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -29175,7 +28114,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29229,7 +28167,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29281,7 +28218,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -29327,7 +28263,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -29376,7 +28311,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -29416,7 +28350,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -29475,7 +28408,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -29535,7 +28467,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29591,7 +28522,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -29629,7 +28559,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -29684,7 +28613,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -29731,7 +28659,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -29778,7 +28705,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29824,7 +28750,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29868,7 +28793,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -29920,7 +28844,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -29982,7 +28905,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_image": 0.00012, "output_cost_per_token": 1.2e-05, @@ -30035,7 +28957,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30088,7 +29009,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30149,7 +29069,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -30196,7 +29115,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -30259,7 +29177,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30320,7 +29237,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30384,7 +29300,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -30440,7 +29355,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 7.5e-06, "output_cost_per_token": 7.5e-06, @@ -30488,7 +29402,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -30531,7 +29444,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_image": 0.00012, "output_cost_per_token": 1.2e-05, @@ -30584,7 +29496,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -30641,7 +29552,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -30674,7 +29584,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_reasoning_token": 2.5e-06, @@ -30712,7 +29621,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -30759,7 +29667,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -30783,7 +29690,6 @@ "input_cost_per_token_batches": 1.2e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30800,7 +29706,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30818,7 +29723,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30837,7 +29741,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30856,7 +29759,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "vertex_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30870,7 +29772,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30882,7 +29783,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "gemini", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30901,7 +29801,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "gemini", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -30922,7 +29821,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -30968,7 +29866,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "image_generation", "output_cost_per_image": 0.045, "output_cost_per_image_token": 6e-05, @@ -31020,7 +29917,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -31080,7 +29976,6 @@ "input_cost_per_video_token_batches": 6e-06, "litellm_provider": "gemini", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -31097,7 +29992,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "rpm": 1000, @@ -31134,7 +30028,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "rpm": 1000, @@ -31180,7 +30073,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31243,7 +30135,6 @@ "supports_reasoning": false, "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -31295,7 +30186,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -31344,7 +30234,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -31387,7 +30276,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.045, "output_cost_per_image_token": 6e-05, @@ -31429,7 +30317,6 @@ "litellm_provider": "gemini", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -31468,7 +30355,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, @@ -31513,7 +30399,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 4e-07, "output_cost_per_token": 4e-07, @@ -31570,7 +30455,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -31629,7 +30513,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31688,7 +30571,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 1e-05, "output_cost_per_token": 1e-05, @@ -31718,7 +30600,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -31772,7 +30653,6 @@ "litellm_provider": "gemini", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_200k_tokens": 1.5e-05, @@ -31815,7 +30695,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -31877,7 +30756,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -31940,7 +30818,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 3e-06, @@ -32002,7 +30879,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32067,7 +30943,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32129,7 +31004,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32192,7 +31066,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32250,7 +31123,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32288,7 +31160,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -32352,7 +31223,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -32421,7 +31291,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 3e-06, @@ -32466,7 +31335,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32501,7 +31369,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -32564,7 +31431,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32625,7 +31491,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32689,7 +31554,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -32745,7 +31609,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 7.5e-06, "output_cost_per_token": 7.5e-06, @@ -32792,7 +31655,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -32827,7 +31689,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Rate limits not documented for gemini-exp-1114. Assuming same as gemini-1.5-pro.", "supports_tool_choice": true @@ -32850,7 +31711,6 @@ "litellm_provider": "gemini", "max_input_tokens": 2097152, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "notes": "Rate limits not documented for gemini-exp-1206. Assuming same as gemini-1.5-pro.", "supports_tool_choice": true @@ -32871,7 +31731,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "gemini", "max_output_tokens": 8192, - "max_tokens": 8192, + "max_input_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.05e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -32885,7 +31745,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "gemini", "max_output_tokens": 8192, - "max_tokens": 8192, + "max_input_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.05e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -32909,7 +31769,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 0, "output_cost_per_character_above_128k_tokens": 0, @@ -32929,7 +31788,6 @@ "litellm_provider": "gemini", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -32945,7 +31803,6 @@ "litellm_provider": "gemini", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -32981,7 +31838,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32767, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 0, "output_cost_per_character_above_128k_tokens": 0, @@ -33000,7 +31856,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -33025,7 +31880,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -33049,7 +31903,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -33066,7 +31919,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -33082,7 +31934,6 @@ "deprecation_date": "2026-10-22", "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.05, "output_cost_per_second_1080p": 0.08, @@ -33097,7 +31948,6 @@ "gemini/veo-3.1-fast-generate-001": { "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -33113,7 +31963,6 @@ "gemini/veo-3.1-generate-001": { "litellm_provider": "gemini", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -33132,7 +31981,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33155,7 +32003,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33178,7 +32025,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 5e-06, "supported_endpoints": [ @@ -33199,7 +32045,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_above_100k_tokens": 2.5e-06, @@ -33224,7 +32069,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33247,7 +32091,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33270,7 +32113,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33292,7 +32134,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33315,7 +32156,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33337,7 +32177,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33360,7 +32199,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33379,7 +32217,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33396,7 +32233,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33404,7 +32240,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33412,7 +32247,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33420,7 +32254,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true }, @@ -33428,7 +32261,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33438,7 +32270,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/model-deprecation-history.yml", "supports_function_calling": true, @@ -33451,7 +32282,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/model-deprecation-history.yml", "supports_function_calling": true, @@ -33467,7 +32297,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33477,7 +32306,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33487,7 +32315,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33496,7 +32323,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -33506,7 +32332,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33515,7 +32340,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 64000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "supports_function_calling": true, "supports_parallel_function_calling": true @@ -33527,7 +32351,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://github.blog/changelog/2026-10-19-github-copilot-models-deprecated-on-october-19-2026", @@ -33542,7 +32365,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "supported_endpoints": [ @@ -33563,7 +32385,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -33585,7 +32406,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 4.5e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33607,7 +32427,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -33631,7 +32450,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_200k_tokens": 1.8e-06, @@ -33655,7 +32473,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -33680,7 +32497,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -33704,7 +32520,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -33728,7 +32543,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-07, "output_cost_per_token_above_272k_tokens": 7.5e-07, @@ -33752,7 +32566,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -33776,7 +32589,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -33796,7 +32608,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 917504, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33814,7 +32625,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.2e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -33829,19 +32639,16 @@ "github_copilot/text-embedding-3-small": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "github_copilot/text-embedding-3-small-inference": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "github_copilot/text-embedding-ada-002": { "litellm_provider": "github_copilot", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding" }, "chatgpt/gpt-6-sol": { @@ -33853,7 +32660,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33874,7 +32680,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33895,7 +32700,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33916,7 +32720,6 @@ ], "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "source": "https://developers.openai.com/api/docs/pricing", "supports_function_calling": true, "supports_max_reasoning_effort": true, @@ -33933,7 +32736,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.5", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33953,7 +32755,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-luna", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33973,7 +32774,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-sol", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -33993,7 +32793,6 @@ "source": "https://platform.openai.com/docs/models/gpt-5.6-terra", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34012,7 +32811,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34032,7 +32830,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34050,7 +32847,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34068,7 +32864,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34082,7 +32877,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34097,7 +32891,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34113,7 +32906,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34128,7 +32920,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/chat/completions", @@ -34148,7 +32939,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34163,7 +32953,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "responses", "supported_endpoints": [ "/v1/responses" @@ -34179,7 +32968,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34190,7 +32978,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34202,7 +32989,6 @@ "litellm_provider": "gigachat", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true, @@ -34213,7 +32999,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -34222,7 +33007,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024 @@ -34231,7 +33015,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560 @@ -34240,7 +33023,6 @@ "input_cost_per_token": 0.0, "litellm_provider": "gigachat", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048 @@ -34250,7 +33032,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_function_calling": true, @@ -34262,7 +33043,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -34273,7 +33053,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -34284,7 +33063,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -34295,7 +33073,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.4e-05, "supports_function_calling": true @@ -34305,7 +33082,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true @@ -34315,7 +33091,6 @@ "litellm_provider": "gmi", "max_input_tokens": 409600, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true @@ -34325,7 +33100,6 @@ "litellm_provider": "gmi", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -34336,7 +33110,6 @@ "litellm_provider": "gmi", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -34347,7 +33120,6 @@ "litellm_provider": "gmi", "max_input_tokens": 163840, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true @@ -34357,7 +33129,6 @@ "litellm_provider": "gmi", "max_input_tokens": 163840, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 8.8e-07, "supports_function_calling": true @@ -34367,7 +33138,6 @@ "litellm_provider": "gmi", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -34379,7 +33149,6 @@ "litellm_provider": "gmi", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -34388,7 +33157,6 @@ "litellm_provider": "gmi", "max_input_tokens": 196608, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -34420,7 +33188,6 @@ "litellm_provider": "baseten", "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -34465,7 +33232,6 @@ "litellm_provider": "baseten", "max_input_tokens": 128072, "max_output_tokens": 128072, - "max_tokens": 128072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -34495,7 +33261,6 @@ "litellm_provider": "gmi", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-06, "supports_vision": true @@ -34505,7 +33270,6 @@ "litellm_provider": "gmi", "max_input_tokens": 202752, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2e-06 }, @@ -34518,7 +33282,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.9e-07, "output_cost_per_token_batches": 1.5e-07, @@ -34540,7 +33303,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 3.8e-07, "output_cost_per_token_batches": 1.9e-07, @@ -34562,7 +33324,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8e-08, "output_cost_per_token_batches": 4e-08, @@ -34594,7 +33355,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -34634,7 +33394,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -34668,7 +33427,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -34700,7 +33458,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -34721,7 +33478,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://developers.openai.com/api/docs/pricing", @@ -34737,7 +33493,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -34755,7 +33510,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 2e-06, @@ -34772,7 +33526,6 @@ "litellm_provider": "openai", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 4e-06, "supports_prompt_caching": true, @@ -34785,7 +33538,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 2e-06, "source": "https://developers.openai.com/api/docs/pricing" @@ -34795,7 +33547,6 @@ "litellm_provider": "text-completion-openai", "max_input_tokens": 8192, "max_output_tokens": 4097, - "max_tokens": 4097, "mode": "completion", "output_cost_per_token": 2e-06 }, @@ -34805,7 +33556,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "supports_function_calling": true, @@ -34820,7 +33570,6 @@ "litellm_provider": "openai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -34836,7 +33585,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -34851,7 +33599,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_function_calling": true, @@ -34869,7 +33616,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_batches": 1.5e-05, @@ -34891,7 +33637,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -34935,7 +33680,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_priority": 1.4e-05, @@ -34978,7 +33722,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_batches": 8e-07, @@ -35022,7 +33765,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "output_cost_per_token_priority": 2.8e-06, @@ -35066,7 +33808,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -35104,7 +33845,6 @@ "litellm_provider": "openai", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_priority": 8e-07, @@ -35141,7 +33881,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -35164,7 +33903,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -35188,7 +33926,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 1.7e-05, @@ -35213,7 +33950,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 1.7e-05, @@ -35235,7 +33971,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -35253,7 +33988,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 8e-05, "output_cost_per_token": 1e-05, @@ -35271,7 +34005,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35308,7 +34041,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35343,7 +34075,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1e-05, @@ -35381,7 +34112,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35419,7 +34149,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35456,7 +34185,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35497,7 +34225,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35527,7 +34254,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 1e-06, @@ -35554,7 +34280,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 6e-07, @@ -35574,7 +34299,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -35592,7 +34316,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35619,7 +34342,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -35700,7 +34422,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -35727,7 +34448,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -36281,7 +35001,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -36327,7 +35046,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5-codex", @@ -36362,7 +35080,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -36412,7 +35129,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-06, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex-mini", @@ -36443,7 +35159,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex-max", @@ -36475,7 +35190,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-codex", @@ -36507,7 +35221,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.1-chat-latest", @@ -36542,7 +35255,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_priority": 2e-05, @@ -36594,7 +35306,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -36645,7 +35356,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.2-codex", @@ -36677,7 +35387,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.2-chat-latest", @@ -36712,7 +35421,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -36762,7 +35470,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -36804,7 +35511,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.000168, "output_cost_per_token_batches": 8.4e-05, @@ -36884,7 +35590,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_above_272k_tokens": 7.5e-05, @@ -36971,7 +35676,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -37062,7 +35766,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_decisions": 0.0, @@ -37148,7 +35851,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37230,7 +35932,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37314,7 +36015,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -37397,7 +36097,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_above_272k_tokens": 1.8e-06, @@ -37455,7 +36154,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37494,7 +36192,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37533,7 +36230,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 7.5e-05, "output_cost_per_token_above_272k_tokens": 0.0001125, @@ -37571,7 +36267,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37610,7 +36305,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 2e-05, "output_cost_per_token_above_272k_tokens": 3e-05, @@ -37644,7 +36338,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "reasoning_effort_levels": [ @@ -37690,7 +36383,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -37753,7 +36445,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "output_cost_per_token_above_272k_tokens": 4.5e-05, @@ -37809,7 +36500,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -37861,7 +36551,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -37920,7 +36609,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -37978,7 +36666,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_272k_tokens": 2.25e-05, @@ -38029,7 +36716,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -38082,7 +36768,6 @@ "litellm_provider": "openai", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 0.00018, "output_cost_per_token_above_272k_tokens": 0.00027, @@ -38138,7 +36823,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_flex": 2.25e-06, @@ -38193,7 +36877,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "output_cost_per_token_flex": 2.25e-06, @@ -38246,7 +36929,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, @@ -38298,7 +36980,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, @@ -38345,7 +37026,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 272000, - "max_tokens": 272000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -38388,7 +37068,6 @@ "litellm_provider": "openai", "max_input_tokens": 400000, "max_output_tokens": 272000, - "max_tokens": 272000, "mode": "responses", "output_cost_per_token": 0.00012, "output_cost_per_token_batches": 6e-05, @@ -38437,7 +37116,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -38483,7 +37161,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supported_endpoints": [ @@ -38519,7 +37196,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5-chat-latest", @@ -38552,7 +37228,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.4e-05, "output_cost_per_token_priority": 2.8e-05, @@ -38596,7 +37271,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://developers.openai.com/api/docs/models/gpt-5.3-chat-latest", @@ -38633,7 +37307,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -38685,7 +37358,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -38735,7 +37407,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -38785,7 +37456,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -38871,7 +37541,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -38906,7 +37575,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -38941,7 +37609,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -38976,7 +37643,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -39013,7 +37679,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -39051,7 +37716,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -39087,7 +37751,6 @@ "litellm_provider": "openai", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 1.6e-05, @@ -39113,7 +37776,6 @@ }, "gradient_ai/alibaba-qwen3-32b": { "litellm_provider": "gradient_ai", - "max_tokens": 40960, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39128,7 +37790,6 @@ "gradient_ai/anthropic-claude-3-opus": { "input_cost_per_token": 1.5e-05, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 7.5e-05, "supported_endpoints": [ @@ -39144,7 +37805,6 @@ "gradient_ai/anthropic-claude-3.5-haiku": { "input_cost_per_token": 8e-07, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 4e-06, "supported_endpoints": [ @@ -39160,7 +37820,6 @@ "gradient_ai/anthropic-claude-3.5-sonnet": { "input_cost_per_token": 3e-06, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.5e-05, "supported_endpoints": [ @@ -39176,7 +37835,6 @@ "gradient_ai/anthropic-claude-3.7-sonnet": { "input_cost_per_token": 3e-06, "litellm_provider": "gradient_ai", - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 1.5e-05, "supported_endpoints": [ @@ -39192,7 +37850,6 @@ "gradient_ai/deepseek-r1-distill-llama-70b": { "input_cost_per_token": 9.9e-07, "litellm_provider": "gradient_ai", - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 9.9e-07, "supported_endpoints": [ @@ -39208,7 +37865,6 @@ "gradient_ai/llama3-8b-instruct": { "input_cost_per_token": 2e-07, "litellm_provider": "gradient_ai", - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 2e-07, "supported_endpoints": [ @@ -39224,7 +37880,6 @@ "gradient_ai/llama3.3-70b-instruct": { "input_cost_per_token": 6.5e-07, "litellm_provider": "gradient_ai", - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 6.5e-07, "supported_endpoints": [ @@ -39240,7 +37895,6 @@ "gradient_ai/mistral-nemo-instruct-2407": { "input_cost_per_token": 3e-07, "litellm_provider": "gradient_ai", - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 3e-07, "supported_endpoints": [ @@ -39255,7 +37909,6 @@ }, "gradient_ai/openai-gpt-4o": { "litellm_provider": "gradient_ai", - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39269,7 +37922,6 @@ }, "gradient_ai/openai-gpt-4o-mini": { "litellm_provider": "gradient_ai", - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions" @@ -39284,7 +37936,6 @@ "gradient_ai/openai-o3": { "input_cost_per_token": 2e-06, "litellm_provider": "gradient_ai", - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supported_endpoints": [ @@ -39300,7 +37951,6 @@ "gradient_ai/openai-o3-mini": { "input_cost_per_token": 1.1e-06, "litellm_provider": "gradient_ai", - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supported_endpoints": [ @@ -39316,7 +37966,6 @@ "lemonade/Qwen3-Coder-30B-A3B-Instruct-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 262144, "max_output_tokens": 32768, "mode": "chat", @@ -39328,7 +37977,6 @@ "lemonade/gpt-oss-20b-mxfp4-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 131072, "max_output_tokens": 32768, "mode": "chat", @@ -39340,7 +37988,6 @@ "lemonade/gpt-oss-120b-mxfp-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 131072, "max_output_tokens": 32768, "mode": "chat", @@ -39352,7 +37999,6 @@ "lemonade/Gemma-3-4b-it-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "mode": "chat", @@ -39364,7 +38010,6 @@ "lemonade/Qwen3-4B-Instruct-2507-GGUF": { "input_cost_per_token": 0, "litellm_provider": "lemonade", - "max_tokens": 32768, "max_input_tokens": 262144, "max_output_tokens": 32768, "mode": "chat", @@ -39378,7 +38023,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "supports_function_calling": true, @@ -39390,7 +38034,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "supports_function_calling": true, @@ -39404,7 +38047,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 1000000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.25e-05, "supports_function_calling": true, @@ -39418,7 +38060,6 @@ "litellm_provider": "amazon_nova", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "supports_function_calling": true, @@ -39432,7 +38073,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "groq", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://console.groq.com/docs/model/llama-guard-3-8b" @@ -39442,7 +38083,6 @@ "litellm_provider": "groq", "max_input_tokens": 512, "max_output_tokens": 512, - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 3e-08, "source": "https://console.groq.com/docs/models" @@ -39452,7 +38092,6 @@ "litellm_provider": "groq", "max_input_tokens": 512, "max_output_tokens": 512, - "max_tokens": 512, "mode": "chat", "output_cost_per_token": 4e-08, "source": "https://console.groq.com/docs/model/meta-llama/llama-prompt-guard-2-86m" @@ -39463,7 +38102,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 6e-07, "search_context_cost_per_query": { @@ -39484,7 +38122,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "search_context_cost_per_query": { @@ -39505,7 +38142,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "search_context_cost_per_query": { @@ -39524,7 +38160,6 @@ "litellm_provider": "groq", "max_input_tokens": 4000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "audio_speech", "source": "https://console.groq.com/docs/model/canopylabs/orpheus-v1-english" }, @@ -39533,7 +38168,6 @@ "litellm_provider": "groq", "max_input_tokens": 4000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "audio_speech", "source": "https://console.groq.com/docs/models" }, @@ -39551,7 +38185,6 @@ }, "heroku/claude-3-5-haiku": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39561,7 +38194,6 @@ }, "heroku/claude-3-5-sonnet-latest": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39571,7 +38203,6 @@ }, "heroku/claude-3-7-sonnet": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39581,7 +38212,6 @@ }, "heroku/claude-4-sonnet": { "litellm_provider": "heroku", - "max_tokens": 8192, "mode": "chat", "supports_function_calling": true, "supports_system_messages": true, @@ -39627,7 +38257,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39640,7 +38269,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -39653,7 +38281,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39666,7 +38293,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39679,7 +38305,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "supports_function_calling": true, @@ -39692,7 +38317,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true, @@ -39705,7 +38329,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_function_calling": true, @@ -39718,7 +38341,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -39731,7 +38353,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_function_calling": true, @@ -39744,7 +38365,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39757,7 +38377,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39770,7 +38389,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39783,7 +38401,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39796,7 +38413,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39809,7 +38425,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -39822,7 +38437,6 @@ "litellm_provider": "hyperbolic", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "supports_function_calling": true, @@ -39835,7 +38449,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 3e-06 }, @@ -39844,7 +38457,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 1e-05 }, @@ -39853,7 +38465,6 @@ "litellm_provider": "ai21", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 1.5e-05 }, @@ -39862,7 +38473,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39872,7 +38482,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39882,7 +38491,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39892,7 +38500,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39902,7 +38509,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39912,7 +38518,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39922,7 +38527,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -39932,7 +38536,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39942,7 +38545,6 @@ "litellm_provider": "ai21", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -39952,7 +38554,6 @@ "litellm_provider": "jina_ai", "max_input_tokens": 1024, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://api.jina.ai/v1/models" @@ -39972,7 +38573,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -40009,7 +38609,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -40037,7 +38636,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 7e-06, "supports_function_calling": false, @@ -40050,7 +38648,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -40063,7 +38660,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -40077,7 +38673,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -40090,7 +38685,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "supports_function_calling": false, @@ -40103,7 +38697,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40116,7 +38709,6 @@ "litellm_provider": "crusoe", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -40130,7 +38722,6 @@ "litellm_provider": "inception", "max_input_tokens": 128000, "max_output_tokens": 50000, - "max_tokens": 50000, "mode": "chat", "output_cost_per_token": 7.5e-07, "supports_function_calling": true, @@ -40145,7 +38736,6 @@ "litellm_provider": "inception", "max_input_tokens": 260000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://docs.inceptionlabs.ai/get-started/models", @@ -40160,7 +38750,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40181,7 +38771,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40202,7 +38792,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40223,7 +38813,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40244,7 +38834,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40265,7 +38855,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40286,7 +38876,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40307,7 +38897,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40328,7 +38918,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40349,7 +38939,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40370,7 +38960,7 @@ "input_cost_per_token": 1.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.8e-07, "source": "https://inferenceapis.com/pricing", @@ -40391,7 +38981,7 @@ "input_cost_per_token": 1.72e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40412,7 +39002,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://inferenceapis.com/pricing", @@ -40433,7 +39023,7 @@ "input_cost_per_token": 1.82e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 5.72e-06, "source": "https://inferenceapis.com/pricing", @@ -40454,7 +39044,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://inferenceapis.com/pricing", @@ -40474,7 +39064,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inferenceapis.com/pricing", @@ -40493,7 +39083,7 @@ "input_cost_per_token": 4e-08, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://inferenceapis.com/pricing", @@ -40513,7 +39103,7 @@ "input_cost_per_token": 8.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.42e-06, "source": "https://inferenceapis.com/pricing", @@ -40533,7 +39123,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40552,7 +39142,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40571,7 +39161,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40590,7 +39180,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40609,7 +39199,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40628,7 +39218,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40648,7 +39238,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://inferenceapis.com/pricing", @@ -40669,7 +39259,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://inferenceapis.com/pricing", @@ -40690,7 +39280,7 @@ "input_cost_per_token": 8.9e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.42e-06, "source": "https://inferenceapis.com/pricing", @@ -40710,7 +39300,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://inferenceapis.com/pricing", @@ -40729,7 +39319,7 @@ "input_cost_per_token": 4e-08, "litellm_provider": "inferenceapis", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://inferenceapis.com/pricing", @@ -40748,7 +39338,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40768,7 +39358,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40788,7 +39378,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40807,7 +39397,7 @@ "input_cost_per_token": 2.6e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://inferenceapis.com/pricing", @@ -40827,7 +39417,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inferenceapis.com/pricing", @@ -40848,7 +39438,7 @@ "input_cost_per_token": 1.82e-06, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 5.72e-06, "source": "https://inferenceapis.com/pricing", @@ -40869,7 +39459,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "inferenceapis", "max_input_tokens": 1048575, - "max_tokens": 1048575, + "max_output_tokens": 1048575, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://inferenceapis.com/pricing", @@ -40891,7 +39481,6 @@ "litellm_provider": "text-completion-inception", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 7.5e-07 }, @@ -40900,7 +39489,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40914,7 +39502,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40928,7 +39515,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40942,7 +39528,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -40955,7 +39540,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -40968,7 +39552,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -40981,7 +39564,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -40994,7 +39576,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "supports_function_calling": true, @@ -41007,7 +39588,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -41020,7 +39600,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41033,7 +39612,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 16384, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41046,7 +39624,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -41059,7 +39636,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41072,7 +39648,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -41085,7 +39660,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41098,7 +39672,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-08, "supports_function_calling": true, @@ -41112,7 +39685,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-08, "supports_function_calling": true, @@ -41125,7 +39697,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -41138,7 +39709,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41151,7 +39721,6 @@ "litellm_provider": "lambda_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "supports_function_calling": true, @@ -41196,14 +39765,12 @@ "max-x-max/50-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.036 }, "max-x-max/max-steps/stability.stable-diffusion-xl-v0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.072 }, @@ -41299,7 +39866,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_character": 1.5e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -41310,7 +39876,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_character": 1e-06, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models", @@ -41321,7 +39886,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -41331,7 +39895,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.56e-06, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -41342,7 +39905,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41356,7 +39918,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -41371,7 +39932,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.2e-07, "output_cost_per_token_batches": 1.1e-07, @@ -41385,7 +39945,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41399,7 +39958,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -41412,7 +39970,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -41425,7 +39982,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -41440,7 +39996,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -41454,7 +40009,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-70b-instruct.md" @@ -41465,7 +40019,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-8b-instruct.md" @@ -41477,7 +40030,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9.7e-07, "output_cost_per_token_batches": 4.85e-07, @@ -41500,7 +40052,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 10000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_batches": 3.3e-07, @@ -41522,7 +40073,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41563,7 +40113,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41604,7 +40153,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "search_context_cost_per_query": { @@ -41645,7 +40193,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.25e-06, "search_context_cost_per_query": { @@ -41686,7 +40233,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "search_context_cost_per_query": { @@ -41743,7 +40289,6 @@ "litellm_provider": "meta", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-05, "search_context_cost_per_query": { @@ -41784,7 +40329,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 128000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41800,7 +40344,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 128000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41816,7 +40359,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 1000000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41833,7 +40375,6 @@ "litellm_provider": "meta_llama", "max_input_tokens": 10000000, "max_output_tokens": 4028, - "max_tokens": 4028, "mode": "chat", "source": "https://ai.developer.meta.com/docs/models", "supported_modalities": [ @@ -41855,7 +40396,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -41878,7 +40418,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 196000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -41902,7 +40441,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 196000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, "output_cost_per_token_priority": 2.1e-06, @@ -42100,7 +40638,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_batches": 1e-06, @@ -42122,7 +40659,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 40000, - "max_tokens": 40000, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -42144,7 +40680,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_batches": 1e-07, @@ -42166,7 +40701,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-07, "output_cost_per_token_batches": 5e-08, @@ -42188,7 +40722,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-07, "output_cost_per_token_batches": 7e-08, @@ -42208,7 +40741,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-7b-instruct.md", @@ -42219,7 +40751,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mistral-large.md", @@ -42230,7 +40761,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -42245,7 +40775,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_batches": 7.5e-07, @@ -42265,7 +40794,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -42278,7 +40806,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-mixtral-8x7b-instruct.md", @@ -42293,7 +40820,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-08, "output_cost_per_token_batches": 2e-08, @@ -42313,7 +40839,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-07, "output_cost_per_token_batches": 1.5e-07, @@ -42330,7 +40855,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -42345,7 +40869,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -42360,7 +40883,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://mistral.ai/technology/", @@ -42373,7 +40895,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://docs.mistral.ai/models/devstral-small-2-25-12", @@ -42388,7 +40909,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -42403,7 +40923,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -42417,7 +40936,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.mistral.ai/models/ministral-3-14b-25-12", @@ -42433,7 +40951,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://docs.mistral.ai/models/ministral-3-14b-25-12", @@ -42449,7 +40966,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.mistral.ai/models/ministral-3-3b-25-12", @@ -42465,7 +40981,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.mistral.ai/models/ministral-3-3b-25-12", @@ -42480,7 +40995,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "source": "https://docs.mistral.ai/models/mistral-embed-23-12" }, @@ -42490,7 +41004,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -42671,7 +41184,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.mistral.ai/models/voxtral-small-25-07", @@ -42686,7 +41198,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://docs.mistral.ai/models/voxtral-small-25-07", @@ -42702,7 +41213,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42728,7 +41238,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42750,7 +41259,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "reasoning_effort_levels": [ @@ -42773,7 +41281,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42795,7 +41302,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42817,7 +41323,6 @@ "litellm_provider": "mistral", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "reasoning_effort_levels": [ @@ -42896,7 +41401,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.mistral.ai/models/model-cards/mistral-medium-3-5-26-04", @@ -42913,7 +41417,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://docs.mistral.ai/models/model-cards/mistral-small-4-0-26-03", @@ -42928,7 +41431,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/codestral-embed": { @@ -42936,7 +41438,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/codestral-embed-2505": { @@ -42944,7 +41445,6 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "mistral", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding" }, "mistral/mistral-large-4": { @@ -42952,7 +41452,7 @@ "input_cost_per_token": 6.8e-07, "litellm_provider": "mistral", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.09e-06, "reasoning_effort_levels": [ @@ -42973,7 +41473,7 @@ "input_cost_per_token": 6.8e-07, "litellm_provider": "mistral", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.09e-06, "reasoning_effort_levels": [ @@ -42995,7 +41495,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43011,7 +41510,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43027,7 +41525,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://docs.mistral.ai/models/mistral-large-3-25-12", @@ -43043,7 +41540,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43064,7 +41560,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43085,7 +41580,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43106,7 +41600,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -43127,7 +41620,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-07, "supports_assistant_prefill": true, @@ -43141,7 +41633,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -43162,7 +41653,6 @@ "litellm_provider": "mistral", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://mistral.ai/pricing", @@ -43178,7 +41668,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43194,7 +41683,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://mistral.ai/pricing", @@ -43210,7 +41698,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43226,7 +41713,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://mistral.ai/pricing", @@ -43242,7 +41728,6 @@ "litellm_provider": "mistral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_assistant_prefill": true, @@ -43255,7 +41740,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://mistral.ai/technology/", @@ -43269,7 +41753,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_assistant_prefill": true, @@ -43286,7 +41769,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 1.25e-06, @@ -43308,7 +41790,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, "output_cost_per_token_batches": 1.5e-06, @@ -43328,7 +41809,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://platform.kimi.ai/docs/pricing/chat-k27-code", @@ -43346,7 +41826,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/guide/kimi-k2-5-quickstart", @@ -43363,7 +41842,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://platform.kimi.ai/docs/pricing/chat-k26", @@ -43380,7 +41858,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -43401,7 +41878,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43414,7 +41890,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43428,7 +41903,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43441,7 +41915,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43455,7 +41928,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43468,7 +41940,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43482,7 +41953,6 @@ "litellm_provider": "moonshot", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://platform.moonshot.ai/docs/pricing", @@ -43495,7 +41965,6 @@ "litellm_provider": "morph", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": false, @@ -43509,7 +41978,6 @@ "litellm_provider": "morph", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.9e-06, "supports_function_calling": false, @@ -43527,7 +41995,6 @@ "input_cost_per_video_per_second_above_8s_interval": 0.001, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -43551,7 +42018,6 @@ "input_cost_per_video_per_second_above_8s_interval": 0.001, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -43711,7 +42177,6 @@ ] }, "nebius/deepseek-ai/DeepSeek-R1": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 8e-07, @@ -43723,7 +42188,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-R1-0528": { - "max_tokens": 164000, "max_input_tokens": 164000, "max_output_tokens": 164000, "input_cost_per_token": 8e-07, @@ -43735,7 +42199,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-R1-Distill-Llama-70B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2.5e-07, @@ -43746,7 +42209,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V3": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 5e-07, @@ -43757,7 +42219,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V3-0324": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 5e-07, @@ -43768,7 +42229,6 @@ "source": "https://nebius.com/prices" }, "nebius/google/gemma-3-27b-it": { - "max_tokens": 110000, "max_input_tokens": 110000, "max_output_tokens": 110000, "input_cost_per_token": 1e-07, @@ -43780,7 +42240,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/google%2Fgemma-3-27b-it" }, "nebius/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -43791,7 +42250,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Llama-Guard-3-8B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-08, @@ -43801,7 +42259,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-8B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-08, @@ -43812,7 +42269,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-70B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.3e-07, @@ -43823,7 +42279,6 @@ "source": "https://nebius.com/prices" }, "nebius/meta-llama/Meta-Llama-3.1-405B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-06, @@ -43834,7 +42289,6 @@ "source": "https://nebius.com/prices" }, "nebius/mistralai/Mistral-Nemo-Instruct-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 4e-08, @@ -43845,7 +42299,6 @@ "source": "https://nebius.com/prices" }, "nebius/NousResearch/Hermes-3-Llama-3.1-405B": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-06, @@ -43856,7 +42309,6 @@ "source": "https://nebius.com/prices" }, "nebius/nvidia/Llama-3.1-Nemotron-Ultra-253B-v1": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 6e-07, @@ -43867,7 +42319,6 @@ "source": "https://nebius.com/prices" }, "nebius/nvidia/Llama-3.3-Nemotron-Super-49B-v1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -43878,7 +42329,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-235B-A22B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -43889,7 +42339,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-32B": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -43900,7 +42349,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-32B" }, "nebius/Qwen/Qwen3-30B-A3B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -43911,7 +42359,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-14B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 8e-08, @@ -43922,7 +42369,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-4B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 8e-08, @@ -43933,7 +42379,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/QwQ-32B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.5e-07, @@ -43945,7 +42390,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-72B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.3e-07, @@ -43956,7 +42400,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-32B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 6e-08, @@ -43967,7 +42410,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-Coder-7B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-08, @@ -43978,7 +42420,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2.5-VL-72B-Instruct": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 2.5e-07, @@ -43990,7 +42431,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/Qwen%2FQwen2.5-VL-72B-Instruct" }, "nebius/Qwen/Qwen2-VL-72B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -44002,7 +42442,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen2-VL-7B-Instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-08, @@ -44013,7 +42452,6 @@ "source": "https://nebius.com/prices" }, "nebius/deepseek-ai/DeepSeek-V4-Flash": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.4e-07, @@ -44025,7 +42463,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/deepseek-ai%2FDeepSeek-V4-Flash" }, "nebius/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 1.4e-07, @@ -44037,7 +42474,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/deepseek-ai%2FDeepSeek-V4-Flash-0731" }, "nebius/deepseek-ai/DeepSeek-V4-Pro": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.75e-06, @@ -44063,7 +42499,6 @@ "litellm_provider": "nebius", "max_input_tokens": 1048000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://tokenfactory.nebius.com/endpoints?modals=endpoint-details&model-id=deepseek-ai/DeepSeek-V4.1-Flash", @@ -44072,7 +42507,6 @@ "supports_vision": true }, "nebius/MiniMaxAI/MiniMax-M2.5": { - "max_tokens": 196608, "max_input_tokens": 196608, "max_output_tokens": 196608, "input_cost_per_token": 3e-07, @@ -44084,7 +42518,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/MiniMaxAI%2FMiniMax-M2.5" }, "nebius/MiniMaxAI/MiniMax-M3": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 3e-07, @@ -44097,7 +42530,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/MiniMaxAI%2FMiniMax-M3" }, "nebius/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9.5e-07, @@ -44110,7 +42542,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/moonshotai%2FKimi-K2.6" }, "nebius/moonshotai/Kimi-K2.7-Code": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9.5e-07, @@ -44123,7 +42554,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/moonshotai%2FKimi-K2.7-Code" }, "nebius/moonshotai/Kimi-K3": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 3e-06, @@ -44136,7 +42566,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/moonshotai%2FKimi-K3" }, "nebius/NousResearch/Hermes-4-405B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-06, @@ -44148,7 +42577,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/NousResearch%2FHermes-4-405B" }, "nebius/NousResearch/Hermes-4-70B": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.3e-07, @@ -44160,7 +42588,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/NousResearch%2FHermes-4-70B" }, "nebius/nvidia/Cosmos3-Super-Reasoner": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -44173,7 +42600,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/nvidia%2FCosmos3-Super-Reasoner" }, "nebius/nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6e-07, @@ -44184,7 +42610,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FLlama-3_1-Nemotron-Ultra-253B-v1" }, "nebius/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-08, @@ -44196,7 +42621,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNVIDIA-Nemotron-3-Nano-30B-A3B" }, "nebius/nvidia/Nemotron-3-Nano-Omni": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-08, @@ -44209,7 +42633,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3-Nano-Omni" }, "nebius/nvidia/nemotron-3-super-120b-a12b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 3e-07, @@ -44221,7 +42644,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2Fnemotron-3-super-120b-a12b" }, "nebius/nvidia/Nemotron-3-Ultra-550b-a55b": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1e-06, @@ -44233,7 +42655,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3-Ultra-550b-a55b" }, "nebius/nvidia/Nemotron-3_5-Lightning": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 6e-08, @@ -44245,7 +42666,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/nvidia%2FNemotron-3_5-Lightning" }, "nebius/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -44257,7 +42677,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/openai%2Fgpt-oss-120b" }, "nebius/openbmb/MiniCPM-V-4_5": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 6.58e-07, @@ -44268,7 +42687,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/image2text/openbmb%2FMiniCPM-V-4_5" }, "nebius/Qwen/Qwen3-235B-A22B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -44279,7 +42697,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-235B-A22B-Instruct-2507" }, "nebius/Qwen/Qwen3-30B-A3B-Instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -44290,7 +42707,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-30B-A3B-Instruct-2507" }, "nebius/Qwen/Qwen3-Next-80B-A3B-Thinking": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.5e-07, @@ -44302,7 +42718,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/Qwen%2FQwen3-Next-80B-A3B-Thinking" }, "nebius/Qwen/Qwen3.5-397B-A17B": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 6e-07, @@ -44324,7 +42739,6 @@ "supports_reasoning": true }, "nebius/zai-org/GLM-5.1": { - "max_tokens": 202752, "max_input_tokens": 202752, "max_output_tokens": 202752, "input_cost_per_token": 1.4e-06, @@ -44336,7 +42750,6 @@ "source": "https://tokenfactory.nebius.com/models/catalog/text2text/zai-org%2FGLM-5.1" }, "nebius/zai-org/GLM-5.2": { - "max_tokens": 1048576, "max_input_tokens": 1048576, "max_output_tokens": 1048576, "input_cost_per_token": 1.4e-06, @@ -44351,7 +42764,7 @@ "input_cost_per_token": 1.4e-06, "litellm_provider": "nebius", "max_input_tokens": 1024000, - "max_tokens": 1024000, + "max_output_tokens": 1024000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://tokenfactory.nebius.com/models/catalog/text2text/zai-org%2FGLM-5.3", @@ -44359,7 +42772,6 @@ "supports_reasoning": true }, "nebius/zai-org/GLM-5.3-Flash": { - "max_tokens": 1024000, "max_input_tokens": 1024000, "max_output_tokens": 1024000, "input_cost_per_token": 1.5e-07, @@ -44372,7 +42784,6 @@ "supports_vision": true }, "nebius/BAAI/bge-en-icl": { - "max_tokens": 32768, "max_input_tokens": 32768, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44381,7 +42792,6 @@ "source": "https://nebius.com/prices" }, "nebius/BAAI/bge-multilingual-gemma2": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44390,7 +42800,6 @@ "source": "https://nebius.com/prices" }, "nebius/intfloat/e5-mistral-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44399,7 +42808,6 @@ "source": "https://nebius.com/prices" }, "nebius/Qwen/Qwen3-Embedding-8B": { - "max_tokens": 40960, "max_input_tokens": 40960, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -44416,7 +42824,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -44438,7 +42845,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.3e-07, "output_cost_per_token_batches": 1.2e-07, @@ -44459,7 +42865,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -44482,7 +42887,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.5e-07, "output_cost_per_token_batches": 3.25e-07, @@ -44505,7 +42909,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -44528,7 +42931,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_batches": 3e-05, @@ -44550,7 +42952,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 0.0006, "output_cost_per_token_batches": 0.0003, @@ -44584,7 +42985,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 0.0006, "output_cost_per_token_batches": 0.0003, @@ -44622,7 +43022,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -44663,7 +43062,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "source": "https://developers.openai.com/api/docs/models/o3-deep-research", @@ -44696,7 +43094,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 4e-05, "source": "https://developers.openai.com/api/docs/models/o3-deep-research", @@ -44733,7 +43130,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "output_cost_per_token_batches": 4e-06, @@ -44776,7 +43172,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44797,7 +43192,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44816,7 +43210,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -44854,7 +43247,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-05, "output_cost_per_token_batches": 4e-05, @@ -44897,7 +43289,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -44925,7 +43316,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-06, "source": "https://developers.openai.com/api/docs/models/o4-mini-deep-research", @@ -44958,7 +43348,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "responses", "output_cost_per_token": 8e-06, "source": "https://developers.openai.com/api/docs/models/o4-mini-deep-research", @@ -44995,7 +43384,6 @@ "litellm_provider": "openai", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_batches": 2.2e-06, @@ -45022,7 +43410,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45035,7 +43422,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45048,7 +43434,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.068e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45061,7 +43446,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45075,7 +43459,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45088,7 +43471,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45102,7 +43484,6 @@ "litellm_provider": "oci", "max_input_tokens": 10485760, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45115,7 +43496,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45128,7 +43508,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45141,7 +43520,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45154,7 +43532,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45167,7 +43544,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45180,7 +43556,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45193,7 +43568,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45206,7 +43580,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45219,7 +43592,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45239,7 +43611,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45258,7 +43629,6 @@ "litellm_provider": "oci", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45278,7 +43648,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45292,7 +43661,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/enterprise-ai/cost-estimator/", @@ -45314,7 +43682,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45326,7 +43693,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45339,7 +43705,6 @@ "litellm_provider": "oci", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 9e-08, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45351,7 +43716,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45363,7 +43727,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.56e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45375,7 +43738,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45388,7 +43750,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45400,7 +43761,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45412,7 +43772,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45424,7 +43783,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [], @@ -45437,7 +43795,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45452,7 +43809,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45468,7 +43824,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45485,7 +43840,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45501,7 +43855,6 @@ "litellm_provider": "oci", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -45515,7 +43868,6 @@ "litellm_provider": "oci", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45527,7 +43879,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45542,7 +43893,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45557,7 +43907,6 @@ "litellm_provider": "oci", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45572,7 +43921,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45585,7 +43933,6 @@ "litellm_provider": "oci", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/pricing", @@ -45597,7 +43944,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45607,7 +43953,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45617,7 +43962,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45627,7 +43971,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45637,7 +43980,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -45648,7 +43990,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45659,7 +44000,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "oci", "max_input_tokens": 512, - "max_tokens": 512, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 384, @@ -45670,7 +44010,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "oci", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -45682,7 +44021,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": false @@ -45692,7 +44030,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45701,7 +44038,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45710,7 +44046,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45720,7 +44055,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45730,7 +44064,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45740,7 +44073,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45750,7 +44082,6 @@ "litellm_provider": "ollama", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45760,7 +44091,6 @@ "litellm_provider": "ollama", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45770,7 +44100,6 @@ "litellm_provider": "ollama", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45780,7 +44109,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45790,7 +44118,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45799,7 +44126,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45808,7 +44134,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45817,7 +44142,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45826,7 +44150,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45835,7 +44158,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45844,7 +44166,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45854,7 +44175,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45863,7 +44183,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -45872,7 +44191,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "completion", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45882,7 +44200,6 @@ "litellm_provider": "ollama", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45892,7 +44209,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45902,7 +44218,6 @@ "litellm_provider": "ollama", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45912,7 +44227,6 @@ "litellm_provider": "ollama", "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45922,7 +44236,6 @@ "litellm_provider": "ollama", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45932,7 +44245,6 @@ "litellm_provider": "ollama", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45941,7 +44253,6 @@ "litellm_provider": "ollama", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 0.0, "supports_function_calling": true @@ -45951,7 +44262,6 @@ "litellm_provider": "ollama", "max_input_tokens": 2048, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -45960,7 +44270,6 @@ "litellm_provider": "openai", "max_input_tokens": 32768, "max_output_tokens": 0, - "max_tokens": 0, "mode": "moderation", "output_cost_per_token": 0.0 }, @@ -45969,7 +44278,6 @@ "litellm_provider": "openai", "max_input_tokens": 32768, "max_output_tokens": 0, - "max_tokens": 0, "mode": "moderation", "output_cost_per_token": 0.0 }, @@ -45987,7 +44295,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46013,7 +44320,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07, "output_cost_per_token_batches": 1.5e-07, @@ -46033,7 +44339,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46055,7 +44360,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_batches": 1e-07, @@ -46074,7 +44378,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46088,7 +44391,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46101,7 +44403,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46114,7 +44415,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 6.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46127,7 +44427,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 127000, "max_output_tokens": 127000, - "max_tokens": 127000, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46140,7 +44439,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 118000, "max_output_tokens": 118000, - "max_tokens": 118000, "mode": "chat", "output_cost_per_token": 1.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46153,7 +44451,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46167,7 +44464,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46180,7 +44476,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 8.7e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46193,7 +44488,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 9.1e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46207,7 +44501,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.3e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46221,7 +44514,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46235,7 +44527,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 131000, "max_output_tokens": 131000, - "max_tokens": 131000, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46249,7 +44540,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2.9e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46263,7 +44553,6 @@ "litellm_provider": "ovhcloud", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.9e-07, "source": "https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog/", @@ -46296,7 +44585,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.4e-06 }, @@ -46305,7 +44593,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46314,7 +44601,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46323,7 +44609,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -46332,7 +44617,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07 }, @@ -46341,7 +44625,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46350,7 +44633,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46359,7 +44641,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46369,7 +44650,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-06 }, @@ -46378,7 +44658,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46388,7 +44667,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46444,7 +44722,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-06, "search_context_cost_per_query": { @@ -46459,7 +44737,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_reasoning_token": 3e-06, "output_cost_per_token": 8e-06, @@ -46476,7 +44754,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.8e-06 }, @@ -46486,7 +44763,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 12000, "max_output_tokens": 12000, - "max_tokens": 12000, "mode": "chat", "output_cost_per_token": 1.8e-06 }, @@ -46495,7 +44771,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 200000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -46509,7 +44784,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "search_context_cost_per_query": { @@ -46524,7 +44799,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "perplexity", "max_input_tokens": 128000, - "max_tokens": 128000, + "max_output_tokens": 128000, "mode": "chat", "output_cost_per_token": 8e-06, "search_context_cost_per_query": { @@ -46540,7 +44815,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46550,7 +44824,6 @@ "litellm_provider": "perplexity", "max_input_tokens": 12000, "max_output_tokens": 12000, - "max_tokens": 12000, "mode": "chat", "output_cost_per_token": 2.8e-07 }, @@ -46559,7 +44832,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46571,7 +44843,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46583,7 +44854,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46595,7 +44865,6 @@ "litellm_provider": "publicai", "max_input_tokens": 16384, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46607,7 +44876,6 @@ "litellm_provider": "publicai", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46619,7 +44887,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46859,7 +45126,6 @@ "input_cost_per_token": 4e-09, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -46869,7 +45135,6 @@ "input_cost_per_token": 3e-08, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560, @@ -46880,7 +45145,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46892,7 +45156,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46905,7 +45168,6 @@ "litellm_provider": "publicai", "max_input_tokens": 32768, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://platform.publicai.co/docs", @@ -46919,7 +45181,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.8e-06, "supports_function_calling": true, @@ -46934,7 +45195,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 8.8e-07, "output_cost_per_token_flex": 4.4e-07, @@ -46954,7 +45214,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46973,7 +45232,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 32768, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_batches": 3e-07, @@ -46990,7 +45248,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -47006,7 +45263,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.45e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47022,7 +45278,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.41e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47038,7 +45293,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.236e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47054,7 +45308,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.41e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47070,7 +45323,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.86e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47086,7 +45338,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.45e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-qwen-qwen3-next-80b-a3b.html", @@ -47104,7 +45355,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.66e-06, "output_cost_per_token_batches": 1.33e-06, @@ -47125,7 +45375,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_batches": 6e-07, @@ -47180,7 +45429,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "supports_tool_choice": true @@ -47190,7 +45438,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 5e-07, "supports_tool_choice": true @@ -47200,7 +45447,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47210,7 +45456,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47220,7 +45465,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47230,7 +45474,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47240,7 +45483,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47250,7 +45492,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.75e-06, "supports_tool_choice": true @@ -47260,7 +45501,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8086, "max_output_tokens": 8086, - "max_tokens": 8086, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47270,7 +45510,6 @@ "litellm_provider": "replicate", "max_input_tokens": 8086, "max_output_tokens": 8086, - "max_tokens": 8086, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47280,7 +45519,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47290,7 +45528,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.5e-07, "supports_tool_choice": true @@ -47300,7 +45537,6 @@ "litellm_provider": "replicate", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-06, "supports_tool_choice": true @@ -47429,7 +45665,6 @@ "mode": "chat", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_system_messages": true }, @@ -47584,7 +45819,6 @@ "mode": "chat", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "supports_function_calling": true, "supports_reasoning": true, "supports_system_messages": true @@ -47605,7 +45839,6 @@ "mode": "chat", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_reasoning": true, "supports_system_messages": true }, @@ -47615,7 +45848,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47625,7 +45857,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47635,7 +45866,6 @@ "litellm_provider": "cohere", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -47645,7 +45875,6 @@ "litellm_provider": "cohere", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://cohere.com/pricing" @@ -47656,7 +45885,6 @@ "litellm_provider": "cohere", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://cohere.com/pricing" @@ -47687,7 +45915,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47696,7 +45923,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47705,7 +45931,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47714,7 +45939,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47723,7 +45947,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "completion", "output_cost_per_token": 0.0 }, @@ -47732,7 +45955,6 @@ "litellm_provider": "sagemaker", "max_input_tokens": 4096, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -47741,7 +45963,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 196608, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://cloud.sambanova.ai/plans/pricing", @@ -47754,7 +45975,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7e-06, "source": "https://cloud.sambanova.ai/plans/pricing" @@ -47764,7 +45984,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "metadata": { "notes": "For vision models, images are converted to 6432 input tokens and are billed at that amount" }, @@ -47781,7 +46000,6 @@ "litellm_provider": "sambanova", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.sambanova.ai/plans/pricing", @@ -47790,7 +46008,6 @@ "supports_tool_choice": true }, "sambanova/DeepSeek-V3.1": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-06, @@ -47803,7 +46020,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.2e-07, @@ -47816,7 +46032,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/DeepSeek-V3.2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 3e-06, @@ -47828,7 +46043,6 @@ "source": "https://cloud.sambanova.ai/plans/pricing" }, "sambanova/gemma-4-31B-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3.8e-07, @@ -47844,7 +46058,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 375000, - "max_tokens": 375000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://scx.ai/models/deepseek-v4-1-flash", @@ -47859,7 +46072,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.08e-06, "source": "https://scx.ai/models/glm-5-3", @@ -47875,7 +46087,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.413e-05, "source": "https://scx.ai/models/kimi-k3", @@ -47891,7 +46102,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.98e-06, "source": "https://scx.ai/pricing", @@ -47908,7 +46118,6 @@ "litellm_provider": "scx-ai", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.99e-06, "source": "https://scx.ai/pricing", @@ -47923,7 +46132,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 200000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 3e-06, "output_cost_per_token": 1.5e-05, @@ -47939,7 +46147,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 1.35e-06, "output_cost_per_token": 5.4e-06, @@ -47950,56 +46157,48 @@ "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-1.5-large": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-1.5-mini": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/jamba-instruct": { "litellm_provider": "snowflake", "max_input_tokens": 256000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama2-70b-chat": { "litellm_provider": "snowflake", "max_input_tokens": 4096, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3-70b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3-8b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.1-405b": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 1.2e-06, "output_cost_per_token": 1.2e-06, @@ -48010,7 +46209,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 7.2e-07, "output_cost_per_token": 7.2e-07, @@ -48021,7 +46219,6 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 2.4e-07, "output_cost_per_token": 2.4e-07, @@ -48031,18 +46228,15 @@ "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.2-3b": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/llama3.3-70b": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 7.2e-07, @@ -48056,21 +46250,18 @@ "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/mistral-large": { "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/mistral-large2": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -48082,39 +46273,33 @@ "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/reka-core": { "litellm_provider": "snowflake", "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/reka-flash": { "litellm_provider": "snowflake", "max_input_tokens": 100000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-arctic": { "litellm_provider": "snowflake", "max_input_tokens": 4096, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-llama-3.1-405b": { "litellm_provider": "snowflake", "max_input_tokens": 8000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat" }, "snowflake/snowflake-llama-3.3-70b": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 7.2e-07, @@ -48311,21 +46496,18 @@ "stability.sd3-5-large-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "stability.sd3-large-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.08 }, "stability.stable-image-core-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, @@ -48501,21 +46683,18 @@ "stability.stable-image-core-v1:1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.04 }, "stability.stable-image-ultra-v1:0": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.14 }, "stability.stable-image-ultra-v1:1": { "litellm_provider": "bedrock", "max_input_tokens": 77, - "max_tokens": 77, "mode": "image_generation", "output_cost_per_image": 0.14 }, @@ -48549,7 +46728,6 @@ "litellm_provider": "text-completion-codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "completion", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/" @@ -48559,7 +46737,6 @@ "litellm_provider": "text-completion-codestral", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "completion", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/capabilities/code_generation/" @@ -48570,7 +46747,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48582,7 +46758,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48593,7 +46768,6 @@ "input_cost_per_token_batches": 6.5e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0, @@ -48605,7 +46779,6 @@ "input_cost_per_token_batches": 1e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0, @@ -48616,7 +46789,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1536, @@ -48627,7 +46799,6 @@ "input_cost_per_token_batches": 5e-08, "litellm_provider": "openai", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0, "output_cost_per_token_batches": 0.0 @@ -48637,7 +46808,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 3072, @@ -48647,7 +46817,6 @@ "input_cost_per_token": 6.25e-09, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 3072, - "max_tokens": 3072, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48659,7 +46828,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", "max_input_tokens": 2048, - "max_tokens": 2048, "mode": "embedding", "output_cost_per_token": 0, "output_vector_size": 768, @@ -48670,7 +46838,6 @@ "litellm_provider": "vertex_ai-text-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "completion", "output_cost_per_token": 2.8e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models" @@ -48680,7 +46847,6 @@ "litellm_provider": "vertex_ai-text-models", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "completion", "output_cost_per_token": 2.8e-05, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#foundation_models" @@ -48706,7 +46872,8 @@ "together-ai-8.1b-21b": { "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", - "max_tokens": 1000, + "max_output_tokens": 1000, + "max_input_tokens": 1000, "mode": "chat", "output_cost_per_token": 3e-07 }, @@ -48759,7 +46926,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 65536, "max_output_tokens": 8192, - "max_tokens": 8192, "metadata": { "successor": "together_ai/deepseek-ai/DeepSeek-V4-Pro-0813" }, @@ -48775,7 +46941,7 @@ "input_cost_per_token": 1.04e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.04e-06, "source": "https://api.together.ai/v1/models", @@ -48802,7 +46968,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models", @@ -48823,7 +46989,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "metadata": { "successor": "together_ai/zai-org/GLM-5.2" }, @@ -48840,7 +47006,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -48857,7 +47023,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://www.together.ai/pricing", @@ -48867,7 +47033,7 @@ "input_cost_per_token": 0.0, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.together.ai/docs/serverless-models" @@ -48876,7 +47042,7 @@ "input_cost_per_token": 1.7e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://api.together.ai/v1/models", @@ -48891,7 +47057,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://api.together.ai/v1/models", @@ -48902,7 +47068,7 @@ "input_cost_per_token": 1.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://www.together.ai/pricing", @@ -48912,7 +47078,7 @@ "input_cost_per_token": 3.2e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.28e-06, "source": "https://api.together.ai/v1/models" @@ -48922,7 +47088,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 1010000, - "max_tokens": 1010000, + "max_output_tokens": 1010000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://api.together.ai/v1/models", @@ -48932,7 +47098,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://api.together.ai/v1/models" @@ -48943,7 +47109,7 @@ "input_cost_per_token": 1.4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://api.together.ai/v1/models", @@ -48958,7 +47124,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -48972,7 +47138,7 @@ "input_cost_per_token": 1.32e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://api.together.ai/v1/models", @@ -48987,7 +47153,7 @@ "input_cost_per_token": 3.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models", @@ -48998,7 +47164,7 @@ "input_cost_per_token": 1.25e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.25e-06, "source": "https://docs.together.ai/docs/serverless-models", @@ -49011,7 +47177,7 @@ "input_cost_per_token": 3e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "reasoning_effort_levels": [ @@ -49033,7 +47199,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 524288, - "max_tokens": 524288, + "max_output_tokens": 524288, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://api.together.ai/v1/models", @@ -49049,7 +47215,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models", @@ -49066,7 +47231,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models", @@ -49083,7 +47247,6 @@ "litellm_provider": "together_ai", "max_input_tokens": 1048575, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://api.together.ai/v1/models", @@ -49168,7 +47331,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 2.4e-07, "output_cost_per_token_batches": 1.2e-07, @@ -49187,7 +47349,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 1.4e-07, "output_cost_per_token_batches": 7e-08, @@ -49208,7 +47369,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 300000, "max_output_tokens": 10000, - "max_tokens": 10000, "mode": "chat", "output_cost_per_token": 3.2e-06, "output_cost_per_token_batches": 1.6e-06, @@ -49229,7 +47389,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_assistant_prefill": true, @@ -49249,7 +47408,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49277,7 +47435,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -49295,7 +47452,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -49314,7 +47470,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -49332,7 +47487,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -49350,7 +47504,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -49389,7 +47542,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.65e-05, "search_context_cost_per_query": { @@ -49431,7 +47583,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.8e-05, "supports_assistant_prefill": true, @@ -49463,7 +47614,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -49503,7 +47653,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -49542,7 +47691,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -49580,7 +47728,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -49618,7 +47765,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -49654,7 +47800,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49672,7 +47817,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -49688,7 +47832,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -49703,7 +47846,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -49721,7 +47863,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -49741,7 +47882,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -49768,7 +47908,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supports_function_calling": true, "supports_prompt_caching": false, @@ -49785,7 +47924,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "supports_assistant_prefill": true, @@ -49815,7 +47953,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "search_context_cost_per_query": { @@ -49843,7 +47980,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -49880,7 +48016,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "search_context_cost_per_query": { @@ -49917,7 +48052,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.75e-05, "search_context_cost_per_query": { @@ -49957,7 +48091,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "search_context_cost_per_query": { @@ -49988,7 +48121,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-r1.html", @@ -50004,7 +48136,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 1.85e-06, "output_cost_per_token_batches": 9.25e-07, @@ -50025,7 +48156,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 163840, "max_output_tokens": 163840, - "max_tokens": 163840, "mode": "chat", "output_cost_per_token": 2.22e-06, "output_cost_per_token_flex": 1.11e-06, @@ -50043,7 +48173,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50057,7 +48186,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -50072,7 +48200,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2.2e-07, "output_cost_per_token_batches": 1.1e-07, @@ -50086,7 +48213,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50100,7 +48226,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-1b-instruct.md", @@ -50113,7 +48238,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-meta-llama-3-2-3b-instruct.md", @@ -50126,7 +48250,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/bedrock/USD/current/bedrock.json", @@ -50141,7 +48264,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -50156,7 +48278,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9.7e-07, "output_cost_per_token_batches": 4.85e-07, @@ -50179,7 +48300,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 10000000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_batches": 3.3e-07, @@ -50200,7 +48320,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-mistral-ai-pixtral-large.md", @@ -50212,7 +48331,6 @@ "litellm_provider": "v0", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50226,7 +48344,6 @@ "litellm_provider": "v0", "max_input_tokens": 512000, "max_output_tokens": 512000, - "max_tokens": 512000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_function_calling": true, @@ -50240,7 +48357,6 @@ "litellm_provider": "v0", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50254,7 +48370,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.4e-07 }, @@ -50263,7 +48378,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07 }, @@ -50272,7 +48386,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07 }, @@ -50281,7 +48394,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 40960, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -50292,7 +48404,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 262144, "max_output_tokens": 66536, - "max_tokens": 66536, "mode": "chat", "output_cost_per_token": 1.6e-06, "supports_function_calling": true, @@ -50303,7 +48414,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 300000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.4e-07, "supports_vision": true, @@ -50315,7 +48425,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.4e-07, "supports_function_calling": true, @@ -50326,7 +48435,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 300000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3.2e-06, "supports_vision": true, @@ -50338,7 +48446,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50349,7 +48456,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_vision": true, @@ -50364,7 +48470,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_vision": true, @@ -50379,7 +48484,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 4e-06, "supports_vision": true, @@ -50394,7 +48498,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -50409,7 +48512,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -50424,7 +48526,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_vision": true, @@ -50439,7 +48540,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -50452,7 +48552,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50470,7 +48569,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50488,7 +48586,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50507,7 +48604,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -50527,7 +48623,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -50547,7 +48642,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -50567,7 +48661,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, @@ -50590,7 +48683,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": false, @@ -50611,7 +48703,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50631,7 +48722,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -50648,7 +48738,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -50660,7 +48749,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -50671,7 +48759,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -50682,7 +48769,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50691,7 +48777,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.19e-06, "supports_tool_choice": true @@ -50701,7 +48786,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 9.9e-07, "supports_function_calling": true, @@ -50713,7 +48797,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 9e-07, "supports_tool_choice": true @@ -50723,7 +48806,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.5e-06, "supports_vision": true, @@ -50742,7 +48824,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -50763,7 +48844,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50772,7 +48852,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "supports_vision": true, @@ -50784,7 +48863,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50793,7 +48871,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -50802,7 +48879,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -50811,7 +48887,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.9e-07, "supports_tool_choice": true @@ -50821,7 +48896,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 8e-08, "supports_tool_choice": true @@ -50831,7 +48905,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_tool_choice": true @@ -50841,7 +48914,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8e-08, "supports_function_calling": true, @@ -50852,7 +48924,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.6e-07, "supports_vision": true, @@ -50864,7 +48935,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1e-07 }, @@ -50873,7 +48943,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_function_calling": true, @@ -50884,7 +48953,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_vision": true, @@ -50896,7 +48964,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "supports_function_calling": true, @@ -50907,7 +48974,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 6e-07, "supports_tool_choice": true @@ -50917,7 +48983,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-07, "supports_vision": true, @@ -50929,7 +48994,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 9e-07, "supports_function_calling": true, @@ -50940,7 +49004,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -50949,7 +49012,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.8e-07, "supports_function_calling": true, @@ -50961,7 +49023,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "supports_function_calling": true, @@ -50973,7 +49034,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true @@ -50983,7 +49043,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 4e-08, "supports_function_calling": true, @@ -50994,7 +49053,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1e-07, "supports_vision": true, @@ -51006,7 +49064,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "chat", "output_cost_per_token": 0.0 }, @@ -51015,7 +49072,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -51026,7 +49082,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.9e-07 }, @@ -51035,7 +49090,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -51047,7 +49101,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 65536, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true @@ -51057,7 +49110,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_vision": true, @@ -51070,7 +49122,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 6e-06, "supports_vision": true, @@ -51083,7 +49134,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.2e-06, "supports_function_calling": true, @@ -51094,7 +49144,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.2e-06 }, @@ -51103,7 +49152,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.9e-06 }, @@ -51112,7 +49160,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 16385, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -51123,7 +49170,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 8192, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-06 }, @@ -51132,7 +49178,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 3e-05, "supports_vision": true, @@ -51146,7 +49191,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "supports_vision": true, @@ -51161,7 +49205,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.6e-06, "supports_vision": true, @@ -51176,7 +49219,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 1047576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4e-07, "supports_vision": true, @@ -51191,7 +49233,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -51206,7 +49247,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-07, "supports_vision": true, @@ -51221,7 +49261,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_vision": true, @@ -51236,7 +49275,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_vision": true, @@ -51251,7 +49289,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_function_calling": true, @@ -51265,7 +49302,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.4e-06, "supports_vision": true, @@ -51278,7 +49314,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51287,7 +49322,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51296,7 +49330,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 0, "max_output_tokens": 0, - "max_tokens": 0, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -51305,7 +49338,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1e-06 }, @@ -51314,7 +49346,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 200000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.5e-05 }, @@ -51323,7 +49354,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 5e-06 }, @@ -51332,7 +49362,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 127000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8e-06 }, @@ -51341,7 +49370,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -51353,7 +49381,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_vision": true, @@ -51365,7 +49392,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "chat", "output_cost_per_token": 1e-05, "supports_function_calling": true, @@ -51376,7 +49402,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1e-05, "supports_vision": true, @@ -51388,7 +49413,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -51399,7 +49423,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.5e-05, "supports_function_calling": true @@ -51409,7 +49432,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "supports_function_calling": true, @@ -51420,7 +49442,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "supports_function_calling": true, @@ -51431,7 +49452,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_function_calling": true, @@ -51442,7 +49462,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-06, "supports_function_calling": true, @@ -51453,7 +49472,6 @@ "litellm_provider": "vercel_ai_gateway", "max_input_tokens": 128000, "max_output_tokens": 96000, - "max_tokens": 96000, "mode": "chat", "output_cost_per_token": 1.1e-06, "supports_function_calling": true, @@ -51465,7 +49483,6 @@ "input_cost_per_token": 4.5e-07, "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 1.8e-06, "source": "https://vercel.com/ai-gateway/models/glm-4.6", @@ -51516,7 +49533,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -51530,7 +49546,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5e-06, "supports_assistant_prefill": true, @@ -51550,7 +49565,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -51579,7 +49593,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -51602,7 +49615,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51618,7 +49630,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51633,7 +49644,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_assistant_prefill": true, @@ -51647,7 +49657,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.25e-06, "supports_assistant_prefill": true, @@ -51661,7 +49670,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -51675,7 +49683,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 7.5e-05, "supports_assistant_prefill": true, @@ -51688,7 +49695,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51701,7 +49707,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, @@ -51721,7 +49726,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51756,7 +49760,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51795,7 +49798,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51833,7 +49835,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51870,7 +49871,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51908,7 +49908,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -51946,7 +49945,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -51984,7 +49982,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -52028,7 +50025,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -52066,7 +50062,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "search_context_cost_per_query": { @@ -52111,7 +50106,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52150,7 +50144,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52187,7 +50180,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52228,7 +50220,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52271,7 +50262,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52310,7 +50300,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -52350,7 +50339,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52380,7 +50368,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -52419,7 +50406,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52458,7 +50444,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -52482,7 +50467,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52494,7 +50478,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52506,7 +50489,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52518,7 +50500,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -52530,7 +50511,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52541,7 +50521,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52552,7 +50531,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true, @@ -52566,7 +50544,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.7e-06, "output_cost_per_token_batches": 8.5e-07, @@ -52588,7 +50565,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.68e-06, "output_cost_per_token_batches": 8.4e-07, @@ -52609,7 +50585,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 65336, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "output_cost_per_token_batches": 2.7e-06, @@ -52632,7 +50607,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_image_token": 3e-05, @@ -52691,7 +50665,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -52726,7 +50699,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -52759,7 +50731,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -52787,7 +50758,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, @@ -52817,7 +50787,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -52857,7 +50826,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -52916,7 +50884,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 1.5e-06, "output_cost_per_token": 1.5e-06, @@ -52977,7 +50944,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -53034,7 +51000,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 65536, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_image": 0.134, "output_cost_per_image_token": 0.00012, @@ -53048,7 +51013,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53059,7 +51023,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -53070,7 +51033,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "supports_tool_choice": true @@ -53081,7 +51043,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53092,7 +51053,6 @@ "litellm_provider": "vertex_ai-ai21_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-07, "supports_tool_choice": true @@ -53122,7 +51082,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "audio_speech", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -53158,7 +51117,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "audio_speech", "output_cost_per_image": 0.08, "output_cost_per_token": 0, @@ -53195,7 +51153,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 1.6e-05, "source": "https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama-3.2-90b-vision-instruct-maas", @@ -53208,7 +51165,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama-3.2-90b-vision-instruct-maas", @@ -53221,7 +51177,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "metadata": { "notes": "VertexAI states that The Llama 3.1 API service for llama-3.1-70b-instruct-maas and llama-3.1-8b-instruct-maas are in public preview and at no cost." }, @@ -53237,7 +51192,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 2048, - "max_tokens": 2048, "metadata": { "notes": "VertexAI states that The Llama 3.2 API service is at no cost during public preview, and will be priced as per dollar-per-1M-tokens at GA." }, @@ -53254,7 +51208,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.15e-06, "output_cost_per_token_batches": 5.75e-07, @@ -53276,7 +51229,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.15e-06, "output_cost_per_token_batches": 5.75e-07, @@ -53298,7 +51250,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 10000000, "max_output_tokens": 10000000, - "max_tokens": 10000000, "mode": "chat", "output_cost_per_token": 7e-07, "output_cost_per_token_batches": 3.5e-07, @@ -53320,7 +51271,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 10000000, "max_output_tokens": 10000000, - "max_tokens": 10000000, "mode": "chat", "output_cost_per_token": 7e-07, "output_cost_per_token_batches": 3.5e-07, @@ -53341,7 +51291,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53352,7 +51301,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53363,7 +51311,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models", @@ -53376,7 +51323,6 @@ "litellm_provider": "vertex_ai-minimax_models", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53390,7 +51336,6 @@ "litellm_provider": "vertex_ai-moonshot_models", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53405,7 +51350,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53423,7 +51367,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53440,7 +51383,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53452,7 +51394,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53464,7 +51405,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53476,7 +51416,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53488,7 +51427,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53499,7 +51437,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53510,7 +51447,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53521,7 +51457,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 6e-06, "supports_function_calling": true, @@ -53532,7 +51467,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, "supports_function_calling": true, @@ -53543,7 +51477,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-07, "supports_function_calling": true, @@ -53554,7 +51487,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -53567,7 +51499,6 @@ "litellm_provider": "vertex_ai-mistral_models", "max_input_tokens": 32000, "max_output_tokens": 8191, - "max_tokens": 8191, "mode": "chat", "output_cost_per_token": 3e-07, "supports_function_calling": true, @@ -53601,7 +51532,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 262144, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/maas/google/gemma-4-26b-a4b-it", @@ -53618,7 +51548,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.6e-07, "output_cost_per_token_batches": 1.8e-07, @@ -53631,7 +51560,6 @@ "litellm_provider": "vertex_ai-openai_models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53646,7 +51574,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53663,7 +51590,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53683,7 +51609,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, - "max_tokens": 2000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53703,7 +51628,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, - "max_tokens": 2000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53724,7 +51648,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 200000, "max_output_tokens": 200000, - "max_tokens": 200000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -53744,7 +51667,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -53763,7 +51685,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 8.8e-07, "output_cost_per_token_batches": 4.4e-07, @@ -53783,7 +51704,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_batches": 9e-07, @@ -53800,7 +51720,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53816,7 +51735,6 @@ "litellm_provider": "vertex_ai-qwen_models", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -53829,7 +51747,6 @@ "vertex_ai/veo-3.1-generate-preview": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -53844,7 +51761,6 @@ "vertex_ai/veo-3.1-fast-generate-preview": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -53861,7 +51777,6 @@ "deprecation_date": "2026-11-17", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "output_cost_per_second_4k": 0.6, @@ -53877,7 +51792,6 @@ "deprecation_date": "2026-11-17", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -53893,7 +51807,6 @@ "vertex_ai/veo-3.1-lite-generate-001": { "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.05, "output_cost_per_second_1080p": 0.08, @@ -53911,7 +51824,6 @@ "litellm_provider": "voyage", "max_input_tokens": 16000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53920,7 +51832,6 @@ "litellm_provider": "voyage", "max_input_tokens": 8000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53929,7 +51840,6 @@ "litellm_provider": "voyage", "max_input_tokens": 8000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53939,7 +51849,6 @@ "litellm_provider": "voyage", "max_input_tokens": 4000, "max_output_tokens": 4000, - "max_tokens": 4000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53949,7 +51858,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53958,7 +51866,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0 }, @@ -53967,7 +51874,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53977,7 +51883,6 @@ "litellm_provider": "voyage", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://docs.voyageai.com/docs/pricing" @@ -53986,7 +51891,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4000, - "max_tokens": 4000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -53994,7 +51898,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54002,7 +51905,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -54012,7 +51914,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54020,7 +51921,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54028,7 +51928,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54036,7 +51935,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54044,7 +51942,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54052,7 +51949,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54060,7 +51956,6 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "voyage", "max_input_tokens": 120000, - "max_tokens": 120000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54068,7 +51963,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54076,7 +51970,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54084,7 +51977,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -54094,7 +51986,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 16000, - "max_tokens": 16000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54102,7 +51993,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4096, - "max_tokens": 4096, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54110,7 +52000,6 @@ "input_cost_per_token": 1e-07, "litellm_provider": "voyage", "max_input_tokens": 4000, - "max_tokens": 4000, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -54118,13 +52007,11 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0 }, "wandb/openai/gpt-oss-120b": { "supports_reasoning": true, - "max_tokens": 131072, "max_input_tokens": 131000, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -54135,7 +52022,6 @@ }, "wandb/openai/gpt-oss-20b": { "supports_reasoning": true, - "max_tokens": 131072, "max_input_tokens": 131000, "max_output_tokens": 131072, "input_cost_per_token": 3e-08, @@ -54145,7 +52031,6 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "wandb/moonshotai/Kimi-K2.5": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "cache_read_input_token_cost": 1e-07, @@ -54160,7 +52045,6 @@ "supports_vision": true }, "wandb/meta-llama/Llama-3.1-8B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 131000, "max_output_tokens": 128000, "input_cost_per_token": 2.2e-07, @@ -54171,7 +52055,6 @@ }, "wandb/deepseek-ai/DeepSeek-V3.1": { "supports_reasoning": true, - "max_tokens": 128000, "max_input_tokens": 161000, "max_output_tokens": 128000, "input_cost_per_token": 5.5e-07, @@ -54181,7 +52064,6 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "wandb/meta-llama/Llama-3.3-70B-Instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 7.1e-07, @@ -54195,7 +52077,6 @@ "litellm_provider": "watsonx", "max_input_tokens": 8192, "max_output_tokens": 1024, - "max_tokens": 1024, "mode": "chat", "output_cost_per_token": 2e-07, "supports_audio_input": false, @@ -54213,7 +52094,6 @@ "litellm_provider": "watsonx", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1e-05, "supports_audio_input": false, @@ -54227,7 +52107,6 @@ "supports_vision": false }, "watsonx/bigscience/mt0-xxl-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.908e-06, @@ -54240,7 +52119,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/bigscience/mt0-xxl": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.908e-06, @@ -54253,7 +52131,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/core42/jais-13b-chat": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 0.0005, @@ -54265,7 +52142,6 @@ "supports_vision": false }, "watsonx/google/flan-t5-xl-3b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54277,7 +52153,6 @@ "supports_vision": false }, "watsonx/ibm/granite-13b-chat-v2": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54289,7 +52164,6 @@ "supports_vision": false }, "watsonx/ibm/granite-13b-instruct-v2": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 6e-07, @@ -54301,7 +52175,6 @@ "supports_vision": false }, "watsonx/ibm/granite-3-3-8b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -54313,7 +52186,6 @@ "supports_vision": false }, "watsonx/ibm/granite-4-h-small": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 6.36e-08, @@ -54326,7 +52198,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/ibm/granite-guardian-3-2-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -54338,7 +52209,6 @@ "supports_vision": false }, "watsonx/ibm/granite-guardian-3-3-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -54350,7 +52220,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-1024-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54362,7 +52231,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-1536-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54374,7 +52242,6 @@ "supports_vision": false }, "watsonx/ibm/granite-ttm-512-96-r2": { - "max_tokens": 512, "max_input_tokens": 512, "max_output_tokens": 512, "input_cost_per_token": 3.8e-07, @@ -54386,7 +52253,6 @@ "supports_vision": false }, "watsonx/ibm/granite-vision-3-2-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -54398,7 +52264,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-2-11b-vision-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54410,7 +52275,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-2-1b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1e-07, @@ -54422,7 +52286,6 @@ "supports_vision": false }, "watsonx/meta-llama/llama-3-2-3b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.5e-07, @@ -54434,7 +52297,6 @@ "supports_vision": false }, "watsonx/meta-llama/llama-3-2-90b-vision-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-06, @@ -54446,7 +52308,6 @@ "supports_vision": true }, "watsonx/meta-llama/llama-3-3-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 7.526e-07, @@ -54459,7 +52320,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-4-maverick-17b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3.71e-07, @@ -54472,7 +52332,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3.71e-07, @@ -54485,7 +52344,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/meta-llama/llama-guard-3-11b-vision": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54497,7 +52355,6 @@ "supports_vision": true }, "watsonx/mistralai/mistral-medium-2505": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3e-06, @@ -54509,7 +52366,6 @@ "supports_vision": false }, "watsonx/mistralai/mistral-small-2503": { - "max_tokens": 32000, "max_input_tokens": 32000, "max_output_tokens": 32000, "input_cost_per_token": 1e-07, @@ -54521,7 +52377,6 @@ "supports_vision": false }, "watsonx/mistralai/mistral-small-3-1-24b-instruct-2503": { - "max_tokens": 16384, "max_input_tokens": 131072, "max_output_tokens": 16384, "input_cost_per_token": 1.06e-07, @@ -54534,7 +52389,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/mistralai/pixtral-12b-2409": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 3.5e-07, @@ -54546,7 +52400,6 @@ "supports_vision": true }, "watsonx/openai/gpt-oss-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.59e-07, @@ -54559,7 +52412,6 @@ "source": "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx" }, "watsonx/sdaia/allam-1-13b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1.8e-06, @@ -54596,7 +52448,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54621,7 +52472,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54645,7 +52495,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -54671,7 +52520,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -54699,7 +52547,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -54727,7 +52574,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_above_200k_tokens": 5e-06, @@ -54751,7 +52597,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54773,7 +52618,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54795,7 +52639,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54817,7 +52660,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54839,7 +52681,6 @@ "litellm_provider": "xai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -54861,7 +52702,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 203000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-06, "output_cost_per_token_batches": 1.1e-06, @@ -54884,7 +52724,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_batches": 1.6e-06, "output_cost_per_token_priority": 5.6e-06, @@ -54907,7 +52746,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 203000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-07, "output_cost_per_token_batches": 2e-07, @@ -55505,7 +53343,6 @@ } }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 4.5e-07, @@ -55515,7 +53352,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/flux-kontext-pro": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 4e-08, @@ -55524,7 +53360,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/SSD-1B": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -55533,7 +53368,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/chronos-hermes-13b-v2": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55542,7 +53376,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55551,7 +53384,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55560,7 +53392,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-13b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55569,7 +53400,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55578,7 +53408,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55587,7 +53416,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-34b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55596,7 +53424,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55605,7 +53432,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55614,7 +53440,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-70b-python": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -55623,7 +53448,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55632,7 +53456,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55641,7 +53464,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-llama-7b-python": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -55650,7 +53472,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/code-qwen-1p5-7b": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 2e-07, @@ -55659,7 +53480,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/codegemma-2b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -55668,7 +53488,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/codegemma-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -55677,7 +53496,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-671b-v2-p1": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.2e-06, @@ -55686,7 +53504,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -55695,7 +53512,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-70b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55704,7 +53520,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55713,7 +53528,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55722,7 +53536,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55731,7 +53544,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-kontext-max": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 8e-08, @@ -55740,7 +53552,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/dbrx-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.2e-06, @@ -55749,7 +53560,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-1b-base": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -55758,7 +53568,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-33b-instruct": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -55767,7 +53576,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55776,7 +53584,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base-v1p5": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55785,7 +53592,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-instruct-v1p5": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55794,7 +53600,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-base": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55803,7 +53608,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-instruct": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55812,7 +53616,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-prover-v2": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1.2e-06, @@ -55821,7 +53624,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528-distill-qwen3-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55830,7 +53632,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-70b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55839,7 +53640,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55848,7 +53648,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55857,7 +53656,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-1p5b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -55866,7 +53664,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55875,7 +53672,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55884,7 +53680,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-v2-lite-chat": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 5e-07, @@ -55893,7 +53688,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/deepseek-v2p5": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.2e-06, @@ -55902,7 +53696,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/devstral-small-2505": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55911,7 +53704,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dobby-mini-unhinged-plus-llama-3-1-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -55920,7 +53712,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dobby-unhinged-llama-3-3-70b-new": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55929,7 +53720,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dolphin-2-9-2-qwen2-72b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55938,7 +53728,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/dolphin-2p6-mixtral-8x7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -55947,7 +53736,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ernie-4p5-21b-a3b-pt": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -55956,7 +53744,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ernie-4p5-300b-a47b-pt": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -55965,7 +53752,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/fare-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -55974,7 +53760,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firefunction-v1": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -55983,7 +53768,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firellava-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -55992,7 +53776,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/firesearch-ocr-v6": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56001,7 +53784,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/fireworks-asr-large": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 0.0, @@ -56010,7 +53792,6 @@ "mode": "audio_transcription" }, "fireworks_ai/accounts/fireworks/models/fireworks-asr-v2": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 0.0, @@ -56019,7 +53800,6 @@ "mode": "audio_transcription" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56028,7 +53808,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev-controlnet-union": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-09, @@ -56037,7 +53816,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-dev-fp8": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 5e-10, @@ -56046,7 +53824,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/flux-1-schnell": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56055,7 +53832,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/flux-1-schnell-fp8": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 3.5e-10, @@ -56064,7 +53840,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/gemma-2b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -56073,7 +53848,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-3-27b-it": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56082,7 +53856,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56091,7 +53864,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma-7b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56100,7 +53872,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gemma2-9b-it": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56109,7 +53880,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/glm-4p5v": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.2e-06, @@ -56119,7 +53889,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-120b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.2e-06, @@ -56128,7 +53897,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-20b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 5e-07, @@ -56137,7 +53905,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/hermes-2-pro-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56146,7 +53913,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-38b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56155,7 +53921,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-78b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56164,7 +53929,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/internvl3-8b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -56173,7 +53937,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/japanese-stable-diffusion-xl": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56182,7 +53945,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/kat-coder": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-07, @@ -56191,7 +53953,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/kat-dev-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56200,7 +53961,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/kat-dev-72b-exp": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56209,7 +53969,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-2-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56218,7 +53977,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-3-1b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56227,7 +53985,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-guard-3-8b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56236,7 +53993,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56245,7 +54001,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-13b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56254,7 +54009,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56263,7 +54017,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-70b-chat": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 9e-07, @@ -56272,7 +54025,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56281,7 +54033,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v2-7b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56290,7 +54041,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 9e-07, @@ -56299,7 +54049,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct-hf": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 9e-07, @@ -56308,7 +54057,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-8b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56317,7 +54065,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3-8b-instruct-hf": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56326,7 +54073,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct-long": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56335,7 +54081,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56344,7 +54089,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct-1b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56353,7 +54097,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p1-nemotron-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56362,7 +54105,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56371,7 +54113,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56380,7 +54121,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56389,7 +54129,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llamaguard-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56398,7 +54137,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/llava-yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56407,7 +54145,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/minimax-m1-80k": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56416,7 +54153,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/minimax-m2": { - "max_tokens": 4096, "max_input_tokens": 196608, "max_output_tokens": 4096, "input_cost_per_token": 3e-07, @@ -56425,7 +54161,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-14b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 2e-07, @@ -56434,7 +54169,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-3b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 1e-07, @@ -56443,7 +54177,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/ministral-3-8b-instruct-2512": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 2e-07, @@ -56452,7 +54185,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56461,7 +54193,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-4k": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56470,7 +54201,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v0p2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56479,7 +54209,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v3": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56488,7 +54217,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-7b-v0p2": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56497,7 +54225,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-large-3-fp8": { - "max_tokens": 256000, "max_input_tokens": 256000, "max_output_tokens": 256000, "input_cost_per_token": 1.2e-06, @@ -56506,7 +54233,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-nemo-base-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -56515,7 +54241,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-nemo-instruct-2407": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -56524,7 +54249,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mistral-small-24b-instruct-2501": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56533,7 +54257,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x22b": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 1.2e-06, @@ -56542,7 +54265,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 1.2e-06, @@ -56551,7 +54273,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56560,7 +54281,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56569,7 +54289,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct-hf": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56578,7 +54297,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/mythomax-l2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56587,7 +54305,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nemotron-nano-v2-12b-vl": { - "max_tokens": 4096, "max_input_tokens": 131072, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -56596,7 +54313,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-capybara-7b-v1p9": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56605,7 +54321,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-2-mixtral-8x7b-dpo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 5e-07, @@ -56614,7 +54329,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-2-yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56623,7 +54337,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-13b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56632,7 +54345,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-70b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -56641,7 +54353,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-7b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -56650,7 +54361,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-12b-v2": { - "max_tokens": 131072, "max_input_tokens": 128000, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56659,7 +54369,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-9b-v2": { - "max_tokens": 131072, "max_input_tokens": 128000, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56668,7 +54377,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openchat-3p5-0106-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -56677,7 +54385,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openhermes-2-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56686,7 +54393,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openhermes-2p5-mistral-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56695,7 +54401,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/openorca-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56704,7 +54409,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-2-3b": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 1e-07, @@ -56713,7 +54417,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-3-mini-128k-instruct": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -56722,7 +54425,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phi-3-vision-128k-instruct": { - "max_tokens": 32064, "max_input_tokens": 32064, "max_output_tokens": 32064, "input_cost_per_token": 2e-07, @@ -56731,7 +54433,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-python-v1": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56740,7 +54441,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v1": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56749,7 +54449,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v2": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 9e-07, @@ -56758,7 +54457,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/playground-v2-1024px-aesthetic": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56767,7 +54465,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/playground-v2-5-1024px-aesthetic": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -56776,7 +54473,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/pythia-12b": { - "max_tokens": 2048, "max_input_tokens": 2048, "max_output_tokens": 2048, "input_cost_per_token": 2e-07, @@ -56785,7 +54481,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-qwq-32b-preview": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56794,7 +54489,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-v2p5-14b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56803,7 +54497,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen-v2p5-7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56812,7 +54505,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen1p5-72b-chat": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56821,7 +54513,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56830,7 +54521,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-2b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56839,7 +54529,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-72b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56848,7 +54537,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2-vl-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56857,7 +54545,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-0p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56866,7 +54553,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-14b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2e-07, @@ -56875,7 +54561,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-1p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56884,7 +54569,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56893,7 +54577,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-32b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56902,7 +54585,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-72b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -56911,7 +54593,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-72b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56920,7 +54601,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56929,7 +54609,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56938,7 +54617,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56947,7 +54625,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56956,7 +54633,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -56965,7 +54641,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56974,7 +54649,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -56983,7 +54657,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -56992,7 +54665,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-128k": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57001,7 +54673,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-32k-rope": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 9e-07, @@ -57010,7 +54681,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-64k": { - "max_tokens": 65536, "max_input_tokens": 65536, "max_output_tokens": 65536, "input_cost_per_token": 9e-07, @@ -57019,7 +54689,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -57028,7 +54697,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1e-07, @@ -57037,7 +54705,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57046,7 +54713,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b-instruct": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57055,7 +54721,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-math-72b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57064,7 +54729,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-32b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 9e-07, @@ -57073,7 +54737,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-3b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57082,7 +54745,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-72b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 9e-07, @@ -57091,7 +54753,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-7b-instruct": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57100,7 +54761,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-0p6b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -57109,7 +54769,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-14b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57118,7 +54777,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -57127,7 +54785,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1e-07, @@ -57136,7 +54793,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-131072": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1e-07, @@ -57145,7 +54801,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-40960": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 1e-07, @@ -57154,7 +54809,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 2.2e-07, @@ -57163,7 +54817,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57172,7 +54825,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57181,7 +54833,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.5e-07, @@ -57190,7 +54841,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 5e-07, @@ -57199,7 +54849,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-thinking-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 9e-07, @@ -57208,7 +54857,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57218,7 +54866,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/qwen3-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57227,7 +54874,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-4b-instruct-2507": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2e-07, @@ -57236,7 +54882,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57246,7 +54891,6 @@ "supports_reasoning": true }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-30b-a3b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57255,7 +54899,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-instruct-bf16": { - "max_tokens": 4096, "max_input_tokens": 262144, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57264,7 +54907,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-embedding-0p6b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 0.0, @@ -57273,7 +54915,6 @@ "mode": "embedding" }, "fireworks_ai/accounts/fireworks/models/qwen3-embedding-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57282,7 +54923,6 @@ "mode": "embedding" }, "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57291,7 +54931,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-thinking": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57300,7 +54939,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-0p6b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57309,7 +54947,6 @@ "mode": "rerank" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-4b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 0.0, @@ -57318,7 +54955,6 @@ "mode": "rerank" }, "fireworks_ai/accounts/fireworks/models/qwen3-reranker-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "max_output_tokens": 40960, "input_cost_per_token": 2e-07, @@ -57328,7 +54964,6 @@ "source": "https://api.fireworks.ai/v1/serverless/models" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57337,7 +54972,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.2e-07, @@ -57346,7 +54980,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-instruct": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57355,7 +54988,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -57364,7 +54996,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-32b-instruct": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57373,7 +55004,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/qwen3-vl-8b-instruct": { - "max_tokens": 4096, "max_input_tokens": 262144, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -57387,7 +55017,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -57398,7 +55027,6 @@ "supports_vision": true }, "fireworks_ai/accounts/fireworks/models/qwq-32b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-07, @@ -57407,7 +55035,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/rolm-ocr": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 2e-07, @@ -57416,7 +55043,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/snorkel-mistral-7b-pairrm-dpo": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57425,7 +55051,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/stable-diffusion-xl-1024-v1-0": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1.3e-10, @@ -57434,7 +55059,6 @@ "mode": "image_generation" }, "fireworks_ai/accounts/fireworks/models/stablecode-3b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 1e-07, @@ -57443,7 +55067,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder-16b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -57452,7 +55075,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder-7b": { - "max_tokens": 8192, "max_input_tokens": 8192, "max_output_tokens": 8192, "input_cost_per_token": 2e-07, @@ -57461,7 +55083,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-15b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -57470,7 +55091,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-3b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -57479,7 +55099,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/starcoder2-7b": { - "max_tokens": 16384, "max_input_tokens": 16384, "max_output_tokens": 16384, "input_cost_per_token": 2e-07, @@ -57488,7 +55107,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/toppy-m-7b": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57497,7 +55115,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57506,7 +55123,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b-200k-capybara": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 9e-07, @@ -57515,7 +55131,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-34b-chat": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 9e-07, @@ -57524,7 +55139,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/yi-6b": { - "max_tokens": 4096, "max_input_tokens": 4096, "max_output_tokens": 4096, "input_cost_per_token": 2e-07, @@ -57533,7 +55147,6 @@ "mode": "chat" }, "fireworks_ai/accounts/fireworks/models/zephyr-7b-beta": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 2e-07, @@ -57547,7 +55160,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57564,7 +55176,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57581,7 +55192,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -57596,7 +55206,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-06, "supports_function_calling": true, @@ -57608,7 +55217,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "supports_function_calling": true, @@ -57620,7 +55228,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 2.25e-06, "supports_function_calling": true @@ -57636,7 +55243,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "supports_function_calling": true, @@ -57647,7 +55253,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "supports_function_calling": true @@ -57663,7 +55268,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 5e-07, "supports_function_calling": true, @@ -57675,7 +55279,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.5e-06, "supports_reasoning": true, @@ -57688,7 +55291,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.5e-07, "supports_function_calling": true, @@ -57699,7 +55301,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 2e-07, "supports_vision": true, @@ -57717,7 +55318,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 9e-07, "supports_function_calling": true @@ -57729,7 +55329,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57746,7 +55345,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57762,7 +55360,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57779,7 +55376,6 @@ "output_cost_per_token": 3.3e-07, "max_input_tokens": 262144, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57796,7 +55392,6 @@ "output_cost_per_token": 1.38e-07, "max_input_tokens": 65536, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_vision": true, "supports_system_messages": true }, @@ -57807,7 +55402,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 262144, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57824,7 +55418,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57841,7 +55434,6 @@ "output_cost_per_token": 2e-08, "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_vision": true, "supports_system_messages": true }, @@ -57852,7 +55444,6 @@ "output_cost_per_token": 4.1e-07, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57867,7 +55458,6 @@ "output_cost_per_token": 3.95e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_vision": true, "supports_system_messages": true, "supports_reasoning": true, @@ -57881,7 +55471,6 @@ "output_cost_per_token": 9e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57899,7 +55488,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57916,7 +55504,6 @@ "output_cost_per_token": 1.2e-06, "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57932,7 +55519,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57947,7 +55533,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57962,7 +55547,6 @@ "output_cost_per_token": 3e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -57975,7 +55559,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -57992,7 +55575,6 @@ "output_cost_per_token": 1.5e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58007,7 +55589,6 @@ "output_cost_per_token": 8.45e-06, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58021,7 +55602,6 @@ "output_cost_per_token": 6e-07, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -58034,7 +55614,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58051,7 +55630,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 262144, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58065,7 +55643,6 @@ "output_cost_per_token": 1.55e-06, "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58079,7 +55656,6 @@ "output_cost_per_token": 2.7e-07, "max_input_tokens": 160000, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58093,7 +55669,6 @@ "output_cost_per_token": 2.5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58109,7 +55684,6 @@ "output_cost_per_token": 2.3e-06, "max_input_tokens": 131072, "max_output_tokens": 100352, - "max_tokens": 100352, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58123,7 +55697,6 @@ "output_cost_per_token": 1.12e-06, "max_input_tokens": 163840, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58139,7 +55712,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 131072, "max_output_tokens": 98304, - "max_tokens": 98304, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58155,7 +55727,6 @@ "output_cost_per_token": 3e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58169,7 +55740,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 16384, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_system_messages": true, "supports_response_schema": true }, @@ -58180,7 +55750,6 @@ "output_cost_per_token": 1e-07, "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_vision": true, "supports_system_messages": true, @@ -58193,7 +55762,6 @@ "output_cost_per_token": 1.8e-06, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58211,7 +55779,6 @@ "output_cost_per_token": 1.5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_tool_choice": true, "supports_vision": false, "supports_system_messages": true, @@ -58225,7 +55792,6 @@ "output_cost_per_token": 5.8e-07, "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58239,7 +55805,6 @@ "output_cost_per_token": 1.5e-07, "max_input_tokens": 32768, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58252,7 +55817,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 12288, "max_output_tokens": 12288, - "max_tokens": 12288, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58266,7 +55830,6 @@ "output_cost_per_token": 4e-07, "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58280,7 +55843,6 @@ "output_cost_per_token": 1.7e-07, "max_input_tokens": 60288, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58292,7 +55854,6 @@ "output_cost_per_token": 2.2e-06, "max_input_tokens": 1000000, "max_output_tokens": 40000, - "max_tokens": 40000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58307,7 +55868,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 163840, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58324,7 +55884,6 @@ "output_cost_per_token": 3e-07, "max_input_tokens": 64000, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58337,7 +55896,6 @@ "output_cost_per_token": 4e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true, "supports_response_schema": true }, @@ -58348,7 +55906,6 @@ "output_cost_per_token": 6.2e-07, "max_input_tokens": 65535, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_system_messages": true, "supports_response_schema": true }, @@ -58359,7 +55916,6 @@ "output_cost_per_token": 9e-08, "max_input_tokens": 128000, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58370,7 +55926,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true, @@ -58383,7 +55938,6 @@ "output_cost_per_token": 7.4e-07, "max_input_tokens": 8192, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58395,7 +55949,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true, "supports_response_schema": true @@ -58407,7 +55960,6 @@ "output_cost_per_token": 8.5e-07, "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_vision": true, "supports_system_messages": true, "supports_response_schema": true @@ -58419,7 +55971,6 @@ "output_cost_per_token": 5.9e-07, "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_vision": true, "supports_system_messages": true }, @@ -58430,7 +55981,6 @@ "output_cost_per_token": 1.4e-07, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58442,7 +55992,6 @@ "output_cost_per_token": 8e-07, "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_vision": true, "supports_system_messages": true }, @@ -58453,7 +56002,6 @@ "output_cost_per_token": 1.48e-06, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58466,7 +56014,6 @@ "output_cost_per_token": 2.8e-07, "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_system_messages": true, "supports_reasoning": true }, @@ -58477,7 +56024,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58489,7 +56035,6 @@ "output_cost_per_token": 7e-08, "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-424b-a47b": { @@ -58499,7 +56044,6 @@ "output_cost_per_token": 1.25e-06, "max_input_tokens": 123000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_vision": true, "supports_system_messages": true, "supports_reasoning": true @@ -58511,7 +56055,6 @@ "output_cost_per_token": 1.1e-06, "max_input_tokens": 123000, "max_output_tokens": 12000, - "max_tokens": 12000, "supports_tool_choice": true, "supports_system_messages": true, "supports_response_schema": true @@ -58523,7 +56066,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 160000, "max_output_tokens": 160000, - "max_tokens": 160000, "supports_system_messages": true }, "novita/qwen/qwen3-32b-fp8": { @@ -58533,7 +56075,6 @@ "output_cost_per_token": 4.5e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58544,7 +56085,6 @@ "output_cost_per_token": 4.5e-07, "max_input_tokens": 40960, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true, "supports_function_calling": true, @@ -58557,7 +56097,6 @@ "output_cost_per_token": 2e-07, "max_input_tokens": 98304, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_vision": true, "supports_system_messages": true }, @@ -58568,7 +56107,6 @@ "output_cost_per_token": 1.3e-06, "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58581,7 +56119,6 @@ "output_cost_per_token": 2.5e-06, "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58596,7 +56133,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 8192, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58609,7 +56145,6 @@ "output_cost_per_token": 9e-08, "max_input_tokens": 4096, "max_output_tokens": 3200, - "max_tokens": 3200, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-28b-a3b-thinking": { @@ -58619,7 +56154,6 @@ "output_cost_per_token": 3.9e-07, "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58635,7 +56169,6 @@ "output_cost_per_token": 5e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58650,7 +56183,6 @@ "output_cost_per_token": 8.5e-07, "max_input_tokens": 131072, "max_output_tokens": 98304, - "max_tokens": 98304, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58666,7 +56198,6 @@ "output_cost_per_token": 7e-07, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58681,7 +56212,6 @@ "output_cost_per_token": 1e-06, "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58696,7 +56226,6 @@ "output_cost_per_token": 9.7e-07, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58713,7 +56242,6 @@ "output_cost_per_token": 9.7e-07, "max_input_tokens": 65536, "max_output_tokens": 16384, - "max_tokens": 16384, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58730,7 +56258,6 @@ "output_cost_per_token": 7.5e-07, "max_input_tokens": 16384, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true }, "novita/baidu/ernie-4.5-vl-28b-a3b": { @@ -58740,7 +56267,6 @@ "output_cost_per_token": 5.6e-07, "max_input_tokens": 30000, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58755,7 +56281,6 @@ "output_cost_per_token": 2.8e-07, "max_input_tokens": 120000, "max_output_tokens": 8000, - "max_tokens": 8000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58768,7 +56293,6 @@ "output_cost_per_token": 1.38e-07, "max_input_tokens": 128000, "max_output_tokens": 20000, - "max_tokens": 20000, "supports_system_messages": true, "supports_reasoning": true }, @@ -58779,7 +56303,6 @@ "output_cost_per_token": 3e-08, "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_system_messages": true, "supports_reasoning": true, "supports_function_calling": true, @@ -58792,7 +56315,6 @@ "output_cost_per_token": 7e-08, "max_input_tokens": 32000, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58806,7 +56328,6 @@ "output_cost_per_token": 5e-08, "max_input_tokens": 32768, "max_output_tokens": 32000, - "max_tokens": 32000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58819,7 +56340,6 @@ "output_cost_per_token": 1.48e-06, "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_tool_choice": true, @@ -58831,8 +56351,7 @@ "input_cost_per_token": 7e-08, "output_cost_per_token": 0, "max_input_tokens": 32768, - "max_output_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "novita/qwen/qwen3-embedding-8b": { "litellm_provider": "novita", @@ -58840,8 +56359,7 @@ "input_cost_per_token": 7e-08, "output_cost_per_token": 0, "max_input_tokens": 32768, - "max_output_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "novita/baai/bge-m3": { "litellm_provider": "novita", @@ -58849,8 +56367,7 @@ "input_cost_per_token": 1e-08, "output_cost_per_token": 1e-08, "max_input_tokens": 8192, - "max_output_tokens": 96000, - "max_tokens": 96000 + "max_output_tokens": 96000 }, "novita/qwen/qwen3-reranker-8b": { "litellm_provider": "novita", @@ -58858,8 +56375,7 @@ "input_cost_per_token": 5e-08, "output_cost_per_token": 5e-08, "max_input_tokens": 32768, - "max_output_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "novita/baai/bge-reranker-v2-m3": { "litellm_provider": "novita", @@ -58867,11 +56383,9 @@ "input_cost_per_token": 1e-08, "output_cost_per_token": 1e-08, "max_input_tokens": 8000, - "max_output_tokens": 8000, - "max_tokens": 8000 + "max_output_tokens": 8000 }, "llamagate/llama-3.1-8b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -58882,7 +56396,6 @@ "supports_response_schema": true }, "llamagate/llama-3.2-3b": { - "max_tokens": 8192, "max_input_tokens": 131072, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -58893,7 +56406,6 @@ "supports_response_schema": true }, "llamagate/mistral-7b-v0.3": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 1e-07, @@ -58904,7 +56416,6 @@ "supports_response_schema": true }, "llamagate/qwen3-8b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 4e-08, @@ -58915,7 +56426,6 @@ "supports_response_schema": true }, "llamagate/dolphin3-8b": { - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "input_cost_per_token": 8e-08, @@ -58926,7 +56436,6 @@ "supports_response_schema": true }, "llamagate/deepseek-r1-8b": { - "max_tokens": 16384, "max_input_tokens": 65536, "max_output_tokens": 16384, "input_cost_per_token": 1e-07, @@ -58938,7 +56447,6 @@ "supports_reasoning": true }, "llamagate/deepseek-r1-7b-qwen": { - "max_tokens": 16384, "max_input_tokens": 131072, "max_output_tokens": 16384, "input_cost_per_token": 8e-08, @@ -58950,7 +56458,6 @@ "supports_reasoning": true }, "llamagate/openthinker-7b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 8e-08, @@ -58962,7 +56469,6 @@ "supports_reasoning": true }, "llamagate/qwen2.5-coder-7b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 6e-08, @@ -58973,7 +56479,6 @@ "supports_response_schema": true }, "llamagate/deepseek-coder-6.7b": { - "max_tokens": 4096, "max_input_tokens": 16384, "max_output_tokens": 4096, "input_cost_per_token": 6e-08, @@ -58984,7 +56489,6 @@ "supports_response_schema": true }, "llamagate/codellama-7b": { - "max_tokens": 4096, "max_input_tokens": 16384, "max_output_tokens": 4096, "input_cost_per_token": 6e-08, @@ -58995,7 +56499,6 @@ "supports_response_schema": true }, "llamagate/qwen3-vl-8b": { - "max_tokens": 8192, "max_input_tokens": 32768, "max_output_tokens": 8192, "input_cost_per_token": 1.5e-07, @@ -59007,7 +56510,6 @@ "supports_vision": true }, "llamagate/llava-7b": { - "max_tokens": 2048, "max_input_tokens": 4096, "max_output_tokens": 2048, "input_cost_per_token": 1e-07, @@ -59018,7 +56520,6 @@ "supports_vision": true }, "llamagate/gemma3-4b": { - "max_tokens": 8192, "max_input_tokens": 128000, "max_output_tokens": 8192, "input_cost_per_token": 3e-08, @@ -59030,7 +56531,6 @@ "supports_vision": true }, "llamagate/nomic-embed-text": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 2e-08, "output_cost_per_token": 0, @@ -59038,7 +56538,6 @@ "mode": "embedding" }, "llamagate/qwen3-embedding-8b": { - "max_tokens": 40960, "max_input_tokens": 40960, "input_cost_per_token": 2e-08, "output_cost_per_token": 0, @@ -59046,7 +56545,6 @@ "mode": "embedding" }, "libertai/hermes-3-8b-tee": { - "max_tokens": 16000, "max_input_tokens": 16000, "max_output_tokens": 16000, "input_cost_per_token": 1.5e-07, @@ -59060,7 +56558,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/gemma-4-31b-it": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59074,7 +56571,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/gemma-4-31b-it-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59089,7 +56585,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-27b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59103,7 +56598,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-27b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59118,7 +56612,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-35b-a3b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59132,7 +56625,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.6-35b-a3b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 1.5e-07, @@ -59147,7 +56639,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.5-122b-a10b": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.5e-07, @@ -59161,7 +56652,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/qwen3.5-122b-a10b-thinking": { - "max_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 262144, "input_cost_per_token": 2.5e-07, @@ -59176,7 +56666,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/deepseek-v4-flash": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 2.5e-07, @@ -59190,7 +56679,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/deepseek-v4-flash-thinking": { - "max_tokens": 200000, "max_input_tokens": 200000, "max_output_tokens": 200000, "input_cost_per_token": 2.5e-07, @@ -59205,7 +56693,6 @@ "source": "https://docs.libertai.io/apis/text/" }, "libertai/bge-m3": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 1e-08, "output_cost_per_token": 0.0, @@ -59221,7 +56708,6 @@ "litellm_provider": "sarvam", "max_input_tokens": 8192, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 0, "supports_reasoning": true @@ -59297,7 +56783,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -59325,7 +56810,6 @@ "litellm_provider": "openai", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -59357,7 +56841,6 @@ "litellm_provider": "openai", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2.4e-06, @@ -59467,7 +56950,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_token": 0.0, @@ -59488,7 +56970,6 @@ "litellm_provider": "gemini", "max_input_tokens": 32768, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.039, "output_cost_per_token": 0.0, @@ -59511,7 +56992,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59537,7 +57017,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59564,7 +57043,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59599,7 +57077,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59634,7 +57111,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59668,7 +57144,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59701,7 +57176,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59729,7 +57203,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59758,7 +57231,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2e-06, @@ -59796,7 +57268,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -59833,7 +57304,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 2e-05, "output_cost_per_token": 2e-05, @@ -59860,7 +57330,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 9e-06, "output_cost_per_token": 9e-06, @@ -59885,7 +57354,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 6e-06, "output_cost_per_token": 6e-06, @@ -59905,7 +57373,6 @@ "litellm_provider": "gemini", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 1e-05, "output_cost_per_token": 1e-05, @@ -59925,7 +57392,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 3.75e-06, "output_cost_per_token": 3.75e-06, @@ -59984,7 +57450,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -60044,7 +57509,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -60106,7 +57570,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_200k_tokens": 1.8e-05, @@ -60167,7 +57630,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65535, - "max_tokens": 65535, "mode": "chat", "output_cost_per_reasoning_token": 2.5e-06, "output_cost_per_token": 2.5e-06, @@ -60218,7 +57680,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -60257,7 +57718,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -60299,7 +57759,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -60325,7 +57784,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 1.5e-07, "output_cost_per_token_priority": 5.25e-07, @@ -60351,7 +57809,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -60375,7 +57832,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1e-07, "output_cost_per_token_priority": 3.5e-07, @@ -60405,7 +57861,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60449,7 +57904,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60484,7 +57938,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-cyber.html", "use_openai_responses_path": true, @@ -60520,7 +57973,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60560,7 +58012,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -60601,7 +58052,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html", "supported_modalities": [ @@ -60638,7 +58088,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html", "supported_modalities": [ @@ -60675,7 +58124,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html", "supported_modalities": [ @@ -60712,7 +58160,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html", "supported_modalities": [ @@ -60749,7 +58196,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html", "supported_modalities": [ @@ -60783,7 +58229,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-54.html", "supported_modalities": [ @@ -60815,7 +58260,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-54.html", "supported_modalities": [ @@ -60847,7 +58291,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-55.html", "supported_modalities": [ @@ -60879,7 +58322,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-55.html", "supported_modalities": [ @@ -60914,7 +58356,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html", "supported_modalities": [ @@ -60955,7 +58396,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token_above_272k_tokens_ultrafast": 0.000495, "output_cost_per_token_ultrafast": 0.00033, @@ -60996,7 +58436,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61035,7 +58474,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61082,7 +58520,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_ultrafast": 0.000495, "output_cost_per_token_ultrafast": 0.00033, @@ -61123,7 +58560,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61162,7 +58598,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61207,7 +58642,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_above_272k_tokens_ultrafast": 0.00045, "output_cost_per_token_ultrafast": 0.0003, @@ -61247,7 +58681,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61282,7 +58715,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61320,7 +58752,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61355,7 +58786,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supported_modalities": [ "text", @@ -61395,7 +58825,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61435,7 +58864,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -61470,7 +58898,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token_batches": 2e-07, "output_cost_per_token_priority": 7e-07, @@ -61496,7 +58923,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token_batches": 2e-07, "output_cost_per_token_priority": 7e-07, @@ -61522,7 +58948,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token_batches": 4e-08, "output_cost_per_token_priority": 1.4e-07, @@ -61552,7 +58977,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1048576, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, "output_cost_per_token_flex": 1.25e-06, @@ -61580,7 +59004,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3.3e-06, "output_cost_per_token_priority": 1.155e-05, @@ -61605,7 +59028,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -61636,7 +59058,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.2e-05, "search_context_cost_per_query": { @@ -61674,7 +59095,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -61722,7 +59142,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3.3e-06, "output_cost_per_token_priority": 1.155e-05, @@ -61750,7 +59169,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token_flex": 3e-06, "output_cost_per_token_priority": 1.05e-05, @@ -61765,7 +59183,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61805,7 +59222,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4.3125e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61821,7 +59237,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 1024000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4.3125e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61839,7 +59254,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 1024000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 3.88126e-07, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61856,7 +59270,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.15625e-06, "source": "https://www.volcengine.com/docs/82379/1544106", @@ -61870,7 +59283,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61908,7 +59320,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -61946,7 +59357,6 @@ "litellm_provider": "volcengine", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "source": "https://docs.volcengine.com/docs/82379/1330310", "supports_function_calling": true, @@ -62041,7 +59451,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -62059,7 +59468,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_function_calling": true, "supports_reasoning": true, @@ -62080,7 +59488,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -62110,7 +59517,6 @@ "supports_tool_search": true, "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/about-aws/whats-new/2025/10/claude-4-5-haiku-anthropic-amazon-bedrock", @@ -62132,7 +59538,6 @@ } }, "snowflake/claude-sonnet-4-5": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62150,7 +59555,6 @@ "snowflake/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "supports_legacy_thinking": true, - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62166,7 +59570,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-4-sonnet": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62182,7 +59585,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-4-opus": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 5e-06, @@ -62199,7 +59601,6 @@ "prompt_cache_min_tokens": 1024 }, "snowflake/claude-haiku-4-5": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 1e-06, @@ -62215,7 +59616,6 @@ "prompt_cache_min_tokens": 4096 }, "snowflake/claude-3-7-sonnet": { - "max_tokens": 16384, "max_input_tokens": 200000, "max_output_tokens": 16384, "input_cost_per_token": 3e-06, @@ -62231,7 +59631,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-4.1": { - "max_tokens": 16384, "max_input_tokens": 300000, "max_output_tokens": 16384, "input_cost_per_token": 2e-06, @@ -62246,7 +59645,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5": { - "max_tokens": 16384, "max_input_tokens": 300000, "max_output_tokens": 16384, "input_cost_per_token": 1.25e-06, @@ -62262,7 +59660,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5-mini": { - "max_tokens": 16384, "max_input_tokens": 1000000, "max_output_tokens": 16384, "input_cost_per_token": 3e-07, @@ -62274,7 +59671,6 @@ "supports_response_schema": true }, "snowflake/openai-gpt-5-nano": { - "max_tokens": 16384, "max_input_tokens": 5000000, "max_output_tokens": 16384, "input_cost_per_token": 1.5e-07, @@ -62286,7 +59682,6 @@ "supports_response_schema": true }, "snowflake/llama4-maverick": { - "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, "input_cost_per_token": 2.4e-07, @@ -62297,7 +59692,6 @@ "supports_system_messages": true }, "snowflake/snowflake-arctic-embed-l-v2.0": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 7e-08, "output_cost_per_token": 0.0, @@ -62305,7 +59699,6 @@ "mode": "embedding" }, "snowflake/snowflake-arctic-embed-m-v2.0": { - "max_tokens": 8192, "max_input_tokens": 8192, "input_cost_per_token": 7e-08, "output_cost_per_token": 0.0, @@ -62315,7 +59708,7 @@ "soniox/stt-async-v4": { "litellm_provider": "soniox", "max_output_tokens": 8000, - "max_tokens": 8000, + "max_input_tokens": 8000, "input_cost_per_second": 0.0, "output_cost_per_second": 2.77778e-05, "mode": "audio_transcription", @@ -62328,7 +59721,7 @@ "soniox/stt-async-v5": { "litellm_provider": "soniox", "max_output_tokens": 8000, - "max_tokens": 8000, + "max_input_tokens": 8000, "input_cost_per_second": 0.0, "output_cost_per_second": 2.77778e-05, "mode": "audio_transcription", @@ -62505,7 +59898,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62559,7 +59951,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62613,7 +60004,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62667,7 +60057,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -62721,7 +60110,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62775,7 +60163,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62829,7 +60216,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -62883,7 +60269,6 @@ "litellm_provider": "deepseek", "max_input_tokens": 1000000, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -62937,7 +60322,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -62963,7 +60347,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -62989,7 +60372,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 2.2e-08, @@ -63044,7 +60426,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "off_peak_pricing": { "cache_read_input_token_cost": 3e-09, @@ -63099,7 +60480,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63124,7 +60504,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63149,7 +60528,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63173,7 +60551,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63197,7 +60574,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63221,7 +60597,6 @@ "litellm_provider": "tencent", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63245,7 +60620,6 @@ "litellm_provider": "tencent", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63269,7 +60643,6 @@ "litellm_provider": "tencent", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://www.tencentcloud.com/document/product/1300/78937", @@ -63336,7 +60709,6 @@ "source": "https://docs.devin.ai/desktop/models" }, "pinstripes/ps/glm-4.5-air": { - "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, "input_cost_per_token": 1.25e-07, @@ -63349,7 +60721,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3.6-35b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -63362,7 +60733,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 9e-08, @@ -63375,7 +60745,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/qwen3-coder-30b-a3b": { - "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, "input_cost_per_token": 3e-07, @@ -63388,7 +60757,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/deepseek-v4-flash": { - "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, "input_cost_per_token": 1e-07, @@ -63401,7 +60769,6 @@ "source": "https://pinstripes.io/" }, "pinstripes/ps/minimax-m2.7": { - "max_tokens": 1000192, "max_input_tokens": 1000192, "max_output_tokens": 1000192, "input_cost_per_token": 2.55e-07, @@ -63418,7 +60785,6 @@ "litellm_provider": "darkbloom", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.65e-07, "source": "https://www.darkbloom.dev/", @@ -63435,7 +60801,6 @@ "litellm_provider": "darkbloom", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7e-08, "source": "https://www.darkbloom.dev/", @@ -63455,7 +60820,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -63482,7 +60846,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -63511,7 +60874,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.x.ai/v1/language-models", @@ -63584,7 +60946,6 @@ "litellm_provider": "openai", "max_input_tokens": 16000, "max_output_tokens": 2000, - "max_tokens": 2000, "mode": "realtime", "source": "https://developers.openai.com/api/docs/pricing", "supported_modalities": [ @@ -63609,7 +60970,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -63653,7 +61013,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -63696,7 +61055,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "prompt_cache_min_tokens": 512, @@ -63732,7 +61090,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 5e-06, "search_context_cost_per_query": { @@ -63767,7 +61124,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -63788,7 +61144,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/models/model-cards/leanstral-1-5", @@ -63878,7 +61233,6 @@ "mode": "chat", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_vision": true, @@ -63977,7 +61331,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 2.1e-05, "output_cost_per_token": 2.1e-05, @@ -64006,7 +61359,6 @@ "litellm_provider": "gemini", "max_input_tokens": 98304, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "audio_transcription", "output_cost_per_token": 1.2e-05, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -64031,7 +61383,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "audio_transcription", "output_cost_per_token": 2.1e-05, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -64112,7 +61463,6 @@ "input_cost_per_token": 8e-09, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64122,7 +61472,6 @@ "input_cost_per_token": 5e-08, "litellm_provider": "perplexity", "max_input_tokens": 32768, - "max_tokens": 32768, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2560, @@ -64132,7 +61481,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64142,7 +61490,6 @@ "input_cost_per_token": 6e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64152,7 +61499,6 @@ "input_cost_per_token": 2e-08, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64162,7 +61508,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64172,7 +61517,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 120000, - "max_tokens": 120000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64182,7 +61526,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", "max_input_tokens": 32000, - "max_tokens": 32000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 1024, @@ -64198,7 +61541,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64217,7 +61559,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -64237,7 +61578,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64257,7 +61597,7 @@ "input_cost_per_token_priority": 2.75e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64275,7 +61615,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_priority": 1.875e-05, @@ -64300,7 +61639,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64319,7 +61657,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -64339,7 +61676,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64359,7 +61695,7 @@ "input_cost_per_token_priority": 2.75e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-07, "output_cost_per_token_priority": 8.25e-07, @@ -64376,7 +61712,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64393,7 +61728,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64411,7 +61745,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_priority": 1.875e-05, @@ -64433,7 +61766,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "reasoning_effort_levels": [ @@ -64456,7 +61788,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "output_cost_per_token_priority": 2.8125e-05, @@ -64498,7 +61829,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64518,7 +61848,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_priority": 2.5e-07, @@ -64538,7 +61867,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.4e-06, "output_cost_per_token_priority": 3e-06, @@ -64558,7 +61886,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 1.5e-06, "output_cost_per_token_priority": 2.25e-06, @@ -64593,7 +61920,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "output_cost_per_token_priority": 2.5e-07, @@ -64613,7 +61939,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.4e-06, "output_cost_per_token_priority": 3e-06, @@ -64648,7 +61973,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -64665,7 +61989,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -64681,7 +62004,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "reasoning_effort_levels": [ @@ -64704,7 +62026,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.25e-05, "output_cost_per_token_priority": 2.8125e-05, @@ -64726,7 +62047,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -64743,7 +62063,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://novita.ai/pricing", @@ -64760,7 +62079,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://novita.ai/pricing", @@ -64777,7 +62095,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 5.8e-07, "source": "https://novita.ai/pricing", @@ -64794,7 +62111,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://novita.ai/pricing", @@ -64811,7 +62127,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://novita.ai/pricing", @@ -64828,7 +62143,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -64845,7 +62159,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.32e-06, "source": "https://novita.ai/pricing", @@ -64862,7 +62175,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://novita.ai/pricing", @@ -64878,7 +62190,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -64895,7 +62206,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://novita.ai/pricing", @@ -64912,7 +62222,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -64929,7 +62238,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://novita.ai/pricing", @@ -64945,7 +62253,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://novita.ai/pricing", @@ -64962,7 +62269,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://novita.ai/pricing", @@ -64978,7 +62284,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.6e-06, "source": "https://novita.ai/pricing", @@ -64994,7 +62299,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 1.15e-06, "source": "https://novita.ai/pricing", @@ -65010,7 +62314,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 2.0000000000000002e-07, "source": "https://novita.ai/pricing", @@ -65026,7 +62329,6 @@ "litellm_provider": "novita", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.13e-06, "source": "https://novita.ai/pricing", @@ -65042,7 +62344,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.3600000000000004e-07, "source": "https://novita.ai/pricing", @@ -65059,7 +62360,6 @@ "litellm_provider": "novita", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://novita.ai/pricing", @@ -65076,7 +62376,6 @@ "litellm_provider": "novita", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.044e-06, "source": "https://novita.ai/pricing", @@ -65092,7 +62391,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.6000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65108,7 +62406,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.4e-06, "source": "https://novita.ai/pricing", @@ -65125,7 +62422,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://novita.ai/pricing", @@ -65142,7 +62438,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65159,7 +62454,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65175,7 +62469,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65190,7 +62483,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65206,7 +62498,6 @@ "litellm_provider": "novita", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65223,7 +62514,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -65240,7 +62530,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://novita.ai/pricing", @@ -65256,7 +62545,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://novita.ai/pricing", @@ -65271,7 +62559,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65286,7 +62573,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://novita.ai/pricing", @@ -65301,7 +62587,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.6000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65317,7 +62602,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://novita.ai/pricing", @@ -65334,7 +62618,6 @@ "litellm_provider": "novita", "max_input_tokens": 202800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.2000000000000003e-06, "source": "https://novita.ai/pricing", @@ -65350,7 +62633,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://novita.ai/pricing", @@ -65364,7 +62646,6 @@ "litellm_provider": "novita", "max_input_tokens": 8192, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 3e-08, "source": "https://novita.ai/pricing", @@ -65376,7 +62657,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://novita.ai/pricing", @@ -65393,7 +62673,6 @@ "litellm_provider": "novita", "max_input_tokens": 204800, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65410,7 +62689,6 @@ "litellm_provider": "novita", "max_input_tokens": 200000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.0000000000000003e-07, "source": "https://novita.ai/pricing", @@ -65426,7 +62704,6 @@ "litellm_provider": "novita", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.4850000000000002e-06, "source": "https://novita.ai/pricing", @@ -65441,7 +62718,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 8.900000000000001e-07, "source": "https://api.novita.ai/v3/openai/models", @@ -65454,7 +62730,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65468,7 +62743,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 8.900000000000001e-07, "source": "https://api.novita.ai/v3/openai/models", @@ -65481,7 +62755,6 @@ "litellm_provider": "novita", "max_input_tokens": 64000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65495,7 +62768,6 @@ "litellm_provider": "novita", "max_input_tokens": 32000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 1.66e-06, "source": "https://api.novita.ai/v3/openai/models", @@ -65509,7 +62781,6 @@ "litellm_provider": "novita", "max_input_tokens": 131000, "max_output_tokens": 32000, - "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 2e-08, "source": "https://api.novita.ai/v3/openai/models", @@ -65518,7 +62789,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Flash": { "supports_reasoning": true, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1049000, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, @@ -65532,7 +62803,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Flash-0731": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1.3e-07, "output_cost_per_token": 2.8e-07, @@ -65545,7 +62816,7 @@ }, "wandb/deepseek-ai/DeepSeek-V4-Pro": { "supports_reasoning": true, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1049000, "input_cost_per_token": 1.15e-06, "output_cost_per_token": 2.55e-06, @@ -65559,7 +62830,7 @@ }, "wandb/google/gemma-4-31B-it": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1e-07, "output_cost_per_token": 3.4e-07, @@ -65570,7 +62841,7 @@ }, "wandb/ibm-granite/granite-4.1-8b": { "deprecation_date": "2026-10-05", - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131000, "input_cost_per_token": 5e-08, "output_cost_per_token": 1e-07, @@ -65581,7 +62852,7 @@ }, "wandb/JetBrains/Mellum2-12B-A2.5B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131000, "input_cost_per_token": 5e-08, "output_cost_per_token": 1e-07, @@ -65592,7 +62863,7 @@ }, "wandb/meta-llama/Llama-3.1-70B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 128000, + "max_output_tokens": 128000, "max_input_tokens": 131000, "input_cost_per_token": 8e-07, "output_cost_per_token": 8e-07, @@ -65603,7 +62874,7 @@ }, "wandb/MiniMaxAI/MiniMax-M3": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.3e-07, "output_cost_per_token": 9.6e-07, @@ -65616,7 +62887,7 @@ }, "wandb/moonshotai/Kimi-K2.7-Code": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 7.1e-07, "output_cost_per_token": 3.5e-06, @@ -65629,7 +62900,7 @@ }, "wandb/moonshotai/Kimi-K2.6": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 6.5e-07, "output_cost_per_token": 3.41e-06, @@ -65642,7 +62913,7 @@ }, "wandb/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 7e-08, "output_cost_per_token": 2e-07, @@ -65655,7 +62926,7 @@ }, "wandb/nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 5e-07, "output_cost_per_token": 2.15e-06, @@ -65668,7 +62939,7 @@ }, "wandb/OpenPipe/Qwen3-14B-Instruct": { "deprecation_date": "2026-10-05", - "max_tokens": 32768, + "max_output_tokens": 32768, "max_input_tokens": 32800, "input_cost_per_token": 5e-08, "output_cost_per_token": 2.2e-07, @@ -65679,7 +62950,7 @@ }, "wandb/Qwen/Qwen3.8-27B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 4e-07, "output_cost_per_token": 3e-06, @@ -65692,7 +62963,7 @@ }, "wandb/Qwen/Qwen3.6-35B-A3B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.25e-06, @@ -65703,7 +62974,7 @@ }, "wandb/Qwen/Qwen3.6-27B": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 6e-07, "output_cost_per_token": 3.6e-06, @@ -65718,7 +62989,7 @@ "wandb/Qwen/Qwen3.5-35B-A3B": { "deprecation_date": "2026-10-05", "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.25e-06, @@ -65729,7 +63000,7 @@ }, "wandb/Qwen/Qwen3-30B-A3B-Instruct-2507": { "deprecation_date": "2026-10-05", - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262000, "input_cost_per_token": 1e-07, "output_cost_per_token": 3e-07, @@ -65762,7 +63033,7 @@ }, "wandb/zai-org/GLM-5.2": { "supports_reasoning": true, - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 1049000, "input_cost_per_token": 7.6e-07, "output_cost_per_token": 2.42e-06, @@ -65774,7 +63045,7 @@ "source": "https://wandb.ai/site/pricing/tokens/" }, "deepinfra/openai/gpt-oss-120b-Turbo": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, @@ -65788,7 +63059,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M2.7": { - "max_tokens": 196608, + "max_output_tokens": 196608, "max_input_tokens": 196608, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1e-06, @@ -65804,7 +63075,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4e-07, "output_cost_per_token": 3e-06, @@ -65820,7 +63091,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it-Ultra": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2.7e-07, "output_cost_per_token": 7.6e-07, @@ -65834,7 +63105,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.5": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 2.25e-06, @@ -65850,7 +63121,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.7-Flash": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 6e-08, "output_cost_per_token": 4e-07, @@ -65866,7 +63137,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.6": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 5e-07, "output_cost_per_token": 2e-06, @@ -65885,7 +63156,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 1024, @@ -65899,7 +63170,7 @@ "supports_vision": true }, "deepinfra/anthropic/claude-sonnet-4-6": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 3e-06, "output_cost_per_token": 1.5e-05, @@ -65915,7 +63186,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.5-flash": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 1.5e-06, "output_cost_per_token": 9e-06, @@ -65930,7 +63201,7 @@ "supports_audio_input": true }, "deepinfra/XiaomiMiMo/MiMo-V2.5": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4e-07, "output_cost_per_token": 2e-06, @@ -65946,7 +63217,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.2e-06, "output_cost_per_token": 6e-06, @@ -65961,7 +63232,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it-turbo": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 9e-08, "output_cost_per_token": 3.4e-07, @@ -65976,7 +63247,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/thinkingmachines/Inkling-Small": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 1.2e-06, @@ -65992,7 +63263,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/meta-models/Muse-Glimmer-30B": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, @@ -66008,7 +63279,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-Max-Thinking": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.2e-06, "output_cost_per_token": 6e-06, @@ -66023,7 +63294,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-VL-235B-A22B-Instruct": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-07, "output_cost_per_token": 8.8e-07, @@ -66038,7 +63309,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3-VL-30B-A3B-Instruct": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, @@ -66051,7 +63322,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2.6e-07, "output_cost_per_token": 2.6e-06, @@ -66065,7 +63336,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.6-35B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1e-07, "output_cost_per_token": 9.5e-07, @@ -66079,7 +63350,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/Nemotron-Content-Safety-3.5": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-07, "output_cost_per_token": 2e-07, @@ -66092,7 +63363,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 512, @@ -66106,7 +63377,7 @@ "supports_vision": true }, "deepinfra/thinkingmachines/Inkling": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 9.5e-07, "output_cost_per_token": 4.05e-06, @@ -66122,7 +63393,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.6": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 3.5e-06, @@ -66138,7 +63409,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Pro-0813": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1.3e-06, "output_cost_per_token": 2.6e-06, @@ -66153,7 +63424,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.7-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 2.5e-06, "output_cost_per_token": 7.5e-06, @@ -66168,7 +63439,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-mini": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1e-07, "output_cost_per_token": 4e-07, @@ -66184,7 +63455,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-2.4T-A95B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -66200,7 +63471,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M3": { - "max_tokens": 524288, + "max_output_tokens": 524288, "max_input_tokens": 524288, "input_cost_per_token": 2.8e-07, "output_cost_per_token": 1.1e-06, @@ -66216,7 +63487,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.1-flash-lite": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 1.5e-06, @@ -66231,7 +63502,7 @@ "supports_audio_input": true }, "deepinfra/google/gemini-3.7-flash": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 3.75e-06, @@ -66247,7 +63518,7 @@ "supports_audio_input": true }, "deepinfra/inclusionAI/Ling-3.0-flash": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 6e-08, "output_cost_per_token": 1.8e-07, @@ -66263,7 +63534,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/stepfun-ai/Step-3.7-Flash": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2e-07, "output_cost_per_token": 1.15e-06, @@ -66277,7 +63548,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-35B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 1e-06, @@ -66293,7 +63564,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-1.8": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 2.5e-07, "output_cost_per_token": 2e-06, @@ -66309,7 +63580,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/tencent/Hy3": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 5.8e-07, @@ -66325,7 +63596,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-code": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 5e-07, "output_cost_per_token": 3e-06, @@ -66341,7 +63612,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/ByteDance/Seed-2.0-pro": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 5e-07, "output_cost_per_token": 3e-06, @@ -66357,7 +63628,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 6e-07, "output_cost_per_token": 2.08e-06, @@ -66373,7 +63644,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/Nemotron-3-Nano-30B-A3B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 5e-08, "output_cost_per_token": 2e-07, @@ -66388,7 +63659,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K2.7-Code": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 6.8e-07, "output_cost_per_token": 3.4e-06, @@ -66407,7 +63678,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 1e-05, "prompt_cache_min_tokens": 1024, @@ -66421,7 +63692,7 @@ "supports_vision": true }, "deepinfra/Qwen/Qwen3.5-397B-A17B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 4.5e-07, "output_cost_per_token": 3e-06, @@ -66437,7 +63708,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 8e-08, "output_cost_per_token": 1.8e-07, @@ -66453,7 +63724,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-E4B-it": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-08, "output_cost_per_token": 1e-07, @@ -66466,7 +63737,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V3.2": { - "max_tokens": 163840, + "max_output_tokens": 163840, "max_input_tokens": 163840, "input_cost_per_token": 2.6e-07, "output_cost_per_token": 3.8e-07, @@ -66481,7 +63752,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.8-Max": { - "max_tokens": 256000, + "max_output_tokens": 256000, "max_input_tokens": 256000, "input_cost_per_token": 1.65e-06, "output_cost_per_token": 4.951e-06, @@ -66499,7 +63770,7 @@ "input_cost_per_token": 1e-05, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 5e-05, "prompt_cache_min_tokens": 512, @@ -66514,7 +63785,7 @@ "thinking_always_on": true }, "deepinfra/nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 5e-07, "output_cost_per_token": 2.2e-06, @@ -66530,7 +63801,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-122B-A10B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 2.9e-07, "output_cost_per_token": 2.4e-06, @@ -66544,7 +63815,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5.1": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 1.05e-06, "output_cost_per_token": 3.5e-06, @@ -66560,7 +63831,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Pro": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1.3e-06, "output_cost_per_token": 2.6e-06, @@ -66575,7 +63846,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 8.5e-08, "output_cost_per_token": 4e-07, @@ -66589,7 +63860,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-5.2": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 7.5e-07, "output_cost_per_token": 2.4e-06, @@ -66605,7 +63876,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/moonshotai/Kimi-K3": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 2.85e-06, "output_cost_per_token": 1.425e-05, @@ -66623,7 +63894,7 @@ "input_cost_per_token": 5e-06, "litellm_provider": "deepinfra", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-05, "prompt_cache_min_tokens": 2048, @@ -66637,7 +63908,7 @@ "supports_vision": true }, "deepinfra/Qwen/Qwen3.6-27B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 3.2e-07, "output_cost_per_token": 3.2e-06, @@ -66651,7 +63922,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-26B-A4B-it": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 7e-08, "output_cost_per_token": 3.4e-07, @@ -66665,7 +63936,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemini-3.1-pro": { - "max_tokens": 1000000, + "max_output_tokens": 1000000, "max_input_tokens": 1000000, "input_cost_per_token": 2e-06, "output_cost_per_token": 1.2e-05, @@ -66680,7 +63951,7 @@ "supports_audio_input": true }, "deepinfra/XiaomiMiMo/MiMo-V2.5-Pro": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 1e-06, "output_cost_per_token": 3e-06, @@ -66696,7 +63967,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/anthropic/claude-haiku-4-5": { - "max_tokens": 200000, + "max_output_tokens": 200000, "max_input_tokens": 200000, "input_cost_per_token": 1e-06, "output_cost_per_token": 5e-06, @@ -66711,7 +63982,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4-Flash": { - "max_tokens": 1048576, + "max_output_tokens": 1048576, "max_input_tokens": 1048576, "input_cost_per_token": 9e-08, "output_cost_per_token": 1.8e-07, @@ -66727,7 +63998,6 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/deepseek-ai/DeepSeek-V4.1-Flash": { - "max_tokens": 131072, "max_input_tokens": 1048576, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -66744,7 +64014,6 @@ "source": "https://deepinfra.com/deepseek-ai/DeepSeek-V4.1-Flash" }, "deepinfra/deepseek-ai/DeepSeek-V4.1-Flash-1017": { - "max_tokens": 131072, "max_input_tokens": 1048576, "max_output_tokens": 131072, "input_cost_per_token": 1.4e-07, @@ -66761,7 +64030,7 @@ "source": "https://deepinfra.com/deepseek-ai/DeepSeek-V4.1-Flash-1017" }, "deepinfra/openai/gpt-oss-120b-Ultra": { - "max_tokens": 131072, + "max_output_tokens": 131072, "max_input_tokens": 131072, "input_cost_per_token": 2e-07, "output_cost_per_token": 9.5e-07, @@ -66775,7 +64044,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/Qwen/Qwen3.5-9B": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1e-07, "output_cost_per_token": 1.5e-07, @@ -66788,7 +64057,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/MiniMaxAI/MiniMax-M2.7-Turbo": { - "max_tokens": 196608, + "max_output_tokens": 196608, "max_input_tokens": 196608, "input_cost_per_token": 3.8e-07, "output_cost_per_token": 1.7e-06, @@ -66804,7 +64073,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/zai-org/GLM-4.7": { - "max_tokens": 202752, + "max_output_tokens": 202752, "max_input_tokens": 202752, "input_cost_per_token": 4e-07, "output_cost_per_token": 1.75e-06, @@ -66820,7 +64089,7 @@ "source": "https://deepinfra.com/pricing" }, "deepinfra/google/gemma-4-31B-it": { - "max_tokens": 262144, + "max_output_tokens": 262144, "max_input_tokens": 262144, "input_cost_per_token": 1.3e-07, "output_cost_per_token": 3.8e-07, @@ -66839,7 +64108,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -66874,7 +64142,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -66902,7 +64169,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -66930,7 +64196,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67206,7 +64471,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67233,7 +64497,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67260,7 +64523,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67291,7 +64553,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -67319,7 +64580,6 @@ "litellm_provider": "groq", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://console.groq.com/docs/model/qwen/qwen3.8-27b", @@ -67335,7 +64595,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67356,7 +64615,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67377,7 +64635,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 7.5e-06, "reasoning_effort_levels": [ @@ -67398,7 +64655,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6e-07, "reasoning_effort_levels": [ @@ -67419,7 +64675,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -67434,7 +64689,6 @@ "litellm_provider": "mistral", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://docs.mistral.ai/models/model-cards/codestral-25-08", @@ -67449,7 +64703,6 @@ "litellm_provider": "mistral", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://mistral.ai/news/devstral-2-vibe-cli", @@ -67550,7 +64803,6 @@ "litellm_provider": "mistral", "max_input_tokens": 262144, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://docs.mistral.ai/models/model-cards/leanstral-1-5", @@ -67566,7 +64818,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -67585,7 +64836,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_priority": 8.25e-06, @@ -67604,7 +64854,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.4e-06, "output_cost_per_token_priority": 5.5e-06, @@ -67623,7 +64872,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_priority": 8.25e-06, @@ -67639,7 +64887,7 @@ "input_cost_per_token": 2.1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -67681,7 +64929,7 @@ "input_cost_per_token": 2.1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://docs.fireworks.ai/serverless/pricing", @@ -67698,7 +64946,7 @@ "input_cost_per_token_priority": 1.875e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_priority": 6.25e-07, @@ -67716,7 +64964,7 @@ "input_cost_per_token_priority": 2.8125e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 7.5e-07, "output_cost_per_token_priority": 9.375e-07, @@ -67733,7 +64981,7 @@ "input_cost_per_token_priority": 1.875e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_priority": 6.25e-07, @@ -67751,7 +64999,7 @@ "input_cost_per_token_priority": 2.8125e-07, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 7.5e-07, "output_cost_per_token_priority": 9.375e-07, @@ -67769,7 +65017,7 @@ "input_cost_per_token_priority": 1e-06, "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.05e-06, "output_cost_per_token_priority": 4.05e-06, @@ -67785,7 +65033,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 40960, "max_output_tokens": 40960, - "max_tokens": 40960, "mode": "embedding", "source": "https://api.fireworks.ai/v1/serverless/models" }, @@ -67808,7 +65055,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 1000000, - "max_tokens": 1000000, + "max_output_tokens": 1000000, "mode": "chat", "output_cost_per_token": 4.7e-07, "source": "https://api.together.ai/v1/models" @@ -67868,7 +65115,6 @@ "litellm_provider": "cerebras", "max_input_tokens": 131072, "max_output_tokens": 40960, - "max_tokens": 40960, "mode": "chat", "output_cost_per_token": 1.49e-06, "source": "https://api.cerebras.ai/public/v1/models/gemma-4-31b", @@ -67894,7 +65140,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://www.scaleway.com/en/pricing/model-as-a-service/", @@ -67908,7 +65153,6 @@ "litellm_provider": "scaleway", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://www.scaleway.com/en/pricing/model-as-a-service/", @@ -67924,7 +65168,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -67947,7 +65190,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.2e-06, "supports_function_calling": true, @@ -67989,7 +65231,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68004,7 +65245,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.1e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68019,7 +65259,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68034,7 +65273,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.25e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68050,7 +65288,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 131072, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6.6e-07, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68067,7 +65304,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68080,7 +65316,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -68155,7 +65390,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68173,7 +65407,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -68189,7 +65422,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -68204,7 +65436,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68222,7 +65453,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -68242,7 +65472,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -68267,7 +65496,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -68306,7 +65534,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -68344,7 +65571,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -68381,7 +65607,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -68419,7 +65644,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -68454,7 +65678,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 262144, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.88e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68472,7 +65695,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-12b-v2-vl-bf16.html", @@ -68488,7 +65710,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 2.76e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-nvidia-nvidia-nemotron-nano-9b-v2.html", @@ -68503,7 +65724,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.8e-07, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -68521,7 +65741,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 3.6e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", @@ -68541,7 +65760,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 7.2e-07, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", @@ -68566,7 +65784,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "prompt_cache_min_tokens": 1024, @@ -68600,7 +65817,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 1024, @@ -68633,7 +65849,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "prompt_cache_min_tokens": 512, @@ -68665,7 +65880,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -68698,7 +65912,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "supports_adaptive_thinking": true, @@ -68727,7 +65940,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68763,7 +65975,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68799,7 +66010,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68830,7 +66040,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1048576, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -68854,7 +66063,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -68873,7 +66081,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68892,7 +66099,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68911,7 +66117,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, "supported_endpoints": [ @@ -68931,7 +66136,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", "supported_endpoints": [ @@ -68951,7 +66155,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", "supported_endpoints": [ @@ -68974,7 +66177,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.4e-05, "prompt_cache_min_tokens": 512, @@ -69010,7 +66212,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -69039,7 +66240,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, "supported_endpoints": [ @@ -69072,7 +66272,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "supported_endpoints": [ "/v1/chat/completions", @@ -69092,7 +66291,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-20b.html", "supported_endpoints": [ @@ -69112,7 +66310,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 131072, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html", "supported_endpoints": [ @@ -69136,7 +66333,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 128000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 8.4e-07, "output_cost_per_token_priority": 2.94e-06, @@ -69158,7 +66354,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, "output_cost_per_token_priority": 4.38e-06, @@ -69180,7 +66375,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 4.4e-07, "output_cost_per_token_priority": 1.54e-06, @@ -69202,7 +66396,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 32000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -69225,7 +66418,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, "output_cost_per_token_priority": 1.05e-06, @@ -69247,7 +66439,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 262144, "max_output_tokens": 16000, - "max_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 9e-07, "output_cost_per_token_priority": 3.15e-06, @@ -69268,7 +66459,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, "output_cost_per_token_priority": 2.1e-06, @@ -69290,7 +66480,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 256000, "max_output_tokens": 8000, - "max_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 1.33e-06, "output_cost_per_token_priority": 4.66e-06, @@ -69309,7 +66498,6 @@ "litellm_provider": "azure", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.375e-05, "supported_endpoints": [ @@ -69343,7 +66531,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 6.05e-06, "supports_prompt_caching": true, @@ -69356,7 +66543,6 @@ "input_cost_per_token": 1.63e-07, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -69364,7 +66550,6 @@ "input_cost_per_token": 2.5e-08, "litellm_provider": "azure", "max_input_tokens": 8191, - "max_tokens": 8191, "mode": "embedding", "output_cost_per_token": 0.0 }, @@ -69395,7 +66580,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_image": 0.04, "output_cost_per_token": 0, @@ -69420,7 +66604,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -69445,7 +66628,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/pricing", @@ -69470,7 +66652,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 0, "source": "https://ai.google.dev/gemini-api/docs/models/lyria-realtime-exp", @@ -69843,7 +67024,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -69867,7 +67047,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://inference.baseten.co/v1/models", @@ -69892,7 +67071,7 @@ "output_cost_per_token": 1.5e-07, "source": "https://api.together.ai/v1/models", "max_input_tokens": 128000, - "max_tokens": 128000 + "max_output_tokens": 128000 }, "vertex_ai/gemini-2.5-flash-native-audio": { "deprecation_date": "2026-12-13", @@ -69953,7 +67132,7 @@ "input_cost_per_token": 1.5e-06, "litellm_provider": "vertex_ai", "max_output_tokens": 57920, - "max_tokens": 57920, + "max_input_tokens": 57920, "mode": "chat", "output_cost_per_reasoning_token": 9e-06, "output_cost_per_token": 9e-06, @@ -70016,7 +67195,7 @@ "output_cost_per_token": 3.5e-06, "source": "https://api.together.ai/v1/models", "max_input_tokens": 4096, - "max_tokens": 4096 + "max_output_tokens": 4096 }, "together_ai/meta-llama/Llama-3.2-1B-Instruct": { "input_cost_per_token": 6e-08, @@ -70025,7 +67204,7 @@ "output_cost_per_token": 6e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 131072, - "max_tokens": 131072 + "max_output_tokens": 131072 }, "together_ai/meta-llama/Llama-3.2-3B-Instruct": { "input_cost_per_token": 6e-08, @@ -70034,7 +67213,7 @@ "output_cost_per_token": 6e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 131072, - "max_tokens": 131072 + "max_output_tokens": 131072 }, "together_ai/Qwen/Qwen2-1.5B-Instruct": { "input_cost_per_token": 2e-08, @@ -70043,7 +67222,7 @@ "output_cost_per_token": 2e-08, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Qwen/Qwen2.5-14B-Instruct": { "input_cost_per_token": 8e-07, @@ -70052,7 +67231,7 @@ "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Qwen/Qwen2.5-72B-Instruct": { "input_cost_per_token": 1.2e-06, @@ -70061,14 +67240,13 @@ "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", "max_input_tokens": 32768, - "max_tokens": 32768 + "max_output_tokens": 32768 }, "together_ai/Salesforce/Llama-Rank-V1": { "deprecation_date": "2026-02-06", "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, "mode": "rerank", "output_cost_per_token": 0.0, "source": "https://api.together.ai/v1/models" @@ -70077,7 +67255,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "completion", "output_cost_per_token": 2e-07, "source": "https://api.together.xyz/v1/models" @@ -70095,7 +67273,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models" @@ -70105,7 +67283,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70115,7 +67293,7 @@ "input_cost_per_token": 9e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://api.together.ai/v1/models" @@ -70125,7 +67303,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70135,7 +67313,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70145,7 +67323,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70155,7 +67333,7 @@ "input_cost_per_token": 1.95e-06, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://api.together.ai/v1/models" @@ -70165,7 +67343,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70175,7 +67353,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models" @@ -70185,7 +67363,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70195,7 +67373,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70205,7 +67383,7 @@ "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-06, "source": "https://api.together.ai/v1/models" @@ -70215,7 +67393,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.8e-07, "source": "https://api.together.ai/v1/models" @@ -70226,7 +67404,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.6e-06, "source": "https://api.together.ai/v1/models" @@ -70236,7 +67414,7 @@ "input_cost_per_token": 2e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70246,7 +67424,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://api.together.ai/v1/models" @@ -70256,7 +67434,7 @@ "input_cost_per_token": 1.6e-06, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.6e-06, "source": "https://api.together.ai/v1/models" @@ -70266,7 +67444,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.7e-06, "source": "https://api.together.ai/v1/models" @@ -70276,7 +67454,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 16384, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70286,7 +67464,7 @@ "input_cost_per_token": 8e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 8e-07, "source": "https://api.together.ai/v1/models" @@ -70296,7 +67474,7 @@ "input_cost_per_token": 3.9e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 9.7e-07, "source": "https://api.together.ai/v1/models" @@ -70306,7 +67484,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70316,7 +67494,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.9e-07, "source": "https://api.together.ai/v1/models" @@ -70326,7 +67504,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70336,7 +67514,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 8192, - "max_tokens": 8192, + "max_output_tokens": 8192, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70346,7 +67524,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70356,7 +67534,7 @@ "input_cost_per_token": 1.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.8e-07, "source": "https://api.together.ai/v1/models" @@ -70366,7 +67544,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70376,7 +67554,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://api.together.ai/v1/models" @@ -70386,7 +67564,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 6e-07, "source": "https://api.together.ai/v1/models" @@ -70397,7 +67575,7 @@ "input_cost_per_token": 1.2e-06, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://api.together.ai/v1/models" @@ -70408,7 +67586,7 @@ "input_cost_per_token": 9.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://api.together.ai/v1/models" @@ -70418,7 +67596,7 @@ "input_cost_per_token": 8.8e-07, "litellm_provider": "together_ai", "max_input_tokens": 32768, - "max_tokens": 32768, + "max_output_tokens": 32768, "mode": "chat", "output_cost_per_token": 8.8e-07, "source": "https://api.together.ai/v1/models" @@ -70429,7 +67607,7 @@ "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 512288, - "max_tokens": 512288, + "max_output_tokens": 512288, "mode": "chat", "output_cost_per_token": 3.6e-06, "source": "https://api.together.ai/v1/models" @@ -70439,7 +67617,7 @@ "input_cost_per_token": 5e-08, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 2e-07, "source": "https://api.together.ai/v1/models" @@ -70449,7 +67627,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.1e-06, "source": "https://api.together.ai/v1/models" @@ -70459,7 +67637,7 @@ "input_cost_per_token": 4.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://api.together.ai/v1/models" @@ -70469,7 +67647,7 @@ "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 3.2e-06, "source": "https://api.together.ai/v1/models" @@ -70480,7 +67658,7 @@ "input_cost_per_token": 1.4e-06, "litellm_provider": "together_ai", "max_input_tokens": 202752, - "max_tokens": 202752, + "max_output_tokens": 202752, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://api.together.ai/v1/models" @@ -70547,7 +67725,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_batches": 8.25e-06, @@ -70720,7 +67897,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-05, "output_cost_per_token_above_272k_tokens": 9e-05, @@ -70764,7 +67940,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_above_272k_tokens": 9e-07, @@ -70813,7 +67988,6 @@ "litellm_provider": "azure", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "output_cost_per_token_above_272k_tokens": 1.8e-05, @@ -70869,7 +68043,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -70883,7 +68056,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 8.8e-06, "output_cost_per_token_batches": 4.4e-06, @@ -70906,7 +68078,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -70920,7 +68091,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 100000, - "max_tokens": 100000, "mode": "chat", "output_cost_per_token": 4.84e-06, "output_cost_per_token_batches": 2.42e-06, @@ -70955,7 +68125,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -70977,7 +68146,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 4.5e-06, @@ -71053,7 +68221,6 @@ "litellm_provider": "azure", "max_input_tokens": 128000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_batches": 8.25e-06, @@ -71266,7 +68433,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "output_cost_per_audio_token": 6.4e-05, "output_cost_per_token": 2.4e-05, @@ -71314,7 +68480,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "audio_transcription", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supported_endpoints": [ @@ -71355,7 +68520,6 @@ "litellm_provider": "azure", "max_input_tokens": 32000, "max_output_tokens": 4096, - "max_tokens": 4096, "mode": "realtime", "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", "supported_modalities": [ @@ -71373,7 +68537,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 512000, "max_output_tokens": 65500, - "max_tokens": 65500, "mode": "chat", "output_cost_per_token": 1.5e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71386,7 +68549,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71399,7 +68561,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71414,7 +68575,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71435,7 +68595,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5.5e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71453,7 +68612,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71473,7 +68631,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71494,7 +68651,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71513,7 +68669,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.2e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71528,7 +68683,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.61333e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71557,7 +68711,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.6e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71573,7 +68726,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 4e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71587,7 +68739,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 128000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71602,7 +68753,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.84e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71617,7 +68767,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 3.38e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71632,7 +68781,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.411e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71648,7 +68796,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.4246e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71665,7 +68812,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3.0136e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71682,7 +68828,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.411e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71699,7 +68844,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2.32375e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71716,7 +68860,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 119000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.5353e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71729,7 +68872,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71746,7 +68888,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71763,7 +68904,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71780,7 +68920,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71797,7 +68936,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.499999e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71815,7 +68953,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 3.75e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71832,7 +68969,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 3.9998e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71844,7 +68980,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 131100, - "max_tokens": 131100, "mode": "chat", "output_cost_per_token": 3.9998e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71857,7 +68992,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 7.889e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71872,7 +69006,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.9438e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71887,7 +69020,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.9438e-07, "source": "https://aihubmix.com/api/v1/models", @@ -71903,7 +69035,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 200000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.09848e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71917,7 +69048,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.4e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71933,7 +69063,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71950,7 +69079,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -71967,7 +69095,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4.5e-06, "source": "https://aihubmix.com/api/v1/models", @@ -71984,7 +69111,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.25e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72001,7 +69127,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72017,7 +69142,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 0.00018, "source": "https://aihubmix.com/api/v1/models", @@ -72034,7 +69158,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72051,7 +69174,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72068,7 +69190,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1050000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72084,7 +69205,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 400000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72129,7 +69249,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72145,7 +69264,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72161,7 +69279,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.248e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72177,7 +69294,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.535e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72193,7 +69309,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 3.9995e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72209,7 +69324,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 7.999e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72225,7 +69339,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://aihubmix.com/api/v1/models", @@ -72241,7 +69354,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.0984e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72254,7 +69366,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 8e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72290,7 +69401,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 204800, "max_output_tokens": 204800, - "max_tokens": 204800, "mode": "chat", "output_cost_per_token": 1.1832e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72304,7 +69414,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.152e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72329,7 +69438,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 5.48e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72341,7 +69449,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9.008e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72356,7 +69463,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 9.864e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72371,7 +69477,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 2.532e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72386,7 +69491,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1.524e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72403,7 +69507,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 262144, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 7.608e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72420,7 +69523,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.128e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72437,7 +69539,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72454,7 +69555,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 3.80025e-07, "source": "https://aihubmix.com/api/v1/models", @@ -72472,7 +69572,6 @@ "litellm_provider": "aihubmix", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5.07e-06, "source": "https://aihubmix.com/api/v1/models", @@ -72657,7 +69756,7 @@ "input_cost_per_token": 2e-07, "litellm_provider": "wandb", "max_input_tokens": 1049000, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6.5e-07, "source": "https://wandb.ai/site/pricing/tokens/", @@ -72670,7 +69769,7 @@ "input_cost_per_token": 1e-07, "litellm_provider": "wandb", "max_input_tokens": 262000, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 3e-07, "source": "https://wandb.ai/site/pricing/tokens/", @@ -72711,7 +69810,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -72743,7 +69841,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_flex": 7.5e-06, @@ -72775,7 +69872,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -72797,7 +69893,6 @@ "litellm_provider": "xiaomi_mimo", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 8.7e-07, "source": "https://platform.xiaomimimo.com/static/docs/price/pay-as-you-go.md", @@ -72820,7 +69915,6 @@ "litellm_provider": "xiaomi_mimo", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.8e-07, "source": "https://platform.xiaomimimo.com/static/docs/price/pay-as-you-go.md", @@ -72845,7 +69939,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "output_cost_per_token_batches": 2e-06, @@ -72871,7 +69964,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72893,7 +69985,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72915,7 +70006,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72937,7 +70027,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72958,7 +70047,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -72980,7 +70068,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73001,7 +70088,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73023,7 +70109,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73045,7 +70130,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73066,7 +70150,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73088,7 +70171,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73110,7 +70192,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73131,7 +70212,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73153,7 +70233,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73178,7 +70257,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73203,7 +70281,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "responses", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73228,7 +70305,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73249,7 +70325,6 @@ "litellm_provider": "xai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, - "max_tokens": 1000000, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://api.x.ai/v1/language-models", @@ -73271,7 +70346,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -73289,7 +70363,6 @@ "litellm_provider": "baseten", "max_input_tokens": 262000, "max_output_tokens": 262000, - "max_tokens": 262000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73307,7 +70380,6 @@ "litellm_provider": "baseten", "max_input_tokens": 262000, "max_output_tokens": 262000, - "max_tokens": 262000, "mode": "chat", "output_cost_per_token": 4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73324,7 +70396,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://inference.baseten.co/v1/models", @@ -73341,7 +70412,6 @@ "litellm_provider": "baseten", "max_input_tokens": 202800, "max_output_tokens": 202800, - "max_tokens": 202800, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73359,7 +70429,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://inference.baseten.co/v1/models", @@ -73377,7 +70446,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -73394,7 +70462,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -73411,7 +70478,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -73428,7 +70494,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.6e-07, "source": "https://inference.baseten.co/v1/models", @@ -73446,7 +70511,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.48e-06, "source": "https://inference.baseten.co/v1/models", @@ -73463,7 +70527,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 3.96e-06, "source": "https://inference.baseten.co/v1/models", @@ -73480,7 +70543,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 6.6e-06, "source": "https://inference.baseten.co/v1/models", @@ -73500,7 +70562,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73521,7 +70582,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73542,7 +70602,6 @@ "litellm_provider": "xai", "max_input_tokens": 256000, "max_output_tokens": 256000, - "max_tokens": 256000, "mode": "chat", "output_cost_per_token": 2e-06, "output_cost_per_token_above_200k_tokens": 4e-06, @@ -73563,7 +70622,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.2e-06, @@ -73581,7 +70639,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 196608, "max_output_tokens": 196608, - "max_tokens": 196608, "mode": "chat", "output_cost_per_token_priority": 1.2e-06, "source": "https://api.fireworks.ai/v1/serverless/models", @@ -73647,7 +70704,6 @@ "litellm_provider": "vertex_ai-zai_models", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 4.4e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -73665,7 +70721,6 @@ "litellm_provider": "vertex_ai-llama_models", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 7.2e-07, "output_cost_per_token_batches": 3.6e-07, @@ -73684,7 +70739,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.4, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#veo", @@ -73700,7 +70754,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.1, "output_cost_per_second_1080p": 0.12, @@ -73717,7 +70770,6 @@ "deprecation_date": "2026-06-30", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, - "max_tokens": 1024, "mode": "video_generation", "output_cost_per_second": 0.5, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#veo", @@ -73764,7 +70816,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73797,7 +70848,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73830,7 +70880,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73863,7 +70912,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73901,7 +70949,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73934,7 +70981,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "thinking_always_on": true, @@ -73970,7 +71016,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74009,7 +71054,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74049,7 +71093,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74087,7 +71130,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74126,7 +71168,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "supports_assistant_prefill": false, "supports_computer_use": true, @@ -74159,7 +71200,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -74197,7 +71237,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74217,7 +71256,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74237,7 +71275,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "thinking_always_on": true, "supports_function_calling": true, @@ -74259,7 +71296,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 8192, - "max_tokens": 8192, "mode": "chat", "output_cost_per_token": 5.4e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-deepseek-deepseek-r1.html", @@ -74272,7 +71308,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 128000, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "chat", "output_cost_per_token": 6e-06, "source": "https://aws.amazon.com/bedrock/pricing/", @@ -74285,7 +71320,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.5e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/microsoft/", @@ -74321,7 +71355,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74359,7 +71392,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -74405,7 +71437,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74447,7 +71478,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74484,7 +71514,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74521,7 +71550,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74562,7 +71590,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74609,7 +71636,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "search_context_cost_per_query": { @@ -74651,7 +71677,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74783,7 +71808,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.2e-05, "supports_forced_tool_use": false, @@ -74829,7 +71853,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.1e-05, "search_context_cost_per_query": { @@ -74874,7 +71897,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -74914,7 +71936,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -74947,7 +71968,6 @@ "litellm_provider": "prism", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 6.3e-07, "source": "https://prisminference.com/pricing", @@ -74972,7 +71992,6 @@ "litellm_provider": "prism", "max_input_tokens": 1000000, "max_output_tokens": 384000, - "max_tokens": 384000, "mode": "chat", "output_cost_per_token": 2.1e-07, "source": "https://prisminference.com/pricing", @@ -75005,7 +72024,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_flex": 3e-06, @@ -75031,7 +72049,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6.6e-06, "output_cost_per_token_flex": 3.3e-06, @@ -75049,7 +72066,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -75104,7 +72120,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -75172,7 +72187,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_above_272k_tokens": 1.5e-05, @@ -75218,7 +72232,6 @@ "litellm_provider": "bedrock_mantle", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "responses", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -75266,7 +72279,6 @@ "supports_bedrock_converse_tool_result_images": false, "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.1e-05, "output_cost_per_token_above_272k_tokens": 1.65e-05, @@ -75301,7 +72313,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 9e-06, "output_cost_per_token": 9e-06, @@ -75315,7 +72326,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 8192, "max_output_tokens": 16384, - "max_tokens": 16384, "mode": "audio_speech", "output_cost_per_audio_token": 6e-06, "output_cost_per_token": 6e-06, @@ -75332,7 +72342,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 524288, "max_output_tokens": 524288, - "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -75350,7 +72359,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 262144, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.5e-06, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/kimi/", @@ -75383,7 +72391,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.28e-06, "output_cost_per_token_batches": 2.64e-06, @@ -75416,7 +72423,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.808e-06, "output_cost_per_token_batches": 2.904e-06, @@ -75439,7 +72445,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "realtime", "output_cost_per_audio_token": 1.2e-05, "output_cost_per_token": 2.75e-06, @@ -75454,7 +72459,6 @@ "litellm_provider": "azure", "max_input_tokens": 200000, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "chat", "output_cost_per_token": 0.0, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/model-router/", @@ -75474,7 +72478,6 @@ "litellm_provider": "gemini", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75519,7 +72522,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75565,7 +72567,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 32768, - "max_tokens": 32768, "mode": "image_generation", "output_cost_per_image": 0.0336, "output_cost_per_image_token": 3e-05, @@ -75609,7 +72610,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -75637,7 +72637,6 @@ "token_count_tokenizer": "kimi_k3", "max_input_tokens": 1000000, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.65e-05, "output_cost_per_token_flex": 8.25e-06, @@ -75670,7 +72669,6 @@ "litellm_provider": "anthropic", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -75724,7 +72722,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -75771,7 +72768,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -75816,7 +72812,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -75859,7 +72854,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "supports_adaptive_thinking": true, "supports_mid_conversation_system": true, @@ -75910,7 +72904,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -75957,7 +72950,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -75999,7 +72991,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76041,7 +73032,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76087,7 +73077,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76139,7 +73128,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "search_context_cost_per_query": { @@ -76186,7 +73174,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76245,7 +73232,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6e-07, "supports_forced_tool_use": true, @@ -76296,7 +73282,6 @@ "supports_tool_search": true, "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-07, "search_context_cost_per_query": { @@ -76346,7 +73331,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -76391,7 +73375,6 @@ "litellm_provider": "vertex_ai-anthropic_models", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -76430,7 +73413,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76441,7 +73423,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76452,7 +73433,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 261120, "max_output_tokens": 98304, - "max_tokens": 98304, "mode": "chat", "output_cost_per_token": 7.5e-06, "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-twelvelabs-pegasus-v1-5.html", @@ -76488,7 +73468,6 @@ "input_cost_per_token": 8e-08, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048.0, @@ -76508,7 +73487,6 @@ "input_cost_per_token": 1.2e-07, "litellm_provider": "azure_ai", "max_input_tokens": 128000, - "max_tokens": 128000, "mode": "embedding", "output_cost_per_token": 0.0, "output_vector_size": 2048.0, @@ -76577,7 +73555,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -76623,7 +73600,6 @@ "litellm_provider": "vertex_ai-language-models", "max_input_tokens": 131072, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_reasoning_token": 5e-06, "output_cost_per_token": 5e-06, @@ -76673,7 +73649,6 @@ "litellm_provider": "gemini", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 1e-05, "output_cost_per_token": 1e-05, @@ -76732,7 +73707,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 1e-05, "output_cost_per_token": 1e-05, @@ -76790,7 +73764,6 @@ "litellm_provider": "vertex_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_reasoning_token": 2e-05, "output_cost_per_token": 2e-05, @@ -76838,7 +73811,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 65536, - "max_tokens": 65536, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -76851,7 +73823,6 @@ "supports_vision": true }, "openinfer/@oi/GLM-5.3-Flash": { - "max_tokens": 393216, "max_input_tokens": 393216, "max_output_tokens": 393216, "input_cost_per_token": 1.95e-07, @@ -76863,7 +73834,6 @@ "source": "https://api.openinfer.ai/v1/models" }, "openinfer/@oi/Qwen3.5-27B": { - "max_tokens": 32768, "max_input_tokens": 32768, "max_output_tokens": 32768, "input_cost_per_token": 1.95e-07, @@ -76875,7 +73845,6 @@ "source": "https://api.openinfer.ai/v1/models" }, "openinfer/@oi/Qwen3.5-9B": { - "max_tokens": 100000, "max_input_tokens": 100000, "max_output_tokens": 100000, "input_cost_per_token": 5e-06, @@ -76954,7 +73923,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, - "max_tokens": 262144, + "max_output_tokens": 262144, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://api.together.ai/v1/models", @@ -76977,7 +73946,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 3.311e-06, "output_cost_per_token_batches": 1.6555e-06, @@ -76999,7 +73967,6 @@ "litellm_provider": "vertex_ai-deepseek_models", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing", @@ -77023,7 +73990,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 4e-05, "prompt_cache_min_tokens": 512.0, @@ -77053,7 +74019,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.25e-06, "source": "https://inference.baseten.co/v1/models", @@ -77071,7 +74036,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5e-07, "source": "https://inference.baseten.co/v1/models", @@ -77087,7 +74051,7 @@ "input_cost_per_token": 1.5e-07, "litellm_provider": "wandb", "max_input_tokens": 1049000, - "max_tokens": 943718, + "max_output_tokens": 943718, "mode": "chat", "output_cost_per_token": 7.5e-07, "source": "https://openrouter.ai/api/v1/models/nvidia/nemotron-3.5-super-vl/endpoints", @@ -77103,7 +74067,7 @@ "input_cost_per_token": 4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -77117,7 +74081,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 131072, - "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -77131,7 +74094,7 @@ "input_cost_per_token": 2.5e-07, "litellm_provider": "nebius", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://tokenfactory.nebius.com/models/catalog/text2text/reflection%2Fbeam", @@ -77144,7 +74107,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://inference.baseten.co/v1/models", @@ -77161,7 +74123,6 @@ "litellm_provider": "baseten", "max_input_tokens": 1048576, "max_output_tokens": 262144, - "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 2.4e-06, "source": "https://inference.baseten.co/v1/models", @@ -77180,7 +74141,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.2e-06, "output_cost_per_token_priority": 1.5e-06, @@ -77200,7 +74160,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -77222,7 +74181,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 272000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 1.25e-06, "source": "https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml", @@ -77244,7 +74202,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77266,7 +74223,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77288,7 +74244,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 500000, "max_output_tokens": 500000, - "max_tokens": 500000, "mode": "chat", "output_cost_per_token": 6e-06, "output_cost_per_token_above_200k_tokens": 1.2e-05, @@ -77307,7 +74262,7 @@ "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, - "max_tokens": 1048576, + "max_output_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://api.together.ai/v1/models", @@ -77344,7 +74299,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 1048576, - "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 6e-07, "output_cost_per_token_priority": 9e-07, @@ -77380,7 +74334,6 @@ "litellm_provider": "fireworks_ai", "max_input_tokens": 1048576, "max_output_tokens": 393216, - "max_tokens": 393216, "mode": "chat", "output_cost_per_token": 1.8e-06, "output_cost_per_token_priority": 2.25e-06, @@ -77429,7 +74382,6 @@ "litellm_provider": "azure_ai", "max_input_tokens": 256000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.2e-06, "source": "https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20'Foundry%20Models'%20and%20armRegionName%20eq%20'eastus'%20and%20priceType%20eq%20'Consumption'", @@ -77459,7 +74411,6 @@ "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, "cache_read_input_token_cost": 5e-07, - "max_tokens": 500000, "max_output_tokens": 500000, "max_input_tokens": 500000, "supports_function_calling": true, @@ -77497,7 +74448,6 @@ "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 1e-08, "cache_creation_input_token_cost": 1.25e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 922000, "supports_function_calling": true, @@ -77537,7 +74487,6 @@ "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 2e-08, "cache_creation_input_token_cost": 2.5e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 922000, "supports_function_calling": true, @@ -77577,7 +74526,6 @@ "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 1e-08, "cache_creation_input_token_cost": 1.25e-07, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77616,7 +74564,6 @@ "input_cost_per_token": 2e-06, "output_cost_per_token": 1e-05, "cache_read_input_token_cost": 1e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77633,7 +74580,6 @@ "input_cost_per_token": 1.5e-07, "output_cost_per_token": 5e-07, "cache_read_input_token_cost": 3e-08, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77650,7 +74596,6 @@ "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77667,7 +74612,6 @@ "input_cost_per_token": 1.4e-06, "output_cost_per_token": 4.4e-06, "cache_read_input_token_cost": 2.6e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77685,7 +74629,6 @@ "output_cost_per_token": 1e-05, "cache_read_input_token_cost": 2.0000000000000002e-07, "cache_creation_input_token_cost": 2e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77703,7 +74646,6 @@ "output_cost_per_token": 1.5e-05, "cache_read_input_token_cost": 3e-07, "cache_creation_input_token_cost": 3e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77720,7 +74662,6 @@ "input_cost_per_token": 9.499999999999999e-07, "output_cost_per_token": 4e-06, "cache_read_input_token_cost": 1.9e-07, - "max_tokens": 262144, "max_output_tokens": 262144, "max_input_tokens": 262144, "supports_function_calling": true, @@ -77737,7 +74678,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77754,7 +74694,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-09, - "max_tokens": 384000, "max_output_tokens": 384000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77771,7 +74710,6 @@ "input_cost_per_token": 1.32e-06, "output_cost_per_token": 3.96e-06, "cache_read_input_token_cost": 4.4e-08, - "max_tokens": 384000, "max_output_tokens": 384000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77788,7 +74726,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, "cache_read_input_token_cost": 2.8e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77805,7 +74742,6 @@ "input_cost_per_token": 4.35e-07, "output_cost_per_token": 8.7e-07, "cache_read_input_token_cost": 3.625e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77822,7 +74758,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 2.8e-07, "cache_read_input_token_cost": 2.8e-09, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77839,7 +74774,6 @@ "input_cost_per_token": 4.35e-07, "output_cost_per_token": 8.7e-07, "cache_read_input_token_cost": 3.625e-09, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77856,7 +74790,6 @@ "input_cost_per_token": 1.5e-07, "output_cost_per_token": 6e-07, "cache_read_input_token_cost": 1.5e-08, - "max_tokens": 512000, "max_output_tokens": 512000, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77873,7 +74806,6 @@ "input_cost_per_token": 3e-07, "output_cost_per_token": 1.2e-06, "cache_read_input_token_cost": 6e-08, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77890,7 +74822,6 @@ "input_cost_per_token": 1.0000000000000001e-07, "output_cost_per_token": 2.0000000000000002e-07, "cache_read_input_token_cost": 2e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77907,7 +74838,6 @@ "input_cost_per_token": 1.0000000000000001e-07, "output_cost_per_token": 2.0000000000000002e-07, "cache_read_input_token_cost": 2e-09, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -77925,7 +74855,6 @@ "output_cost_per_token": 6e-06, "cache_read_input_token_cost": 2.5e-07, "cache_creation_input_token_cost": 2.5e-06, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77943,7 +74872,6 @@ "output_cost_per_token": 4.6999999999999995e-07, "cache_read_input_token_cost": 1.6e-08, "cache_creation_input_token_cost": 2.0000000000000002e-07, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -77961,7 +74889,6 @@ "output_cost_per_token": 1.6000000000000001e-06, "cache_read_input_token_cost": 4e-08, "cache_creation_input_token_cost": 5e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78000,7 +74927,6 @@ "input_cost_per_token": 8.34e-07, "output_cost_per_token": 2.501e-06, "cache_read_input_token_cost": 4.2000000000000006e-08, - "max_tokens": 64000, "max_output_tokens": 64000, "max_input_tokens": 1024000, "supports_function_calling": true, @@ -78017,7 +74943,6 @@ "input_cost_per_token": 1.4e-07, "output_cost_per_token": 5.8e-07, "cache_read_input_token_cost": 3.5e-08, - "max_tokens": 128000, "max_output_tokens": 128000, "max_input_tokens": 192000, "supports_function_calling": true, @@ -78034,7 +74959,6 @@ "input_cost_per_token": 0, "output_cost_per_token": 0, "cache_read_input_token_cost": 0, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78051,7 +74975,6 @@ "input_cost_per_token": 1e-06, "output_cost_per_token": 2.7e-06, "cache_read_input_token_cost": 1.0000000000000001e-07, - "max_tokens": 65536, "max_output_tokens": 65536, "max_input_tokens": 1000000, "supports_function_calling": true, @@ -78068,7 +74991,6 @@ "input_cost_per_token": 0, "output_cost_per_token": 0, "cache_read_input_token_cost": 0, - "max_tokens": 131072, "max_output_tokens": 131072, "max_input_tokens": 1048576, "supports_function_calling": true, @@ -78089,7 +75011,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -78134,7 +75055,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-05, "output_cost_per_token_batches": 2.5e-05, @@ -78178,7 +75098,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_batches": 2.5e-06, @@ -78211,7 +75130,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-07, "output_cost_per_token_batches": 2.5e-07, @@ -78261,7 +75179,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78298,7 +75215,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78341,7 +75257,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78385,7 +75300,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78430,7 +75344,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, "output_cost_per_token_batches": 1.25e-05, @@ -78475,7 +75388,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2e-05, "output_cost_per_token_batches": 1e-05, @@ -78520,7 +75432,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 200000, "max_output_tokens": 64000, - "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -78555,7 +75466,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1.5e-05, "output_cost_per_token_batches": 7.5e-06, @@ -78598,7 +75508,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -78642,7 +75551,6 @@ "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 1e-05, "output_cost_per_token_batches": 5e-06, @@ -78712,7 +75620,6 @@ "litellm_provider": "openai", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, @@ -78761,7 +75668,6 @@ "litellm_provider": "chatgpt", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "source": "https://developers.openai.com/api/docs/pricing", "supported_endpoints": [ @@ -78788,7 +75694,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, @@ -78827,7 +75732,6 @@ "litellm_provider": "bedrock_converse", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5.5e-06, "output_cost_per_token_above_272k_tokens": 8.25e-06, @@ -78884,7 +75788,6 @@ "litellm_provider": "github_copilot", "max_input_tokens": 922000, "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "responses", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, diff --git a/tests/unit/litellm_core_utils/test_fallback_generalizations.py b/tests/unit/litellm_core_utils/test_fallback_generalizations.py index 7807c438503..e59252e1571 100644 --- a/tests/unit/litellm_core_utils/test_fallback_generalizations.py +++ b/tests/unit/litellm_core_utils/test_fallback_generalizations.py @@ -369,7 +369,7 @@ def test_exact_entries_fill_only_missing_fields(restore_generalizations, monkeyp "output_cost_per_token": 2e-6, "litellm_provider": "openai", "mode": "chat", - "max_tokens": 7, + "max_output_tokens": 7, "supports_reasoning": False, }, "acme-bare": { @@ -398,7 +398,7 @@ def test_exact_entries_fill_only_missing_fields(restore_generalizations, monkeyp "name": "acme-backfill", "pattern": r"^acme-", "fill_missing_for_providers": ["openai"], - "model_info": {"supports_reasoning": True, "max_tokens": 5}, + "model_info": {"supports_reasoning": True, "max_output_tokens": 5}, } ] ) @@ -406,10 +406,12 @@ def test_exact_entries_fill_only_missing_fields(restore_generalizations, monkeyp full = litellm.get_model_info("acme-full", custom_llm_provider="openai") assert full["supports_reasoning"] is False + assert full["max_output_tokens"] == 7 assert full["max_tokens"] == 7 bare = litellm.get_model_info("acme-bare", custom_llm_provider="openai") assert bare["supports_reasoning"] is True + assert bare["max_output_tokens"] == 5 assert bare["max_tokens"] == 5 assert bare["input_cost_per_token"] == 3e-6 assert bare["key"] == "acme-bare" @@ -525,7 +527,7 @@ def test_shipped_gemini_chat_baseline_loses_to_exact_entries(shipped_cost_map): model = "gemini-2.5-flash-lite" info = litellm.get_model_info(model, custom_llm_provider="gemini") entry = litellm.model_cost["gemini/gemini-2.5-flash-lite"] - assert info["max_tokens"] == entry["max_tokens"] + assert info["max_tokens"] == entry["max_output_tokens"] assert info["input_cost_per_token"] == entry["input_cost_per_token"] assert entry["input_cost_per_token"] > 0 diff --git a/tests/unit/llms/openai_like/test_openinfer_provider.py b/tests/unit/llms/openai_like/test_openinfer_provider.py index 7bcc489fcf8..6e6e957df0b 100644 --- a/tests/unit/llms/openai_like/test_openinfer_provider.py +++ b/tests/unit/llms/openai_like/test_openinfer_provider.py @@ -11,9 +11,7 @@ from litellm.proxy.auth.model_checks import get_provider_models _REPO_ROOT: Final = Path(__file__).resolve().parents[4] _PRIMARY_PRICES: Final = json.loads((_REPO_ROOT / "model_prices_and_context_window.json").read_text()) -_BACKUP_PRICES: Final = json.loads( - (_REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json").read_text() -) +_BACKUP_PRICES: Final = json.loads((_REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json").read_text()) _CATALOG_MODELS: Final = tuple( key for key, row in _PRIMARY_PRICES.items() if isinstance(row, dict) and row.get("litellm_provider") == "openinfer" ) @@ -31,7 +29,9 @@ _CHAT_RESPONSE: Final = { "object": "chat.completion", "created": 1_792_000_000, "model": "@oi/Qwen3.5-27B", - "choices": [{"index": 0, "message": {"role": "assistant", "content": "Hello from OpenInfer"}, "finish_reason": "stop"}], + "choices": [ + {"index": 0, "message": {"role": "assistant", "content": "Hello from OpenInfer"}, "finish_reason": "stop"} + ], "usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7}, } @@ -228,7 +228,6 @@ def test_catalog_row_matches_its_backup_and_fits_its_context(model: str): assert row == _BACKUP_PRICES[model] assert row["mode"] == "chat" - assert row["max_tokens"] == row["max_output_tokens"] assert 0 < row["max_output_tokens"] <= row["max_input_tokens"] assert row["input_cost_per_token"] > 0 assert row["output_cost_per_token"] > 0 diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index 45222b2c9ab..35658097880 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -50,7 +50,6 @@ class _CopilotEndpointRow(BaseModel): mode: str max_input_tokens: int max_output_tokens: int - max_tokens: int supported_endpoints: tuple[str, ...] @@ -96,18 +95,22 @@ def _anthropic_counterpart(key: str) -> str: @pytest.mark.parametrize(("path",), [(PRICES_PATH,), (BACKUP_PRICES_PATH,)], ids=("main", "backup")) -def test_github_copilot_rows_derive_mode_and_max_tokens_from_their_endpoints(path: Path) -> None: +def test_github_copilot_rows_derive_mode_from_their_endpoints(path: Path) -> None: rows: Final[Mapping[str, _CopilotEndpointRow]] = _copilot_endpoint_rows(path) assert rows, f"No GitHub Copilot endpoint rows found in {path}" - assert {key: (row.mode, row.max_tokens) for key, row in rows.items()} == { - key: ( - "chat" if "/v1/chat/completions" in row.supported_endpoints else "responses", - row.max_output_tokens, - ) - for key, row in rows.items() + assert {key: row.mode for key, row in rows.items()} == { + key: "chat" if "/v1/chat/completions" in row.supported_endpoints else "responses" for key, row in rows.items() } +def _rows_carrying_legacy_max_tokens(prices: Mapping[str, Mapping[str, object]]) -> tuple[str, ...]: + return tuple(key for key, row in prices.items() if "max_tokens" in row) + + +def test_no_row_carries_the_retired_max_tokens_field(prices: dict) -> None: + assert _rows_carrying_legacy_max_tokens(prices) == () + + def test_github_copilot_backup_rows_match_the_main_catalog() -> None: assert _github_copilot_catalog_rows(PRICES_PATH) == _github_copilot_catalog_rows(BACKUP_PRICES_PATH) @@ -601,7 +604,6 @@ def test_tencent_tokenhub_glm_rows_match_published_metadata(path: Path): or entry.get("litellm_provider") != "tencent" or entry.get("max_input_tokens") != 1000000 or entry.get("max_output_tokens") != 128000 - or entry.get("max_tokens") != 128000 or entry.get("input_cost_per_token_cache_hit") != entry.get("cache_read_input_token_cost") ) assert actual == TENCENT_TOKENHUB_GLM_ROWS @@ -619,7 +621,7 @@ def test_tencent_tokenhub_kimi_rows_match_published_metadata(path: Path): entry.get("input_cost_per_token"), entry.get("output_cost_per_token"), entry.get("cache_read_input_token_cost"), - entry.get("max_tokens"), + entry.get("max_output_tokens"), entry.get("supports_vision", False), entry.get("supports_video_input", False), ) @@ -793,7 +795,6 @@ def test_volcengine_doubao_seed_2_1_pro_260915_row_matches_published_metadata(pa assert entry["mode"] == "chat" assert entry["max_input_tokens"] == 1024000 assert entry["max_output_tokens"] == 256000 - assert entry["max_tokens"] == 256000 # Volcengine Ark pricing https://www.volcengine.com/docs/82379/1544106 checked 2026-10-20 assert entry["input_cost_per_token"] == pytest.approx(6.0 / 6.9565 / 1_000_000, rel=1e-4) assert entry["output_cost_per_token"] == pytest.approx(30.0 / 6.9565 / 1_000_000, rel=1e-4) diff --git a/tests/unit/test_together_ai_model_metadata.py b/tests/unit/test_together_ai_model_metadata.py index dccba3d66f9..3616c6216ca 100644 --- a/tests/unit/test_together_ai_model_metadata.py +++ b/tests/unit/test_together_ai_model_metadata.py @@ -75,18 +75,6 @@ def cost_map() -> CostMap: return COST_MAP_ADAPTER.validate_python(json.load(f)) -def test_together_chat_entries_never_carry_context_length_as_output_ceiling(cost_map: CostMap): - inflated = sorted( - model - for model, info in cost_map.items() - if info.get("litellm_provider") == "together_ai" - and info.get("mode") == "chat" - and "max_output_tokens" in info - and info["max_output_tokens"] == info.get("max_input_tokens") - ) - assert inflated == [] - - def _successor(info: dict[str, object]) -> str | None: metadata = info.get("metadata") if not isinstance(metadata, dict): @@ -95,8 +83,6 @@ def _successor(info: dict[str, object]) -> str | None: return successor if isinstance(successor, str) else None - - def test_together_backup_cost_map_in_sync(cost_map: CostMap): with open(REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json") as f: backup = COST_MAP_ADAPTER.validate_python(json.load(f)) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index b9baef4056a..6c7f6a9d869 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -2260,6 +2260,45 @@ class TestProxyFunctionCalling: @pytest.mark.usefixtures("isolated_openai_model_sets", "local_model_cost_map") +def test_get_model_info_derives_max_tokens_from_the_output_limit() -> None: + model: Final = "derived-max-tokens-chat-model" + litellm.register_model( + { + model: { + "litellm_provider": "openai", + "mode": "chat", + "max_input_tokens": 400000, + "max_output_tokens": 128000, + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, + } + }, + persist_across_reloads=False, + ) + info: Final = litellm.get_model_info(model, custom_llm_provider="openai") + assert info["max_tokens"] == 128000 + assert litellm.get_max_tokens(model) == 128000 + + +def test_get_model_info_derives_max_tokens_from_the_input_limit_when_the_row_has_no_output_limit() -> None: + model: Final = "derived-max-tokens-embedding-model" + litellm.register_model( + { + model: { + "litellm_provider": "openai", + "mode": "embedding", + "max_input_tokens": 8191, + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, + } + }, + persist_across_reloads=False, + ) + info: Final = litellm.get_model_info(model, custom_llm_provider="openai") + assert info["max_tokens"] == 8191 + assert info["max_output_tokens"] is None + + def test_register_model_price_is_used_for_completion_cost() -> None: model: Final = "migration-custom-pricing-model" price: Final = { @@ -8629,7 +8668,7 @@ def test_basic_trimming_no_max_tokens_specified(): trimmed_messages = trim_messages(messages, model="gpt-4") print("trimmed messages for gpt-4") print(trimmed_messages) - assert (get_token_count(messages=trimmed_messages, model="gpt-4")) <= litellm.model_cost["gpt-4"]["max_tokens"] + assert (get_token_count(messages=trimmed_messages, model="gpt-4")) <= litellm.model_cost["gpt-4"]["max_input_tokens"] def test_multiple_messages_trimming(): From 554b011787043a4cfc6a48d7d011bbfca05b6e22 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:04 -0700 Subject: [PATCH 41/73] feat(rust): add typed database query foundation (#48214) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/test-rust-db.yml | 106 ++++++ litellm-rust/Cargo.lock | 5 + litellm-rust/Cargo.toml | 1 + ...64311d25428fd393bac4250891f148597e92e.json | 78 ++++ litellm-rust/crates/db/AGENTS.md | 25 ++ litellm-rust/crates/db/Cargo.toml | 7 + .../db/queries/end_user/find_end_user.sql | 10 + litellm-rust/crates/db/src/daily_spend.rs | 76 ++++ litellm-rust/crates/db/src/end_user.rs | 359 ++++++++++++++++++ litellm-rust/crates/db/src/error.rs | 79 ++++ litellm-rust/crates/db/src/lib.rs | 18 +- litellm-rust/crates/db/src/test_support.rs | 10 + 12 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-rust-db.yml create mode 100644 litellm-rust/crates/db/.sqlx/query-377d063cd0fc3a265d07d01f5aa64311d25428fd393bac4250891f148597e92e.json create mode 100644 litellm-rust/crates/db/AGENTS.md create mode 100644 litellm-rust/crates/db/queries/end_user/find_end_user.sql create mode 100644 litellm-rust/crates/db/src/daily_spend.rs create mode 100644 litellm-rust/crates/db/src/end_user.rs create mode 100644 litellm-rust/crates/db/src/error.rs create mode 100644 litellm-rust/crates/db/src/test_support.rs diff --git a/.github/workflows/test-rust-db.yml b/.github/workflows/test-rust-db.yml new file mode 100644 index 00000000000..bb88b593d5b --- /dev/null +++ b/.github/workflows/test-rust-db.yml @@ -0,0 +1,106 @@ +name: LiteLLM Rust DB + +on: + push: + paths: + - "litellm-rust/crates/db/**" + - "litellm-rust/Cargo.toml" + - "litellm-rust/Cargo.lock" + - "litellm-rust/clippy.toml" + - "litellm-proxy-extras/litellm_proxy_extras/migrations/**" + - "**/schema.prisma" + - ".cargo/**" + - "rust-toolchain.toml" + - "pyproject.toml" + - "uv.lock" + - ".github/actions/setup-uv-with-retries/**" + - ".github/workflows/test-rust-db.yml" + pull_request: + branches: + - main + - "litellm_**" + paths: + - "litellm-rust/crates/db/**" + - "litellm-rust/Cargo.toml" + - "litellm-rust/Cargo.lock" + - "litellm-rust/clippy.toml" + - "litellm-proxy-extras/litellm_proxy_extras/migrations/**" + - "**/schema.prisma" + - ".cargo/**" + - "rust-toolchain.toml" + - "pyproject.toml" + - "uv.lock" + - ".github/actions/setup-uv-with-retries/**" + - ".github/workflows/test-rust-db.yml" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + +jobs: + db-queries: + runs-on: ubuntu-latest + timeout-minutes: 30 + services: + postgres: + image: postgres:16.6@sha256:557fea37a744d5f4c8faab304b0a90858b53ab119735a88c131fd19dab802f36 + env: + POSTGRES_DB: litellm + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres -d litellm" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/litellm + SQLX_OFFLINE: "false" + defaults: + run: + working-directory: litellm-rust + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - uses: ./.github/actions/setup-uv-with-retries + with: + version: "0.10.9" + + - run: rustup toolchain install --no-self-update + + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: litellm-rust + cache-on-failure: true + + - name: Install Python dependencies + working-directory: . + run: uv sync --frozen --extra extra-proxy + + - name: Deploy Prisma migrations + working-directory: . + run: uv run --no-sync prisma migrate deploy --schema litellm-proxy-extras/litellm_proxy_extras/schema.prisma + + - name: Install SQLx CLI + run: cargo install sqlx-cli --version 0.9.0 --locked --no-default-features -F postgres + + - name: Check SQLx query metadata + working-directory: litellm-rust/crates/db + run: cargo sqlx prepare --check + + - name: Test database queries + run: cargo test -p litellm-db --locked -- --include-ignored diff --git a/litellm-rust/Cargo.lock b/litellm-rust/Cargo.lock index cb50324e1dd..d58ba9ecb5f 100644 --- a/litellm-rust/Cargo.lock +++ b/litellm-rust/Cargo.lock @@ -3695,8 +3695,13 @@ dependencies = [ name = "litellm-db" version = "0.1.0" dependencies = [ + "chrono", + "macro_rules_attribute", + "rstest", "serde", "sqlx", + "thiserror 2.0.19", + "tokio", ] [[package]] diff --git a/litellm-rust/Cargo.toml b/litellm-rust/Cargo.toml index 9e5e0a96b73..fc65355c047 100644 --- a/litellm-rust/Cargo.toml +++ b/litellm-rust/Cargo.toml @@ -104,6 +104,7 @@ serde_with = { version = "=3.21.0", default-features = false, features = ["std", sha2 = "0.10" syn = { version = "2", default-features = false } sqlx = { version = "0.9.0", default-features = false, features = ["json", "macros", "postgres", "runtime-tokio", "chrono", "tls-rustls-ring-native-roots"] } +chrono = { version = "0.4.45", default-features = false, features = ["std"] } proc-macro2 = "1" quote = "1" subtle = "2" diff --git a/litellm-rust/crates/db/.sqlx/query-377d063cd0fc3a265d07d01f5aa64311d25428fd393bac4250891f148597e92e.json b/litellm-rust/crates/db/.sqlx/query-377d063cd0fc3a265d07d01f5aa64311d25428fd393bac4250891f148597e92e.json new file mode 100644 index 00000000000..734935466ff --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-377d063cd0fc3a265d07d01f5aa64311d25428fd393bac4250891f148597e92e.json @@ -0,0 +1,78 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT e.spend AS \"spend?\",\n e.blocked AS \"blocked?\",\n b.max_budget AS \"max_budget?\",\n b.rpm_limit AS \"rpm_limit?\",\n b.tpm_limit AS \"tpm_limit?\"\nFROM (SELECT 1) AS one\nLEFT JOIN \"LiteLLM_EndUserTable\" AS e ON e.user_id = $1\nLEFT JOIN \"LiteLLM_BudgetTable\" AS assigned ON assigned.budget_id = e.budget_id\nLEFT JOIN \"LiteLLM_BudgetTable\" AS key_budget ON key_budget.budget_id = $3\nLEFT JOIN \"LiteLLM_BudgetTable\" AS b ON b.budget_id = COALESCE(assigned.budget_id, key_budget.budget_id, $2)\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "spend?", + "type_info": "Float8", + "origin": { + "Table": { + "table": "\"LiteLLM_EndUserTable\"", + "name": "spend" + } + } + }, + { + "ordinal": 1, + "name": "blocked?", + "type_info": "Bool", + "origin": { + "Table": { + "table": "\"LiteLLM_EndUserTable\"", + "name": "blocked" + } + } + }, + { + "ordinal": 2, + "name": "max_budget?", + "type_info": "Float8", + "origin": { + "Table": { + "table": "\"LiteLLM_BudgetTable\"", + "name": "max_budget" + } + } + }, + { + "ordinal": 3, + "name": "rpm_limit?", + "type_info": "Int8", + "origin": { + "Table": { + "table": "\"LiteLLM_BudgetTable\"", + "name": "rpm_limit" + } + } + }, + { + "ordinal": 4, + "name": "tpm_limit?", + "type_info": "Int8", + "origin": { + "Table": { + "table": "\"LiteLLM_BudgetTable\"", + "name": "tpm_limit" + } + } + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + true, + true, + true, + true, + true + ] + }, + "hash": "377d063cd0fc3a265d07d01f5aa64311d25428fd393bac4250891f148597e92e" +} diff --git a/litellm-rust/crates/db/AGENTS.md b/litellm-rust/crates/db/AGENTS.md new file mode 100644 index 00000000000..1e08317230e --- /dev/null +++ b/litellm-rust/crates/db/AGENTS.md @@ -0,0 +1,25 @@ +# Database crate + +Keep each static query in `queries/` and call it through a typed SQLx query function. Rust does not create or migrate the database + +- Own reusable row projections in `src/` and export those consumed by other crates or integration tests +- Apply `#[macro_rules_attribute::apply(crate::db_row)]` to row types for shared derives, without adding serialization or schema derives automatically +- Apply `#[macro_rules_attribute::apply(crate::db_input)]` to write inputs, adding `Default` only where supported +- Keep function tests inline in their owning query module and reuse the strict database fixture in `test_support` +- Transaction-local setup in `begin` is the approved exception to static query files +- Keep write inputs separate from stored rows and keep test-only assertions out of public types +- `FromRow` supports runtime reads, but production static reads still use `query_file_as!` for compile-time checking + +Database integration tests are ignored by default, but still compile in workspace checks. Run them explicitly against a local database already migrated by Prisma. Missing or invalid database configuration must fail the selected tests: + +```sh +DATABASE_URL=postgresql://... cargo test -p litellm-db --locked -- --include-ignored +``` + +After changing a query, refresh the checked query metadata from this directory: + +```sh +SQLX_OFFLINE=false cargo sqlx prepare +``` + +Tests use transactions and leave them uncommitted so fixture rows roll back when each test finishes diff --git a/litellm-rust/crates/db/Cargo.toml b/litellm-rust/crates/db/Cargo.toml index ca9d6f555b0..bb6d5655b60 100644 --- a/litellm-rust/crates/db/Cargo.toml +++ b/litellm-rust/crates/db/Cargo.toml @@ -6,5 +6,12 @@ license.workspace = true repository.workspace = true [dependencies] +chrono.workspace = true +macro_rules_attribute.workspace = true serde.workspace = true sqlx.workspace = true +thiserror.workspace = true + +[dev-dependencies] +rstest.workspace = true +tokio.workspace = true diff --git a/litellm-rust/crates/db/queries/end_user/find_end_user.sql b/litellm-rust/crates/db/queries/end_user/find_end_user.sql new file mode 100644 index 00000000000..c10ef8b877d --- /dev/null +++ b/litellm-rust/crates/db/queries/end_user/find_end_user.sql @@ -0,0 +1,10 @@ +SELECT e.spend AS "spend?", + e.blocked AS "blocked?", + b.max_budget AS "max_budget?", + b.rpm_limit AS "rpm_limit?", + b.tpm_limit AS "tpm_limit?" +FROM (SELECT 1) AS one +LEFT JOIN "LiteLLM_EndUserTable" AS e ON e.user_id = $1 +LEFT JOIN "LiteLLM_BudgetTable" AS assigned ON assigned.budget_id = e.budget_id +LEFT JOIN "LiteLLM_BudgetTable" AS key_budget ON key_budget.budget_id = $3 +LEFT JOIN "LiteLLM_BudgetTable" AS b ON b.budget_id = COALESCE(assigned.budget_id, key_budget.budget_id, $2) diff --git a/litellm-rust/crates/db/src/daily_spend.rs b/litellm-rust/crates/db/src/daily_spend.rs new file mode 100644 index 00000000000..9db07514aab --- /dev/null +++ b/litellm-rust/crates/db/src/daily_spend.rs @@ -0,0 +1,76 @@ +use sqlx::types::chrono::NaiveDateTime; + +#[macro_rules_attribute::apply(crate::db_row)] +pub struct DailyUserRow { + pub id: String, + pub user_id: Option, + pub api_key: String, + pub model: Option, + pub custom_llm_provider: Option, + pub endpoint: Option, + pub model_group: Option, + pub mcp_namespaced_tool_name: Option, + pub prompt_tokens: i64, + pub completion_tokens: i64, + pub api_requests: i64, + pub successful_requests: i64, + pub failed_requests: i64, + pub cache_read_input_tokens: i64, + pub cache_creation_input_tokens: i64, + pub total_response_time_ms: i64, + pub timed_requests: i64, + pub spend: f64, + pub date: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + +#[macro_rules_attribute::apply(crate::db_row)] +pub struct DailyTeamRow { + pub id: String, + pub team_id: Option, + pub api_key: String, + pub model: Option, + pub custom_llm_provider: Option, + pub endpoint: Option, + pub model_group: Option, + pub mcp_namespaced_tool_name: Option, + pub prompt_tokens: i64, + pub completion_tokens: i64, + pub api_requests: i64, + pub successful_requests: i64, + pub failed_requests: i64, + pub cache_read_input_tokens: i64, + pub cache_creation_input_tokens: i64, + pub total_response_time_ms: i64, + pub timed_requests: i64, + pub spend: f64, + pub date: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + +#[macro_rules_attribute::apply(crate::db_row)] +pub struct DailyEndUserRow { + pub id: String, + pub end_user_id: Option, + pub api_key: String, + pub model: Option, + pub custom_llm_provider: Option, + pub endpoint: Option, + pub model_group: Option, + pub mcp_namespaced_tool_name: Option, + pub prompt_tokens: i64, + pub completion_tokens: i64, + pub api_requests: i64, + pub successful_requests: i64, + pub failed_requests: i64, + pub cache_read_input_tokens: i64, + pub cache_creation_input_tokens: i64, + pub total_response_time_ms: i64, + pub timed_requests: i64, + pub spend: f64, + pub date: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} diff --git a/litellm-rust/crates/db/src/end_user.rs b/litellm-rust/crates/db/src/end_user.rs new file mode 100644 index 00000000000..66661b51640 --- /dev/null +++ b/litellm-rust/crates/db/src/end_user.rs @@ -0,0 +1,359 @@ +use sqlx::PgExecutor; + +use crate::Error; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct RateLimits { + pub rpm: Option, + pub tpm: Option, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct EndUser { + pub spend: Option, + pub blocked: bool, + pub max_budget: Option, + pub limits: RateLimits, +} + +#[macro_rules_attribute::apply(crate::db_row)] +struct EndUserRow { + spend: Option, + blocked: Option, + max_budget: Option, + rpm_limit: Option, + tpm_limit: Option, +} + +pub async fn find_end_user( + executor: impl PgExecutor<'_>, + end_user_id: &str, + default_budget_id: Option<&str>, + key_budget_id: Option<&str>, +) -> Result { + let row = sqlx::query_file_as!( + EndUserRow, + "queries/end_user/find_end_user.sql", + end_user_id, + default_budget_id, + key_budget_id, + ) + .fetch_one(executor) + .await?; + Ok(EndUser { + spend: row.spend, + blocked: row.blocked.unwrap_or(false), + max_budget: row.max_budget, + limits: RateLimits { + rpm: row.rpm_limit, + tpm: row.tpm_limit, + }, + }) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{EndUser, RateLimits, find_end_user}; + use crate::test_support::database_pool; + use rstest::rstest; + use sqlx::{PgPool, Postgres, Transaction}; + + #[derive(Clone, Copy)] + struct BudgetFixture { + budget_id: &'static str, + max_budget: f64, + rpm_limit: i64, + tpm_limit: i64, + } + + async fn insert_budget( + tx: &mut Transaction<'_, Postgres>, + budget: BudgetFixture, + ) -> Result<(), sqlx::Error> { + #[expect( + clippy::disallowed_methods, + reason = "Fixture inserts are outside the compile-checked query set" + )] + let result = sqlx::query( + r#" + INSERT INTO "LiteLLM_BudgetTable" + (budget_id, max_budget, rpm_limit, tpm_limit, created_by, updated_by) + VALUES ($1, $2, $3, $4, 'db-query-test', 'db-query-test') + "#, + ) + .bind(budget.budget_id) + .bind(budget.max_budget) + .bind(budget.rpm_limit) + .bind(budget.tpm_limit) + .execute(&mut **tx) + .await; + result.map(|_| ()) + } + + async fn insert_end_user( + tx: &mut Transaction<'_, Postgres>, + user_id: &str, + spend: f64, + blocked: bool, + budget_id: Option<&str>, + ) -> Result<(), sqlx::Error> { + #[expect( + clippy::disallowed_methods, + reason = "Fixture inserts are outside the compile-checked query set" + )] + let result = sqlx::query( + r#" + INSERT INTO "LiteLLM_EndUserTable" (user_id, spend, blocked, budget_id) + VALUES ($1, $2, $3, $4) + "#, + ) + .bind(user_id) + .bind(spend) + .bind(blocked) + .bind(budget_id) + .execute(&mut **tx) + .await; + result.map(|_| ()) + } + + #[rstest] + #[case::without_default_budget(None)] + #[case::with_default_budget(Some(BudgetFixture { + budget_id: "missing-user-default-budget", + max_budget: 11.5, + rpm_limit: 12, + tpm_limit: 1300, + }))] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn missing_end_user_maps_to_defaults( + #[case] default_budget: Option, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + if let Some(budget) = default_budget { + insert_budget(&mut tx, budget).await?; + } + + let actual = find_end_user( + &mut *tx, + "missing-end-user", + default_budget.map(|budget| budget.budget_id), + None, + ) + .await?; + + assert_eq!( + actual, + EndUser { + spend: None, + blocked: false, + max_budget: default_budget.map(|budget| budget.max_budget), + limits: RateLimits { + rpm: default_budget.map(|budget| budget.rpm_limit), + tpm: default_budget.map(|budget| budget.tpm_limit), + }, + } + ); + Ok(()) + } + + #[rstest] + #[case::row_budget_precedes_default( + "row-budget-priority-user", + Some(BudgetFixture { + budget_id: "row-budget-priority-row", + max_budget: 10.0, + rpm_limit: 101, + tpm_limit: 1001, + }), + BudgetFixture { + budget_id: "row-budget-priority-default", + max_budget: 20.0, + rpm_limit: 201, + tpm_limit: 2001, + }, + None, + Some(BudgetFixture { + budget_id: "row-budget-priority-row", + max_budget: 10.0, + rpm_limit: 101, + tpm_limit: 1001, + }), + false + )] + #[case::default_applies_without_row_budget( + "default-budget-user", + None, + BudgetFixture { + budget_id: "default-budget-fallback", + max_budget: 30.0, + rpm_limit: 301, + tpm_limit: 3001, + }, + None, + Some(BudgetFixture { + budget_id: "default-budget-fallback", + max_budget: 30.0, + rpm_limit: 301, + tpm_limit: 3001, + }), + false + )] + #[case::assigned_budget_precedes_key_when_all_ids_differ( + "override-priority-user", + Some(BudgetFixture { + budget_id: "override-priority-row", + max_budget: 40.0, + rpm_limit: 401, + tpm_limit: 4001, + }), + BudgetFixture { + budget_id: "override-priority-default", + max_budget: 50.0, + rpm_limit: 501, + tpm_limit: 5001, + }, + Some(BudgetFixture { + budget_id: "override-priority-override", + max_budget: 60.0, + rpm_limit: 601, + tpm_limit: 6001, + }), + Some(BudgetFixture { + budget_id: "override-priority-row", + max_budget: 40.0, + rpm_limit: 401, + tpm_limit: 4001, + }), + true + )] + #[case::missing_key_budget_keeps_assigned_budget( + "dangling-override-user", + Some(BudgetFixture { + budget_id: "dangling-override-row", + max_budget: 70.0, + rpm_limit: 701, + tpm_limit: 7001, + }), + BudgetFixture { + budget_id: "dangling-override-default", + max_budget: 80.0, + rpm_limit: 801, + tpm_limit: 8001, + }, + Some(BudgetFixture { + budget_id: "dangling-override-missing", + max_budget: 90.0, + rpm_limit: 901, + tpm_limit: 9001, + }), + Some(BudgetFixture { + budget_id: "dangling-override-row", + max_budget: 70.0, + rpm_limit: 701, + tpm_limit: 7001, + }), + false + )] + #[case::key_budget_precedes_proxy_default( + "key-default-user", + None, + BudgetFixture { + budget_id: "key-default-proxy", + max_budget: 80.0, + rpm_limit: 801, + tpm_limit: 8001, + }, + Some(BudgetFixture { + budget_id: "key-default-key", + max_budget: 90.0, + rpm_limit: 901, + tpm_limit: 9001, + }), + Some(BudgetFixture { + budget_id: "key-default-key", + max_budget: 90.0, + rpm_limit: 901, + tpm_limit: 9001, + }), + true + )] + #[case::missing_key_budget_falls_back_to_proxy_default( + "missing-key-default-user", + None, + BudgetFixture { + budget_id: "missing-key-default-proxy", + max_budget: 80.0, + rpm_limit: 801, + tpm_limit: 8001, + }, + Some(BudgetFixture { + budget_id: "missing-key-default-key", + max_budget: 90.0, + rpm_limit: 901, + tpm_limit: 9001, + }), + Some(BudgetFixture { + budget_id: "missing-key-default-proxy", + max_budget: 80.0, + rpm_limit: 801, + tpm_limit: 8001, + }), + false + )] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn budget_resolution_matches_python_precedence( + #[case] user_id: &'static str, + #[case] user_budget: Option, + #[case] default_budget: BudgetFixture, + #[case] override_budget: Option, + #[case] expected_budget: Option, + #[case] insert_override_budget: bool, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + insert_budget(&mut tx, default_budget).await?; + if let Some(budget) = user_budget { + insert_budget(&mut tx, budget).await?; + } + if let (Some(budget), true) = (override_budget, insert_override_budget) { + insert_budget(&mut tx, budget).await?; + } + insert_end_user( + &mut tx, + user_id, + 7.5, + true, + user_budget.map(|budget| budget.budget_id), + ) + .await?; + + let actual = find_end_user( + &mut *tx, + user_id, + Some(default_budget.budget_id), + override_budget.map(|budget| budget.budget_id), + ) + .await?; + + assert_eq!( + actual, + EndUser { + spend: Some(7.5), + blocked: true, + max_budget: expected_budget.map(|budget| budget.max_budget), + limits: RateLimits { + rpm: expected_budget.map(|budget| budget.rpm_limit), + tpm: expected_budget.map(|budget| budget.tpm_limit), + }, + } + ); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/error.rs b/litellm-rust/crates/db/src/error.rs new file mode 100644 index 00000000000..af0ee030896 --- /dev/null +++ b/litellm-rust/crates/db/src/error.rs @@ -0,0 +1,79 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("database query failed")] + Query(#[from] sqlx::Error), + #[error("spend batch has {ids} ids and {costs} costs")] + InvalidSpendBatch { ids: usize, costs: usize }, + #[error("invalid spend timestamp")] + InvalidTimestamp(#[from] chrono::ParseError), +} + +impl Error { + pub fn is_retryable(&self) -> bool { + match self { + Self::Query(sqlx::Error::Database(error)) => { + matches!( + error.code().as_deref(), + Some("40P01" | "55P03" | "40001" | "53300") + ) + } + Self::Query(sqlx::Error::Io(_) | sqlx::Error::PoolTimedOut) => true, + _ => false, + } + } +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use rstest::rstest; + use sqlx::PgPool; + + use super::Error; + use crate::test_support::database_pool; + + #[rstest] + #[case::closed_pool(sqlx::Error::PoolClosed, false)] + #[case::pool_timeout(sqlx::Error::PoolTimedOut, true)] + #[case::io(sqlx::Error::Io(std::io::ErrorKind::ConnectionReset.into()), true)] + #[case::missing_row(sqlx::Error::RowNotFound, false)] + fn classifies_client_errors(#[case] error: sqlx::Error, #[case] retryable: bool) { + assert_eq!(Error::from(error).is_retryable(), retryable); + } + + #[rstest] + #[case::connection_capacity("53300", true)] + #[case::deadlock("40P01", true)] + #[case::lock_unavailable("55P03", true)] + #[case::serialization_failure("40001", true)] + #[case::unique_violation("23505", false)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn classifies_server_errors( + #[case] code: &str, + #[case] retryable: bool, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test query must raise the requested SQLSTATE" + )] + let error = sqlx::query(sqlx::AssertSqlSafe(format!( + "DO $$ BEGIN RAISE EXCEPTION USING ERRCODE = '{code}'; END $$" + ))) + .execute(&mut *tx) + .await + .expect_err("PostgreSQL must raise the requested error"); + + assert_eq!( + error.as_database_error().and_then(|error| error.code()), + Some(code.into()) + ); + assert_eq!(Error::from(error).is_retryable(), retryable); + tx.rollback().await?; + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index b7ce211d906..ca5cd366eec 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -1,3 +1,19 @@ -mod failure; +macro_rules_attribute::attribute_alias! { + #[apply(db_input)] = + #[derive(Clone, Debug, PartialEq)]; + #[apply(db_row)] = + #[derive(Clone, Debug, PartialEq, sqlx::FromRow)]; +} + +mod daily_spend; +mod end_user; +mod error; +mod failure; +#[cfg(test)] +mod test_support; + +pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; +pub use end_user::{EndUser, RateLimits, find_end_user}; +pub use error::Error; pub use failure::DbFailure; diff --git a/litellm-rust/crates/db/src/test_support.rs b/litellm-rust/crates/db/src/test_support.rs new file mode 100644 index 00000000000..992be4a4447 --- /dev/null +++ b/litellm-rust/crates/db/src/test_support.rs @@ -0,0 +1,10 @@ +use std::error::Error as StdError; + +use rstest::fixture; +use sqlx::PgPool; + +#[fixture] +pub async fn database_pool() -> Result> { + let database_url = std::env::var("DATABASE_URL")?; + Ok(PgPool::connect(&database_url).await?) +} From 35cd5fc05f7e91c7bc9c91637e14452a3ed55360 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:04 -0700 Subject: [PATCH 42/73] feat(db): add typed key spend query (#48217) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...10731a0cacc1ae5b92eec32967f539948f563.json | 15 + .../crates/db/queries/spend/add_key_spend.sql | 16 + litellm-rust/crates/db/src/lib.rs | 2 + litellm-rust/crates/db/src/spend/key_spend.rs | 309 ++++++++++++++++++ litellm-rust/crates/db/src/spend/mod.rs | 3 + 5 files changed, 345 insertions(+) create mode 100644 litellm-rust/crates/db/.sqlx/query-3829a383d0ae50578f0fd75da3910731a0cacc1ae5b92eec32967f539948f563.json create mode 100644 litellm-rust/crates/db/queries/spend/add_key_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/key_spend.rs create mode 100644 litellm-rust/crates/db/src/spend/mod.rs diff --git a/litellm-rust/crates/db/.sqlx/query-3829a383d0ae50578f0fd75da3910731a0cacc1ae5b92eec32967f539948f563.json b/litellm-rust/crates/db/.sqlx/query-3829a383d0ae50578f0fd75da3910731a0cacc1ae5b92eec32967f539948f563.json new file mode 100644 index 00000000000..e85c0986a60 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-3829a383d0ae50578f0fd75da3910731a0cacc1ae5b92eec32967f539948f563.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH added AS (\n SELECT id, SUM(cost) AS cost\n FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost)\n GROUP BY id\n), locked AS (\n SELECT t.token FROM \"LiteLLM_VerificationToken\" t\n WHERE t.token IN (SELECT id FROM added)\n ORDER BY t.token FOR UPDATE\n)\nUPDATE \"LiteLLM_VerificationToken\" t\nSET spend = t.spend + added.cost,\n total_spend = t.total_spend + added.cost,\n last_active = (clock_timestamp() AT TIME ZONE 'UTC'),\n updated_at = (clock_timestamp() AT TIME ZONE 'UTC')\nFROM added\nWHERE t.token = added.id AND t.token IN (SELECT token FROM locked)\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "Float8Array" + ] + }, + "nullable": [] + }, + "hash": "3829a383d0ae50578f0fd75da3910731a0cacc1ae5b92eec32967f539948f563" +} diff --git a/litellm-rust/crates/db/queries/spend/add_key_spend.sql b/litellm-rust/crates/db/queries/spend/add_key_spend.sql new file mode 100644 index 00000000000..61850714acd --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/add_key_spend.sql @@ -0,0 +1,16 @@ +WITH added AS ( + SELECT id, SUM(cost) AS cost + FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost) + GROUP BY id +), locked AS ( + SELECT t.token FROM "LiteLLM_VerificationToken" t + WHERE t.token IN (SELECT id FROM added) + ORDER BY t.token FOR UPDATE +) +UPDATE "LiteLLM_VerificationToken" t +SET spend = t.spend + added.cost, + total_spend = t.total_spend + added.cost, + last_active = (clock_timestamp() AT TIME ZONE 'UTC'), + updated_at = (clock_timestamp() AT TIME ZONE 'UTC') +FROM added +WHERE t.token = added.id AND t.token IN (SELECT token FROM locked) diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index ca5cd366eec..b3abf5f5173 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -10,6 +10,7 @@ mod daily_spend; mod end_user; mod error; mod failure; +mod spend; #[cfg(test)] mod test_support; @@ -17,3 +18,4 @@ pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; +pub use spend::{EntitySpend, add_key_spend}; diff --git a/litellm-rust/crates/db/src/spend/key_spend.rs b/litellm-rust/crates/db/src/spend/key_spend.rs new file mode 100644 index 00000000000..5a6ba13307c --- /dev/null +++ b/litellm-rust/crates/db/src/spend/key_spend.rs @@ -0,0 +1,309 @@ +use sqlx::PgExecutor; +use std::collections::BTreeMap; + +use crate::Error; + +#[macro_rules_attribute::apply(crate::db_input)] +#[derive(Default)] +pub struct EntitySpend { + pub ids: Vec, + pub costs: Vec, +} + +impl EntitySpend { + pub(crate) fn validate(&self) -> Result<(), Error> { + if self.ids.len() != self.costs.len() { + return Err(Error::InvalidSpendBatch { + ids: self.ids.len(), + costs: self.costs.len(), + }); + } + Ok(()) + } + + pub fn is_empty(&self) -> bool { + self.ids.is_empty() + } +} + +impl FromIterator<(String, f64)> for EntitySpend { + fn from_iter>(entries: I) -> Self { + let entries = entries + .into_iter() + .fold(BTreeMap::new(), |mut sums, (id, cost)| { + *sums.entry(id).or_insert(0.0) += cost; + sums + }); + let (ids, costs) = entries.into_iter().unzip(); + Self { ids, costs } + } +} + +pub async fn add_key_spend( + executor: impl PgExecutor<'_>, + spend: &EntitySpend, +) -> Result<(), Error> { + spend.validate()?; + if spend.is_empty() { + return Ok(()); + } + sqlx::query_file!("queries/spend/add_key_spend.sql", &spend.ids, &spend.costs) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{EntitySpend, add_key_spend}; + use crate::{Error, test_support::database_pool}; + use rstest::rstest; + use sqlx::PgPool; + + #[rstest] + #[case::direct(false)] + #[case::collected(true)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn duplicate_key_spend_accumulates( + #[case] collected: bool, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + sqlx::query( + r#"INSERT INTO "LiteLLM_VerificationToken" (token, spend, total_spend, updated_at) + VALUES ('key-spend-duplicate', 2, 7, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = if collected { + [ + ("key-spend-duplicate".into(), 0.25), + ("key-spend-duplicate".into(), 0.75), + ] + .into_iter() + .collect() + } else { + EntitySpend { + ids: vec!["key-spend-duplicate".into(), "key-spend-duplicate".into()], + costs: vec![0.25, 0.75], + } + }; + add_key_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (f64, f64) = sqlx::query_as( + r#"SELECT spend, total_spend FROM "LiteLLM_VerificationToken" + WHERE token = 'key-spend-duplicate'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(row, (3.0, 8.0)); + Ok(()) + } + + #[rstest] + #[case::extra_id(vec!["key-spend-invalid".into(), "key-spend-invalid".into()], vec![0.25])] + #[case::extra_cost(vec!["key-spend-invalid".into()], vec![0.25, 0.75])] + #[case::empty_ids(vec![], vec![0.25])] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn malformed_key_spend_leaves_transaction_usable( + #[case] ids: Vec, + #[case] costs: Vec, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + sqlx::query( + r#"INSERT INTO "LiteLLM_VerificationToken" + (token, spend, total_spend, last_active, updated_at) + VALUES ('key-spend-invalid', 2, 7, NULL, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { ids, costs }; + let result = add_key_spend(&mut *tx, &spend).await; + assert!(matches!( + result, + Err(Error::InvalidSpendBatch { ids, costs }) + if ids == spend.ids.len() && costs == spend.costs.len() + )); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let unchanged: (f64, f64, bool, bool) = sqlx::query_as( + r#"SELECT spend, total_spend, last_active IS NULL, + updated_at = TIMESTAMP '2000-01-01' + FROM "LiteLLM_VerificationToken" WHERE token = 'key-spend-invalid'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(unchanged, (2.0, 7.0, true, true)); + let valid = EntitySpend { + ids: vec!["key-spend-invalid".into()], + costs: vec![0.5], + }; + add_key_spend(&mut *tx, &valid).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let updated: (f64, f64) = sqlx::query_as( + r#"SELECT spend, total_spend FROM "LiteLLM_VerificationToken" + WHERE token = 'key-spend-invalid'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(updated, (2.5, 7.5)); + Ok(()) + } + + #[rstest] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn key_activity_uses_update_time_in_old_transaction( + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + sqlx::query( + r#"INSERT INTO "LiteLLM_VerificationToken" (token, spend, total_spend, updated_at) + VALUES ('key-spend-old-transaction', 2, 7, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + #[expect( + clippy::disallowed_methods, + reason = "Transaction timing setup is outside the compile-checked query set" + )] + sqlx::query("SELECT pg_sleep(0.02)") + .execute(&mut *tx) + .await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let boundary: chrono::NaiveDateTime = sqlx::query_scalar( + "SELECT date_trunc('milliseconds', clock_timestamp() AT TIME ZONE 'UTC')", + ) + .fetch_one(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec!["key-spend-old-transaction".into()], + costs: vec![0.25], + }; + add_key_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (chrono::NaiveDateTime, chrono::NaiveDateTime) = sqlx::query_as( + r#"SELECT last_active, updated_at FROM "LiteLLM_VerificationToken" + WHERE token = 'key-spend-old-transaction'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert!(row.0 >= boundary, "last_active predates update boundary"); + assert!(row.1 >= boundary, "updated_at predates update boundary"); + Ok(()) + } + + #[rstest] + #[case::fractional_costs(0.25, 2.5, 7.5, 4.0, 12.0)] + #[case::zero_costs(0.0, 2.0, 7.0, 3.0, 11.0)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn key_spend_matches_python_update_many( + #[case] cost: f64, + #[case] expected_a_spend: f64, + #[case] expected_a_total: f64, + #[case] expected_z_spend: f64, + #[case] expected_z_total: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_VerificationToken" + (token, spend, total_spend, last_active, updated_at) + VALUES ('key-spend-a', 2, 7, NULL, '2000-01-01'), + ('key-spend-z', 3, 11, NULL, '2000-01-01'), + ('key-spend-untouched', 100, 200, NULL, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec![ + "key-spend-z".into(), + "key-spend-a".into(), + "key-spend-missing".into(), + ], + costs: vec![2.0 * cost, cost, 50.0], + }; + add_key_spend(&mut *tx, &EntitySpend::default()).await?; + add_key_spend(&mut *tx, &spend).await?; + add_key_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64, f64, bool, bool)> = sqlx::query_as( + r#"SELECT token, spend, total_spend, last_active IS NOT NULL, + updated_at > TIMESTAMP '2000-01-01' + FROM "LiteLLM_VerificationToken" + WHERE token = ANY($1) ORDER BY token"#, + ) + .bind(vec![ + "key-spend-a", + "key-spend-z", + "key-spend-missing", + "key-spend-untouched", + ]) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ( + "key-spend-a".into(), + expected_a_spend, + expected_a_total, + true, + true + ), + ("key-spend-untouched".into(), 100.0, 200.0, false, false), + ( + "key-spend-z".into(), + expected_z_spend, + expected_z_total, + true, + true + ), + ] + ); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs new file mode 100644 index 00000000000..e34886c6130 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -0,0 +1,3 @@ +mod key_spend; + +pub use key_spend::{EntitySpend, add_key_spend}; From 8e61c22fe0be640b4cfb0d22c9bfa178c250cc45 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:04 -0700 Subject: [PATCH 43/73] feat(db): add typed user spend query (#48229) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...3b87a20a2664b0bef3616631d2c24487c1e54.json | 15 ++ .../db/queries/spend/add_user_spend.sql | 14 ++ litellm-rust/crates/db/src/lib.rs | 2 +- litellm-rust/crates/db/src/spend/mod.rs | 2 + .../crates/db/src/spend/user_spend.rs | 173 ++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-43f2e12a3ffb1de29b5b031a1173b87a20a2664b0bef3616631d2c24487c1e54.json create mode 100644 litellm-rust/crates/db/queries/spend/add_user_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/user_spend.rs diff --git a/litellm-rust/crates/db/.sqlx/query-43f2e12a3ffb1de29b5b031a1173b87a20a2664b0bef3616631d2c24487c1e54.json b/litellm-rust/crates/db/.sqlx/query-43f2e12a3ffb1de29b5b031a1173b87a20a2664b0bef3616631d2c24487c1e54.json new file mode 100644 index 00000000000..a758ae5126a --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-43f2e12a3ffb1de29b5b031a1173b87a20a2664b0bef3616631d2c24487c1e54.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH added AS (\n SELECT id, SUM(cost) AS cost\n FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost)\n GROUP BY id\n), locked AS (\n SELECT u.user_id FROM \"LiteLLM_UserTable\" u\n WHERE u.user_id IN (SELECT id FROM added)\n ORDER BY u.user_id FOR UPDATE\n)\nUPDATE \"LiteLLM_UserTable\" u\nSET spend = u.spend + added.cost,\n updated_at = (NOW() AT TIME ZONE 'UTC')\nFROM added\nWHERE u.user_id = added.id AND u.user_id IN (SELECT user_id FROM locked)\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "Float8Array" + ] + }, + "nullable": [] + }, + "hash": "43f2e12a3ffb1de29b5b031a1173b87a20a2664b0bef3616631d2c24487c1e54" +} diff --git a/litellm-rust/crates/db/queries/spend/add_user_spend.sql b/litellm-rust/crates/db/queries/spend/add_user_spend.sql new file mode 100644 index 00000000000..27fc12e82f0 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/add_user_spend.sql @@ -0,0 +1,14 @@ +WITH added AS ( + SELECT id, SUM(cost) AS cost + FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost) + GROUP BY id +), locked AS ( + SELECT u.user_id FROM "LiteLLM_UserTable" u + WHERE u.user_id IN (SELECT id FROM added) + ORDER BY u.user_id FOR UPDATE +) +UPDATE "LiteLLM_UserTable" u +SET spend = u.spend + added.cost, + updated_at = (NOW() AT TIME ZONE 'UTC') +FROM added +WHERE u.user_id = added.id AND u.user_id IN (SELECT user_id FROM locked) diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index b3abf5f5173..6589b46b24a 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -18,4 +18,4 @@ pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; -pub use spend::{EntitySpend, add_key_spend}; +pub use spend::{EntitySpend, add_key_spend, add_user_spend}; diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index e34886c6130..98530805fdd 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,3 +1,5 @@ mod key_spend; +mod user_spend; pub use key_spend::{EntitySpend, add_key_spend}; +pub use user_spend::add_user_spend; diff --git a/litellm-rust/crates/db/src/spend/user_spend.rs b/litellm-rust/crates/db/src/spend/user_spend.rs new file mode 100644 index 00000000000..2c89c04c1ef --- /dev/null +++ b/litellm-rust/crates/db/src/spend/user_spend.rs @@ -0,0 +1,173 @@ +use sqlx::PgExecutor; + +use super::EntitySpend; +use crate::Error; + +pub async fn add_user_spend( + executor: impl PgExecutor<'_>, + spend: &EntitySpend, +) -> Result<(), Error> { + spend.validate()?; + if spend.is_empty() { + return Ok(()); + } + sqlx::query_file!("queries/spend/add_user_spend.sql", &spend.ids, &spend.costs) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{EntitySpend, add_user_spend}; + use crate::{Error, test_support::database_pool}; + use rstest::rstest; + use sqlx::PgPool; + + #[rstest] + #[case::fractional_costs(0.25, 2.5, 4.0)] + #[case::zero_costs(0.0, 2.0, 3.0)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn user_spend_matches_python_update_many( + #[case] cost: f64, + #[case] expected_a_spend: f64, + #[case] expected_z_spend: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_UserTable" (user_id, spend, updated_at) + VALUES ('user-spend-a', 2, '2000-01-01'), + ('user-spend-z', 3, '2000-01-01'), + ('user-spend-untouched', 100, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec![ + "user-spend-z".into(), + "user-spend-a".into(), + "user-spend-missing".into(), + ], + costs: vec![2.0 * cost, cost, 50.0], + }; + add_user_spend(&mut *tx, &EntitySpend::default()).await?; + add_user_spend(&mut *tx, &spend).await?; + add_user_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64, bool)> = sqlx::query_as( + r#"SELECT user_id, spend, updated_at > TIMESTAMP '2000-01-01' + FROM "LiteLLM_UserTable" + WHERE user_id = ANY($1) ORDER BY user_id"#, + ) + .bind(vec![ + "user-spend-a", + "user-spend-z", + "user-spend-missing", + "user-spend-untouched", + ]) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ("user-spend-a".into(), expected_a_spend, true), + ("user-spend-untouched".into(), 100.0, false), + ("user-spend-z".into(), expected_z_spend, true), + ] + ); + Ok(()) + } + + #[rstest] + #[case::direct_duplicate_batch(vec![0.25, 0.75], 3.0)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn user_spend_sums_duplicate_ids( + #[case] costs: Vec, + #[case] expected_spend: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_UserTable" (user_id, spend, updated_at) + VALUES ('user-spend-duplicate', 2, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec!["user-spend-duplicate".into(), "user-spend-duplicate".into()], + costs, + }; + add_user_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (f64, bool) = sqlx::query_as( + r#"SELECT spend, updated_at > TIMESTAMP '2000-01-01' + FROM "LiteLLM_UserTable" WHERE user_id = 'user-spend-duplicate'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(row, (expected_spend, true)); + Ok(()) + } + + #[rstest] + #[case::missing_cost(vec![])] + #[case::extra_cost(vec![0.25, 0.75])] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn user_spend_rejects_malformed_batch_without_writes( + #[case] costs: Vec, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_UserTable" (user_id, spend, updated_at) + VALUES ('user-spend-malformed', 2, '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec!["user-spend-malformed".into()], + costs, + }; + let result = add_user_spend(&mut *tx, &spend).await; + assert!(matches!(result, Err(Error::InvalidSpendBatch { .. }))); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (f64, bool) = sqlx::query_as( + r#"SELECT spend, updated_at = TIMESTAMP '2000-01-01' + FROM "LiteLLM_UserTable" WHERE user_id = 'user-spend-malformed'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(row, (2.0, true)); + Ok(()) + } +} From 3b72c08c4fc15a4607914652b54c21df71b530a1 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:05 -0700 Subject: [PATCH 44/73] feat(db): add typed team spend query (#48230) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...edd37a575d641bb68d91e61f5123173c275ce.json | 15 ++ .../db/queries/spend/add_team_spend.sql | 15 ++ litellm-rust/crates/db/src/lib.rs | 2 +- litellm-rust/crates/db/src/spend/mod.rs | 2 + .../crates/db/src/spend/team_spend.rs | 182 ++++++++++++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-c3e5542906e89995fae2a16eca1edd37a575d641bb68d91e61f5123173c275ce.json create mode 100644 litellm-rust/crates/db/queries/spend/add_team_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/team_spend.rs diff --git a/litellm-rust/crates/db/.sqlx/query-c3e5542906e89995fae2a16eca1edd37a575d641bb68d91e61f5123173c275ce.json b/litellm-rust/crates/db/.sqlx/query-c3e5542906e89995fae2a16eca1edd37a575d641bb68d91e61f5123173c275ce.json new file mode 100644 index 00000000000..250971b053a --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-c3e5542906e89995fae2a16eca1edd37a575d641bb68d91e61f5123173c275ce.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH added AS (\n SELECT id, SUM(cost) AS cost\n FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost)\n GROUP BY id\n),\nlocked AS (\n SELECT t.team_id FROM \"LiteLLM_TeamTable\" t\n WHERE t.team_id IN (SELECT id FROM added)\n ORDER BY t.team_id FOR UPDATE\n)\nUPDATE \"LiteLLM_TeamTable\" t\nSET spend = t.spend + added.cost,\n updated_at = (NOW() AT TIME ZONE 'UTC')\nFROM added\nWHERE t.team_id = added.id AND t.team_id IN (SELECT team_id FROM locked)\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "Float8Array" + ] + }, + "nullable": [] + }, + "hash": "c3e5542906e89995fae2a16eca1edd37a575d641bb68d91e61f5123173c275ce" +} diff --git a/litellm-rust/crates/db/queries/spend/add_team_spend.sql b/litellm-rust/crates/db/queries/spend/add_team_spend.sql new file mode 100644 index 00000000000..58b8e3d6ab9 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/add_team_spend.sql @@ -0,0 +1,15 @@ +WITH added AS ( + SELECT id, SUM(cost) AS cost + FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost) + GROUP BY id +), +locked AS ( + SELECT t.team_id FROM "LiteLLM_TeamTable" t + WHERE t.team_id IN (SELECT id FROM added) + ORDER BY t.team_id FOR UPDATE +) +UPDATE "LiteLLM_TeamTable" t +SET spend = t.spend + added.cost, + updated_at = (NOW() AT TIME ZONE 'UTC') +FROM added +WHERE t.team_id = added.id AND t.team_id IN (SELECT team_id FROM locked) diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 6589b46b24a..1dda3a987b0 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -18,4 +18,4 @@ pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; -pub use spend::{EntitySpend, add_key_spend, add_user_spend}; +pub use spend::{EntitySpend, add_key_spend, add_team_spend, add_user_spend}; diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 98530805fdd..775be335150 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,5 +1,7 @@ mod key_spend; +mod team_spend; mod user_spend; pub use key_spend::{EntitySpend, add_key_spend}; +pub use team_spend::add_team_spend; pub use user_spend::add_user_spend; diff --git a/litellm-rust/crates/db/src/spend/team_spend.rs b/litellm-rust/crates/db/src/spend/team_spend.rs new file mode 100644 index 00000000000..a105899bc2a --- /dev/null +++ b/litellm-rust/crates/db/src/spend/team_spend.rs @@ -0,0 +1,182 @@ +use sqlx::PgExecutor; + +use super::EntitySpend; +use crate::Error; + +pub async fn add_team_spend( + executor: impl PgExecutor<'_>, + spend: &EntitySpend, +) -> Result<(), Error> { + spend.validate()?; + if spend.is_empty() { + return Ok(()); + } + sqlx::query_file!("queries/spend/add_team_spend.sql", &spend.ids, &spend.costs) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{EntitySpend, add_team_spend}; + use crate::Error; + use crate::test_support::database_pool; + use rstest::rstest; + use sqlx::PgPool; + + #[rstest] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn duplicate_team_ids_add_every_cost( + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_TeamTable" (team_id, spend, created_at, updated_at) + VALUES ('team-spend-duplicate-a', 2, '2000-01-01', '2000-01-01'), + ('team-spend-duplicate-z', 3, '2000-01-01', '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec![ + "team-spend-duplicate-z".into(), + "team-spend-duplicate-a".into(), + "team-spend-duplicate-z".into(), + "team-spend-duplicate-a".into(), + ], + costs: vec![0.5, 0.25, 1.0, 0.5], + }; + add_team_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64)> = sqlx::query_as( + r#"SELECT team_id, spend FROM "LiteLLM_TeamTable" + WHERE team_id = ANY($1) ORDER BY team_id"#, + ) + .bind(&spend.ids) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ("team-spend-duplicate-a".into(), 2.75), + ("team-spend-duplicate-z".into(), 4.5), + ] + ); + Ok(()) + } + + #[rstest] + #[case::missing_cost(vec!["team-spend-malformed".into()], vec![])] + #[case::extra_cost(vec!["team-spend-malformed".into()], vec![0.25, 0.5])] + #[case::empty_ids(vec![], vec![0.25])] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn malformed_team_batch_does_not_write( + #[case] ids: Vec, + #[case] costs: Vec, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_TeamTable" (team_id, spend, created_at, updated_at) + VALUES ('team-spend-malformed', 2, '2000-01-01', '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let result = add_team_spend(&mut *tx, &EntitySpend { ids, costs }).await; + assert!(matches!(result, Err(Error::InvalidSpendBatch { .. }))); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (f64, bool) = sqlx::query_as( + r#"SELECT spend, updated_at = TIMESTAMP '2000-01-01' + FROM "LiteLLM_TeamTable" WHERE team_id = 'team-spend-malformed'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(row, (2.0, true)); + Ok(()) + } + + #[rstest] + #[case::fractional_costs(0.25, 2.5, 4.0)] + #[case::zero_costs(0.0, 2.0, 3.0)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn team_spend_matches_python_update_many( + #[case] cost: f64, + #[case] expected_a_spend: f64, + #[case] expected_z_spend: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _inserted = sqlx::query( + r#"INSERT INTO "LiteLLM_TeamTable" (team_id, spend, created_at, updated_at) + VALUES ('team-spend-a', 2, '2000-01-01', '2000-01-01'), + ('team-spend-z', 3, '2000-01-01', '2000-01-01'), + ('team-spend-untouched', 100, '2000-01-01', '2000-01-01')"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { + ids: vec![ + "team-spend-z".into(), + "team-spend-a".into(), + "team-spend-missing".into(), + ], + costs: vec![2.0 * cost, cost, 50.0], + }; + add_team_spend(&mut *tx, &EntitySpend::default()).await?; + add_team_spend(&mut *tx, &spend).await?; + add_team_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64, bool)> = sqlx::query_as( + r#"SELECT team_id, spend, updated_at > TIMESTAMP '2000-01-01' + FROM "LiteLLM_TeamTable" + WHERE team_id = ANY($1) ORDER BY team_id"#, + ) + .bind(vec![ + "team-spend-a", + "team-spend-z", + "team-spend-missing", + "team-spend-untouched", + ]) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ("team-spend-a".into(), expected_a_spend, true), + ("team-spend-untouched".into(), 100.0, false), + ("team-spend-z".into(), expected_z_spend, true), + ] + ); + Ok(()) + } +} From 8f456eb7d51f52d62621e5672ee47ae9079e078d Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:05 -0700 Subject: [PATCH 45/73] feat(db): add typed end-user spend query (#48231) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...2288e0f2c1f12a2a79ed53f0002826724cd0a.json | 15 ++ .../db/queries/spend/add_end_user_spend.sql | 6 + litellm-rust/crates/db/src/lib.rs | 2 +- .../crates/db/src/spend/end_user_spend.rs | 197 ++++++++++++++++++ litellm-rust/crates/db/src/spend/mod.rs | 2 + 5 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-345c02595f55a7f0274ef7214b02288e0f2c1f12a2a79ed53f0002826724cd0a.json create mode 100644 litellm-rust/crates/db/queries/spend/add_end_user_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/end_user_spend.rs diff --git a/litellm-rust/crates/db/.sqlx/query-345c02595f55a7f0274ef7214b02288e0f2c1f12a2a79ed53f0002826724cd0a.json b/litellm-rust/crates/db/.sqlx/query-345c02595f55a7f0274ef7214b02288e0f2c1f12a2a79ed53f0002826724cd0a.json new file mode 100644 index 00000000000..123ff15ade2 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-345c02595f55a7f0274ef7214b02288e0f2c1f12a2a79ed53f0002826724cd0a.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_EndUserTable\" (user_id, spend, blocked)\nSELECT id, SUM(cost), false FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost)\nGROUP BY id\nORDER BY id\nON CONFLICT (user_id) DO UPDATE\nSET spend = \"LiteLLM_EndUserTable\".spend + EXCLUDED.spend\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "Float8Array" + ] + }, + "nullable": [] + }, + "hash": "345c02595f55a7f0274ef7214b02288e0f2c1f12a2a79ed53f0002826724cd0a" +} diff --git a/litellm-rust/crates/db/queries/spend/add_end_user_spend.sql b/litellm-rust/crates/db/queries/spend/add_end_user_spend.sql new file mode 100644 index 00000000000..06a5b96d254 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/add_end_user_spend.sql @@ -0,0 +1,6 @@ +INSERT INTO "LiteLLM_EndUserTable" (user_id, spend, blocked) +SELECT id, SUM(cost), false FROM UNNEST($1::text[], $2::float8[]) AS a(id, cost) +GROUP BY id +ORDER BY id +ON CONFLICT (user_id) DO UPDATE +SET spend = "LiteLLM_EndUserTable".spend + EXCLUDED.spend diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 1dda3a987b0..3a9032d73dc 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -18,4 +18,4 @@ pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; -pub use spend::{EntitySpend, add_key_spend, add_team_spend, add_user_spend}; +pub use spend::{EntitySpend, add_end_user_spend, add_key_spend, add_team_spend, add_user_spend}; diff --git a/litellm-rust/crates/db/src/spend/end_user_spend.rs b/litellm-rust/crates/db/src/spend/end_user_spend.rs new file mode 100644 index 00000000000..ca327ac7e18 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/end_user_spend.rs @@ -0,0 +1,197 @@ +use sqlx::PgExecutor; + +use super::EntitySpend; +use crate::Error; + +pub async fn add_end_user_spend( + executor: impl PgExecutor<'_>, + spend: &EntitySpend, +) -> Result<(), Error> { + spend.validate()?; + if spend.is_empty() { + return Ok(()); + } + sqlx::query_file!( + "queries/spend/add_end_user_spend.sql", + &spend.ids, + &spend.costs + ) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{EntitySpend, add_end_user_spend}; + use crate::test_support::database_pool; + use rstest::rstest; + use sqlx::PgPool; + + #[rstest] + #[case::fractional_costs(0.25, 0.75, false, 2.5, 1.5)] + #[case::zero_costs(0.0, 0.0, false, 2.0, 0.0)] + #[case::duplicate_ids(0.25, 0.75, true, 3.0, 3.0)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn end_user_spend_matches_python_upsert( + #[case] existing_cost: f64, + #[case] new_cost: f64, + #[case] duplicate_ids: bool, + #[case] expected_existing_spend: f64, + #[case] expected_new_spend: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _budget = sqlx::query( + r#"INSERT INTO "LiteLLM_BudgetTable" (budget_id, created_by, updated_by) + VALUES ('end-user-spend-budget', 'db-query-test', 'db-query-test')"#, + ) + .execute(&mut *tx) + .await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _users = sqlx::query( + r#"INSERT INTO "LiteLLM_EndUserTable" (user_id, spend, blocked, budget_id) + VALUES ('end-user-spend-z', 2, true, 'end-user-spend-budget'), + ('end-user-spend-untouched', 100, true, NULL)"#, + ) + .execute(&mut *tx) + .await?; + let spend = if duplicate_ids { + EntitySpend { + ids: vec![ + "end-user-spend-z".into(), + "end-user-spend-a".into(), + "end-user-spend-z".into(), + "end-user-spend-a".into(), + ], + costs: vec![existing_cost, new_cost, existing_cost, new_cost], + } + } else { + EntitySpend { + ids: vec!["end-user-spend-z".into(), "end-user-spend-a".into()], + costs: vec![existing_cost, new_cost], + } + }; + add_end_user_spend(&mut *tx, &spend).await?; + add_end_user_spend(&mut *tx, &spend).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64, bool, Option)> = sqlx::query_as( + r#"SELECT user_id, spend, blocked, budget_id FROM "LiteLLM_EndUserTable" + WHERE user_id = ANY($1) ORDER BY user_id"#, + ) + .bind(vec![ + "end-user-spend-a", + "end-user-spend-z", + "end-user-spend-untouched", + ]) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ("end-user-spend-a".into(), expected_new_spend, false, None), + ("end-user-spend-untouched".into(), 100.0, true, None), + ( + "end-user-spend-z".into(), + expected_existing_spend, + true, + Some("end-user-spend-budget".into()), + ), + ] + ); + Ok(()) + } + + #[rstest] + #[case::missing_cost(vec!["end-user-invalid-existing".into(), "end-user-invalid-new".into()], vec![1.0])] + #[case::extra_cost(vec!["end-user-invalid-existing".into()], vec![1.0, 2.0])] + #[case::empty_ids(vec![], vec![1.0])] + #[case::empty_costs(vec!["end-user-invalid-existing".into()], vec![])] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn malformed_end_user_spend_is_rejected_before_writes( + #[case] ids: Vec, + #[case] costs: Vec, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _user = sqlx::query( + r#"INSERT INTO "LiteLLM_EndUserTable" (user_id, spend, blocked) + VALUES ('end-user-invalid-existing', 8.5, true)"#, + ) + .execute(&mut *tx) + .await?; + let spend = EntitySpend { ids, costs }; + let result = add_end_user_spend(&mut *tx, &spend).await; + assert!(matches!( + result, + Err(crate::Error::InvalidSpendBatch { ids, costs }) + if ids == spend.ids.len() && costs == spend.costs.len() + )); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, f64, bool)> = sqlx::query_as( + r#"SELECT user_id, spend, blocked FROM "LiteLLM_EndUserTable" + WHERE user_id IN ('end-user-invalid-existing', 'end-user-invalid-new')"#, + ) + .fetch_all(&mut *tx) + .await?; + assert_eq!(rows, vec![("end-user-invalid-existing".into(), 8.5, true)]); + Ok(()) + } + + #[rstest] + #[case::empty_batch()] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn empty_end_user_spend_is_a_noop( + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _user = sqlx::query( + r#"INSERT INTO "LiteLLM_EndUserTable" (user_id, spend, blocked) + VALUES ('end-user-empty-spend', 8.5, true)"#, + ) + .execute(&mut *tx) + .await?; + add_end_user_spend(&mut *tx, &EntitySpend::default()).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let row: (f64, bool, Option) = sqlx::query_as( + r#"SELECT spend, blocked, budget_id FROM "LiteLLM_EndUserTable" + WHERE user_id = 'end-user-empty-spend'"#, + ) + .fetch_one(&mut *tx) + .await?; + assert_eq!(row, (8.5, true, None)); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 775be335150..5191da2adb5 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,7 +1,9 @@ +mod end_user_spend; mod key_spend; mod team_spend; mod user_spend; +pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; pub use team_spend::add_team_spend; pub use user_spend::add_user_spend; From c531692976b348dc4c44f9c6d235e0cf981632fe Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:06 -0700 Subject: [PATCH 46/73] feat(db): add transaction-scoped team lock query (#48232) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...7bb2d97532a9cd461cc5d8da1e1090560ca1f.json | 23 +++++ ...92bf33e16fea1dbb0c32cf89a376a874927f4.json | 21 ++++ .../crates/db/queries/spend/lock_team.sql | 1 + litellm-rust/crates/db/src/lib.rs | 5 +- litellm-rust/crates/db/src/spend/mod.rs | 2 + litellm-rust/crates/db/src/spend/team_lock.rs | 98 +++++++++++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-085edfb9af2a914466c75b447bc7bb2d97532a9cd461cc5d8da1e1090560ca1f.json create mode 100644 litellm-rust/crates/db/.sqlx/query-5c74f47a2b84574133c520d235192bf33e16fea1dbb0c32cf89a376a874927f4.json create mode 100644 litellm-rust/crates/db/queries/spend/lock_team.sql create mode 100644 litellm-rust/crates/db/src/spend/team_lock.rs diff --git a/litellm-rust/crates/db/.sqlx/query-085edfb9af2a914466c75b447bc7bb2d97532a9cd461cc5d8da1e1090560ca1f.json b/litellm-rust/crates/db/.sqlx/query-085edfb9af2a914466c75b447bc7bb2d97532a9cd461cc5d8da1e1090560ca1f.json new file mode 100644 index 00000000000..a415666d0be --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-085edfb9af2a914466c75b447bc7bb2d97532a9cd461cc5d8da1e1090560ca1f.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT pg_advisory_xact_lock(hashtext($1)) IS NULL AS \"locked!\"\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "locked!", + "type_info": "Bool", + "origin": "Expression" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "085edfb9af2a914466c75b447bc7bb2d97532a9cd461cc5d8da1e1090560ca1f" +} diff --git a/litellm-rust/crates/db/.sqlx/query-5c74f47a2b84574133c520d235192bf33e16fea1dbb0c32cf89a376a874927f4.json b/litellm-rust/crates/db/.sqlx/query-5c74f47a2b84574133c520d235192bf33e16fea1dbb0c32cf89a376a874927f4.json new file mode 100644 index 00000000000..7eef5278cb5 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-5c74f47a2b84574133c520d235192bf33e16fea1dbb0c32cf89a376a874927f4.json @@ -0,0 +1,21 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT set_config('lock_timeout', '5000ms', true)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "set_config", + "type_info": "Text", + "origin": "Expression" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "5c74f47a2b84574133c520d235192bf33e16fea1dbb0c32cf89a376a874927f4" +} diff --git a/litellm-rust/crates/db/queries/spend/lock_team.sql b/litellm-rust/crates/db/queries/spend/lock_team.sql new file mode 100644 index 00000000000..06cdb806ef0 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/lock_team.sql @@ -0,0 +1 @@ +SELECT pg_advisory_xact_lock(hashtext($1)) IS NULL AS "locked!" diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 3a9032d73dc..4f009b411fd 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -18,4 +18,7 @@ pub use daily_spend::{DailyEndUserRow, DailyTeamRow, DailyUserRow}; pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; -pub use spend::{EntitySpend, add_end_user_spend, add_key_spend, add_team_spend, add_user_spend}; +pub use spend::{ + EntitySpend, add_end_user_spend, add_key_spend, add_team_spend, add_user_spend, begin, + lock_team, +}; diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 5191da2adb5..0459e63aaf8 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,9 +1,11 @@ mod end_user_spend; mod key_spend; +mod team_lock; mod team_spend; mod user_spend; pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; +pub use team_lock::{begin, lock_team}; pub use team_spend::add_team_spend; pub use user_spend::add_user_spend; diff --git a/litellm-rust/crates/db/src/spend/team_lock.rs b/litellm-rust/crates/db/src/spend/team_lock.rs new file mode 100644 index 00000000000..7a6b9c7f7d0 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/team_lock.rs @@ -0,0 +1,98 @@ +use sqlx::{PgPool, Postgres, Transaction}; + +use crate::Error; + +pub async fn begin(pool: &PgPool) -> Result, Error> { + let mut transaction = pool.begin().await?; + sqlx::query!("SELECT set_config('lock_timeout', '5000ms', true)") + .fetch_one(&mut *transaction) + .await?; + Ok(transaction) +} + +pub async fn lock_team( + transaction: &mut Transaction<'_, Postgres>, + team_id: &str, +) -> Result<(), Error> { + sqlx::query_file!("queries/spend/lock_team.sql", team_id) + .fetch_one(&mut **transaction) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{begin, lock_team}; + use crate::test_support::database_pool; + use rstest::rstest; + use sqlx::{PgPool, postgres::PgPoolOptions}; + + #[rstest] + #[case::transaction_scoped("team-lock-scope")] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn team_lock_is_held_until_rollback( + #[case] team_id: &str, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let database_pool = database_pool?; + let pool = PgPoolOptions::new() + .max_connections(2) + .connect_with(database_pool.connect_options().as_ref().clone()) + .await?; + let mut first = begin(&pool).await?; + let mut second = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let original_timeout: String = sqlx::query_scalar("SELECT current_setting('lock_timeout')") + .fetch_one(&mut *second) + .await?; + lock_team(&mut first, team_id).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let (backend, timeout): (i32, String) = + sqlx::query_as("SELECT pg_backend_pid(), current_setting('lock_timeout')") + .fetch_one(&mut *first) + .await?; + assert_eq!(timeout, "5s"); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let held: bool = sqlx::query_scalar("SELECT pg_try_advisory_xact_lock(hashtext($1))") + .bind(team_id) + .fetch_one(&mut *second) + .await?; + assert!(!held); + first.rollback().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let released: bool = sqlx::query_scalar("SELECT pg_try_advisory_xact_lock(hashtext($1))") + .bind(team_id) + .fetch_one(&mut *second) + .await?; + assert!(released); + let mut next = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let (next_backend, next_timeout): (i32, String) = + sqlx::query_as("SELECT pg_backend_pid(), current_setting('lock_timeout')") + .fetch_one(&mut *next) + .await?; + assert_eq!(next_backend, backend); + assert_eq!(next_timeout, original_timeout); + next.rollback().await?; + second.rollback().await?; + Ok(()) + } +} From 46410c8da9589c716fcd2619ea4a0358dac1adc8 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:06 -0700 Subject: [PATCH 47/73] feat(db): add typed team-member spend upsert (#48233) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm-rust/Cargo.lock | 1 + ...27df60ff71d8fed05b53be4458b335af88147.json | 14 ++ litellm-rust/crates/db/Cargo.toml | 1 + .../spend/upsert_team_member_spend.sql | 15 ++ litellm-rust/crates/db/src/lib.rs | 4 +- litellm-rust/crates/db/src/spend/mod.rs | 2 + .../crates/db/src/spend/team_member_spend.rs | 162 ++++++++++++++++++ 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-b0eb48b795bdaf728c79e986d2027df60ff71d8fed05b53be4458b335af88147.json create mode 100644 litellm-rust/crates/db/queries/spend/upsert_team_member_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/team_member_spend.rs diff --git a/litellm-rust/Cargo.lock b/litellm-rust/Cargo.lock index d58ba9ecb5f..d0757ebbd2c 100644 --- a/litellm-rust/Cargo.lock +++ b/litellm-rust/Cargo.lock @@ -3699,6 +3699,7 @@ dependencies = [ "macro_rules_attribute", "rstest", "serde", + "serde_json", "sqlx", "thiserror 2.0.19", "tokio", diff --git a/litellm-rust/crates/db/.sqlx/query-b0eb48b795bdaf728c79e986d2027df60ff71d8fed05b53be4458b335af88147.json b/litellm-rust/crates/db/.sqlx/query-b0eb48b795bdaf728c79e986d2027df60ff71d8fed05b53be4458b335af88147.json new file mode 100644 index 00000000000..b333ec814a1 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-b0eb48b795bdaf728c79e986d2027df60ff71d8fed05b53be4458b335af88147.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_TeamMembership\" (user_id, team_id, spend, total_spend)\nSELECT member.user_id, member.team_id, member.cost, member.cost\nFROM jsonb_to_recordset($1::jsonb) AS member(user_id text, team_id text, cost float8)\nWHERE EXISTS (\n SELECT 1 FROM \"LiteLLM_TeamTable\" t\n WHERE t.team_id = member.team_id\n AND t.members_with_roles @> jsonb_build_array(jsonb_build_object('user_id', member.user_id))\n)\n OR EXISTS (\n SELECT 1 FROM \"LiteLLM_TeamMembership\" m\n WHERE m.user_id = member.user_id AND m.team_id = member.team_id\n)\nON CONFLICT (user_id, team_id) DO UPDATE\nSET spend = \"LiteLLM_TeamMembership\".spend + EXCLUDED.spend,\n total_spend = \"LiteLLM_TeamMembership\".total_spend + EXCLUDED.total_spend\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "b0eb48b795bdaf728c79e986d2027df60ff71d8fed05b53be4458b335af88147" +} diff --git a/litellm-rust/crates/db/Cargo.toml b/litellm-rust/crates/db/Cargo.toml index bb6d5655b60..8c1a35c7535 100644 --- a/litellm-rust/crates/db/Cargo.toml +++ b/litellm-rust/crates/db/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true chrono.workspace = true macro_rules_attribute.workspace = true serde.workspace = true +serde_json.workspace = true sqlx.workspace = true thiserror.workspace = true diff --git a/litellm-rust/crates/db/queries/spend/upsert_team_member_spend.sql b/litellm-rust/crates/db/queries/spend/upsert_team_member_spend.sql new file mode 100644 index 00000000000..460c631cbbd --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/upsert_team_member_spend.sql @@ -0,0 +1,15 @@ +INSERT INTO "LiteLLM_TeamMembership" (user_id, team_id, spend, total_spend) +SELECT member.user_id, member.team_id, member.cost, member.cost +FROM jsonb_to_recordset($1::jsonb) AS member(user_id text, team_id text, cost float8) +WHERE EXISTS ( + SELECT 1 FROM "LiteLLM_TeamTable" t + WHERE t.team_id = member.team_id + AND t.members_with_roles @> jsonb_build_array(jsonb_build_object('user_id', member.user_id)) +) + OR EXISTS ( + SELECT 1 FROM "LiteLLM_TeamMembership" m + WHERE m.user_id = member.user_id AND m.team_id = member.team_id +) +ON CONFLICT (user_id, team_id) DO UPDATE +SET spend = "LiteLLM_TeamMembership".spend + EXCLUDED.spend, + total_spend = "LiteLLM_TeamMembership".total_spend + EXCLUDED.total_spend diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 4f009b411fd..afa962cff0e 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -19,6 +19,6 @@ pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; pub use spend::{ - EntitySpend, add_end_user_spend, add_key_spend, add_team_spend, add_user_spend, begin, - lock_team, + EntitySpend, TeamMemberSpend, add_end_user_spend, add_key_spend, add_team_member_spend, + add_team_spend, add_user_spend, begin, lock_team, }; diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 0459e63aaf8..1acf629af4e 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,11 +1,13 @@ mod end_user_spend; mod key_spend; mod team_lock; +mod team_member_spend; mod team_spend; mod user_spend; pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; pub use team_lock::{begin, lock_team}; +pub use team_member_spend::{TeamMemberSpend, add_team_member_spend}; pub use team_spend::add_team_spend; pub use user_spend::add_user_spend; diff --git a/litellm-rust/crates/db/src/spend/team_member_spend.rs b/litellm-rust/crates/db/src/spend/team_member_spend.rs new file mode 100644 index 00000000000..965a39ada4f --- /dev/null +++ b/litellm-rust/crates/db/src/spend/team_member_spend.rs @@ -0,0 +1,162 @@ +use sqlx::{Postgres, Transaction}; + +use super::lock_team; +use crate::Error; + +#[macro_rules_attribute::apply(crate::db_input)] +pub struct TeamMemberSpend { + pub team_id: String, + pub user_id: String, + pub cost: f64, +} + +pub async fn add_team_member_spend( + transaction: &mut Transaction<'_, Postgres>, + members: &[TeamMemberSpend], +) -> Result<(), Error> { + if members.is_empty() { + return Ok(()); + } + let mut members = members.to_vec(); + members.sort_by(|left, right| { + (&left.team_id, &left.user_id).cmp(&(&right.team_id, &right.user_id)) + }); + let mut teams: Vec<&str> = members + .iter() + .map(|member| member.team_id.as_str()) + .collect(); + teams.dedup(); + for team in teams { + lock_team(transaction, team).await?; + } + let rows = serde_json::Value::Array( + members + .iter() + .map(|member| { + serde_json::json!({ + "user_id": member.user_id, + "team_id": member.team_id, + "cost": member.cost, + }) + }) + .collect(), + ); + sqlx::query_file!("queries/spend/upsert_team_member_spend.sql", rows) + .execute(&mut **transaction) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::super::begin; + use super::{TeamMemberSpend, add_team_member_spend}; + use crate::test_support::database_pool; + use rstest::rstest; + use sqlx::PgPool; + + fn members(cost: f64) -> [TeamMemberSpend; 3] { + [ + TeamMemberSpend { + team_id: "member-spend-z".into(), + user_id: "off-roster-existing".into(), + cost: 2.0 * cost, + }, + TeamMemberSpend { + team_id: "member-spend-a".into(), + user_id: "on-roster-missing".into(), + cost, + }, + TeamMemberSpend { + team_id: "member-spend-a".into(), + user_id: "off-roster-missing".into(), + cost: 50.0 * cost, + }, + ] + } + + #[rstest] + #[case::zero_then_quarter(0.25, 0.5, 3.0, 8.0)] + #[case::zero_then_eighth(0.125, 0.25, 2.5, 7.5)] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn membership_spend_preserves_roster_guard_and_fractional_costs( + #[case] cost: f64, + #[case] expected_created: f64, + #[case] expected_spend: f64, + #[case] expected_total: f64, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = begin(&pool).await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _teams = sqlx::query( + r#"INSERT INTO "LiteLLM_TeamTable" (team_id, members_with_roles) + VALUES ('member-spend-a', '[{"user_id":"on-roster-missing","role":"user"}]'), + ('member-spend-z', '[]')"#, + ) + .execute(&mut *tx) + .await?; + #[expect( + clippy::disallowed_methods, + reason = "Fixture writes are outside the compile-checked query set" + )] + let _members = sqlx::query( + r#"INSERT INTO "LiteLLM_TeamMembership" (team_id, user_id, spend, total_spend) + VALUES ('member-spend-z', 'off-roster-existing', 2, 7), + ('member-spend-a', 'untouched', 100, 200)"#, + ) + .execute(&mut *tx) + .await?; + add_team_member_spend(&mut tx, &[]).await?; + add_team_member_spend(&mut tx, &members(0.0)).await?; + add_team_member_spend(&mut tx, &members(cost)).await?; + add_team_member_spend(&mut tx, &members(cost)).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let rows: Vec<(String, String, f64, f64)> = sqlx::query_as( + r#"SELECT team_id, user_id, spend, total_spend FROM "LiteLLM_TeamMembership" + WHERE team_id IN ('member-spend-a', 'member-spend-z') ORDER BY team_id, user_id"#, + ) + .fetch_all(&mut *tx) + .await?; + assert_eq!( + rows, + vec![ + ( + "member-spend-a".into(), + "on-roster-missing".into(), + expected_created, + expected_created + ), + ("member-spend-a".into(), "untouched".into(), 100.0, 200.0), + ( + "member-spend-z".into(), + "off-roster-existing".into(), + expected_spend, + expected_total + ), + ] + ); + tx.rollback().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let remaining: i64 = sqlx::query_scalar( + r#"SELECT COUNT(*) FROM "LiteLLM_TeamMembership" + WHERE team_id IN ('member-spend-a', 'member-spend-z')"#, + ) + .fetch_one(&pool) + .await?; + assert_eq!(remaining, 0); + Ok(()) + } +} From b446d589090e73f118462f4d1d4d57823cd03bcf Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:06 -0700 Subject: [PATCH 48/73] feat(db): add typed spend-log insert query (#48234) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...acf6d06eb22facdf478f88107c9dccdac326b.json | 40 ++ .../db/queries/spend/insert_spend_logs.sql | 26 + litellm-rust/crates/db/src/lib.rs | 4 +- litellm-rust/crates/db/src/spend/mod.rs | 2 + .../crates/db/src/spend/spend_logs.rs | 517 ++++++++++++++++++ 5 files changed, 587 insertions(+), 2 deletions(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-32f3c07ced96324b535bbc703c1acf6d06eb22facdf478f88107c9dccdac326b.json create mode 100644 litellm-rust/crates/db/queries/spend/insert_spend_logs.sql create mode 100644 litellm-rust/crates/db/src/spend/spend_logs.rs diff --git a/litellm-rust/crates/db/.sqlx/query-32f3c07ced96324b535bbc703c1acf6d06eb22facdf478f88107c9dccdac326b.json b/litellm-rust/crates/db/.sqlx/query-32f3c07ced96324b535bbc703c1acf6d06eb22facdf478f88107c9dccdac326b.json new file mode 100644 index 00000000000..c4eb1737d85 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-32f3c07ced96324b535bbc703c1acf6d06eb22facdf478f88107c9dccdac326b.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_SpendLogs\" (\n request_id, call_type, api_key, spend, total_tokens, prompt_tokens,\n completion_tokens, \"startTime\", \"endTime\", \"completionStartTime\",\n request_duration_ms, model, model_id, model_group, custom_llm_provider, api_base,\n \"user\", metadata, cache_hit, cache_key, request_tags, team_id, organization_id,\n end_user, requester_ip_address, messages, response, proxy_server_request,\n session_id, status, litellm_call_id\n)\nSELECT l.request_id, l.call_type, l.api_key, l.spend, l.total_tokens, l.prompt_tokens,\n l.completion_tokens, l.start_time::timestamp(3), l.end_time::timestamp(3),\n l.completion_start_time::timestamp(3), l.request_duration_ms, l.model,\n l.model_id, l.model_group, l.custom_llm_provider, l.api_base, l.user_id,\n l.metadata, l.cache_hit, l.cache_key, '[]'::jsonb, l.team_id,\n l.organization_id, l.end_user, l.requester_ip_address, '{}'::jsonb,\n '{}'::jsonb, '{}'::jsonb, l.session_id, l.status, l.litellm_call_id\nFROM UNNEST($1::text[], $2::text[], $3::text[], $4::float8[], $5::int4[], $6::int4[],\n $7::int4[], $8::timestamp[], $9::timestamp[], $10::timestamp[], $11::int4[], $12::text[],\n $13::text[], $14::text[], $15::text[], $16::text[], $17::text[],\n $18::jsonb[], $19::text[], $20::text[], $21::text[], $22::text[],\n $23::text[], $24::text[], $25::text[], $26::text[], $27::text[])\n AS l(request_id, call_type, api_key, spend, total_tokens, prompt_tokens,\n completion_tokens, start_time, end_time, completion_start_time,\n request_duration_ms, model, model_id, model_group, custom_llm_provider,\n api_base, user_id, metadata, cache_hit, cache_key, team_id, organization_id,\n end_user, requester_ip_address, session_id, status, litellm_call_id)\nON CONFLICT DO NOTHING\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "TextArray", + "TextArray", + "Float8Array", + "Int4Array", + "Int4Array", + "Int4Array", + "TimestampArray", + "TimestampArray", + "TimestampArray", + "Int4Array", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "JsonbArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray" + ] + }, + "nullable": [] + }, + "hash": "32f3c07ced96324b535bbc703c1acf6d06eb22facdf478f88107c9dccdac326b" +} diff --git a/litellm-rust/crates/db/queries/spend/insert_spend_logs.sql b/litellm-rust/crates/db/queries/spend/insert_spend_logs.sql new file mode 100644 index 00000000000..fcffee181a1 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/insert_spend_logs.sql @@ -0,0 +1,26 @@ +INSERT INTO "LiteLLM_SpendLogs" ( + request_id, call_type, api_key, spend, total_tokens, prompt_tokens, + completion_tokens, "startTime", "endTime", "completionStartTime", + request_duration_ms, model, model_id, model_group, custom_llm_provider, api_base, + "user", metadata, cache_hit, cache_key, request_tags, team_id, organization_id, + end_user, requester_ip_address, messages, response, proxy_server_request, + session_id, status, litellm_call_id +) +SELECT l.request_id, l.call_type, l.api_key, l.spend, l.total_tokens, l.prompt_tokens, + l.completion_tokens, l.start_time::timestamp(3), l.end_time::timestamp(3), + l.completion_start_time::timestamp(3), l.request_duration_ms, l.model, + l.model_id, l.model_group, l.custom_llm_provider, l.api_base, l.user_id, + l.metadata, l.cache_hit, l.cache_key, '[]'::jsonb, l.team_id, + l.organization_id, l.end_user, l.requester_ip_address, '{}'::jsonb, + '{}'::jsonb, '{}'::jsonb, l.session_id, l.status, l.litellm_call_id +FROM UNNEST($1::text[], $2::text[], $3::text[], $4::float8[], $5::int4[], $6::int4[], + $7::int4[], $8::timestamp[], $9::timestamp[], $10::timestamp[], $11::int4[], $12::text[], + $13::text[], $14::text[], $15::text[], $16::text[], $17::text[], + $18::jsonb[], $19::text[], $20::text[], $21::text[], $22::text[], + $23::text[], $24::text[], $25::text[], $26::text[], $27::text[]) + AS l(request_id, call_type, api_key, spend, total_tokens, prompt_tokens, + completion_tokens, start_time, end_time, completion_start_time, + request_duration_ms, model, model_id, model_group, custom_llm_provider, + api_base, user_id, metadata, cache_hit, cache_key, team_id, organization_id, + end_user, requester_ip_address, session_id, status, litellm_call_id) +ON CONFLICT DO NOTHING diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index afa962cff0e..511a4bd387a 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -19,6 +19,6 @@ pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; pub use spend::{ - EntitySpend, TeamMemberSpend, add_end_user_spend, add_key_spend, add_team_member_spend, - add_team_spend, add_user_spend, begin, lock_team, + EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, add_end_user_spend, add_key_spend, + add_team_member_spend, add_team_spend, add_user_spend, begin, insert_spend_logs, lock_team, }; diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 1acf629af4e..dcfcee41f83 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,5 +1,6 @@ mod end_user_spend; mod key_spend; +mod spend_logs; mod team_lock; mod team_member_spend; mod team_spend; @@ -7,6 +8,7 @@ mod user_spend; pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; +pub use spend_logs::{SpendLog, SpendLogRow, insert_spend_logs}; pub use team_lock::{begin, lock_team}; pub use team_member_spend::{TeamMemberSpend, add_team_member_spend}; pub use team_spend::add_team_spend; diff --git a/litellm-rust/crates/db/src/spend/spend_logs.rs b/litellm-rust/crates/db/src/spend/spend_logs.rs new file mode 100644 index 00000000000..57a9ff85301 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/spend_logs.rs @@ -0,0 +1,517 @@ +use chrono::{DateTime, NaiveDateTime}; +use sqlx::PgExecutor; + +use crate::Error; + +#[macro_rules_attribute::apply(crate::db_input)] +#[derive(Default)] +pub struct SpendLog { + pub request_id: String, + pub call_type: String, + pub api_key: String, + pub spend: f64, + pub total_tokens: i32, + pub prompt_tokens: i32, + pub completion_tokens: i32, + pub start_time: String, + pub end_time: String, + pub completion_start_time: Option, + pub request_duration_ms: Option, + pub model: String, + pub model_id: String, + pub model_group: String, + pub custom_llm_provider: String, + pub api_base: String, + pub user: String, + pub metadata: serde_json::Value, + pub cache_hit: String, + pub cache_key: String, + pub team_id: String, + pub organization_id: String, + pub end_user: String, + pub requester_ip_address: Option, + pub session_id: Option, + pub status: String, + pub litellm_call_id: Option, +} + +#[macro_rules_attribute::apply(crate::db_row)] +pub struct SpendLogRow { + pub request_id: String, + pub call_type: String, + pub api_key: String, + pub spend: f64, + pub total_tokens: i32, + pub prompt_tokens: i32, + pub completion_tokens: i32, + #[sqlx(rename = "startTime")] + pub start_time: NaiveDateTime, + #[sqlx(rename = "endTime")] + pub end_time: NaiveDateTime, + #[sqlx(rename = "completionStartTime")] + pub completion_start_time: Option, + pub request_duration_ms: Option, + pub model: String, + pub model_id: Option, + pub model_group: Option, + pub custom_llm_provider: Option, + pub api_base: Option, + pub user: Option, + pub metadata: Option, + pub cache_hit: Option, + pub cache_key: Option, + pub request_tags: Option, + pub team_id: Option, + pub organization_id: Option, + pub end_user: Option, + pub requester_ip_address: Option, + pub messages: Option, + pub response: Option, + pub proxy_server_request: Option, + pub session_id: Option, + pub status: Option, + pub litellm_call_id: Option, +} + +fn parse_timestamp(value: &str) -> Result { + DateTime::parse_from_rfc3339(value) + .map(|timestamp| timestamp.naive_utc()) + .or_else(|_| NaiveDateTime::parse_from_str(value, "%Y-%m-%dT%H:%M:%S%.f")) + .or_else(|_| NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S%.f")) +} + +pub async fn insert_spend_logs( + executor: impl PgExecutor<'_>, + logs: &[SpendLog], +) -> Result<(), Error> { + if logs.is_empty() { + return Ok(()); + } + let start_times = logs + .iter() + .map(|log| parse_timestamp(&log.start_time)) + .collect::, _>>()?; + let end_times = logs + .iter() + .map(|log| parse_timestamp(&log.end_time)) + .collect::, _>>()?; + let completion_start_times = logs + .iter() + .map(|log| { + log.completion_start_time + .as_deref() + .map(parse_timestamp) + .transpose() + }) + .collect::, _>>()?; + macro_rules! column { + ($field:ident) => { + logs.iter() + .map(|log| log.$field.clone()) + .collect::>() + }; + } + sqlx::query_file!( + "queries/spend/insert_spend_logs.sql", + &column!(request_id), + &column!(call_type), + &column!(api_key), + &column!(spend), + &column!(total_tokens), + &column!(prompt_tokens), + &column!(completion_tokens), + &start_times, + &end_times, + &completion_start_times as &[Option], + &column!(request_duration_ms) as &[Option], + &column!(model), + &column!(model_id), + &column!(model_group), + &column!(custom_llm_provider), + &column!(api_base), + &column!(user), + &column!(metadata), + &column!(cache_hit), + &column!(cache_key), + &column!(team_id), + &column!(organization_id), + &column!(end_user), + &column!(requester_ip_address) as &[Option], + &column!(session_id) as &[Option], + &column!(status), + &column!(litellm_call_id) as &[Option], + ) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{SpendLog, SpendLogRow, insert_spend_logs}; + use crate::test_support::database_pool; + use rstest::{fixture, rstest}; + use serde_json::json; + use sqlx::{Acquire, Connection, PgExecutor, PgPool, types::chrono::NaiveDateTime}; + + #[fixture] + fn logs() -> [SpendLog; 2] { + [ + SpendLog { + request_id: "spend-logs-a".into(), + call_type: "acompletion".into(), + api_key: "key-a".into(), + spend: 0.125, + total_tokens: 17, + prompt_tokens: 11, + completion_tokens: 6, + start_time: "2026-01-02 03:04:05.123".into(), + end_time: "2026-01-02 03:04:06.789".into(), + completion_start_time: Some("2026-01-02 03:04:05.456".into()), + request_duration_ms: Some(1666), + model: "model-a".into(), + model_id: "deployment-a".into(), + model_group: "group-a".into(), + custom_llm_provider: "provider-a".into(), + api_base: "https://a.example.test".into(), + user: "user-a".into(), + metadata: json!({"tenant": "a", "nested": {"tokens": 11}, "flag": false}), + cache_hit: "False".into(), + cache_key: "cache-a".into(), + team_id: "team-a".into(), + organization_id: "org-a".into(), + end_user: "end-user-a".into(), + requester_ip_address: Some("192.0.2.1".into()), + session_id: Some("session-a".into()), + status: "success".into(), + litellm_call_id: Some("call-a".into()), + }, + SpendLog { + request_id: "spend-logs-b".into(), + call_type: "aembedding".into(), + api_key: "key-b".into(), + spend: 2.75, + total_tokens: 43, + prompt_tokens: 29, + completion_tokens: 14, + start_time: "2026-02-03 14:15:16.234".into(), + end_time: "2026-02-03 14:15:18.567".into(), + completion_start_time: None, + request_duration_ms: None, + model: "model-b".into(), + model_id: "deployment-b".into(), + model_group: "group-b".into(), + custom_llm_provider: "provider-b".into(), + api_base: "https://b.example.test".into(), + user: "user-b".into(), + metadata: json!({"tenant": "b", "tags": ["distinct", 7], "nullable": null}), + cache_hit: "True".into(), + cache_key: "cache-b".into(), + team_id: "team-b".into(), + organization_id: "org-b".into(), + end_user: "end-user-b".into(), + requester_ip_address: None, + session_id: None, + status: "failure".into(), + litellm_call_id: None, + }, + ] + } + + async fn read_log( + executor: impl PgExecutor<'_>, + request_id: &str, + ) -> Result { + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + sqlx::query_as::<_, SpendLogRow>( + r#"SELECT * FROM "LiteLLM_SpendLogs" WHERE request_id = $1"#, + ) + .bind(request_id) + .fetch_one(executor) + .await + } + + fn expected_log(log: &SpendLog) -> Result { + Ok(SpendLogRow { + request_id: log.request_id.clone(), + call_type: log.call_type.clone(), + api_key: log.api_key.clone(), + spend: log.spend, + total_tokens: log.total_tokens, + prompt_tokens: log.prompt_tokens, + completion_tokens: log.completion_tokens, + start_time: log.start_time.replace(' ', "T").parse()?, + end_time: log.end_time.replace(' ', "T").parse()?, + completion_start_time: log + .completion_start_time + .as_ref() + .map(|value| value.replace(' ', "T").parse()) + .transpose()?, + request_duration_ms: log.request_duration_ms, + model: log.model.clone(), + model_id: Some(log.model_id.clone()), + model_group: Some(log.model_group.clone()), + custom_llm_provider: Some(log.custom_llm_provider.clone()), + api_base: Some(log.api_base.clone()), + user: Some(log.user.clone()), + metadata: Some(log.metadata.clone()), + cache_hit: Some(log.cache_hit.clone()), + cache_key: Some(log.cache_key.clone()), + request_tags: Some(json!([])), + team_id: Some(log.team_id.clone()), + organization_id: Some(log.organization_id.clone()), + end_user: Some(log.end_user.clone()), + requester_ip_address: log.requester_ip_address.clone(), + messages: Some(json!({})), + response: Some(json!({})), + proxy_server_request: Some(json!({})), + session_id: log.session_id.clone(), + status: Some(log.status.clone()), + litellm_call_id: log.litellm_call_id.clone(), + }) + } + + #[rstest] + #[case::all_columns_and_duplicate()] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn inserts_distinct_rows_without_replacing_duplicates( + logs: [SpendLog; 2], + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + insert_spend_logs(&mut *tx, &logs).await?; + let replacement = SpendLog { + request_id: logs[0].request_id.clone(), + ..logs[1].clone() + }; + insert_spend_logs(&mut *tx, &[replacement]).await?; + assert_eq!( + read_log(&mut *tx, &logs[0].request_id).await?, + expected_log(&logs[0])? + ); + assert_eq!( + read_log(&mut *tx, &logs[1].request_id).await?, + expected_log(&logs[1])? + ); + tx.rollback().await?; + Ok(()) + } + + #[rstest] + #[case::partitioned_duplicate()] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn inserts_into_partitioned_logs_without_replacing_duplicates( + logs: [SpendLog; 2], + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "The test creates a temporary partitioned table at runtime" + )] + sqlx::raw_sql( + r#"CREATE TEMP TABLE "LiteLLM_SpendLogs" ( + LIKE public."LiteLLM_SpendLogs" INCLUDING DEFAULTS INCLUDING GENERATED, + PRIMARY KEY (request_id, "startTime") + ) PARTITION BY RANGE ("startTime"); + CREATE TEMP TABLE spend_logs_default + PARTITION OF pg_temp."LiteLLM_SpendLogs" DEFAULT; + SET LOCAL search_path TO pg_temp, public;"#, + ) + .execute(&mut *tx) + .await?; + tx.clear_cached_statements().await?; + insert_spend_logs(&mut *tx, &logs).await?; + let replacement = SpendLog { + spend: 99.0, + ..logs[0].clone() + }; + insert_spend_logs(&mut *tx, &[replacement]).await?; + #[expect( + clippy::disallowed_methods, + reason = "The partitioned test fixture is created at runtime" + )] + let count: (i64,) = sqlx::query_as(r#"SELECT count(*) FROM pg_temp."LiteLLM_SpendLogs""#) + .fetch_one(&mut *tx) + .await?; + assert_eq!(count.0, 2); + assert_eq!( + read_log(&mut *tx, &logs[0].request_id).await?, + expected_log(&logs[0])? + ); + assert_eq!( + read_log(&mut *tx, &logs[1].request_id).await?, + expected_log(&logs[1])? + ); + let distinct_partition_key = SpendLog { + request_id: logs[0].request_id.clone(), + ..logs[1].clone() + }; + insert_spend_logs(&mut *tx, &[distinct_partition_key]).await?; + #[expect( + clippy::disallowed_methods, + reason = "The partitioned test fixture is created at runtime" + )] + let count: (i64,) = sqlx::query_as(r#"SELECT count(*) FROM pg_temp."LiteLLM_SpendLogs""#) + .fetch_one(&mut *tx) + .await?; + assert_eq!(count.0, 3); + tx.rollback().await?; + Ok(()) + } + + #[rstest] + #[case::positive_offset_across_midnight( + ["2026-01-02T00:04:05.1236+02:00", "2026-01-02T00:04:06.7894+02:00", "2026-01-02T00:04:05.4567+02:00"], + ["2026-01-01T22:04:05.124", "2026-01-01T22:04:06.789", "2026-01-01T22:04:05.457"] + )] + #[case::negative_offset_across_midnight( + ["2026-01-01T23:04:05-02:00", "2026-01-01T23:04:06-02:00", "2026-01-01T23:04:05.456-02:00"], + ["2026-01-02T01:04:05", "2026-01-02T01:04:06", "2026-01-02T01:04:05.456"] + )] + #[case::utc( + ["2026-01-02T03:04:05Z", "2026-01-02T03:04:06Z", "2026-01-02T03:04:05.456Z"], + ["2026-01-02T03:04:05", "2026-01-02T03:04:06", "2026-01-02T03:04:05.456"] + )] + #[case::naive_iso( + ["2026-01-02T03:04:05", "2026-01-02T03:04:06", "2026-01-02T03:04:05.456"], + ["2026-01-02T03:04:05", "2026-01-02T03:04:06", "2026-01-02T03:04:05.456"] + )] + #[case::naive_space( + ["2026-01-02 03:04:05", "2026-01-02 03:04:06", "2026-01-02 03:04:05.456"], + ["2026-01-02T03:04:05", "2026-01-02T03:04:06", "2026-01-02T03:04:05.456"] + )] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn stores_utc_timestamps( + #[case] input: [&str; 3], + #[case] expected: [&str; 3], + logs: [SpendLog; 2], + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "The test sets a transaction-local timezone to verify timestamp handling" + )] + sqlx::raw_sql("SET LOCAL TIME ZONE 'America/Los_Angeles'") + .execute(&mut *tx) + .await?; + let log = SpendLog { + start_time: input[0].into(), + end_time: input[1].into(), + completion_start_time: Some(input[2].into()), + ..logs[0].clone() + }; + insert_spend_logs(&mut *tx, std::slice::from_ref(&log)).await?; + let row = read_log(&mut *tx, &log.request_id).await?; + assert_eq!(row.start_time, expected[0].parse::()?); + assert_eq!(row.end_time, expected[1].parse::()?); + assert_eq!( + row.completion_start_time, + Some(expected[2].parse::()?) + ); + tx.rollback().await?; + Ok(()) + } + + #[rstest] + #[case::empty_batch()] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn empty_input_leaves_rows_unchanged( + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let before: (i64,) = sqlx::query_as(r#"SELECT count(*) FROM "LiteLLM_SpendLogs""#) + .fetch_one(&mut *tx) + .await?; + insert_spend_logs(&mut *tx, &[]).await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let after: (i64,) = sqlx::query_as(r#"SELECT count(*) FROM "LiteLLM_SpendLogs""#) + .fetch_one(&mut *tx) + .await?; + assert_eq!(before, after); + tx.rollback().await?; + Ok(()) + } + + #[rstest] + #[case::invalid_timestamp("not-a-timestamp", "2026-01-02T03:04:06", None)] + #[case::invalid_end("2026-01-02T03:04:05", "not-a-timestamp", None)] + #[case::invalid_completion( + "2026-01-02T03:04:05", + "2026-01-02T03:04:06", + Some("not-a-timestamp") + )] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn invalid_timestamp_allows_caller_rollback( + #[case] start_time: &str, + #[case] end_time: &str, + #[case] completion_start_time: Option<&str>, + logs: [SpendLog; 2], + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut outer = pool.begin().await?; + let mut tx = outer.begin().await?; + let valid = SpendLog { + request_id: "spend-logs-rollback-valid".into(), + ..logs[0].clone() + }; + let invalid = SpendLog { + request_id: "spend-logs-rollback-invalid".into(), + start_time: start_time.into(), + end_time: end_time.into(), + completion_start_time: completion_start_time.map(str::to_owned), + ..logs[1].clone() + }; + insert_spend_logs(&mut *tx, std::slice::from_ref(&valid)).await?; + let result = insert_spend_logs(&mut *tx, &[logs[0].clone(), invalid]).await; + assert!(matches!(result, Err(crate::Error::InvalidTimestamp(_)))); + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let partial_batch: (i64,) = sqlx::query_as( + r#"SELECT count(*) FROM "LiteLLM_SpendLogs" WHERE request_id = ANY($1)"#, + ) + .bind([logs[0].request_id.as_str(), "spend-logs-rollback-invalid"].as_slice()) + .fetch_one(&mut *tx) + .await?; + assert_eq!(partial_batch.0, 0); + tx.rollback().await?; + #[expect( + clippy::disallowed_methods, + reason = "Test assertions are outside the compile-checked query set" + )] + let remaining: (i64,) = sqlx::query_as( + r#"SELECT count(*) FROM "LiteLLM_SpendLogs" WHERE request_id = ANY($1)"#, + ) + .bind([valid.request_id.as_str(), "spend-logs-rollback-invalid"].as_slice()) + .fetch_one(&mut *outer) + .await?; + assert_eq!(remaining.0, 0); + outer.rollback().await?; + Ok(()) + } +} From 1b874ceb85313d5ff9a27e18db75e5cb90fcad81 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:07 -0700 Subject: [PATCH 49/73] feat(db): add typed daily user spend upsert (#48235) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe.json | 31 ++ .../queries/spend/upsert_daily_user_spend.sql | 40 ++ litellm-rust/crates/db/src/lib.rs | 5 +- .../crates/db/src/spend/daily_user_spend.rs | 500 ++++++++++++++++++ litellm-rust/crates/db/src/spend/mod.rs | 2 + 5 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-bec3b53762af021a118d63b841b8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe.json create mode 100644 litellm-rust/crates/db/queries/spend/upsert_daily_user_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/daily_user_spend.rs diff --git a/litellm-rust/crates/db/.sqlx/query-bec3b53762af021a118d63b841b8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe.json b/litellm-rust/crates/db/.sqlx/query-bec3b53762af021a118d63b841b8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe.json new file mode 100644 index 00000000000..6c56868e48e --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-bec3b53762af021a118d63b841b8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_DailyUserSpend\" (\n id, user_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens,\n completion_tokens, api_requests, successful_requests, failed_requests,\n cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms,\n timed_requests, spend, updated_at\n)\nSELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model,\n r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens,\n r.completion_tokens, r.api_requests, r.successful_requests,\n r.failed_requests, r.cache_read, r.cache_creation, r.response_time,\n r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC')\nFROM UNNEST(\n $1::text[], $2::text[], $3::text[], $4::text[], $5::text[],\n $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[],\n $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[],\n $16::int8[], $17::float8[], $18::text[]\n) AS r(\n entity_id, date, api_key, model, custom_llm_provider, endpoint,\n model_group, prompt_tokens, completion_tokens, api_requests,\n successful_requests, failed_requests, cache_read, cache_creation,\n response_time, timed_requests, spend, mcp_namespaced_tool_name\n)\nORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider,\n r.mcp_namespaced_tool_name, r.endpoint\nON CONFLICT (\n user_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint\n) DO UPDATE SET\n prompt_tokens = \"LiteLLM_DailyUserSpend\".prompt_tokens + EXCLUDED.prompt_tokens,\n completion_tokens = \"LiteLLM_DailyUserSpend\".completion_tokens + EXCLUDED.completion_tokens,\n api_requests = \"LiteLLM_DailyUserSpend\".api_requests + EXCLUDED.api_requests,\n successful_requests = \"LiteLLM_DailyUserSpend\".successful_requests + EXCLUDED.successful_requests,\n failed_requests = \"LiteLLM_DailyUserSpend\".failed_requests + EXCLUDED.failed_requests,\n cache_read_input_tokens = \"LiteLLM_DailyUserSpend\".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens,\n cache_creation_input_tokens = \"LiteLLM_DailyUserSpend\".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens,\n total_response_time_ms = \"LiteLLM_DailyUserSpend\".total_response_time_ms + EXCLUDED.total_response_time_ms,\n timed_requests = \"LiteLLM_DailyUserSpend\".timed_requests + EXCLUDED.timed_requests,\n spend = \"LiteLLM_DailyUserSpend\".spend + EXCLUDED.spend,\n updated_at = (NOW() AT TIME ZONE 'UTC')\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Float8Array", + "TextArray" + ] + }, + "nullable": [] + }, + "hash": "bec3b53762af021a118d63b841b8c2ebd7fc9fcbf19c1c25aa131a1053d96cfe" +} diff --git a/litellm-rust/crates/db/queries/spend/upsert_daily_user_spend.sql b/litellm-rust/crates/db/queries/spend/upsert_daily_user_spend.sql new file mode 100644 index 00000000000..4a5db90dee2 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/upsert_daily_user_spend.sql @@ -0,0 +1,40 @@ +INSERT INTO "LiteLLM_DailyUserSpend" ( + id, user_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens, + completion_tokens, api_requests, successful_requests, failed_requests, + cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, updated_at +) +SELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model, + r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens, + r.completion_tokens, r.api_requests, r.successful_requests, + r.failed_requests, r.cache_read, r.cache_creation, r.response_time, + r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC') +FROM UNNEST( + $1::text[], $2::text[], $3::text[], $4::text[], $5::text[], + $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[], + $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[], + $16::int8[], $17::float8[], $18::text[] +) AS r( + entity_id, date, api_key, model, custom_llm_provider, endpoint, + model_group, prompt_tokens, completion_tokens, api_requests, + successful_requests, failed_requests, cache_read, cache_creation, + response_time, timed_requests, spend, mcp_namespaced_tool_name +) +ORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider, + r.mcp_namespaced_tool_name, r.endpoint +ON CONFLICT ( + user_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint +) DO UPDATE SET + prompt_tokens = "LiteLLM_DailyUserSpend".prompt_tokens + EXCLUDED.prompt_tokens, + completion_tokens = "LiteLLM_DailyUserSpend".completion_tokens + EXCLUDED.completion_tokens, + api_requests = "LiteLLM_DailyUserSpend".api_requests + EXCLUDED.api_requests, + successful_requests = "LiteLLM_DailyUserSpend".successful_requests + EXCLUDED.successful_requests, + failed_requests = "LiteLLM_DailyUserSpend".failed_requests + EXCLUDED.failed_requests, + cache_read_input_tokens = "LiteLLM_DailyUserSpend".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens, + cache_creation_input_tokens = "LiteLLM_DailyUserSpend".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens, + total_response_time_ms = "LiteLLM_DailyUserSpend".total_response_time_ms + EXCLUDED.total_response_time_ms, + timed_requests = "LiteLLM_DailyUserSpend".timed_requests + EXCLUDED.timed_requests, + spend = "LiteLLM_DailyUserSpend".spend + EXCLUDED.spend, + updated_at = (NOW() AT TIME ZONE 'UTC') diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 511a4bd387a..3bcd050440a 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -19,6 +19,7 @@ pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; pub use spend::{ - EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, add_end_user_spend, add_key_spend, - add_team_member_spend, add_team_spend, add_user_spend, begin, insert_spend_logs, lock_team, + DailySpend, EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, add_end_user_spend, + add_key_spend, add_team_member_spend, add_team_spend, add_user_spend, begin, insert_spend_logs, + lock_team, upsert_daily_user_spend, }; diff --git a/litellm-rust/crates/db/src/spend/daily_user_spend.rs b/litellm-rust/crates/db/src/spend/daily_user_spend.rs new file mode 100644 index 00000000000..3530eb245c5 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/daily_user_spend.rs @@ -0,0 +1,500 @@ +use sqlx::PgExecutor; + +use crate::Error; + +#[macro_rules_attribute::apply(crate::db_input)] +#[derive(Default)] +pub struct DailySpend { + pub entity_id: String, + pub date: String, + pub api_key: String, + pub model: String, + pub custom_llm_provider: String, + pub mcp_namespaced_tool_name: String, + pub endpoint: String, + pub model_group: Option, + pub prompt_tokens: i64, + pub completion_tokens: i64, + pub api_requests: i64, + pub successful_requests: i64, + pub failed_requests: i64, + pub cache_read_input_tokens: i64, + pub cache_creation_input_tokens: i64, + pub total_response_time_ms: i64, + pub timed_requests: i64, + pub spend: f64, +} + +pub async fn upsert_daily_user_spend( + executor: impl PgExecutor<'_>, + rows: &[DailySpend], +) -> Result<(), Error> { + if rows.is_empty() { + return Ok(()); + } + macro_rules! column { + ($field:ident) => { + rows.iter() + .map(|row| row.$field.clone()) + .collect::>() + }; + } + sqlx::query_file!( + "queries/spend/upsert_daily_user_spend.sql", + &column!(entity_id), + &column!(date), + &column!(api_key), + &column!(model), + &column!(custom_llm_provider), + &column!(endpoint), + &column!(model_group) as &[Option], + &column!(prompt_tokens), + &column!(completion_tokens), + &column!(api_requests), + &column!(successful_requests), + &column!(failed_requests), + &column!(cache_read_input_tokens), + &column!(cache_creation_input_tokens), + &column!(total_response_time_ms), + &column!(timed_requests), + &column!(spend), + &column!(mcp_namespaced_tool_name), + ) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{DailySpend, upsert_daily_user_spend}; + use crate::{DailyUserRow, test_support::database_pool}; + use rstest::rstest; + use sqlx::{PgPool, types::chrono::NaiveDateTime}; + + #[rstest] + #[case::distinct_tools("server/search", "server/write")] + #[case::default_empty_tool("", "server/search")] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_user_spend_separates_tools_and_accumulates( + #[case] first_tool: &str, + #[case] second_tool: &str, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + let first = DailySpend { + entity_id: format!("daily-user-tools-{first_tool}"), + date: "2026-01-15".to_owned(), + api_key: "tool-key".to_owned(), + model: "tool-model".to_owned(), + custom_llm_provider: "tool-provider".to_owned(), + endpoint: "/mcp".to_owned(), + model_group: Some("tool-group".to_owned()), + mcp_namespaced_tool_name: first_tool.to_owned(), + prompt_tokens: 3, + api_requests: 1, + spend: 0.25, + ..Default::default() + }; + let second = DailySpend { + mcp_namespaced_tool_name: second_tool.to_owned(), + prompt_tokens: 7, + api_requests: 2, + spend: 0.5, + ..first.clone() + }; + upsert_daily_user_spend(&mut *tx, &[second.clone(), first.clone()]).await?; + upsert_daily_user_spend(&mut *tx, &[first.clone(), second]).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads stored rows to verify tool attribution" + )] + let rows: Vec = sqlx::query_as( + r#"SELECT * FROM "LiteLLM_DailyUserSpend" + WHERE user_id = $1 + ORDER BY mcp_namespaced_tool_name, endpoint"#, + ) + .bind(&first.entity_id) + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 2); + assert_eq!( + rows[0].mcp_namespaced_tool_name.as_deref(), + Some(first_tool) + ); + assert_eq!( + rows[1].mcp_namespaced_tool_name.as_deref(), + Some(second_tool) + ); + assert_eq!( + (rows[0].prompt_tokens, rows[0].api_requests, rows[0].spend), + (6, 2, 0.5) + ); + assert_eq!( + (rows[1].prompt_tokens, rows[1].api_requests, rows[1].spend), + (14, 4, 1.0) + ); + Ok(()) + } + + #[rstest] + #[case::full_keys( + "daily-user-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + Some("first-group") + )] + #[case::initial_model_group_none( + "daily-user-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + None + )] + #[case::empty_optional_keys("", "", "", "", "", Some(""))] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_user_spend_upsert_preserves_keys_and_accumulates( + #[case] user_id: &'static str, + #[case] api_key: &'static str, + #[case] model: &'static str, + #[case] custom_llm_provider: &'static str, + #[case] endpoint: &'static str, + #[case] first_model_group: Option<&'static str>, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test exercises UTC timestamp behavior under a non-UTC session" + )] + sqlx::query("SET LOCAL TIME ZONE 'America/Los_Angeles'") + .execute(&mut *tx) + .await?; + + let first_batch = [ + DailySpend { + entity_id: user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: first_model_group.map(str::to_owned), + prompt_tokens: 1, + completion_tokens: 2, + api_requests: 3, + successful_requests: 4, + failed_requests: 5, + cache_read_input_tokens: 6, + cache_creation_input_tokens: 7, + total_response_time_ms: 8, + timed_requests: 9, + spend: 0.125, + }, + DailySpend { + entity_id: user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: "endpoint-sentinel".to_owned(), + model_group: Some("sentinel-endpoint-group".to_owned()), + prompt_tokens: 101, + completion_tokens: 102, + api_requests: 103, + successful_requests: 104, + failed_requests: 105, + cache_read_input_tokens: 106, + cache_creation_input_tokens: 107, + total_response_time_ms: 108, + timed_requests: 109, + spend: 1.25, + }, + DailySpend { + entity_id: user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: "api-key-sentinel".to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-key-group".to_owned()), + prompt_tokens: 201, + completion_tokens: 202, + api_requests: 203, + successful_requests: 204, + failed_requests: 205, + cache_read_input_tokens: 206, + cache_creation_input_tokens: 207, + total_response_time_ms: 208, + timed_requests: 209, + spend: 2.5, + }, + DailySpend { + entity_id: "entity-sentinel".to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-entity-group".to_owned()), + prompt_tokens: 301, + completion_tokens: 302, + api_requests: 303, + successful_requests: 304, + failed_requests: 305, + cache_read_input_tokens: 306, + cache_creation_input_tokens: 307, + total_response_time_ms: 308, + timed_requests: 309, + spend: 3.75, + }, + ]; + upsert_daily_user_spend(&mut *tx, &first_batch).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the inserted row identity before conflict handling" + )] + let (initial_id, initial_created_at): (String, NaiveDateTime) = sqlx::query_as( + r#"SELECT id, created_at + FROM "LiteLLM_DailyUserSpend" + WHERE user_id = $1 AND date = $2 AND api_key = $3 AND model = $4 + AND custom_llm_provider = $5 AND endpoint = $6"#, + ) + .bind(user_id) + .bind("2026-01-15") + .bind(api_key) + .bind(model) + .bind(custom_llm_provider) + .bind(endpoint) + .fetch_one(&mut *tx) + .await?; + assert!(!initial_id.is_empty()); + + #[expect( + clippy::disallowed_methods, + reason = "The integration test seeds updated_at before exercising the conflict update" + )] + let seeded_updated_at: NaiveDateTime = sqlx::query_scalar( + r#"UPDATE "LiteLLM_DailyUserSpend" + SET updated_at = TIMESTAMP '2000-01-01' + WHERE id = $1 + RETURNING updated_at"#, + ) + .bind(&initial_id) + .fetch_one(&mut *tx) + .await?; + + let increment = [DailySpend { + entity_id: user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("second-group".to_owned()), + prompt_tokens: 10, + completion_tokens: 20, + api_requests: 30, + successful_requests: 40, + failed_requests: 50, + cache_read_input_tokens: 60, + cache_creation_input_tokens: 70, + total_response_time_ms: 80, + timed_requests: 90, + spend: 0.625, + }]; + upsert_daily_user_spend(&mut *tx, &increment).await?; + upsert_daily_user_spend(&mut *tx, &[]).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the transaction timestamp for assertions" + )] + let expected_updated_at: NaiveDateTime = + sqlx::query_scalar("SELECT (NOW() AT TIME ZONE 'UTC')::timestamp(3)") + .fetch_one(&mut *tx) + .await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads all rows to verify attribution and accumulation" + )] + let rows: Vec = sqlx::query_as( + r#"SELECT id, user_id, api_key, model, custom_llm_provider, endpoint, + model_group, mcp_namespaced_tool_name, prompt_tokens, + completion_tokens, api_requests, successful_requests, + failed_requests, cache_read_input_tokens, + cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, date, created_at, updated_at + FROM "LiteLLM_DailyUserSpend" + WHERE date = $1 AND model = $2 AND custom_llm_provider = $3 + AND mcp_namespaced_tool_name = '' AND user_id = ANY($4) + ORDER BY CASE + WHEN user_id = $5 AND api_key = $6 AND endpoint = $7 THEN 0 + WHEN user_id = $5 AND api_key = $6 AND endpoint = $9 THEN 1 + WHEN user_id = $5 AND api_key = $8 AND endpoint = $7 THEN 2 + WHEN user_id = $10 AND api_key = $6 AND endpoint = $7 THEN 3 + ELSE 4 + END"#, + ) + .bind("2026-01-15") + .bind(model) + .bind(custom_llm_provider) + .bind(vec![user_id, "entity-sentinel"]) + .bind(user_id) + .bind(api_key) + .bind(endpoint) + .bind("api-key-sentinel") + .bind("endpoint-sentinel") + .bind("entity-sentinel") + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 4); + assert_eq!(rows[0].id, initial_id); + assert_eq!(rows[0].user_id.as_deref(), Some(user_id)); + assert_eq!(rows[0].api_key, api_key); + assert_eq!(rows[0].model.as_deref(), Some(model)); + assert_eq!( + rows[0].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[0].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[0].model_group.as_deref(), first_model_group); + assert_eq!(rows[0].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[0].prompt_tokens, + rows[0].completion_tokens, + rows[0].api_requests, + rows[0].successful_requests, + rows[0].failed_requests, + rows[0].cache_read_input_tokens, + rows[0].cache_creation_input_tokens, + rows[0].total_response_time_ms, + rows[0].timed_requests, + rows[0].spend + ), + (11, 22, 33, 44, 55, 66, 77, 88, 99, 0.75) + ); + assert_eq!(rows[0].date, "2026-01-15"); + assert_eq!(rows[0].created_at, initial_created_at); + assert!(rows[0].updated_at > seeded_updated_at); + assert_eq!(rows[0].updated_at, expected_updated_at); + + assert!(!rows[1].id.is_empty()); + assert_eq!(rows[1].user_id.as_deref(), Some(user_id)); + assert_eq!(rows[1].api_key, api_key); + assert_eq!(rows[1].model.as_deref(), Some(model)); + assert_eq!( + rows[1].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[1].endpoint.as_deref(), Some("endpoint-sentinel")); + assert_eq!( + rows[1].model_group.as_deref(), + Some("sentinel-endpoint-group") + ); + assert_eq!(rows[1].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[1].prompt_tokens, + rows[1].completion_tokens, + rows[1].api_requests, + rows[1].successful_requests, + rows[1].failed_requests, + rows[1].cache_read_input_tokens, + rows[1].cache_creation_input_tokens, + rows[1].total_response_time_ms, + rows[1].timed_requests, + rows[1].spend + ), + (101, 102, 103, 104, 105, 106, 107, 108, 109, 1.25) + ); + assert_eq!(rows[1].date, "2026-01-15"); + assert!(rows[1].updated_at > seeded_updated_at); + assert_eq!(rows[1].updated_at, expected_updated_at); + + assert!(!rows[2].id.is_empty()); + assert_eq!(rows[2].user_id.as_deref(), Some(user_id)); + assert_eq!(rows[2].api_key, "api-key-sentinel"); + assert_eq!(rows[2].model.as_deref(), Some(model)); + assert_eq!( + rows[2].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[2].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[2].model_group.as_deref(), Some("sentinel-key-group")); + assert_eq!(rows[2].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[2].prompt_tokens, + rows[2].completion_tokens, + rows[2].api_requests, + rows[2].successful_requests, + rows[2].failed_requests, + rows[2].cache_read_input_tokens, + rows[2].cache_creation_input_tokens, + rows[2].total_response_time_ms, + rows[2].timed_requests, + rows[2].spend + ), + (201, 202, 203, 204, 205, 206, 207, 208, 209, 2.5) + ); + assert_eq!(rows[2].date, "2026-01-15"); + assert!(rows[2].updated_at > seeded_updated_at); + assert_eq!(rows[2].updated_at, expected_updated_at); + + assert!(!rows[3].id.is_empty()); + assert_eq!(rows[3].user_id.as_deref(), Some("entity-sentinel")); + assert_eq!(rows[3].api_key, api_key); + assert_eq!(rows[3].model.as_deref(), Some(model)); + assert_eq!( + rows[3].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[3].endpoint.as_deref(), Some(endpoint)); + assert_eq!( + rows[3].model_group.as_deref(), + Some("sentinel-entity-group") + ); + assert_eq!(rows[3].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[3].prompt_tokens, + rows[3].completion_tokens, + rows[3].api_requests, + rows[3].successful_requests, + rows[3].failed_requests, + rows[3].cache_read_input_tokens, + rows[3].cache_creation_input_tokens, + rows[3].total_response_time_ms, + rows[3].timed_requests, + rows[3].spend + ), + (301, 302, 303, 304, 305, 306, 307, 308, 309, 3.75) + ); + assert_eq!(rows[3].date, "2026-01-15"); + assert!(rows[3].updated_at > seeded_updated_at); + assert_eq!(rows[3].updated_at, expected_updated_at); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index dcfcee41f83..f50440e06de 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,3 +1,4 @@ +mod daily_user_spend; mod end_user_spend; mod key_spend; mod spend_logs; @@ -6,6 +7,7 @@ mod team_member_spend; mod team_spend; mod user_spend; +pub use daily_user_spend::{DailySpend, upsert_daily_user_spend}; pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; pub use spend_logs::{SpendLog, SpendLogRow, insert_spend_logs}; From a2545134f370696ae1dd2ba7102d0ac4da552968 Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:07 -0700 Subject: [PATCH 50/73] feat(db): add typed daily team spend upsert (#48236) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...74dca981ff954452b35ffbace3e022035b416.json | 31 ++ .../queries/spend/upsert_daily_team_spend.sql | 40 ++ litellm-rust/crates/db/src/lib.rs | 2 +- .../crates/db/src/spend/daily_team_spend.rs | 485 ++++++++++++++++++ litellm-rust/crates/db/src/spend/mod.rs | 2 + 5 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-9c51401a8907f818e96da28050b74dca981ff954452b35ffbace3e022035b416.json create mode 100644 litellm-rust/crates/db/queries/spend/upsert_daily_team_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/daily_team_spend.rs diff --git a/litellm-rust/crates/db/.sqlx/query-9c51401a8907f818e96da28050b74dca981ff954452b35ffbace3e022035b416.json b/litellm-rust/crates/db/.sqlx/query-9c51401a8907f818e96da28050b74dca981ff954452b35ffbace3e022035b416.json new file mode 100644 index 00000000000..9886a9505d8 --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-9c51401a8907f818e96da28050b74dca981ff954452b35ffbace3e022035b416.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_DailyTeamSpend\" (\n id, team_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens,\n completion_tokens, api_requests, successful_requests, failed_requests,\n cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms,\n timed_requests, spend, updated_at\n)\nSELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model,\n r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens,\n r.completion_tokens, r.api_requests, r.successful_requests,\n r.failed_requests, r.cache_read, r.cache_creation, r.response_time,\n r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC')\nFROM UNNEST(\n $1::text[], $2::text[], $3::text[], $4::text[], $5::text[],\n $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[],\n $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[],\n $16::int8[], $17::float8[], $18::text[]\n) AS r(\n entity_id, date, api_key, model, custom_llm_provider, endpoint,\n model_group, prompt_tokens, completion_tokens, api_requests,\n successful_requests, failed_requests, cache_read, cache_creation,\n response_time, timed_requests, spend, mcp_namespaced_tool_name\n)\nORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider,\n r.mcp_namespaced_tool_name, r.endpoint\nON CONFLICT (\n team_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint\n) DO UPDATE SET\n prompt_tokens = \"LiteLLM_DailyTeamSpend\".prompt_tokens + EXCLUDED.prompt_tokens,\n completion_tokens = \"LiteLLM_DailyTeamSpend\".completion_tokens + EXCLUDED.completion_tokens,\n api_requests = \"LiteLLM_DailyTeamSpend\".api_requests + EXCLUDED.api_requests,\n successful_requests = \"LiteLLM_DailyTeamSpend\".successful_requests + EXCLUDED.successful_requests,\n failed_requests = \"LiteLLM_DailyTeamSpend\".failed_requests + EXCLUDED.failed_requests,\n cache_read_input_tokens = \"LiteLLM_DailyTeamSpend\".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens,\n cache_creation_input_tokens = \"LiteLLM_DailyTeamSpend\".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens,\n total_response_time_ms = \"LiteLLM_DailyTeamSpend\".total_response_time_ms + EXCLUDED.total_response_time_ms,\n timed_requests = \"LiteLLM_DailyTeamSpend\".timed_requests + EXCLUDED.timed_requests,\n spend = \"LiteLLM_DailyTeamSpend\".spend + EXCLUDED.spend,\n updated_at = (NOW() AT TIME ZONE 'UTC')\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Float8Array", + "TextArray" + ] + }, + "nullable": [] + }, + "hash": "9c51401a8907f818e96da28050b74dca981ff954452b35ffbace3e022035b416" +} diff --git a/litellm-rust/crates/db/queries/spend/upsert_daily_team_spend.sql b/litellm-rust/crates/db/queries/spend/upsert_daily_team_spend.sql new file mode 100644 index 00000000000..a9bdef34043 --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/upsert_daily_team_spend.sql @@ -0,0 +1,40 @@ +INSERT INTO "LiteLLM_DailyTeamSpend" ( + id, team_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens, + completion_tokens, api_requests, successful_requests, failed_requests, + cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, updated_at +) +SELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model, + r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens, + r.completion_tokens, r.api_requests, r.successful_requests, + r.failed_requests, r.cache_read, r.cache_creation, r.response_time, + r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC') +FROM UNNEST( + $1::text[], $2::text[], $3::text[], $4::text[], $5::text[], + $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[], + $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[], + $16::int8[], $17::float8[], $18::text[] +) AS r( + entity_id, date, api_key, model, custom_llm_provider, endpoint, + model_group, prompt_tokens, completion_tokens, api_requests, + successful_requests, failed_requests, cache_read, cache_creation, + response_time, timed_requests, spend, mcp_namespaced_tool_name +) +ORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider, + r.mcp_namespaced_tool_name, r.endpoint +ON CONFLICT ( + team_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint +) DO UPDATE SET + prompt_tokens = "LiteLLM_DailyTeamSpend".prompt_tokens + EXCLUDED.prompt_tokens, + completion_tokens = "LiteLLM_DailyTeamSpend".completion_tokens + EXCLUDED.completion_tokens, + api_requests = "LiteLLM_DailyTeamSpend".api_requests + EXCLUDED.api_requests, + successful_requests = "LiteLLM_DailyTeamSpend".successful_requests + EXCLUDED.successful_requests, + failed_requests = "LiteLLM_DailyTeamSpend".failed_requests + EXCLUDED.failed_requests, + cache_read_input_tokens = "LiteLLM_DailyTeamSpend".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens, + cache_creation_input_tokens = "LiteLLM_DailyTeamSpend".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens, + total_response_time_ms = "LiteLLM_DailyTeamSpend".total_response_time_ms + EXCLUDED.total_response_time_ms, + timed_requests = "LiteLLM_DailyTeamSpend".timed_requests + EXCLUDED.timed_requests, + spend = "LiteLLM_DailyTeamSpend".spend + EXCLUDED.spend, + updated_at = (NOW() AT TIME ZONE 'UTC') diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 3bcd050440a..5b88bcabf32 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -21,5 +21,5 @@ pub use failure::DbFailure; pub use spend::{ DailySpend, EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, add_end_user_spend, add_key_spend, add_team_member_spend, add_team_spend, add_user_spend, begin, insert_spend_logs, - lock_team, upsert_daily_user_spend, + lock_team, upsert_daily_team_spend, upsert_daily_user_spend, }; diff --git a/litellm-rust/crates/db/src/spend/daily_team_spend.rs b/litellm-rust/crates/db/src/spend/daily_team_spend.rs new file mode 100644 index 00000000000..f2acd23f468 --- /dev/null +++ b/litellm-rust/crates/db/src/spend/daily_team_spend.rs @@ -0,0 +1,485 @@ +use sqlx::PgExecutor; + +use super::DailySpend; +use crate::Error; + +pub async fn upsert_daily_team_spend( + executor: impl PgExecutor<'_>, + rows: &[DailySpend], +) -> Result<(), Error> { + if rows.is_empty() { + return Ok(()); + } + macro_rules! column { + ($field:ident) => { + rows.iter() + .map(|row| row.$field.clone()) + .collect::>() + }; + } + sqlx::query_file!( + "queries/spend/upsert_daily_team_spend.sql", + &column!(entity_id), + &column!(date), + &column!(api_key), + &column!(model), + &column!(custom_llm_provider), + &column!(endpoint), + &column!(model_group) as &[Option], + &column!(prompt_tokens), + &column!(completion_tokens), + &column!(api_requests), + &column!(successful_requests), + &column!(failed_requests), + &column!(cache_read_input_tokens), + &column!(cache_creation_input_tokens), + &column!(total_response_time_ms), + &column!(timed_requests), + &column!(spend), + &column!(mcp_namespaced_tool_name), + ) + .execute(executor) + .await?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{DailySpend, upsert_daily_team_spend}; + use crate::{DailyTeamRow, test_support::database_pool}; + use rstest::rstest; + use sqlx::{PgPool, types::chrono::NaiveDateTime}; + + #[rstest] + #[case::distinct_tools("server.fetch", "server.search")] + #[case::default_and_named_tool("", "server.search")] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_team_spend_separates_tools_and_accumulates( + #[case] first_tool: &str, + #[case] second_tool: &str, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + let first = DailySpend { + entity_id: "daily-team-tools".to_owned(), + date: "2026-01-15".to_owned(), + api_key: "daily-key-tools".to_owned(), + model: "daily-model".to_owned(), + custom_llm_provider: "daily-provider".to_owned(), + endpoint: "/mcp".to_owned(), + mcp_namespaced_tool_name: first_tool.to_owned(), + api_requests: 1, + spend: 0.125, + ..Default::default() + }; + let second = DailySpend { + mcp_namespaced_tool_name: second_tool.to_owned(), + api_requests: 2, + spend: 0.25, + ..first.clone() + }; + upsert_daily_team_spend(&mut *tx, &[second.clone(), first.clone()]).await?; + upsert_daily_team_spend( + &mut *tx, + &[ + DailySpend { + api_requests: 3, + spend: 0.5, + ..first + }, + DailySpend { + api_requests: 5, + spend: 1.0, + ..second + }, + ], + ) + .await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads stored tool attribution and counters" + )] + let rows = sqlx::query_as::<_, DailyTeamRow>( + r#"SELECT * FROM "LiteLLM_DailyTeamSpend" + WHERE team_id = $1 AND date = $2 + ORDER BY mcp_namespaced_tool_name"#, + ) + .bind("daily-team-tools") + .bind("2026-01-15") + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 2); + assert_eq!( + rows[0].mcp_namespaced_tool_name.as_deref(), + Some(first_tool) + ); + assert_eq!( + rows[1].mcp_namespaced_tool_name.as_deref(), + Some(second_tool) + ); + assert_eq!((rows[0].api_requests, rows[0].spend), (4, 0.625)); + assert_eq!((rows[1].api_requests, rows[1].spend), (7, 1.25)); + Ok(()) + } + + #[rstest] + #[case::full_keys( + "daily-team-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + Some("first-group") + )] + #[case::initial_model_group_none( + "daily-team-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + None + )] + #[case::empty_optional_keys("", "", "", "", "", Some(""))] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_team_spend_upsert_preserves_keys_and_accumulates( + #[case] team_id: &'static str, + #[case] api_key: &'static str, + #[case] model: &'static str, + #[case] custom_llm_provider: &'static str, + #[case] endpoint: &'static str, + #[case] first_model_group: Option<&'static str>, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test exercises UTC timestamp behavior under a non-UTC session" + )] + sqlx::query("SET LOCAL TIME ZONE 'America/Los_Angeles'") + .execute(&mut *tx) + .await?; + + let first_batch = [ + DailySpend { + entity_id: team_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: first_model_group.map(str::to_owned), + prompt_tokens: 1, + completion_tokens: 2, + api_requests: 3, + successful_requests: 4, + failed_requests: 5, + cache_read_input_tokens: 6, + cache_creation_input_tokens: 7, + total_response_time_ms: 8, + timed_requests: 9, + spend: 0.125, + }, + DailySpend { + entity_id: team_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: "endpoint-sentinel".to_owned(), + model_group: Some("sentinel-endpoint-group".to_owned()), + prompt_tokens: 101, + completion_tokens: 102, + api_requests: 103, + successful_requests: 104, + failed_requests: 105, + cache_read_input_tokens: 106, + cache_creation_input_tokens: 107, + total_response_time_ms: 108, + timed_requests: 109, + spend: 1.25, + }, + DailySpend { + entity_id: team_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: "api-key-sentinel".to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-key-group".to_owned()), + prompt_tokens: 201, + completion_tokens: 202, + api_requests: 203, + successful_requests: 204, + failed_requests: 205, + cache_read_input_tokens: 206, + cache_creation_input_tokens: 207, + total_response_time_ms: 208, + timed_requests: 209, + spend: 2.5, + }, + DailySpend { + entity_id: "entity-sentinel".to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-entity-group".to_owned()), + prompt_tokens: 301, + completion_tokens: 302, + api_requests: 303, + successful_requests: 304, + failed_requests: 305, + cache_read_input_tokens: 306, + cache_creation_input_tokens: 307, + total_response_time_ms: 308, + timed_requests: 309, + spend: 3.75, + }, + ]; + upsert_daily_team_spend(&mut *tx, &first_batch).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the inserted row identity before conflict handling" + )] + let (initial_id, initial_created_at): (String, NaiveDateTime) = sqlx::query_as( + r#"SELECT id, created_at + FROM "LiteLLM_DailyTeamSpend" + WHERE team_id = $1 AND date = $2 AND api_key = $3 AND model = $4 + AND custom_llm_provider = $5 AND endpoint = $6"#, + ) + .bind(team_id) + .bind("2026-01-15") + .bind(api_key) + .bind(model) + .bind(custom_llm_provider) + .bind(endpoint) + .fetch_one(&mut *tx) + .await?; + assert!(!initial_id.is_empty()); + + #[expect( + clippy::disallowed_methods, + reason = "The integration test seeds updated_at before exercising the conflict update" + )] + let seeded_updated_at: NaiveDateTime = sqlx::query_scalar( + r#"UPDATE "LiteLLM_DailyTeamSpend" + SET updated_at = TIMESTAMP '2000-01-01' + WHERE id = $1 + RETURNING updated_at"#, + ) + .bind(&initial_id) + .fetch_one(&mut *tx) + .await?; + + let increment = [DailySpend { + entity_id: team_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("second-group".to_owned()), + prompt_tokens: 10, + completion_tokens: 20, + api_requests: 30, + successful_requests: 40, + failed_requests: 50, + cache_read_input_tokens: 60, + cache_creation_input_tokens: 70, + total_response_time_ms: 80, + timed_requests: 90, + spend: 0.625, + }]; + upsert_daily_team_spend(&mut *tx, &increment).await?; + upsert_daily_team_spend(&mut *tx, &[]).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the transaction timestamp for assertions" + )] + let expected_updated_at: NaiveDateTime = + sqlx::query_scalar("SELECT (NOW() AT TIME ZONE 'UTC')::timestamp(3)") + .fetch_one(&mut *tx) + .await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads all rows to verify attribution and accumulation" + )] + let rows: Vec = sqlx::query_as( + r#"SELECT id, team_id, api_key, model, custom_llm_provider, endpoint, + model_group, mcp_namespaced_tool_name, prompt_tokens, + completion_tokens, api_requests, successful_requests, + failed_requests, cache_read_input_tokens, + cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, date, created_at, updated_at + FROM "LiteLLM_DailyTeamSpend" + WHERE date = $1 AND model = $2 AND custom_llm_provider = $3 + AND mcp_namespaced_tool_name = '' AND team_id = ANY($4) + ORDER BY CASE + WHEN team_id = $5 AND api_key = $6 AND endpoint = $7 THEN 0 + WHEN team_id = $5 AND api_key = $6 AND endpoint = $9 THEN 1 + WHEN team_id = $5 AND api_key = $8 AND endpoint = $7 THEN 2 + WHEN team_id = $10 AND api_key = $6 AND endpoint = $7 THEN 3 + ELSE 4 + END"#, + ) + .bind("2026-01-15") + .bind(model) + .bind(custom_llm_provider) + .bind(vec![team_id, "entity-sentinel"]) + .bind(team_id) + .bind(api_key) + .bind(endpoint) + .bind("api-key-sentinel") + .bind("endpoint-sentinel") + .bind("entity-sentinel") + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 4); + assert_eq!(rows[0].id, initial_id); + assert_eq!(rows[0].team_id.as_deref(), Some(team_id)); + assert_eq!(rows[0].api_key, api_key); + assert_eq!(rows[0].model.as_deref(), Some(model)); + assert_eq!( + rows[0].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[0].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[0].model_group.as_deref(), first_model_group); + assert_eq!(rows[0].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[0].prompt_tokens, + rows[0].completion_tokens, + rows[0].api_requests, + rows[0].successful_requests, + rows[0].failed_requests, + rows[0].cache_read_input_tokens, + rows[0].cache_creation_input_tokens, + rows[0].total_response_time_ms, + rows[0].timed_requests, + rows[0].spend + ), + (11, 22, 33, 44, 55, 66, 77, 88, 99, 0.75) + ); + assert_eq!(rows[0].date, "2026-01-15"); + assert_eq!(rows[0].created_at, initial_created_at); + assert!(rows[0].updated_at > seeded_updated_at); + assert_eq!(rows[0].updated_at, expected_updated_at); + + assert!(!rows[1].id.is_empty()); + assert_eq!(rows[1].team_id.as_deref(), Some(team_id)); + assert_eq!(rows[1].api_key, api_key); + assert_eq!(rows[1].model.as_deref(), Some(model)); + assert_eq!( + rows[1].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[1].endpoint.as_deref(), Some("endpoint-sentinel")); + assert_eq!( + rows[1].model_group.as_deref(), + Some("sentinel-endpoint-group") + ); + assert_eq!(rows[1].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[1].prompt_tokens, + rows[1].completion_tokens, + rows[1].api_requests, + rows[1].successful_requests, + rows[1].failed_requests, + rows[1].cache_read_input_tokens, + rows[1].cache_creation_input_tokens, + rows[1].total_response_time_ms, + rows[1].timed_requests, + rows[1].spend + ), + (101, 102, 103, 104, 105, 106, 107, 108, 109, 1.25) + ); + assert_eq!(rows[1].date, "2026-01-15"); + assert!(rows[1].updated_at > seeded_updated_at); + assert_eq!(rows[1].updated_at, expected_updated_at); + + assert!(!rows[2].id.is_empty()); + assert_eq!(rows[2].team_id.as_deref(), Some(team_id)); + assert_eq!(rows[2].api_key, "api-key-sentinel"); + assert_eq!(rows[2].model.as_deref(), Some(model)); + assert_eq!( + rows[2].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[2].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[2].model_group.as_deref(), Some("sentinel-key-group")); + assert_eq!(rows[2].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[2].prompt_tokens, + rows[2].completion_tokens, + rows[2].api_requests, + rows[2].successful_requests, + rows[2].failed_requests, + rows[2].cache_read_input_tokens, + rows[2].cache_creation_input_tokens, + rows[2].total_response_time_ms, + rows[2].timed_requests, + rows[2].spend + ), + (201, 202, 203, 204, 205, 206, 207, 208, 209, 2.5) + ); + assert_eq!(rows[2].date, "2026-01-15"); + assert!(rows[2].updated_at > seeded_updated_at); + assert_eq!(rows[2].updated_at, expected_updated_at); + + assert!(!rows[3].id.is_empty()); + assert_eq!(rows[3].team_id.as_deref(), Some("entity-sentinel")); + assert_eq!(rows[3].api_key, api_key); + assert_eq!(rows[3].model.as_deref(), Some(model)); + assert_eq!( + rows[3].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[3].endpoint.as_deref(), Some(endpoint)); + assert_eq!( + rows[3].model_group.as_deref(), + Some("sentinel-entity-group") + ); + assert_eq!(rows[3].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[3].prompt_tokens, + rows[3].completion_tokens, + rows[3].api_requests, + rows[3].successful_requests, + rows[3].failed_requests, + rows[3].cache_read_input_tokens, + rows[3].cache_creation_input_tokens, + rows[3].total_response_time_ms, + rows[3].timed_requests, + rows[3].spend + ), + (301, 302, 303, 304, 305, 306, 307, 308, 309, 3.75) + ); + assert_eq!(rows[3].date, "2026-01-15"); + assert!(rows[3].updated_at > seeded_updated_at); + assert_eq!(rows[3].updated_at, expected_updated_at); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index f50440e06de..3f71ae4bf9f 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,3 +1,4 @@ +mod daily_team_spend; mod daily_user_spend; mod end_user_spend; mod key_spend; @@ -7,6 +8,7 @@ mod team_member_spend; mod team_spend; mod user_spend; +pub use daily_team_spend::upsert_daily_team_spend; pub use daily_user_spend::{DailySpend, upsert_daily_user_spend}; pub use end_user_spend::add_end_user_spend; pub use key_spend::{EntitySpend, add_key_spend}; From a783f8e78b87b3c61e834725592c665f7124f7ac Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:08 -0700 Subject: [PATCH 51/73] feat(db): add typed daily end-user spend upsert (#48237) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm-rust/Cargo.lock | 1 + ...3bcab69ff6ad3555a3926537586f5d81d933d.json | 31 + litellm-rust/crates/db/Cargo.toml | 1 + .../spend/upsert_daily_end_user_spend.sql | 40 ++ litellm-rust/crates/db/src/lib.rs | 7 +- .../db/src/spend/daily_end_user_spend.rs | 584 ++++++++++++++++++ litellm-rust/crates/db/src/spend/mod.rs | 2 + 7 files changed, 663 insertions(+), 3 deletions(-) create mode 100644 litellm-rust/crates/db/.sqlx/query-f207ff0f5310dea0833361d55993bcab69ff6ad3555a3926537586f5d81d933d.json create mode 100644 litellm-rust/crates/db/queries/spend/upsert_daily_end_user_spend.sql create mode 100644 litellm-rust/crates/db/src/spend/daily_end_user_spend.rs diff --git a/litellm-rust/Cargo.lock b/litellm-rust/Cargo.lock index d0757ebbd2c..32ae431f863 100644 --- a/litellm-rust/Cargo.lock +++ b/litellm-rust/Cargo.lock @@ -3701,6 +3701,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "strum", "thiserror 2.0.19", "tokio", ] diff --git a/litellm-rust/crates/db/.sqlx/query-f207ff0f5310dea0833361d55993bcab69ff6ad3555a3926537586f5d81d933d.json b/litellm-rust/crates/db/.sqlx/query-f207ff0f5310dea0833361d55993bcab69ff6ad3555a3926537586f5d81d933d.json new file mode 100644 index 00000000000..b2635c549ac --- /dev/null +++ b/litellm-rust/crates/db/.sqlx/query-f207ff0f5310dea0833361d55993bcab69ff6ad3555a3926537586f5d81d933d.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"LiteLLM_DailyEndUserSpend\" (\n id, end_user_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens,\n completion_tokens, api_requests, successful_requests, failed_requests,\n cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms,\n timed_requests, spend, updated_at\n)\nSELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model,\n r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens,\n r.completion_tokens, r.api_requests, r.successful_requests,\n r.failed_requests, r.cache_read, r.cache_creation, r.response_time,\n r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC')\nFROM UNNEST(\n $1::text[], $2::text[], $3::text[], $4::text[], $5::text[],\n $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[],\n $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[],\n $16::int8[], $17::float8[], $18::text[]\n) AS r(\n entity_id, date, api_key, model, custom_llm_provider, endpoint,\n model_group, prompt_tokens, completion_tokens, api_requests,\n successful_requests, failed_requests, cache_read, cache_creation,\n response_time, timed_requests, spend, mcp_namespaced_tool_name\n)\nORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider,\n r.mcp_namespaced_tool_name, r.endpoint\nON CONFLICT (\n end_user_id, date, api_key, model, custom_llm_provider,\n mcp_namespaced_tool_name, endpoint\n) DO UPDATE SET\n prompt_tokens = \"LiteLLM_DailyEndUserSpend\".prompt_tokens + EXCLUDED.prompt_tokens,\n completion_tokens = \"LiteLLM_DailyEndUserSpend\".completion_tokens + EXCLUDED.completion_tokens,\n api_requests = \"LiteLLM_DailyEndUserSpend\".api_requests + EXCLUDED.api_requests,\n successful_requests = \"LiteLLM_DailyEndUserSpend\".successful_requests + EXCLUDED.successful_requests,\n failed_requests = \"LiteLLM_DailyEndUserSpend\".failed_requests + EXCLUDED.failed_requests,\n cache_read_input_tokens = \"LiteLLM_DailyEndUserSpend\".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens,\n cache_creation_input_tokens = \"LiteLLM_DailyEndUserSpend\".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens,\n total_response_time_ms = \"LiteLLM_DailyEndUserSpend\".total_response_time_ms + EXCLUDED.total_response_time_ms,\n timed_requests = \"LiteLLM_DailyEndUserSpend\".timed_requests + EXCLUDED.timed_requests,\n spend = \"LiteLLM_DailyEndUserSpend\".spend + EXCLUDED.spend,\n updated_at = (NOW() AT TIME ZONE 'UTC')\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "TextArray", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Int8Array", + "Float8Array", + "TextArray" + ] + }, + "nullable": [] + }, + "hash": "f207ff0f5310dea0833361d55993bcab69ff6ad3555a3926537586f5d81d933d" +} diff --git a/litellm-rust/crates/db/Cargo.toml b/litellm-rust/crates/db/Cargo.toml index 8c1a35c7535..4f8fe4e28e2 100644 --- a/litellm-rust/crates/db/Cargo.toml +++ b/litellm-rust/crates/db/Cargo.toml @@ -11,6 +11,7 @@ macro_rules_attribute.workspace = true serde.workspace = true serde_json.workspace = true sqlx.workspace = true +strum.workspace = true thiserror.workspace = true [dev-dependencies] diff --git a/litellm-rust/crates/db/queries/spend/upsert_daily_end_user_spend.sql b/litellm-rust/crates/db/queries/spend/upsert_daily_end_user_spend.sql new file mode 100644 index 00000000000..8c4936e1a7a --- /dev/null +++ b/litellm-rust/crates/db/queries/spend/upsert_daily_end_user_spend.sql @@ -0,0 +1,40 @@ +INSERT INTO "LiteLLM_DailyEndUserSpend" ( + id, end_user_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint, model_group, prompt_tokens, + completion_tokens, api_requests, successful_requests, failed_requests, + cache_read_input_tokens, cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, updated_at +) +SELECT gen_random_uuid()::text, r.entity_id, r.date, r.api_key, r.model, + r.custom_llm_provider, r.mcp_namespaced_tool_name, r.endpoint, r.model_group, r.prompt_tokens, + r.completion_tokens, r.api_requests, r.successful_requests, + r.failed_requests, r.cache_read, r.cache_creation, r.response_time, + r.timed_requests, r.spend, (NOW() AT TIME ZONE 'UTC') +FROM UNNEST( + $1::text[], $2::text[], $3::text[], $4::text[], $5::text[], + $6::text[], $7::text[], $8::int8[], $9::int8[], $10::int8[], + $11::int8[], $12::int8[], $13::int8[], $14::int8[], $15::int8[], + $16::int8[], $17::float8[], $18::text[] +) AS r( + entity_id, date, api_key, model, custom_llm_provider, endpoint, + model_group, prompt_tokens, completion_tokens, api_requests, + successful_requests, failed_requests, cache_read, cache_creation, + response_time, timed_requests, spend, mcp_namespaced_tool_name +) +ORDER BY r.date, r.entity_id, r.api_key, r.model, r.custom_llm_provider, + r.mcp_namespaced_tool_name, r.endpoint +ON CONFLICT ( + end_user_id, date, api_key, model, custom_llm_provider, + mcp_namespaced_tool_name, endpoint +) DO UPDATE SET + prompt_tokens = "LiteLLM_DailyEndUserSpend".prompt_tokens + EXCLUDED.prompt_tokens, + completion_tokens = "LiteLLM_DailyEndUserSpend".completion_tokens + EXCLUDED.completion_tokens, + api_requests = "LiteLLM_DailyEndUserSpend".api_requests + EXCLUDED.api_requests, + successful_requests = "LiteLLM_DailyEndUserSpend".successful_requests + EXCLUDED.successful_requests, + failed_requests = "LiteLLM_DailyEndUserSpend".failed_requests + EXCLUDED.failed_requests, + cache_read_input_tokens = "LiteLLM_DailyEndUserSpend".cache_read_input_tokens + EXCLUDED.cache_read_input_tokens, + cache_creation_input_tokens = "LiteLLM_DailyEndUserSpend".cache_creation_input_tokens + EXCLUDED.cache_creation_input_tokens, + total_response_time_ms = "LiteLLM_DailyEndUserSpend".total_response_time_ms + EXCLUDED.total_response_time_ms, + timed_requests = "LiteLLM_DailyEndUserSpend".timed_requests + EXCLUDED.timed_requests, + spend = "LiteLLM_DailyEndUserSpend".spend + EXCLUDED.spend, + updated_at = (NOW() AT TIME ZONE 'UTC') diff --git a/litellm-rust/crates/db/src/lib.rs b/litellm-rust/crates/db/src/lib.rs index 5b88bcabf32..541f97c7db9 100644 --- a/litellm-rust/crates/db/src/lib.rs +++ b/litellm-rust/crates/db/src/lib.rs @@ -19,7 +19,8 @@ pub use end_user::{EndUser, RateLimits, find_end_user}; pub use error::Error; pub use failure::DbFailure; pub use spend::{ - DailySpend, EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, add_end_user_spend, - add_key_spend, add_team_member_spend, add_team_spend, add_user_spend, begin, insert_spend_logs, - lock_team, upsert_daily_team_spend, upsert_daily_user_spend, + DailySpend, DailyTable, EntitySpend, SpendLog, SpendLogRow, TeamMemberSpend, + add_end_user_spend, add_key_spend, add_team_member_spend, add_team_spend, add_user_spend, + begin, insert_spend_logs, lock_team, upsert_daily_end_user_spend, upsert_daily_spend, + upsert_daily_team_spend, upsert_daily_user_spend, }; diff --git a/litellm-rust/crates/db/src/spend/daily_end_user_spend.rs b/litellm-rust/crates/db/src/spend/daily_end_user_spend.rs new file mode 100644 index 00000000000..6c88d47cdcc --- /dev/null +++ b/litellm-rust/crates/db/src/spend/daily_end_user_spend.rs @@ -0,0 +1,584 @@ +use sqlx::PgExecutor; + +use super::{DailySpend, upsert_daily_team_spend, upsert_daily_user_spend}; +use crate::Error; + +pub async fn upsert_daily_end_user_spend( + executor: impl PgExecutor<'_>, + rows: &[DailySpend], +) -> Result<(), Error> { + if rows.is_empty() { + return Ok(()); + } + macro_rules! column { + ($field:ident) => { + rows.iter() + .map(|row| row.$field.clone()) + .collect::>() + }; + } + sqlx::query_file!( + "queries/spend/upsert_daily_end_user_spend.sql", + &column!(entity_id), + &column!(date), + &column!(api_key), + &column!(model), + &column!(custom_llm_provider), + &column!(endpoint), + &column!(model_group) as &[Option], + &column!(prompt_tokens), + &column!(completion_tokens), + &column!(api_requests), + &column!(successful_requests), + &column!(failed_requests), + &column!(cache_read_input_tokens), + &column!(cache_creation_input_tokens), + &column!(total_response_time_ms), + &column!(timed_requests), + &column!(spend), + &column!(mcp_namespaced_tool_name), + ) + .execute(executor) + .await?; + Ok(()) +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::VariantArray)] +pub enum DailyTable { + User, + Team, + EndUser, +} + +pub async fn upsert_daily_spend( + executor: impl PgExecutor<'_>, + table: DailyTable, + rows: &[DailySpend], +) -> Result<(), Error> { + match table { + DailyTable::User => upsert_daily_user_spend(executor, rows).await, + DailyTable::Team => upsert_daily_team_spend(executor, rows).await, + DailyTable::EndUser => upsert_daily_end_user_spend(executor, rows).await, + } +} + +#[cfg(test)] +mod tests { + use std::error::Error as StdError; + + use super::{DailySpend, DailyTable, upsert_daily_end_user_spend, upsert_daily_spend}; + use crate::{DailyEndUserRow, test_support::database_pool}; + use rstest::rstest; + use sqlx::{PgPool, types::chrono::NaiveDateTime}; + + #[rstest] + #[case::full_keys( + "daily-end-user-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + Some("first-group") + )] + #[case::initial_model_group_none( + "daily-end-user-base", + "daily-key-base", + "daily-model", + "daily-provider", + "/chat/completions", + None + )] + #[case::empty_optional_keys("", "", "", "", "", Some(""))] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_end_user_spend_upsert_preserves_keys_and_accumulates( + #[case] end_user_id: &'static str, + #[case] api_key: &'static str, + #[case] model: &'static str, + #[case] custom_llm_provider: &'static str, + #[case] endpoint: &'static str, + #[case] first_model_group: Option<&'static str>, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test exercises UTC timestamp behavior under a non-UTC session" + )] + sqlx::query("SET LOCAL TIME ZONE 'America/Los_Angeles'") + .execute(&mut *tx) + .await?; + + let first_batch = [ + DailySpend { + entity_id: end_user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: first_model_group.map(str::to_owned), + prompt_tokens: 1, + completion_tokens: 2, + api_requests: 3, + successful_requests: 4, + failed_requests: 5, + cache_read_input_tokens: 6, + cache_creation_input_tokens: 7, + total_response_time_ms: 8, + timed_requests: 9, + spend: 0.125, + }, + DailySpend { + entity_id: end_user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: "endpoint-sentinel".to_owned(), + model_group: Some("sentinel-endpoint-group".to_owned()), + prompt_tokens: 101, + completion_tokens: 102, + api_requests: 103, + successful_requests: 104, + failed_requests: 105, + cache_read_input_tokens: 106, + cache_creation_input_tokens: 107, + total_response_time_ms: 108, + timed_requests: 109, + spend: 1.25, + }, + DailySpend { + entity_id: end_user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: "api-key-sentinel".to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-key-group".to_owned()), + prompt_tokens: 201, + completion_tokens: 202, + api_requests: 203, + successful_requests: 204, + failed_requests: 205, + cache_read_input_tokens: 206, + cache_creation_input_tokens: 207, + total_response_time_ms: 208, + timed_requests: 209, + spend: 2.5, + }, + DailySpend { + entity_id: "entity-sentinel".to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("sentinel-entity-group".to_owned()), + prompt_tokens: 301, + completion_tokens: 302, + api_requests: 303, + successful_requests: 304, + failed_requests: 305, + cache_read_input_tokens: 306, + cache_creation_input_tokens: 307, + total_response_time_ms: 308, + timed_requests: 309, + spend: 3.75, + }, + ]; + upsert_daily_end_user_spend(&mut *tx, &first_batch).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the inserted row identity before conflict handling" + )] + let (initial_id, initial_created_at): (String, NaiveDateTime) = sqlx::query_as( + r#"SELECT id, created_at + FROM "LiteLLM_DailyEndUserSpend" + WHERE end_user_id = $1 AND date = $2 AND api_key = $3 AND model = $4 + AND custom_llm_provider = $5 AND endpoint = $6"#, + ) + .bind(end_user_id) + .bind("2026-01-15") + .bind(api_key) + .bind(model) + .bind(custom_llm_provider) + .bind(endpoint) + .fetch_one(&mut *tx) + .await?; + assert!(!initial_id.is_empty()); + + #[expect( + clippy::disallowed_methods, + reason = "The integration test seeds updated_at before exercising the conflict update" + )] + let seeded_updated_at: NaiveDateTime = sqlx::query_scalar( + r#"UPDATE "LiteLLM_DailyEndUserSpend" + SET updated_at = TIMESTAMP '2000-01-01' + WHERE id = $1 + RETURNING updated_at"#, + ) + .bind(&initial_id) + .fetch_one(&mut *tx) + .await?; + + let increment = [DailySpend { + entity_id: end_user_id.to_owned(), + date: "2026-01-15".to_owned(), + api_key: api_key.to_owned(), + model: model.to_owned(), + custom_llm_provider: custom_llm_provider.to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: endpoint.to_owned(), + model_group: Some("second-group".to_owned()), + prompt_tokens: 10, + completion_tokens: 20, + api_requests: 30, + successful_requests: 40, + failed_requests: 50, + cache_read_input_tokens: 60, + cache_creation_input_tokens: 70, + total_response_time_ms: 80, + timed_requests: 90, + spend: 0.625, + }]; + upsert_daily_end_user_spend(&mut *tx, &increment).await?; + upsert_daily_end_user_spend(&mut *tx, &[]).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test captures the transaction timestamp for assertions" + )] + let expected_updated_at: NaiveDateTime = + sqlx::query_scalar("SELECT (NOW() AT TIME ZONE 'UTC')::timestamp(3)") + .fetch_one(&mut *tx) + .await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads all rows to verify attribution and accumulation" + )] + let rows: Vec = sqlx::query_as( + r#"SELECT id, end_user_id, api_key, model, custom_llm_provider, endpoint, + model_group, mcp_namespaced_tool_name, prompt_tokens, + completion_tokens, api_requests, successful_requests, + failed_requests, cache_read_input_tokens, + cache_creation_input_tokens, total_response_time_ms, + timed_requests, spend, date, created_at, updated_at + FROM "LiteLLM_DailyEndUserSpend" + WHERE date = $1 AND model = $2 AND custom_llm_provider = $3 + AND mcp_namespaced_tool_name = '' AND end_user_id = ANY($4) + ORDER BY CASE + WHEN end_user_id = $5 AND api_key = $6 AND endpoint = $7 THEN 0 + WHEN end_user_id = $5 AND api_key = $6 AND endpoint = $9 THEN 1 + WHEN end_user_id = $5 AND api_key = $8 AND endpoint = $7 THEN 2 + WHEN end_user_id = $10 AND api_key = $6 AND endpoint = $7 THEN 3 + ELSE 4 + END"#, + ) + .bind("2026-01-15") + .bind(model) + .bind(custom_llm_provider) + .bind(vec![end_user_id, "entity-sentinel"]) + .bind(end_user_id) + .bind(api_key) + .bind(endpoint) + .bind("api-key-sentinel") + .bind("endpoint-sentinel") + .bind("entity-sentinel") + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 4); + assert_eq!(rows[0].id, initial_id); + assert_eq!(rows[0].end_user_id.as_deref(), Some(end_user_id)); + assert_eq!(rows[0].api_key, api_key); + assert_eq!(rows[0].model.as_deref(), Some(model)); + assert_eq!( + rows[0].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[0].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[0].model_group.as_deref(), first_model_group); + assert_eq!(rows[0].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[0].prompt_tokens, + rows[0].completion_tokens, + rows[0].api_requests, + rows[0].successful_requests, + rows[0].failed_requests, + rows[0].cache_read_input_tokens, + rows[0].cache_creation_input_tokens, + rows[0].total_response_time_ms, + rows[0].timed_requests, + rows[0].spend + ), + (11, 22, 33, 44, 55, 66, 77, 88, 99, 0.75) + ); + assert_eq!(rows[0].date, "2026-01-15"); + assert_eq!(rows[0].created_at, initial_created_at); + assert!(rows[0].updated_at > seeded_updated_at); + assert_eq!(rows[0].updated_at, expected_updated_at); + + assert!(!rows[1].id.is_empty()); + assert_eq!(rows[1].end_user_id.as_deref(), Some(end_user_id)); + assert_eq!(rows[1].api_key, api_key); + assert_eq!(rows[1].model.as_deref(), Some(model)); + assert_eq!( + rows[1].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[1].endpoint.as_deref(), Some("endpoint-sentinel")); + assert_eq!( + rows[1].model_group.as_deref(), + Some("sentinel-endpoint-group") + ); + assert_eq!(rows[1].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[1].prompt_tokens, + rows[1].completion_tokens, + rows[1].api_requests, + rows[1].successful_requests, + rows[1].failed_requests, + rows[1].cache_read_input_tokens, + rows[1].cache_creation_input_tokens, + rows[1].total_response_time_ms, + rows[1].timed_requests, + rows[1].spend + ), + (101, 102, 103, 104, 105, 106, 107, 108, 109, 1.25) + ); + assert_eq!(rows[1].date, "2026-01-15"); + assert!(rows[1].updated_at > seeded_updated_at); + assert_eq!(rows[1].updated_at, expected_updated_at); + + assert!(!rows[2].id.is_empty()); + assert_eq!(rows[2].end_user_id.as_deref(), Some(end_user_id)); + assert_eq!(rows[2].api_key, "api-key-sentinel"); + assert_eq!(rows[2].model.as_deref(), Some(model)); + assert_eq!( + rows[2].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[2].endpoint.as_deref(), Some(endpoint)); + assert_eq!(rows[2].model_group.as_deref(), Some("sentinel-key-group")); + assert_eq!(rows[2].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[2].prompt_tokens, + rows[2].completion_tokens, + rows[2].api_requests, + rows[2].successful_requests, + rows[2].failed_requests, + rows[2].cache_read_input_tokens, + rows[2].cache_creation_input_tokens, + rows[2].total_response_time_ms, + rows[2].timed_requests, + rows[2].spend + ), + (201, 202, 203, 204, 205, 206, 207, 208, 209, 2.5) + ); + assert_eq!(rows[2].date, "2026-01-15"); + assert!(rows[2].updated_at > seeded_updated_at); + assert_eq!(rows[2].updated_at, expected_updated_at); + + assert!(!rows[3].id.is_empty()); + assert_eq!(rows[3].end_user_id.as_deref(), Some("entity-sentinel")); + assert_eq!(rows[3].api_key, api_key); + assert_eq!(rows[3].model.as_deref(), Some(model)); + assert_eq!( + rows[3].custom_llm_provider.as_deref(), + Some(custom_llm_provider) + ); + assert_eq!(rows[3].endpoint.as_deref(), Some(endpoint)); + assert_eq!( + rows[3].model_group.as_deref(), + Some("sentinel-entity-group") + ); + assert_eq!(rows[3].mcp_namespaced_tool_name.as_deref(), Some("")); + assert_eq!( + ( + rows[3].prompt_tokens, + rows[3].completion_tokens, + rows[3].api_requests, + rows[3].successful_requests, + rows[3].failed_requests, + rows[3].cache_read_input_tokens, + rows[3].cache_creation_input_tokens, + rows[3].total_response_time_ms, + rows[3].timed_requests, + rows[3].spend + ), + (301, 302, 303, 304, 305, 306, 307, 308, 309, 3.75) + ); + assert_eq!(rows[3].date, "2026-01-15"); + assert!(rows[3].updated_at > seeded_updated_at); + assert_eq!(rows[3].updated_at, expected_updated_at); + Ok(()) + } + + #[rstest] + #[case::distinct_tools("server/tool-a", "server/tool-b")] + #[case::empty_and_named_tool("", "server/tool-b")] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_end_user_spend_preserves_tool_attribution( + #[case] first_tool: &str, + #[case] second_tool: &str, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + let first = DailySpend { + entity_id: "daily-end-user-tools".to_owned(), + date: "2026-02-21".to_owned(), + api_key: "tool-key".to_owned(), + model: "tool-model".to_owned(), + custom_llm_provider: "tool-provider".to_owned(), + endpoint: "/mcp".to_owned(), + mcp_namespaced_tool_name: first_tool.to_owned(), + prompt_tokens: 2, + spend: 0.25, + ..Default::default() + }; + let second = DailySpend { + mcp_namespaced_tool_name: second_tool.to_owned(), + prompt_tokens: 5, + spend: 0.5, + ..first.clone() + }; + upsert_daily_end_user_spend(&mut *tx, &[second, first.clone()]).await?; + upsert_daily_end_user_spend(&mut *tx, &[first]).await?; + + #[expect( + clippy::disallowed_methods, + reason = "The integration test reads stored tool attribution and counters" + )] + let rows: Vec = sqlx::query_as( + r#"SELECT * FROM "LiteLLM_DailyEndUserSpend" + WHERE end_user_id = $1 AND date = $2 AND api_key = $3 + ORDER BY mcp_namespaced_tool_name"#, + ) + .bind("daily-end-user-tools") + .bind("2026-02-21") + .bind("tool-key") + .fetch_all(&mut *tx) + .await?; + + assert_eq!(rows.len(), 2); + assert_eq!( + rows[0].mcp_namespaced_tool_name.as_deref(), + Some(first_tool) + ); + assert_eq!( + rows[1].mcp_namespaced_tool_name.as_deref(), + Some(second_tool) + ); + assert_eq!((rows[0].prompt_tokens, rows[0].spend), (4, 0.5)); + assert_eq!((rows[1].prompt_tokens, rows[1].spend), (5, 0.5)); + assert_ne!(rows[0].id, rows[1].id); + Ok(()) + } + + #[rstest] + #[case::user(DailyTable::User, "daily-dispatch-user")] + #[case::team(DailyTable::Team, "daily-dispatch-team")] + #[case::end_user(DailyTable::EndUser, "daily-dispatch-end-user")] + #[tokio::test] + #[ignore = "requires DATABASE_URL pointing to a Prisma-migrated PostgreSQL database"] + async fn daily_spend_dispatches_to_selected_table( + #[case] table: DailyTable, + #[case] entity_id: &'static str, + #[future(awt)] database_pool: Result>, + ) -> Result<(), Box> { + let pool = database_pool?; + let mut tx = pool.begin().await?; + let api_key = format!("{entity_id}-key"); + let rows = [DailySpend { + entity_id: entity_id.to_owned(), + date: "2026-02-20".to_owned(), + api_key: api_key.clone(), + model: "dispatcher-model".to_owned(), + custom_llm_provider: "dispatcher-provider".to_owned(), + mcp_namespaced_tool_name: String::new(), + endpoint: "/dispatcher".to_owned(), + model_group: Some("dispatcher-group".to_owned()), + prompt_tokens: 1, + completion_tokens: 2, + api_requests: 3, + successful_requests: 4, + failed_requests: 5, + cache_read_input_tokens: 6, + cache_creation_input_tokens: 7, + total_response_time_ms: 8, + timed_requests: 9, + spend: 0.125, + }]; + upsert_daily_spend(&mut *tx, table, &rows).await?; + + let count: i64 = match table { + DailyTable::User => + { + #[expect( + clippy::disallowed_methods, + reason = "The integration test checks the user-table dispatcher route" + )] + sqlx::query_scalar( + r#"SELECT COUNT(*) FROM "LiteLLM_DailyUserSpend" + WHERE user_id = $1 AND date = $2 AND api_key = $3 AND model = $4"#, + ) + .bind(entity_id) + .bind("2026-02-20") + .bind(&api_key) + .bind("dispatcher-model") + .fetch_one(&mut *tx) + .await? + } + DailyTable::Team => + { + #[expect( + clippy::disallowed_methods, + reason = "The integration test checks the team-table dispatcher route" + )] + sqlx::query_scalar( + r#"SELECT COUNT(*) FROM "LiteLLM_DailyTeamSpend" + WHERE team_id = $1 AND date = $2 AND api_key = $3 AND model = $4"#, + ) + .bind(entity_id) + .bind("2026-02-20") + .bind(&api_key) + .bind("dispatcher-model") + .fetch_one(&mut *tx) + .await? + } + DailyTable::EndUser => + { + #[expect( + clippy::disallowed_methods, + reason = "The integration test checks the end-user-table dispatcher route" + )] + sqlx::query_scalar( + r#"SELECT COUNT(*) FROM "LiteLLM_DailyEndUserSpend" + WHERE end_user_id = $1 AND date = $2 AND api_key = $3 AND model = $4"#, + ) + .bind(entity_id) + .bind("2026-02-20") + .bind(&api_key) + .bind("dispatcher-model") + .fetch_one(&mut *tx) + .await? + } + }; + assert_eq!(count, 1); + Ok(()) + } +} diff --git a/litellm-rust/crates/db/src/spend/mod.rs b/litellm-rust/crates/db/src/spend/mod.rs index 3f71ae4bf9f..da255843547 100644 --- a/litellm-rust/crates/db/src/spend/mod.rs +++ b/litellm-rust/crates/db/src/spend/mod.rs @@ -1,3 +1,4 @@ +mod daily_end_user_spend; mod daily_team_spend; mod daily_user_spend; mod end_user_spend; @@ -8,6 +9,7 @@ mod team_member_spend; mod team_spend; mod user_spend; +pub use daily_end_user_spend::{DailyTable, upsert_daily_end_user_spend, upsert_daily_spend}; pub use daily_team_spend::upsert_daily_team_spend; pub use daily_user_spend::{DailySpend, upsert_daily_user_spend}; pub use end_user_spend::add_end_user_spend; From b9825e1f87aecd13c87f53851f3fd0d4cb16bfef Mon Sep 17 00:00:00 2001 From: yujonglee Date: Mon, 26 Oct 2026 17:05:08 -0700 Subject: [PATCH 52/73] docs: document incremental database access migration (#48476) * docs(rust): document incremental database access migration * docs(rust): organize database guidance by task * docs: move database access skill to repository root --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .agents/skills/database-access/SKILL.md | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .agents/skills/database-access/SKILL.md diff --git a/.agents/skills/database-access/SKILL.md b/.agents/skills/database-access/SKILL.md new file mode 100644 index 00000000000..796dfafe4f6 --- /dev/null +++ b/.agents/skills/database-access/SKILL.md @@ -0,0 +1,52 @@ +--- +name: database-access +description: Add database queries, extract existing Prisma calls into repositories, or explicitly port queries to Rust and SQLx without expanding migration scope +--- + +# Database access + +## Adding new database access + +- Put Python database access behind an operation in `litellm/repositories` +- Give callers typed inputs and outputs, not Prisma clients, models, or query dictionaries +- Keep query execution and backend selection inside the repository +- Prisma is fine for the implementation, adding a query does not require a Rust port +- Native Rust callers use `litellm-rust/crates/db` directly + +## Asked to move existing access behind a repository + +- Identify the calls being moved and their transaction boundary +- Move the existing Prisma implementation into the relevant repository +- Replace those direct database calls with repository operations +- Preserve return values, errors, and transaction behavior, with focused regression tests +- Keep this an extraction, not a backend migration or unrelated cleanup + +## An equivalent Rust or SQLx query already exists + +- Leave it and the current backend selection unchanged unless integration is explicitly requested +- Its existence alone does not justify switching a Prisma caller to Rust +- Repository extraction can keep using Prisma even when the Rust query is available + +## Asked to port a Prisma query to Rust or SQLx + +- Identify the existing inputs, outputs, errors, and transaction boundary +- Check `litellm-rust/crates/db` for an equivalent query and reuse it if its behavior matches +- Otherwise follow `litellm-rust/crates/db/AGENTS.md` + - Put static SQL in `queries/` and compile-check it with SQLx + - Keep typed inputs and reusable row projections in `src/` + - Refresh SQLx query metadata after SQL changes +- Keep Python conversion in the Python bridge, not in `litellm-rust/crates/db` +- When caller integration is requested, expose the Rust operation through the bridge and call it from the repository + - A query-only port can land without switching callers +- Verify behavior against real PostgreSQL before switching callers + - Check relevant null handling, conflicts, ordering, errors, and transaction semantics +- Remove the old implementation only when no callers need it + +## Migration context and boundaries + +- Long-term direction is to centralize Python database access in repositories, then replace their Prisma implementations incrementally with Rust APIs +- Mixed Prisma and Rust implementations are expected during the transition, unrelated existing calls can stay as they are +- Both backends may have separate pools through PgBouncer, size them together and verify pooling-mode compatibility +- PgBouncer does not share transactions between Prisma and SQLx, keep each atomic unit of work on one backend and transaction +- Never retry a failed Rust write through Prisma when it may already have committed +- Prisma owns schema migrations today, eventual SQLx migration ownership and Prisma removal are separate planned steps, not part of a query port From 1263f70ab63dc662ce043687a85cece965d76c46 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:05:37 -0700 Subject: [PATCH 53/73] feat(ui): budget period tooltip on key, team, user and org spend tiles (#48487) * feat(ui): budget period tooltip on key, team, user and org spend tiles Adds a shared BudgetPeriodTooltip to the four detail-view spend tiles showing the current budget period, max budget, reset interval and spend so far, and makes organization create schedule budget_reset_at and organization info return it Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): give the budget period tooltip trigger its own aria label The default SimpleTooltip trigger shares the question-circle label with InheritedBudgetHint, which broke the key budget display tests that count that icon Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): match the proxy's budget window rules in the period tooltip 30d and Nmo now span calendar months like get_budget_window_start, the arithmetic runs in UTC so a viewer's DST change cannot shift the start, the hourly/daily/weekly/monthly aliases the proxy accepts are normalized before parsing, and spend without a duration reads as lifetime spend Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/_types.py | 2 +- .../organization_endpoints.py | 3 +- .../test_organization_endpoints.py | 65 +++++++++++++++ .../_components/view_users/user_info_view.tsx | 11 ++- .../organization/organization_view.tsx | 11 ++- .../shared/BudgetPeriodTooltip.test.tsx | 83 +++++++++++++++++++ .../components/shared/BudgetPeriodTooltip.tsx | 65 +++++++++++++++ .../src/components/team/TeamInfo.tsx | 11 ++- .../components/templates/key_info_view.tsx | 11 ++- ui/litellm-dashboard/src/lib/http/schema.d.ts | 2 +- .../src/utils/budgetUtils.test.ts | 40 ++++++++- ui/litellm-dashboard/src/utils/budgetUtils.ts | 41 +++++++++ 12 files changed, 336 insertions(+), 9 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.tsx diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 6280e2caf87..49d23d82aa9 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -3277,7 +3277,7 @@ class LiteLLM_OrganizationTableWithMembers(LiteLLM_OrganizationTable): members: list[LiteLLM_OrganizationMembershipTable] = [] teams: list[LiteLLM_TeamTable] = [] - litellm_budget_table: LiteLLM_BudgetTable | None = None + litellm_budget_table: LiteLLM_BudgetTableFull | None = None created_at: datetime updated_at: datetime diff --git a/litellm/proxy/management_endpoints/organization_endpoints.py b/litellm/proxy/management_endpoints/organization_endpoints.py index d7a44cdd0b8..e1d19d8efae 100644 --- a/litellm/proxy/management_endpoints/organization_endpoints.py +++ b/litellm/proxy/management_endpoints/organization_endpoints.py @@ -504,9 +504,8 @@ async def new_organization( _budget: Final = await _table(BudgetRepository(prisma_client)).create( data={ - **new_budget, + **build_budget_write_data(new_budget, user_api_key_dict.user_id or litellm_proxy_admin_name), "created_by": user_api_key_dict.user_id or litellm_proxy_admin_name, - "updated_by": user_api_key_dict.user_id or litellm_proxy_admin_name, } ) diff --git a/tests/unit/proxy/management_endpoints/test_organization_endpoints.py b/tests/unit/proxy/management_endpoints/test_organization_endpoints.py index a5cae02ea10..16c59aa20b9 100644 --- a/tests/unit/proxy/management_endpoints/test_organization_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_organization_endpoints.py @@ -1615,3 +1615,68 @@ async def test_delete_organization_is_refused_while_one_of_its_teams_holds_a_ptu prisma_client.db.litellm_teamtable.delete_many.assert_not_called() prisma_client.db.litellm_verificationtoken.delete_many.assert_not_called() prisma_client.db.litellm_organizationtable.delete.assert_not_called() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("budget_duration", ["30d", None]) +async def test_new_organization_schedules_budget_reset_from_duration(monkeypatch, budget_duration): + """/organization/new with an inline budget_duration must write budget_reset_at on the auto-created + budget row, the way team create and /budget/new do, or the org budget never resets and the UI + cannot show the current budget period.""" + from datetime import datetime + + from litellm.proxy._types import LitellmUserRoles, NewOrganizationRequest, UserAPIKeyAuth + from litellm.proxy.management_endpoints.organization_endpoints import new_organization + from litellm.proxy.utils import PrismaClient + + prisma_client = MagicMock() + prisma_client.jsonify_object = MagicMock(side_effect=lambda data: PrismaClient.jsonify_object(prisma_client, data)) + prisma_client.db.litellm_usertable.find_unique = AsyncMock(return_value=None) + prisma_client.db.litellm_budgettable.create = AsyncMock(return_value=MagicMock(budget_id="budget-1")) + prisma_client.db.litellm_organizationtable.create = AsyncMock(return_value={"organization_id": "org-1"}) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", prisma_client) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", MagicMock()) + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True, raising=False) + + await new_organization( + data=NewOrganizationRequest(organization_alias="org", max_budget=100, budget_duration=budget_duration), + user_api_key_dict=UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + budget_write = prisma_client.db.litellm_budgettable.create.await_args.kwargs["data"] + if budget_duration is None: + assert "budget_reset_at" not in budget_write, budget_write + return + reset_at = budget_write["budget_reset_at"] + assert isinstance(reset_at, datetime) and reset_at.tzinfo is not None, budget_write + assert budget_write["budget_duration"] == budget_duration + + +def test_organization_info_response_keeps_budget_reset_at(): + """/organization/info must return the budget row's budget_reset_at so the dashboard can show + which budget period the org's spend covers.""" + from datetime import datetime, timezone + + from litellm.proxy._types import LiteLLM_OrganizationTableWithMembers + + reset_at = datetime(2026, 11, 1, tzinfo=timezone.utc) + created_at = datetime(2026, 10, 1, tzinfo=timezone.utc) + response = LiteLLM_OrganizationTableWithMembers( + organization_id="org-1", + organization_alias="org", + budget_id="budget-1", + models=[], + created_by="admin", + updated_by="admin", + created_at=created_at, + updated_at=created_at, + litellm_budget_table={ + "budget_id": "budget-1", + "max_budget": 100.0, + "budget_duration": "30d", + "budget_reset_at": reset_at, + "created_at": created_at, + }, + ) + + assert response.model_dump()["litellm_budget_table"]["budget_reset_at"] == reset_at diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx index 2056142b50a..476c043a258 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx @@ -47,6 +47,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u import ScopedSavingsTab from "@/components/shared/ScopedSavingsTab"; import { AutoRouterUsageView } from "@/app/(dashboard)/cost-optimization/_components/AutoRouterBenchmarksTab"; import { useActivityDateRange } from "@/app/(dashboard)/cost-optimization/_components/useDailyActivityRange"; +import { BudgetPeriodTooltip } from "@/components/shared/BudgetPeriodTooltip"; interface UserInfoViewProps { userId: string; @@ -507,7 +508,15 @@ export default function UserInfoView({
-

Spend

+

+ Spend + +

${formatNumberWithCommas(userData.spend || 0, 2)}

diff --git a/ui/litellm-dashboard/src/components/organization/organization_view.tsx b/ui/litellm-dashboard/src/components/organization/organization_view.tsx index f325e92d2b5..2be549261c3 100644 --- a/ui/litellm-dashboard/src/components/organization/organization_view.tsx +++ b/ui/litellm-dashboard/src/components/organization/organization_view.tsx @@ -27,6 +27,7 @@ import ObjectPermissionsView from "../object_permissions_view"; import MemberModal from "../team/EditMembership"; import { OrgSettingsForm } from "./org-settings/OrgSettingsForm"; import { ORGANIZATION_TAB_URL_KEY, ORGANIZATION_TABS, type OrganizationTab } from "./organizationTabs"; +import { BudgetPeriodTooltip } from "@/components/shared/BudgetPeriodTooltip"; interface OrganizationInfoProps { organizationId: string; @@ -193,7 +194,15 @@ const OrganizationInfoView: React.FC = ({ -

Budget Status

+

+ Budget Status + +

${formatNumberWithCommas(orgData.spend, 4)}

diff --git a/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.test.tsx b/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.test.tsx new file mode 100644 index 00000000000..4d77218a776 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.test.tsx @@ -0,0 +1,83 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it } from "vitest"; + +import { BudgetPeriodTooltip, budgetPeriodLines } from "./BudgetPeriodTooltip"; + +const originalTimezone: string | undefined = process.env.TZ; + +afterEach(() => { + if (originalTimezone === undefined) { + delete process.env.TZ; + return; + } + process.env.TZ = originalTimezone; +}); + +const scheduled = { spend: 12.5, maxBudget: 100, budgetDuration: "30d", budgetResetAt: "2026-04-01T00:00:00Z" }; +const unparseableDuration = { ...scheduled, spend: 0, budgetDuration: "fortnight" }; +const unscheduled = { spend: 0, maxBudget: 100, budgetDuration: "30d", budgetResetAt: null }; +const lifetime = { spend: 3, maxBudget: 50, budgetDuration: null, budgetResetAt: null }; +const noBudget = { spend: null, maxBudget: null, budgetDuration: "30d", budgetResetAt: null }; +const noBudgetNoDuration = { spend: 2, maxBudget: null, budgetDuration: null, budgetResetAt: null }; + +describe("budgetPeriodLines", () => { + it("describes the current period, max budget, interval and spend when a reset is scheduled", () => { + process.env.TZ = "UTC"; + expect(budgetPeriodLines(scheduled)).toEqual([ + "Budget period: Mar 1, 2026 00:00 to Apr 1, 2026 00:00 (UTC)", + "Max budget: $100.00", + "Reset interval: monthly", + "Spend this period: $12.5000", + ]); + }); + + it("falls back to the next reset when the duration cannot be parsed", () => { + process.env.TZ = "UTC"; + expect(budgetPeriodLines(unparseableDuration)).toEqual([ + "Next reset: Apr 1, 2026 00:00 (UTC)", + "Max budget: $100.00", + "Reset interval: fortnight", + "Spend this period: $0.0000", + ]); + }); + + it("omits the period line when no reset has been scheduled yet", () => { + expect(budgetPeriodLines(unscheduled)).toEqual([ + "Max budget: $100.00", + "Reset interval: monthly", + "Spend this period: $0.0000", + ]); + }); + + it("reports a lifetime budget when there is no duration", () => { + expect(budgetPeriodLines(lifetime)).toEqual([ + "Lifetime budget, never resets", + "Max budget: $50.00", + "Spend so far: $3.0000", + ]); + }); + + it("reports no budget when max budget is unset", () => { + expect(budgetPeriodLines(noBudget)).toEqual(["No budget set", "Spend this period: $0.0000"]); + }); + + it("uses lifetime wording when there is neither a budget nor a duration", () => { + expect(budgetPeriodLines(noBudgetNoDuration)).toEqual(["No budget set", "Spend so far: $2.0000"]); + }); +}); + +describe("BudgetPeriodTooltip", () => { + it("shows the budget period details on hover", async () => { + const user = userEvent.setup(); + render(); + + await user.hover(screen.getByLabelText("budget-period-details")); + + const tooltip = await screen.findByTestId("budget-period-tooltip"); + expect(tooltip).toHaveTextContent("Max budget: $10.00"); + expect(tooltip).toHaveTextContent("Reset interval: 1d"); + expect(tooltip).toHaveTextContent("Spend this period: $1.0000"); + expect(tooltip).not.toHaveTextContent("Invalid Date"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.tsx b/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.tsx new file mode 100644 index 00000000000..56c0667b2a5 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/BudgetPeriodTooltip.tsx @@ -0,0 +1,65 @@ +"use client"; + +import dayjs from "dayjs"; +import { CircleHelp } from "lucide-react"; + +import { getBudgetDurationLabel } from "@/components/common_components/budget_duration_dropdown"; +import { SimpleTooltip } from "@/components/ui/tooltip"; +import { budgetPeriodRange } from "@/utils/budgetUtils"; +import { formatNumberWithCommas } from "@/utils/dataUtils"; + +export interface BudgetPeriodTooltipProps { + spend: number | null | undefined; + maxBudget: number | null | undefined; + budgetDuration: string | null | undefined; + budgetResetAt: string | null | undefined; +} + +const formatPeriodDate = (iso: string): string => dayjs(iso).format("MMM D, YYYY HH:mm"); + +const describePeriod = (budgetDuration: string, budgetResetAt: string | null | undefined): string | null => { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const period = budgetPeriodRange(budgetDuration, budgetResetAt); + if (period) { + return `Budget period: ${formatPeriodDate(period.start)} to ${formatPeriodDate(period.end)} (${timeZone})`; + } + if (budgetResetAt && dayjs(budgetResetAt).isValid()) { + return `Next reset: ${formatPeriodDate(budgetResetAt)} (${timeZone})`; + } + return null; +}; + +export const budgetPeriodLines = ({ + spend, + maxBudget, + budgetDuration, + budgetResetAt, +}: BudgetPeriodTooltipProps): readonly string[] => { + const spendLine = `${budgetDuration ? "Spend this period" : "Spend so far"}: $${formatNumberWithCommas(spend ?? 0, 4)}`; + if (maxBudget == null) return ["No budget set", spendLine]; + const maxBudgetLine = `Max budget: $${formatNumberWithCommas(maxBudget, 2)}`; + if (!budgetDuration) return ["Lifetime budget, never resets", maxBudgetLine, spendLine]; + const periodLine = describePeriod(budgetDuration, budgetResetAt); + return [ + ...(periodLine ? [periodLine] : []), + maxBudgetLine, + `Reset interval: ${getBudgetDurationLabel(budgetDuration)}`, + spendLine, + ]; +}; + +export function BudgetPeriodTooltip(props: BudgetPeriodTooltipProps) { + return ( + + {budgetPeriodLines(props).map((line) => ( + {line} + ))} +

+ } + > + + + ); +} diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index 0d63f5d0833..0d20d160283 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -135,6 +135,7 @@ import ResetMemberBudgetsDialog from "./ResetMemberBudgetsDialog"; import { customBudgetMemberUserIds, shouldPromptMemberBudgetReset } from "./memberBudgetReset"; import { useMemberBudgetReset } from "./useMemberBudgetReset"; import { fetchClient } from "@/lib/http/api"; +import { BudgetPeriodTooltip } from "@/components/shared/BudgetPeriodTooltip"; const UI_MANAGED_METADATA_KEYS: ReadonlySet = new Set([ "logging", @@ -1375,7 +1376,15 @@ const TeamInfoView: React.FC = ({ children: (
-

Budget Status

+

+ Budget Status + +

${formatNumberWithCommas(info.spend, 2)}

of {info.max_budget === null ? "Unlimited" : `$${formatNumberWithCommas(info.max_budget, 2)}`}

diff --git a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx index 7b924243fb5..4680368943a 100644 --- a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx @@ -57,6 +57,7 @@ import { } from "../shared/InheritedBudgetHint"; import { KeyEditView } from "./key_edit_view"; import { isTeamAdminEditingMemberKey, teamAdminMemberKeyPayload } from "./teamAdminMemberKeyPayload"; +import { BudgetPeriodTooltip } from "@/components/shared/BudgetPeriodTooltip"; export function needsLifetimeSpendBackfill(spend: number, totalSpend: number | null | undefined): boolean { return (totalSpend ?? 0) < spend; @@ -722,7 +723,15 @@ export default function KeyInfoView({
-

Spend

+

+ Spend + +

${formatNumberWithCommas(currentKeyData.spend, 4)}

diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index d3c1e0422df..ababe4507b8 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -35066,7 +35066,7 @@ export interface components { created_at: string; /** Created By */ created_by: string; - litellm_budget_table?: components["schemas"]["LiteLLM_BudgetTable"] | null; + litellm_budget_table?: components["schemas"]["LiteLLM_BudgetTableFull"] | null; /** * Members * @default [] diff --git a/ui/litellm-dashboard/src/utils/budgetUtils.test.ts b/ui/litellm-dashboard/src/utils/budgetUtils.test.ts index a3b5f4da00a..e33c779bb65 100644 --- a/ui/litellm-dashboard/src/utils/budgetUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/budgetUtils.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; -import { describeBudgetResetSchedule, formatBudgetReset, formatBudgetResetUtc } from "./budgetUtils"; +import { budgetPeriodRange, describeBudgetResetSchedule, formatBudgetReset, formatBudgetResetUtc } from "./budgetUtils"; const originalTimezone: string | undefined = process.env.TZ; @@ -44,3 +44,41 @@ describe("describeBudgetResetSchedule", () => { expect(schedule.tooltip).toContain("carries into the next cycle"); }); }); + +describe("budgetPeriodRange", () => { + const resetAt = "2026-04-01T00:00:00Z"; + + it.each([ + ["30d", "2026-03-01T00:00:00.000Z"], + ["monthly", "2026-03-01T00:00:00.000Z"], + ["1mo", "2026-03-01T00:00:00.000Z"], + ["2mo", "2026-02-01T00:00:00.000Z"], + ["7d", "2026-03-25T00:00:00.000Z"], + ["weekly", "2026-03-25T00:00:00.000Z"], + ["1w", "2026-03-25T00:00:00.000Z"], + ["15d", "2026-03-17T00:00:00.000Z"], + ["24h", "2026-03-31T00:00:00.000Z"], + ["daily", "2026-03-31T00:00:00.000Z"], + ["1h", "2026-03-31T23:00:00.000Z"], + ["hourly", "2026-03-31T23:00:00.000Z"], + ])("computes the period start for %s the way the proxy does", (duration, start) => { + expect(budgetPeriodRange(duration, resetAt)).toEqual({ start, end: "2026-04-01T00:00:00.000Z" }); + }); + + it("spans a calendar month for 30d even when the viewer is in a DST-shifting timezone", () => { + process.env.TZ = "America/New_York"; + expect(budgetPeriodRange("30d", "2026-04-01T00:00:00Z")?.start).toBe("2026-03-01T00:00:00.000Z"); + expect(budgetPeriodRange("30d", "2026-03-01T00:00:00Z")?.start).toBe("2026-02-01T00:00:00.000Z"); + }); + + it.each([ + ["bogus", resetAt], + ["0d", resetAt], + ["30d", "not-a-date"], + ["30d", null], + [null, resetAt], + ["", resetAt], + ])("returns null for duration %s and reset %s instead of an invalid date", (duration, reset) => { + expect(budgetPeriodRange(duration, reset)).toBeNull(); + }); +}); diff --git a/ui/litellm-dashboard/src/utils/budgetUtils.ts b/ui/litellm-dashboard/src/utils/budgetUtils.ts index 171ce5818f7..23af15eb539 100644 --- a/ui/litellm-dashboard/src/utils/budgetUtils.ts +++ b/ui/litellm-dashboard/src/utils/budgetUtils.ts @@ -54,3 +54,44 @@ export function describeBudgetResetSchedule( tooltip: `Resets every ${budgetDuration}, but the next reset time has not been scheduled yet. ${ROLLOVER_NOTE}`, }; } + +const BUDGET_DURATION_WORD_ALIASES: Record = { + hourly: "1h", + daily: "24h", + weekly: "7d", + monthly: "30d", +}; + +const BUDGET_DURATION_UNITS: Record = { + s: "second", + m: "minute", + h: "hour", + d: "day", + w: "week", + mo: "month", +}; + +export interface BudgetPeriod { + start: string; + end: string; +} + +/** Mirrors the proxy's get_budget_window_start: `30d` and `Nmo` span calendar months, the rest are fixed deltas. */ +export function budgetPeriodRange( + budgetDuration: string | null | undefined, + budgetResetAt: string | null | undefined, +): BudgetPeriod | null { + if (!budgetDuration || !budgetResetAt) return null; + const end = dayjs.utc(budgetResetAt); + if (!end.isValid()) return null; + const normalized = budgetDuration.trim().toLowerCase(); + const match = /^(\d+)(mo|s|m|h|d|w)$/.exec(BUDGET_DURATION_WORD_ALIASES[normalized] ?? normalized); + if (!match) return null; + const amount = Number(match[1]); + if (amount <= 0) return null; + const start = + match[2] === "d" && amount === 30 + ? end.subtract(1, "month") + : end.subtract(amount, BUDGET_DURATION_UNITS[match[2]]); + return { start: start.toISOString(), end: end.toISOString() }; +} From 76d001a7c06d5dbf93f9191b247db0ef981b6c66 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:06:09 -0700 Subject: [PATCH 54/73] fix(router): serve a team's wildcard deployment before a proxy-wide wildcard (#48490) * fix(router): serve a team's wildcard deployment before a proxy-wide wildcard Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(router): keep the wildcard precedence helpers free of partially typed locals Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(router): call each pattern router once in the wildcard precedence helpers Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(ptu): a team's wildcard reservation serves beside a proxy-wide catch-all Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/router.py | 20 +++++------ .../test_ptu_consumption.py | 16 +++------ tests/unit/test_router/test_router.py | 36 ++++++++++++++++++- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 548df8e6721..ddb4b9bc075 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -13788,21 +13788,21 @@ class Router: return [{**DeploymentTypedDict(**m), "model_name": model_name} for m in matches] def wildcard_route_deployments(self, model: str, team_id: str | None) -> Sequence[Mapping[str, object]]: - """The deployments routing serves ``model`` from through a wildcard: the most specific - proxy-wide pattern matching it, else ``team_id``'s own pattern, never both.""" - proxy_wide: Final[Sequence[Mapping[str, object]]] = self.pattern_router.get_deployments_by_pattern(model=model) + """The deployments routing serves ``model`` from through a wildcard: ``team_id``'s own pattern + matching it, else the most specific proxy-wide pattern, never both.""" team_router: Final = self.team_pattern_routers.get(team_id) if team_id is not None else None - if proxy_wide or team_router is None: - return proxy_wide - return team_router.get_deployments_by_pattern(model=model) + team_owned: Final[Sequence[Mapping[str, object]]] = ( + team_router.get_deployments_by_pattern(model=model) if team_router is not None else () + ) + return team_owned or self.pattern_router.get_deployments_by_pattern(model=model) def wildcard_routed_models(self, model: str, team_id: str | None) -> frozenset[str]: """The models wildcard_route_deployments would route ``model`` to, without copying the deployments.""" - proxy_wide: Final = self.pattern_router.get_models_by_pattern(model=model) team_router: Final = self.team_pattern_routers.get(team_id) if team_id is not None else None - if proxy_wide or team_router is None: - return proxy_wide - return team_router.get_models_by_pattern(model=model) + team_owned: Final = ( + team_router.get_models_by_pattern(model=model) if team_router is not None else frozenset[str]() + ) + return team_owned or self.pattern_router.get_models_by_pattern(model=model) def team_ptu_ceiling(self, team_id: str, model_group: str) -> PTUTeamCeiling | None: """The per-minute ceiling ``team_id``'s PTU shares set on the group serving ``model_group``, diff --git a/tests/unit/proxy/management_endpoints/test_ptu_consumption.py b/tests/unit/proxy/management_endpoints/test_ptu_consumption.py index a446afbd809..065ec93dcd0 100644 --- a/tests/unit/proxy/management_endpoints/test_ptu_consumption.py +++ b/tests/unit/proxy/management_endpoints/test_ptu_consumption.py @@ -232,14 +232,10 @@ def test_rows_keyed_by_a_name_a_wildcard_route_serves_are_sized_by_its_shared_de assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx(1.0) -@pytest.mark.parametrize( - ("catch_all_routes", "expected_ptu_hours"), [(False, 1.0), (True, 0.0)], ids=["team-wildcard", "behind-catch-all"] -) -def test_a_teams_wildcard_reservation_reads_ptu_hours_only_while_the_router_serves_from_it( - monkeypatch, catch_all_routes, expected_ptu_hours -): - """The router tries the proxy-wide wildcards before a team's own, so a team's `ptu-*` - reservation behind an open `*` route never serves `ptu-chat`, and its row reads no PTU-hours.""" +@pytest.mark.parametrize("catch_all_routes", [False, True], ids=["team-wildcard", "beside-catch-all"]) +def test_a_teams_wildcard_reservation_reads_ptu_hours_even_beside_a_proxy_wide_catch_all(monkeypatch, catch_all_routes): + """The router tries a team's own wildcard before the proxy-wide ones, so a team's `ptu-*` + reservation serves `ptu-chat` whether or not an open `*` route exists, and its row reads its PTU-hours.""" monkeypatch.setenv("LITELLM_ENABLE_PTU_COST_ATTRIBUTION", "True") reservation: Final = { "model_name": "ptu-team-r", @@ -264,9 +260,7 @@ def test_a_teams_wildcard_reservation_reads_ptu_hours_only_while_the_router_serv attached: Final = with_ptu_consumption(_response(_day("2026-09-23", {"ptu-chat": one_hour})), router, "team-r") - assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx( - expected_ptu_hours - ) + assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx(1.0) def _mixed_ptu_router() -> Router: diff --git a/tests/unit/test_router/test_router.py b/tests/unit/test_router/test_router.py index 55628cb0c34..3004f83b1df 100644 --- a/tests/unit/test_router/test_router.py +++ b/tests/unit/test_router/test_router.py @@ -8885,7 +8885,8 @@ def test_get_wildcard_deployment_usable_by_team_prefers_the_team_pattern(): pytest.param(False, "rwild-chat", "team-r", ("team-r-wildcard",), id="team-pattern-when-no-proxy-wide-match"), pytest.param(False, "rwild-chat", "team-b", (), id="another-teams-pattern-never-serves"), pytest.param(False, "rwild-chat", None, (), id="no-team-no-team-pattern"), - pytest.param(True, "rwild-chat", "team-r", ("catch-all",), id="proxy-wide-pattern-beats-the-team-pattern"), + pytest.param(True, "rwild-chat", "team-r", ("team-r-wildcard",), id="team-pattern-beats-the-proxy-wide-pattern"), + pytest.param(True, "rwild-chat", "team-b", ("catch-all",), id="another-team-falls-through-to-proxy-wide"), pytest.param(True, "ptu-chat", "team-r", ("ptu-wildcard",), id="only-the-most-specific-proxy-wide-pattern"), ], ) @@ -8924,6 +8925,39 @@ def test_wildcard_route_deployments_is_the_one_pattern_routing_serves_from( ) +@pytest.mark.parametrize( + ("team_id", "expected_id"), + [ + pytest.param("team-a", "team-a-byok", id="team-key-uses-its-own-byok-wildcard"), + pytest.param("team-b", "shared-openrouter", id="team-without-byok-uses-the-shared-wildcard"), + pytest.param(None, "shared-openrouter", id="key-without-team-uses-the-shared-wildcard"), + ], +) +def test_team_byok_wildcard_is_served_before_a_proxy_wide_wildcard_of_the_same_name( + team_id: str | None, expected_id: str +): + """Regression: a proxy-wide openrouter/* shadowed a team's own openrouter/* BYOK deployment for that team's keys.""" + router: Final = litellm.Router( + model_list=[ + { + "model_name": "model_name_team-a_internal", + "litellm_params": {"model": "openrouter/*", "api_key": "sk-team-a"}, + "model_info": {"id": "team-a-byok", "team_id": "team-a", "team_public_model_name": "openrouter/*"}, + }, + { + "model_name": "openrouter/*", + "litellm_params": {"model": "openrouter/*", "api_key": "sk-shared"}, + "model_info": {"id": "shared-openrouter"}, + }, + ] + ) + requested: Final = "openrouter/openai/gpt-5-nano" + + served: Final = router.wildcard_route_deployments(requested, team_id) + + assert tuple(Deployment.model_validate(d).model_info.id for d in served) == (expected_id,) + + def test_get_deployment_credentials_with_provider_aws_bedrock_runtime_endpoint(): """ Test that get_deployment_credentials_with_provider correctly copies From 9873128b42165e8b016afc6cdc0dc1c6ae23f961 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:13:56 -0700 Subject: [PATCH 55/73] fix(team): keep the team reset schedule when a temp budget increase is set on a member inheriting the team default (#48495) * fix(team): keep the team reset schedule when a temp budget increase is set on a member inheriting the team default Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(integration): expect the temp-only member row to carry the team default cap Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../management_endpoints/common_utils.py | 21 ++-- .../spend/test_team_member_spend.py | 106 +++++++++++++++++- .../test_upsert_budget_membership.py | 17 ++- 3 files changed, 127 insertions(+), 17 deletions(-) diff --git a/litellm/proxy/management_endpoints/common_utils.py b/litellm/proxy/management_endpoints/common_utils.py index adf89c29279..983c3d24513 100644 --- a/litellm/proxy/management_endpoints/common_utils.py +++ b/litellm/proxy/management_endpoints/common_utils.py @@ -569,8 +569,8 @@ _TEAM_MEMBER_BUDGET_LIMIT_FIELDS: Final = ( "temp_budget_expiry", ) -_TEMP_BUDGET_FIELDS: Final = frozenset({"temp_budget_increase", "temp_budget_expiry"}) +_BUDGET_COLUMNS: Final = TypeAdapter(dict[str, object]) MEMBER_BUDGET_PATCH_FIELDS: Final = MappingProxyType( { @@ -653,8 +653,8 @@ async def upsert_budget_and_membership( ``shared_budget_ids`` extends that protection to any other row more than one membership points at, which a caller patching several members at once has already counted; a row listed there is cloned rather than written in place. - A patch that only touches the temporary budget pair never copies permanent - limits into a new row, so the member keeps inheriting the live team default. + A patch whose every field already matches the shared row changes nothing and + is a no-op rather than a clone. """ ignore_default_model_budget_clear: Final = ( existing_budget_id is not None @@ -685,7 +685,6 @@ async def upsert_budget_and_membership( is_shared_default: Final = existing_budget_id is not None and ( existing_budget_id == team_default_budget_id or existing_budget_id in (shared_budget_ids or frozenset()) ) - temp_only: Final = frozenset(write_data) <= _TEMP_BUDGET_FIELDS async def _disconnect(): await tx.litellm_teammembership.update( @@ -707,11 +706,15 @@ async def upsert_budget_and_membership( return source_row: Final = ( - await tx.litellm_budgettable.find_unique(where={"budget_id": existing_budget_id}) - if is_shared_default and not temp_only - else None + await tx.litellm_budgettable.find_unique(where={"budget_id": existing_budget_id}) if is_shared_default else None ) - source: Final[Mapping[str, object]] = source_row.model_dump() if source_row is not None else MappingProxyType({}) + source: Final[Mapping[str, object]] = ( + MappingProxyType(_BUDGET_COLUMNS.validate_python(source_row.model_dump())) + if source_row is not None + else MappingProxyType({}) + ) + if is_shared_default and all(source.get(field) == value for field, value in effective_patch.items()): + return create_data: Final[dict[str, object]] = { # mutable-ok: Prisma create payloads are dict-shaped "created_by": user_api_key_dict.user_id or "", @@ -740,7 +743,7 @@ async def upsert_budget_and_membership( create_data.pop("budget_reset_at", None) if not _has_meaningful_budget_limit(create_data): - if existing_budget_id is not None and not temp_only: + if existing_budget_id is not None: await _disconnect() return diff --git a/tests/integration/spend/test_team_member_spend.py b/tests/integration/spend/test_team_member_spend.py index 306138e2bb1..c11dc9db4bf 100644 --- a/tests/integration/spend/test_team_member_spend.py +++ b/tests/integration/spend/test_team_member_spend.py @@ -1,3 +1,4 @@ +import asyncio import os import uuid from collections.abc import Callable, Iterator @@ -7,10 +8,14 @@ from typing import Final import httpx import pytest from integration._support.client import Gateway, Scenario, eventually, object_value, string_value -from integration._support.database import read_rows +from integration._support.database import read_rows, write_rows from pydantic import BaseModel, JsonValue from redis import Redis +from litellm.proxy.common_utils.reset_budget_job import ResetBudgetJob +from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache +from litellm.proxy.utils import PrismaClient, ProxyLogging + def _python_isoformat(value: datetime) -> str: return value.isoformat() @@ -444,9 +449,7 @@ def test_member_update_temp_budget_increase_readmits_a_blocked_member( ) assert added.status_code == 200, added.text default_budget_id: Final = _team_default_budget_id(team) if budget_source == "team-default" else None - default_budget_before: Final = ( - _budget_record(default_budget_id) if default_budget_id is not None else () - ) + default_budget_before: Final = _budget_record(default_budget_id) if default_budget_id is not None else () if default_budget_id is not None: assert len(default_budget_before) == 1, repr(default_budget_before) assert default_budget_before[0].max_budget == 0.05, default_budget_before @@ -503,7 +506,7 @@ def test_member_update_temp_budget_increase_readmits_a_blocked_member( else: assert default_budget_id is not None assert state[0].budget_id != default_budget_id, state - assert state[0].max_budget is None, state + assert state[0].max_budget == 0.05, state default_budget_after: Final = _budget_record(default_budget_id) assert default_budget_after == default_budget_before, repr(default_budget_after) assert default_budget_after[0].max_budget == 0.05, default_budget_after @@ -1024,3 +1027,96 @@ def test_bulk_update_restores_team_default_when_last_member_limit_is_cleared( assert first_observations + second_observations == (provider_model, provider_model), repr( (first_observations, second_observations) ) + + +def _budget_reset_at(budget_id: str) -> str: + rows: Final = read_rows( + 'SELECT budget_reset_at::text AS budget_reset_at FROM "LiteLLM_BudgetTable" WHERE budget_id = %s', (budget_id,) + ) + assert len(rows) == 1, rows + return string_value(rows[0]["budget_reset_at"]) + + +async def _run_scheduled_budget_reset() -> None: + client: Final = PrismaClient( + database_url=os.environ["DATABASE_URL"], proxy_logging_obj=ProxyLogging(user_api_key_cache=UserApiKeyCache()) + ) + await client.connect() + try: + await ResetBudgetJob( + ProxyLogging(user_api_key_cache=UserApiKeyCache()), client + ).reset_budget_for_litellm_budget_table() + finally: + await client.db.disconnect() + + +def test_temp_budget_increase_on_a_team_default_member_keeps_the_team_reset_schedule( + gateway: Gateway, upstream: httpx.Client +) -> None: + with gateway.scenario() as scenario: + model, provider_model = _priced_model(scenario) + team: Final = scenario.team(models=[model], team_member_budget=5, team_member_budget_duration="30d") + topped_up: Final = scenario.user() + untouched: Final = scenario.user() + for user in (topped_up, untouched): + added = gateway.request( + "POST", "/team/member_add", {"team_id": team, "member": {"user_id": user, "role": "user"}} + ) + assert added.status_code == 200, added.text + default_budget_id: Final = _team_default_budget_id(team) + for user in (topped_up, untouched): + key = scenario.key(team_id=team, user_id=user) + chat = _chat(gateway, model, key, f"spend before top-up {uuid.uuid4().hex}") + assert chat.status_code == 200, chat.text + observed: Final = _observed_models(upstream) + assert observed == (provider_model, provider_model), repr(observed) + for user in (topped_up, untouched): + accrued = eventually( + lambda user=user: _budget_state(team, user), + lambda values: len(values) == 1 and values[0].spend is not None and values[0].spend > 0, + seconds=70, + ) + assert accrued[0].budget_id == default_budget_id, accrued + + expiry: Final = datetime.now(timezone.utc) + timedelta(days=30) + granted: Final = _member_update( + gateway, + { + "team_id": team, + "user_id": topped_up, + "temp_budget_increase": 1, + "temp_budget_expiry": expiry.isoformat(), + }, + ) + assert granted.temp_budget_increase == 1, granted + topped_up_state: Final = _budget_state(team, topped_up) + assert len(topped_up_state) == 1, topped_up_state + topped_up_budget_id: Final = topped_up_state[0].budget_id + assert topped_up_budget_id is not None and topped_up_budget_id != default_budget_id, topped_up_state + assert topped_up_state[0].max_budget == 5, topped_up_state + assert topped_up_state[0].budget_duration == "30d", topped_up_state + assert topped_up_state[0].temp_budget_increase == 1, topped_up_state + assert _budget_reset_at(topped_up_budget_id) == _budget_reset_at(default_budget_id), topped_up_state + assert _budget_state(team, untouched)[0].budget_id == default_budget_id + + write_rows( + "UPDATE \"LiteLLM_BudgetTable\" SET budget_reset_at = NOW() - INTERVAL '1 hour' WHERE budget_id IN (%s, %s)", + (default_budget_id, topped_up_budget_id), + ) + asyncio.run(_run_scheduled_budget_reset()) + for user in (topped_up, untouched): + after_reset = _budget_state(team, user) + assert len(after_reset) == 1 and after_reset[0].spend == 0, (user, after_reset) + assert _budget_reset_at(topped_up_budget_id) == _budget_reset_at(default_budget_id) + + cleared: Final = _member_update( + gateway, + {"team_id": team, "user_id": topped_up, "temp_budget_increase": None, "temp_budget_expiry": None}, + ) + assert cleared.temp_budget_increase is None, cleared + final_state: Final = _budget_state(team, topped_up) + assert len(final_state) == 1, final_state + assert final_state[0].budget_id == topped_up_budget_id, final_state + assert final_state[0].budget_duration == "30d", final_state + assert final_state[0].max_budget == 5, final_state + assert final_state[0].temp_budget_increase is None, final_state diff --git a/tests/unit/proxy/common_utils/test_upsert_budget_membership.py b/tests/unit/proxy/common_utils/test_upsert_budget_membership.py index 1f7154f0a74..4aa1a7f7cb1 100644 --- a/tests/unit/proxy/common_utils/test_upsert_budget_membership.py +++ b/tests/unit/proxy/common_utils/test_upsert_budget_membership.py @@ -267,10 +267,17 @@ async def test_create_from_temp_pair_never_snapshots_team_default(mock_tx, fake_ @pytest.mark.asyncio -async def test_temp_pair_on_shared_default_member_creates_bare_row(mock_tx, fake_user): +async def test_temp_pair_on_shared_default_member_clones_default_with_its_reset_schedule(mock_tx, fake_user): expiry = datetime(2100, 1, 1, tzinfo=timezone.utc) + default_reset_at = datetime(2026, 11, 1, tzinfo=timezone.utc) mock_tx.litellm_budgettable.find_unique = AsyncMock( - return_value=budget_row(budget_id="team-default-budget-1", max_budget=0.4, rpm_limit=10) + return_value=budget_row( + budget_id="team-default-budget-1", + max_budget=0.4, + rpm_limit=10, + budget_duration="30d", + budget_reset_at=default_reset_at, + ) ) await upsert_budget_and_membership( mock_tx, @@ -282,10 +289,14 @@ async def test_temp_pair_on_shared_default_member_creates_bare_row(mock_tx, fake team_default_budget_id="team-default-budget-1", ) - mock_tx.litellm_budgettable.find_unique.assert_not_awaited() mock_tx.litellm_budgettable.update.assert_not_called() + mock_tx.litellm_teammembership.update.assert_not_called() assert stored_budget_row(mock_tx) == { "model_max_budget": "{}", + "max_budget": 0.4, + "rpm_limit": 10, + "budget_duration": "30d", + "budget_reset_at": default_reset_at, "temp_budget_increase": 1.0, "temp_budget_expiry": expiry, } From 833d82bb4ee4886a9d9058a219fddf0d09e700b3 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 20:14:57 -0400 Subject: [PATCH 56/73] fix(mcp): reject revoked browser sessions during gateway authorization (#48494) --- .../mcp_server/discoverable_endpoints.py | 24 +- .../mcp_server/test_discoverable_endpoints.py | 271 ++++++++++++++---- 2 files changed, 228 insertions(+), 67 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index f5c54451006..ca7ad9e3384 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -472,15 +472,15 @@ def redeem_passthrough_authorization_code( return sealed -def _session_cookie_user_id(request: Request) -> str | None: +async def _session_cookie_user_id(request: Request) -> str | None: """The signed-in litellm user for a browser request, or ``None``. Thin wrapper so the aggregate DCR flow's verbs receive the identity as a plain value instead of parsing cookies themselves.""" from litellm.proxy._experimental.mcp_server.byok_oauth_endpoints import ( # noqa: PLC0415 # circular import at module load - user_id_from_session_cookie, + get_authenticated_browser_user_id, ) - return user_id_from_session_cookie(request) + return await get_authenticated_browser_user_id(request) def _redirect_to_litellm_login(request: Request) -> RedirectResponse: @@ -996,17 +996,13 @@ async def _resolve_oauth_authorization_user( enforce_binding: bool, ) -> str | RedirectResponse: """Resolve the authorization subject without replacing denied credentials with cookie grants.""" - from litellm.proxy._experimental.mcp_server.byok_oauth_endpoints import ( # noqa: PLC0415 # proxy import cycle - user_id_from_session_cookie, - ) - use_gateway_credential: Final = enforce_binding and await oauth_authorization_uses_gateway_credential(request) request_user_id: Final = ( await authorize_oauth_credential_request(request, mcp_server.server_id) if use_gateway_credential else None ) if use_gateway_credential and request_user_id is None: return _bridge_access_denied_redirect(redirect_uri, state, mcp_server) - user_id: Final = request_user_id or user_id_from_session_cookie(request) + user_id: Final = request_user_id or await _session_cookie_user_id(request) if user_id is None: return _redirect_to_litellm_login(request) if not await _user_can_reach_mcp_server(user_id, mcp_server.server_id): @@ -1024,7 +1020,7 @@ async def _managed_authorization_scope( request ) session_user: Final = ( - _session_cookie_user_id(request) if authorized_user_id is None and not gateway_credential else None + await _session_cookie_user_id(request) if authorized_user_id is None and not gateway_credential else None ) scope_user_id: Final = authorized_user_id or ( await authorize_oauth_credential_request(request, server.server_id) @@ -2359,7 +2355,7 @@ async def authorize_mcp_session( code_challenge=code_challenge, code_challenge_method=code_challenge_method, response_type=response_type, - session_user_id=_session_cookie_user_id(request), + session_user_id=await _session_cookie_user_id(request), resource=resource, scope=scope, ) @@ -2391,7 +2387,7 @@ async def authorize( code_challenge=code_challenge, code_challenge_method=code_challenge_method, response_type=response_type, - session_user_id=_session_cookie_user_id(request), + session_user_id=await _session_cookie_user_id(request), lookup_consent_teams=lookup_consent_teams, ) return aggregate_authorize( @@ -2402,7 +2398,7 @@ async def authorize( code_challenge=code_challenge, code_challenge_method=code_challenge_method, response_type=response_type, - session_user_id=_session_cookie_user_id(request), + session_user_id=await _session_cookie_user_id(request), resource=resource, scope=scope, ) @@ -2560,7 +2556,7 @@ async def authorize_flow(request: Request, flow: str) -> Response: return await describe_connect_flow( request=request, flow_handle=flow, - session_user_id=_session_cookie_user_id(request), + session_user_id=await _session_cookie_user_id(request), lookup_vendor_credential=_vendor_credential_state, lookup_server_reachability=_user_can_reach_mcp_server, ) @@ -2586,7 +2582,7 @@ async def authorize_complete( return await complete_connect_flow( request=request, flow_handle=flow, - session_user_id=_session_cookie_user_id(request), + session_user_id=await _session_cookie_user_id(request), cache=user_api_key_cache, delivery=delivery, team_id=team_id, diff --git a/tests/unit/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/unit/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index a43dacaf100..cedfa296515 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -5599,12 +5599,13 @@ def _bridge_server(**overrides): return MCPServer(**fields) -def _bridge_mock_request(): +def _bridge_mock_request(cookie: str | None = None) -> MagicMock: from fastapi import Request - mock_request = MagicMock(spec=Request) + mock_request: Final = MagicMock(spec=Request) mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} + mock_request.cookies = {"token": cookie} if cookie else {} return mock_request @@ -6524,7 +6525,7 @@ async def test_interactive_bridge_gateway_code_for_another_server_is_rejected_40 @pytest.mark.asyncio -async def test_interactive_bridge_authorize_seals_sso_user_into_state(): +async def test_interactive_bridge_authorize_seals_sso_user_into_state(monkeypatch: pytest.MonkeyPatch) -> None: """On the short-circuit bridge oauth_delegate arm, authorize captures the SSO user from the UI session cookie and seals it (and the target server) into the encrypted OAuth state, so the callback can later mint a user-bound gateway code; it still proceeds to the upstream redirect. @@ -6540,16 +6541,13 @@ async def test_interactive_bridge_authorize_seals_sso_user_into_state(): admitted = UserAPIKeyAuth(user_id="sso-user-42") admitted.mcp_admitted_user_subject = True captured: dict = {} + cookie: Final = _browser_session_cookie(monkeypatch, "sso-user-42") def _capture(**kwargs): captured.update(kwargs) return "mocked_encrypted_state" with ( - patch( - "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints.user_id_from_session_cookie", - return_value="sso-user-42", - ), patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler.reload_admitted_user", new=AsyncMock(return_value=admitted), @@ -6565,7 +6563,7 @@ async def test_interactive_bridge_authorize_seals_sso_user_into_state(): ), ): response = await authorize_with_server( - request=_bridge_mock_request(), + request=_bridge_mock_request(cookie), mcp_server=server, client_id="ignored", redirect_uri="http://127.0.0.1:60108/callback", @@ -6581,7 +6579,9 @@ async def test_interactive_bridge_authorize_seals_sso_user_into_state(): @pytest.mark.asyncio @pytest.mark.parametrize("user_can_reach_server", [True, False]) -async def test_bridge_authorize_gates_on_the_egress_server_access_resolver(user_can_reach_server): +async def test_bridge_authorize_gates_on_the_egress_server_access_resolver( + monkeypatch: pytest.MonkeyPatch, user_can_reach_server: bool +) -> None: """The interactive dcr_bridge oauth_delegate authorize admits the signed-in user the way MCP egress will and refuses with an RFC 6749 access_denied redirect when that admitted subject cannot reach the target server, instead of minting an envelope whose every tool request would @@ -6607,14 +6607,11 @@ async def test_bridge_authorize_gates_on_the_egress_server_access_resolver(user_ mock_request = MagicMock(spec=Request) mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} + mock_request.cookies = {"token": _browser_session_cookie(monkeypatch, "bridge-user-1")} client_redirect = "http://127.0.0.1:60108/callback" try: with ( - patch( - "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints.user_id_from_session_cookie", - return_value="bridge-user-1", - ), patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler.reload_admitted_user", new=AsyncMock(return_value=admitted), @@ -6660,7 +6657,9 @@ async def test_bridge_authorize_gates_on_the_egress_server_access_resolver(user_ @pytest.mark.asyncio @pytest.mark.parametrize("reload_status,expect_denial", [(401, True), (500, False), (503, False)]) -async def test_bridge_authorize_reload_failure_denies_or_stays_retryable(reload_status, expect_denial): +async def test_bridge_authorize_reload_failure_denies_or_stays_retryable( + monkeypatch: pytest.MonkeyPatch, reload_status: int, expect_denial: bool +) -> None: """An unknown or deactivated signed-in user denies like a missing grant (fail closed); a DB outage keeps its retryable 503 instead of masquerading as an access denial.""" from urllib.parse import parse_qs, urlparse @@ -6679,13 +6678,10 @@ async def test_bridge_authorize_reload_failure_denies_or_stays_retryable(reload_ mock_request = MagicMock(spec=Request) mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} + mock_request.cookies = {"token": _browser_session_cookie(monkeypatch, "bridge-user-1")} try: with ( - patch( - "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints.user_id_from_session_cookie", - return_value="bridge-user-1", - ), patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler.reload_admitted_user", new=AsyncMock(side_effect=HTTPException(status_code=reload_status, detail="x")), @@ -6728,19 +6724,15 @@ async def test_interactive_bridge_authorize_without_session_redirects_to_login() from litellm.types.mcp import MCPAuth server = _bridge_server(auth_type=MCPAuth.oauth_delegate, client_id="admin-client", registration_url=None) - with patch( - "litellm.proxy._experimental.mcp_server.byok_oauth_endpoints.user_id_from_session_cookie", - return_value=None, - ): - response = await authorize_with_server( - request=_bridge_mock_request(), - mcp_server=server, - client_id="ignored", - redirect_uri="http://127.0.0.1:60108/callback", - state="s", - code_challenge="chal", - code_challenge_method="S256", - ) + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=server, + client_id="ignored", + redirect_uri="http://127.0.0.1:60108/callback", + state="s", + code_challenge="chal", + code_challenge_method="S256", + ) assert "/sso/key/generate" in response.headers["location"] @@ -11997,10 +11989,97 @@ def _s256(verifier: str) -> str: _NATIVE_CLIENT_MASTER_KEY = "sk-test-salt-for-LIT-5874" +def _browser_session_cookie( + monkeypatch: pytest.MonkeyPatch, + user_id: str, + *, + signing_key: str = _NATIVE_CLIENT_MASTER_KEY, + session_key: str | None = None, + expired_session: bool = False, + expired_cookie: bool = False, +) -> str: + import jwt + + from litellm.proxy import proxy_server + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.auth_checks import LITELLM_SESSION_TOKEN_PREFIX + from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_bearer_token + + monkeypatch.setattr(proxy_server, "master_key", signing_key) + if proxy_server.prisma_client is None: + monkeypatch.setattr(proxy_server, "prisma_client", object()) + monkeypatch.setattr(proxy_server, "general_settings", {**proxy_server.general_settings, "supported_db_objects": []}) + session: Final = UserAPIKeyAuth( + user_id=user_id, + expires=datetime(2000 if expired_session else 2099, 1, 1, tzinfo=timezone.utc), + ) + key: Final = ( + encrypt_bearer_token(session.model_dump_json(exclude_none=True), LITELLM_SESSION_TOKEN_PREFIX) + if session_key is None + else session_key + ) + return jwt.encode( + {"user_id": user_id, "key": key, "login_method": "sso", "exp": 946684800 if expired_cookie else 9999999999}, + signing_key, + algorithm="HS256", + ) + + +class _BrowserSessionDatabase: + def __init__(self) -> None: + from litellm.proxy._types import UserAPIKeyAuth, hash_token + + self.row: UserAPIKeyAuth | None = UserAPIKeyAuth( + token=hash_token("sk-browser-session"), + user_id="u1", + expires=datetime(2099, 1, 1, tzinfo=timezone.utc), + ) + self.litellm_deprecatedverificationtoken = self + + def __call__(self, **kwargs: object) -> "_BrowserSessionDatabase": + return self + + async def query_first(self, query: str, *args: object) -> dict[str, object] | None: + if self.row is None: + return None + return { + **self.row.model_dump(exclude={"last_refreshed_at"}), + "team_models": None, + "team_blocked": None, + "team_members_with_roles": None, + } + + async def find_first(self, **kwargs: object) -> None: + return None + + +@pytest.fixture +def browser_session_database(monkeypatch: pytest.MonkeyPatch) -> _BrowserSessionDatabase: + import prisma + + from litellm.proxy import proxy_server + from litellm.proxy._types import hash_token + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + from litellm.proxy.utils import PrismaClient + + database: Final = _BrowserSessionDatabase() + monkeypatch.setattr(prisma, "Prisma", database) + for name in ("DATABASE_URL_READ_REPLICA", "IAM_TOKEN_DB_AUTH", "AZURE_POSTGRESQL_AUTH"): + monkeypatch.delenv(name, raising=False) + monkeypatch.setattr( + proxy_server, + "prisma_client", + PrismaClient("postgresql://local-test@localhost/browser-session", proxy_server.proxy_logging_obj), + ) + cache: Final = UserApiKeyCache() + cache.set_cache(hash_token("sk-browser-session"), database.row) + monkeypatch.setattr(proxy_server, "user_api_key_cache", cache) + return database + + def _native_client_app(monkeypatch): """The unauthenticated discoverable router served over TestClient with a signed UI session cookie available, plus fakes for the two database-backed hooks the native-client flow calls.""" - import jwt from fastapi import FastAPI from fastapi.testclient import TestClient @@ -12029,14 +12108,108 @@ def _native_client_app(monkeypatch): app = FastAPI() app.include_router(router) client = TestClient(app) - session_cookie = jwt.encode( - {"user_id": "u1", "login_method": "username_password", "exp": int(time.time()) + 600}, - _NATIVE_CLIENT_MASTER_KEY, - algorithm="HS256", - ) + session_cookie: Final = _browser_session_cookie(monkeypatch, "u1") return client, session_cookie, minted +@pytest.mark.parametrize( + "authorize_path,resource", + [("/authorize", None), ("/authorize/mcp-session", None), ("/authorize", "http://testserver")], +) +@pytest.mark.parametrize( + "session_state", ["valid", "revoked", "blocked", "expired", "wrong_owner", "missing_key", "no_database"] +) +def test_gateway_authorize_requires_live_browser_session( + monkeypatch: pytest.MonkeyPatch, + browser_session_database: _BrowserSessionDatabase, + authorize_path: str, + resource: str | None, + session_state: str, +) -> None: + from urllib.parse import urlparse + + from litellm.proxy import proxy_server + + client, _, _ = _native_client_app(monkeypatch) + database: Final = browser_session_database + assert database.row is not None + database.row = None if session_state == "revoked" else database.row.model_copy( + update={ + "blocked": session_state == "blocked", + "expires": datetime(2000 if session_state == "expired" else 2099, 1, 1, tzinfo=timezone.utc), + "user_id": "another-user" if session_state == "wrong_owner" else "u1", + } + ) + cookie: Final = _browser_session_cookie( + monkeypatch, "u1", session_key="" if session_state == "missing_key" else "sk-browser-session" + ) + client.cookies.set("token", cookie) + if session_state == "no_database": + monkeypatch.setattr(proxy_server, "prisma_client", None) + registered: Final = client.post("/register", json={"redirect_uris": ["http://127.0.0.1/callback"]}) + assert registered.status_code == 201, registered.text + response: Final = client.get( + authorize_path, + params={ + "client_id": registered.json()["client_id"], + "redirect_uri": "http://127.0.0.1/callback", + "response_type": "code", + "code_challenge": _s256("v" * 43), + "code_challenge_method": "S256", + **({"resource": resource} if resource else {}), + }, + follow_redirects=False, + ) + if session_state == "valid" and resource: + assert response.status_code == 200 and 'name="decision"' in response.text, response.text + else: + assert response.status_code == 303, response.text + assert urlparse(response.headers["location"]).path == ( + "/ui/connect" if session_state == "valid" else "/sso/key/generate" + ) + + +@pytest.mark.parametrize("endpoint", ["flow", "complete"]) +@pytest.mark.parametrize("revoked", [False, True]) +def test_gateway_connect_rechecks_session_after_authorize( + monkeypatch: pytest.MonkeyPatch, + browser_session_database: _BrowserSessionDatabase, + endpoint: str, + revoked: bool, +) -> None: + from urllib.parse import parse_qs, urlparse + + client, _, _ = _native_client_app(monkeypatch) + client.cookies.set("token", _browser_session_cookie(monkeypatch, "u1", session_key="sk-browser-session")) + registered: Final = client.post("/register", json={"redirect_uris": ["http://127.0.0.1/callback"]}) + assert registered.status_code == 201, registered.text + authorized: Final = client.get( + "/authorize/mcp-session", + params={ + "client_id": registered.json()["client_id"], + "redirect_uri": "http://127.0.0.1/callback", + "response_type": "code", + "code_challenge": _s256("v" * 43), + "code_challenge_method": "S256", + }, + follow_redirects=False, + ) + flow: Final = parse_qs(urlparse(authorized.headers["location"]).query)["connect_flow"][0] + if revoked: + browser_session_database.row = None + response: Final = ( + client.get("/authorize/flow", params={"flow": flow}) + if endpoint == "flow" + else client.post("/authorize/complete", data={"flow": flow}, follow_redirects=False) + ) + assert response.status_code == (401 if revoked else 200 if endpoint == "flow" else 303), response.text + if revoked: + assert response.json()["error"] == "login_required" + assert "location" not in response.headers + elif endpoint == "complete": + assert parse_qs(urlparse(response.headers["location"]).query)["code"] + + @pytest.mark.parametrize("authorize_path", ["/authorize/mcp-session", "/authorize"]) def test_gateway_authorize_routes_forward_server_scope(monkeypatch: pytest.MonkeyPatch, authorize_path: str) -> None: from http.cookies import SimpleCookie @@ -13605,11 +13778,7 @@ async def test_identity_bound_authorize_preserves_presented_jwt_permissions( else "https://idp.example.test", ) ) - cookie: Final = jwt.encode( - {"user_id": "cookie-owner", "login_method": "sso", "exp": int(time.time()) + 300}, - master, - algorithm="HS256", - ) + cookie: Final = _browser_session_cookie(monkeypatch, "cookie-owner", signing_key=master) response: Final = await discoverable_endpoints.authorize_with_server( request=_token_request( { @@ -13645,7 +13814,7 @@ async def test_identity_bound_authorize_preserves_presented_jwt_permissions( @pytest.mark.asyncio @pytest.mark.parametrize("credential", ["none", "opaque", "foreign_jwt"]) -@pytest.mark.parametrize("cookie_state", ["allowed", "server_denied", "expired", "missing"]) +@pytest.mark.parametrize("cookie_state", ["allowed", "server_denied", "expired", "missing", "session_expired"]) async def test_identity_bound_authorize_unrelated_bearer_uses_browser_session( jwt_oauth_identity: tuple["JWTHandler", "RSAPrivateKey"], monkeypatch: pytest.MonkeyPatch, @@ -13654,8 +13823,6 @@ async def test_identity_bound_authorize_unrelated_bearer_uses_browser_session( ) -> None: from urllib.parse import parse_qs, urlparse - import jwt - from litellm.models.user import LiteLLM_UserTable from litellm.proxy import proxy_server from litellm.proxy._experimental.mcp_server import discoverable_endpoints, mcp_server_manager @@ -13694,14 +13861,12 @@ async def test_identity_bound_authorize_unrelated_bearer_uses_browser_session( if credential == "foreign_jwt" else "unrelated-upstream-bearer" ) - cookie: Final = jwt.encode( - { - "user_id": "cookie-owner", - "login_method": "sso", - "exp": int(time.time()) + (-60 if cookie_state == "expired" else 300), - }, - master, - algorithm="HS256", + cookie: Final = _browser_session_cookie( + monkeypatch, + "cookie-owner", + signing_key=master, + expired_cookie=cookie_state == "expired", + expired_session=cookie_state == "session_expired", ) response: Final = await discoverable_endpoints.authorize_with_server( request=_token_request( From 549e6d4f465ec9cf4ca80d3d7e2541ff61ea29ef Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:17:52 -0700 Subject: [PATCH 57/73] fix(router): keep DB-only deployments priced across price data reloads (#48486) * fix(router): track every router for price data reloads so DB-only deployments keep custom pricing Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(router): register the router for reload replay only once model_list exists, and type the regression test Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/router.py | 7 +++-- .../unit/test_router_model_cost_isolation.py | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index ddb4b9bc075..8d95e3e91d4 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1483,15 +1483,16 @@ class Router: if model_list is not None: # set_model_list will build indices automatically self.set_model_list(model_list) - # Track this router so a price data reload can rebuild its deployments' - # cost-map entries from the list it is serving at that moment. - _live_routers.add(self) self.healthy_deployments: list = self.model_list for m in model_list: if "model" in m["litellm_params"]: self.deployment_latency_map[m["litellm_params"]["model"]] = 0 else: self.model_list: list = [] # initialize an empty list - to allow _add_deployment and delete_deployment to work + # Track this router so a price data reload can rebuild its deployments' + # cost-map entries from the list it is serving at that moment, whether + # those deployments came from model_list or were added later from the DB. + _live_routers.add(self) if allowed_fails is not None: self.allowed_fails = allowed_fails diff --git a/tests/unit/test_router_model_cost_isolation.py b/tests/unit/test_router_model_cost_isolation.py index f001008ee2c..20b0055f729 100644 --- a/tests/unit/test_router_model_cost_isolation.py +++ b/tests/unit/test_router_model_cost_isolation.py @@ -3081,6 +3081,36 @@ def test_reload_keeps_custom_pricing_configured_on_litellm_params_for_a_db_model _invalidate_model_cost_lowercase_map() +def test_reload_keeps_custom_pricing_of_a_db_model_on_a_router_built_without_a_model_list( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """ + A proxy whose config.yaml has no model_list builds its router without one and + takes every deployment from the DB afterwards. A price data reload must restore + that deployment's custom pricing exactly as it does for a config router. + """ + fetched_catalog: Final = copy.deepcopy(litellm.model_cost) + router: Final = Router() + router.add_deployment( + deployment=Deployment( + model_name="db-only-priced", + litellm_params=LiteLLM_Params( + model="openai/gpt-4o", + api_key="sk-fake", + input_cost_per_token=0.000123, + output_cost_per_token=0.000456, + ), + model_info=ModelInfo(id="db-only-priced-id"), + ) + ) + assert litellm.model_cost["db-only-priced-id"]["input_cost_per_token"] == 0.000123 + + _simulate_price_data_reload_with_provider_sets(monkeypatch, fetched_catalog) + + assert litellm.model_cost["db-only-priced-id"]["input_cost_per_token"] == 0.000123 + assert litellm.model_cost["db-only-priced-id"]["output_cost_per_token"] == 0.000456 + + def test_replay_live_router_model_cost_rebuilds_every_live_router(): """ A process can hold more than one Router, so the rebuild has to fan out across From 100529391a249e2f6c24bdc2c198afb0d57e59cf Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:24:44 -0700 Subject: [PATCH 58/73] fix(ui): show the budget period date range instead of "cycle" on team member spend (#48488) * fix(ui): show the budget period date range instead of "cycle" on team member spend Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): derive the budget period start in UTC before showing it in local time Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): keep clock times on sub-day budget periods and restore the total spend lower bound wording Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../src/components/team/MyUserTab.test.tsx | 6 +- .../src/components/team/MyUserTab.tsx | 16 ++-- .../components/team/TeamMemberTab.test.tsx | 29 ++++++- .../src/components/team/TeamMemberTab.tsx | 41 ++++++---- .../src/utils/budgetUtils.test.ts | 77 ++++++++++++++++++- ui/litellm-dashboard/src/utils/budgetUtils.ts | 64 +++++++++++++-- 6 files changed, 198 insertions(+), 35 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx b/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx index 24ce626ecb7..eee9fcdadd1 100644 --- a/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx +++ b/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx @@ -48,7 +48,7 @@ describe("MyUserTab", () => { expect(screen.getByText("Never resets")).toBeInTheDocument(); }); - it("shows the next reset time when the member budget has a reset window", () => { + it("shows the budget period and next reset when the member budget has a reset window", () => { vi.mocked(useMyTeamMember).mockReturnValue({ data: { user_id: "user-1", @@ -69,6 +69,8 @@ describe("MyUserTab", () => { renderWithProviders(); - expect(screen.getByText("Resets Jul 15, 2026 12:00")).toBeInTheDocument(); + expect(screen.getByText("Current Period Spend (USD)")).toBeInTheDocument(); + expect(screen.getByText("Jun 15 - Jul 15, 2026 (resets Jul 15, 2026 12:00)")).toBeInTheDocument(); + expect(screen.queryByText(/cycle/i)).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx index bf1a2c2c242..8d7583c51e4 100644 --- a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx +++ b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx @@ -1,4 +1,4 @@ -import { describeBudgetResetSchedule } from "@/utils/budgetUtils"; +import { BudgetResetSchedule, describeBudgetResetSchedule } from "@/utils/budgetUtils"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { SimpleTooltip } from "@/components/ui/tooltip"; import { Badge } from "@/components/ui/badge"; @@ -30,6 +30,12 @@ const formatRateLimit = (value: number | null | undefined): string => { return formatNumberWithCommas(value, 0); }; +function describePeriodLine(schedule: BudgetResetSchedule): string { + if (schedule.period) return `${schedule.period} (resets ${schedule.label})`; + if (schedule.resetAt) return `Resets ${schedule.label}`; + return schedule.label; +} + export default function MyUserTab({ teamId }: MyUserTabProps) { const { data, isLoading, error } = useMyTeamMember(teamId); @@ -93,16 +99,14 @@ export default function MyUserTab({ teamId }: MyUserTabProps) {

- {labelWithTooltip("Current Cycle Spend (USD)", budgetReset.tooltip)} + {labelWithTooltip("Current Period Spend (USD)", budgetReset.tooltip)}

${formatNumber(spend, 4)}

of {maxBudget === null ? "Unlimited" : `$${formatNumber(maxBudget, 4)}`}
-
- {budgetReset.resetAt ? `Resets ${budgetReset.label}` : budgetReset.label} -
+
{describePeriodLine(budgetReset)}
@@ -121,7 +125,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { {labelWithTooltip( "Total Spend (USD)", - "Cumulative spend across budget cycles within this team. This is a lower bound: lifetime tracking began 2026-04-21, so cycles completed before then are not included. It is never less than Current Cycle Spend.", + "Cumulative spend within this team across budget periods. This is a lower bound: lifetime tracking began 2026-04-21, so periods that reset before then are not included. It is never less than Current Period Spend.", )}

${formatNumber(totalSpend, 4)}

diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx index 38a8e4f97d5..9350de8a8fc 100644 --- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx @@ -316,6 +316,29 @@ describe("TeamMembersComponent", () => { expect(screen.getByText("Jul 15, 2026 12:00")).toBeInTheDocument(); }); + it("shows the budget period date range instead of a bare reset date when the duration is known", () => { + const teamData = createMockTeamData(); + teamData.team_memberships[0].litellm_budget_table!.budget_duration = "30d"; + renderWithProviders( + , + ); + + expect(screen.getByRole("columnheader", { name: /^Budget Period/ })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /^Current Period Spend \(USD\)/ })).toBeInTheDocument(); + expect(screen.getByText("Jun 15 - Jul 15, 2026")).toBeInTheDocument(); + expect(screen.queryByText("Jul 15, 2026 12:00")).not.toBeInTheDocument(); + expect(screen.queryByText(/cycle/i)).not.toBeInTheDocument(); + }); + it("shows Never resets for a member whose budget has no reset window", () => { const teamData = createMockTeamData(); teamData.team_memberships[0].litellm_budget_table!.budget_reset_at = null; @@ -696,7 +719,7 @@ describe("TeamMembersComponent", () => { />, ); - it("resets the member's current cycle spend to $0 after confirming, then refreshes the team", async () => { + it("resets the member's current period spend to $0 after confirming, then refreshes the team", async () => { const user = userEvent.setup(); POST.mockResolvedValue({ data: {} }); renderEditableTab(); @@ -745,7 +768,7 @@ describe("TeamMembersComponent", () => { expect(POST).not.toHaveBeenCalled(); }); - it("disables the reset with a reason on members that have no current cycle spend", async () => { + it("disables the reset with a reason on members that have no current period spend", async () => { const user = userEvent.setup(); renderEditableTab(); @@ -753,7 +776,7 @@ describe("TeamMembersComponent", () => { "reset-member-spend", ); await user.hover(resetButton); - expect(await screen.findByText("No current cycle spend to reset")).toBeInTheDocument(); + expect(await screen.findByText("No current period spend to reset")).toBeInTheDocument(); await user.click(resetButton); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx index 319f8c973f6..1ff77f565a5 100644 --- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx @@ -102,7 +102,7 @@ export default function TeamMemberTab({ return "0"; }; - const getUserCurrentCycleSpend = (userId: string | null): number => { + const getUserCurrentPeriodSpend = (userId: string | null): number => { if (!userId) return 0; return findMembership(userId)?.spend ?? 0; }; @@ -153,12 +153,16 @@ export default function TeamMemberTab({ return findMembership(userId)?.litellm_budget_table?.budget_reset_at ?? null; }; - const renderBudgetReset = (userId: string | null) => { + const getUserBudgetSchedule = (userId: string | null) => { const budget = findMembership(userId)?.litellm_budget_table; - const schedule = describeBudgetResetSchedule(budget?.budget_duration ?? null, budget?.budget_reset_at ?? null); + return describeBudgetResetSchedule(budget?.budget_duration ?? null, budget?.budget_reset_at ?? null); + }; + + const renderBudgetPeriod = (userId: string | null) => { + const schedule = getUserBudgetSchedule(userId); return ( - {schedule.label} + {schedule.period ?? schedule.label} ); }; @@ -200,22 +204,22 @@ export default function TeamMemberTab({ { title: ( - Current Cycle Spend (USD) - - + Current Period Spend (USD) + + ), key: "spend", numeric: true, - sortValue: (record: Member) => getUserCurrentCycleSpend(record.user_id), - render: (record: Member) => , + sortValue: (record: Member) => getUserCurrentPeriodSpend(record.user_id), + render: (record: Member) => , }, { title: ( Total Spend (USD) - + @@ -263,10 +267,10 @@ export default function TeamMemberTab({ }, }, { - title: "Budget Reset", + title: "Budget Period", key: "budget_reset", sortValue: (record: Member) => getUserBudgetReset(record.user_id), - render: (record: Member) => renderBudgetReset(record.user_id), + render: (record: Member) => renderBudgetPeriod(record.user_id), }, { title: ( @@ -314,10 +318,12 @@ export default function TeamMemberTab({ const resetSpendDisabledReason = (member: Member): string | null => { if (!isProxyAdmin && member.user_id === userId) return "Ask a proxy admin to reset your own spend"; - if (getUserCurrentCycleSpend(member.user_id) <= 0) return "No current cycle spend to reset"; + if (getUserCurrentPeriodSpend(member.user_id) <= 0) return "No current period spend to reset"; return null; }; + const resetSpendPeriod = getUserBudgetSchedule(memberToResetSpend?.user_id ?? null).period; + return ( <> Reset Team Member Spend

- Reset current cycle spend for{" "} + Reset current period spend for{" "} {memberToResetSpend?.user_email || memberToResetSpend?.user_id} in this team to{" "} $0?

- Current cycle spend:{" "} - ${formatNumberWithCommas(getUserCurrentCycleSpend(memberToResetSpend?.user_id ?? null), 4)} + Current period spend + {resetSpendPeriod ? ` (${resetSpendPeriod})` : ""}:{" "} + + ${formatNumberWithCommas(getUserCurrentPeriodSpend(memberToResetSpend?.user_id ?? null), 4)} + . This is the value checked against the member's budget. Total spend and logs are preserved.

diff --git a/ui/litellm-dashboard/src/utils/budgetUtils.test.ts b/ui/litellm-dashboard/src/utils/budgetUtils.test.ts index e33c779bb65..11339d08bc5 100644 --- a/ui/litellm-dashboard/src/utils/budgetUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/budgetUtils.test.ts @@ -2,6 +2,9 @@ import { afterEach, describe, expect, it } from "vitest"; import { budgetPeriodRange, describeBudgetResetSchedule, formatBudgetReset, formatBudgetResetUtc } from "./budgetUtils"; +const localIso = (year: number, monthIndex: number, day: number, hour = 12): string => + new Date(year, monthIndex, day, hour, 0, 0).toISOString(); + const originalTimezone: string | undefined = process.env.TZ; afterEach(() => { @@ -23,15 +26,18 @@ describe("formatBudgetResetUtc", () => { }); describe("describeBudgetResetSchedule", () => { - it("reports Never resets when the budget has no duration and no scheduled reset", () => { + it("reports lifetime spend with no period when the budget has no duration and no scheduled reset", () => { const schedule = describeBudgetResetSchedule(null, null); expect(schedule.label).toBe("Never resets"); expect(schedule.resetAt).toBeNull(); + expect(schedule.period).toBeNull(); + expect(schedule.tooltip).toContain("lifetime spend"); }); it("reports Unknown when a duration is set but no reset has been scheduled yet", () => { const schedule = describeBudgetResetSchedule("30d", null); expect(schedule.label).toBe("Unknown"); + expect(schedule.period).toBeNull(); expect(schedule.tooltip).toContain("every 30d"); }); @@ -41,7 +47,74 @@ describe("describeBudgetResetSchedule", () => { expect(schedule.label).toBe("Jul 15, 2026 12:00"); expect(schedule.resetAt).toBe(resetAt.toISOString()); expect(schedule.tooltip).toContain(Intl.DateTimeFormat().resolvedOptions().timeZone); - expect(schedule.tooltip).toContain("carries into the next cycle"); + expect(schedule.tooltip).toContain("carries into the next period"); + }); + + it("treats 30d as one calendar month back from the reset, like the proxy does", () => { + const schedule = describeBudgetResetSchedule("30d", localIso(2026, 10, 1, 0)); + expect(schedule.period).toBe("Oct 1 - Nov 1, 2026"); + expect(schedule.tooltip).toContain("covers Oct 1 - Nov 1, 2026 and resets every 30d"); + }); + + it("subtracts the exact duration for day and week windows", () => { + expect(describeBudgetResetSchedule("7d", localIso(2026, 6, 15)).period).toBe("Jul 8 - Jul 15, 2026"); + expect(describeBudgetResetSchedule("2w", localIso(2026, 6, 15)).period).toBe("Jul 1 - Jul 15, 2026"); + expect(describeBudgetResetSchedule("weekly", localIso(2026, 6, 15)).period).toBe("Jul 8 - Jul 15, 2026"); + }); + + it("walks the month back in UTC so a non-UTC viewer sees the proxy's window", () => { + const originalTz = process.env.TZ; + process.env.TZ = "America/New_York"; + try { + const schedule = describeBudgetResetSchedule("30d", "2026-03-01T00:00:00Z"); + expect(schedule.period).toBe("Jan 31 - Feb 28, 2026"); + } finally { + process.env.TZ = originalTz; + } + }); + + it("clamps month windows to the last day of a shorter month", () => { + expect(describeBudgetResetSchedule("1mo", localIso(2026, 2, 31)).period).toBe("Feb 28 - Mar 31, 2026"); + }); + + it("spells out both years when the period crosses a year boundary", () => { + expect(describeBudgetResetSchedule("30d", localIso(2027, 0, 10)).period).toBe("Dec 10, 2026 - Jan 10, 2027"); + }); + + it("keeps both clock times when an hourly window crosses midnight", () => { + expect(describeBudgetResetSchedule("1h", localIso(2026, 6, 16, 0)).period).toBe( + "Jul 15, 2026 23:00 - Jul 16, 2026 00:00", + ); + }); + + it("shows seconds for a window shorter than a minute", () => { + expect(describeBudgetResetSchedule("30s", localIso(2026, 6, 15)).period).toBe("Jul 15, 2026 11:59:30 - 12:00:00"); + }); + + it("shows clock times for a window that starts and ends on the same day", () => { + expect(describeBudgetResetSchedule("1h", localIso(2026, 6, 15)).period).toBe("Jul 15, 2026 11:00 - 12:00"); + }); + + it("falls back to a one day window for a duration the proxy would not recognise", () => { + expect(describeBudgetResetSchedule("fortnightly", localIso(2026, 6, 15)).period).toBe("Jul 14 - Jul 15, 2026"); + }); + + it("shows no period when the reset date is unparseable", () => { + const schedule = describeBudgetResetSchedule("30d", "not-a-date"); + expect(schedule.label).toBe("Unknown"); + expect(schedule.period).toBeNull(); + expect(schedule.tooltip).not.toMatch(/NaN|Invalid/); + }); + + it("never describes the window as a cycle", () => { + const schedules = [ + describeBudgetResetSchedule("30d", localIso(2026, 6, 15)), + describeBudgetResetSchedule("30d", null), + describeBudgetResetSchedule(null, null), + ]; + for (const schedule of schedules) { + expect(`${schedule.label} ${schedule.tooltip}`).not.toMatch(/cycle/i); + } }); }); diff --git a/ui/litellm-dashboard/src/utils/budgetUtils.ts b/ui/litellm-dashboard/src/utils/budgetUtils.ts index 23af15eb539..71638551f9f 100644 --- a/ui/litellm-dashboard/src/utils/budgetUtils.ts +++ b/ui/litellm-dashboard/src/utils/budgetUtils.ts @@ -1,4 +1,4 @@ -import dayjs from "dayjs"; +import dayjs, { Dayjs } from "dayjs"; import utc from "dayjs/plugin/utc"; dayjs.extend(utc); @@ -20,10 +20,55 @@ export function formatBudgetResetUtc(iso: string | null | undefined): string | n export interface BudgetResetSchedule { label: string; resetAt: string | null; + period: string | null; tooltip: string; } -const ROLLOVER_NOTE = "Spend above a rollover cap at reset carries into the next cycle."; +const ROLLOVER_NOTE = "Spend above a rollover cap at reset carries into the next period."; + +const DURATION_WORD_ALIASES: Record = { + hourly: "1h", + daily: "24h", + weekly: "7d", + monthly: "30d", +}; + +const DURATION_PATTERN = /^(\d+)(mo|[smhdw])$/; + +// Mirrors the proxy's get_budget_window_start: "30d" and "Nmo" span calendar months, +// everything else is a fixed length, and an unrecognised duration resets at the next midnight. +export function getBudgetPeriodStart(budgetDuration: string, resetAt: Dayjs): Dayjs { + const trimmed = budgetDuration.trim(); + const normalized = DURATION_WORD_ALIASES[trimmed.toLowerCase()] ?? trimmed; + const match = DURATION_PATTERN.exec(normalized); + if (!match) return resetAt.subtract(1, "day"); + const value = Number(match[1]); + switch (match[2]) { + case "mo": + return resetAt.subtract(value, "month"); + case "d": + return value === 30 ? resetAt.subtract(1, "month") : resetAt.subtract(value, "day"); + case "w": + return resetAt.subtract(value, "week"); + case "h": + return resetAt.subtract(value, "hour"); + case "m": + return resetAt.subtract(value, "minute"); + default: + return resetAt.subtract(value, "second"); + } +} + +export function formatBudgetPeriod(start: Dayjs, end: Dayjs): string { + const windowSeconds = end.diff(start, "second"); + if (windowSeconds < 24 * 60 * 60) { + const time = windowSeconds < 60 ? "HH:mm:ss" : "HH:mm"; + const endFormat = start.isSame(end, "day") ? time : `MMM D, YYYY ${time}`; + return `${start.format(`MMM D, YYYY ${time}`)} - ${end.format(endFormat)}`; + } + if (start.isSame(end, "year")) return `${start.format("MMM D")} - ${end.format("MMM D, YYYY")}`; + return `${start.format("MMM D, YYYY")} - ${end.format("MMM D, YYYY")}`; +} export function describeBudgetResetSchedule( budgetDuration: string | null | undefined, @@ -31,26 +76,33 @@ export function describeBudgetResetSchedule( ): BudgetResetSchedule { const resetDate = budgetResetAt ? dayjs(budgetResetAt) : null; if (resetDate?.isValid()) { - const cadence = budgetDuration ? `every ${budgetDuration}` : "on a schedule"; const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const label = resetDate.format("MMM D, YYYY HH:mm"); + const period = budgetDuration + ? formatBudgetPeriod(getBudgetPeriodStart(budgetDuration, resetDate.utc()).local(), resetDate) + : null; + const coverage = period + ? `Current Period Spend covers ${period} and resets every ${budgetDuration}.` + : "Current Period Spend resets on a schedule."; return { label, resetAt: resetDate.toISOString(), - tooltip: `Current Cycle Spend resets ${cadence}. Next reset ${label} (${timeZone}). ${ROLLOVER_NOTE}`, + period, + tooltip: `${coverage} Next reset ${label} (${timeZone}). ${ROLLOVER_NOTE}`, }; } if (!budgetDuration) { return { label: "Never resets", resetAt: null, - tooltip: - "This budget has no reset window, so Current Cycle Spend accumulates for as long as the membership exists.", + period: null, + tooltip: "This budget has no reset window, so this is lifetime spend for as long as the membership exists.", }; } return { label: "Unknown", resetAt: null, + period: null, tooltip: `Resets every ${budgetDuration}, but the next reset time has not been scheduled yet. ${ROLLOVER_NOTE}`, }; } From b5befe2dd0bb0899e5df8f05a20e9840609ada25 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:54:32 -0700 Subject: [PATCH 59/73] test(routing): a team's wildcard serves its keys before a proxy-wide wildcard of the same pattern (#48501) Co-authored-by: joshua Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../routing/test_team_wildcard_precedence.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/integration/routing/test_team_wildcard_precedence.py diff --git a/tests/integration/routing/test_team_wildcard_precedence.py b/tests/integration/routing/test_team_wildcard_precedence.py new file mode 100644 index 00000000000..38fda682129 --- /dev/null +++ b/tests/integration/routing/test_team_wildcard_precedence.py @@ -0,0 +1,61 @@ +"""A team's own wildcard deployment serves that team's keys even when a proxy-wide wildcard of the +same pattern exists, and the proxy-wide one serves every other key (#48490).""" + +import uuid +from typing import Final + +import pytest +from integration._support.client import Gateway, Scenario + +pytestmark: Final = pytest.mark.timeout(120) + + +def _wildcard(scenario: Scenario, pattern: str, *, team_id: str | None) -> str: + identity: Final = f"wildcard-{uuid.uuid4().hex}" + scenario.gateway.post( + "/model/new", + { + "model_name": pattern, + "litellm_params": { + "model": "openai/*", + "api_key": f"provider-key-{identity}", + "api_base": f"{scenario.gateway.upstream_url}/v1", + }, + "model_info": {"id": identity, **({"team_id": team_id} if team_id is not None else {})}, + }, + ) + scenario.cleanups.callback(scenario.delete_model, identity) + return identity + + +def _served_by(gateway: Gateway, key: str, model: str) -> str: + response: Final = gateway.request( + "POST", + "/v1/chat/completions", + {"model": model, "messages": [{"role": "user", "content": "wildcard precedence control"}]}, + key=key, + ) + assert response.status_code == 200, response.text + return response.headers["x-litellm-model-id"] + + +def test_a_teams_wildcard_serves_its_keys_before_a_proxy_wide_wildcard_of_the_same_pattern(gateway: Gateway) -> None: + with gateway.scenario() as scenario: + byok_team: Final = scenario.team(models=[]) + other_team: Final = scenario.team(models=[]) + pattern: Final = f"byok-{uuid.uuid4().hex}/*" + team_wildcard: Final = _wildcard(scenario, pattern, team_id=byok_team) + shared_wildcard: Final = _wildcard(scenario, pattern, team_id=None) + model: Final = pattern.replace("*", "gpt-4o-mini") + + served: Final = { + "byok team key": _served_by(gateway, scenario.key(team_id=byok_team), model), + "other team key": _served_by(gateway, scenario.key(team_id=other_team), model), + "key without a team": _served_by(gateway, scenario.key(), model), + } + + assert served == { + "byok team key": team_wildcard, + "other team key": shared_wildcard, + "key without a team": shared_wildcard, + } From 715e9bb08d7567d58beebff640a7b32c4370342d Mon Sep 17 00:00:00 2001 From: yucheng-berri Date: Mon, 26 Oct 2026 17:57:06 -0700 Subject: [PATCH 60/73] fix(prometheus): count provider-side 401s in LLM failure and deployment metrics (#46668) * fix(prometheus): count provider-side 401s in LLM failure and deployment metrics The invalid virtual key skip no longer applies to an error the LLM provider returned while the deployment was called with its own credential, so a 401 for a bad deployment credential increments litellm_llm_api_failed_requests_metric, litellm_deployment_failure_responses and litellm_deployment_total_requests and marks litellm_deployment_state as a partial outage again. Proxy-gate rejections and a failing credential that belongs to the caller (a rejected per-user credential, an api_key sent with the request, a per-user GitHub Copilot session) keep the skip * fix(prometheus): keep a caller's own Anthropic OAuth token failure out of the deployment metrics A provider 401 on an Anthropic OAuth token the caller forwarded through the proxy is the caller's credential failing, so it stays out of the deployment failure and outage series like a caller-supplied api_key. Adds integration cells for an Anthropic deployment's own key, the forwarded caller OAuth token and a direct SDK call that brings its own key * test(prometheus): pin how a caller's own key or Authorization header on a deployment is counted An SDK Router call that passes its own api_key is counted against the one-off deployment the router builds for that key, so the operator deployment keeps only its own failure. A provider 401 on an Authorization header the caller sends through the proxy counts against the deployment, as on v1.80.12 * fix(prometheus): keep caller-sent provider credentials and token-exchange 401s out of deployment metrics A caller can send its own provider credential in the request body, such as azure_ad_token, azure_scope, the Azure Entra app fields, watsonx token, gigachat_access_token, OCI keys, AWS keys or Vertex credentials, at the top level or inside extra_body. A provider 401 on that credential is the caller's failure, so it stays out of the shared deployment's failure, request, LLM-failed and state series, as on main. A token-exchange deployment trades the caller's identity token on every call, so its 401s stay out as well, matching the router cooldown. * test(prometheus): Final locals, precise helper types and one for per comprehension in the new test helpers * test(prometheus): pin that a provider 401 at a caller-sent api_base counts against its one-off deployment * fix(prometheus): keep a provider 401 on caller-sent headers out of deployment metrics A caller can override the deployment's credential with headers it sends in the request body: headers or provider_specific_header, or an Authorization, api-key, x-api-key, x-goog-api-key or Ocp-Apim-Subscription-Key entry in extra_headers. A provider 401 on such a request is the caller's failure, so it stays out of the shared deployment's failure, request, LLM-failed and state series, as on main. * fix(prometheus): skip a provider 401 only when the caller's headers carry a credential The proxy now records headers, extra_headers or provider_specific_header in credential_fields only when they name a provider credential header (Authorization, api-key, x-api-key, x-goog-api-key, Ocp-Apim-Subscription-Key), the same way api_key is recorded. A caller that sends a plain header such as a trace id still has a provider 401 on the deployment's own key counted against the deployment. * fix(prometheus): stamp credential_fields after the proxy forwards the caller's headers A provider credential header the proxy forwards from the caller's HTTP request (forward_llm_provider_auth_headers + forward_client_headers_to_llm_api) lands in data["headers"] after proxy_server_request is built, so the stamp missed it and a 401 on that caller credential was counted against the deployment. Compute the stamp after the header forwarding steps and pin both forwarded cases. * fix(prometheus): skip a provider 401 when the caller sends any header or an OpenAI organization The proxy now records headers, extra_headers or provider_specific_header in credential_fields whenever the caller sends or the proxy forwards any header there, and records organization when the caller sends one in the body or as a forwarded OpenAI-Organization header. A forwarded header can be a gateway key such as x-portkey-api-key or an OpenAI-Project id, and a wrong organization gets a 401 from OpenAI, so these 401s stay out of the deployment metrics as on main instead of marking a healthy deployment as a partial outage. * fix(prometheus): ignore the user information the proxy adds when stamping caller headers With add_user_information_to_llm_headers on and header forwarding enabled, the proxy adds x-litellm-user_api_key_* headers to the forwarded headers. Those carry the proxy's own key and user details, not the caller's, so they no longer make a request look caller-scoped, and a provider 401 on the deployment's own key on such a request is counted. * fix(prometheus): skip a 401 raised before the provider is called or at the caller's own endpoint A deployment with no key of its own fails with LiteLLM's local "Missing Anthropic API Key" 401 before any provider call, and a caller who sends aws_bedrock_runtime_endpoint (or another provider endpoint field) can get a 401 from their own gateway. Both were counted against the shared deployment and marked it as a partial outage, where main skipped every 401. Count a 401 only once the provider was called (api_call_start_time is set), and treat caller-sent provider endpoint fields like caller credential fields. * fix(prometheus): count the deployment's own 401 on requests that carry only anthropic-version or anthropic-beta The Anthropic SDK sends anthropic-version on every request, and the proxy copies anthropic-version and anthropic-beta into provider_specific_header for Anthropic, Bedrock and Vertex. Both were treated as caller headers, so a 401 on an Anthropic deployment's own key from any Anthropic SDK client stayed out of the deployment metrics. These two headers only pick the API version and beta features. Anthropic answers a bad anthropic-version or an unknown anthropic-beta with a 400, never a 401, so they cannot turn the deployment's own key into a 401 and no longer mark the request as carrying a caller header. * test(prometheus): read and write the owned proxy's own database in the expired-key and MCP cells Since #47213 an owned proxy boots on a private database, so the expired-key update and the MCP guardrail row read now go through the gateway's database_url instead of the worker's. * fix(prometheus): keep credential_fields as main stamps it and give Prometheus its own caller_credential_fields credential_fields tells the Rust OCR bridge which stripped body fields the caller sent. Repurposing it for the Prometheus caller-credential check changed its value (organization and extra_headers added, anthropic-version-only headers dropped). Restore main's stamp unchanged and stamp the Prometheus classification under caller_credential_fields, which only Prometheus reads. * fix(prometheus): skip a provider 401 on a stored credential the caller picks by litellm_credential_name A request body litellm_credential_name swaps the deployment's stored credential for one the caller chose, so a 401 on it is the caller's, not the deployment's. Treat it like the other caller credential fields so it no longer marks a healthy shared deployment as a partial outage. --------- Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com> --- litellm/integrations/prometheus.py | 97 +- litellm/proxy/litellm_pre_call_utils.py | 45 + .../copilot_session_prometheus_worker.py | 162 +++ .../test_prometheus_provider_auth_failure.py | 1074 +++++++++++++++++ ..._prometheus_provider_auth_failure_chaos.py | 147 +++ ...etheus_copilot_session_auth_failure_sdk.py | 99 ++ .../test_prometheus_invalid_key_filtering.py | 441 ++++++- .../unit/proxy/test_litellm_pre_call_utils.py | 216 ++++ 8 files changed, 2220 insertions(+), 61 deletions(-) create mode 100644 tests/integration/_support/copilot_session_prometheus_worker.py create mode 100644 tests/integration/observability/test_prometheus_provider_auth_failure.py create mode 100644 tests/integration/observability/test_prometheus_provider_auth_failure_chaos.py create mode 100644 tests/integration/sdk/test_prometheus_copilot_session_auth_failure_sdk.py diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index 19d161b8aa4..bc8344fa686 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -14,6 +14,7 @@ from functools import partial from types import MappingProxyType from typing import TYPE_CHECKING, Annotated, Any, Final, Literal, Protocol, TypeAlias, TypeVar, cast +import openai from pydantic import BaseModel, Field, TypeAdapter, ValidationError from typing_extensions import ReadOnly, TypedDict, assert_never @@ -22,11 +23,13 @@ from litellm._internal_context import with_service_target from litellm._logging import print_verbose, verbose_logger from litellm.constants import ( AUTH_OBJECTS_TARGET, + LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL, PROMETHEUS_OVERFLOW_SERIES_LABEL_VALUE, PROXY_LLM_PROVIDER_FALLBACK, PROXY_REJECTED_BEFORE_ROUTING_KEY, ) from litellm.exceptions import ( + CallerCredentialAuthenticationError, MalformedRequestFieldError, validate_rate_limit_category, validate_rate_limit_type, @@ -51,6 +54,7 @@ from litellm.litellm_core_utils.get_llm_provider_logic import is_registered_cust from litellm.litellm_core_utils.service_tier_utils import ( get_service_tier_from_standard_logging_payload, ) +from litellm.llms.github_copilot.per_user_auth import github_copilot_user_session_from from litellm.llms.openai_like.json_loader import JSONProviderRegistry from litellm.models.end_user import LiteLLM_EndUserTable from litellm.models.team import LiteLLM_TeamTable @@ -269,7 +273,10 @@ _NO_LOGGED_FIELDS: Final[Mapping[str, object]] = MappingProxyType({}) def _logged_fields(value: object) -> Mapping[str, object]: - return _LOGGED_FIELDS.validate_python(value) if isinstance(value, Mapping) else _NO_LOGGED_FIELDS + try: + return _LOGGED_FIELDS.validate_python(value) + except ValidationError: + return _NO_LOGGED_FIELDS def _is_llm_provider(name: str) -> bool: @@ -359,6 +366,87 @@ def _get_proxy_llm_router() -> Router | None: return llm_router +_CALLER_CREDENTIAL_FIELDS: Final = frozenset( + { + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token", + "aws_web_identity_token", + "azure_ad_token", + "azure_password", + "azure_scope", + "azure_username", + "client_id", + "client_secret", + "gigachat_access_token", + "litellm_credential_name", + "oci_fingerprint", + "oci_key", + "oci_key_file", + "oci_tenancy", + "oci_user", + "tenant_id", + "token", + "user_config", + "vertex_ai_credentials", + "vertex_credentials", + "watsonx_credentials", + "wx_credentials", + "zen_api_key", + } +) + + +_CALLER_ENDPOINT_FIELDS: Final = frozenset( + {"aws_bedrock_runtime_endpoint", "aws_sts_endpoint", "deployment_url", "s3_endpoint_url", "sagemaker_base_url"} +) + + +_CALLER_TRANSPORT_FIELDS: Final = frozenset( + {"api_key", "extra_headers", "headers", "organization", "provider_specific_header"} +) + + +def _sent_a_provider_credential_or_endpoint(caller_body: Mapping[str, object]) -> bool: + """True when the caller's request body, or its ``extra_body``, carries a provider credential field or a + provider endpoint field.""" + sent: Final = {*caller_body, *_logged_fields(caller_body.get("extra_body"))} + return not (_CALLER_CREDENTIAL_FIELDS.isdisjoint(sent) and _CALLER_ENDPOINT_FIELDS.isdisjoint(sent)) + + +def _used_a_caller_credential(logged_call: Mapping[str, object]) -> bool: + """True when the provider was called with a credential or at an endpoint that belongs to the caller rather than + the deployment: an api_key, header, OpenAI organization or other provider credential or endpoint field the caller + sent with the request, a stored credential the caller picked by name, the caller's own Anthropic OAuth token, the caller's own GitHub Copilot connection, or the + caller's identity token that a token-exchange deployment trades in on every call.""" + litellm_params: Final = _logged_fields(logged_call.get("litellm_params")) + proxy_server_request: Final = _logged_fields(litellm_params.get("proxy_server_request")) + caller_credential_fields: Final = proxy_server_request.get("caller_credential_fields") + logged_metadata: Final = _logged_fields(_logged_fields(logged_call.get("standard_logging_object")).get("metadata")) + return ( + ( + isinstance(caller_credential_fields, tuple) + and any(field in caller_credential_fields for field in _CALLER_TRANSPORT_FIELDS) + ) + or _sent_a_provider_credential_or_endpoint(_logged_fields(proxy_server_request.get("body"))) + or bool(litellm_params.get("token_exchange_endpoint")) + or logged_metadata.get("used_client_oauth_token") is True + or github_copilot_user_session_from(litellm_params) is not None + ) + + +def _is_deployment_failure(exception: openai.APIError, logged_call: Mapping[str, object]) -> bool: + """True for an error raised while calling the deployment with its own credential, on a request the proxy + did not reject itself. An error LiteLLM raises before it calls the provider, such as a missing deployment key, + is not one.""" + return not ( + isinstance(exception, CallerCredentialAuthenticationError) + or bool(logged_call.get(LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL)) + or logged_call.get("api_call_start_time") is None + or _used_a_caller_credential(logged_call) + ) + + def _bounded_requested_model_label(requested_model: object, router_originated: bool = False) -> str | None: """ Bound ``requested_model`` label cardinality: names the router recognizes @@ -2817,6 +2905,8 @@ class PrometheusLogger(CustomLogger): information from various callback function signatures and determines if the request represents an invalid API key attempt that should be filtered from metrics. + An error raised while calling the deployment with its own credential is never skipped. + Args: kwargs: Dictionary potentially containing exception and other data user_api_key_dict: User API key authentication object (currently unused) @@ -2836,6 +2926,11 @@ class PrometheusLogger(CustomLogger): if exception is None and kwargs: exception = kwargs.get("exception") + if isinstance(exception, openai.APIError) and _is_deployment_failure( + exception, _LOGGED_FIELDS.validate_python(kwargs or {}) + ): + return False + if self._is_invalid_api_key_request(status_code, exception=exception): verbose_logger.debug( "Skipping Prometheus metrics for invalid API key request: status_code=%s, exception=%s", diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index b51eea7a876..5f1948d1c1c 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -99,6 +99,8 @@ _CREDENTIAL_HEADER_NAMES: Final = SpecialHeaders.litellm_credential_header_names {"cookie", "proxy-authorization"} ) _TRANSPORT_ONLY_CREDENTIAL_KEYS: Final = frozenset({"provider_specific_header", "headers", "api_key"}) +_REQUEST_FIELDS: Final = TypeAdapter(Mapping[str, object]) +_REQUEST_ENTRIES: Final = TypeAdapter(tuple[object, ...]) # Matches any header of the form x--session-id (case-insensitive). # Excludes the two explicit litellm headers which are handled with higher priority. @@ -289,6 +291,9 @@ _DESTINATION_ONLY_CALLBACK_VARS: Final[frozenset[str]] = frozenset({CAPTURE_MESS _CALLBACK_CREDENTIAL_KEYS: Final = frozenset(StandardCallbackDynamicParams.__annotations__) | frozenset( {TRUSTED_CALLBACK_VARS_FIELD} ) +_HEADERS_THAT_CANNOT_CHANGE_PROVIDER_AUTH: Final = frozenset( + {*(f"x-litellm-{key}" for key in StandardLoggingUserAPIKeyMetadata.__annotations__), *ANTHROPIC_API_HEADERS} +) service_logger_obj: Final = ServiceLogging() # used for tracking latency on OTEL # Bounded dedup for stale-alias warnings (FIFO eviction when over cap). _MAX_STALE_ALIAS_WARNING_KEYS: Final = 10_000 @@ -1451,6 +1456,44 @@ def clean_headers( return clean_headers +def _request_fields(value: object) -> Mapping[str, object]: + try: + return _REQUEST_FIELDS.validate_python(value) + except PydanticValidationError: + return MappingProxyType({}) + + +def _request_entries(value: object) -> tuple[object, ...]: + if value is None: + return () + try: + return _REQUEST_ENTRIES.validate_python(value) + except PydanticValidationError: + return (value,) + + +def _carries_a_caller_header(headers: object) -> bool: + return any(name.lower() not in _HEADERS_THAT_CANNOT_CHANGE_PROVIDER_AUTH for name in _request_fields(headers)) + + +def _caller_credential_fields(data: Mapping[str, object]) -> tuple[str, ...]: + """Names of the request fields through which the caller can change how the provider authenticates the call: + ``api_key``, ``organization``, and ``headers``, ``extra_headers`` or a ``provider_specific_header`` entry's + ``extra_headers`` that carry any header besides the key and user details the proxy adds under + ``add_user_information_to_llm_headers`` and the ``anthropic-version`` and ``anthropic-beta`` headers.""" + carried: Final = { + "api_key": "api_key" in data, + "extra_headers": _carries_a_caller_header(data.get("extra_headers")), + "headers": _carries_a_caller_header(data.get("headers")), + "organization": "organization" in data, + "provider_specific_header": any( + _carries_a_caller_header(_request_fields(entry).get("extra_headers")) + for entry in _request_entries(data.get("provider_specific_header")) + ), + } + return tuple(name for name, sent in sorted(carried.items()) if sent) + + def _is_credential_header(header: str) -> bool: """Whether `header` carries a caller credential rather than request context.""" return header.lower().removeprefix("x-pass-") in _CREDENTIAL_HEADER_NAMES @@ -2605,6 +2648,8 @@ async def add_litellm_data_to_request( data[_metadata_variable_name]["used_client_oauth_token"] = add_provider_specific_headers_to_request( data=data, headers=_headers ) + caller_fields: Final = _caller_credential_fields(_REQUEST_FIELDS.validate_python(data)) + data["proxy_server_request"]["caller_credential_fields"] = caller_fields # rebind-ok: data is an out-param ## Cache Controls cache_control_header: Final = _headers.get("Cache-Control", None) diff --git a/tests/integration/_support/copilot_session_prometheus_worker.py b/tests/integration/_support/copilot_session_prometheus_worker.py new file mode 100644 index 00000000000..a6b2c5d4692 --- /dev/null +++ b/tests/integration/_support/copilot_session_prometheus_worker.py @@ -0,0 +1,162 @@ +"""One SDK process for the SDK provider-401 metric cells: a provider 401 on a caller's own Copilot session, one on +the router deployment's own key, one on a key the router caller passes and one on a direct call's own key, then a +report of the deployment series on that process's default registry. Run as a script by ``run_copilot_session_worker`` so each process registers the +Prometheus metrics exactly once.""" + +from __future__ import annotations + +import asyncio +import json +import subprocess +import sys +from itertools import chain +from dataclasses import dataclass +from typing import Final + +import litellm +import openai +from litellm.integrations.prometheus import PrometheusLogger +from litellm.llms.github_copilot.per_user_auth import GithubCopilotUserSession +from prometheus_client import REGISTRY +from pydantic import TypeAdapter + +COPILOT_DEPLOYMENT: Final = "copilot-per-user-deployment" +OPERATOR_DEPLOYMENT: Final = "operator-key-deployment" +CALLER_SESSION_TOKEN: Final = "caller-copilot-session-token" +OPERATOR_KEY: Final = "rotated-operator-key" +ROUTER_CALLER_KEY: Final = "rotated-router-caller-key" +DIRECT_CALL: Final = "direct-sdk-call" +DIRECT_KEY: Final = "rotated-direct-call-key" +FAILURES: Final = "litellm_deployment_failure_responses_total" +TOTALS: Final = "litellm_deployment_total_requests_total" +LLM_FAILED: Final = "litellm_llm_api_failed_requests_metric_total" +STATE: Final = "litellm_deployment_state" +DEPLOYMENT_SERIES: Final = frozenset({FAILURES, TOTALS, LLM_FAILED, STATE}) +WORKER_TIMEOUT_SECONDS: Final = 120 + + +@dataclass(frozen=True, slots=True) +class Outcome: + deployment: str + status: int | None + + +@dataclass(frozen=True, slots=True) +class Series: + metric: str + model_id: str + api_base: str + value: float + + +@dataclass(frozen=True, slots=True) +class WorkerReport: + api_base: str + outcomes: tuple[Outcome, ...] + series: tuple[Series, ...] + + def values(self, metric: str, model_id: str) -> tuple[float, ...]: + return tuple(s.value for s in self.series if s.metric == metric and s.model_id == model_id) + + def on_the_endpoint(self, metric: str, model_id: str) -> tuple[float, ...]: + return tuple( + s.value for s in self.series if (s.metric, s.model_id, s.api_base) == (metric, model_id, self.api_base) + ) + + def outside_the_router(self, metric: str) -> tuple[float, ...]: + return tuple(s.value for s in self.series if s.metric == metric and not s.model_id) + + def on_a_one_off_deployment(self, metric: str) -> tuple[float, ...]: + named_ids: Final = ("", COPILOT_DEPLOYMENT, OPERATOR_DEPLOYMENT) + return tuple(s.value for s in self.series if s.metric == metric and s.model_id not in named_ids) + + +_REPORT: Final = TypeAdapter(WorkerReport) + + +def run_copilot_session_worker(api_base: str) -> WorkerReport: + completed: Final = subprocess.run( + (sys.executable, "-P", __file__, api_base), + capture_output=True, + text=True, + timeout=WORKER_TIMEOUT_SECONDS, + check=False, + ) + assert completed.returncode == 0, completed.stderr[-3000:] + return _REPORT.validate_json(completed.stdout.splitlines()[-1]) + + +def _deployment_series() -> tuple[Series, ...]: + samples: Final = tuple(chain.from_iterable(family.samples for family in REGISTRY.collect())) + return tuple( + Series( + metric=sample.name, + model_id=str(sample.labels.get("model_id", "")), + api_base=str(sample.labels.get("api_base", "")), + value=float(sample.value), + ) + for sample in samples + if sample.name in DEPLOYMENT_SERIES + ) + + +def _router(api_base: str) -> litellm.Router: + return litellm.Router( + model_list=[ + { + "model_name": COPILOT_DEPLOYMENT, + "litellm_params": { + "model": "github_copilot/gpt-4o", + "litellm_credential_name": "copilot-per-user", + "github_copilot_auth_type": "per_user_oauth", + }, + "model_info": {"id": COPILOT_DEPLOYMENT}, + }, + { + "model_name": OPERATOR_DEPLOYMENT, + "litellm_params": {"model": "openai/gpt-5.6", "api_base": api_base, "api_key": OPERATOR_KEY}, + "model_info": {"id": OPERATOR_DEPLOYMENT}, + }, + ], + num_retries=0, + disable_cooldowns=True, + ) + + +async def _attempt(router: litellm.Router, model: str, **extra: object) -> Outcome: + try: + await router.acompletion(model=model, messages=[{"role": "user", "content": f"{model} worker"}], **extra) + except openai.APIStatusError as error: + return Outcome(deployment=model, status=error.status_code) + return Outcome(deployment=model, status=None) + + +async def _direct_attempt(api_base: str) -> Outcome: + try: + await litellm.acompletion( + model="openai/gpt-5.6", + api_base=api_base, + api_key=DIRECT_KEY, + messages=[{"role": "user", "content": f"{DIRECT_CALL} worker"}], + ) + except openai.APIStatusError as error: + return Outcome(deployment=DIRECT_CALL, status=error.status_code) + return Outcome(deployment=DIRECT_CALL, status=None) + + +async def main(api_base: str) -> None: + litellm.callbacks = [PrometheusLogger()] # test-quality-ok: this worker process exits right after the report + session: Final = GithubCopilotUserSession(token=CALLER_SESSION_TOKEN, api_base=api_base) + router: Final = _router(api_base) + outcomes: Final = ( + await _attempt(router, COPILOT_DEPLOYMENT, github_copilot_user_session=session), + await _attempt(router, OPERATOR_DEPLOYMENT), + await _attempt(router, OPERATOR_DEPLOYMENT, api_key=ROUTER_CALLER_KEY), + await _direct_attempt(api_base), + ) + report: Final = WorkerReport(api_base=api_base, outcomes=outcomes, series=_deployment_series()) + sys.stdout.write(json.dumps(_REPORT.dump_python(report)) + "\n") + + +if __name__ == "__main__": + asyncio.run(main(sys.argv[1])) diff --git a/tests/integration/observability/test_prometheus_provider_auth_failure.py b/tests/integration/observability/test_prometheus_provider_auth_failure.py new file mode 100644 index 00000000000..13d264ca6b0 --- /dev/null +++ b/tests/integration/observability/test_prometheus_provider_auth_failure.py @@ -0,0 +1,1074 @@ +from __future__ import annotations + +import asyncio +import json +import threading +import uuid +from collections import Counter +from collections.abc import Iterator, Sequence +from dataclasses import dataclass +from hashlib import sha256 +from pathlib import Path +from typing import Final + +import httpx +import openai +import pytest +import yaml +from integration._support.client import Gateway, Scenario, eventually, gateway_from_environment +from integration._support.database import write_rows +from integration._support.mcp import ENTRY_POINTS, McpCaller, McpPeer, echo_tool, register_mcp, scripted_peer +from integration._support.process import owned_proxy +from integration._support.prometheus_series import Sample, scrape +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue + +RUN: Final = uuid.uuid4().hex[:8] +REJECTS: Final = "provider-rejects-credential" +HELD_REJECTS: Final = "provider-rejects-credential-when-released" +KEY_NOT_VALID: Final = "provider-key-not-valid" +RATE_LIMITS: Final = "provider-rate-limits" +RECOVERS: Final = "provider-rejects-once" +HEALTHY: Final = "provider-healthy" +AGENT_365_GUARDRAIL: Final = f"agent-365-{RUN}" +CALLER_OAUTH_TOKEN: Final = f"sk-ant-oat01-caller-{RUN}" +REVOKED_GATEWAY_KEY: Final = f"revoked-gateway-key-{RUN}" +UNKNOWN_ORGANIZATION: Final = f"org-unknown-{RUN}" +REVOKED_STORED_KEY: Final = f"sk-revoked-stored-credential-{RUN}" +AZURE_API_VERSION: Final = "2024-02-01" +CALLER_AZURE_CELLS: Final = ( + ("chat", False), + ("embeddings", False), + ("responses", False), + ("messages", False), + ("responses", True), +) +CALLER_CREDENTIAL_CELLS: Final = ( + ("caller-watsonx-token", "token"), + ("caller-gigachat-access-token", "gigachat_access_token"), +) +TOKEN_EXCHANGE_CELLS: Final = ("token-exchange", "token-exchange-credential") +KEYLESS_CELLS: Final = ("keyless", "messages-keyless") +TOKEN_EXCHANGE_SETTINGS: Final = { + "token_exchange_endpoint": "https://login.microsoftonline.com/00000000-0000-4000-8000-0000000c0b01/oauth2/v2.0/token", + "client_id": "synthetic-obo-client-id", + "client_secret": "synthetic-obo-client-secret", +} + +FAILURES: Final = "litellm_deployment_failure_responses_total" +TOTALS: Final = "litellm_deployment_total_requests_total" +LLM_FAILED: Final = "litellm_llm_api_failed_requests_metric_total" +STATE: Final = "litellm_deployment_state" +PROXY_FAILED: Final = "litellm_proxy_failed_requests_metric_total" +SUCCESSES: Final = "litellm_deployment_success_responses_total" +DEPLOYMENT_SERIES: Final = (FAILURES, TOTALS, LLM_FAILED, STATE) + +ENDPOINT_CELLS: Final = ( + "chat", + "chat-stream", + "messages", + "messages-stream", + "responses", + "responses-stream", + "embeddings", +) +REJECTED_CELLS: Final = ( + *ENDPOINT_CELLS, + "sdk-sync", + "sdk-async", + "repeated", + "fallback", + "burst", + "caller-api-key", + "caller-extra-headers", + "caller-headers", + "caller-provider-specific-header", + "plain-extra-headers", + "plain-headers", + "plain-provider-specific-header", + "forwarded-provider-header", + "forwarded-plain-header", + "forwarded-user-information", +) +GATED_CELLS: Final = ("invalid-key", "missing-key", "expired-key", "blocked-key", "no-model-access") +CALLER_SCOPE_CELLS: Final = ("forwarded-gateway-key", "caller-organization", "forwarded-organization") +HEALTHY_CELLS: Final = (*GATED_CELLS, *CALLER_SCOPE_CELLS, "healthy", "key-rpm-limit") +GUARDRAIL_ROWS: Final = ( + "SELECT COALESCE(jsonb_path_query_first(metadata, " + "'$.guardrail_information[*] ? (@.guardrail_name == $name).guardrail_status', " + "jsonb_build_object('name', %s::text)) #>> '{}', 'none') AS status " + 'FROM "LiteLLM_SpendLogs" WHERE api_key = %s AND call_type = %s' +) + + +def name(cell: str) -> str: + return f"prometheus-{cell}-{RUN}" + + +def identity(cell: str) -> str: + return f"{name(cell)}-deployment" + + +def _error(status: int, message: str, kind: str, code: str) -> Reply: + return Reply(status=status, body=json.dumps({"error": {"message": message, "type": kind, "code": code}}).encode()) + + +def _completion() -> Reply: + return Reply( + body=json.dumps( + { + "id": f"chatcmpl-{uuid.uuid4().hex}", + "object": "chat.completion", + "created": 1, + "model": "gpt-4o-mini", + "choices": [{"index": 0, "message": {"role": "assistant", "content": "ok"}, "finish_reason": "stop"}], + "usage": {"prompt_tokens": 3, "completion_tokens": 1, "total_tokens": 4}, + } + ).encode() + ) + + +class Provider: + """Answers by upstream model name. `HELD_REJECTS` waits for `release` before rejecting the credential.""" + + def __init__(self, release: threading.Event) -> None: + self._lock: Final = threading.Lock() + self._seen: Final[Counter[str]] = Counter() + self._release: Final = release + + def __call__(self, request: Request) -> Reply: + sent: Final = json.loads(request.body) + model: Final = str(sent.get("model") or sent.get("model_id") or request.target.split("/")[2]) + if request.headers.get("x-portkey-api-key") == REVOKED_GATEWAY_KEY: + return _error(401, "Invalid gateway API key", "invalid_request_error", "invalid_api_key") + if request.headers.get("authorization") == f"Bearer {REVOKED_STORED_KEY}": + return _error(401, "Incorrect API key provided: sk-revok*****", "invalid_request_error", "invalid_api_key") + if request.headers.get("openai-organization") == UNKNOWN_ORGANIZATION: + return _error( + 401, + "OpenAI-Organization header should match organization for API key", + "invalid_request_error", + "mismatched_organization", + ) + with self._lock: + self._seen[model] += 1 + attempt: Final = self._seen[model] + if model == HELD_REJECTS: + assert self._release.wait(timeout=30), "held provider reply was never released" + if model in (REJECTS, HELD_REJECTS) or (model == RECOVERS and attempt == 1): + return _error(401, "Incorrect API key provided: sk-rotat*****", "invalid_request_error", "invalid_api_key") + if model == KEY_NOT_VALID: + return _error(400, "API key not valid. Please pass a valid API key.", "invalid_request_error", "400") + if model == RATE_LIMITS: + return _error(429, "Rate limit reached for requests", "rate_limit_error", "rate_limit_exceeded") + return _completion() + + +def deployment(cell: str, upstream_model: str, api_base: str) -> dict[str, JsonValue]: + return { + "model_name": name(cell), + "litellm_params": { + "model": f"openai/{upstream_model}", + "api_key": f"sk-deployment-credential-{cell}", + "api_base": api_base, + }, + "model_info": {"id": identity(cell)}, + } + + +def anthropic_deployment(cell: str, api_base: str) -> dict[str, JsonValue]: + return { + "model_name": name(cell), + "litellm_params": { + "model": f"anthropic/{REJECTS}", + "api_key": f"sk-ant-api03-deployment-credential-{cell}", + "api_base": api_base, + }, + "model_info": {"id": identity(cell)}, + } + + +def bedrock_deployment(cell: str, opt_in: dict[str, JsonValue]) -> dict[str, JsonValue]: + return { + "model_name": name(cell), + "litellm_params": { + "model": f"bedrock/converse/{REJECTS}", + "aws_access_key_id": "AKIADEPLOYMENTCREDENTIAL", + "aws_secret_access_key": f"deployment-credential-{cell}", + "aws_region_name": "us-east-1", + **opt_in, + }, + "model_info": {"id": identity(cell)}, + } + + +def keyless_anthropic_deployment(cell: str, api_base: str) -> dict[str, JsonValue]: + return { + "model_name": name(cell), + "litellm_params": {"model": f"anthropic/{REJECTS}", "api_base": api_base}, + "model_info": {"id": identity(cell)}, + } + + +def azure_deployment(cell: str, api_base: str, credential: dict[str, JsonValue]) -> dict[str, JsonValue]: + return { + "model_name": name(cell), + "litellm_params": { + "model": f"azure/{REJECTS}", + "api_base": api_base, + "api_version": AZURE_API_VERSION, + **credential, + }, + "model_info": {"id": identity(cell)}, + } + + +def caller_credential_deployment(cell: str, field: str, api_base: str) -> dict[str, JsonValue]: + provider: Final = "watsonx" if field == "token" else "gigachat" + project: Final[dict[str, JsonValue]] = {"project_id": f"project-{RUN}"} if provider == "watsonx" else {} + return { + "model_name": name(cell), + "litellm_params": { + "model": f"{provider}/{REJECTS}", + "api_base": api_base, + field: f"deployment-credential-{cell}", + **project, + }, + "model_info": {"id": identity(cell)}, + } + + +def token_exchange_deployment(cell: str) -> dict[str, JsonValue]: + settings: Final[dict[str, JsonValue]] = ( + {"litellm_credential_name": f"m365-obo-{RUN}"} if cell.endswith("-credential") else {**TOKEN_EXCHANGE_SETTINGS} + ) + return { + "model_name": name(cell), + "litellm_params": {"model": "microsoft_365_copilot/chat", **settings}, + "model_info": {"id": identity(cell)}, + } + + +def caller_azure_cell(opt_in: str, route: str, inside_extra_body: bool) -> str: + return f"{opt_in}-caller-azure-token-{route}{'-extra-body' if inside_extra_body else ''}" + + +def config(directory: Path, upstream: Wire) -> Path: + api_base: Final = f"{upstream.url}/v1" + configuration: Final = yaml.safe_load(Path("tests/integration/proxy_config.yaml").read_text()) + configuration["model_list"] = [ + *configuration["model_list"], + *(deployment(cell, REJECTS, api_base) for cell in REJECTED_CELLS), + *(deployment(cell, HEALTHY, api_base) for cell in HEALTHY_CELLS), + deployment("held", HELD_REJECTS, api_base), + deployment("key-not-valid", KEY_NOT_VALID, api_base), + deployment("rate-limited", RATE_LIMITS, api_base), + deployment("recovers", RECOVERS, api_base), + anthropic_deployment("anthropic-own-key", upstream.url), + anthropic_deployment("anthropic-sdk-own-key", upstream.url), + anthropic_deployment("caller-oauth", upstream.url), + *(keyless_anthropic_deployment(cell, upstream.url) for cell in KEYLESS_CELLS), + bedrock_deployment( + "deployment-caller-bedrock-endpoint", + {"configurable_clientside_auth_params": ["aws_bedrock_runtime_endpoint"]}, + ), + azure_deployment("azure-own-token", upstream.url, {"azure_ad_token": "deployment-entra-token-azure-own-token"}), + *( + azure_deployment( + caller_azure_cell("deployment", route, inside_extra_body), + upstream.url, + {"configurable_clientside_auth_params": ["azure_ad_token"]}, + ) + for route, inside_extra_body in CALLER_AZURE_CELLS + ), + *(caller_credential_deployment(cell, field, upstream.url) for cell, field in CALLER_CREDENTIAL_CELLS), + *(token_exchange_deployment(cell) for cell in TOKEN_EXCHANGE_CELLS), + { + "model_name": name("caller-api-base"), + "litellm_params": { + "model": f"openai/{REJECTS}", + "api_key": "sk-deployment-credential-caller-api-base", + "api_base": api_base, + "configurable_clientside_auth_params": ["api_base"], + }, + "model_info": {"id": identity("caller-api-base")}, + }, + { + "model_name": name("caller-credential"), + "litellm_params": { + "model": "github_copilot/gpt-4o", + "litellm_credential_name": f"copilot-per-user-{RUN}", + "github_copilot_auth_type": "per_user_oauth", + }, + "model_info": {"id": identity("caller-credential")}, + }, + { + "model_name": name("caller-credential-name"), + "litellm_params": { + "model": f"openai/{HEALTHY}", + "api_base": api_base, + "litellm_credential_name": f"deployment-key-{RUN}", + }, + "model_info": {"id": identity("caller-credential-name")}, + }, + ] + configuration["credential_list"] = [ + *configuration.get("credential_list", []), + {"credential_name": f"m365-obo-{RUN}", "credential_values": {**TOKEN_EXCHANGE_SETTINGS}, "credential_info": {}}, + { + "credential_name": f"deployment-key-{RUN}", + "credential_values": {"api_key": "sk-deployment-credential-caller-credential-name"}, + "credential_info": {}, + }, + { + "credential_name": f"revoked-key-{RUN}", + "credential_values": {"api_key": REVOKED_STORED_KEY}, + "credential_info": {}, + }, + ] + configuration["guardrails"] = [ + { + "guardrail_name": AGENT_365_GUARDRAIL, + "litellm_params": { + "guardrail": "agent_365", + "mode": "pre_mcp_call", + "default_on": True, + "tenant_id": "00000000-0000-4000-8000-0000000a3650", + "client_id": "synthetic-client-id", + "client_secret": "synthetic-client-secret", + }, + } + ] + configuration["litellm_settings"]["callbacks"] = ["prometheus"] + configuration["litellm_settings"]["model_group_settings"] = { + "forward_client_headers_to_llm_api": [ + name("forwarded-provider-header"), + name("forwarded-plain-header"), + name("forwarded-gateway-key"), + name("forwarded-user-information"), + ] + } + configuration["litellm_settings"]["add_user_information_to_llm_headers"] = True + configuration["general_settings"]["forward_llm_provider_auth_headers"] = True + configuration["general_settings"]["forward_openai_org_id"] = True + configuration.setdefault("router_settings", {}).update({"num_retries": 0, "disable_cooldowns": True}) + path: Final = directory / "prometheus-provider-auth-failure.yaml" + path.write_text(yaml.safe_dump(configuration)) + return path + + +@dataclass(frozen=True, slots=True) +class Rig: + gateway: Gateway + upstream: Wire + + @property + def api_base(self) -> str: + return f"{self.upstream.url}/v1" + + +@pytest.fixture(scope="module") +def rig(tmp_path_factory: pytest.TempPathFactory) -> Iterator[Rig]: + directory: Final = tmp_path_factory.mktemp("prometheus-provider-auth-failure") + released: Final = threading.Event() + released.set() + with ( + wire_server(Provider(released)) as upstream, + gateway_from_environment() as shared, + owned_proxy( + shared, + directory, + {"PROXY_CONFIG_RELOAD_INTERVAL_SECONDS": "2"}, + config=config(directory, upstream), + remove_environment=("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"), + ) as owned, + ): + yield Rig(owned, upstream) + + +def opted_in_config(directory: Path, upstream: Wire) -> Path: + configuration: Final = yaml.safe_load(Path("tests/integration/proxy_config.yaml").read_text()) + configuration["model_list"] = [ + *configuration["model_list"], + *( + azure_deployment(caller_azure_cell("proxy-wide", route, inside_extra_body), upstream.url, {}) + for route, inside_extra_body in CALLER_AZURE_CELLS + ), + bedrock_deployment("proxy-wide-caller-bedrock-endpoint", {}), + ] + configuration["general_settings"]["allow_client_side_credentials"] = True + configuration["litellm_settings"]["callbacks"] = ["prometheus"] + configuration.setdefault("router_settings", {}).update({"num_retries": 0, "disable_cooldowns": True}) + path: Final = directory / "prometheus-caller-credentials-opted-in.yaml" + path.write_text(yaml.safe_dump(configuration)) + return path + + +@pytest.fixture(scope="module") +def opted_in(tmp_path_factory: pytest.TempPathFactory) -> Iterator[Rig]: + directory: Final = tmp_path_factory.mktemp("prometheus-caller-credentials-opted-in") + released: Final = threading.Event() + released.set() + with ( + wire_server(Provider(released)) as upstream, + gateway_from_environment() as shared, + owned_proxy(shared, directory, {}, config=opted_in_config(directory, upstream)) as owned, + ): + yield Rig(owned, upstream) + + +def series(samples: Sequence[Sample], metric: str, **labels: str) -> tuple[Sample, ...]: + return tuple( + sample + for sample in samples + if sample.name == metric and all(sample.labels.get(label) == value for label, value in labels.items()) + ) + + +def values(samples: Sequence[Sample], metric: str, **labels: str) -> list[float]: + return [sample.value for sample in series(samples, metric, **labels)] + + +def until(gateway: Gateway, metric: str, **labels: str) -> tuple[Sample, ...]: + return eventually(lambda: scrape(gateway), lambda samples: bool(series(samples, metric, **labels)), 30) + + +def messages(cell: str) -> list[JsonValue]: + return [{"role": "user", "content": f"{cell} {uuid.uuid4().hex}"}] + + +def send(gateway: Gateway, cell: str, model: str | None = None, key: str | None = None) -> httpx.Response: + asked: Final = model or name(cell) + stream: Final = cell.endswith("-stream") + if cell.startswith("messages"): + anthropic: Final = {"model": asked, "max_tokens": 16, "messages": messages(cell), "stream": stream} + return gateway.request("POST", "/v1/messages", anthropic, key=key) + if cell.startswith("responses"): + return gateway.request("POST", "/v1/responses", {"model": asked, "input": cell, "stream": stream}, key=key) + if cell == "embeddings": + return gateway.request("POST", "/v1/embeddings", {"model": asked, "input": cell}, key=key) + return gateway.request( + "POST", "/v1/chat/completions", {"model": asked, "messages": messages(cell), "stream": stream}, key=key + ) + + +def assert_reached_with_the_deployment_credential(rig: Rig, cell: str, attempts: int = 1) -> None: + reached: Final = rig.upstream.drain() + assert [request.headers["authorization"] for request in reached] == [ + f"Bearer sk-deployment-credential-{cell}" + ] * attempts, [request.target for request in reached] + + +def recorded(samples: Sequence[Sample], **labels: str) -> list[tuple[str, dict[str, str], float]]: + return [(sample.name, dict(sample.labels), sample.value) for sample in series_of(samples, **labels)] + + +def series_of(samples: Sequence[Sample], **labels: str) -> tuple[Sample, ...]: + return tuple( + sample + for sample in samples + if sample.name.startswith("litellm_") + and all(sample.labels.get(label) == value for label, value in labels.items()) + ) + + +def assert_kept_out_of_the_deployment_metrics(rig: Rig, cell: str) -> None: + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + assert {metric: values(samples, metric, model_id=identity(cell)) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + }, recorded(samples, model_id=identity(cell)) + + +def assert_recorded_as_a_deployment_failure(rig: Rig, cell: str, status: str, count: float = 1.0) -> None: + samples: Final = until(rig.gateway, FAILURES, model_id=identity(cell)) + observed: Final = recorded(samples, model_id=identity(cell)) + assert values(samples, FAILURES, model_id=identity(cell), exception_status=status) == [count], observed + assert values(samples, TOTALS, model_id=identity(cell)) == [count], observed + assert values(samples, LLM_FAILED, model_id=identity(cell)) == [count], observed + assert values(samples, STATE, model_id=identity(cell)) == [1.0], observed + + +@pytest.mark.parametrize("cell", ENDPOINT_CELLS) +def test_a_provider_401_on_every_endpoint_counts_as_a_deployment_failure_and_a_partial_outage( + rig: Rig, cell: str +) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, cell) + assert response.status_code == 401, response.text + assert_reached_with_the_deployment_credential(rig, cell) + assert_recorded_as_a_deployment_failure(rig, cell, "401") + + +def test_a_provider_401_seen_by_the_sync_openai_client_is_recorded(rig: Rig) -> None: + rig.upstream.drain() + with ( + openai.OpenAI( + base_url=f"{rig.gateway.client.base_url}/v1", + api_key=rig.gateway.key, + max_retries=0, + http_client=httpx.Client(trust_env=False), + ) as client, + pytest.raises(openai.AuthenticationError, match="Incorrect API key provided"), + ): + client.chat.completions.create(model=name("sdk-sync"), messages=[{"role": "user", "content": "sync"}]) + assert_reached_with_the_deployment_credential(rig, "sdk-sync") + assert_recorded_as_a_deployment_failure(rig, "sdk-sync", "401") + + +def test_a_provider_401_seen_by_the_async_openai_client_is_recorded(rig: Rig) -> None: + async def ask() -> None: + async with openai.AsyncOpenAI( + base_url=f"{rig.gateway.client.base_url}/v1", + api_key=rig.gateway.key, + max_retries=0, + http_client=httpx.AsyncClient(trust_env=False), + ) as client: + await client.chat.completions.create(model=name("sdk-async"), messages=[{"role": "user", "content": "a"}]) + + rig.upstream.drain() + with pytest.raises(openai.AuthenticationError, match="Incorrect API key provided"): + asyncio.run(ask()) + assert_reached_with_the_deployment_credential(rig, "sdk-async") + assert_recorded_as_a_deployment_failure(rig, "sdk-async", "401") + + +def test_each_repeated_provider_401_is_counted_once(rig: Rig) -> None: + rig.upstream.drain() + assert [send(rig.gateway, "repeated").status_code for _ in range(3)] == [401, 401, 401] + assert_reached_with_the_deployment_credential(rig, "repeated", attempts=3) + assert_recorded_as_a_deployment_failure(rig, "repeated", "401", count=3.0) + + +def test_a_provider_401_rescued_by_a_fallback_still_counts_against_the_rejected_deployment(rig: Rig) -> None: + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("fallback"), "messages": messages("fallback"), "fallbacks": [name("healthy")]}, + ) + assert response.status_code == 200, response.text + assert [request.headers["authorization"] for request in rig.upstream.drain()] == [ + "Bearer sk-deployment-credential-fallback", + "Bearer sk-deployment-credential-healthy", + ] + assert_recorded_as_a_deployment_failure(rig, "fallback", "401") + rescued: Final = eventually( + lambda: scrape(rig.gateway), lambda samples: values(samples, SUCCESSES, model_id=identity("healthy")) == [1.0] + ) + assert values(rescued, FAILURES, model_id=identity("healthy")) == [] + + +def test_a_provider_auth_message_on_a_non_401_status_is_recorded(rig: Rig) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, "key-not-valid") + assert response.status_code == 400, response.text + assert_reached_with_the_deployment_credential(rig, "key-not-valid") + assert_recorded_as_a_deployment_failure(rig, "key-not-valid", "400") + + +def test_a_provider_429_is_recorded_as_before(rig: Rig) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, "rate-limited") + assert response.status_code == 429, response.text + assert_reached_with_the_deployment_credential(rig, "rate-limited") + assert_recorded_as_a_deployment_failure(rig, "rate-limited", "429") + + +def test_a_deployment_marked_down_by_a_provider_401_is_healthy_again_after_a_success(rig: Rig) -> None: + rig.upstream.drain() + assert send(rig.gateway, "recovers").status_code == 401 + assert_recorded_as_a_deployment_failure(rig, "recovers", "401") + recovered: Final = send(rig.gateway, "recovers") + assert recovered.status_code == 200, recovered.text + assert len(rig.upstream.drain()) == 2 + eventually( + lambda: scrape(rig.gateway), + lambda samples: values(samples, STATE, model_id=identity("recovers"), api_base=rig.api_base) == [0.0], + ) + + +def test_a_per_caller_credential_rejection_stays_out_of_the_deployment_metrics(rig: Rig) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, "caller-credential") + assert response.status_code == 401, response.text + assert "Connect GitHub Copilot" in response.text, response.text + assert rig.upstream.drain() == () + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name("caller-credential")) + assert values(samples, FAILURES, model_id=identity("caller-credential")) == [] + assert values(samples, TOTALS, model_id=identity("caller-credential")) == [] + assert values(samples, LLM_FAILED, model_id=identity("caller-credential")) == [] + assert values(samples, STATE, model_id=identity("caller-credential")) == [] + + +def deployment_series(samples: Sequence[Sample]) -> frozenset[tuple[str, tuple[tuple[str, str], ...]]]: + return frozenset((sample.name, sample.identity()) for sample in samples if sample.name in DEPLOYMENT_SERIES) + + +def test_a_provider_401_on_an_api_key_the_caller_sent_stays_out_of_the_deployment_metrics(rig: Rig) -> None: + caller_key: Final = f"sk-caller-supplied-{uuid.uuid4().hex}" + before: Final = deployment_series(scrape(rig.gateway)) + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("caller-api-key"), "messages": messages("caller-api-key"), "api_key": caller_key}, + ) + assert response.status_code == 401, response.text + assert [request.headers["authorization"] for request in rig.upstream.drain()] == [f"Bearer {caller_key}"] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name("caller-api-key")) + assert deployment_series(samples) - before == frozenset(), recorded(samples, requested_model=name("caller-api-key")) + + +def test_a_provider_401_on_a_stored_credential_the_caller_names_leaves_a_healthy_deployment_healthy(rig: Rig) -> None: + cell: Final = "caller-credential-name" + rig.upstream.drain() + assert send(rig.gateway, cell).status_code == 200 + assert_reached_with_the_deployment_credential(rig, cell) + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name(cell), "messages": messages(cell), "litellm_credential_name": f"revoked-key-{RUN}"}, + ) + assert response.status_code == 401, response.text + assert [request.headers["authorization"] for request in rig.upstream.drain()] == [f"Bearer {REVOKED_STORED_KEY}"] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + observed: Final = recorded(samples, model_id=identity(cell)) + assert values(samples, FAILURES, model_id=identity(cell)) == [], observed + assert values(samples, LLM_FAILED, model_id=identity(cell)) == [], observed + assert values(samples, TOTALS, model_id=identity(cell)) == [1.0], observed + assert values(samples, STATE, model_id=identity(cell)) == [0.0], observed + + +def anthropic_messages(gateway: Gateway, cell: str, key: str, headers: dict[str, str]) -> httpx.Response: + body: Final[dict[str, JsonValue]] = {"model": name(cell), "max_tokens": 16, "messages": messages(cell)} + return gateway.request("POST", "/v1/messages", body, key=key, headers=headers) + + +def caller_header(field: str, header: str, value: str) -> JsonValue: + if field == "provider_specific_header": + return {"custom_llm_provider": "openai", "extra_headers": {header: value}} + return {header: value} + + +@pytest.mark.parametrize( + "cell,field", + [ + ("caller-extra-headers", "extra_headers"), + ("caller-headers", "headers"), + ("caller-provider-specific-header", "provider_specific_header"), + ], +) +def test_a_provider_401_on_an_authorization_header_the_caller_sends_stays_out_of_the_deployment_metrics( + rig: Rig, cell: str, field: str +) -> None: + caller_value: Final = f"Bearer sk-{cell}-{uuid.uuid4().hex}" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name(cell), "messages": messages(cell), field: caller_header(field, "Authorization", caller_value)}, + ) + assert response.status_code == 401, response.text + assert [request.headers["authorization"] for request in rig.upstream.drain()] == [caller_value] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + observed: Final = recorded(samples, model_id=identity(cell)) + assert values(samples, FAILURES, model_id=identity(cell)) == [], observed + assert values(samples, TOTALS, model_id=identity(cell)) == [], observed + assert values(samples, LLM_FAILED, model_id=identity(cell)) == [], observed + assert values(samples, STATE, model_id=identity(cell)) == [], observed + + +@pytest.mark.parametrize( + "cell,field", + [ + ("plain-extra-headers", "extra_headers"), + ("plain-headers", "headers"), + ("plain-provider-specific-header", "provider_specific_header"), + ], +) +def test_a_provider_401_on_the_deployments_own_key_stays_out_when_the_caller_sends_a_plain_header( + rig: Rig, cell: str, field: str +) -> None: + trace: Final = f"{cell}-{uuid.uuid4().hex}" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name(cell), "messages": messages(cell), field: caller_header(field, "X-Caller-Trace", trace)}, + ) + assert response.status_code == 401, response.text + reached: Final = rig.upstream.drain() + assert [(request.headers["authorization"], request.headers.get("x-caller-trace")) for request in reached] == [ + (f"Bearer sk-deployment-credential-{cell}", trace) + ] + assert_kept_out_of_the_deployment_metrics(rig, cell) + + +def test_a_provider_401_on_a_provider_credential_header_the_proxy_forwards_stays_out_of_the_deployment_metrics( + rig: Rig, +) -> None: + caller_key: Final = f"AIza-forwarded-{uuid.uuid4().hex}" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("forwarded-provider-header"), "messages": messages("forwarded-provider-header")}, + headers={"x-goog-api-key": caller_key}, + ) + assert response.status_code == 401, response.text + assert [request.headers.get("x-goog-api-key") for request in rig.upstream.drain()] == [caller_key] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name("forwarded-provider-header")) + observed: Final = recorded(samples, model_id=identity("forwarded-provider-header")) + assert values(samples, FAILURES, model_id=identity("forwarded-provider-header")) == [], observed + assert values(samples, TOTALS, model_id=identity("forwarded-provider-header")) == [], observed + assert values(samples, LLM_FAILED, model_id=identity("forwarded-provider-header")) == [], observed + assert values(samples, STATE, model_id=identity("forwarded-provider-header")) == [], observed + + +def test_a_provider_401_on_the_deployments_own_key_stays_out_when_the_proxy_forwards_a_plain_header(rig: Rig) -> None: + trace: Final = f"forwarded-plain-header-{uuid.uuid4().hex}" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("forwarded-plain-header"), "messages": messages("forwarded-plain-header")}, + headers={"x-caller-trace": trace}, + ) + assert response.status_code == 401, response.text + assert [ + (request.headers["authorization"], request.headers.get("x-caller-trace")) for request in rig.upstream.drain() + ] == [("Bearer sk-deployment-credential-forwarded-plain-header", trace)] + assert_kept_out_of_the_deployment_metrics(rig, "forwarded-plain-header") + + +def test_a_provider_401_on_the_deployments_own_key_counts_when_the_proxy_forwards_only_its_own_user_information( + rig: Rig, +) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, "forwarded-user-information") + assert response.status_code == 401, response.text + reached: Final = rig.upstream.drain() + assert [ + (request.headers["authorization"], "x-litellm-user_api_key_hash" in request.headers) for request in reached + ] == [("Bearer sk-deployment-credential-forwarded-user-information", True)] + assert_recorded_as_a_deployment_failure(rig, "forwarded-user-information", "401") + + +def test_a_provider_401_on_a_gateway_key_the_proxy_forwards_stays_out_of_a_healthy_deployments_metrics( + rig: Rig, +) -> None: + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("forwarded-gateway-key"), "messages": messages("forwarded-gateway-key")}, + headers={"x-portkey-api-key": REVOKED_GATEWAY_KEY}, + ) + assert response.status_code == 401, response.text + assert [ + (request.headers["authorization"], request.headers.get("x-portkey-api-key")) for request in rig.upstream.drain() + ] == [("Bearer sk-deployment-credential-forwarded-gateway-key", REVOKED_GATEWAY_KEY)] + assert_kept_out_of_the_deployment_metrics(rig, "forwarded-gateway-key") + + +@pytest.mark.parametrize( + "cell,body,headers", + [ + ("caller-organization", {"organization": UNKNOWN_ORGANIZATION}, {}), + ("forwarded-organization", {}, {"OpenAI-Organization": UNKNOWN_ORGANIZATION}), + ], +) +def test_a_provider_401_on_an_organization_the_caller_sends_stays_out_of_a_healthy_deployments_metrics( + rig: Rig, cell: str, body: dict[str, JsonValue], headers: dict[str, str] +) -> None: + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", "/v1/chat/completions", {"model": name(cell), "messages": messages(cell), **body}, headers=headers + ) + assert response.status_code == 401, response.text + assert [ + (request.headers["authorization"], request.headers.get("openai-organization")) + for request in rig.upstream.drain() + ] == [(f"Bearer sk-deployment-credential-{cell}", UNKNOWN_ORGANIZATION)] + assert_kept_out_of_the_deployment_metrics(rig, cell) + + +@pytest.mark.parametrize( + ("cell", "headers"), + [ + ("anthropic-own-key", {}), + ("anthropic-sdk-own-key", {"anthropic-version": "2023-06-01", "anthropic-beta": "tools-2024-04-04"}), + ], + ids=["no-client-headers", "anthropic-sdk-headers"], +) +def test_a_provider_401_on_an_anthropic_deployments_own_key_is_recorded( + rig: Rig, cell: str, headers: dict[str, str] +) -> None: + with rig.gateway.scenario() as scenario: + key: Final = scenario.key(models=[name(cell)]) + rig.upstream.drain() + response: Final = anthropic_messages(rig.gateway, cell, key, headers) + assert response.status_code == 401, response.text + assert [request.headers.get("x-api-key") for request in rig.upstream.drain()] == [ + f"sk-ant-api03-deployment-credential-{cell}" + ] + assert_recorded_as_a_deployment_failure(rig, cell, "401") + + +def test_a_provider_401_on_an_anthropic_oauth_token_the_caller_sent_stays_out_of_the_deployment_metrics( + rig: Rig, +) -> None: + with rig.gateway.scenario() as scenario: + key: Final = scenario.key(models=[name("caller-oauth")]) + rig.upstream.drain() + response: Final = anthropic_messages( + rig.gateway, + "caller-oauth", + CALLER_OAUTH_TOKEN, + {"x-litellm-api-key": key}, + ) + assert response.status_code == 401, response.text + assert [request.headers.get("authorization") for request in rig.upstream.drain()] == [ + f"Bearer {CALLER_OAUTH_TOKEN}" + ] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name("caller-oauth")) + assert {metric: values(samples, metric, model_id=identity("caller-oauth")) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + }, recorded(samples, requested_model=name("caller-oauth")) + + +def test_a_provider_401_on_an_azure_deployments_own_ad_token_is_recorded(rig: Rig) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, "azure-own-token") + assert response.status_code == 401, response.text + assert [request.headers.get("authorization") for request in rig.upstream.drain()] == [ + "Bearer deployment-entra-token-azure-own-token" + ] + assert_recorded_as_a_deployment_failure(rig, "azure-own-token", "401") + + +def caller_azure_request(cell: str, route: str, credential: dict[str, JsonValue]) -> tuple[str, dict[str, JsonValue]]: + if route == "embeddings": + return "/v1/embeddings", {"model": name(cell), "input": cell, **credential} + if route == "responses": + return "/v1/responses", {"model": name(cell), "input": cell, **credential} + if route == "messages": + return "/v1/messages", {"model": name(cell), "max_tokens": 16, "messages": messages(cell), **credential} + return "/v1/chat/completions", {"model": name(cell), "messages": messages(cell), **credential} + + +@pytest.mark.parametrize( + ("route", "inside_extra_body"), + CALLER_AZURE_CELLS, + ids=[f"{route}{'-extra-body' if inside_extra_body else ''}" for route, inside_extra_body in CALLER_AZURE_CELLS], +) +@pytest.mark.parametrize("opt_in", ("deployment", "proxy-wide")) +def test_a_provider_401_on_an_azure_ad_token_the_caller_sent_stays_out_of_the_deployment_metrics( + request: pytest.FixtureRequest, opt_in: str, route: str, inside_extra_body: bool +) -> None: + opted: Final[Rig] = request.getfixturevalue("rig" if opt_in == "deployment" else "opted_in") + cell: Final = caller_azure_cell(opt_in, route, inside_extra_body) + token: Final = f"caller-entra-token-{uuid.uuid4().hex}" + credential: Final[dict[str, JsonValue]] = ( + {"extra_body": {"azure_ad_token": token}} if inside_extra_body else {"azure_ad_token": token} + ) + opted.upstream.drain() + response: Final = opted.gateway.request("POST", *caller_azure_request(cell, route, credential)) + assert response.status_code == 401, response.text + assert [reached.headers.get("authorization") for reached in opted.upstream.drain()] == [ + None if inside_extra_body else f"Bearer {token}" + ] + samples: Final = until(opted.gateway, PROXY_FAILED, requested_model=name(cell)) + assert {metric: values(samples, metric, model_id=identity(cell)) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + }, recorded(samples, requested_model=name(cell)) + + +@pytest.mark.parametrize(("cell", "field"), CALLER_CREDENTIAL_CELLS, ids=[cell for cell, _ in CALLER_CREDENTIAL_CELLS]) +def test_a_provider_401_on_a_provider_credential_the_caller_sent_stays_out_of_the_deployment_metrics( + rig: Rig, cell: str, field: str +) -> None: + caller_credential: Final = f"caller-credential-{uuid.uuid4().hex}" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", "/v1/chat/completions", {"model": name(cell), "messages": messages(cell), field: caller_credential} + ) + assert response.status_code == 401, response.text + assert [reached.headers.get("authorization") for reached in rig.upstream.drain()] == [f"Bearer {caller_credential}"] + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + assert {metric: values(samples, metric, model_id=identity(cell)) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + }, recorded(samples, requested_model=name(cell)) + + +@pytest.mark.parametrize("cell", TOKEN_EXCHANGE_CELLS) +def test_a_token_exchange_deployment_rejecting_a_caller_without_an_identity_token_stays_out_of_the_deployment_metrics( + rig: Rig, cell: str +) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, cell) + assert response.status_code == 401, response.text + assert "IdP-issued access token" in response.text, response.text + assert rig.upstream.drain() == () + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + assert {metric: values(samples, metric, model_id=identity(cell)) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + }, recorded(samples, requested_model=name(cell)) + + +@pytest.mark.parametrize("cell", KEYLESS_CELLS) +def test_a_missing_deployment_key_reported_before_the_provider_is_called_stays_out_of_the_deployment_metrics( + rig: Rig, cell: str +) -> None: + rig.upstream.drain() + response: Final = send(rig.gateway, cell) + assert response.status_code == 401, response.text + assert "Missing Anthropic API Key" in response.text, response.text + assert rig.upstream.drain() == () + assert_kept_out_of_the_deployment_metrics(rig, cell) + + +@pytest.mark.parametrize("opt_in", ("deployment", "proxy-wide")) +def test_a_provider_401_at_a_bedrock_endpoint_the_caller_sends_stays_out_of_the_deployment_metrics( + request: pytest.FixtureRequest, opt_in: str +) -> None: + opted: Final[Rig] = request.getfixturevalue("rig" if opt_in == "deployment" else "opted_in") + cell: Final = f"{opt_in}-caller-bedrock-endpoint" + opted.upstream.drain() + response: Final = opted.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name(cell), "messages": messages(cell), "aws_bedrock_runtime_endpoint": opted.upstream.url}, + ) + assert response.status_code == 401, response.text + assert [reached.target for reached in opted.upstream.drain()] == [f"/model/{REJECTS}/converse"] + assert_kept_out_of_the_deployment_metrics(opted, cell) + + +def test_a_provider_401_at_an_api_base_the_caller_sends_counts_against_a_one_off_deployment(rig: Rig) -> None: + caller_api_base: Final = f"{rig.upstream.url}/caller-endpoint-{RUN}/v1" + rig.upstream.drain() + response: Final = rig.gateway.request( + "POST", + "/v1/chat/completions", + {"model": name("caller-api-base"), "messages": messages("caller-api-base"), "api_base": caller_api_base}, + ) + assert response.status_code == 401, response.text + assert [(reached.target, reached.headers["authorization"]) for reached in rig.upstream.drain()] == [ + (f"/caller-endpoint-{RUN}/v1/chat/completions", "Bearer sk-deployment-credential-caller-api-base") + ] + samples: Final = until(rig.gateway, FAILURES, api_base=caller_api_base) + one_off: Final = {sample.labels["model_id"] for sample in series(samples, FAILURES, api_base=caller_api_base)} + assert len(one_off) == 1 and identity("caller-api-base") not in one_off, recorded(samples, api_base=caller_api_base) + assert values(samples, FAILURES, api_base=caller_api_base, exception_status="401") == [1.0] + assert values(samples, TOTALS, api_base=caller_api_base) == [1.0] + assert values(samples, STATE, api_base=caller_api_base) == [1.0] + assert values(samples, LLM_FAILED, model_id=next(iter(one_off))) == [1.0] + assert {metric: values(samples, metric, model_id=identity("caller-api-base")) for metric in DEPLOYMENT_SERIES} == { + metric: [] for metric in DEPLOYMENT_SERIES + } + + +def _gated_key(scenario: Scenario, cell: str) -> str | None: + if cell == "invalid-key": + return f"sk-not-a-virtual-key-{uuid.uuid4().hex}" + if cell == "missing-key": + return None + if cell == "no-model-access": + return scenario.key(models=[name("healthy")]) + key: Final = scenario.key(models=[name(cell)]) + if cell == "expired-key": + write_rows( + "UPDATE \"LiteLLM_VerificationToken\" SET expires = now() - interval '1 hour' WHERE token = %s", + (sha256(key.encode()).hexdigest(),), + database_url=scenario.gateway.database_url, + ) + return key + scenario.gateway.post("/key/block", {"key": key}) + return key + + +def _gated(gateway: Gateway, cell: str, key: str | None) -> httpx.Response: + if key is None: + return gateway.client.post("/v1/chat/completions", json={"model": name(cell), "messages": messages(cell)}) + return send(gateway, cell, key=key) + + +def recorded_before(cell: str) -> list[tuple[str, str, str, float]]: + if cell in ("invalid-key", "expired-key"): + return [] + return sorted([(FAILURES, "", "", 1.0), (TOTALS, "", "", 1.0), (LLM_FAILED, identity(cell), "", 1.0)]) + + +def deployment_rows(samples: Sequence[Sample], cell: str) -> list[tuple[str, str, str, float]]: + return sorted( + (sample.name, sample.labels.get("model_id", ""), sample.labels.get("api_base", ""), sample.value) + for sample in samples + if sample.name in DEPLOYMENT_SERIES + and name(cell) in (sample.labels.get("requested_model"), sample.labels.get("model")) + ) + + +@pytest.mark.parametrize("cell", GATED_CELLS) +def test_a_request_the_proxy_rejects_itself_never_marks_the_deployment_down_and_is_recorded_as_before( + rig: Rig, cell: str +) -> None: + with rig.gateway.scenario() as scenario: + key: Final = _gated_key(scenario, cell) + rig.upstream.drain() + response: Final = _gated(rig.gateway, cell, key) + assert response.status_code == (403 if cell == "no-model-access" else 401), response.text + assert rig.upstream.drain() == () + samples: Final = until(rig.gateway, PROXY_FAILED, requested_model=name(cell)) + assert deployment_rows(samples, cell) == recorded_before(cell), recorded(samples, requested_model=name(cell)) + + +def test_a_key_rate_limit_rejected_at_the_proxy_is_still_counted_as_before(rig: Rig) -> None: + with rig.gateway.scenario() as scenario: + key: Final = scenario.key(models=[name("key-rpm-limit")], rpm_limit=1) + rig.upstream.drain() + admitted: Final = send(rig.gateway, "key-rpm-limit", key=key) + assert admitted.status_code == 200, admitted.text + limited: Final = send(rig.gateway, "key-rpm-limit", key=key) + assert limited.status_code == 429, limited.text + assert len(rig.upstream.drain()) == 1 + samples: Final = until(rig.gateway, FAILURES, requested_model=name("key-rpm-limit"), exception_status="429") + assert values(samples, FAILURES, requested_model=name("key-rpm-limit"), exception_status="429") == [1.0] + eventually( + lambda: scrape(rig.gateway), + lambda observed: ( + values(observed, STATE, model_id=identity("key-rpm-limit"), api_base=rig.api_base) == [0.0] + ), + ) + + +@pytest.fixture(scope="module") +def mcp_peer() -> Iterator[McpPeer]: + with scripted_peer(echo_tool("add")) as peer: + yield peer + + +def test_an_mcp_tool_call_the_proxy_rejects_for_the_caller_stays_out_of_the_llm_failure_metrics( + rig: Rig, mcp_peer: McpPeer +) -> None: + alias: Final = "a365" + RUN + with rig.gateway.scenario() as scenario: + server: Final = register_mcp(scenario, mcp_peer, alias) + key: Final = scenario.key(object_permission={"mcp_servers": [server]}) + hashed: Final = sha256(key.encode()).hexdigest() + outcomes: Final = { + entry: McpCaller(rig.gateway, key, entry, alias).call(f"{alias}-add", {"entry": entry}, server) + for entry in ENTRY_POINTS + } + assert all( + outcome.error is not None and "Agent 365 guardrail rejected" in outcome.raw for outcome in outcomes.values() + ), {entry: outcome.raw for entry, outcome in outcomes.items()} + eventually( + lambda: rig.gateway.rows(GUARDRAIL_ROWS, (AGENT_365_GUARDRAIL, hashed, "call_mcp_tool")), + lambda rows: [row["status"] for row in rows] == ["guardrail_intervened"] * len(ENTRY_POINTS), + seconds=70, + ) + samples: Final = scrape(rig.gateway) + assert values(samples, LLM_FAILED, hashed_api_key=hashed) == [] + assert values(samples, FAILURES, hashed_api_key=hashed) == [] + assert values(samples, TOTALS, hashed_api_key=hashed) == [] diff --git a/tests/integration/observability/test_prometheus_provider_auth_failure_chaos.py b/tests/integration/observability/test_prometheus_provider_auth_failure_chaos.py new file mode 100644 index 00000000000..a1fe1cf4f33 --- /dev/null +++ b/tests/integration/observability/test_prometheus_provider_auth_failure_chaos.py @@ -0,0 +1,147 @@ +import threading +import uuid +from collections.abc import Iterator, Sequence +from concurrent.futures import ThreadPoolExecutor +from contextlib import contextmanager +from dataclasses import dataclass +from pathlib import Path +from typing import Final + +import httpx +import psutil +from integration._support.client import Gateway, eventually +from integration._support.process import OwnedProxy, owned_proxy_process +from integration._support.prometheus_series import Sample, scrape +from integration._support.wire import Wire, wire_server +from test_prometheus_provider_auth_failure import ( + FAILURES, + LLM_FAILED, + STATE, + TOTALS, + Provider, + config, + identity, + name, + send, + values, +) + +BURST: Final = 12 +ENDPOINTS: Final = ("chat", "chat-stream", "messages", "responses") + + +@dataclass(frozen=True, slots=True) +class ChaosRig: + owned: OwnedProxy + upstream: Wire + release: threading.Event + + +@contextmanager +def _rig(gateway: Gateway, tmp_path: Path) -> Iterator[ChaosRig]: + release: Final = threading.Event() + prom_dir: Final = tmp_path / "prom" + prom_dir.mkdir() + with ( + wire_server(Provider(release)) as upstream, + owned_proxy_process( + gateway, + tmp_path, + {"PROMETHEUS_MULTIPROC_DIR": str(prom_dir)}, + config=config(tmp_path, upstream), + workers=2, + ) as owned, + ): + try: + yield ChaosRig(owned, upstream, release) + finally: + release.set() + + +def _status(gateway: Gateway, cell: str, model: str, key: str | None = None) -> int: + try: + return send(gateway, cell, model=model, key=key).status_code + except httpx.HTTPError: + return -1 + + +def _holds_a_connection_to(pid: int, port: int) -> bool: + return any(connection.raddr[1:] == (port,) for connection in psutil.Process(pid).net_connections(kind="tcp")) + + +def _workers_calling_upstream(rig: ChaosRig) -> frozenset[int]: + port: Final = int(rig.upstream.url.rsplit(":", 1)[1]) + return frozenset(pid for pid in rig.owned.worker_pids() if _holds_a_connection_to(pid, port)) + + +def _counted(samples: Sequence[Sample], cell: str) -> tuple[float, float, float]: + return ( + sum(values(samples, FAILURES, model_id=identity(cell))), + sum(values(samples, TOTALS, model_id=identity(cell))), + sum(values(samples, LLM_FAILED, model_id=identity(cell))), + ) + + +def test_a_burst_of_provider_401s_and_proxy_rejects_across_two_workers_counts_only_the_provider_401s( + gateway: Gateway, tmp_path: Path +) -> None: + with _rig(gateway, tmp_path) as rig: + rig.release.set() + workers: Final = eventually(rig.owned.worker_pids, lambda pids: len(pids) == 2) + invalid: Final = f"sk-not-a-virtual-key-{uuid.uuid4().hex}" + calls: Final[tuple[tuple[str, str, str | None], ...]] = tuple( + (ENDPOINTS[index % len(ENDPOINTS)], name("burst"), None) for index in range(BURST) + ) + tuple(("chat", name("invalid-key"), invalid) for _ in range(BURST)) + + def call(cell: str, model: str, key: str | None) -> int: + return _status(rig.owned.gateway, cell, model, key) + + with ThreadPoolExecutor(max_workers=8) as pool: + statuses: Final = tuple(pool.map(call, *zip(*calls, strict=True))) + assert statuses == (401,) * (2 * BURST), statuses + assert len(rig.upstream.drain()) == BURST + samples: Final = eventually( + lambda: scrape(rig.owned.gateway), + lambda observed: _counted(observed, "burst") == (BURST, BURST, BURST), + seconds=70, + return_last_on_timeout=True, + ) + assert _counted(samples, "burst") == (BURST, BURST, BURST), (workers, _counted(samples, "burst")) + assert set(values(samples, STATE, model_id=identity("burst"))) == {1.0} + assert values(samples, FAILURES, requested_model=name("invalid-key")) == [] + assert values(samples, LLM_FAILED, model=name("invalid-key")) == [] + + +def test_a_worker_killed_while_provider_401s_are_in_flight_loses_no_answered_failure( + gateway: Gateway, tmp_path: Path +) -> None: + with _rig(gateway, tmp_path) as rig: + eventually(rig.owned.worker_pids, lambda pids: len(pids) == 2) + with ThreadPoolExecutor(max_workers=8) as pool: + futures: Final = tuple(pool.submit(_status, rig.owned.gateway, "chat", name("held")) for _ in range(BURST)) + eventually(lambda: rig.upstream.received.qsize() >= 8, bool, seconds=30) + victim: Final = min(eventually(lambda: _workers_calling_upstream(rig), bool)) + with rig.owned.replacing_worker(victim) as kill: + kill() + rig.release.set() + statuses: Final = tuple(future.result() for future in futures) + reached: Final = len(rig.upstream.drain()) + answered: Final = statuses.count(401) + lost: Final = statuses.count(-1) + assert answered + lost == BURST and lost > 0, statuses + samples: Final = eventually( + lambda: scrape(rig.owned.gateway), + lambda observed: answered <= _counted(observed, "held")[0] <= reached, + seconds=70, + return_last_on_timeout=True, + ) + counted: Final = _counted(samples, "held") + assert answered <= counted[0] <= reached and counted[0] == counted[1] == counted[2], (statuses, counted) + follow_up: Final = _status(rig.owned.gateway, "chat", name("held")) + assert follow_up == 401 + recounted: Final = eventually( + lambda: _counted(scrape(rig.owned.gateway), "held"), + lambda observed: observed[0] == counted[0] + 1, + seconds=70, + ) + assert recounted == (counted[0] + 1,) * 3 diff --git a/tests/integration/sdk/test_prometheus_copilot_session_auth_failure_sdk.py b/tests/integration/sdk/test_prometheus_copilot_session_auth_failure_sdk.py new file mode 100644 index 00000000000..1b5e990fe41 --- /dev/null +++ b/tests/integration/sdk/test_prometheus_copilot_session_auth_failure_sdk.py @@ -0,0 +1,99 @@ +"""A provider 401 on a caller's own per-user GitHub Copilot session is the caller's credential failing, so the +shared Copilot deployment keeps no failure, request or outage series for it, while a provider 401 on the router +deployment's own key, on a key a router caller passes or on the key a direct SDK call brings is counted as a failed +request, the router caller's key against its own one-off deployment.""" + +from __future__ import annotations + +import json +from collections.abc import Iterator +from typing import Final + +import pytest +from integration._support.copilot_session_prometheus_worker import ( + CALLER_SESSION_TOKEN, + COPILOT_DEPLOYMENT, + DEPLOYMENT_SERIES, + DIRECT_CALL, + DIRECT_KEY, + FAILURES, + LLM_FAILED, + OPERATOR_DEPLOYMENT, + OPERATOR_KEY, + ROUTER_CALLER_KEY, + STATE, + TOTALS, + Outcome, + WorkerReport, + run_copilot_session_worker, +) +from integration._support.wire import Reply, Request, wire_server + + +@pytest.fixture(scope="module") +def ran() -> Iterator[tuple[WorkerReport, tuple[Request, ...]]]: + def reject(request: Request) -> Reply: + assert request.method == "POST", request.method + return Reply( + status=401, + body=json.dumps({"error": {"message": "bad credentials", "type": "authentication_error"}}).encode(), + ) + + with wire_server(reject) as upstream: + report: Final = run_copilot_session_worker(upstream.url) + yield report, upstream.drain() + + +def test_every_call_reaches_the_provider_with_its_own_credential_and_fails_with_its_401( + ran: tuple[WorkerReport, tuple[Request, ...]], +) -> None: + report, received = ran + assert report.outcomes == ( + Outcome(deployment=COPILOT_DEPLOYMENT, status=401), + Outcome(deployment=OPERATOR_DEPLOYMENT, status=401), + Outcome(deployment=OPERATOR_DEPLOYMENT, status=401), + Outcome(deployment=DIRECT_CALL, status=401), + ) + assert [r.headers.get("authorization") for r in received] == [ + f"Bearer {CALLER_SESSION_TOKEN}", + f"Bearer {OPERATOR_KEY}", + f"Bearer {ROUTER_CALLER_KEY}", + f"Bearer {DIRECT_KEY}", + ] + + +def test_a_provider_401_on_a_callers_copilot_session_stays_out_of_the_shared_deployment_metrics( + ran: tuple[WorkerReport, tuple[Request, ...]], +) -> None: + report, _ = ran + assert {s.metric for s in report.series if s.model_id == COPILOT_DEPLOYMENT} & DEPLOYMENT_SERIES == set() + + +def test_a_provider_401_on_the_deployments_own_key_counts_as_a_deployment_failure( + ran: tuple[WorkerReport, tuple[Request, ...]], +) -> None: + report, _ = ran + assert report.values(FAILURES, OPERATOR_DEPLOYMENT) == (1.0,) + assert report.values(TOTALS, OPERATOR_DEPLOYMENT) == (1.0,) + assert report.values(LLM_FAILED, OPERATOR_DEPLOYMENT) == (1.0,) + assert report.on_the_endpoint(STATE, OPERATOR_DEPLOYMENT) == (1.0,) + + +def test_a_provider_401_on_the_key_a_direct_sdk_call_brings_is_counted_as_a_failed_request( + ran: tuple[WorkerReport, tuple[Request, ...]], +) -> None: + report, _ = ran + assert report.outside_the_router(FAILURES) == (1.0,) + assert report.outside_the_router(TOTALS) == (1.0,) + assert report.outside_the_router(LLM_FAILED) == (1.0,) + + +def test_a_provider_401_on_a_key_a_router_caller_passes_counts_against_its_own_one_off_deployment( + ran: tuple[WorkerReport, tuple[Request, ...]], +) -> None: + report, _ = ran + assert report.on_a_one_off_deployment(FAILURES) == (1.0,) + assert report.on_a_one_off_deployment(TOTALS) == (1.0,) + assert report.on_a_one_off_deployment(LLM_FAILED) == (1.0,) + assert report.values(FAILURES, OPERATOR_DEPLOYMENT) == (1.0,) + assert report.values(TOTALS, OPERATOR_DEPLOYMENT) == (1.0,) diff --git a/tests/unit/integrations/test_prometheus_invalid_key_filtering.py b/tests/unit/integrations/test_prometheus_invalid_key_filtering.py index 9e8348e860a..453fd28636d 100644 --- a/tests/unit/integrations/test_prometheus_invalid_key_filtering.py +++ b/tests/unit/integrations/test_prometheus_invalid_key_filtering.py @@ -2,19 +2,63 @@ Unit tests for Prometheus invalid API key request filtering. Tests the 401 detection helpers, that LLM-level metrics skip invalid API key -requests, and that the proxy-level failed request counter still records them. +requests rejected at the proxy gate while recording a provider's 401, and that +the proxy-level failed request counter still records them. """ +from collections.abc import Iterator +from datetime import datetime +from typing import Final from unittest.mock import Mock, patch import pytest from fastapi import HTTPException from prometheus_client import REGISTRY +from prometheus_client.samples import Sample +import litellm +from litellm.constants import LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL from litellm.integrations.prometheus import PrometheusLogger +from litellm.llms.github_copilot.per_user_auth import GithubCopilotUserSession from litellm.proxy._types import ProxyErrorTypes, ProxyException, UserAPIKeyAuth +PROVIDER_CREDENTIAL_FIELDS = [ + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token", + "aws_web_identity_token", + "azure_ad_token", + "azure_password", + "azure_scope", + "azure_username", + "client_id", + "client_secret", + "gigachat_access_token", + "oci_fingerprint", + "oci_key", + "oci_key_file", + "oci_tenancy", + "oci_user", + "tenant_id", + "token", + "user_config", + "vertex_ai_credentials", + "vertex_credentials", + "watsonx_credentials", + "wx_credentials", + "zen_api_key", +] +PROVIDER_ENDPOINT_FIELDS = [ + "aws_bedrock_runtime_endpoint", + "aws_sts_endpoint", + "deployment_url", + "s3_endpoint_url", + "sagemaker_base_url", +] +CALLED_THE_PROVIDER: Final = {"api_call_start_time": datetime(2026, 10, 19, 12, 0, 0)} + + @pytest.fixture(scope="function") def prometheus_logger(): """Create a PrometheusLogger instance for testing.""" @@ -48,18 +92,13 @@ class TestExtractStatusCode: (ExceptionWithStatusCode, 401, 401), ], ) - def test_extract_from_exception( - self, prometheus_logger, exception_class, code_value, expected - ): + def test_extract_from_exception(self, prometheus_logger, exception_class, code_value, expected): exception = exception_class(code_value) assert prometheus_logger._extract_status_code(exception=exception) == expected def test_extract_from_kwargs(self, prometheus_logger): exception = ExceptionWithCode("401") - assert ( - prometheus_logger._extract_status_code(kwargs={"exception": exception}) - == 401 - ) + assert prometheus_logger._extract_status_code(kwargs={"exception": exception}) == 401 def test_extract_from_enum_values(self, prometheus_logger): enum_values = Mock(status_code="401") @@ -79,50 +118,271 @@ class TestInvalidAPIKeyDetection: ], ) def test_status_code_detection(self, prometheus_logger, status_code, expected): - assert ( - prometheus_logger._is_invalid_api_key_request(status_code=status_code) - == expected - ) + assert prometheus_logger._is_invalid_api_key_request(status_code=status_code) == expected def test_auth_error_message_detection(self, prometheus_logger): exception = AssertionError( "LiteLLM Virtual Key expected. Received=invalid-key-12345, expected to start with 'sk-'." ) - assert ( - prometheus_logger._is_invalid_api_key_request( - status_code=None, exception=exception - ) - is True - ) + assert prometheus_logger._is_invalid_api_key_request(status_code=None, exception=exception) is True def test_non_auth_exception_not_detected(self, prometheus_logger): exception = ValueError("Some other error") - assert ( - prometheus_logger._is_invalid_api_key_request( - status_code=None, exception=exception - ) - is False - ) + assert prometheus_logger._is_invalid_api_key_request(status_code=None, exception=exception) is False class TestSkipMetricsValidation: """Test high-level validation method that orchestrates detection and extraction.""" - def test_skip_for_401_exception(self, prometheus_logger): - """Test full flow: extraction -> detection -> skip decision.""" - exception = ExceptionWithCode("401") - assert ( - prometheus_logger._should_skip_metrics_for_invalid_key(exception=exception) - is True - ) + @pytest.mark.parametrize( + "exception,proxy_gate,expected", + [ + (ExceptionWithCode("401"), True, True), + (AssertionError("expected to start with 'sk-'"), True, True), + (ExceptionWithStatusCode(429), True, False), + (ExceptionWithCode("401"), False, True), + (AssertionError("expected to start with 'sk-'"), False, True), + ( + litellm.AuthenticationError( + message="Incorrect API key provided: sk-rotat*****", + llm_provider="openai", + model="gpt-4o-mini", + ), + False, + False, + ), + ( + litellm.AuthenticationError( + message="API key not valid. Please pass a valid API key.", + llm_provider="gemini", + model="gemini-2.5-flash", + ), + False, + False, + ), + ( + litellm.AuthenticationError( + message="Authentication Error, Invalid proxy server token passed.", + llm_provider="openai", + model="gpt-4o-mini", + ), + True, + True, + ), + ( + litellm.CallerCredentialAuthenticationError( + message="GitHub Copilot connection is missing for this user", + llm_provider="github_copilot", + model="gpt-4o", + ), + False, + True, + ), + ( + HTTPException(status_code=401, detail="caller did not present a token"), + False, + True, + ), + ( + litellm.BadRequestError( + message="API key not valid. Please pass a valid API key.", + llm_provider="gemini", + model="gemini-2.5-flash", + ), + False, + False, + ), + ], + ids=[ + "proxy-gate-401", + "proxy-gate-auth-message", + "proxy-gate-429", + "unmarked-proxy-401", + "unmarked-auth-message", + "provider-401", + "provider-auth-message", + "proxy-gate-litellm-auth-error", + "caller-credential-401", + "unmarked-http-exception-401", + "provider-400-auth-message", + ], + ) + def test_skip_except_for_provider_errors_the_proxy_did_not_raise( + self, prometheus_logger, exception, proxy_gate, expected + ): + kwargs: Final[dict[str, object]] = {"exception": exception, **CALLED_THE_PROVIDER} + if proxy_gate: + kwargs[LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL] = True + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is expected - def test_skip_for_auth_error_message(self, prometheus_logger): - """Test full flow: exception message -> detection -> skip decision.""" - exception = AssertionError("expected to start with 'sk-'") - assert ( - prometheus_logger._should_skip_metrics_for_invalid_key(exception=exception) - is True + @pytest.mark.parametrize( + "logged_call,expected", + [ + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": ("api_key",)}}}, True), + ( + { + "litellm_params": { + "github_copilot_user_session": GithubCopilotUserSession( + token="caller-session-token", api_base="https://api.githubcopilot.com" + ) + } + }, + True, + ), + ({"standard_logging_object": {"metadata": {"used_client_oauth_token": True}}}, True), + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": ("headers",)}}}, True), + ( + { + "litellm_params": { + "proxy_server_request": {"caller_credential_fields": ("provider_specific_header",)} + } + }, + True, + ), + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": ()}}}, False), + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": "api_key"}}}, False), + ({"standard_logging_object": {"metadata": {"used_client_oauth_token": False}}}, False), + ({"litellm_params": {"proxy_server_request": {"body": {"azure_ad_token": "caller-token"}}}}, True), + ( + { + "litellm_params": { + "proxy_server_request": {"body": {"extra_body": {"azure_ad_token": "caller-token"}}} + } + }, + True, + ), + ( + { + "litellm_params": { + "azure_ad_token": "deployment-token", + "proxy_server_request": {"body": {"model": "azure-deployment"}}, + } + }, + False, + ), + ({"litellm_params": {"proxy_server_request": {"body": {"extra_body": "azure_ad_token"}}}}, False), + ( + { + "litellm_params": { + "proxy_server_request": {"body": {"litellm_credential_name": "caller-credential"}} + } + }, + True, + ), + ( + { + "litellm_params": { + "litellm_credential_name": "deployment-credential", + "proxy_server_request": {"body": {"model": "openai-deployment"}}, + } + }, + False, + ), + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": ("extra_headers",)}}}, True), + ({"litellm_params": {"proxy_server_request": {"caller_credential_fields": ("organization",)}}}, True), + ( + { + "litellm_params": { + "proxy_server_request": {"credential_fields": ("headers",), "caller_credential_fields": ()} + } + }, + False, + ), + ( + { + "litellm_params": { + "proxy_server_request": {"body": {"extra_headers": {"Authorization": "Bearer sk-caller"}}} + } + }, + False, + ), + ], + ids=[ + "caller-supplied-api-key", + "caller-copilot-session", + "caller-anthropic-oauth-token", + "caller-supplied-headers", + "caller-supplied-provider-specific-header", + "no-caller-credential-fields", + "malformed-credential-fields", + "deployment-anthropic-key", + "caller-supplied-azure-ad-token", + "caller-supplied-azure-ad-token-in-extra-body", + "deployment-azure-ad-token", + "malformed-extra-body", + "caller-named-credential", + "deployment-named-credential", + "caller-supplied-extra-headers", + "caller-supplied-organization", + "headers-that-cannot-change-provider-auth", + "extra-headers-not-stamped-by-the-proxy", + ], + ) + def test_skip_a_provider_401_on_a_credential_the_caller_owns(self, prometheus_logger, logged_call, expected): + exception: Final = litellm.AuthenticationError( + message="Incorrect API key provided: sk-calle*****", + llm_provider="openai", + model="gpt-4o-mini", ) + kwargs: Final = {"exception": exception, **CALLED_THE_PROVIDER, **logged_call} + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is expected + + @pytest.mark.parametrize( + "logged_call,expected", + [(CALLED_THE_PROVIDER, False), ({}, True), ({"api_call_start_time": None}, True)], + ids=["provider-401", "401-before-the-provider-call", "blank-provider-call-start"], + ) + def test_skip_a_401_raised_before_the_provider_was_called(self, prometheus_logger, logged_call, expected): + exception: Final = litellm.AuthenticationError( + message="Missing Anthropic API Key - A call is being made to anthropic but no key is set", + llm_provider="anthropic", + model="claude-haiku-4-5", + ) + kwargs: Final = {"exception": exception, **logged_call} + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is expected + + @pytest.mark.parametrize("field", PROVIDER_CREDENTIAL_FIELDS + PROVIDER_ENDPOINT_FIELDS) + @pytest.mark.parametrize("inside_extra_body", [False, True], ids=["body", "extra-body"]) + def test_skip_a_provider_401_on_a_provider_credential_field_the_caller_sent( + self, prometheus_logger, field, inside_extra_body + ): + sent: Final = {field: "caller-credential"} + body: Final = {"model": "shared-deployment", **({"extra_body": sent} if inside_extra_body else sent)} + kwargs: Final = { + "exception": litellm.AuthenticationError(message="Unauthorized", llm_provider="azure", model="gpt-4o"), + "litellm_params": {"proxy_server_request": {"body": body}}, + **CALLED_THE_PROVIDER, + } + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is True + + @pytest.mark.parametrize("field", PROVIDER_CREDENTIAL_FIELDS + PROVIDER_ENDPOINT_FIELDS) + def test_count_a_provider_401_on_a_provider_credential_field_the_deployment_owns(self, prometheus_logger, field): + kwargs: Final = { + "exception": litellm.AuthenticationError(message="Unauthorized", llm_provider="azure", model="gpt-4o"), + **CALLED_THE_PROVIDER, + "litellm_params": { + field: "deployment-credential", + "proxy_server_request": {"body": {"model": "shared-deployment"}}, + }, + } + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is False + + @pytest.mark.parametrize( + "token_exchange_endpoint,expected", + [("https://login.example.com/oauth2/v2.0/token", True), ("", False), (None, False)], + ids=["token-exchange-deployment", "blank-endpoint", "no-endpoint"], + ) + def test_skip_a_provider_401_on_a_token_exchange_deployment( + self, prometheus_logger, token_exchange_endpoint, expected + ): + kwargs: Final = { + "exception": litellm.AuthenticationError( + message="Unauthorized", llm_provider="microsoft_365_copilot", model="copilot" + ), + "litellm_params": {"token_exchange_endpoint": token_exchange_endpoint, "client_secret": "deployment"}, + **CALLED_THE_PROVIDER, + } + assert prometheus_logger._should_skip_metrics_for_invalid_key(kwargs=kwargs) is expected def test_no_skip_for_valid_request(self, prometheus_logger): assert prometheus_logger._should_skip_metrics_for_invalid_key() is False @@ -147,19 +407,13 @@ class TestAsyncHooks: ), ], ) - async def test_post_call_failure_hook_counts_401_without_key_hash( - self, prometheus_logger, exception - ): + async def test_post_call_failure_hook_counts_401_without_key_hash(self, prometheus_logger, exception): unauthenticated = UserAPIKeyAuth(request_route="/v1/chat/completions") unauthenticated.api_key = "notakeyatall" with ( - patch.object( - prometheus_logger, "litellm_proxy_failed_requests_metric" - ) as mock_failed, - patch.object( - prometheus_logger, "litellm_proxy_total_requests_metric" - ) as mock_total, + patch.object(prometheus_logger, "litellm_proxy_failed_requests_metric") as mock_failed, + patch.object(prometheus_logger, "litellm_proxy_total_requests_metric") as mock_total, ): await prometheus_logger.async_post_call_failure_hook( request_data={"model": "test-model"}, @@ -176,9 +430,7 @@ class TestAsyncHooks: mock_total.labels.return_value.inc.assert_called_once() @pytest.mark.asyncio - async def test_post_call_failure_hook_keeps_resolved_identity_labels_for_401( - self, prometheus_logger - ): + async def test_post_call_failure_hook_keeps_resolved_identity_labels_for_401(self, prometheus_logger): expired_key = UserAPIKeyAuth( api_key="sk-expired", key_alias="expired-alias", @@ -191,9 +443,7 @@ class TestAsyncHooks: code=401, ) - with patch.object( - prometheus_logger, "litellm_proxy_failed_requests_metric" - ) as mock_failed: + with patch.object(prometheus_logger, "litellm_proxy_failed_requests_metric") as mock_failed: await prometheus_logger.async_post_call_failure_hook( request_data={"model": "test-model"}, original_exception=exception, @@ -207,7 +457,7 @@ class TestAsyncHooks: assert failed_labels["team"] == "team-1" @pytest.mark.asyncio - async def test_log_failure_event_skips_401(self, prometheus_logger): + async def test_log_failure_event_skips_401_rejected_at_the_proxy_gate(self, prometheus_logger): exception = ExceptionWithCode("401") kwargs = { "model": "test-model", @@ -220,20 +470,91 @@ class TestAsyncHooks: }, "exception": exception, "litellm_params": {}, + LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL: True, } with ( - patch.object( - prometheus_logger, "litellm_llm_api_failed_requests_metric" - ) as mock_failed, - patch.object( - prometheus_logger, "set_llm_deployment_failure_metrics" - ) as mock_deployment, + patch.object(prometheus_logger, "litellm_llm_api_failed_requests_metric") as mock_failed, + patch.object(prometheus_logger, "set_llm_deployment_failure_metrics") as mock_deployment, ): - await prometheus_logger.async_log_failure_event( kwargs=kwargs, response_obj=None, start_time=None, end_time=None ) mock_failed.labels.assert_not_called() mock_deployment.assert_not_called() + + +def _provider_auth_failure_kwargs() -> dict[str, object]: + exception: Final = litellm.AuthenticationError( + message="Incorrect API key provided: sk-rotat*****", + llm_provider="openai", + model="gpt-4o-mini", + ) + return { + "model": "gpt-4o-mini", + "standard_logging_object": { + "metadata": { + "user_api_key_hash": "test-key", + "user_api_key_alias": "test-alias", + "user_api_key_user_id": "test-user", + "user_api_key_team_id": "test-team", + "user_api_key_team_alias": "test-team-alias", + }, + "model_group": "rotated-credential", + "model_id": "deployment-1", + "api_base": "https://api.openai.com/v1", + "custom_llm_provider": "openai", + }, + "exception": exception, + "litellm_params": {"custom_llm_provider": "openai", "metadata": {}}, + **CALLED_THE_PROVIDER, + } + + +def _metric_values(name: str, model_id: str) -> list[float]: + return [ + sample.value + for sample in _registry_samples() + if sample.name == name and sample.labels.get("model_id") == model_id + ] + + +def _registry_samples() -> Iterator[Sample]: + for metric in REGISTRY.collect(): + yield from metric.samples + + +class TestProviderAuthFailures: + """A 401 raised by the provider is an upstream failure and is recorded.""" + + @pytest.mark.asyncio + async def test_log_failure_event_records_a_provider_401(self, prometheus_logger): + await prometheus_logger.async_log_failure_event( + kwargs=_provider_auth_failure_kwargs(), + response_obj=None, + start_time=None, + end_time=None, + ) + + assert _metric_values("litellm_llm_api_failed_requests_metric_total", "deployment-1") == [1.0] + assert _metric_values("litellm_deployment_failure_responses_total", "deployment-1") == [1.0] + assert _metric_values("litellm_deployment_state", "deployment-1") == [1.0] + + def test_deployment_failure_metrics_record_a_provider_401(self, prometheus_logger): + prometheus_logger.set_llm_deployment_failure_metrics(_provider_auth_failure_kwargs()) + + failures: Final = [ + sample for sample in _registry_samples() if sample.name == "litellm_deployment_failure_responses_total" + ] + assert [ + ( + sample.labels["model_id"], + sample.labels["exception_status"], + sample.labels["api_provider"], + sample.value, + ) + for sample in failures + ] == [("deployment-1", "401", "openai", 1.0)] + assert _metric_values("litellm_deployment_total_requests_total", "deployment-1") == [1.0] + assert _metric_values("litellm_deployment_state", "deployment-1") == [1.0] diff --git a/tests/unit/proxy/test_litellm_pre_call_utils.py b/tests/unit/proxy/test_litellm_pre_call_utils.py index ed7817bd660..e03fd9850c1 100644 --- a/tests/unit/proxy/test_litellm_pre_call_utils.py +++ b/tests/unit/proxy/test_litellm_pre_call_utils.py @@ -927,6 +927,222 @@ async def test_add_litellm_data_to_request_body_snapshot_excludes_proxy_server_r assert snapshot_body["messages"] == [{"role": "user", "content": "hello"}] +FORWARD_PROVIDER_AUTH_HEADERS: Final = { + "forward_llm_provider_auth_headers": True, + "forward_client_headers_to_llm_api": True, +} + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "sent,request_headers,general_settings,expected", + [ + ({"headers": {"Authorization": "Bearer sk-caller"}}, {}, {}, ("headers",)), + ({"headers": {"X-Trace-Id": "trace-1"}}, {}, {}, ("headers",)), + ({"headers": {}}, {}, {}, ()), + ({"extra_headers": {"API-Key": "caller-azure-key"}}, {}, {}, ("extra_headers",)), + ({"extra_headers": {"OpenAI-Project": "proj_caller"}}, {}, {}, ("extra_headers",)), + ( + {"provider_specific_header": {"custom_llm_provider": "openai", "extra_headers": {"x-api-key": "caller"}}}, + {}, + {}, + ("provider_specific_header",), + ), + ( + { + "provider_specific_header": [ + {"custom_llm_provider": "anthropic", "extra_headers": {"anthropic-beta": "tools"}}, + {"custom_llm_provider": "gemini", "extra_headers": {"X-Goog-Api-Key": "caller"}}, + ] + }, + {}, + {}, + ("provider_specific_header",), + ), + ( + { + "provider_specific_header": { + "custom_llm_provider": "anthropic", + "extra_headers": {"x-caller-trace": "x"}, + } + }, + {}, + {}, + ("provider_specific_header",), + ), + ( + { + "provider_specific_header": { + "custom_llm_provider": "anthropic", + "extra_headers": {"anthropic-version": "2023-06-01", "anthropic-beta": "tools-2024-04-04"}, + } + }, + {}, + {}, + (), + ), + ({"headers": {"Anthropic-Version": "2023-06-01"}}, {}, {}, ()), + ({"organization": "org-caller"}, {}, {}, ("organization",)), + ({"headers": "authorization", "provider_specific_header": "x-api-key"}, {}, {}, ()), + ( + {"api_key": "sk-caller", "headers": {"Ocp-Apim-Subscription-Key": "caller"}}, + {}, + {}, + ("api_key", "headers"), + ), + ({}, {"anthropic-beta": "tools-2024-04-04"}, {}, ()), + ({}, {"anthropic-version": "2023-06-01", "anthropic-beta": "tools-2024-04-04"}, {}, ()), + ({}, {"openai-organization": "org-caller"}, {"forward_openai_org_id": True}, ("organization",)), + ( + {}, + {"x-litellm-api-key": "sk-virtual", "authorization": "Bearer sk-ant-oat01-caller"}, + {}, + ("provider_specific_header",), + ), + ( + {}, + {"authorization": "Bearer sk-virtual", "x-goog-api-key": "AIza-caller"}, + FORWARD_PROVIDER_AUTH_HEADERS, + ("headers",), + ), + ( + {}, + {"authorization": "Bearer sk-virtual", "x-portkey-api-key": "caller-gateway-key"}, + FORWARD_PROVIDER_AUTH_HEADERS, + ("headers",), + ), + ({}, {"authorization": "Bearer sk-virtual"}, FORWARD_PROVIDER_AUTH_HEADERS, ()), + ], + ids=[ + "authorization-in-headers", + "plain-headers", + "empty-headers", + "api-key-in-extra-headers", + "openai-project-in-extra-headers", + "credential-in-provider-specific-header", + "credential-in-one-provider-specific-header-entry", + "plain-provider-specific-header", + "anthropic-version-and-beta-in-provider-specific-header", + "anthropic-version-in-headers", + "organization", + "malformed-header-fields", + "api-key-and-credential-headers", + "proxy-forwarded-anthropic-beta", + "proxy-forwarded-anthropic-sdk-headers", + "proxy-forwarded-openai-organization", + "proxy-forwarded-anthropic-oauth-token", + "proxy-forwarded-provider-auth-header", + "proxy-forwarded-gateway-key", + "proxy-forwarding-nothing", + ], +) +async def test_caller_credential_fields_name_every_field_that_carries_a_caller_header_or_credential( + sent: dict[str, object], + request_headers: dict[str, str], + general_settings: dict[str, object], + expected: tuple[str, ...], +) -> None: + updated: Final = await _request_data_added_by_the_proxy(sent, request_headers, general_settings) + + assert updated["proxy_server_request"]["caller_credential_fields"] == expected + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "request_headers,expected", + [({}, ()), ({"x-caller-trace": "trace-1"}, ("headers",))], + ids=["user-information-only", "user-information-and-caller-header"], +) +async def test_caller_credential_fields_ignore_the_user_information_the_proxy_adds_to_forwarded_headers( + monkeypatch: pytest.MonkeyPatch, request_headers: dict[str, str], expected: tuple[str, ...] +) -> None: + monkeypatch.setattr(litellm, "add_user_information_to_llm_headers", True) + + updated: Final = await _request_data_added_by_the_proxy({}, request_headers, FORWARD_PROVIDER_AUTH_HEADERS) + + assert updated["headers"]["x-litellm-user_api_key_user_id"] == "test-user" + assert updated["proxy_server_request"]["caller_credential_fields"] == expected + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "sent,request_headers,general_settings,expected", + [ + ({"headers": {"Anthropic-Version": "2023-06-01"}}, {}, {}, ("headers",)), + ( + { + "provider_specific_header": { + "custom_llm_provider": "anthropic", + "extra_headers": {"anthropic-version": "2023-06-01"}, + } + }, + {}, + {}, + ("provider_specific_header",), + ), + ({"organization": "org-caller"}, {}, {}, ()), + ({"extra_headers": {"API-Key": "caller-azure-key"}}, {}, {}, ()), + ( + {"api_key": "sk-caller", "headers": {"Ocp-Apim-Subscription-Key": "caller"}}, + {}, + {}, + ("api_key", "headers"), + ), + ({}, {"openai-organization": "org-caller"}, {"forward_openai_org_id": True}, ()), + ( + {}, + {"authorization": "Bearer sk-virtual", "x-goog-api-key": "AIza-caller"}, + FORWARD_PROVIDER_AUTH_HEADERS, + (), + ), + ], + ids=[ + "anthropic-version-in-headers", + "anthropic-version-in-provider-specific-header", + "organization", + "api-key-in-extra-headers", + "api-key-and-credential-headers", + "proxy-forwarded-openai-organization", + "proxy-forwarded-provider-auth-header", + ], +) +async def test_credential_fields_name_the_stripped_fields_the_caller_sent( + sent: dict[str, object], + request_headers: dict[str, str], + general_settings: dict[str, object], + expected: tuple[str, ...], +) -> None: + updated: Final = await _request_data_added_by_the_proxy(sent, request_headers, general_settings) + + assert updated["proxy_server_request"]["credential_fields"] == expected + + +async def _request_data_added_by_the_proxy( + sent: dict[str, object], request_headers: dict[str, str], general_settings: dict[str, object] +) -> dict: + from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request + + request_mock: Final = MagicMock(spec=Request) + request_mock.url = MagicMock() + request_mock.url.__str__.return_value = "http://localhost/v1/chat/completions" + request_mock.url.path = "/v1/chat/completions" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = {"content-type": "application/json", **request_headers} + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + data: Final = {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hello"}], **sent} + + return await add_litellm_data_to_request( + data=data, + request=request_mock, + user_api_key_dict=UserAPIKeyAuth(api_key="hashed-key", user_id="test-user"), + proxy_config=MagicMock(), + general_settings=dict(general_settings), + version="test-version", + ) + + def test_initial_snapshot_refresh_clears_a_previous_guardrail_checkpoint() -> None: from litellm.integrations.shadow_eval_logger import GuardrailRequestSnapshot from litellm.litellm_core_utils.litellm_logging import Logging From 11779b260e4f6ef6fdd7e7add735baef4fd10e3b Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 20:58:27 -0400 Subject: [PATCH 61/73] fix(ui): include endpoint-qualified models in Decisions Playground (#48504) --- .../playground/llm_calls/system_one.test.ts | 21 ++++++++++++++++++- .../playground/llm_calls/system_one.ts | 12 ++++++++--- .../src/lib/decisionModels.ts | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.test.ts index 90e9482c89c..6fb1f8c8b8d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.test.ts @@ -207,18 +207,37 @@ describe("fetchDecisionModels", () => { vi.unstubAllGlobals(); }); - it("lists the key's decision models from the proxy that answers the request, sorted by name", async () => { + it("lists the key's models with decision modes or endpoints from the request proxy, sorted by name", async () => { mockFetch.mockResolvedValueOnce( modelGroupInfo([ { model_group: "pplx-decider", mode: "evaluation" }, { model_group: "gpt-5.5", mode: "chat" }, { model_group: "jev-latest", mode: "evaluation" }, { model_group: "unknown-mode", mode: null }, + { model_group: "native-decision", mode: "decisions" }, + { + model_group: "chat-decisions", + mode: "chat", + supported_endpoints: ["/v1/chat/completions", "/v1/decisions"], + }, + { model_group: "chat-systemone", mode: "chat", supported_endpoints: ["/v1/systemone"] }, + { model_group: "custom-decision", mode: "custom", supported_endpoints: ["/v1/decisions"] }, + { model_group: "endpoint-only", supported_endpoints: ["/v1/decisions"] }, + { model_group: "chat-only", mode: "chat", supported_endpoints: ["/v1/chat/completions"] }, + { model_group: "no-endpoints", mode: "chat", supported_endpoints: [] }, + { model_group: "null-endpoints", mode: "chat", supported_endpoints: null }, + { model_group: "missing-mode" }, + { model_group: "custom-mode", mode: "custom" }, ]), ); await expect(fetchDecisionModels("virtual-key", "https://custom.example.com/")).resolves.toEqual([ + "chat-decisions", + "chat-systemone", + "custom-decision", + "endpoint-only", "jev-latest", + "native-decision", "pplx-decider", ]); expect(mockFetch).toHaveBeenCalledWith( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.ts b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.ts index a4030029903..035a05729ba 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/system_one.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { getGatewayBaseUrl, getGlobalLitellmHeaderName, getProxyBaseUrl } from "@/components/networking"; import { withRequiredHeaders } from "@/components/llm_calls/request_headers"; import { createApiClient } from "@/lib/http/client"; -import { isDecisionMode } from "@/lib/decisionModels"; +import { isDecisionEntry } from "@/lib/decisionModels"; import { systemOneResponseSchema, type PlaygroundRequest, @@ -26,7 +26,13 @@ export type PlaygroundDecisionRequest = | { endpoint: "/v1/decisions"; payload: OpenAIDecisionsRequest }; const modelGroupInfoSchema = z.object({ - data: z.array(z.object({ model_group: z.string(), mode: z.string().nullish() })), + data: z.array( + z.object({ + model_group: z.string(), + mode: z.string().nullish(), + supported_endpoints: z.array(z.string()).nullish(), + }), + ), }); function proxyClient(customBaseUrl?: string, getDefaultBaseUrl: () => string = getProxyBaseUrl) { @@ -56,7 +62,7 @@ export async function fetchDecisionModels( }); return modelGroupInfoSchema .parse(body) - .data.filter((group) => isDecisionMode(group.mode)) + .data.filter(isDecisionEntry) .map((group) => group.model_group) .sort((a, b) => a.localeCompare(b)); } diff --git a/ui/litellm-dashboard/src/lib/decisionModels.ts b/ui/litellm-dashboard/src/lib/decisionModels.ts index 1808b13c62b..15c632fe055 100644 --- a/ui/litellm-dashboard/src/lib/decisionModels.ts +++ b/ui/litellm-dashboard/src/lib/decisionModels.ts @@ -38,7 +38,7 @@ export const displayMode = (mode: string | null | undefined): string | undefined export const expandModeFilterValues = (modes: readonly string[]): readonly string[] => modes.flatMap((mode) => (mode === DECISIONS_MODE ? DECISION_MODE_VALUES : [mode])); -const isDecisionEntry = (entry: CostMapEntry): boolean => +export const isDecisionEntry = (entry: Pick): boolean => isDecisionMode(entry.mode) || (entry.supported_endpoints ?? []).some((endpoint) => DECISION_ENDPOINTS.has(endpoint)); const parsedEntries = (costMap: unknown): ReadonlyArray => { From 45183428a54043186a401e419b8c0b09df682169 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 26 Oct 2026 21:00:39 -0400 Subject: [PATCH 62/73] feat(ui): persist model group alias display ordering (#48500) --- ui/litellm-dashboard/eslint-suppressions.json | 3 - .../ModelGroupAliasPanel.integration.test.tsx | 1 + .../panels/ModelGroupAliasPanel.tsx | 5 +- .../model_group_alias_ordering.test.ts | 76 ++++++ .../components/model_group_alias_ordering.ts | 57 +++++ ..._group_alias_settings.integration.test.tsx | 218 ++++++++++++++++++ .../model_group_alias_settings.test.tsx | 66 ------ .../components/model_group_alias_settings.tsx | 186 ++++++++++++++- 8 files changed, 533 insertions(+), 79 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/model_group_alias_ordering.test.ts create mode 100644 ui/litellm-dashboard/src/components/model_group_alias_ordering.ts create mode 100644 ui/litellm-dashboard/src/components/model_group_alias_settings.integration.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/model_group_alias_settings.test.tsx diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 32777b43504..724c49eb3b3 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1730,9 +1730,6 @@ "src/components/model_group_alias_settings.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 } }, "src/components/model_info_view.tsx": { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.integration.test.tsx index 227a34335a3..36a9f92dfd4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.integration.test.tsx @@ -10,6 +10,7 @@ vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ vi.mock("@/components/networking", () => ({ getCallbacksCall: vi.fn(), + getProxyBaseUrl: () => "https://proxy.test", getRouterSettingsCall: vi.fn(), setCallbacksCall: vi.fn(), })); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx index 025923e08e9..e6c720f38cf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import ModelGroupAliasSettings from "@/components/model_group_alias_settings"; -import { getCallbacksCall, getRouterSettingsCall } from "@/components/networking"; +import { getCallbacksCall, getProxyBaseUrl, getRouterSettingsCall } from "@/components/networking"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function ModelGroupAliasPanel() { @@ -22,6 +22,7 @@ export default function ModelGroupAliasPanel() { : null; const managedByConfig = currentAliasOwnership?.managedByConfig ?? false; const ownershipLoading = Boolean(accessToken && !currentAliasOwnership); + const preferenceScope = userID ? JSON.stringify([getProxyBaseUrl(), userID]) : null; useEffect(() => { if (!accessToken) { @@ -62,6 +63,8 @@ export default function ModelGroupAliasPanel() { return ( ({ + id: aliasName, + aliasName, + targetModelGroup, +}); +const names = (aliases: readonly AliasItem[]): string[] => aliases.map((alias) => alias.aliasName); + +describe("orderAliases", () => { + const aliases = Object.freeze([row("Zebra", "alpha10"), row("alpha10", "beta"), row("alpha2", "Alpha2")]); + + it.each<{ sortBy: AliasDisplayPreferences["sortBy"]; descending: boolean; expected: string[] }>([ + { sortBy: "aliasName", descending: false, expected: ["alpha2", "alpha10", "Zebra"] }, + { sortBy: "aliasName", descending: true, expected: ["Zebra", "alpha10", "alpha2"] }, + { sortBy: "targetModelGroup", descending: false, expected: ["alpha2", "Zebra", "alpha10"] }, + { sortBy: "targetModelGroup", descending: true, expected: ["alpha10", "Zebra", "alpha2"] }, + ])("sorts $sortBy with descending=$descending without changing the input", ({ sortBy, descending, expected }) => { + expect(names(orderAliases(aliases, { ...DEFAULT_ALIAS_DISPLAY, sortBy, descending }))).toEqual(expected); + expect(names(aliases)).toEqual(["Zebra", "alpha10", "alpha2"]); + }); + + it.each([false, true])( + "breaks equal-target ties by alias name regardless of input order, descending=%s", + (descending) => { + const tied = [row("beta", "shared"), row("Alpha", "SHARED"), row("gamma", "shared")]; + const preferences: AliasDisplayPreferences = { ...DEFAULT_ALIAS_DISPLAY, sortBy: "targetModelGroup", descending }; + expect(names(orderAliases(tied, preferences))).toEqual(["Alpha", "beta", "gamma"]); + expect(names(orderAliases([...tied].reverse(), preferences))).toEqual(["Alpha", "beta", "gamma"]); + }, + ); + + it.each([ + { descending: false, remaining: ["alpha", "beta", "subscription/a", "subscription/c"] }, + { descending: true, remaining: ["beta", "alpha", "subscription/c", "subscription/a"] }, + ])("keeps ordered pins first and alias-prefix matches last, descending=$descending", ({ descending, remaining }) => { + const grouped = [ + row("subscription/c", "c"), + row("beta", "b"), + row("ordinary", "y"), + row("subscription/a", "a"), + row("alpha", "subscription/x"), + row("subscription/z", "z"), + ]; + const preferences = { + ...DEFAULT_ALIAS_DISPLAY, + descending, + pinned: ["missing", "subscription/z", "ordinary"], + lastPrefix: "subscription/", + }; + expect(names(orderAliases(grouped, preferences))).toEqual(["subscription/z", "ordinary", ...remaining]); + }); +}); + +describe("movePinnedAlias", () => { + const pinned = Object.freeze(["first", "middle", "last"]); + + it.each<{ name: string; direction: -1 | 1; expected: string[] }>([ + { name: "middle", direction: -1, expected: ["middle", "first", "last"] }, + { name: "middle", direction: 1, expected: ["first", "last", "middle"] }, + { name: "first", direction: -1, expected: ["first", "middle", "last"] }, + { name: "last", direction: 1, expected: ["first", "middle", "last"] }, + { name: "missing", direction: -1, expected: ["first", "middle", "last"] }, + { name: "missing", direction: 1, expected: ["first", "middle", "last"] }, + ])("moves $name in direction $direction without changing the input", ({ name, direction, expected }) => { + expect(movePinnedAlias(pinned, name, direction)).toEqual(expected); + expect(pinned).toEqual(["first", "middle", "last"]); + }); +}); diff --git a/ui/litellm-dashboard/src/components/model_group_alias_ordering.ts b/ui/litellm-dashboard/src/components/model_group_alias_ordering.ts new file mode 100644 index 00000000000..cbec78b63cb --- /dev/null +++ b/ui/litellm-dashboard/src/components/model_group_alias_ordering.ts @@ -0,0 +1,57 @@ +import { z } from "zod"; +import { storageKey } from "@/lib/storage"; + +export interface AliasItem { + id: string; + aliasName: string; + targetModelGroup: string; +} + +const aliasDisplayFields = { + sortBy: z.enum(["aliasName", "targetModelGroup"]), + descending: z.boolean(), + pinned: z.array(z.string()).transform((names) => [...new Set(names)]), + lastPrefix: z.string(), +}; +const aliasDisplaySchema = z.object(aliasDisplayFields); + +export type AliasDisplayPreferences = z.infer; + +export const DEFAULT_ALIAS_DISPLAY: AliasDisplayPreferences = { + sortBy: "aliasName", + descending: false, + pinned: [], + lastPrefix: "", +}; + +export const aliasDisplayStorage = (scope: string) => + storageKey("local", `litellm_alias_display_v1_${scope}`, aliasDisplaySchema, DEFAULT_ALIAS_DISPLAY); + +const alphabetical = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); + +export function orderAliases(aliases: readonly AliasItem[], preferences: AliasDisplayPreferences): AliasItem[] { + const ranks = new Map(preferences.pinned.map((name, index) => [name, index])); + const group = (name: string): number => { + if (ranks.has(name)) return 0; + return preferences.lastPrefix && name.startsWith(preferences.lastPrefix) ? 2 : 1; + }; + return [...aliases].sort((a, b) => { + const groupDifference = group(a.aliasName) - group(b.aliasName); + if (groupDifference) return groupDifference; + if (ranks.has(a.aliasName) && ranks.has(b.aliasName)) { + return ranks.get(a.aliasName)! - ranks.get(b.aliasName)!; + } + const fieldDifference = alphabetical.compare(a[preferences.sortBy], b[preferences.sortBy]); + return fieldDifference * (preferences.descending ? -1 : 1) || a.aliasName.localeCompare(b.aliasName); + }); +} + +export function movePinnedAlias(pinned: readonly string[], name: string, direction: -1 | 1): string[] { + const index = pinned.indexOf(name); + const target = index + direction; + if (index < 0 || target < 0 || target >= pinned.length) return [...pinned]; + return pinned.map((value, position) => { + if (position === index) return pinned[target]; + return position === target ? name : value; + }); +} diff --git a/ui/litellm-dashboard/src/components/model_group_alias_settings.integration.test.tsx b/ui/litellm-dashboard/src/components/model_group_alias_settings.integration.test.tsx new file mode 100644 index 00000000000..816f5629567 --- /dev/null +++ b/ui/litellm-dashboard/src/components/model_group_alias_settings.integration.test.tsx @@ -0,0 +1,218 @@ +/* @vitest-environment jsdom */ +import { act, fireEvent, render, screen, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { setCallbacksCall } from "@/components/networking"; +import { aliasDisplayStorage } from "./model_group_alias_ordering"; +import ModelGroupAliasSettings from "./model_group_alias_settings"; + +vi.mock("@/components/networking", () => ({ + setCallbacksCall: vi.fn(), +})); + +const BLOCKED_STORAGE = { + getItem: () => null, + setItem: () => { + throw new Error("storage blocked"); + }, +}; + +const aliases = { + "fast-model": "claude-haiku", + "smart-model": { model: "claude-sonnet", hidden: true }, +}; + +const displayAliases = { + zeta: "standard", + "subscription/zeta": "standard", + tester: "standard", + alpha: "subscription/fast", + reviewer: { model: "subscription/reasoning", hidden: true }, + "subscription/alpha": "standard", +}; + +const rowNames = () => + within(screen.getByRole("table")) + .getAllByRole("row") + .slice(1) + .map((row) => within(row).getAllByRole("cell")[0].textContent); + +describe("ModelGroupAliasSettings", () => { + beforeEach(() => { + vi.clearAllMocks(); + localStorage.clear(); + vi.mocked(setCallbacksCall).mockResolvedValue({}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + vi.unstubAllGlobals(); + }); + + it("locks config-owned aliases while keeping their names and targets visible", async () => { + render( + , + ); + + expect( + screen.getByText( + "These aliases are defined in config.yaml and are read-only here. Edit the config file to change them.", + ), + ).toBeInTheDocument(); + expect(screen.getByPlaceholderText("e.g., gpt-4o")).toBeDisabled(); + expect(screen.getByPlaceholderText("e.g., gpt-4o-mini-openai")).toBeDisabled(); + expect(screen.getByRole("button", { name: "Add Alias" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Edit alias fast-model" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Delete alias fast-model" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Edit alias smart-model" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Delete alias smart-model" })).toBeDisabled(); + + expect(await screen.findByText("fast-model")).toBeInTheDocument(); + expect(screen.getByText("claude-haiku")).toBeInTheDocument(); + expect(screen.getByText("smart-model")).toBeInTheDocument(); + expect(screen.getByText("claude-sonnet")).toBeInTheDocument(); + + await userEvent.setup().click(screen.getByRole("button", { name: "Delete alias fast-model" })); + expect(setCallbacksCall).not.toHaveBeenCalled(); + }); + + it("keeps aliases editable by default and saves the current string representation after delete", async () => { + const user = userEvent.setup(); + render( + , + ); + + expect(await screen.findByText("fast-model")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Edit alias fast-model" })).toBeEnabled(); + expect(screen.getByRole("button", { name: "Delete alias fast-model" })).toBeEnabled(); + + await user.click(screen.getByRole("button", { name: "Delete alias fast-model" })); + + expect(setCallbacksCall).toHaveBeenCalledWith("test-token", { + router_settings: { model_group_alias: { "smart-model": "claude-sonnet" } }, + }); + }); + + it("keeps ordered pins above sorted groups across remounts without changing routing", async () => { + const user = userEvent.setup(); + const originalAliases = structuredClone(displayAliases); + const props = { + preferenceScope: "test-user-proxy", + accessToken: "test-token", + initialModelGroupAlias: displayAliases, + managedByConfig: true, + }; + const view = render(); + + await user.click(screen.getByRole("button", { name: "Pin alias tester" })); + await user.click(screen.getByRole("button", { name: "Pin alias reviewer" })); + await user.click(screen.getByRole("button", { name: "Move reviewer up" })); + fireEvent.change(screen.getByLabelText("Place aliases with this prefix last"), { + target: { value: "subscription/" }, + }); + expect(rowNames()).toEqual(["reviewer", "tester", "alpha", "zeta", "subscription/alpha", "subscription/zeta"]); + + await user.click(screen.getByRole("button", { name: "Alias Name" })); + expect(rowNames()).toEqual(["reviewer", "tester", "zeta", "alpha", "subscription/zeta", "subscription/alpha"]); + expect(screen.getByRole("columnheader", { name: "Alias Name" })).toHaveAttribute("aria-sort", "descending"); + await user.click(screen.getByRole("button", { name: "Target Model Group" })); + expect(rowNames()).toEqual(["reviewer", "tester", "zeta", "alpha", "subscription/alpha", "subscription/zeta"]); + await user.click(screen.getByRole("button", { name: "Target Model Group" })); + expect(rowNames()).toEqual(["reviewer", "tester", "alpha", "zeta", "subscription/alpha", "subscription/zeta"]); + view.unmount(); + render(); + expect(rowNames()).toEqual(["reviewer", "tester", "alpha", "zeta", "subscription/alpha", "subscription/zeta"]); + expect(screen.getByLabelText("Place aliases with this prefix last")).toHaveValue("subscription/"); + expect(screen.getByRole("button", { name: "Move reviewer up" })).toBeDisabled(); + expect(setCallbacksCall).not.toHaveBeenCalled(); + expect(displayAliases).toEqual(originalAliases); + + await user.click(screen.getByRole("button", { name: "Reset display order" })); + expect(rowNames()).toEqual(["alpha", "reviewer", "subscription/alpha", "subscription/zeta", "tester", "zeta"]); + expect(screen.getByRole("columnheader", { name: "Alias Name" })).toHaveAttribute("aria-sort", "ascending"); + }); + + it.each([false, true])( + "preserves pins through loading and successful edits (storage blocked: %s)", + async (blocked) => { + if (blocked) vi.stubGlobal("localStorage", BLOCKED_STORAGE); + const user = userEvent.setup(); + const props = { preferenceScope: "test-user-proxy", accessToken: "test-token" }; + const view = render(); + await user.click(screen.getByRole("button", { name: "Pin alias tester" })); + view.rerender(); + view.rerender(); + expect(rowNames()[0]).toBe("tester"); + await user.click(screen.getByRole("button", { name: "Edit alias tester" })); + const editRow = screen.getByRole("row", { name: /Save Cancel$/ }); + fireEvent.change(within(editRow).getAllByRole("textbox")[0], { target: { value: "renamed" } }); + vi.spyOn(console, "error").mockImplementation(() => {}); + vi.mocked(setCallbacksCall).mockRejectedValueOnce(new Error("save failed")); + await user.click(screen.getByRole("button", { name: "Save" })); + await user.click(screen.getByRole("button", { name: "Cancel" })); + expect(screen.getByRole("button", { name: "Unpin alias tester" })).toBeInTheDocument(); + + await user.click(screen.getByRole("button", { name: "Edit alias tester" })); + const retryRow = screen.getByRole("row", { name: /Save Cancel$/ }); + fireEvent.change(within(retryRow).getAllByRole("textbox")[0], { target: { value: "renamed" } }); + const pendingSave = Promise.withResolvers(); + vi.mocked(setCallbacksCall).mockReturnValueOnce(pendingSave.promise); + await user.click(screen.getByRole("button", { name: "Save" })); + fireEvent.change(screen.getByLabelText("Place aliases with this prefix last"), { + target: { value: "subscription/" }, + }); + await act(async () => pendingSave.resolve()); + expect(screen.getByLabelText("Place aliases with this prefix last")).toHaveValue("subscription/"); + expect(rowNames()[0]).toBe("renamed"); + expect(screen.getByRole("button", { name: "Unpin alias renamed" })).toBeInTheDocument(); + await user.click(screen.getByRole("button", { name: "Delete alias renamed" })); + fireEvent.change(screen.getByPlaceholderText("e.g., gpt-4o"), { target: { value: "renamed" } }); + fireEvent.change(screen.getByPlaceholderText("e.g., gpt-4o-mini-openai"), { target: { value: "standard" } }); + await user.click(screen.getByRole("button", { name: "Add Alias" })); + expect(rowNames()[0]).toBe("alpha"); + expect(screen.getByRole("button", { name: "Pin alias renamed" })).toBeInTheDocument(); + }, + ); + + it("falls back from corrupt preferences and isolates preferences by scope", async () => { + const user = userEvent.setup(); + localStorage.setItem(aliasDisplayStorage("test-user-proxy").name, '{"sortBy":"unknown"}'); + const props = { accessToken: "test-token", initialModelGroupAlias: displayAliases }; + const view = render(); + expect(rowNames()[0]).toBe("alpha"); + await user.click(screen.getByRole("button", { name: "Pin alias tester" })); + view.rerender(); + expect(rowNames()[0]).toBe("alpha"); + view.rerender(); + expect(rowNames()[0]).toBe("tester"); + view.rerender(); + expect(screen.getByRole("button", { name: "Pin alias tester" })).toBeDisabled(); + }); + + it("keeps display changes usable when browser storage refuses writes", async () => { + const user = userEvent.setup(); + vi.stubGlobal("localStorage", BLOCKED_STORAGE); + render( + , + ); + await user.click(screen.getByRole("button", { name: "Pin alias tester" })); + expect(rowNames()[0]).toBe("tester"); + expect(screen.getByRole("status")).toHaveTextContent("could not be saved"); + await user.click(screen.getByRole("button", { name: "Unpin alias tester" })); + expect(rowNames()[0]).toBe("alpha"); + expect(setCallbacksCall).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/model_group_alias_settings.test.tsx b/ui/litellm-dashboard/src/components/model_group_alias_settings.test.tsx deleted file mode 100644 index fe59e6eb139..00000000000 --- a/ui/litellm-dashboard/src/components/model_group_alias_settings.test.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* @vitest-environment jsdom */ -import { render, screen } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { setCallbacksCall } from "@/components/networking"; -import ModelGroupAliasSettings from "./model_group_alias_settings"; - -vi.mock("@/components/networking", () => ({ - setCallbacksCall: vi.fn(), -})); - -const aliases = { - "fast-model": "claude-haiku", - "smart-model": { model: "claude-sonnet", hidden: true }, -}; - -describe("ModelGroupAliasSettings", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(setCallbacksCall).mockResolvedValue({}); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it("locks config-owned aliases while keeping their names and targets visible", async () => { - render(); - - expect( - screen.getByText( - "These aliases are defined in config.yaml and are read-only here. Edit the config file to change them.", - ), - ).toBeInTheDocument(); - expect(screen.getByPlaceholderText("e.g., gpt-4o")).toBeDisabled(); - expect(screen.getByPlaceholderText("e.g., gpt-4o-mini-openai")).toBeDisabled(); - expect(screen.getByRole("button", { name: "Add Alias" })).toBeDisabled(); - expect(screen.getByRole("button", { name: "Edit alias fast-model" })).toBeDisabled(); - expect(screen.getByRole("button", { name: "Delete alias fast-model" })).toBeDisabled(); - expect(screen.getByRole("button", { name: "Edit alias smart-model" })).toBeDisabled(); - expect(screen.getByRole("button", { name: "Delete alias smart-model" })).toBeDisabled(); - - expect(await screen.findByText("fast-model")).toBeInTheDocument(); - expect(screen.getByText("claude-haiku")).toBeInTheDocument(); - expect(screen.getByText("smart-model")).toBeInTheDocument(); - expect(screen.getByText("claude-sonnet")).toBeInTheDocument(); - - await userEvent.setup().click(screen.getByRole("button", { name: "Delete alias fast-model" })); - expect(setCallbacksCall).not.toHaveBeenCalled(); - }); - - it("keeps aliases editable by default and saves the current string representation after delete", async () => { - const user = userEvent.setup(); - render(); - - expect(await screen.findByText("fast-model")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Edit alias fast-model" })).toBeEnabled(); - expect(screen.getByRole("button", { name: "Delete alias fast-model" })).toBeEnabled(); - - await user.click(screen.getByRole("button", { name: "Delete alias fast-model" })); - - expect(setCallbacksCall).toHaveBeenCalledWith("test-token", { - router_settings: { model_group_alias: { "smart-model": "claude-sonnet" } }, - }); - }); -}); diff --git a/ui/litellm-dashboard/src/components/model_group_alias_settings.tsx b/ui/litellm-dashboard/src/components/model_group_alias_settings.tsx index d3cebc76e98..4a5e7fce9ba 100644 --- a/ui/litellm-dashboard/src/components/model_group_alias_settings.tsx +++ b/ui/litellm-dashboard/src/components/model_group_alias_settings.tsx @@ -1,9 +1,30 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo, useRef } from "react"; +import { + ColumnDef, + TableOptions, + Updater, + functionalUpdate, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { ArrowDown, ArrowUp, Pin, PinOff } from "lucide-react"; import { PlusCircleIcon, PencilIcon, TrashIcon, ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; import { setCallbacksCall } from "./networking"; import { Card, CardTitle } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { toast } from "@/lib/toast"; +import { readStorage, useStoredValue } from "@/lib/storage"; +import { DataTableSortHeader } from "@/components/shared/DataTable/DataTableSortHeader"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + AliasDisplayPreferences, + AliasItem, + aliasDisplayStorage, + DEFAULT_ALIAS_DISPLAY, + movePinnedAlias, + orderAliases, +} from "./model_group_alias_ordering"; type ModelGroupAliasValue = string | { model: string; hidden?: boolean }; @@ -13,12 +34,30 @@ interface ModelGroupAliasSettingsProps { onAliasUpdate?: (updatedAlias: { [key: string]: string }) => void; managedByConfig?: boolean; ownershipLoading?: boolean; + preferenceScope: string | null; } -interface AliasItem { - id: string; - aliasName: string; - targetModelGroup: string; +const ALIAS_COLUMNS: ColumnDef[] = [ + { accessorKey: "aliasName", sortDescFirst: false }, + { accessorKey: "targetModelGroup", sortDescFirst: false }, +]; + +function useAliasDisplayPreferences(preferenceScope: string | null) { + const preferenceKey = useMemo(() => aliasDisplayStorage(preferenceScope ?? ""), [preferenceScope]); + const [storedPreferences, storePreferences] = useStoredValue(preferenceKey); + const [unsavedPreferences, setUnsavedPreferences] = useState(null); + const unsavedRef = useRef(null); + const preferences = preferenceScope === null ? DEFAULT_ALIAS_DISPLAY : unsavedPreferences ?? storedPreferences; + const updatePreferences = (updater: Updater) => { + if (preferenceScope === null) return; + const next = functionalUpdate(updater, unsavedRef.current ?? readStorage(preferenceKey)); + storePreferences(next); + const saved = JSON.stringify(readStorage(preferenceKey)) === JSON.stringify(next); + // rebind-ok: Async saves need the latest fallback when browser storage refuses writes. + unsavedRef.current = saved ? null : next; + setUnsavedPreferences(unsavedRef.current); + }; + return { preferences, updatePreferences, unsavedPreferences }; } const ModelGroupAliasSettings: React.FC = ({ @@ -27,12 +66,39 @@ const ModelGroupAliasSettings: React.FC = ({ onAliasUpdate, managedByConfig = false, ownershipLoading = false, + preferenceScope, }) => { + "use no memo"; + const [aliases, setAliases] = useState([]); const [newAlias, setNewAlias] = useState({ aliasName: "", targetModelGroup: "" }); const [editingAlias, setEditingAlias] = useState(null); const writesDisabled = managedByConfig || ownershipLoading; const [isExpanded, setIsExpanded] = useState(true); + const { preferences, updatePreferences, unsavedPreferences } = useAliasDisplayPreferences(preferenceScope); + const displayAliases = useMemo(() => orderAliases(aliases, preferences), [aliases, preferences]); + const visiblePins = preferences.pinned.filter((name) => aliases.some((alias) => alias.aliasName === name)); + const sorting = [{ id: preferences.sortBy, desc: preferences.descending }]; + const tableOptions: TableOptions = { + data: displayAliases, + columns: ALIAS_COLUMNS, + getCoreRowModel: getCoreRowModel(), + manualSorting: true, + enableSorting: preferenceScope !== null, + enableSortingRemoval: false, + enableMultiSort: false, + state: { sorting }, + onSortingChange: (updater) => { + const selected = functionalUpdate(updater, sorting)[0]; + updatePreferences({ + ...preferences, + sortBy: selected?.id === "targetModelGroup" ? "targetModelGroup" : "aliasName", + descending: selected?.desc ?? false, + }); + }, + }; + const table = useReactTable(tableOptions); + const sortDirection = preferences.descending ? "descending" : "ascending"; useEffect(() => { const aliasArray = Object.entries(initialModelGroupAlias).map(([aliasName, value], index) => ({ @@ -128,8 +194,15 @@ const ModelGroupAliasSettings: React.FC = ({ } const updatedAliases = aliases.map((alias) => (alias.id === editingAlias.id ? editingAlias : alias)); + const originalName = aliases.find((alias) => alias.id === editingAlias.id)?.aliasName; if (await saveAliasesToBackend(updatedAliases)) { + if (originalName !== editingAlias.aliasName) { + updatePreferences((current) => ({ + ...current, + pinned: current.pinned.map((name) => (name === originalName ? editingAlias.aliasName : name)), + })); + } setAliases(updatedAliases); setEditingAlias(null); toast.success("Alias updated successfully"); @@ -144,8 +217,13 @@ const ModelGroupAliasSettings: React.FC = ({ if (writesDisabled) return; const updatedAliases = aliases.filter((alias) => alias.id !== aliasId); + const deletedName = aliases.find((alias) => alias.id === aliasId)?.aliasName; if (await saveAliasesToBackend(updatedAliases)) { + updatePreferences((current) => ({ + ...current, + pinned: current.pinned.filter((name) => name !== deletedName), + })); setAliases(updatedAliases); toast.success("Alias deleted successfully"); } @@ -235,18 +313,57 @@ const ModelGroupAliasSettings: React.FC = ({

Manage Existing Aliases

+
+
+ + updatePreferences({ ...preferences, lastPrefix: event.target.value })} + /> +
+ +
+

+ {unsavedPreferences + ? "Display order could not be saved in this browser. Your changes apply until you leave this page." + : "Display preferences are saved for your account in this browser. Pinned aliases stay first in your chosen order."} +

- Alias Name - Target Model Group + + + + + + + Display order Actions - {aliases.map((alias) => ( + {displayAliases.map((alias) => ( {editingAlias && !writesDisabled && editingAlias.id === alias.id ? ( <> @@ -276,6 +393,7 @@ const ModelGroupAliasSettings: React.FC = ({ className="w-full px-2 py-1 border border-border rounded-md text-sm" /> +
+ {visiblePins.includes(alias.aliasName) && + ([-1, 1] as const).map((direction) => ( + + ))} +
+