mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 15:52:52 +03:00
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:
@@ -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 },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user