Skip to content

fix(scripts): sanitize tilde prefix in OpenRouter alias model constant names#574

Open
AlemTuzlak wants to merge 2 commits into
mainfrom
fix/sync-models-skip-openrouter-aliases
Open

fix(scripts): sanitize tilde prefix in OpenRouter alias model constant names#574
AlemTuzlak wants to merge 2 commits into
mainfrom
fix/sync-models-skip-openrouter-aliases

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented May 18, 2026

Summary

The daily Sync Model Metadata workflow has been failing for several weeks.

Example: https://github.com/TanStack/ai/actions/runs/26025311706/job/76496996066

Root cause

OpenRouter started returning ~16 model IDs prefixed with ~ to denote unstable routing aliases (e.g. ~anthropic/claude-haiku-latest). The model-meta generator (scripts/convert-openrouter-models.ts) transformed those IDs into JS identifiers verbatim, producing:

const ~ANTHROPIC_CLAUDE_HAIKU_LATEST = {
  id: '~anthropic/claude-haiku-latest',}

Not a valid identifier. The file got written, then the next workflow step (pnpm format → prettier) bailed with SyntaxError: Variable declaration expected. (4:7) and the whole sync step failed.

Fix

  • isRoutingAlias(id) recognises the ~ prefix.
  • convertModels filters routing aliases out before they reach the generator. They don't belong in static model-meta.ts anyway — the alias resolves to whatever the current canonical model is, and we'd never know if it changed between sync runs.
  • generateModelMetaString now throws a clear, actionable error if a generated constant name isn't a valid JS identifier. Future OpenRouter ID quirks will fail loudly at generation time instead of writing invalid TS that breaks later in the pipeline.

The provider-specific sync (sync-provider-models.ts) was unaffected because it filters by prefix/ (e.g. anthropic/) which doesn't match ~anthropic/.

Test plan

  • Re-run the Sync Model Metadata workflow (workflow_dispatch) on this branch — should complete green and either no-op or open the automated/sync-models PR.
  • Verified locally: pnpm regenerate:models && pnpm exec prettier --check packages/typescript/ai-openrouter/src/model-meta.ts both succeed. The script logs Skipped N OpenRouter routing-alias model(s) so the filter's behaviour is visible.

Summary by CodeRabbit

  • Bug Fixes
    • Sanitized routing-alias model IDs (those starting with "~") so generated metadata uses safe identifiers.
    • Added validation to detect and block invalid model identifiers in generated metadata, preventing emission of malformed output and improving build reliability.

Review Change Stack

@AlemTuzlak AlemTuzlak requested a review from a team as a code owner May 18, 2026 11:58
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

generateModelMetaString now maps OpenRouter routing-alias ~ to _ when deriving constant names and adds a validation check that throws if the sanitized name is not a valid JavaScript identifier.

Changes

OpenRouter Routing-Alias Handling

Layer / File(s) Summary
Constant-name derivation including ~ remap
scripts/convert-openrouter-models.ts
Sanitizes model IDs when building constant identifiers and replaces ~ with _ to handle OpenRouter routing-alias IDs in derived constant names.
Identifier validation safety net
scripts/convert-openrouter-models.ts
Adds a regex check after sanitization that throws an error including the invalid constant name and the originating model.id if the identifier does not match JavaScript identifier rules.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I’m a rabbit with a tiny quill,
I turn tildes into underscores with skill,
I check each name before it’s free,
So constants hop safe in your tree,
🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description thoroughly explains the root cause, the fix, and test plan. However, the checklist items are incomplete: the Contributing guide step and test verification boxes are unchecked, and the Release Impact items lack clarity on whether a changeset was generated. Complete the checklist by checking off items or confirming test execution and changeset status. Ensure all required steps have been verified before merge.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main fix: sanitizing tilde prefixes in OpenRouter alias model constant names to prevent invalid JavaScript identifiers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sync-models-skip-openrouter-aliases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 18, 2026

View your CI Pipeline Execution ↗ for commit b0ae8f6

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 49s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-18 13:14:03 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

🚀 Changeset Version Preview

