Skip to content

Commit f2e73f2

Browse files
authored
Merge pull request #72 from langchain-ai/dqbd/compiled-subagent
feat: add CompiledSubAgent back
2 parents 0be846e + 6b914ba commit f2e73f2

File tree

6 files changed

+81
-17
lines changed

6 files changed

+81
-17
lines changed

.changeset/funky-fans-notice.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"deepagents": minor
3+
---
4+
5+
Add CompiledSubAgent back to `createDeepAgent`

src/agent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { StateBackend, type BackendProtocol } from "./backends/index.js";
2525
import { InteropZodObject } from "@langchain/core/utils/types";
2626
import { AnnotationRoot } from "@langchain/langgraph";
27+
import { CompiledSubAgent } from "./middleware/subagents.js";
2728

2829
/**
2930
* Configuration parameters for creating a Deep Agent
@@ -43,7 +44,7 @@ export interface CreateDeepAgentParams<
4344
/** Custom middleware to apply after standard middleware */
4445
middleware?: AgentMiddleware[];
4546
/** List of subagent specifications for task delegation */
46-
subagents?: SubAgent[];
47+
subagents?: (SubAgent | CompiledSubAgent)[];
4748
/** Structured output response format for the agent */
4849
responseFormat?: any; // ResponseFormat type is complex, using any for now
4950
/** Optional schema for context (not persisted between invocations) */

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export {
1515
type FilesystemMiddlewareOptions,
1616
type SubAgentMiddlewareOptions,
1717
type SubAgent,
18+
type CompiledSubAgent,
1819
type FileData,
1920
} from "./middleware/index.js";
2021

src/middleware/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ export {
77
createSubAgentMiddleware,
88
type SubAgentMiddlewareOptions,
99
type SubAgent,
10+
type CompiledSubAgent,
1011
} from "./subagents.js";
1112
export { createPatchToolCallsMiddleware } from "./patch_tool_calls.js";

src/middleware/subagents.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ When NOT to use the task tool:
170170
- Remember to use the \`task\` tool to silo independent tasks within a multi-part objective.
171171
- 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.`;
172172

173+
/**
174+
* Type definitions for pre-compiled agents.
175+
*/
176+
export interface CompiledSubAgent {
177+
/** The name of the agent */
178+
name: string;
179+
/** The description of the agent */
180+
description: string;
181+
/** The agent instance */
182+
runnable: ReactAgent<any, any, any, any> | Runnable;
183+
}
184+
173185
/**
174186
* Type definitions for subagents
175187
*/
@@ -238,7 +250,7 @@ function getSubagents(options: {
238250
defaultTools: StructuredTool[];
239251
defaultMiddleware: AgentMiddleware[] | null;
240252
defaultInterruptOn: Record<string, boolean | InterruptOnConfig> | null;
241-
subagents: Array<SubAgent>;
253+
subagents: (SubAgent | CompiledSubAgent)[];
242254
generalPurposeAgent: boolean;
243255
}): {
244256
agents: Record<string, ReactAgent<any, any, any, any> | Runnable>;
@@ -285,19 +297,24 @@ function getSubagents(options: {
285297
`- ${agentParams.name}: ${agentParams.description}`,
286298
);
287299

288-
const middleware = agentParams.middleware
289-
? [...defaultSubagentMiddleware, ...agentParams.middleware]
290-
: [...defaultSubagentMiddleware];
291-
292-
const interruptOn = agentParams.interruptOn || defaultInterruptOn;
293-
if (interruptOn) middleware.push(humanInTheLoopMiddleware({ interruptOn }));
294-
295-
agents[agentParams.name] = createAgent({
296-
model: agentParams.model ?? defaultModel,
297-
systemPrompt: agentParams.systemPrompt,
298-
tools: agentParams.tools ?? defaultTools,
299-
middleware,
300-
});
300+
if ("runnable" in agentParams) {
301+
agents[agentParams.name] = agentParams.runnable;
302+
} else {
303+
const middleware = agentParams.middleware
304+
? [...defaultSubagentMiddleware, ...agentParams.middleware]
305+
: [...defaultSubagentMiddleware];
306+
307+
const interruptOn = agentParams.interruptOn || defaultInterruptOn;
308+
if (interruptOn)
309+
middleware.push(humanInTheLoopMiddleware({ interruptOn }));
310+
311+
agents[agentParams.name] = createAgent({
312+
model: agentParams.model ?? defaultModel,
313+
systemPrompt: agentParams.systemPrompt,
314+
tools: agentParams.tools ?? defaultTools,
315+
middleware,
316+
});
317+
}
301318
}
302319

303320
return { agents, descriptions: subagentDescriptions };
@@ -311,7 +328,7 @@ function createTaskTool(options: {
311328
defaultTools: StructuredTool[];
312329
defaultMiddleware: AgentMiddleware[] | null;
313330
defaultInterruptOn: Record<string, boolean | InterruptOnConfig> | null;
314-
subagents: Array<SubAgent>;
331+
subagents: (SubAgent | CompiledSubAgent)[];
315332
generalPurposeAgent: boolean;
316333
taskDescription: string | null;
317334
}) {
@@ -406,7 +423,7 @@ export interface SubAgentMiddlewareOptions {
406423
/** The tool configs for the default general-purpose subagent */
407424
defaultInterruptOn?: Record<string, boolean | InterruptOnConfig> | null;
408425
/** A list of additional subagents to provide to the agent */
409-
subagents?: Array<SubAgent>;
426+
subagents?: (SubAgent | CompiledSubAgent)[];
410427
/** Full system prompt override */
411428
systemPrompt?: string | null;
412429
/** Whether to include the general-purpose agent */

tests/integration/subagents.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,45 @@ describe("Subagent Middleware Integration Tests", () => {
258258
},
259259
);
260260

261+
it.concurrent(
262+
"should use pre-compiled subagent",
263+
{ timeout: 60000 },
264+
async () => {
265+
const customSubagent = createAgent({
266+
model: SAMPLE_MODEL,
267+
systemPrompt: "Use the get_weather tool to get the weather in a city.",
268+
tools: [getWeather],
269+
});
270+
271+
const agent = createAgent({
272+
model: SAMPLE_MODEL,
273+
systemPrompt: "Use the task tool to call a subagent.",
274+
middleware: [
275+
createSubAgentMiddleware({
276+
defaultModel: SAMPLE_MODEL,
277+
defaultTools: [],
278+
subagents: [
279+
{
280+
name: "weather",
281+
description: "This subagent can get weather in cities.",
282+
runnable: customSubagent,
283+
},
284+
],
285+
}),
286+
],
287+
});
288+
289+
const expectedToolCalls = [
290+
{ name: "task", args: { subagent_type: "weather" } },
291+
{ name: "get_weather" },
292+
];
293+
294+
await assertExpectedSubgraphActions(expectedToolCalls, agent, {
295+
messages: [new HumanMessage("What is the weather in Tokyo?")],
296+
});
297+
},
298+
);
299+
261300
it.concurrent(
262301
"should handle multiple subagents without middleware accumulation",
263302
{ timeout: 120000 },

0 commit comments

Comments
 (0)