Files
OmniRoute/open-sse/services/wildcardRouter.ts
diegosouzapw 33dfbf0177 refactor: harden open-sse services, eliminate any casts, add dashboard pages
- Replace all `as any` casts in MCP advancedTools with typed helpers (toRecord, toString, toNumber)
- Harden open-sse services: rateLimitManager, sessionManager, usage, roleNormalizer, signatureCache, comboMetrics
- Improve responseSanitizer and responseTranslator type safety
- Remove deprecated openai-responses request translator
- Add dashboard pages: /a2a, /mcp, /auto-combo with live data
- Improve error/loading/not-found pages with consistent design
- Add root loading.tsx and typecheck tsconfig variants
- Add check-t11-any-budget.mjs audit script
2026-03-04 19:38:34 -03:00

118 lines
3.4 KiB
TypeScript

/**
* Wildcard Model Routing — Phase 8
*
* Glob-style wildcard matching for model aliases with specificity ranking.
* Integrates into the existing model resolution pipeline.
*/
/**
* Match a model name against a pattern with glob wildcards.
* Supports * (wildcard sequence) and ? (single char).
*
* @param {string} model - Model name to match
* @param {string} pattern - Pattern with wildcards
* @returns {boolean}
*/
export function wildcardMatch(model, pattern) {
if (!model || !pattern) return false;
if (pattern === "*") return true;
if (pattern === model) return true;
// Convert glob pattern to regex
const regexStr = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
const regex = new RegExp(`^${regexStr}$`, "i");
return regex.test(model);
}
/**
* Calculate specificity score for a pattern.
* Higher score = more specific match.
* Used to rank multiple matching patterns.
*
* @param {string} pattern
* @returns {number} Specificity score (higher = more specific)
*/
export function getSpecificity(pattern) {
if (!pattern) return 0;
let score = 0;
// Exact segments (no wildcards) contribute most
const segments = pattern.split(/[/*?]/);
for (const seg of segments) {
if (seg.length > 0) score += seg.length * 10;
}
// Wildcards reduce specificity
const wildcardCount = (pattern.match(/\*/g) || []).length;
const questionCount = (pattern.match(/\?/g) || []).length;
score -= wildcardCount * 50;
score -= questionCount * 5;
// Longer patterns tend to be more specific
score += pattern.length;
return score;
}
/**
* Find the best matching alias for a model name from a list of alias entries.
* Returns the most specific match.
*
* @param {string} model - Model name to resolve
* @param {Array<{ pattern: string, target: string, [key: string]: unknown }>} aliases - Alias entries
* @returns {{ pattern: string, target: string, specificity: number } | null}
*/
export function resolveWildcardAlias(model, aliases) {
if (!model || !aliases || !Array.isArray(aliases)) return null;
const matches = [];
for (const alias of aliases) {
const pattern = alias.pattern || alias.alias || alias.from;
const target = alias.target || alias.model || alias.to;
if (!pattern || !target) continue;
if (wildcardMatch(model, pattern)) {
matches.push({
pattern,
target,
specificity: getSpecificity(pattern),
...alias,
});
}
}
if (matches.length === 0) return null;
// Sort by specificity (highest first)
matches.sort((a, b) => b.specificity - a.specificity);
return matches[0];
}
/**
* Resolve model through wildcard aliases, falling back to exact match.
* Can be integrated into existing model resolution.
*
* @param {string} model - Requested model name
* @param {Map|Object} exactAliases - Exact model alias map
* @param {Array} wildcardAliases - Wildcard pattern aliases
* @returns {string} Resolved model name
*/
export function resolveModel(model, exactAliases = {}, wildcardAliases = []) {
if (!model) return model;
// 1. Check exact aliases first (fastest)
if (exactAliases instanceof Map) {
if (exactAliases.has(model)) return exactAliases.get(model);
} else if (exactAliases[model]) {
return exactAliases[model];
}
// 2. Check wildcard aliases
const match = resolveWildcardAlias(model, wildcardAliases);
if (match) return match.target;
// 3. Return original
return model;
}