Repository navigation
fix(api): honor the Claude Code exposure toggle for compatible providers (#18517) #18522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
diegosouzapw
merged 8 commits into
cheaper-inference:release/v3.8.53
from
yourspraveen:fix/18517-cc-alias-provider-key
Nov 4, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e34362c
fix(api): honor the Claude Code exposure toggle for compatible provid…
yourspraveen 3df33d8
chore(changelog): add the #18522 fragment
yourspraveen c182a48
Merge remote-tracking branch 'upstream/release/v3.8.53' into fix/1851…
yourspraveen e24169c
Merge remote-tracking branch 'origin/release/v3.8.53' into rework/18522
diegosouzapw 57b34fc
fix(api): honor node-id alias toggles on the request path
diegosouzapw 9ea7b9d
Merge commit '0edd1ac47acbafadfd9e6f985bb47a5d795b6288' into rework/1…
diegosouzapw 5ab943f
Merge branch 'release/v3.8.53' into fix/18517-cc-alias-provider-key
diegosouzapw cb43595
Merge branch 'release/v3.8.53' into fix/18517-cc-alias-provider-key
diegosouzapw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| - **fix(api):** the "Expose in Claude Code" provider toggle now takes effect for OpenAI-compatible providers. The dashboard stores the setting under the provider's node id while `/v1/models` and the request-time `claude/<prefix>/<model>` resolver read it by the node's prefix, so no mirror was listed or routed ([#18522](https://github.com/cheaper-inference/OmniRoute/pull/18522), fixes [#18517](https://github.com/cheaper-inference/OmniRoute/issues/18517)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| /** | ||
| * #18517: the "Expose in Claude Code" toggle on a provider's dashboard page saves | ||
| * `provider:<[id]>` / `model:<[id]>/<model>` under the page's route id, which for an | ||
| * OpenAI-compatible provider is the node UUID. The catalog predicate looked the | ||
| * setting up by the entry's public prefix (`local/...`), so a toggle saved from the | ||
| * dashboard never matched and no `claude/<prefix>/<model>` mirror was listed. | ||
| */ | ||
|
|
||
| import assert from "node:assert/strict"; | ||
| import fs from "node:fs"; | ||
| import os from "node:os"; | ||
| import path from "node:path"; | ||
| import test from "node:test"; | ||
|
|
||
| const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-cc-alias-18517-")); | ||
| process.env.DATA_DIR = TEST_DATA_DIR; | ||
| process.env.DISABLE_SQLITE_AUTO_BACKUP = "true"; | ||
| delete process.env.EXPOSE_CC_DISCOVERY_ALIASES; | ||
|
|
||
| const core = await import("../../src/lib/db/core.ts"); | ||
| const ccAliases = await import("../../src/lib/db/ccDiscoveryAliases.ts"); | ||
| const { buildCcAliasPredicate } = await import("../../src/app/api/v1/models/ccAliasPredicate.ts"); | ||
| const { applyCatalogPostFilters } = await import("../../src/app/api/v1/models/catalogResponse.ts"); | ||
| const { resolveCcDiscoveryAliasStrip, resolveCcDiscoveryAliasStripWith } = | ||
| await import("../../src/lib/ccDiscoveryAliasResolve.ts"); | ||
| const { createProviderNode, deleteProviderNode } = await import("../../src/lib/db/providers.ts"); | ||
| const { invalidateDbCache } = await import("../../src/lib/db/readCache.ts"); | ||
|
|
||
| const NODE_ID = "openai-compatible-chat-3f2a9c1e-0000-4000-8000-000000000001"; | ||
|
|
||
| test.after(() => { | ||
| core.resetDbInstance(); | ||
| fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); | ||
| }); | ||
|
|
||
| test("provider toggle saved under a compatible node id matches the node's prefix", () => { | ||
| const predicate = buildCcAliasPredicate({ | ||
| global: false, | ||
| providers: new Map([[NODE_ID, "on"]]), | ||
| models: new Map(), | ||
| providerNodeIdsByPrefix: { local: NODE_ID }, | ||
| }); | ||
| assert.equal(predicate({ id: "local/qwen/qwen3.8-27b", owned_by: "local" }), true); | ||
| assert.equal(predicate({ id: "openai/gpt-5.6", owned_by: "openai" }), false); | ||
| }); | ||
|
|
||
| test("model toggle saved under a compatible node id matches the node's prefix", () => { | ||
| const predicate = buildCcAliasPredicate({ | ||
| global: true, | ||
| providers: new Map(), | ||
| models: new Map([[`${NODE_ID}/qwen/qwen3.8-27b`, "off"]]), | ||
| providerNodeIdsByPrefix: { local: NODE_ID }, | ||
| }); | ||
| assert.equal(predicate({ id: "local/qwen/qwen3.8-27b", owned_by: "local" }), false); | ||
| assert.equal(predicate({ id: "local/qwen/qwen3.8-9b", owned_by: "local" }), true); | ||
| }); | ||
|
|
||
| test("a setting keyed by the public prefix still wins over the resolved id", () => { | ||
| const predicate = buildCcAliasPredicate({ | ||
| global: false, | ||
| providers: new Map([ | ||
| ["local", "off"], | ||
| [NODE_ID, "on"], | ||
| ]), | ||
| models: new Map(), | ||
| providerNodeIdsByPrefix: { local: NODE_ID }, | ||
| }); | ||
| assert.equal(predicate({ id: "local/qwen/qwen3.8-27b", owned_by: "local" }), false); | ||
| }); | ||
|
|
||
| test("model-level setting under any key wins over a provider setting under any key", () => { | ||
| const predicate = buildCcAliasPredicate({ | ||
| global: false, | ||
| providers: new Map([["local", "on"]]), | ||
| models: new Map([[`${NODE_ID}/qwen/qwen3.8-27b`, "off"]]), | ||
| providerNodeIdsByPrefix: { local: NODE_ID }, | ||
| }); | ||
| assert.equal(predicate({ id: "local/qwen/qwen3.8-27b", owned_by: "local" }), false); | ||
| }); | ||
|
|
||
| test("catalog post-filter lists claude/<prefix>/ mirrors for a toggle saved by node id", async () => { | ||
| ccAliases.setCcAliasProviderSetting(NODE_ID, "on"); | ||
| const rows = [ | ||
| { id: "local/qwen/qwen3.8-27b", object: "model", owned_by: "local", root: "qwen/qwen3.8-27b" }, | ||
| ]; | ||
| const out = await applyCatalogPostFilters(new Request("http://localhost/v1/models"), rows, { | ||
| connections: [], | ||
| prefixMode: "dual", | ||
| aliasToProviderId: {}, | ||
| pricing: { providerNodeIdsByPrefix: { local: NODE_ID }, resolveComboTarget: () => null }, | ||
| }); | ||
| assert.ok( | ||
| out.some((m) => m.id === "claude/local/qwen/qwen3.8-27b"), | ||
| `expected a claude/ mirror, got ${JSON.stringify(out.map((m) => m.id))}` | ||
| ); | ||
| }); | ||
|
|
||
| const MIRROR_ID = "claude/local/qwen/qwen3.8-27b"; | ||
| const REAL_ID = "local/qwen/qwen3.8-27b"; | ||
|
|
||
| /** Injected lookups over plain maps: no DB, so only the gate's key choice is under test. */ | ||
| function resolverDeps( | ||
| settings: { | ||
| global?: boolean; | ||
| providers?: Record<string, "on" | "off">; | ||
| models?: Record<string, "on" | "off">; | ||
| }, | ||
| nodeIds: Array<[string, string]> = [["local", NODE_ID]] | ||
| ) { | ||
| return { | ||
| claudeModelIds: new Set<string>(), | ||
| isRegistryProvider: () => false, | ||
| customProviderPrefixes: new Set(["local", "constructor"]), | ||
| providerNodeIdsByPrefix: new Map(nodeIds), | ||
| getCombo: async () => null, | ||
| gateGlobal: () => settings.global ?? false, | ||
| gateProvider: (providerId: string) => settings.providers?.[providerId] ?? null, | ||
| gateModel: (providerId: string, modelId: string) => | ||
| settings.models?.[`${providerId}/${modelId}`] ?? null, | ||
| }; | ||
| } | ||
|
|
||
| test("resolver strips a mirror when the provider toggle is saved under the node id", async () => { | ||
| const result = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ providers: { [NODE_ID]: "on" } }) | ||
| ); | ||
| assert.deepEqual(result, { model: REAL_ID, stripped: true }); | ||
| }); | ||
|
|
||
| test("resolver honors a provider 'off' saved under the node id over the global flag", async () => { | ||
| const result = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ global: true, providers: { [NODE_ID]: "off" } }) | ||
| ); | ||
| assert.equal(result.stripped, false); | ||
| }); | ||
|
|
||
| test("resolver honors a model toggle saved under the node id", async () => { | ||
| const on = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ models: { [`${NODE_ID}/qwen/qwen3.8-27b`]: "on" } }) | ||
| ); | ||
| assert.deepEqual(on, { model: REAL_ID, stripped: true }); | ||
|
|
||
| const off = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ global: true, models: { [`${NODE_ID}/qwen/qwen3.8-27b`]: "off" } }) | ||
| ); | ||
| assert.equal(off.stripped, false); | ||
| }); | ||
|
|
||
| test("resolver keeps the catalog's precedence: prefix before node id, model before provider", async () => { | ||
| const prefixWins = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ providers: { local: "off", [NODE_ID]: "on" } }) | ||
| ); | ||
| assert.equal(prefixWins.stripped, false); | ||
|
|
||
| const modelWins = await resolveCcDiscoveryAliasStripWith( | ||
| MIRROR_ID, | ||
| resolverDeps({ | ||
| providers: { local: "on" }, | ||
| models: { [`${NODE_ID}/qwen/qwen3.8-27b`]: "off" }, | ||
| }) | ||
| ); | ||
| assert.equal(modelWins.stripped, false); | ||
| }); | ||
|
|
||
| test("resolver falls back to the prefix alone without a node map or for an unmapped prefix", async () => { | ||
| const { providerNodeIdsByPrefix: _omitted, ...withoutMap } = resolverDeps({ | ||
| providers: { [NODE_ID]: "on" }, | ||
| }); | ||
| const noMap = await resolveCcDiscoveryAliasStripWith(MIRROR_ID, withoutMap); | ||
| assert.equal(noMap.stripped, false); | ||
|
|
||
| // A client-supplied prefix naming an Object.prototype member must not reach the gate as a key. | ||
| const asked: string[] = []; | ||
| const deps = resolverDeps({}); | ||
| await resolveCcDiscoveryAliasStripWith("claude/constructor/x", { | ||
| ...deps, | ||
| gateProvider: (providerId: string) => { | ||
| asked.push(providerId); | ||
| return null; | ||
| }, | ||
| }); | ||
| assert.deepEqual(asked, ["constructor"]); | ||
| }); | ||
|
|
||
| test("catalog and request path agree for a toggle saved under a real node's id", async () => { | ||
| const node = await createProviderNode({ | ||
| type: "openai-compatible", | ||
| name: "Local", | ||
| prefix: "local", | ||
| baseUrl: "http://localhost:9999/v1", | ||
| apiType: "chat", | ||
| }); | ||
| invalidateDbCache(); | ||
| try { | ||
| ccAliases.setCcAliasProviderSetting(NODE_ID, null); | ||
| assert.equal((await resolveCcDiscoveryAliasStrip(MIRROR_ID)).stripped, false); | ||
|
|
||
| ccAliases.setCcAliasProviderSetting(node.id, "on"); | ||
| assert.deepEqual(await resolveCcDiscoveryAliasStrip(MIRROR_ID), { | ||
| model: REAL_ID, | ||
| stripped: true, | ||
| }); | ||
|
|
||
| ccAliases.setCcAliasProviderSetting(node.id, "off"); | ||
| assert.equal((await resolveCcDiscoveryAliasStrip(MIRROR_ID)).stripped, false); | ||
| } finally { | ||
| ccAliases.setCcAliasProviderSetting(node.id, null); | ||
| await deleteProviderNode(node.id); | ||
| invalidateDbCache(); | ||
| } | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve node-ID lookup for the
__proto__prefix.If an operator configures a compatible node with prefix
__proto__, the catalog's plain-object assignment does not store its node ID. This lookup then readsObject.prototypeinstead. With the global gate off, a provider toggle saved under the node ID cannot list its mirror, although the request resolver'sMapfinds the toggle. Use a null-prototype map insrc/app/api/v1/models/catalog.tsforproviderNodeIdByPrefix. The provider-node schema permits this prefix. (raw.githubusercontent.com)