CommunitySchreiben & Editierengithub.com

AMindToThink/openrouter_reliable_research_search

Do researchers use OpenRouter reliably? Best practices, a survey of public research code, taxonomy of mistakes, and a Claude skill.

Was ist openrouter_reliable_research_search?

openrouter_reliable_research_search is a Claude Code agent skill that do researchers use OpenRouter reliably? Best practices, a survey of public research code, taxonomy of mistakes, and a Claude skill.

Funktioniert mitClaude Code~Codex CLI~CursorGemini CLI
npx skills add AMindToThink/openrouter_reliable_research_search

Installed? Explore more Schreiben & Editieren skills: steipete/notion, affaan-m/seo, affaan-m/brand-voice · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Using OpenRouter safely for research

The core problem: with default settings, "a model" on OpenRouter is not a fixed artifact. The same slug can be served by different providers, at different quantizations (int4→fp32), with different context windows and output caps, on different inference engines, with silently-dropped parameters. If a result depends on which weights actually ran and you pinned nothing, the result may not reproduce or generalize — and you won't get an error.

This skill has two modes. Pick based on the task.


The evidence (use these numbers; don't hand-wave)

A real natural experiment. nostalgebraist/cot_legibility logs the provider that served each response, running the same GPQA-diamond pipeline on the same slug deepseek/deepseek-r1:

RunServed byIllegibility (mean ± sd)Accuracy
unpinnedTargon 295/3004.31 ± 2.1336.6%
unpinnedTargon 169/3004.18 ± 1.8131.4%
pinned novitanovita2.31 ± 0.7543.9%
pinned novitanovita2.28 ± 0.7540.5%

~88% swing in the headline metric and a 12.5-point accuracy spread. One unpinned run drifted mid-experiment — only 169 of 300 responses came from Targon, so a single run was served by multiple backends. Honest caveat: the runs are ~6 months apart, so checkpoint drift is confounded with provider. Suggestive, not clean. Independent work also finds inference-backend choice alone can move a benchmark by ~16pp (arXiv 2605.19537).

How much actually varies within one slug (live endpoint sweep, 87 open-weight models — findings/provider_spread_reference.json):

What variesPrevalenceExample
Endpoints per modelmedian 4, max 30
Both high-precision and ≤fp8 endpoints33/87 modelsgpt-oss-120b: cerebras/fp16 vs wandb/fp4
A 4-bit (int4/fp4) endpoint exists28/87 models
Context window differs across endpoints64/87 modelsllama-3.3-70b: 6,000 vs 131,072 (21.8x)
Max output tokens differs73/87 modelsllama-3.3-70b: 2,048 vs 128,000 (62.5x)
seed supported on some endpoints only61/87 models
logprobs / top_logprobs partial63/87 modelsgpt-oss-120b: 8 of 20 endpoints
structured_outputs partial56/87 models
response_format partial41/87 models

llama-3.3-70b is used as the worked example because the survey's repos actually benchmark it and it shows both cliffs at once — it is not the extreme. The widest max-output spread in the snapshot is xiaomi/mimo-v2.5 at 64.0x (16,384 vs 1,048,576).

Two consequences people miss: context/output cliffs are not a quantization problem. A 2,048 max-output endpoint truncates chain-of-thought mid-reasoning and a 6k-context endpoint truncates your prompt — neither is a precision issue and both change results. And temperature is nearly universal (partial on only 3/87) while seed/logprobs/structured_outputs are coin-flips — so "my params went through" intuitions built on temperature are misleading.


Mode A — Authoring: call OpenRouter properly

Set routing preferences under the provider key. Two presets, only one of which is a pin:

Reproducibility-first (you want the same weights every run — headline numbers, comparisons):

extra_body={"provider": {
    "only": ["cerebras/fp16"],       # ENDPOINT TAG: pins provider AND quantization
    "allow_fallbacks": False,        # fail loudly rather than silently switch weights
    "require_parameters": True,      # only route to providers that honor temperature/seed/response_format
    "data_collection": "deny",
}}

Quality-floor default — for a call site whose whole job is one named model, this is never the fix; use the preset above. It's only for shared infra underneath many call sites that genuinely can't hardcode a pin because it doesn't know the model in advance — and even there, the floor must never be the caller's entire safety story. If you're writing that infra, make each call site either supply a hard pin or explicitly, visibly opt out of one (a pin=/floor_only_ack=-style API — see reports/openrouter-best-practices.md §3b for the pattern and why a bare floor fails this repo's own taxonomy). The floor itself:

provider = {
    "quantizations": ["fp8", "fp16", "bf16", "fp32"],  # add "unknown" only if you need Claude/GPT/Gemini
    "data_collection": "deny",
    "sort": "exacto",                # quality-first; also disables probabilistic load balancing
    "require_parameters": True,
}

