@sweny-ai/core 0.1.191 → 0.1.192
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/budget.d.ts +15 -5
- package/dist/budget.js +24 -9
- package/dist/executor.js +6 -3
- package/package.json +1 -1
package/dist/budget.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export declare function spendOf(u: NodeUsage | undefined): {
|
|
|
59
59
|
export declare function describeOverrun(o: BudgetOverrun, nodeId?: string): string;
|
|
60
60
|
/**
|
|
61
61
|
* Tracks spend for one run. `node()` opens a visit to a node; `attempt()` opens
|
|
62
|
-
* one agent run inside it. Spend is committed when an attempt
|
|
62
|
+
* one agent run inside it. Spend is committed once, when an attempt settles.
|
|
63
63
|
*/
|
|
64
64
|
export declare class BudgetGuard {
|
|
65
65
|
readonly runLimits: SpendLimits;
|
|
@@ -102,6 +102,7 @@ export declare class BudgetAttempt {
|
|
|
102
102
|
private live;
|
|
103
103
|
private liveUsage;
|
|
104
104
|
private stopped;
|
|
105
|
+
private settled;
|
|
105
106
|
private readonly onOuterAbort;
|
|
106
107
|
constructor(guard: BudgetGuard, node: NodeBudget, outer?: AbortSignal | undefined);
|
|
107
108
|
get signal(): AbortSignal;
|
|
@@ -113,11 +114,20 @@ export declare class BudgetAttempt {
|
|
|
113
114
|
readonly onUsage: (u: NodeUsage) => void;
|
|
114
115
|
private check;
|
|
115
116
|
/**
|
|
116
|
-
* The attempt is over
|
|
117
|
-
*
|
|
118
|
-
* stop, else a crossing visible only in the final usage
|
|
117
|
+
* The attempt is over: commit its spend exactly once (#549). The spend is the
|
|
118
|
+
* larger of `finalUsage` and what was seen live, per unit. The breach (the
|
|
119
|
+
* live stop, else a crossing visible only in the final usage) is judged
|
|
120
|
+
* against the totals before this commit. Later calls change nothing and
|
|
121
|
+
* return the first call's breach, so the executor settles as soon as the
|
|
122
|
+
* harness returns, before any fallible work, and every exit path may settle
|
|
123
|
+
* again without double counting.
|
|
119
124
|
*/
|
|
125
|
+
settle(finalUsage?: NodeUsage): BudgetOverrun | undefined;
|
|
126
|
+
/** Settle with the attempt's final usage and return the breach, if any. */
|
|
120
127
|
finish(finalUsage: NodeUsage | undefined): BudgetOverrun | undefined;
|
|
121
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Every exit path ends here: an attempt that threw still commits what it
|
|
130
|
+
* reported live. A no-op once settled.
|
|
131
|
+
*/
|
|
122
132
|
dispose(): void;
|
|
123
133
|
}
|
package/dist/budget.js
CHANGED
|
@@ -69,7 +69,7 @@ export function describeOverrun(o, nodeId) {
|
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Tracks spend for one run. `node()` opens a visit to a node; `attempt()` opens
|
|
72
|
-
* one agent run inside it. Spend is committed when an attempt
|
|
72
|
+
* one agent run inside it. Spend is committed once, when an attempt settles.
|
|
73
73
|
*/
|
|
74
74
|
export class BudgetGuard {
|
|
75
75
|
runLimits;
|
|
@@ -139,6 +139,7 @@ export class BudgetAttempt {
|
|
|
139
139
|
live = { tokens: 0, costUsd: 0 };
|
|
140
140
|
liveUsage;
|
|
141
141
|
stopped;
|
|
142
|
+
settled;
|
|
142
143
|
onOuterAbort = () => this.controller.abort();
|
|
143
144
|
constructor(guard, node, outer) {
|
|
144
145
|
this.guard = guard;
|
|
@@ -184,23 +185,37 @@ export class BudgetAttempt {
|
|
|
184
185
|
breachOf("run", this.guard.runLimits, { tokens: r.tokens + attempt.tokens, costUsd: r.costUsd + attempt.costUsd }, true));
|
|
185
186
|
}
|
|
186
187
|
/**
|
|
187
|
-
* The attempt is over
|
|
188
|
-
*
|
|
189
|
-
* stop, else a crossing visible only in the final usage
|
|
188
|
+
* The attempt is over: commit its spend exactly once (#549). The spend is the
|
|
189
|
+
* larger of `finalUsage` and what was seen live, per unit. The breach (the
|
|
190
|
+
* live stop, else a crossing visible only in the final usage) is judged
|
|
191
|
+
* against the totals before this commit. Later calls change nothing and
|
|
192
|
+
* return the first call's breach, so the executor settles as soon as the
|
|
193
|
+
* harness returns, before any fallible work, and every exit path may settle
|
|
194
|
+
* again without double counting.
|
|
190
195
|
*/
|
|
191
|
-
|
|
196
|
+
settle(finalUsage) {
|
|
197
|
+
if (this.settled)
|
|
198
|
+
return this.settled.breach;
|
|
192
199
|
this.outer?.removeEventListener("abort", this.onOuterAbort);
|
|
193
200
|
const f = spendOf(finalUsage);
|
|
194
201
|
const spent = {
|
|
195
202
|
tokens: Math.max(this.live.tokens, f.tokens ?? 0),
|
|
196
203
|
costUsd: Math.max(this.live.costUsd, f.costUsd ?? 0),
|
|
197
204
|
};
|
|
198
|
-
const
|
|
205
|
+
const breach = this.stopped ?? this.check(spent);
|
|
206
|
+
this.settled = { breach };
|
|
199
207
|
this.node.commit(spent);
|
|
200
|
-
return
|
|
208
|
+
return breach;
|
|
209
|
+
}
|
|
210
|
+
/** Settle with the attempt's final usage and return the breach, if any. */
|
|
211
|
+
finish(finalUsage) {
|
|
212
|
+
return this.settle(finalUsage);
|
|
201
213
|
}
|
|
202
|
-
/**
|
|
214
|
+
/**
|
|
215
|
+
* Every exit path ends here: an attempt that threw still commits what it
|
|
216
|
+
* reported live. A no-op once settled.
|
|
217
|
+
*/
|
|
203
218
|
dispose() {
|
|
204
|
-
this.
|
|
219
|
+
this.settle();
|
|
205
220
|
}
|
|
206
221
|
}
|
package/dist/executor.js
CHANGED
|
@@ -598,6 +598,8 @@ async function executeRun(workflow, input, options, results, trace, at) {
|
|
|
598
598
|
safeObserve(observer, { type: "node:progress", node: currentId, message }, logger);
|
|
599
599
|
},
|
|
600
600
|
});
|
|
601
|
+
// Commit the attempt's spend before anything here can throw (#549); `finish` below reads the breach.
|
|
602
|
+
attemptBudget?.settle(result.usage);
|
|
601
603
|
}
|
|
602
604
|
catch (err) {
|
|
603
605
|
if (journalFault)
|
|
@@ -608,8 +610,9 @@ async function executeRun(workflow, input, options, results, trace, at) {
|
|
|
608
610
|
throw err;
|
|
609
611
|
}
|
|
610
612
|
finally {
|
|
611
|
-
|
|
613
|
+
// A throw from the harness still commits the spend it reported live; a no-op once settled.
|
|
612
614
|
attemptBudget?.dispose();
|
|
615
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
613
616
|
}
|
|
614
617
|
if (journalFault)
|
|
615
618
|
throw journalFault;
|
|
@@ -632,8 +635,8 @@ async function executeRun(workflow, input, options, results, trace, at) {
|
|
|
632
635
|
result = { ...result, degraded: [...new Set([...(result.degraded ?? []), ...budgetCheck.degraded])] };
|
|
633
636
|
}
|
|
634
637
|
if (attemptBudget) {
|
|
635
|
-
//
|
|
636
|
-
// in the final usage, fails the node whatever else it did.
|
|
638
|
+
// The spend was committed when the harness returned; a live stop, or a
|
|
639
|
+
// crossing visible only in the final usage, fails the node whatever else it did.
|
|
637
640
|
const breach = attemptBudget.finish(result.usage);
|
|
638
641
|
if (breach) {
|
|
639
642
|
const error = describeOverrun(breach, currentId);
|