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
+1 -1
View File
@@ -306,7 +306,7 @@ export async function POST(request: NextRequest) {
dataClass,
maxInputTokens: policy.maxInputTokens,
maxOutputTokens: policy.maxOutputTokens,
maximumCredits: Math.max(1, maximumCredits),
maximumCredits,
},
policy: {
tenantId,
+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 };
@@ -18,6 +18,14 @@ describe('model execution admission', () => {
expect(admitModelExecution(request, policy, { ...hold, status: 'settled' })).toEqual({ allowed: false, reason: 'reservation_required' });
expect(admitModelExecution(request, policy, { ...hold, jobId: 'other' })).toEqual({ allowed: false, reason: 'reservation_required' });
});
it('pilot mode (maximumCredits 0) requires no reservation but still enforces every other rule', () => {
const pilot = { ...request, maximumCredits: 0 };
expect(admitModelExecution(pilot, policy, undefined)).toEqual({ allowed: true });
expect(admitModelExecution(pilot, { ...policy, allowedModelIds: [] }, undefined)).toEqual({ allowed: false, reason: 'model_not_allowed' });
expect(admitModelExecution(pilot, { ...policy, projectId: 'other' }, undefined)).toEqual({ allowed: false, reason: 'scope_mismatch' });
expect(admitModelExecution({ ...pilot, maximumCredits: -1 }, policy, undefined)).toEqual({ allowed: false, reason: 'invalid_request' });
expect(admitModelExecution(request, policy, undefined)).toEqual({ allowed: false, reason: 'reservation_required' });
});
it('rejects invalid limits and never treats an unknown destination as local', () => {
expect(admitModelExecution({ ...request, maxInputTokens: NaN }, policy, hold).allowed).toBe(false);
expect(admitModelExecution(request, { ...policy, destination: 'unknown' as never }, hold).allowed).toBe(false);