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:
Diego Rodrigues de Sa e Souza
2026-06-13 17:27:40 -03:00
committed by GitHub
parent 420d62b420
commit 76a07cf7a5
308 changed files with 22331 additions and 5873 deletions

View File

@@ -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"),
};
}
}