mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-08 00:02:20 +03:00
Release v3.8.24 (#3747)
Release v3.8.24 — see CHANGELOG.md [3.8.24] for the full notes and the PR description for the contributors hall. Integration of release/v3.8.24 into main.
This commit is contained in:
committed by
GitHub
parent
420d62b420
commit
76a07cf7a5
@@ -9,12 +9,21 @@
|
||||
*/
|
||||
|
||||
import { extractApiKey } from "@/sse/services/auth";
|
||||
import { getApiKeyMetadata, getComboByName, isModelAllowedForKey, getApiKeyById } from "@/lib/localDb";
|
||||
import {
|
||||
getApiKeyMetadata,
|
||||
getComboByName,
|
||||
isModelAllowedForKey,
|
||||
getApiKeyById,
|
||||
} from "@/lib/localDb";
|
||||
import { isDashboardSessionAuthenticated } from "./apiAuth";
|
||||
import { resolveComboForModel } from "@/lib/db/modelComboMappings";
|
||||
import { checkBudget } from "@/domain/costRules";
|
||||
import { checkTokenLimits } from "@omniroute/open-sse/services/tokenLimitCounter.ts";
|
||||
import { errorResponse, buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import {
|
||||
errorResponse,
|
||||
buildErrorBody,
|
||||
sanitizeErrorMessage,
|
||||
} from "@omniroute/open-sse/utils/error.ts";
|
||||
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
||||
import * as log from "@/sse/utils/logger";
|
||||
import { checkRateLimit, RateLimitRule } from "./rateLimiter";
|
||||
@@ -173,6 +182,45 @@ function matchesComboAccessRule(comboName: string, requestedModel: string, rule:
|
||||
);
|
||||
}
|
||||
|
||||
function isAnthropicMessagesRequest(request: Request): boolean {
|
||||
if (request.headers.has("anthropic-version")) return true;
|
||||
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
return url.pathname.endsWith("/v1/messages");
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function policyErrorResponse(
|
||||
request: Request,
|
||||
statusCode: number,
|
||||
message: string,
|
||||
anthropicMessage = message,
|
||||
anthropicErrorType = "permission_error",
|
||||
anthropicStatusCode = statusCode
|
||||
): Response {
|
||||
if (!isAnthropicMessagesRequest(request)) {
|
||||
return errorResponse(statusCode, message);
|
||||
}
|
||||
|
||||
const safeMessage = sanitizeErrorMessage(anthropicMessage);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
error: {
|
||||
type: anthropicErrorType,
|
||||
message: safeMessage,
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: anthropicStatusCode,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function resolveRequestedComboName(modelStr: string): Promise<string | null> {
|
||||
const exact = await getComboByName(modelStr);
|
||||
if (exact && typeof exact.name === "string") return exact.name;
|
||||
@@ -435,7 +483,12 @@ export async function enforceApiKeyPolicy(
|
||||
let requestedComboName: string | null = null;
|
||||
const isQuotaExclusive =
|
||||
Boolean(apiKeyInfo.allowedQuotas) && (apiKeyInfo.allowedQuotas as string[]).length > 0;
|
||||
if (!isQuotaExclusive && modelStr && apiKeyInfo.allowedCombos && apiKeyInfo.allowedCombos.length > 0) {
|
||||
if (
|
||||
!isQuotaExclusive &&
|
||||
modelStr &&
|
||||
apiKeyInfo.allowedCombos &&
|
||||
apiKeyInfo.allowedCombos.length > 0
|
||||
) {
|
||||
try {
|
||||
const comboAccess = await isComboAllowedForKey(apiKeyInfo.allowedCombos, modelStr);
|
||||
requestedComboName = comboAccess.comboName;
|
||||
@@ -487,9 +540,13 @@ export async function enforceApiKeyPolicy(
|
||||
return {
|
||||
apiKey,
|
||||
apiKeyInfo,
|
||||
rejection: errorResponse(
|
||||
rejection: policyErrorResponse(
|
||||
request,
|
||||
HTTP_STATUS.FORBIDDEN,
|
||||
`Model "${modelStr}" is not allowed for this API key`
|
||||
`Model "${modelStr}" is not allowed for this API key`,
|
||||
`Model "${modelStr}" is not enabled or quota is insufficient. Choose another allowed model.`,
|
||||
"invalid_request_error",
|
||||
HTTP_STATUS.BAD_REQUEST
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -526,9 +583,7 @@ export async function enforceApiKeyPolicy(
|
||||
const breach = checkTokenLimits(apiKeyInfo.id, undefined, modelStr ?? undefined);
|
||||
if (breach) {
|
||||
const scopeLabel =
|
||||
breach.scopeType === "global"
|
||||
? "account"
|
||||
: `${breach.scopeType} "${breach.scopeValue}"`;
|
||||
breach.scopeType === "global" ? "account" : `${breach.scopeType} "${breach.scopeValue}"`;
|
||||
return {
|
||||
apiKey,
|
||||
apiKeyInfo,
|
||||
@@ -545,10 +600,7 @@ export async function enforceApiKeyPolicy(
|
||||
return {
|
||||
apiKey,
|
||||
apiKeyInfo,
|
||||
rejection: errorResponse(
|
||||
HTTP_STATUS.SERVICE_UNAVAILABLE,
|
||||
"Token limit policy unavailable"
|
||||
),
|
||||
rejection: errorResponse(HTTP_STATUS.SERVICE_UNAVAILABLE, "Token limit policy unavailable"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user