Search

↑↓ to move↵ to openesc to closetop 6 documentation results

Book a demoTry TokenTriage

In development · Explore the product preview. Join the launch list or book a walkthrough.

SDKs

Point the OpenAI Python SDK at TokenTriage
The /v1 is the whole change.

One argument on the client, and the /v1 is part of it. Nothing else changes. Then the row tells you it was costed.

from openai import OpenAI

# The /v1 belongs to the address. OPENAI_BASE_URL takes the same string.
# The key is forwarded upstream as-is, unless the upstream names api_key_env.
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="sk-lab")

client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "hello"}],
)
That is the whole integration. Keep the /v1: the gateway matches that path exactly, so a bare root is a different request that still reaches your upstream. Nothing else in your code moves.
The setting
OpenAI(base_url=…), or the OPENAI_BASE_URL env var
The base URL
http://127.0.0.1:8787/v1 — /v1 or /v1/, never the bare root

How to tell it worked

Ask the SDK for the raw response and print the request id. Then find that id in decisions.jsonl. The header proves arrival. The row proves costing.

r = client.chat.completions.with_raw_response.create(
    model="gpt-4o-mini", messages=[{"role": "user", "content": "hi"}]
)
print(r.headers["x-tt-request-id"])

What you should see

tt_pibe5apkzccashrmhqnfyzuzki — and that row reads "dialect":"openai", "pricing_key":"openai/gpt-4o-mini"

How sure we are

Reproduced

A person ran it by hand against a real gateway on a stated date, and the transcript is on the page.

Verified against openai 3.3.1 on Python 3.14.5 on

# LAB RUN · MOCK UPSTREAM · openai 3.3.1 · Python 3.14.5 · go1.25.6 · 2026-08-25
# venv A — pip install openai, then pip install -e "sdk/python[dev,httpx]"
$ venv/Scripts/python.exe -m pytest sdk/python/tests/test_adapter_httpx_e2e.py
============================= test session starts =============================
platform win32 -- Python 3.14.5, pytest-9.1.1, pluggy-1.6.0
rootdir: D:\tokentriage\sdk\python
configfile: pyproject.toml
plugins: anyio-4.14.2, hypothesis-6.165.10
collected 2 items

sdk\python\tests\test_adapter_httpx_e2e.py ..                            [100%]

============================== 2 passed in 9.21s ==============================

# venv B — pip install -e "sdk/python[dev]", the string CI runs at sdk-python.yml:285
$ venv-ci/Scripts/python.exe -m pytest sdk/python/tests/test_adapter_httpx_e2e.py -rs
plugins: hypothesis-6.165.10
collected 0 items / 1 skipped

=========================== short test summary info ===========================
SKIPPED [1] sdk\python\tests\test_adapter_httpx_e2e.py:12: could not import 'httpx': No module named 'httpx'
============================= 1 skipped in 0.27s ==============================
sdk/python/tests/test_adapter_httpx_e2e.py drives the real openai package against the real proxy engine. It passed here, and under CI’s own install string it collects nothing at all. Venv paths abbreviated; no line reworded.

The gotcha

Your provider gets a body you did not send

TokenTriage splices stream_options.include_usage into the outbound body, then strips the extra frame out of your copy. Your chunks carry no usage object. The row still reads usage.source provider. A provider that rejects unknown fields, or signs the request body, sees the difference.

# Stop the injection. Output tokens are estimated instead.
ledger:
    usage_injection: false

“If set, an additional chunk will be streamed before the `data: [DONE]` message.”

openai 3.3.1 · chat_completion_stream_options_param.py · read 2026-08-25

What you get now

What one argument buys you.

Three things that start the first time a request reaches the gateway. Each links the page that evidences it. Read those first.

1 argument
base_url on the constructor, or OPENAI_BASE_URL carrying the same string
2 tests, neither in CI
both passed by hand on 2026-08-25; the CI install collects zero of them
6 row fields emptied
dialect, model, tier, usage source, pricing key, estimated: the passthrough shape
0 chunks carrying usage
the injected frame is stripped before your client sees it, and the row is priced anyway

What does not carry across

Five things this swap will not do.

Four are about your setup. The fifth is about our evidence. The first one is how this goes wrong quietly.

  • A base URL without /v1 still reaches your upstream

    TokenTriage matches /v1/chat/completions exactly. A bare root sends /chat/completions, which matches no dialect and is relayed with the path as written. Our mock answered 404. An upstream that serves that path answers 200, and the row is still unpriced.

    What the path has to be
  • A trailing slash does not match, and it is not yours

    /v1/chat/completions/ matches nothing. The OpenAI Python SDK cannot produce it: a base_url ending /v1 and one ending /v1/ build the same path, checked on 3.3.1. The slash arrives from a rewriting proxy or an ingress rule in front.

  • The evidence on this page is not one level

    Levels are not uniform here. The chip is Reproduced because the Python test skips in CI. The injection gotcha is gated instead: go test ./test/e2e runs TestRealOpenAIStreamStrip on every push.

  • openai 3.3.1 installs httpx2, and not httpx

    The adapter tt.httpx_client() rides the httpx package, which pip install openai stopped pulling at 3.0.0. Install the extra. openai 3.3.1 accepts a client from either package, so the adapter still works.

    Every exported name in the Python SDK
  • What we ran was chat completions, and only that

    TokenTriage registers /v1/responses, /v1/completions and /v1/embeddings in the same OpenAI driver. We sent none of them. So the rows behind this page describe chat.completions.create and nothing else.

Set it, then read one row.

The change is one argument and it is reversible. Read one row first. Point base_url back and the SDK is talking to OpenAI again.

Also: What lands in the decision logEvery integration