-
Notifications
You must be signed in to change notification settings - Fork 0
fix(restore): restore 9 deleted files from git history #189
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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
|
||
| .replace(/\*/g, ".*") | ||
|
Check warning on line 25 in open-sse/services/wildcardRouter.ts
|
||
| .replace(/\?/g, "."); | ||
|
Check warning on line 26 in open-sse/services/wildcardRouter.ts
|
||
| 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; | ||
| }; | ||
| 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", | ||
| }; |
| 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
|
||
|
|
||
| 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
|
||
|
|
||
| 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, | ||
| }); | ||
| } | ||
| 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 ( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This restored DB module assumes 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
|
||
| const params = since ? [since.toISOString()] : []; | ||
|
Check warning on line 50 in src/lib/db/compressionCacheStats.ts
|
||
|
|
||
| // 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, | ||
| }; | ||
| } | ||
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.
When routes catch an internal exception and call
createErrorResponseFromUnknown()(for example the proxy/deploy settings routes), this path returnserror.messageanderror.detailsverbatim 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 👍 / 👎.