fix(x/inference): ceil dynamic price increases so sub-50 prices can rise#1451
Open
len5ky wants to merge 1 commit into
Open
fix(x/inference): ceil dynamic price increases so sub-50 prices can rise#1451len5ky wants to merge 1 commit into
len5ky wants to merge 1 commit into
Conversation
CalculateModelDynamicPrice computed the increased price with uint64(newPriceDec.IntPart()), which truncates toward zero. At the max +2%/block cap, floor(p*1.02) = p for every currentPrice <= 49, so the increase branch is a no-op at any utilization once a price decays below 50 — the price can never rise again. Mainnet evidence: all 8 models are pinned at 1 ngonka (the min price), verified live 2026-07-14. Fix: quantize upward in the increase branch only, using newPriceDec.Ceil().IntPart(). Any block above the stability zone now raises the price by at least the integer quantum (1). For prices >= 50 behavior is nearly identical (ceil vs floor differ by <=1), and exact cap multiples (e.g. 1000*1.02 = 1020.0) are unchanged. Ceil, not round-half-up: round(1*1.02) = 1 would only shrink the trap to p < 25, not eliminate it. The decrease branch is left untouched — its floor guarantees downward progress, and ceiling it would create a mirror-image downward trap. This changes on-chain price evolution and is consensus-breaking; it must ship in a coordinated network upgrade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
@len5ky we're currently handling an inference through the gateway. This way is deprecated for now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(x/inference): ceil price increases so sub-50 prices can rise
Part 1 of 2 for #1450 — ceil the dynamic-pricing increase branch so a price
that has decayed below 50 ngonka can rise again under load.
Fix
Increase branch only (
CalculateModelDynamicPrice,inference-chain/x/inference/keeper/dynamic_pricing.go):Any block above the stability zone now raises the price by at least the integer
quantum (1). For
p ≥ 50behavior is nearly identical (ceil vs floor differ by≤1); exact cap multiples are unchanged (
1000 × 1.02 = 1020.0→ 1020). Thedecrease branch stays floored: its floor guarantees downward progress
(
floor(2 × 0.98) = 1), and ceiling it would create a mirror-image downwardtrap where a small price could never fall (
ceil(2 × 0.98) = 2).Why this is needed — analysis excerpt
CalculateModelDynamicPricecomputes the increased price asuint64(newPriceDec.IntPart()).IntPart()truncates toward zero. The maximumincrease per block is ×1.02 (elasticity 0.05 × max excess 0.40), so
floor(p × 1.02) > prequires p ≥ 50:So the increase branch is a no-op at any utilization once a price decays below
50. The
MinPerTokenPriceclamp runs after truncation, so withmin_per_token_price = 1it does not rescue anything. Any stored price in 1–49is a one-way trap: it can fall in but never climb out through utilization alone.
The only other writers of the price map (the grace-period initializer and the
missing-price →
base_per_token_pricefallback) don't apply to an already-pricedmodel after the grace boundary, so escaping the trap otherwise requires a param
change, a code change, or a state migration.
How mainnet got there. After the grace boundary, with
base_per_token_price = 100,stability_zone_lower_bound = 0.40and demand belowthe stability zone, the price decays at up to −2%/block — and truncation makes it
faster than the continuous math suggests: 100 falls below the p=50 trap line in
26 blocks (≈2 min) and reaches the floor of 1 within 74 blocks (≈6 min; below 50,
floor(0.98p)loses a full 1 every block).grace_period_end_epoch = 90and thechain is past epoch 300, so this happened long ago.
Mainnet evidence.
GET .../inference/all_model_per_token_pricesonnode3.gonka.aireturns"price": "1"for all 8 models (incl.moonshotai/Kimi-K2.6,zai-org/GLM-5.2-FP8,MiniMaxAI/MiniMax-M2.7), verifiedlive 2026-07-14.
RecordInferencePricelocks this price onto every inference atstart/finish, so all mainnet inference settles at 1/100th of the intended base
price and cannot recover under any demand until intervention.
Why ceil, not round-half-up:
round(1 × 1.02) = round(1.02) = 1. Roundingonly shrinks the trap to
p < 25; it does not eliminate it. Only ceil guaranteesa strict +1 for every sub-cap increase.
Tests
dynamic_pricing_test.go, table-driven, added cases:currentPrice=1, U=1.0 → 2(trap escape; was 1 pre-fix)49 → 50,50 → 51,1000 → 1020(cap intact),1000 @ U=0.61 → 1001(sub-quantum rounds up),
0 → 1(literal-zero stored price clamps to min).newPrice > oldPriceon every increase case) and anindependent quantum property (any
0 < oldPrice < 50under the 2% cap mustrise by exactly one quantum) — both derived from the trap arithmetic, not from
the keeper's own expression, so a regression to plain floor or round-half-up
fails the suite.
Consensus impact
UpdateDynamicPricingruns for every model inBeginBlock, so this changeson-chain price evolution and is consensus-breaking. It must ship in a
coordinated network upgrade. After this fix, prices climb from their current
floor values under demand (≈+1/block initially at 1 ngonka); if the team prefers
a reset to
base_per_token_priceat upgrade time, that would be a separateparam/state decision outside this PR.
Out of scope (disclosed)
uint64(...IntPart())is undefined once the decimal exceeds int64. Pre-existing; unchanged by this
patch (the cast was already there).
U = 1.0the factor is exactly thecap (1.02), so the
> maxIncreasePerBlockclamp binds only aboveU = 1.0;that strict-clamp path is not separately exercised.
floor(2 × 0.98) = 1is a −50% step, far past the relative −2% cap.Pre-existing; the decrease branch is intentionally left floored (see above).
🤖 Generated with Claude Code