feat(api): wire shared admission across LLM routes

Acquire admission once after API-key policy, preserve lazy raw-request snapshots, and bind lease settlement to JSON, SSE, abort, deadline, and failure lifecycles. Expose a low-cardinality health summary and preserve non-SSE Ollama errors unchanged.
This commit is contained in:
Xiangzhe
2026-08-03 07:58:45 +08:00
committed by diegosouzapw
parent a61020153c
commit 8ca40e7971
16 changed files with 2163 additions and 35 deletions

View File

@@ -56,6 +56,7 @@ export async function GET() {
sessionManagerModule,
credentialHealthModule,
localHealthModule,
adaptiveAdmissionModule,
settingsResult,
connectionsResult,
] = await Promise.allSettled([
@@ -67,6 +68,7 @@ export async function GET() {
import("@omniroute/open-sse/services/sessionManager.ts"),
import("@/lib/credentialHealth/cache"),
import("@/lib/localHealthCheck"),
import("@omniroute/open-sse/services/admission/runtime.ts"),
getCachedSettings(),
getProviderConnections(),
]);
@@ -145,6 +147,14 @@ export async function GET() {
: {};
const settings = settingsResult.status === "fulfilled" ? settingsResult.value : {};
const connections = connectionsResult.status === "fulfilled" ? connectionsResult.value : [];
const adaptiveAdmission =
adaptiveAdmissionModule.status === "fulfilled"
? readHealthValue(
"adaptive admission",
() => adaptiveAdmissionModule.value.getAdaptiveAdmissionRuntime().snapshot(),
null
)
: null;
const payload = buildHealthPayload({
appVersion: APP_CONFIG.version,
@@ -169,6 +179,7 @@ export async function GET() {
activeSessions,
activeSessionsByKey,
credentialHealth,
adaptiveAdmission,
});
healthPayloadCache = { payload, expiresAt: Date.now() + HEALTH_PAYLOAD_TTL_MS };
@@ -186,6 +197,7 @@ export async function GET() {
lockouts: [],
quotaMonitor: { ...fallbackQuotaMonitorSummary, monitors: [] },
sessions: { activeCount: 0, stickyBoundCount: 0, byApiKey: {}, top: [] },
adaptiveAdmission: null,
dedup: { inflightRequests: 0 },
});
}

View File

@@ -82,6 +82,7 @@ export async function POST(request: Request) {
method: request.method,
headers: request.headers,
body: JSON.stringify(normalized),
signal: request.signal,
});
// #3571 — translate the chat-pipeline response back to the legacy
// text-completion shape so OpenAI Completion clients (e.g. TabbyML) work.
@@ -90,7 +91,7 @@ export async function POST(request: Request) {
// echo the compression header on the way out.
return withCompressionHeaderEcho(
await asTextCompletionResponse(
await handleChat(newRequest, buildClientRawRequest(request, body)),
await handleChat(newRequest, () => buildClientRawRequest(request, body)),
typeof body.model === "string" ? body.model : undefined
),
compressionRequestHeader
@@ -106,7 +107,10 @@ export async function POST(request: Request) {
// Re-read body.model so the response echoes the caller's requested identifier.
let requestedModel: string | undefined;
try {
const bodyForModel = await request.clone().json().catch(() => null);
const bodyForModel = await request
.clone()
.json()
.catch(() => null);
if (bodyForModel && typeof bodyForModel.model === "string") {
requestedModel = bodyForModel.model;
}

View File

@@ -98,7 +98,8 @@ export async function POST(request, { params }) {
method: request.method,
headers: request.headers,
body: JSON.stringify(body),
signal: request.signal,
});
return await handleChat(newRequest, buildClientRawRequest(request, rawBody));
return await handleChat(newRequest, () => buildClientRawRequest(request, rawBody));
}

View File

@@ -89,9 +89,7 @@ export async function POST(request, { params }) {
action = modelAction.includes(":streamGenerateContent")
? ":streamGenerateContent"
: ":generateContent";
model = modelAction
.replace(":streamGenerateContent", "")
.replace(":generateContent", "");
model = modelAction.replace(":streamGenerateContent", "").replace(":generateContent", "");
}
const validation = validateBody(v1betaGeminiGenerateSchema, rawBody);
@@ -113,9 +111,10 @@ export async function POST(request, { params }) {
method: "POST",
headers: request.headers,
body: JSON.stringify(convertedBody),
signal: request.signal,
});
const response = await handleChat(newRequest, buildClientRawRequest(request, rawBody));
const response = await handleChat(newRequest, () => buildClientRawRequest(request, rawBody));
if (stream) {
// Transform OpenAI SSE => Gemini SSE on the fly. The @google/genai SDK