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.

Agent frameworks

Point the Vercel AI SDK at TokenTriage
Both providers want the /v1 base.

Each provider factory takes its own baseURL, and both want /v1. tokentriage wrap sets a different root for Anthropic. Read the gotcha.

import { generateText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { createAnthropic } from '@ai-sdk/anthropic';

const baseURL = 'http://127.0.0.1:8787/v1';   // both providers. Anthropic too.
const openai = createOpenAI({ baseURL });
const anthropic = createAnthropic({ baseURL });

// openai(id) is the Responses model, so name the chat endpoint yourself.
await generateText({ model: openai.chat('gpt-4o-mini'), prompt: 'One line.' });
await generateText({ model: anthropic('claude-3-5-haiku-20241022'), prompt: 'One line.' });
Both factories take their own baseURL and both want the /v1. Write openai.chat() explicitly: the bare callable is the Responses model and posts to /v1/responses instead. Your API keys stay in the environment.
The setting
createOpenAI({ baseURL }) · createAnthropic({ baseURL })
The base URL
http://127.0.0.1:8787/v1 — the same /v1 for Anthropic, not the bare root

How to tell it worked

Send one call, then read the row it wrote. Three fields separate a parsed request from a relayed one: the path, the dialect and the pricing key. Empty means passthrough.

tail -1 ~/.tokentriage/decisions.jsonl |
  jq -r '[.http.path, .http.dialect, .cost.pricing_key] | join(" · ")'

What you should see

/v1/chat/completions · openai · 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 ai 7.0.79 · @ai-sdk/openai 4.0.47 · @ai-sdk/anthropic 4.0.42 on

LAB RUN · MOCK UPSTREAM — tokentriage wrap child — 2026-08-25T21:02:31Z
tokentriage wrap: base=http://127.0.0.1:12164 trace=tt_nbh6gepdtz5kfpqx356z3yhxnq agent="vercel-ai-sdk-lab"

env ANTHROPIC_BASE_URL=http://127.0.0.1:12164
env OPENAI_BASE_URL=http://127.0.0.1:12164/v1
env ANTHROPIC_CUSTOM_HEADERS="x-tt-trace-id: tt_nbh6gepdtz5kfpqx356z3yhxnq\nx-tt-agent: vercel-ai-sdk-lab"

## wrap-child-openai — createOpenAI({ apiKey }) with NO baseURL — OPENAI_BASE_URL only
   WIRE  POST http://127.0.0.1:12164/v1/chat/completions  -> status=200 req=tt_qlddxhnstli7ungt25s22adr7u
         x-tt-mode=route x-tt-decision=fast x-tt-cache=miss
   CLIENT OK     {"text":"Hello from the mock upstream.","usage":{"inputTokens":100,"outputTokens":20,"totalTokens":120}}
   LEDGER ROW    path=/v1/chat/completions dialect=openai model=gpt-4o-mini tier=fast
                 trace_agent=(empty) trace_id=(empty)
                 status=200 cost.usd=0.000027 pricing_key=openai/gpt-4o-mini unpriced=null

## wrap-child-anthropic — createAnthropic({ apiKey }) with NO baseURL — ANTHROPIC_BASE_URL only
   WIRE  POST http://127.0.0.1:12164/messages  -> status=404 req=tt_sxldnmcfws6hrx6wippys2ff2y
         x-tt-mode=route x-tt-decision=- x-tt-cache=-
   CLIENT ERROR  AI_APICallError: Not Found
   LEDGER ROW    path=/messages dialect=(empty) model=(empty) tier=(empty)
                 trace_agent=(empty) trace_id=(empty)
                 status=404 cost.usd=0 pricing_key=(empty) unpriced=null

LAB RUN · MOCK UPSTREAM — no vendor API was called.
tokentriage wrap around both providers, against the real proxy engine and a mock upstream. Reproduced twice on independent ports.

The gotcha

tokentriage wrap points @ai-sdk/anthropic at the wrong root

tokentriage wrap sets ANTHROPIC_BASE_URL to the bare root. The provider appends /messages, which matches no dialect and takes the ledgered passthrough. Your client sees AI_APICallError: Not Found. The row carries no dialect, no model, no tier and no price. Pass baseURL yourself.

// An explicit baseURL beats ANTHROPIC_BASE_URL, so this survives tokentriage wrap.
const anthropic = createAnthropic({ baseURL: 'http://127.0.0.1:8787/v1' });

“Use a different URL prefix for API calls, e.g. to use proxy servers. The default prefix is https://api.anthropic.com/v1.”

@ai-sdk/anthropic 4.0.42 · AnthropicProviderSettings.baseURL · read 2026-08-25

What you get now

What both providers get you once traffic flows.

Three things that switch on the first time either provider sends a request. Each links its evidence. Read those before you trust this page.

2 providers
openai and anthropic, one baseURL each, both ending in /v1
1 string to change
the base URL your app hands each factory
4 environment variables
the whole env surface of both providers: two keys, two base URLs
10 scenarios
the matrix this page was written from, run on 2026-08-25

What does not carry across

Six edges, and two of them are ours.

Four are facts about the SDK. Two are facts about our evidence. The last one is the limit of the chip above.

  • The chip does not cover a successful Anthropic call

    The lab harness serves the OpenAI surface only. What was reproduced is that the provider builds /v1/messages and that TokenTriage matches the anthropic dialect and parses the model. Nothing here saw a reply.

    The two dialect paths
  • openai(id) is the Responses model, not chat

    Call it without .chat() and the SDK posts to /v1/responses with a different body. TokenTriage registers that path, so the row reads /v1/responses and not the chat path you looked for. The anthropic factory has no such split.

    Which paths are registered
  • A relayed call can return 200 and still cost nothing

    A path TokenTriage does not register still reaches your upstream, with the path as written. An unregistered GET returned 200 in the same run and wrote a row with no dialect and no price. The two are separate claims.

    Base URL and path, per client
  • Four environment variables, and none of them carries a header

    Each provider reads its API key and its base URL. That is the whole list. wrap also exports a custom-header variable that neither provider reads, so both rows came back with an empty trace id and agent.

    Where a trace id comes from
  • No env fallback on openai-compatible

    createOpenAICompatible reads nothing from the environment. Set baseURL in code. OPENAI_API_BASE is never read by any of these providers, so a base-URL warning written for another framework does not apply here.

  • Vercel AI Gateway is a different thing

    TokenTriage carries a vercel_ai_gateway provider descriptor, which names Vercel AI Gateway as an upstream you point at. This page is about the client library. Nothing here exercised the descriptor.

    What a provider descriptor is

Point both providers, then read one row.

The change is one string in two factory calls. Reading the row is the work. Send a call, check the dialect and the pricing key, then decide about the empty ones.

Also: What the ledger holdsEvery integration