Build a Pricing Source-of-Truth Gate for AI Agent SDKs
AI agent SDKs often hide pricing in the wrong place.
The model string is in code. The current rate card is in a dashboard. The public docs quote a dated provider page. The finance team uses an export from last week. The support team sees a customer ticket with a different number. None of those views is necessarily dishonest, but together they create a fragile release process.
When upstream providers change token rows, add context tiers, alter cache rules, or publish new concurrency limits, a production SDK should not rely on tribal memory. It needs a pricing source-of-truth gate: a small data contract and CI check that blocks stale rates before they ship.
This article shows one implementation for an OpenAI-compatible gateway that routes Chinese model families such as DeepSeek, Kimi, GLM, and Qwen through a unified API like AIWave. The goal is practical: make pricing facts dated, reviewable, testable, and visible to agent code before traffic moves.
Source Snapshot for August 20, 2026
For this run I checked public pricing and operations pages on August 20, 2026. These are source facts, not permanent product truth.
| Source | Current signal to record |
|---|---|
| AIWave Pricing and Predictable Pricing | AIWave lists all-day DeepSeek V4 Flash rows of $0.638/M input, $1.914/M output, and $0.0203/M cache-hit input; V4 Pro rows of $1.914/M input, $5.742/M output, and $0.0638/M cache-hit input. |
| DeepSeek Models and Pricing | DeepSeek documents V4 Flash and V4 Pro token pricing, 1M context, 384K maximum output, OpenAI and Anthropic base URLs, and cache-hit/cache-miss input categories. |
| DeepSeek Rate Limit and Isolation | DeepSeek lists account-level concurrency limits of 2500 for V4 Flash and 500 for V4 Pro, plus HTTP 429 behavior and user_id isolation rules. |
| Kimi K3 Pricing | Kimi K3 lists $0.30/M cache-hit input, $3.00/M cache-miss input, $15.00/M output, and a 1,048,576-token context window. |
| Z.AI Pricing | GLM-5.3, GLM-5.2, and GLM-5.1 list $1.40/M input, $0.26/M cached input, and $4.40/M output. |
| QwenCloud Pricing | QwenCloud documents per-million-token billing, request-length tiers, Batch API behavior, context caching, thinking-token billing, and built-in tool fees. |
A good gate does not flatten this table into one price_per_token number. Each provider uses its own billing vocabulary. DeepSeek has cache-hit and cache-miss input plus a concurrency surface. Kimi makes long-context caching central. GLM separates cached input from output. Qwen adds request tiers, thinking tokens, and tool calls. The gate should preserve that shape.
What the Gate Should Prevent
The gate exists to stop four release failures.
First, stale documentation. A developer reads a quickstart, copies an SDK route, and builds a budget around a number that was correct before a provider update. That is a trust failure even if the code still runs.
Second, silent route drift. The SDK points to deepseek-v4-pro, but the policy file was updated for a different route family. The application still returns answers, but finance cannot reproduce the bill from the published docs.
Third, cache math mismatch. The docs show input and output only, while the provider bills cache-hit input separately. Long-context agents can look much more expensive or much less expensive than expected depending on whether repeated context hits cache.
Fourth, bad fallback behavior. A 429 retry or fallback route can multiply cost. If the SDK does not carry rate-card dates and retry policy into logs, the incident review becomes guesswork.
These failures are common because pricing is treated as copy. Treat it as code instead.
A Minimal Rate-Card Contract
Start with a checked-in JSON document. It should be small enough for SDK tests and rich enough for finance review.
{
"generated_at": "2026-08-20T13:00:00Z",
"cards": [
{
"public_model": "deepseek-v4-flash",
"provider_family": "DeepSeek",
"source_url": "https://aiwave.live/pricing",
"source_checked_at": "2026-08-20",
"unit": "1M tokens",
"currency": "USD",
"input": 0.638,
"cached_input": 0.0203,
"output": 1.914,
"context_tokens": 1000000,
"max_output_tokens": 384000,
"policy_note": "AIWave all-day public row"
}
]
}
There are two important details here.
First, the source_checked_at field is mandatory. A rate without a date is not a production fact. It is a rumor with decimal places.
Second, the policy_note field explains whether the row is direct provider pricing, a unified gateway row, an official schedule row, a batch row, or an account-specific row. AIWave's DeepSeek rows, for example, should not be described as DeepSeek's official peak schedule. They are AIWave all-day rows. Keeping those layers separate prevents bad comparisons later.
CI Validation
The CI gate should block three classes of errors: missing evidence, unsafe values, and expired snapshots.
from __future__ import annotations
import json
from datetime import date
from pathlib import Path
from urllib.parse import urlparse
MAX_AGE_DAYS = 7
REQUIRED_FIELDS = {
"public_model",
"provider_family",
"source_url",
"source_checked_at",
"unit",
"currency",
"input",
"output",
}
def load_cards(path: str) -> list[dict]:
data = json.loads(Path(path).read_text(encoding="utf-8"))
return data["cards"]
def validate_card(card: dict, today: date) -> list[str]:
errors: list[str] = []
missing = REQUIRED_FIELDS - set(card)
if missing:
errors.append(f"missing fields: {sorted(missing)}")
if urlparse(card.get("source_url", "")).scheme != "https":
errors.append("source_url must be https")
checked_at = date.fromisoformat(card["source_checked_at"])
if (today - checked_at).days > MAX_AGE_DAYS:
errors.append("source snapshot is older than policy allows")
for field in ("input", "cached_input", "output"):
value = card.get(field)
if value is not None and value < 0:
errors.append(f"{field} cannot be negative")
if card.get("currency") != "USD":
errors.append("this SDK release expects USD rows")
return errors
if __name__ == "__main__":
failures = []
for card in load_cards("rate_cards.json"):
for error in validate_card(card, date(2026, 8, 20)):
failures.append(f"{card.get('public_model', '<unknown>')}: {error}")
if failures:
raise SystemExit("\n".join(failures))
This is intentionally boring. It does not scrape provider pages during every CI run. It validates the checked-in contract. A separate refresh job can update snapshots, but release CI should answer a narrower question: is the SDK about to publish with complete, dated, internally consistent facts?
Runtime Enforcement
The SDK should also carry pricing metadata into each route decision. That does not mean the SDK calculates the final invoice. It means the SDK leaves enough evidence for the gateway ledger to reproduce why a route was selected.
import os
from dataclasses import dataclass, asdict
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("AIWAVE_API_KEY", "YOUR_API_KEY_HERE"),
base_url="https://aiwave.live/v1",
)
@dataclass(frozen=True)
class RouteDecision:
model: str
task_kind: str
source_checked_at: str
policy_version: str
max_tokens: int
fallback_allowed: bool
def choose_route(task_kind: str) -> RouteDecision:
if task_kind == "long_context_code_review":
return RouteDecision(
model="kimi-k3",
task_kind=task_kind,
source_checked_at="2026-08-20",
policy_version="sdk-rate-gate-2026-08-20",
max_tokens=2600,
fallback_allowed=False,
)
return RouteDecision(
model="deepseek-v4-flash",
task_kind=task_kind,
source_checked_at="2026-08-20",
policy_version="sdk-rate-gate-2026-08-20",
max_tokens=1200,
fallback_allowed=True,
)
route = choose_route("support_summary")
response = client.chat.completions.create(
model=route.model,
messages=[{"role": "user", "content": "Summarize the incident timeline for engineering review."}],
max_tokens=route.max_tokens,
)
print({"response_id": response.id, **asdict(route)})
The printed route metadata is not a billing record by itself. It is the join key. The gateway can attach actual token counts, cache-hit tokens, retry count, status, latency, and final model. When a customer asks why the SDK used a route, support can answer from the same policy version that engineering reviewed.
Handling Provider-Specific Shapes
A strong source-of-truth gate allows provider-specific fields without forcing every model into the same shape.
For DeepSeek, include cache-hit input, cache-miss input, output, context, maximum output, concurrency, and 429 policy. Because DeepSeek documents user_id isolation, also decide whether the SDK passes a privacy-safe tenant identifier through extra_body.
For Kimi K3, include long-context size, cache-hit input, cache-miss input, output, and whether the route is approved for whole-repository or large-document workloads. A long-context route without cache visibility is hard to budget.
For GLM, include cached input and output rows, tool capability, reasoning tier, and any agent/tool rows that could affect invoice behavior. Do not put text-only assumptions on a route that may call vision, web search, image, video, or agent tools.
For Qwen, include request-length tiers, Batch API policy, thinking-token billing, and tool fees. A Qwen request with thinking enabled can bill differently from a compact text response, even when the public model name is the same.
The contract can support this by adding a billing_dimensions object:
{
"public_model": "qwen-route-example",
"provider_family": "Qwen",
"source_checked_at": "2026-08-20",
"billing_dimensions": {
"request_tiers": true,
"context_caching": true,
"thinking_tokens_bill_as_output": true,
"batch_api_has_separate_policy": true,
"built_in_tool_fees": true
}
}
The SDK does not need to know every provider detail to make a call. It does need to know which details exist, because those details drive warnings, usage ledgers, and release review.
Release Workflow
The release workflow should be simple.
Step one: refresh public source snapshots. Record the provider URL, the AIWave URL when using AIWave rows, retrieval date, currency, unit, and any notes about schedule or account scope.
Step two: review diffs like code. A price change, context change, output cap change, or concurrency change deserves the same review discipline as an API signature change.
Step three: run CI validation. The release should fail if required fields are missing, dates are stale, values are negative, model names do not appear in the SDK allowlist, or the article/docs layer quotes a different row than the JSON contract.
Step four: publish docs and SDK together. The docs should render from the same checked-in JSON, not from hand-edited tables.
Step five: log runtime route decisions. Every production request should carry model, policy version, source date, token counts, cache fields, retry count, fallback reason, and status. Store usage metadata, not prompt bodies or real customer identifiers.
What to Show Customers
Customer-facing docs do not need every internal control. They need enough to trust the route.
Show the model family, public model ID, supported request shape, context window, output cap, pricing source date, cache categories, and known limits. Explain whether the row is a direct provider row or an AIWave unified row. Link to the source page. Show a small calculator or formula that uses input, cached input, and output separately.
Avoid universal claims. A route can be predictable without being below every official provider row. A model can be strong for one task and poor for another. A cache row can reduce repeated-context cost only when the workload actually reuses context. Precision is more credible than broad marketing language.
Conclusion
Chinese AI model APIs are moving quickly. That is good for developers, but it makes stale pricing dangerous. A production SDK should not ship with numbers that only live in a blog post, screenshot, or private spreadsheet.
A pricing source-of-truth gate turns rate cards into a release artifact. It forces every model route to carry a source URL, retrieval date, billing dimensions, and policy version. It gives CI something concrete to reject. It gives runtime logs a join key. It gives support and finance the same evidence engineering used at release time.
Before your next agent SDK release, make the pricing contract part of the build.
Top comments (0)