mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-24 20:09:02 +00:00
The docs read /v1/models rates from its per-million keys and say that pricing.prompt and completion are per token.
This commit is contained in:
@@ -15,7 +15,9 @@ import { PROVIDER_ICONS, providerKey } from '@/components/provider-icons';
|
||||
|
||||
const ENDPOINT = 'https://api.hanzo.ai/v1/models';
|
||||
|
||||
type Pricing = { input: number | null; output: number | null; cacheRead: number | null; cacheWrite: number | null };
|
||||
// `prompt`/`completion` are USD per token (OpenRouter's keys); the per-million
|
||||
// rates sit under keys that name the unit.
|
||||
type Pricing = { prompt?: string; completion?: string; input_per_million?: number; output_per_million?: number };
|
||||
type Model = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -41,7 +43,7 @@ const FAMILY_ICON: Record<string, typeof Sparkle> = { Sparkle, Zap, Cpu, Box };
|
||||
// catalogue's raw key; these are the names we use for them.
|
||||
const OURS: Record<string, string> = { hanzo: 'Hanzo', zenlm: 'Zen' };
|
||||
|
||||
// Prices come back as USD per 1M tokens.
|
||||
// Prices shown are USD per 1M tokens.
|
||||
function price(v: number | null | undefined): string {
|
||||
if (v == null) return '—';
|
||||
if (v === 0) return 'Free';
|
||||
@@ -148,8 +150,8 @@ function ModelRow({ m }: { m: Model }) {
|
||||
</YStack>
|
||||
</Td>
|
||||
<Td align="right">{ctx(m.context_window)}</Td>
|
||||
<Td align="right">{price(p?.input)}</Td>
|
||||
<Td align="right">{price(p?.output)}</Td>
|
||||
<Td align="right">{price(p?.input_per_million)}</Td>
|
||||
<Td align="right">{price(p?.output_per_million)}</Td>
|
||||
<Td align="right">
|
||||
<XStack gap={4} flexWrap="wrap" justify="flex-end">
|
||||
{capabilities(m).slice(0, 4).map((f) => (
|
||||
|
||||
@@ -195,8 +195,9 @@ and each call is a row in the org's usage (`GET /v1/billing/usage`, model
|
||||
| `typesafe/jev-1.13`, `~typesafe/jev-latest` | $0.03 per call |
|
||||
|
||||
Kai's rate is its row in the model catalogue every model is billed from:
|
||||
`GET https://api.hanzo.ai/v1/models` lists `kai` with `pricing.input` 0.021 and
|
||||
`pricing.output` 0. A refused or failed call is not billed.
|
||||
`GET https://api.hanzo.ai/v1/models` lists `kai` with `pricing.input_per_million`
|
||||
0.021 (`pricing.prompt` `"0.000000021"` per token) and `pricing.output_per_million`
|
||||
0. A refused or failed call is not billed.
|
||||
|
||||
## Errors
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@ curl -sS https://api.hanzo.ai/v1/models | jq '.data[] | select(.id == "kai")'
|
||||
|
||||
Step 2 needs an org whose plan covers paid models; without one it answers
|
||||
`402` with `"code": "plan_required"` and a link to pick a plan. Step 3 answers
|
||||
`"outputs": ["decision"]` and `"pricing": {"input": 0.021, "output": 0}`: the
|
||||
dollars per million input tokens every `kai` call is billed at.
|
||||
`"outputs": ["decision"]` and `"pricing"` with `"input_per_million": 0.021` and
|
||||
`"output_per_million": 0`: the dollars per million input tokens every `kai` call
|
||||
is billed at (`"prompt": "0.000000021"` is the same rate per token).
|
||||
|
||||
## What changes
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ The managed ids, from the gateway:
|
||||
|
||||
```bash
|
||||
curl -s https://api.hanzo.ai/v1/models \
|
||||
| jq -r '.data[] | select(.id | startswith("enso")) | "\(.id)\t\(.context_window)\t\(.pricing.input)/\(.pricing.output)"'
|
||||
| jq -r '.data[] | select(.id | startswith("enso")) | "\(.id)\t\(.context_window)\t\(.pricing.input_per_million)/\(.pricing.output_per_million)"'
|
||||
```
|
||||
|
||||
| Id | Reasoning | Note |
|
||||
|
||||
@@ -54,14 +54,16 @@ needs no key:
|
||||
|
||||
```bash
|
||||
curl -s https://api.hanzo.ai/v1/models \
|
||||
| jq -r '.data[] | select(.id | test("^(zen|enso)")) | "\(.id)\t\(.owned_by)\t\(.pricing.input)/\(.pricing.output)"'
|
||||
| jq -r '.data[] | select(.id | test("^(zen|enso)")) | "\(.id)\t\(.owned_by)\t\(.pricing.input_per_million)/\(.pricing.output_per_million)"'
|
||||
```
|
||||
|
||||
<ModelsCatalog />
|
||||
|
||||
## What a rate means
|
||||
|
||||
Rates are per million tokens, input and output priced separately, and they are
|
||||
Rates are per million tokens (`pricing.input_per_million`, `output_per_million`;
|
||||
`pricing.prompt` and `completion` state the same rates per token, as OpenRouter
|
||||
does), input and output priced separately, and they are
|
||||
the same whether you call from the CLI, an SDK, HTTP or an MCP tool — the price
|
||||
is a property of the model, not of the surface you reach it through.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ needs no key:
|
||||
|
||||
```bash
|
||||
curl -s https://api.hanzo.ai/v1/models \
|
||||
| jq -r '.data[] | select(.owned_by == "zenlm") | "\(.id)\t\(.context_window)\t\(.pricing.input)/\(.pricing.output)"'
|
||||
| jq -r '.data[] | select(.owned_by == "zenlm") | "\(.id)\t\(.context_window)\t\(.pricing.input_per_million)/\(.pricing.output_per_million)"'
|
||||
```
|
||||
|
||||
The gateway is the source of truth for ids, windows and rates. If this page and
|
||||
|
||||
@@ -177,12 +177,19 @@ async function fetchCatalogue(): Promise<Map<string, Rate & { context: number |
|
||||
console.warn(`[pricing] catalogue answered ${r.status}`);
|
||||
return out;
|
||||
}
|
||||
// `pricing.prompt`/`completion` are USD per token; the per-million rates this
|
||||
// page prints sit under `input_per_million`/`output_per_million`.
|
||||
const d = (await r.json()) as {
|
||||
data?: Array<{ id: string; context_window?: number | null; pricing?: Rate }>;
|
||||
data?: Array<{
|
||||
id: string;
|
||||
context_window?: number | null;
|
||||
pricing?: { input_per_million?: number; output_per_million?: number };
|
||||
}>;
|
||||
};
|
||||
for (const m of d.data ?? []) {
|
||||
if (m.pricing?.input == null) continue;
|
||||
out.set(m.id, { ...m.pricing, context: m.context_window ?? null });
|
||||
const input = m.pricing?.input_per_million;
|
||||
if (input == null) continue;
|
||||
out.set(m.id, { input, output: m.pricing?.output_per_million ?? null, context: m.context_window ?? null });
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`[pricing] catalogue unreachable: ${(e as Error).message}`);
|
||||
|
||||
Reference in New Issue
Block a user