OpenRouter added as third LLM provider, but tool-use format is broken and missing migration

idukeric wires up OpenRouter as an alternative to direct Claude and Gemini keys, with a cleaner generic `PUT /user/api-keys` route and frontend model selection. The direction is sound; the implementation has enough bugs that it needs a rewrite before use.

integrationinfrastructure

The design intent: store a single openrouter_api_key in the user profile, route any model whose ID starts with anthropic/ or openai/ (or contains "openrouter") through https://openrouter.ai/api/v1/chat/completions, and let users access anthropic/claude-3.5-sonnet, openai/gpt-4-turbo, and anthropic/claude-3-haiku without needing direct vendor keys. The PUT /user/api-keys route accepts a provider-keyed object, which is more extensible than separate per-provider endpoints.

The bugs are concrete. openrouter.ts sends system as a top-level field (Anthropic convention) instead of a system-role message inside the messages array as OpenRouter expects. Tool formatting is worse: toClaudeTools() shapes definitions in Anthropic's format, those get passed to OpenRouter's OpenAI-compatible function-calling endpoint, and then tool results come back as Anthropic-format {type: "tool_result", tool_use_id} blocks. Multi-turn tool loops will not function. There's also a typo - openrouterApikey vs openrouterApiKey - and GET /user/models is missing authentication.

The missing Supabase migration is the most immediate problem. The code reads user_profiles.openrouter_api_key but the column is never created. Any deployment applying this commit gets an error on the first profile fetch.

The package-lock changes - dropping react/react-dom/scheduler peer entries from the backend lockfile and stripping dev: true from ~25 @img native binaries on the frontend - look like environmental noise from a different npm install. Drop those hunks when cherry-picking.

So what Worth a look if you want the route design: `PUT /user/api-keys` with a provider map is the right abstraction. The OpenRouter client itself needs to be rewritten from scratch against the OpenAI-compatible schema - send `system` inside `messages`, use OpenAI function-calling format end-to-end, and add the missing migration. Don't import the current `openrouter.ts` as-is.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

1 commit from idukeric/mike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
7a3c47d3 Add OpenRouter model provider support Iain Duke-Richarde 2026-05-05 ↗ GitHub

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-137.md from inside the repo you want the changes in.

⬇ Download capture-thread-137.md