Attempt budgets and error ownership
Express used attempts, invalid data, and workflow contract errors separately instead of turning every failure into successful prose.
AttemptBudget.take consumes an attempt only when it succeeds; when the quota is exhausted it returns false and issues no further attempts. It is a counter for local retry policy and does not automatically control model call counts or the global deadline. Engine rounds, ModelRunner call quotas, and App business attempts are different measurements.
BudgetError represents a budget rejection, ValidationError carries issues, and WorkflowContractError represents a node, route, or result violating the runtime contract. getErrorInfo extracts the code and retry information uniformly, but that does not mean every error should be retried. Model parse errors, tool authorization errors, and persistence failures each return to the App handler that owns that boundary.
Progressive examples for the same business case
Allow only two business attempts
Beginner · Node.js
import { AttemptBudget } from '@jangts/agent-core/engine';
const attempts = new AttemptBudget(2);
while (attempts.take()) console.log('attempt', attempts.used);
console.log(attempts.available); // falseimport { AttemptBudget } from '@jangts/agent-core/engine';
const attempts = new AttemptBudget(2);
while (attempts.take())
console.log('attempt', attempts.used);
console.log(attempts.available); // falseDistinguish an exhausted quota from invalid data
Configuration · Node.js
import { BudgetError, ValidationError, getErrorInfo } from '@jangts/agent-core';
function requireAnswer(answer: string) {
if (!answer.trim()) throw new ValidationError([{ code: 'ANSWER_EMPTY', message: '回答不能为空' }]);
return answer;
}
try { requireAnswer(''); }
catch (error) { console.log(getErrorInfo(error)); }
const exhausted = new BudgetError('本次业务的尝试额度已用尽');
console.log(getErrorInfo(exhausted));import { BudgetError, ValidationError, getErrorInfo } from '@jangts/agent-core';
function requireAnswer(answer) {
if (!answer.trim())
throw new ValidationError([{ code: 'ANSWER_EMPTY', message: '回答不能为空' }]);
return answer;
}
try {
requireAnswer('');
}
catch (error) {
console.log(getErrorInfo(error));
}
const exhausted = new BudgetError('本次业务的尝试额度已用尽');
console.log(getErrorInfo(exhausted));