Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions open-sse/services/wildcardRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// @ts-nocheck
/**
* 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, "\\$&")

Check warning on line 24 in open-sse/services/wildcardRouter.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

`String.raw` should be used to avoid escaping `\`.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xv00wtKe1XOcOu&open=AZ8h80xv00wtKe1XOcOu&pullRequest=189
.replace(/\*/g, ".*")

Check warning on line 25 in open-sse/services/wildcardRouter.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xv00wtKe1XOcOv&open=AZ8h80xv00wtKe1XOcOv&pullRequest=189
.replace(/\?/g, ".");

Check warning on line 26 in open-sse/services/wildcardRouter.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xv00wtKe1XOcOw&open=AZ8h80xv00wtKe1XOcOw&pullRequest=189
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: string,
aliases: WildcardAliasEntry[]
): ResolvedWildcardAlias | null {
if (!model || !aliases || !Array.isArray(aliases)) return null;

const matches: ResolvedWildcardAlias[] = [];
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;
}
type WildcardAliasEntry = {
pattern?: string;
alias?: string;
from?: string;
target?: string;
model?: string;
to?: string;
[key: string]: unknown;
};

type ResolvedWildcardAlias = WildcardAliasEntry & {
pattern: string;
target: string;
specificity: number;
};
13 changes: 13 additions & 0 deletions open-sse/utils/cors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Static CORS headers for open-sse handlers.
*
* `Access-Control-Allow-Origin` is set by the Next.js middleware
* (`src/server/cors/origins.ts`). Handlers in this package only need the
* methods/headers list; the middleware overlays the allowed origin per
* the central allowlist on the way out.
*/
export const CORS_HEADERS: Record<string, string> = {
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"Content-Type, Authorization, x-api-key, anthropic-version, x-omniroute-connection, x-internal-test, accept",
};
54 changes: 54 additions & 0 deletions src/lib/api/errorResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { randomUUID } from "crypto";

Check warning on line 1 in src/lib/api/errorResponse.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:crypto` over `crypto`.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xD00wtKe1XOcOp&open=AZ8h80xD00wtKe1XOcOp&pullRequest=189

export type ApiErrorType = "invalid_request" | "not_found" | "conflict" | "server_error";

interface ApiErrorPayload {
status: number;
message: string;
type?: ApiErrorType;
details?: unknown;
}

export function createErrorResponse(payload: ApiErrorPayload): Response {
const requestId = randomUUID();
const resolvedType =
payload.type ||
(payload.status >= 500
? "server_error"
: payload.status === 404
? "not_found"
: payload.status === 409
? "conflict"
: "invalid_request");

Check warning on line 22 in src/lib/api/errorResponse.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xD00wtKe1XOcOq&open=AZ8h80xD00wtKe1XOcOq&pullRequest=189

Check warning on line 22 in src/lib/api/errorResponse.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80xD00wtKe1XOcOr&open=AZ8h80xD00wtKe1XOcOr&pullRequest=189

return Response.json(
{
error: {
message: payload.message,
type: resolvedType,
details: payload.details,
},
requestId,
},
{ status: payload.status }
);
}

export function createErrorResponseFromUnknown(
error: unknown,
fallbackMessage = "Unexpected server error"
): Response {
const anyError = error as {
message?: string;
status?: number;
type?: ApiErrorType;
details?: unknown;
};
const status = Number(anyError?.status) || 500;
return createErrorResponse({
status,
message: typeof anyError?.message === "string" ? anyError.message : fallbackMessage,
type: anyError?.type,
details: anyError?.details,
Comment on lines +50 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sanitize unknown errors before returning them

When routes catch an internal exception and call createErrorResponseFromUnknown() (for example the proxy/deploy settings routes), this path returns error.message and error.details verbatim to the client. For 5xx unknown errors those strings can contain stack frames, local paths, upstream response bodies, or credentials, so this helper should use the existing sanitized error body/message path or fall back to the supplied generic message instead of echoing raw exception fields.

Useful? React with 👍 / 👎.

});
}
112 changes: 112 additions & 0 deletions src/lib/db/compressionCacheStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { getDbInstance } from "./core";

export interface CacheStatsEntry {
provider: string;
model?: string;
compressionMode: string;
cacheControlPresent: boolean;
estimatedCacheHit: boolean;
tokensSavedCompression: number;
tokensSavedCaching: number;
netSavings: number;
}

export interface CacheStatsSummary {
totalRequests: number;
avgNetSavings: number;
cacheHitRate: number;
byProvider: Record<string, { count: number; avgNetSavings: number; cacheHitRate: number }>;
}

export function recordCacheStats(entry: CacheStatsEntry): void {
const db = getDbInstance();

const sql = `INSERT INTO compression_cache_stats (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the compression_cache_stats schema

This restored DB module assumes compression_cache_stats already exists, but the commit does not restore a migration/table definition for it; checking src/lib/db/core.ts and src/lib/db/migrations shows no CREATE TABLE for this table. On a fresh or upgraded DB, the chat compression hook drops every stats write with no such table, and getCacheStatsSummary() callers such as the MCP compression tool can fail outright, so the matching migration needs to be restored with the module.

Useful? React with 👍 / 👎.

provider,
model,
compression_mode,
cache_control_present,
estimated_cache_hit,
tokens_saved_compression,
tokens_saved_caching,
net_savings
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;

db.prepare(sql).run(
entry.provider,
entry.model ?? "",
entry.compressionMode,
entry.cacheControlPresent ? 1 : 0,
entry.estimatedCacheHit ? 1 : 0,
entry.tokensSavedCompression,
entry.tokensSavedCaching,
entry.netSavings
);
}

