🪵 chore: Log Subagent Limit Hits (#13068)

This commit is contained in:
Danny Avila 2026-05-11 09:25:08 -04:00 committed by GitHub
parent 70b6bb69d3
commit 7631366f52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 92 additions and 1 deletions

View file

@ -647,6 +647,12 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
return;
}
if (subagentGraphIds.size >= MAX_SUBAGENT_GRAPH_NODES) {
logger.warn('[initializeClient] Subagent graph node limit exceeded', {
agentId,
primaryAgentId: primaryConfig.id,
loadedSubagentCount: subagentGraphIds.size,
maxSubagentGraphNodes: MAX_SUBAGENT_GRAPH_NODES,
});
throw new Error(
`Subagent graph exceeds the maximum of ${MAX_SUBAGENT_GRAPH_NODES} unique agents.`,
);
@ -670,6 +676,13 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
if (loadedSubagentConfigIds.has(config.id)) {
if ((config.subagentAgentConfigs?.length ?? 0) > 0 && depth >= MAX_SUBAGENT_DEPTH) {
logger.warn('[initializeClient] Subagent graph depth limit exceeded', {
agentId: config.id,
primaryAgentId: primaryConfig.id,
depth,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
childCount: config.subagentAgentConfigs.length,
});
throw new Error(
`Subagent graph exceeds the maximum depth of ${MAX_SUBAGENT_DEPTH} at agent ${config.id}.`,
);
@ -690,6 +703,13 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
);
if (explicitSubagentIds.length > 0 && depth >= MAX_SUBAGENT_DEPTH) {
logger.warn('[initializeClient] Subagent graph depth limit exceeded', {
agentId: config.id,
primaryAgentId: primaryConfig.id,
depth,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
childCount: explicitSubagentIds.length,
});
throw new Error(
`Subagent graph exceeds the maximum depth of ${MAX_SUBAGENT_DEPTH} at agent ${config.id}.`,
);

View file

@ -68,9 +68,12 @@ jest.mock('~/cache', () => ({
}));
const { initializeClient } = require('./initialize');
const { logger } = require('@librechat/data-schemas');
const { User, AclEntry } = require('~/db/models');
const { createAgent } = require('~/models');
jest.spyOn(logger, 'warn').mockImplementation(() => {});
const PRIMARY_ID = 'agent_primary';
const TARGET_ID = 'agent_target';
const AUTHORIZED_ID = 'agent_authorized';
@ -504,6 +507,16 @@ describe('initializeClient — subagent loading', () => {
endpointOption: makeEndpointOption(),
}),
).rejects.toThrow(`maximum depth of ${MAX_SUBAGENT_DEPTH}`);
expect(logger.warn).toHaveBeenCalledWith(
'[initializeClient] Subagent graph depth limit exceeded',
expect.objectContaining({
agentId: `agent_depth_${MAX_SUBAGENT_DEPTH - 1}`,
primaryAgentId: PRIMARY_ID,
depth: MAX_SUBAGENT_DEPTH,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
childCount: 1,
}),
);
expect(agentClientArgs).toBeUndefined();
});
@ -542,6 +555,15 @@ describe('initializeClient — subagent loading', () => {
endpointOption: makeEndpointOption(),
}),
).rejects.toThrow(`maximum depth of ${MAX_SUBAGENT_DEPTH}`);
expect(logger.warn).toHaveBeenCalledWith(
'[initializeClient] Subagent graph depth limit exceeded',
expect.objectContaining({
primaryAgentId: PRIMARY_ID,
depth: MAX_SUBAGENT_DEPTH,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
childCount: 1,
}),
);
expect(agentClientArgs).toBeUndefined();
});
@ -581,6 +603,14 @@ describe('initializeClient — subagent loading', () => {
endpointOption: makeEndpointOption(),
}),
).rejects.toThrow(`maximum of ${MAX_SUBAGENT_GRAPH_NODES} unique agents`);
expect(logger.warn).toHaveBeenCalledWith(
'[initializeClient] Subagent graph node limit exceeded',
expect.objectContaining({
primaryAgentId: PRIMARY_ID,
loadedSubagentCount: MAX_SUBAGENT_GRAPH_NODES,
maxSubagentGraphNodes: MAX_SUBAGENT_GRAPH_NODES,
}),
);
expect(agentClientArgs).toBeUndefined();
});

