跪拜 Guibai
← All articles
Backend

Inside New API: A Production AI Gateway's Inverted Index, Star-Topology Converter, and Redis-Backed Ledger

By 岩钧 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Teams operating self-hosted gateways across multiple LLM providers get a working blueprint for three problems that usually become bespoke glue: index-backed channel selection at request rates, protocol conversion that logs what it drops rather than silently losing fields, and a billing ledger that stays correct under concurrency. The source also demonstrates a clean consistency split, config converges eventually in 60 seconds, balances stay strongly consistent through Redis Lua, that most gateways hand-wave.

Summary

New API, the AGPLv3 fork of one-api, runs 47 channel adapters covering 40+ vendors, yet its production design reduces to three solved problems: who serves a request, how protocols speak to each other, and how money gets accounted. A single `/v1/chat/completions` request passes through seven stages, from multi-protocol auth to a retry loop whose failure decisions are logged as replayable policy events.

Two structural choices carry most of the engineering. Channel scheduling materializes an abilities table, an inverted index keyed by (group, model, channel_id) that turns "who can serve gpt-4o" into one indexed lookup, with retry count doubling as a priority-tier index so the Nth retry automatically drops to the Nth tier. Protocol conversion is a star topology centered on OpenAI Chat: 12 registered converters collapse to 6 real transformation pairs, with two-hop paths like Claude↔Gemini explicitly marked "discouraged" because every hop loses fields.

Billing is a true ledger lifecycle. The gateway pre-deducts an estimated deposit, settles against real upstream usage, and charges or refunds the delta through a two-phase commit that makes double-refunds impossible. Redis holds the authoritative balance through atomic Lua scripts while the database catches up 5 seconds later. Trusted users above a $10 threshold skip pre-deduction, settlement never fails (negative balances are accepted by design, capped by a single request's maximum bill), and a 5-second window exists where a stale DB read would be wrong if the fences didn't protect it.

Takeaways
— 47 channel adapters cover 40+ vendors; the gateway natively converts between OpenAI Chat, OpenAI Responses, Anthropic Messages, and Gemini.
— Internal pricing uses one unit: 500,000 quota equals $1.
— Config changes propagate across nodes within 60 seconds without a restart.
— The abilities table stores the inverted index of the channels table with a composite primary key (group, model, channel_id), so channel selection is one indexed query.
— Retry count is reused as a priority tier subscript, so the Nth retry automatically selects the Nth tier.
— The default retryable status codes are 100-199, 300-399, 401-407, 409-499, 500-503, 505-523, and 525-599; 400, 408, 504, and 524 are never retried.
— OpenAI's prompt_tokens includes cached tokens and must be split out before cache pricing; Claude's input_tokens excludes them, and mixing the two semantics double-counts cache charges.
— Requests keep exactly one copy of the body in a Seek-able store, reused across auth, conversion, pass-through, and every retry round.
— Users with balance above a $10 trust threshold skip pre-deduction; the full order is recalculated against real usage at settlement.
— All channels share one SSE streaming engine where a client disconnect immediately closes the upstream body, and per-write 30-second timeouts keep slow clients from stalling coroutines.
Conclusions

Treating Redis as the authoritative ledger rather than a cache is the source's boldest call: for 5 seconds the DB is knowingly stale, and the code spends real effort (fences, version floors, TTL-only refreshes) making sure nothing reads that staleness into a wrong deduction.

The retry-count-as-priority-tier trick couples two existing quantities instead of adding a state object, which is elegant, but it quietly makes the retry policy and the routing policy inseparable.

Settlement's no-failure branch is a deliberate cost-behavior trade: the ceiling on a one-request bill makes unpaid orders survivable, and blocking every request on a pre-check would cost more in latency than the occasional negative balance costs in revenue.

The star topology for protocol conversion is the more honest answer to a many-format world: fewer converters and explicit "discouraged" labels on lossy paths beat trying to write perfect pairwise converters.

Concepts & terms
abilities table
A materialized inverted index of the channels table. Instead of scanning each channel's comma-separated model list to answer "who can serve gpt-4o", New API pre-builds a table keyed by (group, model, channel_id) so the reverse query is a single indexed lookup, the same idea as term-to-doc_ids in Elasticsearch.
star-topology protocol conversion
Instead of writing pairwise converters for every protocol combination (O(n²)), New API converts everything through OpenAI Chat as a hub (O(n)), composing two-hop chains like Claude→Gemini from two existing direct converters. Two-hop paths lose fields, so they're labeled "discouraged" in the registry.
pre-deduct / settle / refund ledger
New API's billing lifecycle. Before a request runs, estimated cost is deducted via an atomic Redis Lua script as a deposit; after the response, the bill is recalculated against the upstream's real usage and the delta is charged or refunded. A two-phase commit prevents double-refunds.
retry-as-degradation
The retry counter doubles as a priority-tier subscript: attempt N automatically selects the Nth channel-priority tier, so retrying is the same as degrading to a lower-priority channel. A small state machine adds cross-group fallback without introducing any new state object.
ToolLossPolicy
A three-tier policy for what happens when a protocol conversion can't map a field: allow (default) logs and continues, safe errors out on request-stage losses, strict rejects any loss. New API recommends strict for business-sensitive paths because silent field loss is the worst outcome.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