No changeset entries found. Merging this PR will not cause a version bump for any packages.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 18, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@574

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@574

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@574

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@574

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@574

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@574

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@574

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@574

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@574

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@574

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@574

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@574

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@574

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@574

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@574

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@574

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@574

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@574

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@574

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@574

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@574

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@574

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@574

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@574

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@574

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@574

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@574

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@574

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@574

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@574

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@574

commit: b0ae8f6

…t names

The daily sync-models workflow has been failing for several weeks. Example:
https://github.com/TanStack/ai/actions/runs/26025311706/job/76496996066

OpenRouter started returning model IDs prefixed with `~` to denote routing
aliases (e.g. `~anthropic/claude-haiku-latest`, `~anthropic/claude-opus-latest`,
~16 in the current snapshot). These point to whatever the current canonical
model is for that slot and are valid models users can call.

The generator transformed the IDs into JavaScript identifiers verbatim,
producing `const ~ANTHROPIC_CLAUDE_HAIKU_LATEST = {…}` — not a valid
identifier. Prettier then bailed with `SyntaxError: Variable declaration
expected. (4:7)` and the workflow failed.

Fix:

- Map a leading `~` in the model ID to `_` only when deriving the constant
  name. The original ID is still emitted as a string literal on the meta
  object's `id` field (`id: '~anthropic/claude-haiku-latest'`), so users can
  pass the exact ID to `chat({ model: '~anthropic/claude-haiku-latest' })`
  and the constant list (`OPENROUTER_CHAT_MODELS`) references the alias by
  that same ID.
- Add a regex check inside `generateModelMetaString` that throws with a
  clear, actionable error if a generated constant name isn't a valid JS
  identifier. Future OpenRouter ID quirks will fail loudly at generation
  time instead of writing invalid TS that breaks later in the pipeline.

Verified locally: `pnpm regenerate:models` now produces valid identifiers
like `_ANTHROPIC_CLAUDE_HAIKU_LATEST` with the original `'~anthropic/...'`
strings intact, and `pnpm exec prettier --write` on the generated file
succeeds.
@AlemTuzlak AlemTuzlak force-pushed the fix/sync-models-skip-openrouter-aliases branch from 07cd914 to f1bd255 Compare May 18, 2026 12:03
@AlemTuzlak AlemTuzlak changed the title fix(scripts): skip OpenRouter routing-alias model IDs in sync workflow fix(scripts): sanitize tilde prefix in OpenRouter alias model constant names May 18, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/convert-openrouter-models.ts (1)

284-286: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Routing-alias models are still included in generated output.

convertModels still processes every model, so ~-prefixed routing aliases will remain in static model-meta.ts (just renamed), which defeats the “skip unstable aliases” objective.

Suggested fix
 function convertModels(models: Array<OpenRouterModel>): string {
-  const modelStrings = models.map(generateModelMetaString)
+  const modelStrings = models
+    .filter((model) => !model.id.startsWith('~'))
+    .map(generateModelMetaString)
   return modelStrings.join('\n')
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/convert-openrouter-models.ts` around lines 284 - 286, convertModels
is still including routing-alias models (those prefixed with '~'), so filter
them out before mapping; update convertModels to first filter the input models
array (e.g., models.filter(m => !(m.id?.startsWith('~') ||
m.name?.startsWith('~')))) and then map the remaining models with
generateModelMetaString so the generated output omits unstable routing-alias
entries; reference convertModels and generateModelMetaString and ensure the
predicate checks the model identifier fields (id/name) that carry the '~'
prefix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/convert-openrouter-models.ts`:
- Around line 284-286: convertModels is still including routing-alias models
(those prefixed with '~'), so filter them out before mapping; update
convertModels to first filter the input models array (e.g., models.filter(m =>
!(m.id?.startsWith('~') || m.name?.startsWith('~')))) and then map the remaining
models with generateModelMetaString so the generated output omits unstable
routing-alias entries; reference convertModels and generateModelMetaString and
ensure the predicate checks the model identifier fields (id/name) that carry the
'~' prefix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ab2f9af4-5d38-43d3-aa0c-2f7ea9d70bb3

📥 Commits

Reviewing files that changed from the base of the PR and between 07cd914 and f1bd255.

📒 Files selected for processing (1)
  • scripts/convert-openrouter-models.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant