deepagents 0.3.6__py3-none-any.whl → 0.3.7__py3-none-any.whl

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.
@@ -114,6 +114,8 @@ from langchain_core.runnables import RunnableConfig
114
114
  from langgraph.prebuilt import ToolRuntime
115
115
  from langgraph.runtime import Runtime
116
116
 
117
+ from deepagents.middleware._utils import append_to_system_message
118
+
117
119
  logger = logging.getLogger(__name__)
118
120
 
119
121
  # Security: Maximum size for SKILL.md files to prevent DoS attacks (10MB)
@@ -558,18 +560,20 @@ class SkillsMiddleware(AgentMiddleware):
558
560
  lines = []
559
561
  for skill in skills:
560
562
  lines.append(f"- **{skill['name']}**: {skill['description']}")
563
+ if skill["allowed_tools"]:
564
+ lines.append(f" -> Allowed tools: {', '.join(skill['allowed_tools'])}")
561
565
  lines.append(f" -> Read `{skill['path']}` for full instructions")
562
566
 
563
567
  return "\n".join(lines)
564
568
 
565
569
  def modify_request(self, request: ModelRequest) -> ModelRequest:
566
- """Inject skills documentation into a model request's system prompt.
570
+ """Inject skills documentation into a model request's system message.
567
571
 
568
572
  Args:
569
573
  request: Model request to modify
570
574
 
571
575
  Returns:
572
- New model request with skills documentation injected into system prompt
576
+ New model request with skills documentation injected into system message
573
577
  """
574
578
  skills_metadata = request.state.get("skills_metadata", [])
575
579
  skills_locations = self._format_skills_locations()
@@ -580,12 +584,9 @@ class SkillsMiddleware(AgentMiddleware):
580
584
  skills_list=skills_list,
581
585
  )
582
586
 
583
- if request.system_prompt:
584
- system_prompt = request.system_prompt + "\n\n" + skills_section
585
- else:
586
- system_prompt = skills_section
587
+ new_system_message = append_to_system_message(request.system_message, skills_section)
587
588
 
588
- return request.override(system_prompt=system_prompt)
589
+ return request.override(system_message=new_system_message)
589
590
 
590
591
  def before_agent(self, state: SkillsState, runtime: Runtime, config: RunnableConfig) -> SkillsStateUpdate | None:
591
592
  """Load skills metadata before agent execution (synchronous).
@@ -602,7 +603,7 @@ class SkillsMiddleware(AgentMiddleware):
602
603
  config: Runnable config.
603
604
 
604
605
  Returns:
605
- State update with skills_metadata populated, or None if already present
606
+ State update with `skills_metadata` populated, or `None` if already present
606
607
  """
607
608
  # Skip if skills_metadata is already present in state (even if empty)
608
609
  if "skills_metadata" in state:
@@ -637,7 +638,7 @@ class SkillsMiddleware(AgentMiddleware):
637
638
  config: Runnable config.
638
639
 
639
640
  Returns:
640
- State update with skills_metadata populated, or None if already present
641
+ State update with `skills_metadata` populated, or `None` if already present
641
642
  """
642
643
  # Skip if skills_metadata is already present in state (even if empty)
643
644
  if "skills_metadata" in state:
@@ -1,7 +1,7 @@
1
1
  """Middleware for providing subagents to an agent via a `task` tool."""
2
2
 
3
3
  from collections.abc import Awaitable, Callable, Sequence
4
- from typing import Any, NotRequired, TypedDict, cast
4
+ from typing import Annotated, Any, NotRequired, TypedDict, cast
5
5
 
6
6
  from langchain.agents import create_agent
7
7
  from langchain.agents.middleware import HumanInTheLoopMiddleware, InterruptOnConfig
@@ -13,6 +13,8 @@ from langchain_core.runnables import Runnable
13
13
  from langchain_core.tools import StructuredTool
14
14
  from langgraph.types import Command
15
15
 
16
+ from deepagents.middleware._utils import append_to_system_message
17
+
16
18
 
17
19
  class SubAgent(TypedDict):
18
20
  """Specification for an agent.
@@ -71,10 +73,14 @@ class SubAgent(TypedDict):
71
73
  class CompiledSubAgent(TypedDict):
72
74
  """A pre-compiled agent spec.
73
75
 
74
- Important: The runnable's state schema must include a 'messages' key.
75
- This is required for the subagent to communicate results back to the main agent.
76
+ !!! note
77
+
78
+ The runnable's state schema must include a 'messages' key.
79
+
80
+ This is required for the subagent to communicate results back to the main agent.
81
+
76
82
  When the subagent completes, the final message in the 'messages' list will be