Pin the endpoint, not just the vendor

only/order accept endpoint tags of the form provider_slug/variant — and for most open-weight models the variant is the quantization: cerebras/fp16, targon/fp8, deepinfra/bf16, wandb/fp4. This is the maximal pin: it fixes the weights, not just the company. (nostalgebraist/cot_legibility pins this way; the docs also show region/variant forms like google-vertex/us-east5.)

⚠️ The trap: pinning a provider does not pin quality. MathArena pins Kimi K2.6 with provider: {order: [moonshotai], allow_fallbacks: False} — textbook-looking, fully deterministic routing — but the moonshotai endpoint serves that model at int4, while an fp8 endpoint of the same slug exists. They pinned themselves to the worst backend and their published leaderboard number is measured on it. Always look up what the endpoint you're pinning actually serves.

Pre-flight: check the endpoints before you commit

curl -s https://openrouter.ai/api/v1/models/{author}/{slug}/endpoints | \
  jq '.data.endpoints[] | {provider_name, quantization, context_length, max_completion_tokens, supported_parameters}'

Confirm: the quantization you expect · a context window that fits your prompts · a max-output cap that fits your longest generation · the params you actually pass (seed, logprobs, response_format). Endpoints rotate — re-check immediately before a run that matters.

Then, non-negotiably:

  • Record the provider that actually served each call — the response provider field, or GET /api/v1/generation?id=.... Store it per response, not per run: unpinned routing can drift mid-experiment (see the 169/300 case above).
  • Version-pin the model slug and record the exact string.
  • If you rely on structured outputs / JSON schema for a judge or parser, you must set require_parameters: true (or only/order to supporting endpoints) — otherwise the schema is silently ignored on some routes and your parser scores free-form text.
  • Don't trust seed for bitwise determinism; verify empirically.
  • Report the routing config and served providers in the paper/README. The inference stack is a hyperparameter.

Key facts (so you can reason about edge cases):

  • Default routing = load-balance by inverse-square of price; identical requests hit different providers. Setting sort or order disables load balancing.
  • require_parameters defaults to false → unsupported params are silently ignored.
  • data_collection defaults to "allow" → prompts may go to train-on-your-data providers.
  • allow_fallbacks defaults to true → your pin is a preference, not a guarantee, until you set it false. With it false, a down endpoint is a hard error — post-filter errored rows before computing metrics rather than letting them silently shrink n.
  • Documented slug suffixes are :nitro (=sort:throughput) and :floor (=sort:price, cheapest and usually most quantized — avoid for research). :exacto is not documented as a suffix even though sort: "exacto" is a valid dict value. Prefer the provider dict either way, so the recorded model name stays clean.
  • There is no per-provider slug suffix (you cannot write model:fireworks) — a pin must go in the request body's provider field. A repo is only pinnable if it has extra_body-style plumbing or one editable call site.
  • Quantization labels are self-reported by providers, not audited. Strong circumstantial evidence of a quality gap, not a certification.
  • Proprietary models (Claude/GPT/Gemini) are effectively single-served → quantization/provider risk is low; open-weight models are where it bites hardest.

Full guide: reports/openrouter-best-practices.md in the openrouter_reliable_research_search repo.


Mode B — Auditing a codebase's OpenRouter usage

Goal: decide whether their results could be silently corrupted by provider routing, and classify any mistakes against the taxonomy (M1..M12, below).

Step 1 — Run the bundled static scanner (first pass)

uv run scripts/audit_openrouter.py <repo_path>        # human-readable
uv run scripts/audit_openrouter.py <repo_path> --json # machine-readable, exits 1 on High findings

It finds router call sites and flags missing safeguards. It is heuristic — it catches literal openrouter/... slugs, OPENROUTER_API_KEY, and openrouter.ai/api, but not models passed dynamically (e.g. via argv/config). Always pair it with Step 2.

