mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
fix(pricing): add the zeroentropyai:zerank-2 reranker entry the budget tracker needs (#3223) (#3233)
zerank-2 is the default reranker under search_mode: tokenmax, but had no pricing entry — any --max-cost-capped rerank call TX2 hard-failed in BudgetTracker.reserve() with "no pricing entry". Adding the entry to EMBEDDING_PRICING alone (the issue's suggested fix) does not resolve this: lookupPricing()'s rerank branch in budget-tracker.ts never consulted that table at all, only ANTHROPIC_PRICING and the FREE_LOCAL_RERANK_PROVIDERS zero-price set. Verified by reproducing the hard-fail with only the pricing-table entry added and confirming it still threw. Fix: add the $0.025/1M-token entry (docs/ai-providers/zeroentropy.md) and wire the rerank branch to fall back to lookupEmbeddingPrice, reusing the existing provider:model-keyed table instead of duplicating a third pricing surface. Addresses the report in #3223. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -166,9 +166,13 @@ const FREE_LOCAL_EMBED_PROVIDERS: ReadonlySet<string> = new Set([
|
||||
* local-inference providers (FREE_LOCAL_EMBED_PROVIDERS) price at $0 so
|
||||
* `--max-cost` callers don't hard-fail.
|
||||
* - Rerank: try ANTHROPIC_PRICING (legacy path for any Claude-priced
|
||||
* rerank); else if the provider half is in FREE_LOCAL_RERANK_PROVIDERS,
|
||||
* return zero pricing so `--max-cost` callers don't TX2 hard-fail on
|
||||
* local inference recipes (electricity, not tokens); else unknown.
|
||||
* rerank); else try lookupEmbeddingPrice — paid rerank providers (e.g.
|
||||
* ZeroEntropy's zerank-2) share the same provider:model-keyed,
|
||||
* $/1M-token table as their embedding siblings, so it's reused here
|
||||
* rather than duplicated into a third table; else if the provider half
|
||||
* is in FREE_LOCAL_RERANK_PROVIDERS, return zero pricing so `--max-cost`
|
||||
* callers don't TX2 hard-fail on local inference recipes (electricity,
|
||||
* not tokens); else unknown.
|
||||
*/
|
||||
function lookupPricing(modelId: string, kind: BudgetKind): ModelPricing | null {
|
||||
if (kind === 'embed') {
|
||||
@@ -194,6 +198,14 @@ function lookupPricing(modelId: string, kind: BudgetKind): ModelPricing | null {
|
||||
const tailHit = ANTHROPIC_PRICING[modelTail];
|
||||
if (tailHit) return tailHit;
|
||||
}
|
||||
// Paid rerank providers (e.g. ZeroEntropy's zerank-2) aren't Claude-priced,
|
||||
// so they miss the ANTHROPIC_PRICING checks above. Reuse the embedding
|
||||
// pricing table (issue #3223) — same provider:model key shape, same
|
||||
// $/1M-token unit — instead of hand-copying a third pricing surface.
|
||||
if (kind === 'rerank') {
|
||||
const hit = lookupEmbeddingPrice(modelId);
|
||||
if (hit.kind === 'known') return { input: hit.pricePerMTok, output: 0 };
|
||||
}
|
||||
// v0.40.6.1: zero-price local-inference rerank providers so the budget
|
||||
// tracker's TX2 hard-fail doesn't trip on `llama-server-reranker:<model>`
|
||||
// under `--max-cost`. Only the rerank kind — chat/embed already have
|
||||
|
||||
@@ -37,6 +37,10 @@ export const EMBEDDING_PRICING: Record<string, EmbeddingPricing> = {
|
||||
'voyage:voyage-4-large': { pricePerMTok: 0.18 },
|
||||
// ZeroEntropy (https://zeroentropy.dev/pricing — zembed-1)
|
||||
'zeroentropyai:zembed-1': { pricePerMTok: 0.05 },
|
||||
// ZeroEntropy reranker (docs/ai-providers/zeroentropy.md — $0.025/1M tokens).
|
||||
// Reused here (not a separate rerank table) because budget-tracker.ts's
|
||||
// rerank-kind lookup falls back to this same table for paid providers.
|
||||
'zeroentropyai:zerank-2': { pricePerMTok: 0.025 },
|
||||
// Mistral (https://mistral.ai/pricing/api/, verified 2026-07-19)
|
||||
'mistral:mistral-embed': { pricePerMTok: 0.10 },
|
||||
'mistral:mistral-embed-2312': { pricePerMTok: 0.10 },
|
||||
|
||||
@@ -248,6 +248,37 @@ describe('BudgetTracker.reserve', () => {
|
||||
expect((caught as BudgetExhausted).reason).toBe('no_pricing');
|
||||
});
|
||||
|
||||
test('#3223: rerank kind for zeroentropyai:zerank-2 prices from the embedding table (no TX2 throw under --max-cost)', () => {
|
||||
// Pre-fix: `search_mode: tokenmax` defaults the zerank-2 reranker ON
|
||||
// (docs/ai-providers/zeroentropy.md), but lookupPricing's rerank branch
|
||||
// never consulted the embedding pricing table (where ZeroEntropy's
|
||||
// provider:model-keyed prices live) — so any --max-cost run that
|
||||
// reranked TX2 hard-failed with "no pricing entry" even after adding
|
||||
// the entry to EMBEDDING_PRICING alone. Fixed by wiring the rerank
|
||||
// branch to fall back to lookupEmbeddingPrice.
|
||||
const t = new BudgetTracker({ maxCostUsd: 0.0001, label: 'test', auditPath });
|
||||
expect(() =>
|
||||
t.reserve({
|
||||
modelId: 'zeroentropyai:zerank-2',
|
||||
estimatedInputTokens: 3000,
|
||||
maxOutputTokens: 0,
|
||||
kind: 'rerank',
|
||||
}),
|
||||
).not.toThrow();
|
||||
expect(t.totalSpent).toBe(0); // reserve() only projects; record() below banks it.
|
||||
expect(() =>
|
||||
t.record({
|
||||
modelId: 'zeroentropyai:zerank-2',
|
||||
inputTokens: 3000,
|
||||
outputTokens: 0,
|
||||
kind: 'rerank',
|
||||
}),
|
||||
).not.toThrow();
|
||||
// $0.025/1M * 3000 tokens = $0.000075, under the $0.0001 cap — proves the
|
||||
// real ZeroEntropy price was used, not a $0 fallback.
|
||||
expect(t.totalSpent).toBeCloseTo(0.000075, 9);
|
||||
});
|
||||
|
||||
test('v0.40.x: local embed providers price at $0 (no TX2 throw under --max-cost)', () => {
|
||||
// FREE_LOCAL_EMBED_PROVIDERS — ollama / llama-server run on local inference
|
||||
// (electricity, not tokens). Pre-fix a --max-cost embed/reindex job
|
||||
|
||||
Reference in New Issue
Block a user