@librechat/agents 3.2.34 → 3.2.36
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/cjs/agents/AgentContext.cjs +119 -9
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/agents/projection.cjs +25 -0
- package/dist/cjs/agents/projection.cjs.map +1 -0
- package/dist/cjs/common/enum.cjs +13 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +106 -3
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +26 -4
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +20 -0
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +49 -8
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/main.cjs +7 -0
- package/dist/cjs/messages/budget.cjs +23 -0
- package/dist/cjs/messages/budget.cjs.map +1 -0
- package/dist/cjs/messages/cache.cjs +1 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/content.cjs +12 -14
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -0
- package/dist/cjs/messages/prune.cjs +31 -13
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs +7 -2
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +12 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs +91 -2
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +4 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +138 -2
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +30 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +121 -11
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/agents/projection.mjs +25 -0
- package/dist/esm/agents/projection.mjs.map +1 -0
- package/dist/esm/common/enum.mjs +13 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +107 -4
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +26 -4
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +20 -0
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +49 -8
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/main.mjs +6 -4
- package/dist/esm/messages/budget.mjs +23 -0
- package/dist/esm/messages/budget.mjs.map +1 -0
- package/dist/esm/messages/cache.mjs +1 -1
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/content.mjs +12 -15
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -0
- package/dist/esm/messages/prune.mjs +31 -13
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs +7 -2
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +12 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs +91 -2
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +4 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +138 -2
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +30 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +37 -4
- package/dist/types/agents/projection.d.ts +26 -0
- package/dist/types/common/enum.d.ts +13 -0
- package/dist/types/graphs/Graph.d.ts +8 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/invoke.d.ts +1 -1
- package/dist/types/messages/budget.d.ts +11 -0
- package/dist/types/messages/cache.d.ts +7 -0
- package/dist/types/messages/content.d.ts +5 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/prune.d.ts +4 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/search/format.d.ts +4 -1
- package/dist/types/tools/search/types.d.ts +7 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +11 -1
- package/dist/types/types/graph.d.ts +89 -3
- package/dist/types/types/run.d.ts +13 -0
- package/dist/types/utils/tokens.d.ts +7 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +172 -8
- package/src/agents/__tests__/AgentContext.test.ts +235 -2
- package/src/agents/__tests__/projection.test.ts +73 -0
- package/src/agents/projection.ts +46 -0
- package/src/common/enum.ts +13 -0
- package/src/graphs/Graph.ts +168 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/utils/cross-provider-reasoning.test.ts +317 -0
- package/src/llm/anthropic/utils/message_inputs.ts +78 -16
- package/src/llm/bedrock/utils/cross-provider-reasoning.test.ts +131 -0
- package/src/llm/bedrock/utils/message_inputs.ts +35 -0
- package/src/llm/invoke.test.ts +79 -1
- package/src/llm/invoke.ts +58 -4
- package/src/messages/budget.ts +32 -0
- package/src/messages/cache.ts +1 -1
- package/src/messages/content.ts +24 -32
- package/src/messages/index.ts +1 -0
- package/src/messages/prune.ts +39 -2
- package/src/run.ts +5 -0
- package/src/scripts/subagent-usage-sink.ts +176 -0
- package/src/specs/context-accuracy.live.test.ts +409 -0
- package/src/specs/context-usage-event.test.ts +117 -0
- package/src/specs/context-usage.live.test.ts +297 -0
- package/src/specs/prune.test.ts +51 -1
- package/src/specs/subagent.test.ts +124 -1
- package/src/summarization/__tests__/node.test.ts +60 -1
- package/src/summarization/node.ts +20 -1
- package/src/tools/__tests__/SubagentExecutor.test.ts +443 -1
- package/src/tools/search/format.test.ts +242 -0
- package/src/tools/search/format.ts +122 -5
- package/src/tools/search/tool.ts +5 -1
- package/src/tools/search/types.ts +7 -0
- package/src/tools/subagent/SubagentExecutor.ts +221 -3
- package/src/types/graph.ts +94 -1
- package/src/types/run.ts +13 -0
- package/src/utils/__tests__/apportion.test.ts +32 -0
- package/src/utils/tokens.ts +33 -0
package/src/messages/prune.ts
CHANGED
|
@@ -1312,16 +1312,36 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
|
|
|
1312
1312
|
originalToolContent?: Map<number, string>;
|
|
1313
1313
|
calibrationRatio?: number;
|
|
1314
1314
|
resolvedInstructionOverhead?: number;
|
|
1315
|
+
/** Usable budget this call: maxTokens minus output reserve */
|
|
1316
|
+
contextBudget?: number;
|
|
1317
|
+
/** Calibrated instruction overhead actually applied this call */
|
|
1318
|
+
effectiveInstructionTokens?: number;
|
|
1315
1319
|
} {
|
|
1316
1320
|
if (params.messages.length === 0) {
|
|
1321
|
+
/** Post-compaction calls still invoke the model — report the same
|
|
1322
|
+
* reserve-adjusted budget fields as the populated paths */
|
|
1323
|
+
const emptyInstructionTokens =
|
|
1324
|
+
factoryParams.getInstructionTokens?.() ?? 0;
|
|
1325
|
+
const emptyReserveRatio =
|
|
1326
|
+
factoryParams.reserveRatio ?? DEFAULT_RESERVE_RATIO;
|
|
1327
|
+
const emptyBudget =
|
|
1328
|
+
factoryParams.maxTokens -
|
|
1329
|
+
(emptyReserveRatio > 0 && emptyReserveRatio < 1
|
|
1330
|
+
? Math.round(factoryParams.maxTokens * emptyReserveRatio)
|
|
1331
|
+
: 0);
|
|
1317
1332
|
return {
|
|
1318
1333
|
context: [],
|
|
1319
1334
|
indexTokenCountMap,
|
|
1320
1335
|
messagesToRefine: [],
|
|
1321
1336
|
prePruneContextTokens: 0,
|
|
1322
|
-
remainingContextTokens:
|
|
1337
|
+
remainingContextTokens: Math.max(
|
|
1338
|
+
0,
|
|
1339
|
+
emptyBudget - emptyInstructionTokens
|
|
1340
|
+
),
|
|
1323
1341
|
calibrationRatio,
|
|
1324
1342
|
resolvedInstructionOverhead: bestInstructionOverhead,
|
|
1343
|
+
contextBudget: emptyBudget,
|
|
1344
|
+
effectiveInstructionTokens: emptyInstructionTokens,
|
|
1325
1345
|
};
|
|
1326
1346
|
}
|
|
1327
1347
|
|
|
@@ -1549,6 +1569,8 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
|
|
|
1549
1569
|
pruningBudget > 0 ? calibratedTotalTokens / pruningBudget : 0,
|
|
1550
1570
|
calibrationRatio,
|
|
1551
1571
|
resolvedInstructionOverhead: bestInstructionOverhead,
|
|
1572
|
+
contextBudget: pruningBudget,
|
|
1573
|
+
effectiveInstructionTokens: currentInstructionTokens,
|
|
1552
1574
|
};
|
|
1553
1575
|
}
|
|
1554
1576
|
|
|
@@ -1752,6 +1774,8 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
|
|
|
1752
1774
|
originalToolContent.size > 0 ? originalToolContent : undefined,
|
|
1753
1775
|
calibrationRatio,
|
|
1754
1776
|
resolvedInstructionOverhead: bestInstructionOverhead,
|
|
1777
|
+
contextBudget: pruningBudget,
|
|
1778
|
+
effectiveInstructionTokens: currentInstructionTokens,
|
|
1755
1779
|
};
|
|
1756
1780
|
}
|
|
1757
1781
|
|
|
@@ -2099,9 +2123,20 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
|
|
|
2099
2123
|
}
|
|
2100
2124
|
}
|
|
2101
2125
|
|
|
2126
|
+
/** Scale raw-space remaining back to calibrated/provider units so it is
|
|
2127
|
+
* directly comparable with pruningBudget and prePruneContextTokens */
|
|
2128
|
+
const rawRemaining = Math.max(
|
|
2129
|
+
0,
|
|
2130
|
+
initialRemainingContextTokens + reclaimedTokens
|
|
2131
|
+
);
|
|
2102
2132
|
const remainingContextTokens = Math.max(
|
|
2103
2133
|
0,
|
|
2104
|
-
Math.min(
|
|
2134
|
+
Math.min(
|
|
2135
|
+
pruningBudget,
|
|
2136
|
+
calibrationRatio > 0
|
|
2137
|
+
? Math.round(rawRemaining * calibrationRatio)
|
|
2138
|
+
: rawRemaining
|
|
2139
|
+
)
|
|
2105
2140
|
);
|
|
2106
2141
|
|
|
2107
2142
|
runThinkingStartIndex = thinkingStartIndex ?? -1;
|
|
@@ -2123,6 +2158,8 @@ export function createPruneMessages(factoryParams: PruneMessagesFactoryParams) {
|
|
|
2123
2158
|
originalToolContent.size > 0 ? originalToolContent : undefined,
|
|
2124
2159
|
calibrationRatio,
|
|
2125
2160
|
resolvedInstructionOverhead: bestInstructionOverhead,
|
|
2161
|
+
contextBudget: pruningBudget,
|
|
2162
|
+
effectiveInstructionTokens: currentInstructionTokens,
|
|
2126
2163
|
};
|
|
2127
2164
|
};
|
|
2128
2165
|
}
|
package/src/run.ts
CHANGED
|
@@ -78,6 +78,7 @@ const CUSTOM_GRAPH_EVENTS = new Set<string>([
|
|
|
78
78
|
GraphEvents.ON_SUMMARIZE_COMPLETE,
|
|
79
79
|
GraphEvents.ON_SUBAGENT_UPDATE,
|
|
80
80
|
GraphEvents.ON_AGENT_LOG,
|
|
81
|
+
GraphEvents.ON_CONTEXT_USAGE,
|
|
81
82
|
GraphEvents.ON_CUSTOM_EVENT,
|
|
82
83
|
]);
|
|
83
84
|
|
|
@@ -129,6 +130,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
129
130
|
private toolOutputReferences?: t.ToolOutputReferencesConfig;
|
|
130
131
|
private eagerEventToolExecution?: t.EagerEventToolExecutionConfig;
|
|
131
132
|
private toolExecution?: t.ToolExecutionConfig;
|
|
133
|
+
private subagentUsageSink?: t.SubagentUsageSink;
|
|
132
134
|
private indexTokenCountMap?: Record<string, number>;
|
|
133
135
|
calibrationRatio: number = 1;
|
|
134
136
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
@@ -176,6 +178,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
176
178
|
this.toolOutputReferences = config.toolOutputReferences;
|
|
177
179
|
this.eagerEventToolExecution = config.eagerEventToolExecution;
|
|
178
180
|
this.toolExecution = config.toolExecution;
|
|
181
|
+
this.subagentUsageSink = config.subagentUsageSink;
|
|
179
182
|
|
|
180
183
|
if (!config.graphConfig) {
|
|
181
184
|
throw new Error('Graph config not provided');
|
|
@@ -249,6 +252,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
249
252
|
tokenCounter: this.tokenCounter,
|
|
250
253
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
251
254
|
calibrationRatio: this.calibrationRatio,
|
|
255
|
+
subagentUsageSink: this.subagentUsageSink,
|
|
252
256
|
});
|
|
253
257
|
/** Propagate compile options from graph config */
|
|
254
258
|
standardGraph.compileOptions = this.applyHITLCheckpointerFallback(
|
|
@@ -276,6 +280,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
276
280
|
tokenCounter: this.tokenCounter,
|
|
277
281
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
278
282
|
calibrationRatio: this.calibrationRatio,
|
|
283
|
+
subagentUsageSink: this.subagentUsageSink,
|
|
279
284
|
});
|
|
280
285
|
|
|
281
286
|
multiAgentGraph.compileOptions =
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
|
|
4
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
6
|
+
import type * as t from '@/types';
|
|
7
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
8
|
+
import { Providers, GraphEvents } from '@/common';
|
|
9
|
+
import { Run } from '@/run';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Live verification for `subagentUsageSink` (host billing of subagent
|
|
13
|
+
* child-run model usage).
|
|
14
|
+
*
|
|
15
|
+
* Runs a supervisor that MUST delegate to a "researcher" subagent, then
|
|
16
|
+
* asserts:
|
|
17
|
+
* 1. The host's CHAT_MODEL_END handler collected the PARENT's calls only.
|
|
18
|
+
* 2. The sink received one event per CHILD model call, tagged with the
|
|
19
|
+
* subagent type, child run id, and the child's model/provider.
|
|
20
|
+
* 3. Child usage has real token counts (the previously-unbilled tokens).
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* OPENAI_API_KEY=... npx ts-node -r tsconfig-paths/register src/scripts/subagent-usage-sink.ts
|
|
24
|
+
*
|
|
25
|
+
* Or with Anthropic:
|
|
26
|
+
* ANTHROPIC_API_KEY=... npx ts-node -r tsconfig-paths/register src/scripts/subagent-usage-sink.ts --provider anthropic
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const useAnthropic =
|
|
30
|
+
process.argv.includes('--provider') &&
|
|
31
|
+
process.argv[process.argv.indexOf('--provider') + 1] === 'anthropic';
|
|
32
|
+
|
|
33
|
+
const provider = useAnthropic ? Providers.ANTHROPIC : Providers.OPENAI;
|
|
34
|
+
const apiKey = useAnthropic
|
|
35
|
+
? process.env.ANTHROPIC_API_KEY
|
|
36
|
+
: process.env.OPENAI_API_KEY;
|
|
37
|
+
const modelName = useAnthropic ? 'claude-sonnet-4-20250514' : 'gpt-4o-mini';
|
|
38
|
+
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
console.error(
|
|
41
|
+
`Missing ${useAnthropic ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'} environment variable`
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main(): Promise<void> {
|
|
47
|
+
console.log('=== Subagent Usage Sink Live Verification ===\n');
|
|
48
|
+
console.log(`Provider: ${provider}`);
|
|
49
|
+
console.log(`Model: ${modelName}\n`);
|
|
50
|
+
|
|
51
|
+
const parentAgent: t.AgentInputs = {
|
|
52
|
+
agentId: 'supervisor',
|
|
53
|
+
provider,
|
|
54
|
+
clientOptions: { modelName, apiKey },
|
|
55
|
+
instructions: `You are a supervisor agent. For ANY user question, you MUST delegate to the "researcher" subagent via the subagent tool — never answer directly. After the subagent returns, give the user a one-sentence final answer.`,
|
|
56
|
+
maxContextTokens: 16000,
|
|
57
|
+
subagentConfigs: [
|
|
58
|
+
{
|
|
59
|
+
type: 'researcher',
|
|
60
|
+
name: 'Research Specialist',
|
|
61
|
+
description: 'Researches questions and returns concise answers.',
|
|
62
|
+
agentInputs: {
|
|
63
|
+
agentId: 'researcher',
|
|
64
|
+
provider,
|
|
65
|
+
clientOptions: { modelName, apiKey },
|
|
66
|
+
instructions:
|
|
67
|
+
'You are a research specialist. Answer the task in one or two sentences.',
|
|
68
|
+
maxContextTokens: 8000,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const collectedUsage: UsageMetadata[] = [];
|
|
75
|
+
const sunkEvents: t.SubagentUsageEvent[] = [];
|
|
76
|
+
|
|
77
|
+
const runId = `usage-sink-live-${Date.now()}`;
|
|
78
|
+
const run = await Run.create<t.IState>({
|
|
79
|
+
runId,
|
|
80
|
+
graphConfig: {
|
|
81
|
+
type: 'standard',
|
|
82
|
+
agents: [parentAgent],
|
|
83
|
+
},
|
|
84
|
+
returnContent: true,
|
|
85
|
+
customHandlers: {
|
|
86
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
87
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
88
|
+
},
|
|
89
|
+
subagentUsageSink: (event) => {
|
|
90
|
+
sunkEvents.push(event);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const callerConfig = {
|
|
95
|
+
configurable: { thread_id: `usage-sink-${Date.now()}` },
|
|
96
|
+
streamMode: 'values' as const,
|
|
97
|
+
version: 'v2' as const,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
await run.processStream(
|
|
101
|
+
{
|
|
102
|
+
messages: [
|
|
103
|
+
new HumanMessage(
|
|
104
|
+
'In what year was the Eiffel Tower completed? Use the researcher subagent.'
|
|
105
|
+
),
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
callerConfig
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
console.log('\n--- Parent collectedUsage (CHAT_MODEL_END handler) ---');
|
|
112
|
+
console.dir(collectedUsage, { depth: null });
|
|
113
|
+
|
|
114
|
+
console.log('\n--- Subagent usage sink events ---');
|
|
115
|
+
console.dir(sunkEvents, { depth: null });
|
|
116
|
+
|
|
117
|
+
const failures: string[] = [];
|
|
118
|
+
|
|
119
|
+
if (collectedUsage.length < 2) {
|
|
120
|
+
failures.push(
|
|
121
|
+
`expected >= 2 parent model calls in collectedUsage, got ${collectedUsage.length}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (sunkEvents.length === 0) {
|
|
125
|
+
failures.push('sink received NO child usage events');
|
|
126
|
+
}
|
|
127
|
+
for (const event of sunkEvents) {
|
|
128
|
+
if (event.subagentType !== 'researcher') {
|
|
129
|
+
failures.push(`unexpected subagentType: ${event.subagentType}`);
|
|
130
|
+
}
|
|
131
|
+
if (event.runId !== runId) {
|
|
132
|
+
failures.push(`event.runId mismatch: ${event.runId}`);
|
|
133
|
+
}
|
|
134
|
+
if (!event.subagentRunId.startsWith(`${runId}_sub_`)) {
|
|
135
|
+
failures.push(`event.subagentRunId mismatch: ${event.subagentRunId}`);
|
|
136
|
+
}
|
|
137
|
+
if (event.provider !== provider) {
|
|
138
|
+
failures.push(`event.provider mismatch: ${event.provider}`);
|
|
139
|
+
}
|
|
140
|
+
if (event.model == null || event.model === '') {
|
|
141
|
+
failures.push('event.model missing');
|
|
142
|
+
}
|
|
143
|
+
const input = Number(event.usage.input_tokens) || 0;
|
|
144
|
+
const output = Number(event.usage.output_tokens) || 0;
|
|
145
|
+
if (input <= 0 || output <= 0) {
|
|
146
|
+
failures.push(
|
|
147
|
+
`child usage has non-positive tokens: input=${input} output=${output}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const childTotal = sunkEvents.reduce(
|
|
153
|
+
(sum, e) =>
|
|
154
|
+
sum +
|
|
155
|
+
(Number(e.usage.input_tokens) || 0) +
|
|
156
|
+
(Number(e.usage.output_tokens) || 0),
|
|
157
|
+
0
|
|
158
|
+
);
|
|
159
|
+
console.log(
|
|
160
|
+
`\nChild tokens that were previously invisible to billing: ${childTotal}`
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (failures.length > 0) {
|
|
164
|
+
console.error('\nFAIL:');
|
|
165
|
+
for (const failure of failures) {
|
|
166
|
+
console.error(` - ${failure}`);
|
|
167
|
+
}
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
console.log('\nPASS: subagent child usage reported through the sink.');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main().catch((error) => {
|
|
174
|
+
console.error(error);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
});
|