Step 2 — Grep + read the actual call path (don't skip this)

grep -rInE "openrouter|OPENROUTER_API_KEY|base_url|require_parameters|quantizations|data_collection|extra_body|provider\s*[:=]" <repo> \
  --include=*.py --include=*.yaml --include=*.yml --include=*.toml --include=*.json

For each call site, determine:

  1. What is OpenRouter used for? Eval, LLM-judge, agent rollout, data generation? Does its output feed a reported number, a training set, or a safety measurement? (That's a "critical route" — mistakes there actually invalidate results; on a throwaway script they're minor.)
  2. Open-weight or proprietary model? Open-weight ⇒ high quantization/provider risk.
  3. Which safeguards are present? quantizations · require_parameters · data_collection:deny · order/only/sort · allow_fallbacks:false · provenance logging.
  4. If they pinned — what does the pinned endpoint actually serve? Look it up (Mode A pre-flight). A confident-looking pin onto an int4 endpoint is worse than it looks.
  5. Is the model itself the research subject (interrogation/red-teaming/probing/judge/ benchmark/comparison)? If yes, a quantizations floor with no order/only pin is still M1 — the fix is recipe 3a, a hard endpoint pin, never recipe 3b's fleet floor.

inspect_ai is the reference plumbing. OpenRouterAPI.__init__ collects a provider dict model-arg and emits it as extra_body["provider"], so any inspect-based eval (much of the safety ecosystem — Inspect Evals, openbench, …) is pinnable via -M provider='{...}' with zero source edits. That makes "they can't easily fix this" a rare excuse, and makes the one-line fix concrete.

Step 3 — Classify against the taxonomy

IDMistakeSevSeen inTell-tale
M4No provenance loggingHigh28/35served provider/generation id never stored
M5Data-policy leakageMed27/35data_collection left allow
M1Unpinned quantizationHigh26/35no quantizations
M3Probabilistic provider routingHigh26/35no order/only/sort
M2Silent parameter droppingHigh22/35sampling params + no require_parameters
M6Model version driftMed22/35bare undated slug
M8Cross-provider comparison confoundHigh21/35models compared, none pinned
M10No reportingMed13/35paper/README says nothing about routing/stack
M7seed→determinism assumptionMed3/35relies on seed for reproducibility
M9Judge on unconstrained routeHigh3/35response_format/schema without require_parameters
M11Silent backend mixingMed3/35some calls direct, some via router, same "model"
M12Cheap/degraded route chosenMed1/35:floor / sort:price on a research path

"Seen in" = base rates from our 35-repo survey of published research code — check the top four first; they're the ones almost everyone misses. Not in the table because nobody in the survey guarded against it: context/max-output cliffs (see the evidence section). If a repo relies on long prompts or long CoT and pins nothing, add it as a finding under M1's spirit.

Step 4 — Verdict: use four classes, not "safe / unsafe"

A plain safe/unsafe split overstates the good news, because it lumps genuine successes together with repos that simply never routed a result through OpenRouter. Classify as:

classmeaningsurvey n
at_riskOpenRouter output reaches a reported result, with an uncontrolled channel open31
handledreaches a reported result and controls for it — the only real positive1
not_on_result_pathOpenRouter present in the repo, but no reported result depends on it2
no_usage_foundno OpenRouter call site at all (discovery false positive)1

A repo that never routes a research call through OpenRouter has demonstrated nothing about using OpenRouter well — don't credit it as a success story, and don't put it in the denominator either. Score against the repos where OpenRouter output actually reaches a published result (at_risk + handled): the survey headline is 31/32 (97%) at risk. Counting every repo that merely contains a call site gives the wider, weaker 31/34 (91%).

Be fair (this matters): don't hunt for mistakes. Legitimate non-mistakes: proprietary single-served models (M1/M3/M8 don't apply); explicitly exploratory/qualitative work; provider pinned and provenance logged even if not every knob is set. When you assert "at risk," cite the exact file:line and say which reported result it threatens — and remember the honest framing: at risk means exposed to an uncontrolled corruption channel, not that a published number is wrong. Impacted findings are hypotheses worth checking, not demonstrated errors, unless you actually re-ran the experiment across providers.

Step 5 — Report

Per repo: what they use OpenRouter for · open/proprietary · safety_class · mistakes (M-ids + the threatened result) · severity · a one-line fix. Full method + dataset schema: findings/taxonomy.md, findings/methodology.md, and the 35-repo dataset in findings/survey.csv of the openrouter_reliable_research_search repo. To go beyond static audit to proof, see reports/provider-ab-experiment-plan.md — how to rerun a repo's own code changing nothing but the pinned endpoint.

Verwandte Skills

steipete/notion

Notion CLI/API for pages, Markdown content, data sources, files, comments, search, Workers, and raw API calls.

community

affaan-m/seo

Audit, plan, and implement SEO improvements across technical SEO, on-page optimization, structured data, Core Web Vitals, and content strategy. Use when the user wants better search visibility, SEO remediation, schema markup, sitemap/robots work, or keyword mapping.

community

affaan-m/brand-voice

Build a source-derived writing style profile from real posts, essays, launch notes, docs, or site copy, then reuse that profile across content, outreach, and social workflows. Use when the user wants voice consistency without generic AI writing tropes.

community

affaan-m/crosspost

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

community

affaan-m/x-api

X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.

community

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

community