export function getCacheStatsSummary(since?: Date): CacheStatsSummary {
const db = getDbInstance();
const whereClause = since ? "WHERE created_at >= ?" : "";

Check warning on line 49 in src/lib/db/compressionCacheStats.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "whereClause".

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80wM00wtKe1XOcOl&open=AZ8h80wM00wtKe1XOcOl&pullRequest=189
const params = since ? [since.toISOString()] : [];

Check warning on line 50 in src/lib/db/compressionCacheStats.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "params".

See more on https://sonarcloud.io/project/issues?id=KooshaPari_OmniRoute&issues=AZ8h80wM00wtKe1XOcOm&open=AZ8h80wM00wtKe1XOcOm&pullRequest=189

// Global aggregates
const globalRow = since
? (db
.prepare(
`SELECT COUNT(*) as totalRequests, AVG(net_savings) as avgNetSavings, SUM(estimated_cache_hit) * 1.0 / COUNT(*) as cacheHitRate FROM compression_cache_stats WHERE created_at >= ?`
)
.get(since.toISOString()) as
| { totalRequests: number; avgNetSavings: number; cacheHitRate: number }
| undefined)
: (db
.prepare(
`SELECT COUNT(*) as totalRequests, AVG(net_savings) as avgNetSavings, SUM(estimated_cache_hit) * 1.0 / COUNT(*) as cacheHitRate FROM compression_cache_stats`
)
.get() as
| { totalRequests: number; avgNetSavings: number; cacheHitRate: number }
| undefined);

if (!globalRow || globalRow.totalRequests === 0) {
return { totalRequests: 0, avgNetSavings: 0, cacheHitRate: 0, byProvider: {} };
}

// Per-provider aggregates
const providerRows = since
? (db
.prepare(
`SELECT provider, COUNT(*) as count, AVG(net_savings) as avgNetSavings, SUM(estimated_cache_hit) * 1.0 / COUNT(*) as cacheHitRate FROM compression_cache_stats WHERE created_at >= ? GROUP BY provider`
)
.all(since.toISOString()) as Array<{
provider: string;
count: number;
avgNetSavings: number;
cacheHitRate: number;
}>)
: (db
.prepare(
`SELECT provider, COUNT(*) as count, AVG(net_savings) as avgNetSavings, SUM(estimated_cache_hit) * 1.0 / COUNT(*) as cacheHitRate FROM compression_cache_stats GROUP BY provider`
)
.all() as Array<{
provider: string;
count: number;
avgNetSavings: number;
cacheHitRate: number;
}>);

const byProvider: Record<string, { count: number; avgNetSavings: number; cacheHitRate: number }> =
{};
for (const row of providerRows) {
byProvider[row.provider] = {
count: row.count,
avgNetSavings: row.avgNetSavings,
cacheHitRate: row.cacheHitRate,
};
}

return {
totalRequests: globalRow.totalRequests,
avgNetSavings: globalRow.avgNetSavings ?? 0,
cacheHitRate: globalRow.cacheHitRate ?? 0,
byProvider,
};
}
Loading
Loading