reland: feat(recipes): add reranker touchpoint to OpenRouter (#2164) (#3302)

* feat(recipes): add reranker touchpoint to OpenRouter (#2164)

OpenRouter's POST /api/v1/rerank is wire-compatible with gateway.rerank()
({query, documents, model} → {results: [{index, relevance_score}]}). This
adds a recipe-only reranker touchpoint declaring four models:

  - cohere/rerank-v3.5          (default; $0.001/search)
  - cohere/rerank-4-fast        ($0.002/search, 32K context)
  - cohere/rerank-4-pro         ($0.0025/search, SOTA quality)
  - nvidia/llama-nemotron-rerank-vl-1b-v2:free  (multimodal)

Unlike embedding/chat, the reranker path strictly enforces the models
allowlist — the openai-compat extended-model bypass does not apply. New
rerank models must be added to this recipe before they can be called.

The cost_per_1m_tokens_usd value is a pseudo-rate for the budget tracker's
chars/4 heuristic — Cohere bills per-search, not per-token. At ~4K chars
the estimated cost is in the right ballpark.

Recipe-only change; no gateway or search-layer modifications. gateway
auto-concatenates path → .../api/v1/rerank.

Adds hermetic unit test (test/openrouter-reranker-recipe.test.ts) covering
shape, models, default_model, path, max_payload_bytes, default_timeout_ms,
and cost field. No DB, no env mutation — survives the parallel 8-shard
fan-out.

Verified: bun run verify (30/30 green); 285 targeted recipe+rerank+budget
tests pass.

Co-authored-by: Hippityy <Hippityy@users.noreply.github.com>

* test(facts): pin gateway to 1536d in facts-engine.test.ts beforeAll

Shard-composition hermeticity fix. The legacy preload's beforeEach only
re-applies the 1536-d gateway default before each TEST, not before a
file's beforeAll — so when the previous file in the shard resets the
gateway in its teardown (e.g. test/providers-test-model-base-url.test.ts
via afterEach), this file's initSchema() sized facts.embedding at the
1280-d production default and the 1536-d fixture inserts threw
'expected 1280 dimensions, not 1536' (CI shard 1 failure on #3302).
Same pattern as test/consolidate-valid-until.test.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Ryan Xie <64182766+Hippityy@users.noreply.github.com>
Co-authored-by: Hippityy <Hippityy@users.noreply.github.com>
Co-authored-by: Garry Tan <garrytan@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Time Attakc
2026-07-24 12:27:48 -07:00
committed by GitHub
co-authored by Claude Fable 5 Ryan Xie Hippityy Garry Tan
parent f30d789c3a
commit 54c0c93376
3 changed files with 77 additions and 0 deletions
+32
View File
@@ -119,6 +119,14 @@ export const openrouterCompatFetch = (async (
* envelope, not every individual model's capability. When in doubt about a
* specific model, check https://openrouter.ai/models.
*
* Reranker: `/api/v1/rerank` proxies cross-encoder rerankers (Cohere v3.5/4-fast/4-pro
* and NVIDIA Nemotron VL). Wire shape matches `gateway.rerank()`:
* `{ query, documents, model }` → `{ results: [{ index, relevance_score }] }`.
* Unlike embedding/chat, the reranker path strictly enforces the `models`
* allowlist (no openai-compat bypass) — adding new rerank models requires a
* recipe edit. Cohere bills per-search; the `cost_per_1m_tokens_usd` value
* is a pseudo-rate for the budget tracker's `chars/4` heuristic.
*
* Attribution: OpenRouter recommends `HTTP-Referer` (required for app
* attribution) + `X-OpenRouter-Title` (preferred; `X-Title` kept as
* back-compat alias per OR docs). Defaults to `https://gbrain.ai` / `gbrain`;
@@ -197,6 +205,30 @@ export const openrouter: Recipe = {
// Let upstream errors surface per-model.
price_last_verified: '2026-05-20',
},
reranker: {
models: [
'cohere/rerank-v3.5',
'cohere/rerank-4-fast',
'cohere/rerank-4-pro',
'nvidia/llama-nemotron-rerank-vl-1b-v2:free',
],
default_model: 'cohere/rerank-v3.5',
// Cohere bills per-search, not per-token. This is a pseudo-per-1M rate
// for the budget tracker's heuristic (estimates tokens as chars/4).
// At ~4K chars/search the tracker estimates ~$0.00025 — in the right
// ballpark for the per-search bill. Patch budget-tracker.ts to honour a
// `cost_per_search_usd` field for exact accounting.
cost_per_1m_tokens_usd: 0.001,
price_last_verified: '2026-06-13',
// OpenRouter doesn't publish an explicit payload cap; 5MB matches
// ZeroEntropy's upstream limit and the gateway's pre-flight ceiling.
max_payload_bytes: 5_000_000,
// OR serves /rerank under /api/v1. base_url_default already ends in /v1,
// so gateway concatenates to …/api/v1/rerank.
path: '/rerank',
// OpenRouter rerank is fast (<200 ms p50); 5 s covers cold path safely.
default_timeout_ms: 5_000,
},
},
setup_hint:
'Get an API key at https://openrouter.ai/settings/keys, then `export OPENROUTER_API_KEY=...` and use `openrouter:<provider>/<model>`. Optional overrides: OPENROUTER_BASE_URL (proxy), OPENROUTER_REFERER (attribution URL), OPENROUTER_TITLE (attribution name).',
+1
View File
@@ -27,6 +27,7 @@ beforeAll(async () => {
// dimensions, not 1536"). Same fix + rationale as
// doctor-hidden-by-search-policy.test.ts (#2801),
// engine-find-trajectory.test.ts and cosine-rescore-column.test.ts.
resetGateway();
configureGateway({
embedding_model: 'openai:text-embedding-3-large',
embedding_dimensions: 1536,
+44
View File
@@ -0,0 +1,44 @@
import { describe, test, expect } from 'bun:test';
import { getRecipe } from '../src/core/ai/recipes/index.ts';
describe('OpenRouter recipe — reranker touchpoint', () => {
test('declares a reranker touchpoint', () => {
const r = getRecipe('openrouter');
expect(r).toBeDefined();
expect(r!.touchpoints.reranker).toBeDefined();
});
test('models list includes all supported IDs (incl. NVIDIA :free suffix)', () => {
const m = getRecipe('openrouter')!.touchpoints.reranker!.models;
expect(m).toContain('cohere/rerank-v3.5');
expect(m).toContain('cohere/rerank-4-fast');
expect(m).toContain('cohere/rerank-4-pro');
// The :free suffix must appear in full — gateway.rerank() does exact
// string matching against the allowlist (no v0.31.12 extended-model bypass
// on the rerank path), so truncating to `nvidia/.../v2` would 403.
expect(m).toContain('nvidia/llama-nemotron-rerank-vl-1b-v2:free');
});
test('default_model is cohere/rerank-v3.5', () => {
const tp = getRecipe('openrouter')!.touchpoints.reranker!;
expect(tp.default_model).toBe('cohere/rerank-v3.5');
expect(tp.models).toContain(tp.default_model);
});
test('path is /rerank (NOT ZeroEntropy default /models/rerank)', () => {
const tp = getRecipe('openrouter')!.touchpoints.reranker!;
expect(tp.path).toBe('/rerank');
});
test('max_payload_bytes and timeout match plan', () => {
const tp = getRecipe('openrouter')!.touchpoints.reranker!;
expect(tp.max_payload_bytes).toBe(5_000_000);
expect(tp.default_timeout_ms).toBe(5_000);
});
test('cost_per_1m_tokens_usd is set (pseudo-rate for per-search billing)', () => {
const tp = getRecipe('openrouter')!.touchpoints.reranker!;
expect(typeof tp.cost_per_1m_tokens_usd).toBe('number');
expect(tp.cost_per_1m_tokens_usd).toBeGreaterThan(0);
});
});