View file

@ -1,3 +1,4 @@
import { logger } from '@librechat/data-schemas';
import type { AppConfig } from '@librechat/data-schemas';
import type { SummarizationConfig, TEndpoint } from 'librechat-data-provider';
import {
@ -26,6 +27,16 @@ jest.mock('~/utils/env', () => ({
createSafeUser: jest.fn(() => ({})),
}));
jest.mock('@librechat/data-schemas', () => ({
...jest.requireActual('@librechat/data-schemas'),
logger: {
debug: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
info: jest.fn(),
},
}));
// Mock Run.create to capture the graphConfig it receives
jest.mock('@librechat/agents', () => {
const actual = jest.requireActual('@librechat/agents');
@ -1007,6 +1018,14 @@ describe('subagentConfigs', () => {
}),
).rejects.toThrow(`maximum depth of ${MAX_SUBAGENT_DEPTH}`);
expect(logger.warn).toHaveBeenCalledWith(
'[createRun] Subagent graph depth limit exceeded',
expect.objectContaining({
agentId: `agent_chain_${MAX_SUBAGENT_DEPTH + 1}`,
depth: MAX_SUBAGENT_DEPTH + 1,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
}),
);
expect(Run.create).not.toHaveBeenCalled();
});
@ -1020,6 +1039,14 @@ describe('subagentConfigs', () => {
}),
).rejects.toThrow(`maximum of ${MAX_SUBAGENT_RUN_CONFIGS} expanded entries`);
expect(logger.warn).toHaveBeenCalledWith(
'[createRun] Subagent run configuration limit exceeded',
expect.objectContaining({
expandedConfigCount: MAX_SUBAGENT_RUN_CONFIGS + 1,
maxSubagentRunConfigs: MAX_SUBAGENT_RUN_CONFIGS,
rootAgentIds: ['agent_dag_root'],
}),
);
expect(Run.create).not.toHaveBeenCalled();
});
});

View file

@ -551,11 +551,17 @@ const SELF_SUBAGENT_TYPE = 'self';
interface SubagentBuildState {
configCount: number;
rootAgentIds: string[];
}
function countSubagentConfig(state: SubagentBuildState): void {
state.configCount += 1;
if (state.configCount > MAX_SUBAGENT_RUN_CONFIGS) {
logger.warn('[createRun] Subagent run configuration limit exceeded', {
expandedConfigCount: state.configCount,
maxSubagentRunConfigs: MAX_SUBAGENT_RUN_CONFIGS,
rootAgentIds: state.rootAgentIds,
});
throw new Error(
`Subagent run configuration exceeds the maximum of ${MAX_SUBAGENT_RUN_CONFIGS} expanded entries.`,
);
@ -564,6 +570,11 @@ function countSubagentConfig(state: SubagentBuildState): void {
function assertSubagentDepth(depth: number, agentId: string): void {
if (depth > MAX_SUBAGENT_DEPTH) {
logger.warn('[createRun] Subagent graph depth limit exceeded', {
agentId,
depth,
maxSubagentDepth: MAX_SUBAGENT_DEPTH,
});
throw new Error(
`Subagent graph exceeds the maximum depth of ${MAX_SUBAGENT_DEPTH} at agent ${agentId}.`,
);
@ -922,7 +933,10 @@ export async function createRun({
};
const agentInputs: AgentInputs[] = [];
const subagentBuildState: SubagentBuildState = { configCount: 0 };
const subagentBuildState: SubagentBuildState = {
configCount: 0,
rootAgentIds: agents.map((agent) => agent.id),
};
for (const agent of agents) {
const agentInput = buildAgentInput(agent);
const subagentConfigs = buildSubagentConfigs(