77
- extracted and returned as a ToolMessage to the parent agent.
83
+ extracted and returned as a `ToolMessage` to the parent agent.
78
84
  """
79
85
 
80
86
  name: str
@@ -84,7 +90,16 @@ class CompiledSubAgent(TypedDict):
84
90
  """What this subagent does."""
85
91
 
86
92
  runnable: Runnable
87
- """The Runnable to use for the agent. Must return a state with a 'messages' key."""
93
+ """A custom agent implementation.
94
+
95
+ Create a custom agent using either:
96
+
97
+ 1. LangChain's [`create_agent()`](https://docs.langchain.com/oss/python/langchain/quickstart)
98
+ 2. A custom graph using [`langgraph`](https://docs.langchain.com/oss/python/langgraph/quickstart)
99
+
100
+ If you're creating a custom graph, make sure the state schema includes a 'messages' key.
101
+ This is required for the subagent to communicate results back to the main agent.
102
+ """
88
103
 
89
104
 
90
105
  DEFAULT_SUBAGENT_PROMPT = "In order to complete the objective that the user asks of you, you have access to a number of standard tools."
@@ -384,8 +399,11 @@ def _create_task_tool(
384
399
  task_description = task_description.format(available_agents=subagent_description_str)
385
400
 
386
401
  def task(
387
- description: str,
388
- subagent_type: str,
402
+ description: Annotated[
403
+ str,
404
+ "A detailed description of the task for the subagent to perform autonomously. Include all necessary context and specify the expected output format.", # noqa: E501
405
+ ],
406
+ subagent_type: Annotated[str, "The type of subagent to use. Must be one of the available agent types listed in the tool description."],
389
407
  runtime: ToolRuntime,
390
408
  ) -> str | Command:
391
409
  if subagent_type not in subagent_graphs:
@@ -399,8 +417,11 @@ def _create_task_tool(
399
417
  return _return_command_with_state_update(result, runtime.tool_call_id)
400
418
 
401
419
  async def atask(
402
- description: str,
403
- subagent_type: str,
420
+ description: Annotated[
421
+ str,
422
+ "A detailed description of the task for the subagent to perform autonomously. Include all necessary context and specify the expected output format.", # noqa: E501
423
+ ],
424
+ subagent_type: Annotated[str, "The type of subagent to use. Must be one of the available agent types listed in the tool description."],
404
425
  runtime: ToolRuntime,
405
426
  ) -> str | Command:
406
427
  if subagent_type not in subagent_graphs:
@@ -439,18 +460,24 @@ class SubAgentMiddleware(AgentMiddleware):
439
460
 
440
461
  Args:
441
462
  default_model: The model to use for subagents.
442
- Can be a LanguageModelLike or a dict for init_chat_model.
463
+
464
+ Can be a `LanguageModelLike` or a dict for `init_chat_model`.
443
465
  default_tools: The tools to use for the default general-purpose subagent.
444
- default_middleware: Default middleware to apply to all subagents. If `None` (default),
445
- no default middleware is applied. Pass a list to specify custom middleware.
446
- default_interrupt_on: The tool configs to use for the default general-purpose subagent. These
447
- are also the fallback for any subagents that don't specify their own tool configs.
466
+ default_middleware: Default middleware to apply to all subagents.
467
+
468
+ If `None`, no default middleware is applied.
469
+
470
+ Pass a list to specify custom middleware.
471
+ default_interrupt_on: The tool configs to use for the default general-purpose subagent.
472
+
473
+ These are also the fallback for any subagents that don't specify their own tool configs.
448
474
  subagents: A list of additional subagents to provide to the agent.
449
475
  system_prompt: Full system prompt override. When provided, completely replaces
450
476
  the agent's system prompt.
451
- general_purpose_agent: Whether to include the general-purpose agent. Defaults to `True`.
452
- task_description: Custom description for the task tool. If `None`, uses the
453
- default description template.
477
+ general_purpose_agent: Whether to include the general-purpose agent.
478
+ task_description: Custom description for the task tool.
479
+
480
+ If `None`, uses the default description template.
454
481
 
455
482
  Example:
456
483
  ```python
@@ -494,7 +521,7 @@ class SubAgentMiddleware(AgentMiddleware):
494
521
  general_purpose_agent: bool = True,
495
522
  task_description: str | None = None,
496
523
  ) -> None:
497
- """Initialize the SubAgentMiddleware."""
524
+ """Initialize the `SubAgentMiddleware`."""
498
525
  super().__init__()
499
526
  self.system_prompt = system_prompt
500
527
  task_tool = _create_task_tool(
@@ -513,10 +540,10 @@ class SubAgentMiddleware(AgentMiddleware):
513
540
  request: ModelRequest,
514
541
  handler: Callable[[ModelRequest], ModelResponse],
515
542
  ) -> ModelResponse:
516
- """Update the system prompt to include instructions on using subagents."""
543
+ """Update the system message to include instructions on using subagents."""
517
544
  if self.system_prompt is not None:
518
- system_prompt = request.system_prompt + "\n\n" + self.system_prompt if request.system_prompt else self.system_prompt
519
- return handler(request.override(system_prompt=system_prompt))
545
+ new_system_message = append_to_system_message(request.system_message, self.system_prompt)
546
+ return handler(request.override(system_message=new_system_message))
520
547
  return handler(request)
521
548
 
522
549
  async def awrap_model_call(
@@ -524,8 +551,8 @@ class SubAgentMiddleware(AgentMiddleware):
524
551
  request: ModelRequest,
525
552
  handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
526
553
  ) -> ModelResponse:
527
- """(async) Update the system prompt to include instructions on using subagents."""
554
+ """(async) Update the system message to include instructions on using subagents."""
528
555
  if self.system_prompt is not None:
529
- system_prompt = request.system_prompt + "\n\n" + self.system_prompt if request.system_prompt else self.system_prompt
530
- return await handler(request.override(system_prompt=system_prompt))
556
+ new_system_message = append_to_system_message(request.system_message, self.system_prompt)
557
+ return await handler(request.override(system_message=new_system_message))
531
558
  return await handler(request)