fix(admission): zero maximumCredits means pilot mode, not a missing reservation
CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
masterplan-lock / check (push) Successful in 5s
CI (SIAX Cloud) / security (push) Successful in 17s
Security Scan (SIAX Cloud) / security (push) Successful in 17s
CI (SIAX Cloud) / quality (push) Successful in 46s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 5s
CI (SIAX Cloud) / security (pull_request) Successful in 23s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 25s
CI (SIAX Cloud) / integration (pull_request) Successful in 45s
CI (SIAX Cloud) / quality (pull_request) Successful in 59s

The structure route ran the model with maximumCredits=1 and no reservation when
credits were off, so admission returned reservation_required and the route
answered 502. Admission now requires a reservation only when maximumCredits>0.

Verified against the live stack: DOC0 segments -> LiteLLM/Ollama
(inf0-valuation) -> evidence validation -> needs_review (no export).
This commit is contained in:
2026-11-02 16:42:01 +01:00
parent 717aca6ff0
commit 48dff2078e
3 changed files with 20 additions and 5 deletions
+11 -4
View File
@@ -47,8 +47,11 @@ export function admitModelExecution(
|| !Array.isArray(policy.allowedModelIds) || !Array.isArray(policy.allowedDataClasses)
|| !['none', 'low', 'medium', 'high', 'restricted'].includes(request.dataClass)
|| !['internal', 'external'].includes(policy.destination) || typeof policy.externalTransferAllowed !== 'boolean'
|| ![request.maxInputTokens, request.maxOutputTokens, request.maximumCredits, policy.maxInputTokens, policy.maxOutputTokens]
.every((n) => Number.isSafeInteger(n) && n > 0)) return { allowed: false, reason: 'invalid_request' };
|| ![request.maxInputTokens, request.maxOutputTokens, policy.maxInputTokens, policy.maxOutputTokens]
.every((n) => Number.isSafeInteger(n) && n > 0)
|| !Number.isSafeInteger(request.maximumCredits) || request.maximumCredits < 0) {
return { allowed: false, reason: 'invalid_request' };
}
if (request.tenantId !== policy.tenantId || request.projectId !== policy.projectId) return { allowed: false, reason: 'scope_mismatch' };
if (!policy.allowedModelIds.includes(request.modelId)) return { allowed: false, reason: 'model_not_allowed' };
if (!policy.allowedDataClasses.includes(request.dataClass)) return { allowed: false, reason: 'data_class_not_allowed' };
@@ -58,9 +61,13 @@ export function admitModelExecution(
if (request.maxInputTokens > policy.maxInputTokens || request.maxOutputTokens > policy.maxOutputTokens) {
return { allowed: false, reason: 'token_limit_exceeded' };
}
if (!reservation || reservation.tenantId !== request.tenantId || reservation.projectId !== request.projectId
// maximumCredits === 0 means the operator runs this route without a wallet
// (explicit pilot mode), so no reservation is required. Any positive amount
// still requires an active, matching reservation.
if (request.maximumCredits > 0 && (
!reservation || reservation.tenantId !== request.tenantId || reservation.projectId !== request.projectId
|| reservation.jobId !== request.jobId || reservation.status !== 'reserved'
|| !Number.isSafeInteger(reservation.credits) || reservation.credits < request.maximumCredits) {
|| !Number.isSafeInteger(reservation.credits) || reservation.credits < request.maximumCredits)) {
return { allowed: false, reason: 'reservation_required' };
}
return { allowed: true };