fix(providers): preserve openrouter/ prefix for native models (#12942)

* fix(providers): preserve openrouter/ prefix for native models (#12924)

OpenRouter-native models like 'openrouter/aurora-alpha' need the full
'openrouter/<name>' as the model ID in API requests. The existing
parseModelRef() stripped the prefix, sending just 'aurora-alpha'
which OpenRouter rejects with 400.

Fix: normalizeProviderModelId() now re-adds the 'openrouter/' prefix
for models without a slash (native models), while passing through
external provider models (e.g. 'anthropic/claude-sonnet-4-5') as-is.

Closes #12924

* Changelog: add OpenRouter note for #12942

---------

Co-authored-by: Luna AI <luna@coredirection.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Omair Afzal
2026-02-22 22:08:46 +05:00
committed by GitHub
parent 4cad674387
commit 3891ba4bb5
3 changed files with 22 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Providers/OpenRouter: preserve the required `openrouter/` prefix for OpenRouter-native model IDs during model-ref normalization. (#12942) Thanks @omair445.
- Telegram/Webhook: keep webhook monitors alive until gateway abort signals fire, preventing false channel exits and immediate webhook auto-restart loops.
- Telegram/Polling: retry recoverable setup-time network failures in monitor startup and await runner teardown before retry to avoid overlapping polling sessions.
- Telegram/Polling: clear Telegram webhooks (`deleteWebhook`) before starting long-poll `getUpdates`, including retry handling for transient cleanup failures.

View File

@@ -83,6 +83,20 @@ describe("model-selection", () => {
expect(parseModelRef(" ", "anthropic")).toBeNull();
});
it("should preserve openrouter/ prefix for native models", () => {
expect(parseModelRef("openrouter/aurora-alpha", "openai")).toEqual({
provider: "openrouter",
model: "openrouter/aurora-alpha",
});
});
it("should pass through openrouter external provider models as-is", () => {
expect(parseModelRef("openrouter/anthropic/claude-sonnet-4-5", "openai")).toEqual({
provider: "openrouter",
model: "anthropic/claude-sonnet-4-5",
});
});
it("should handle invalid slash usage", () => {
expect(parseModelRef("/", "anthropic")).toBeNull();
expect(parseModelRef("anthropic/", "anthropic")).toBeNull();

View File

@@ -111,6 +111,13 @@ function normalizeProviderModelId(provider: string, model: string): string {
if (provider === "google") {
return normalizeGoogleModelId(model);
}
// OpenRouter-native models (e.g. "openrouter/aurora-alpha") need the full
// "openrouter/<name>" as the model ID sent to the API. Models from external
// providers already contain a slash (e.g. "anthropic/claude-sonnet-4-5") and
// are passed through as-is (#12924).
if (provider === "openrouter" && !model.includes("/")) {
return `openrouter/${model}`;
}
return model;
}