feat: add CompiledSubAgent back

This commit is contained in:
Tat Dat Duong
2025-12-02 02:36:20 +01:00
parent 0be846e2b2
commit 6b914ba358
6 changed files with 79 additions and 15 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"deepagents": minor
---
Add CompiledSubAgent back to `createDeepAgent`
+2 -1
View File
@@ -24,6 +24,7 @@ import {
import { StateBackend, type BackendProtocol } from "./backends/index.js";
import { InteropZodObject } from "@langchain/core/utils/types";
import { AnnotationRoot } from "@langchain/langgraph";
import { CompiledSubAgent } from "./middleware/subagents.js";
/**
* Configuration parameters for creating a Deep Agent
@@ -43,7 +44,7 @@ export interface CreateDeepAgentParams<
/** Custom middleware to apply after standard middleware */
middleware?: AgentMiddleware[];
/** List of subagent specifications for task delegation */
subagents?: SubAgent[];
subagents?: (SubAgent | CompiledSubAgent)[];
/** Structured output response format for the agent */
responseFormat?: any; // ResponseFormat type is complex, using any for now
/** Optional schema for context (not persisted between invocations) */
+1
View File
@@ -15,6 +15,7 @@ export {
type FilesystemMiddlewareOptions,
type SubAgentMiddlewareOptions,
type SubAgent,
type CompiledSubAgent,
type FileData,
} from "./middleware/index.js";
+1
View File
@@ -7,5 +7,6 @@ export {
createSubAgentMiddleware,
type SubAgentMiddlewareOptions,
type SubAgent,
type CompiledSubAgent,
} from "./subagents.js";
export { createPatchToolCallsMiddleware } from "./patch_tool_calls.js";
+31 -14
View File
@@ -170,6 +170,18 @@ When NOT to use the task tool:
- Remember to use the \`task\` tool to silo independent tasks within a multi-part objective.
- You should use the \`task\` tool whenever you have a complex task that will take multiple steps, and is independent from other tasks that the agent needs to complete. These agents are highly competent and efficient.`;
/**
* Type definitions for pre-compiled agents.
*/
export interface CompiledSubAgent {
/** The name of the agent */
name: string;
/** The description of the agent */
description: string;
/** The agent instance */
runnable: ReactAgent<any, any, any, any> | Runnable;
}
/**
* Type definitions for subagents
*/
@@ -238,7 +250,7 @@ function getSubagents(options: {
defaultTools: StructuredTool[];
defaultMiddleware: AgentMiddleware[] | null;
defaultInterruptOn: Record<string, boolean | InterruptOnConfig> | null;
subagents: Array<SubAgent>;
subagents: (SubAgent | CompiledSubAgent)[];
generalPurposeAgent: boolean;
}): {
agents: Record<string, ReactAgent<any, any, any, any> | Runnable>;
@@ -285,19 +297,24 @@ function getSubagents(options: {
`- ${agentParams.name}: ${agentParams.description}`,
);
const middleware = agentParams.middleware
? [...defaultSubagentMiddleware, ...agentParams.middleware]
: [...defaultSubagentMiddleware];
if ("runnable" in agentParams) {
agents[agentParams.name] = agentParams.runnable;
} else {
const middleware = agentParams.middleware
? [...defaultSubagentMiddleware, ...agentParams.middleware]
: [...defaultSubagentMiddleware];
const interruptOn = agentParams.interruptOn || defaultInterruptOn;
if (interruptOn) middleware.push(humanInTheLoopMiddleware({ interruptOn }));
const interruptOn = agentParams.interruptOn || defaultInterruptOn;
if (interruptOn)
middleware.push(humanInTheLoopMiddleware({ interruptOn }));
agents[agentParams.name] = createAgent({
model: agentParams.model ?? defaultModel,
systemPrompt: agentParams.systemPrompt,
tools: agentParams.tools ?? defaultTools,
middleware,
});
agents[agentParams.name] = createAgent({
model: agentParams.model ?? defaultModel,
systemPrompt: agentParams.systemPrompt,
tools: agentParams.tools ?? defaultTools,
middleware,
});
}
}
return { agents, descriptions: subagentDescriptions };
@@ -311,7 +328,7 @@ function createTaskTool(options: {
defaultTools: StructuredTool[];
defaultMiddleware: AgentMiddleware[] | null;
defaultInterruptOn: Record<string, boolean | InterruptOnConfig> | null;
subagents: Array<SubAgent>;
subagents: (SubAgent | CompiledSubAgent)[];
generalPurposeAgent: boolean;
taskDescription: string | null;
}) {
@@ -406,7 +423,7 @@ export interface SubAgentMiddlewareOptions {
/** The tool configs for the default general-purpose subagent */
defaultInterruptOn?: Record<string, boolean | InterruptOnConfig> | null;
/** A list of additional subagents to provide to the agent */
subagents?: Array<SubAgent>;
subagents?: (SubAgent | CompiledSubAgent)[];
/** Full system prompt override */
systemPrompt?: string | null;
/** Whether to include the general-purpose agent */
+39
View File
@@ -258,6 +258,45 @@ describe("Subagent Middleware Integration Tests", () => {
},
);
it.concurrent(
"should use pre-compiled subagent",
{ timeout: 60000 },
async () => {
const customSubagent = createAgent({
model: SAMPLE_MODEL,
systemPrompt: "Use the get_weather tool to get the weather in a city.",
tools: [getWeather],
});
const agent = createAgent({
model: SAMPLE_MODEL,
systemPrompt: "Use the task tool to call a subagent.",
middleware: [
createSubAgentMiddleware({
defaultModel: SAMPLE_MODEL,
defaultTools: [],
subagents: [
{
name: "weather",
description: "This subagent can get weather in cities.",
runnable: customSubagent,
},
],
}),
],
});
const expectedToolCalls = [
{ name: "task", args: { subagent_type: "weather" } },
{ name: "get_weather" },
];
await assertExpectedSubgraphActions(expectedToolCalls, agent, {
messages: [new HumanMessage("What is the weather in Tokyo?")],
});
},
);
it.concurrent(
"should handle multiple subagents without middleware accumulation",
{ timeout: 120000 },