mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-19 13:42:09 +03:00
* feat(db): cc discovery alias gate storage + EXPOSE_CC_DISCOVERY_ALIASES flag
Adds the gate for claude/<provider>/<model> discovery-alias mirror ids on
the /v1/models catalog: a new runtime feature flag (env forces on and wins
over the dashboard DB override), per-provider and per-model "on"/"off"/null
overrides stored in key_value under the ccDiscoveryAliases namespace, and a
pure precedence resolver (model > provider > global). Catalog wiring is a
separate follow-up task; this only lands the gate + storage.
* feat(sse): synthesize claude/ discovery aliases for the model catalog
* feat(api): advertise cc discovery aliases on /v1/models behind the 3-level gate
* feat(sse): resolve claude/ discovery aliases on the request path
* fix(sse): import getComboByName from db/combos, not the localDb barrel
* fix(sse): cover custom-node prefixes and the Codex WS bridge in cc discovery alias resolution
* feat(dashboard): cc discovery alias toggles + flag-screen env warning
Adds the operator-facing UI/API layer for the Claude Code discovery-alias
gate (claude/<provider>/<model> mirror ids on /v1/models): REST endpoint
for provider/model overrides, a provider-detail card with 3-state
(inherit/on/off) toggles, an info button on the Claude Code tool card
linking to Feature Flags, and an env-source warning on the
EXPOSE_CC_DISCOVERY_ALIASES flag card when it's forced on via env.
* feat(api): cc discovery usage metrics
* fix(api): record cc alias metric in the production wrapper + atomic counter upsert
* docs: document cc discovery aliases (Claude Code guide + feature flag catalog)
* fix(sse): don't mirror built-in auto/* combos as discovery aliases (advertised-but-unroutable)
* i18n(vi): translate the discovery-alias strings instead of shipping placeholders
vi is the one locale with a strict "no internal missing markers" test, so the 17
__MISSING__ entries this branch added (the provider ccAlias panel, the info
button, the feature-flag description and the env warning) would have turned that
test red the moment the base itself was repaired. Translated, keeping every ICU
placeholder ({modelId}, {error}) and the literal claude/<provider>/<model> id
shape intact.
* chore(quality): raise the frozen caps this feature legitimately grows
catalog.ts 1615 -> 1639: the alias synthesis is wired into the catalog builder,
which is where the per-key-filtered list is assembled — the only place the mirror
entries can be appended after model hiding has been applied.
localDb.ts 808 -> 810: two re-export lines for the new ccDiscoveryAliases db
module, which is exactly what the "Adding a New DB Module" recipe prescribes.
* refactor(dashboard,api): keep the complexity ratchets flat
The feature added four cyclomatic violations and one cognitive one, which the
ratchets reject — the baseline only moves when a metric improves. Split the new
code instead:
- appendCcDiscoveryAliases: the four skip-guards become isMirrorableId().
- resolveCcDiscoveryAliasStripWith: alias parsing and gate resolution become
parseCcAliasTarget() and resolveGateFor(), replacing a chain of ternaries that
each re-tested isComboAlias.
- FeatureFlagCard: the env-precedence warning becomes its own component instead
of a conditional branch inside an already-large render.
- ProviderCcAliasSection: the loader moves to useCcAliasData(), and the override
list and add-row become ModelOverrideList / AddOverrideRow, bringing both
oversized functions back under the 80-line rule.
Behavior unchanged — the 74 discovery-alias tests pass untouched. Both ratchets
now sit exactly at baseline (2188 / 971).
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
/**
|
|
* db/ccDiscoveryAliases.ts — Claude Code discovery-alias gate.
|
|
*
|
|
* Gates `claude/<provider>/<model>` mirror ids on the /v1/models catalog so
|
|
* the Claude Code gateway's model discovery can list non-Claude models. This
|
|
* module only owns the gate (global flag + per-provider/per-model overrides
|
|
* + precedence resolver) — the catalog wiring itself lives elsewhere.
|
|
*
|
|
* Storage: `key_value` table, namespace `ccDiscoveryAliases`, following the
|
|
* established key_value pattern (see `db/models.ts::setModelAlias` /
|
|
* `db/paramFilters.ts`). Keys:
|
|
* - `provider:<providerId>` -> "on" | "off"
|
|
* - `model:<providerId>/<modelId>` -> "on" | "off"
|
|
* A missing key means "inherit" (null).
|
|
*
|
|
* Global enablement precedence (see `getCcAliasGlobalState`):
|
|
* env EXPOSE_CC_DISCOVERY_ALIASES ("1"|"true") > DB flag override > default
|
|
* (env wins over the dashboard DB override, matching the brief — this is the
|
|
* opposite precedence from the generic `resolveFeatureFlag` helper, which is
|
|
* why this module resolves the flag directly instead of reusing it).
|
|
*/
|
|
|
|
import { getFeatureFlagOverride } from "./featureFlags";
|
|
import { getDbInstance } from "./core";
|
|
|
|
const NAMESPACE = "ccDiscoveryAliases";
|
|
const FLAG_KEY = "EXPOSE_CC_DISCOVERY_ALIASES";
|
|
|
|
export type CcAliasSetting = "on" | "off" | null;
|
|
|
|
function providerKey(providerId: string): string {
|
|
return `provider:${providerId}`;
|
|
}
|
|
|
|
function modelKey(providerId: string, modelId: string): string {
|
|
return `model:${providerId}/${modelId}`;
|
|
}
|
|
|
|
function parseSetting(value: string | undefined): CcAliasSetting {
|
|
if (value === "on" || value === "off") return value;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Pure precedence resolver: model setting wins over provider setting, which
|
|
* wins over the global flag. `null`/`undefined` means "inherit" from the
|
|
* next level down.
|
|
*/
|
|
export function resolveCcAliasEnabled(opts: {
|
|
model?: CcAliasSetting;
|
|
provider?: CcAliasSetting;
|
|
global: boolean;
|
|
}): boolean {
|
|
if (opts.model === "on") return true;
|
|
if (opts.model === "off") return false;
|
|
if (opts.provider === "on") return true;
|
|
if (opts.provider === "off") return false;
|
|
return opts.global;
|
|
}
|
|
|
|
export function getCcAliasProviderSetting(providerId: string): CcAliasSetting {
|
|
const db = getDbInstance();
|
|
const row = db
|
|
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
|
.get(NAMESPACE, providerKey(providerId)) as { value: string } | undefined;
|
|
return parseSetting(row?.value);
|
|
}
|
|
|
|
export function setCcAliasProviderSetting(providerId: string, v: CcAliasSetting): void {
|
|
const db = getDbInstance();
|
|
const key = providerKey(providerId);
|
|
if (v === null) {
|
|
db.prepare("DELETE FROM key_value WHERE namespace = ? AND key = ?").run(NAMESPACE, key);
|
|
return;
|
|
}
|
|
db.prepare("INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
|
|
NAMESPACE,
|
|
key,
|
|
v
|
|
);
|
|
}
|
|
|
|
export function getCcAliasModelSetting(providerId: string, modelId: string): CcAliasSetting {
|
|
const db = getDbInstance();
|
|
const row = db
|
|
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
|
|
.get(NAMESPACE, modelKey(providerId, modelId)) as { value: string } | undefined;
|
|
return parseSetting(row?.value);
|
|
}
|
|
|
|
export function setCcAliasModelSetting(
|
|
providerId: string,
|
|
modelId: string,
|
|
v: CcAliasSetting
|
|
): void {
|
|
const db = getDbInstance();
|
|
const key = modelKey(providerId, modelId);
|
|
if (v === null) {
|
|
db.prepare("DELETE FROM key_value WHERE namespace = ? AND key = ?").run(NAMESPACE, key);
|
|
return;
|
|
}
|
|
db.prepare("INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
|
|
NAMESPACE,
|
|
key,
|
|
v
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Loads every provider/model override in a single query — intended for the
|
|
* catalog builder, which needs the full set rather than per-row lookups.
|
|
*/
|
|
export function getCcAliasSettingsBulk(): {
|
|
providers: Map<string, "on" | "off">;
|
|
models: Map<string, "on" | "off">;
|
|
} {
|
|
const db = getDbInstance();
|
|
const rows = db
|
|
.prepare("SELECT key, value FROM key_value WHERE namespace = ?")
|
|
.all(NAMESPACE) as Array<{ key: string; value: string }>;
|
|
|
|
const providers = new Map<string, "on" | "off">();
|
|
const models = new Map<string, "on" | "off">();
|
|
|
|
for (const row of rows) {
|
|
const setting = parseSetting(row.value);
|
|
if (setting === null) continue;
|
|
if (row.key.startsWith("provider:")) {
|
|
providers.set(row.key.slice("provider:".length), setting);
|
|
} else if (row.key.startsWith("model:")) {
|
|
models.set(row.key.slice("model:".length), setting);
|
|
}
|
|
}
|
|
|
|
return { providers, models };
|
|
}
|
|
|
|
function envForcesGlobalOn(): boolean {
|
|
const raw = process.env[FLAG_KEY];
|
|
return raw === "1" || raw === "true";
|
|
}
|
|
|
|
/**
|
|
* Resolves the effective global state, and where it came from.
|
|
* Precedence: env forces on > DB override > definition default.
|
|
*/
|
|
export function getCcAliasGlobalState(): { enabled: boolean; source: "env" | "db" | "default" } {
|
|
if (envForcesGlobalOn()) {
|
|
return { enabled: true, source: "env" };
|
|
}
|
|
|
|
const dbOverride = getFeatureFlagOverride(FLAG_KEY);
|
|
if (dbOverride !== undefined) {
|
|
return { enabled: dbOverride === "true" || dbOverride === "1", source: "db" };
|
|
}
|
|
|
|
return { enabled: false, source: "default" };
|
|
}
|
|
|
|
export function isCcAliasGlobalEnabled(): boolean {
|
|
return getCcAliasGlobalState().enabled;
|
|
}
|