deepagents 0.2.5__py3-none-any.whl → 0.2.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.
- deepagents/backends/composite.py +37 -2
- deepagents/backends/protocol.py +48 -0
- deepagents/backends/sandbox.py +341 -0
- deepagents/backends/store.py +3 -11
- deepagents/graph.py +7 -3
- deepagents/middleware/__init__.py +0 -2
- deepagents/middleware/filesystem.py +224 -21
- deepagents/middleware/subagents.py +7 -4
- {deepagents-0.2.5.dist-info → deepagents-0.2.7.dist-info}/METADATA +5 -4
- deepagents-0.2.7.dist-info/RECORD +18 -0
- deepagents-0.2.7.dist-info/top_level.txt +1 -0
- deepagents/middleware/resumable_shell.py +0 -86
- deepagents-0.2.5.dist-info/RECORD +0 -38
- deepagents-0.2.5.dist-info/licenses/LICENSE +0 -21
- deepagents-0.2.5.dist-info/top_level.txt +0 -2
- deepagents-cli/README.md +0 -3
- deepagents-cli/deepagents_cli/README.md +0 -196
- deepagents-cli/deepagents_cli/__init__.py +0 -5
- deepagents-cli/deepagents_cli/__main__.py +0 -6
- deepagents-cli/deepagents_cli/agent.py +0 -278
- deepagents-cli/deepagents_cli/agent_memory.py +0 -226
- deepagents-cli/deepagents_cli/commands.py +0 -89
- deepagents-cli/deepagents_cli/config.py +0 -118
- deepagents-cli/deepagents_cli/default_agent_prompt.md +0 -110
- deepagents-cli/deepagents_cli/execution.py +0 -636
- deepagents-cli/deepagents_cli/file_ops.py +0 -347
- deepagents-cli/deepagents_cli/input.py +0 -270
- deepagents-cli/deepagents_cli/main.py +0 -226
- deepagents-cli/deepagents_cli/py.typed +0 -0
- deepagents-cli/deepagents_cli/token_utils.py +0 -63
- deepagents-cli/deepagents_cli/tools.py +0 -140
- deepagents-cli/deepagents_cli/ui.py +0 -489
- deepagents-cli/tests/test_file_ops.py +0 -119
- deepagents-cli/tests/test_placeholder.py +0 -5
- {deepagents-0.2.5.dist-info → deepagents-0.2.7.dist-info}/WHEEL +0 -0
|
@@ -19,7 +19,15 @@ from langgraph.types import Command
|
|
|
19
19
|
from typing_extensions import TypedDict
|
|
20
20
|
|
|
21
21
|
from deepagents.backends import StateBackend
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
# Re-export type here for backwards compatibility
|
|
24
|
+
from deepagents.backends.protocol import BACKEND_TYPES as BACKEND_TYPES
|
|
25
|
+
from deepagents.backends.protocol import (
|
|
26
|
+
BackendProtocol,
|
|
27
|
+
EditResult,
|
|
28
|
+
SandboxBackendProtocol,
|
|
29
|
+
WriteResult,
|
|
30
|
+
)
|
|
23
31
|
from deepagents.backends.utils import (
|
|
24
32
|
format_content_with_line_numbers,
|
|
25
33
|
format_grep_matches,
|
|
@@ -32,7 +40,6 @@ MAX_LINE_LENGTH = 2000
|
|
|
32
40
|
LINE_NUMBER_WIDTH = 6
|
|
33
41
|
DEFAULT_READ_OFFSET = 0
|
|
34
42
|
DEFAULT_READ_LIMIT = 500
|
|
35
|
-
BACKEND_TYPES = BackendProtocol | BackendFactory
|
|
36
43
|
|
|
37
44
|
|
|
38
45
|
class FileData(TypedDict):
|
|
@@ -211,6 +218,50 @@ Examples:
|
|
|
211
218
|
- Search Python files only: `grep(pattern="import", glob="*.py")`
|
|
212
219
|
- Show matching lines: `grep(pattern="error", output_mode="content")`"""
|
|
213
220
|
|
|
221
|
+
EXECUTE_TOOL_DESCRIPTION = """Executes a given command in the sandbox environment with proper handling and security measures.
|
|
222
|
+
|
|
223
|
+
Before executing the command, please follow these steps:
|
|
224
|
+
|
|
225
|
+
1. Directory Verification:
|
|
226
|
+
- If the command will create new directories or files, first use the ls tool to verify the parent directory exists and is the correct location
|
|
227
|
+
- For example, before running "mkdir foo/bar", first use ls to check that "foo" exists and is the intended parent directory
|
|
228
|
+
|
|
229
|
+
2. Command Execution:
|
|
230
|
+
- Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt")
|
|
231
|
+
- Examples of proper quoting:
|
|
232
|
+
- cd "/Users/name/My Documents" (correct)
|
|
233
|
+
- cd /Users/name/My Documents (incorrect - will fail)
|
|
234
|
+
- python "/path/with spaces/script.py" (correct)
|
|
235
|
+
- python /path/with spaces/script.py (incorrect - will fail)
|
|
236
|
+
- After ensuring proper quoting, execute the command
|
|
237
|
+
- Capture the output of the command
|
|
238
|
+
|
|
239
|
+
Usage notes:
|
|
240
|
+
- The command parameter is required
|
|
241
|
+
- Commands run in an isolated sandbox environment
|
|
242
|
+
- Returns combined stdout/stderr output with exit code
|
|
243
|
+
- If the output is very large, it may be truncated
|
|
244
|
+
- VERY IMPORTANT: You MUST avoid using search commands like find and grep. Instead use the grep, glob tools to search. You MUST avoid read tools like cat, head, tail, and use read_file to read files.
|
|
245
|
+
- When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings)
|
|
246
|
+
- Use '&&' when commands depend on each other (e.g., "mkdir dir && cd dir")
|
|
247
|
+
- Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
|
|
248
|
+
- Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of cd
|
|
249
|
+
|
|
250
|
+
Examples:
|
|
251
|
+
Good examples:
|
|
252
|
+
- execute(command="pytest /foo/bar/tests")
|
|
253
|
+
- execute(command="python /path/to/script.py")
|
|
254
|
+
- execute(command="npm install && npm test")
|
|
255
|
+
|
|
256
|
+
Bad examples (avoid these):
|
|
257
|
+
- execute(command="cd /foo/bar && pytest tests") # Use absolute path instead
|
|
258
|
+
- execute(command="cat file.txt") # Use read_file tool instead
|
|
259
|
+
- execute(command="find . -name '*.py'") # Use glob tool instead
|
|
260
|
+
- execute(command="grep -r 'pattern' .") # Use grep tool instead
|
|
261
|
+
|
|
262
|
+
Note: This tool is only available if the backend supports execution (SandboxBackendProtocol).
|
|
263
|
+
If execution is not supported, the tool will return an error message."""
|
|
264
|
+
|
|
214
265
|
FILESYSTEM_SYSTEM_PROMPT = """## Filesystem Tools `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`
|
|
215
266
|
|
|
216
267
|
You have access to a filesystem which you can interact with using these tools.
|
|
@@ -223,6 +274,13 @@ All file paths must start with a /.
|
|
|
223
274
|
- glob: find files matching a pattern (e.g., "**/*.py")
|
|
224
275
|
- grep: search for text within files"""
|
|
225
276
|
|
|
277
|
+
EXECUTION_SYSTEM_PROMPT = """## Execute Tool `execute`
|
|
278
|
+
|
|
279
|
+
You have access to an `execute` tool for running shell commands in a sandboxed environment.
|
|
280
|
+
Use this tool to run commands, scripts, tests, builds, and other shell operations.
|
|
281
|
+
|
|
282
|
+
- execute: run a shell command in the sandbox (returns output and exit code)"""
|
|
283
|
+
|
|
226
284
|
|
|
227
285
|
def _get_backend(backend: BACKEND_TYPES, runtime: ToolRuntime) -> BackendProtocol:
|
|
228
286
|
"""Get the resolved backend instance from backend or factory.
|
|
@@ -440,6 +498,80 @@ def _grep_tool_generator(
|
|
|
440
498
|
return grep
|
|
441
499
|
|
|
442
500
|
|
|
501
|
+
def _supports_execution(backend: BackendProtocol) -> bool:
|
|
502
|
+
"""Check if a backend supports command execution.
|
|
503
|
+
|
|
504
|
+
For CompositeBackend, checks if the default backend supports execution.
|
|
505
|
+
For other backends, checks if they implement SandboxBackendProtocol.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
backend: The backend to check.
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
True if the backend supports execution, False otherwise.
|
|
512
|
+
"""
|
|
513
|
+
# Import here to avoid circular dependency
|
|
514
|
+
from deepagents.backends.composite import CompositeBackend
|
|
515
|
+
|
|
516
|
+
# For CompositeBackend, check the default backend
|
|
517
|
+
if isinstance(backend, CompositeBackend):
|
|
518
|
+
return isinstance(backend.default, SandboxBackendProtocol)
|
|
519
|
+
|
|
520
|
+
# For other backends, use isinstance check
|
|
521
|
+
return isinstance(backend, SandboxBackendProtocol)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _execute_tool_generator(
|
|
525
|
+
backend: BackendProtocol | Callable[[ToolRuntime], BackendProtocol],
|
|
526
|
+
custom_description: str | None = None,
|
|
527
|
+
) -> BaseTool:
|
|
528
|
+
"""Generate the execute tool for sandbox command execution.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
backend: Backend to use for execution, or a factory function that takes runtime and returns a backend.
|
|
532
|
+
custom_description: Optional custom description for the tool.
|
|
533
|
+
|
|
534
|
+
Returns:
|
|
535
|
+
Configured execute tool that runs commands if backend supports SandboxBackendProtocol.
|
|
536
|
+
"""
|
|
537
|
+
tool_description = custom_description or EXECUTE_TOOL_DESCRIPTION
|
|
538
|
+
|
|
539
|
+
@tool(description=tool_description)
|
|
540
|
+
def execute(
|
|
541
|
+
command: str,
|
|
542
|
+
runtime: ToolRuntime[None, FilesystemState],
|
|
543
|
+
) -> str:
|
|
544
|
+
resolved_backend = _get_backend(backend, runtime)
|
|
545
|
+
|
|
546
|
+
# Runtime check - fail gracefully if not supported
|
|
547
|
+
if not _supports_execution(resolved_backend):
|
|
548
|
+
return (
|
|
549
|
+
"Error: Execution not available. This agent's backend "
|
|
550
|
+
"does not support command execution (SandboxBackendProtocol). "
|
|
551
|
+
"To use the execute tool, provide a backend that implements SandboxBackendProtocol."
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
try:
|
|
555
|
+
result = resolved_backend.execute(command)
|
|
556
|
+
except NotImplementedError as e:
|
|
557
|
+
# Handle case where execute() exists but raises NotImplementedError
|
|
558
|
+
return f"Error: Execution not available. {e}"
|
|
559
|
+
|
|
560
|
+
# Format output for LLM consumption
|
|
561
|
+
parts = [result.output]
|
|
562
|
+
|
|
563
|
+
if result.exit_code is not None:
|
|
564
|
+
status = "succeeded" if result.exit_code == 0 else "failed"
|
|
565
|
+
parts.append(f"\n[Command {status} with exit code {result.exit_code}]")
|
|
566
|
+
|
|
567
|
+
if result.truncated:
|
|
568
|
+
parts.append("\n[Output was truncated due to size limits]")
|
|
569
|
+
|
|
570
|
+
return "".join(parts)
|
|
571
|
+
|
|
572
|
+
return execute
|
|
573
|
+
|
|
574
|
+
|
|
443
575
|
TOOL_GENERATORS = {
|
|
444
576
|
"ls": _ls_tool_generator,
|
|
445
577
|
"read_file": _read_file_tool_generator,
|
|
@@ -447,6 +579,7 @@ TOOL_GENERATORS = {
|
|
|
447
579
|
"edit_file": _edit_file_tool_generator,
|
|
448
580
|
"glob": _glob_tool_generator,
|
|
449
581
|
"grep": _grep_tool_generator,
|
|
582
|
+
"execute": _execute_tool_generator,
|
|
450
583
|
}
|
|
451
584
|
|
|
452
585
|
|
|
@@ -454,18 +587,19 @@ def _get_filesystem_tools(
|
|
|
454
587
|
backend: BackendProtocol,
|
|
455
588
|
custom_tool_descriptions: dict[str, str] | None = None,
|
|
456
589
|
) -> list[BaseTool]:
|
|
457
|
-
"""Get filesystem tools.
|
|
590
|
+
"""Get filesystem and execution tools.
|
|
458
591
|
|
|
459
592
|
Args:
|
|
460
|
-
backend: Backend to use for file storage, or a factory function that takes runtime and returns a backend.
|
|
593
|
+
backend: Backend to use for file storage and optional execution, or a factory function that takes runtime and returns a backend.
|
|
461
594
|
custom_tool_descriptions: Optional custom descriptions for tools.
|
|
462
595
|
|
|
463
596
|
Returns:
|
|
464
|
-
List of configured
|
|
597
|
+
List of configured tools: ls, read_file, write_file, edit_file, glob, grep, execute.
|
|
465
598
|
"""
|
|
466
599
|
if custom_tool_descriptions is None:
|
|
467
600
|
custom_tool_descriptions = {}
|
|
468
601
|
tools = []
|
|
602
|
+
|
|
469
603
|
for tool_name, tool_generator in TOOL_GENERATORS.items():
|
|
470
604
|
tool = tool_generator(backend, custom_tool_descriptions.get(tool_name))
|
|
471
605
|
tools.append(tool)
|
|
@@ -483,16 +617,20 @@ Here are the first 10 lines of the result:
|
|
|
483
617
|
|
|
484
618
|
|
|
485
619
|
class FilesystemMiddleware(AgentMiddleware):
|
|
486
|
-
"""Middleware for providing filesystem tools to an agent.
|
|
620
|
+
"""Middleware for providing filesystem and optional execution tools to an agent.
|
|
487
621
|
|
|
488
|
-
This middleware adds
|
|
622
|
+
This middleware adds filesystem tools to the agent: ls, read_file, write_file,
|
|
489
623
|
edit_file, glob, and grep. Files can be stored using any backend that implements
|
|
490
624
|
the BackendProtocol.
|
|
491
625
|
|
|
626
|
+
If the backend implements SandboxBackendProtocol, an execute tool is also added
|
|
627
|
+
for running shell commands.
|
|
628
|
+
|
|
492
629
|
Args:
|
|
493
|
-
backend: Backend for file storage. If not provided, defaults to StateBackend
|
|
630
|
+
backend: Backend for file storage and optional execution. If not provided, defaults to StateBackend
|
|
494
631
|
(ephemeral storage in agent state). For persistent storage or hybrid setups,
|
|
495
|
-
use CompositeBackend with custom routes.
|
|
632
|
+
use CompositeBackend with custom routes. For execution support, use a backend
|
|
633
|
+
that implements SandboxBackendProtocol.
|
|
496
634
|
system_prompt: Optional custom system prompt override.
|
|
497
635
|
custom_tool_descriptions: Optional custom tool descriptions override.
|
|
498
636
|
tool_token_limit_before_evict: Optional token limit before evicting a tool result to the filesystem.
|
|
@@ -500,15 +638,21 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
500
638
|
Example:
|
|
501
639
|
```python
|
|
502
640
|
from deepagents.middleware.filesystem import FilesystemMiddleware
|
|
503
|
-
from deepagents.
|
|
641
|
+
from deepagents.backends import StateBackend, StoreBackend, CompositeBackend
|
|
504
642
|
from langchain.agents import create_agent
|
|
505
643
|
|
|
506
|
-
# Ephemeral storage only (default)
|
|
644
|
+
# Ephemeral storage only (default, no execution)
|
|
507
645
|
agent = create_agent(middleware=[FilesystemMiddleware()])
|
|
508
646
|
|
|
509
647
|
# With hybrid storage (ephemeral + persistent /memories/)
|
|
510
648
|
backend = CompositeBackend(default=StateBackend(), routes={"/memories/": StoreBackend()})
|
|
511
|
-
agent = create_agent(middleware=[FilesystemMiddleware(
|
|
649
|
+
agent = create_agent(middleware=[FilesystemMiddleware(backend=backend)])
|
|
650
|
+
|
|
651
|
+
# With sandbox backend (supports execution)
|
|
652
|
+
from my_sandbox import DockerSandboxBackend
|
|
653
|
+
|
|
654
|
+
sandbox = DockerSandboxBackend(container_id="my-container")
|
|
655
|
+
agent = create_agent(middleware=[FilesystemMiddleware(backend=sandbox)])
|
|
512
656
|
```
|
|
513
657
|
"""
|
|
514
658
|
|
|
@@ -525,7 +669,8 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
525
669
|
"""Initialize the filesystem middleware.
|
|
526
670
|
|
|
527
671
|
Args:
|
|
528
|
-
backend: Backend for file storage, or a factory callable.
|
|
672
|
+
backend: Backend for file storage and optional execution, or a factory callable.
|
|
673
|
+
Defaults to StateBackend if not provided.
|
|
529
674
|
system_prompt: Optional custom system prompt override.
|
|
530
675
|
custom_tool_descriptions: Optional custom tool descriptions override.
|
|
531
676
|
tool_token_limit_before_evict: Optional token limit before evicting a tool result to the filesystem.
|
|
@@ -535,8 +680,8 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
535
680
|
# Use provided backend or default to StateBackend factory
|
|
536
681
|
self.backend = backend if backend is not None else (lambda rt: StateBackend(rt))
|
|
537
682
|
|
|
538
|
-
# Set system prompt (allow full override)
|
|
539
|
-
self.
|
|
683
|
+
# Set system prompt (allow full override or None to generate dynamically)
|
|
684
|
+
self._custom_system_prompt = system_prompt
|
|
540
685
|
|
|
541
686
|
self.tools = _get_filesystem_tools(self.backend, custom_tool_descriptions)
|
|
542
687
|
|
|
@@ -558,7 +703,7 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
558
703
|
request: ModelRequest,
|
|
559
704
|
handler: Callable[[ModelRequest], ModelResponse],
|
|
560
705
|
) -> ModelResponse:
|
|
561
|
-
"""Update the system prompt
|
|
706
|
+
"""Update the system prompt and filter tools based on backend capabilities.
|
|
562
707
|
|
|
563
708
|
Args:
|
|
564
709
|
request: The model request being processed.
|
|
@@ -567,8 +712,37 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
567
712
|
Returns:
|
|
568
713
|
The model response from the handler.
|
|
569
714
|
"""
|
|
570
|
-
if
|
|
571
|
-
|
|
715
|
+
# Check if execute tool is present and if backend supports it
|
|
716
|
+
has_execute_tool = any((tool.name if hasattr(tool, "name") else tool.get("name")) == "execute" for tool in request.tools)
|
|
717
|
+
|
|
718
|
+
backend_supports_execution = False
|
|
719
|
+
if has_execute_tool:
|
|
720
|
+
# Resolve backend to check execution support
|
|
721
|
+
backend = self._get_backend(request.runtime)
|
|
722
|
+
backend_supports_execution = _supports_execution(backend)
|
|
723
|
+
|
|
724
|
+
# If execute tool exists but backend doesn't support it, filter it out
|
|
725
|
+
if not backend_supports_execution:
|
|
726
|
+
filtered_tools = [tool for tool in request.tools if (tool.name if hasattr(tool, "name") else tool.get("name")) != "execute"]
|
|
727
|
+
request = request.override(tools=filtered_tools)
|
|
728
|
+
has_execute_tool = False
|
|
729
|
+
|
|
730
|
+
# Use custom system prompt if provided, otherwise generate dynamically
|
|
731
|
+
if self._custom_system_prompt is not None:
|
|
732
|
+
system_prompt = self._custom_system_prompt
|
|
733
|
+
else:
|
|
734
|
+
# Build dynamic system prompt based on available tools
|
|
735
|
+
prompt_parts = [FILESYSTEM_SYSTEM_PROMPT]
|
|
736
|
+
|
|
737
|
+
# Add execution instructions if execute tool is available
|
|
738
|
+
if has_execute_tool and backend_supports_execution:
|
|
739
|
+
prompt_parts.append(EXECUTION_SYSTEM_PROMPT)
|
|
740
|
+
|
|
741
|
+
system_prompt = "\n\n".join(prompt_parts)
|
|
742
|
+
|
|
743
|
+
if system_prompt:
|
|
744
|
+
request = request.override(system_prompt=request.system_prompt + "\n\n" + system_prompt if request.system_prompt else system_prompt)
|
|
745
|
+
|
|
572
746
|
return handler(request)
|
|
573
747
|
|
|
574
748
|
async def awrap_model_call(
|
|
@@ -576,7 +750,7 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
576
750
|
request: ModelRequest,
|
|
577
751
|
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
|
|
578
752
|
) -> ModelResponse:
|
|
579
|
-
"""(async) Update the system prompt
|
|
753
|
+
"""(async) Update the system prompt and filter tools based on backend capabilities.
|
|
580
754
|
|
|
581
755
|
Args:
|
|
582
756
|
request: The model request being processed.
|
|
@@ -585,8 +759,37 @@ class FilesystemMiddleware(AgentMiddleware):
|
|
|
585
759
|
Returns:
|
|
586
760
|
The model response from the handler.
|
|
587
761
|
"""
|
|
588
|
-
if
|
|
589
|
-
|
|
762
|
+
# Check if execute tool is present and if backend supports it
|
|
763
|
+
has_execute_tool = any((tool.name if hasattr(tool, "name") else tool.get("name")) == "execute" for tool in request.tools)
|
|
764
|
+
|
|
765
|
+
backend_supports_execution = False
|
|
766
|
+
if has_execute_tool:
|
|
767
|
+
# Resolve backend to check execution support
|
|
768
|
+
backend = self._get_backend(request.runtime)
|
|
769
|
+
backend_supports_execution = _supports_execution(backend)
|
|
770
|
+
|
|
771
|
+
# If execute tool exists but backend doesn't support it, filter it out
|
|
772
|
+
if not backend_supports_execution:
|
|
773
|
+
filtered_tools = [tool for tool in request.tools if (tool.name if hasattr(tool, "name") else tool.get("name")) != "execute"]
|
|
774
|
+
request = request.override(tools=filtered_tools)
|
|
775
|
+
has_execute_tool = False
|
|
776
|
+
|
|
777
|
+
# Use custom system prompt if provided, otherwise generate dynamically
|
|
778
|
+
if self._custom_system_prompt is not None:
|
|
779
|
+
system_prompt = self._custom_system_prompt
|
|
780
|
+
else:
|
|
781
|
+
# Build dynamic system prompt based on available tools
|
|
782
|
+
prompt_parts = [FILESYSTEM_SYSTEM_PROMPT]
|
|
783
|
+
|
|
784
|
+
# Add execution instructions if execute tool is available
|
|
785
|
+
if has_execute_tool and backend_supports_execution:
|
|
786
|
+
prompt_parts.append(EXECUTION_SYSTEM_PROMPT)
|
|
787
|
+
|
|
788
|
+
system_prompt = "\n\n".join(prompt_parts)
|
|
789
|
+
|
|
790
|
+
if system_prompt:
|
|
791
|
+
request = request.override(system_prompt=request.system_prompt + "\n\n" + system_prompt if request.system_prompt else system_prompt)
|
|
792
|
+
|
|
590
793
|
return await handler(request)
|
|
591
794
|
|
|
592
795
|
def _process_large_message(
|
|
@@ -322,10 +322,7 @@ def _create_task_tool(
|
|
|
322
322
|
)
|
|
323
323
|
|
|
324
324
|
def _validate_and_prepare_state(subagent_type: str, description: str, runtime: ToolRuntime) -> tuple[Runnable, dict]:
|
|
325
|
-
"""
|
|
326
|
-
if subagent_type not in subagent_graphs:
|
|
327
|
-
msg = f"Error: invoked agent of type {subagent_type}, the only allowed types are {[f'`{k}`' for k in subagent_graphs]}"
|
|
328
|
-
raise ValueError(msg)
|
|
325
|
+
"""Prepare state for invocation."""
|
|
329
326
|
subagent = subagent_graphs[subagent_type]
|
|
330
327
|
# Create a new state dict to avoid mutating the original
|
|
331
328
|
subagent_state = {k: v for k, v in runtime.state.items() if k not in _EXCLUDED_STATE_KEYS}
|
|
@@ -344,6 +341,9 @@ def _create_task_tool(
|
|
|
344
341
|
subagent_type: str,
|
|
345
342
|
runtime: ToolRuntime,
|
|
346
343
|
) -> str | Command:
|
|
344
|
+
if subagent_type not in subagent_graphs:
|
|
345
|
+
allowed_types = ", ".join([f"`{k}`" for k in subagent_graphs])
|
|
346
|
+
return f"We cannot invoke subagent {subagent_type} because it does not exist, the only allowed types are {allowed_types}"
|
|
347
347
|
subagent, subagent_state = _validate_and_prepare_state(subagent_type, description, runtime)
|
|
348
348
|
result = subagent.invoke(subagent_state)
|
|
349
349
|
if not runtime.tool_call_id:
|
|
@@ -356,6 +356,9 @@ def _create_task_tool(
|
|
|
356
356
|
subagent_type: str,
|
|
357
357
|
runtime: ToolRuntime,
|
|
358
358
|
) -> str | Command:
|
|
359
|
+
if subagent_type not in subagent_graphs:
|
|
360
|
+
allowed_types = ", ".join([f"`{k}`" for k in subagent_graphs])
|
|
361
|
+
return f"We cannot invoke subagent {subagent_type} because it does not exist, the only allowed types are {allowed_types}"
|
|
359
362
|
subagent, subagent_state = _validate_and_prepare_state(subagent_type, description, runtime)
|
|
360
363
|
result = await subagent.ainvoke(subagent_state)
|
|
361
364
|
if not runtime.tool_call_id:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: General purpose 'deep agent' with sub-agent spawning, todo list capabilities, and mock file system. Built on LangGraph.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
|
|
@@ -11,12 +11,13 @@ Project-URL: Slack, https://www.langchain.com/join-community
|
|
|
11
11
|
Project-URL: Reddit, https://www.reddit.com/r/LangChain/
|
|
12
12
|
Requires-Python: <4.0,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
14
|
Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0
|
|
16
15
|
Requires-Dist: langchain<2.0.0,>=1.0.2
|
|
17
16
|
Requires-Dist: langchain-core<2.0.0,>=1.0.0
|
|
18
17
|
Requires-Dist: wcmatch
|
|
19
|
-
|
|
18
|
+
Requires-Dist: daytona>=0.113.0
|
|
19
|
+
Requires-Dist: runloop-api-client>=0.66.1
|
|
20
|
+
Requires-Dist: tavily>=1.1.0
|
|
20
21
|
|
|
21
22
|
# 🧠🤖Deep Agents
|
|
22
23
|
|
|
@@ -347,7 +348,7 @@ Deep Agents are built with a modular middleware architecture. As a reminder, Dee
|
|
|
347
348
|
- A filesystem for storing context and long-term memories
|
|
348
349
|
- The ability to spawn subagents
|
|
349
350
|
|
|
350
|
-
Each of these features is implemented as separate middleware. When you create a deep agent with `create_deep_agent`, we automatically attach **
|
|
351
|
+
Each of these features is implemented as separate middleware. When you create a deep agent with `create_deep_agent`, we automatically attach **TodoListMiddleware**, **FilesystemMiddleware** and **SubAgentMiddleware** to your agent.
|
|
351
352
|
|
|
352
353
|
Middleware is a composable concept, and you can choose to add as many or as few middleware to an agent depending on your use case. That means that you can also use any of the aforementioned middleware independently!
|
|
353
354
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
deepagents/__init__.py,sha256=9BVNn4lfF5N8l2KY8Ttxi82zO609I-fGqoSIF7DAxiU,342
|
|
2
|
+
deepagents/graph.py,sha256=c6ggWJIPaFOK2OCWxZdGEPDfuDvjdDuqHY06-bUiqPg,6379
|
|
3
|
+
deepagents/backends/__init__.py,sha256=BOKu2cQ1OdMyO_l2rLqZQiXppYFmQbx7OIQb7WYwvZc,457
|
|
4
|
+
deepagents/backends/composite.py,sha256=2BSyAurAt1FXV7iFyajzVaRZvjGkUPBybg7J8E6kRNE,9548
|
|
5
|
+
deepagents/backends/filesystem.py,sha256=SsVDx__j_AARIwRzaDuokbxbkquJ_Lw3Qi7dLWaqRUs,18674
|
|
6
|
+
deepagents/backends/protocol.py,sha256=Hi6u3MWIfMUGFWwnIFwvmwJbHYsx8IxeU2aaoP_9OMk,5831
|
|
7
|
+
deepagents/backends/sandbox.py,sha256=JueMe_2cZcA359JIqIi8kDUkmdtevC4VbKHw-fBPOWs,10125
|
|
8
|
+
deepagents/backends/state.py,sha256=ST_tUExPxArJaA3U8vc1dyzxuYl2BQH-HU7P0eu_Ty8,6518
|
|
9
|
+
deepagents/backends/store.py,sha256=f2LVSl65Dg-BZl-cY3pl3RqrUJCBMBm2kuAzZEODwsE,13098
|
|
10
|
+
deepagents/backends/utils.py,sha256=Iyk2jW-gfoLvMnz-W_2FRCoJW_j3r1zoumU9iww-jd0,13973
|
|
11
|
+
deepagents/middleware/__init__.py,sha256=_OGIHcHZ2pRD0gzUBS7R48agwI6P7-FCBKBgjyaWlsg,303
|
|
12
|
+
deepagents/middleware/filesystem.py,sha256=3PAetXqWy0i9bE6moM0FDZAEmjMm_M48B4AWWYl4Luk,37271
|
|
13
|
+
deepagents/middleware/patch_tool_calls.py,sha256=PdNhxPaQqwnFkhEAZEE2kEzadTNAOO3_iJRA30WqpGE,1981
|
|
14
|
+
deepagents/middleware/subagents.py,sha256=RbNpWLXC0Bhr0nUIs40whybNnzNkhxG9Fie7QKsICRk,23748
|
|
15
|
+
deepagents-0.2.7.dist-info/METADATA,sha256=H5FgpjWX5qzV5uHcpm0ba4F1Jwtw_Kvj79U9mdUR3Os,18887
|
|
16
|
+
deepagents-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
deepagents-0.2.7.dist-info/top_level.txt,sha256=drAzchOzPNePwpb3_pbPuvLuayXkN7SNqeIKMBWJoAo,11
|
|
18
|
+
deepagents-0.2.7.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deepagents
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""Shell tool middleware that survives HITL pauses."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from collections.abc import Awaitable, Callable
|
|
6
|
-
from typing import Any, cast
|
|
7
|
-
|
|
8
|
-
from langchain.agents.middleware.shell_tool import (
|
|
9
|
-
ShellToolMiddleware,
|
|
10
|
-
ShellToolState,
|
|
11
|
-
_PersistentShellTool,
|
|
12
|
-
_SessionResources,
|
|
13
|
-
)
|
|
14
|
-
from langchain.agents.middleware.types import AgentState
|
|
15
|
-
from langchain.tools.tool_node import ToolCallRequest
|
|
16
|
-
from langchain_core.messages import ToolMessage
|
|
17
|
-
from langgraph.types import Command
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ResumableShellToolMiddleware(ShellToolMiddleware):
|
|
21
|
-
"""Shell middleware that recreates session resources after human interrupts.
|
|
22
|
-
|
|
23
|
-
``ShellToolMiddleware`` stores its session handle in middleware state using an
|
|
24
|
-
``UntrackedValue``. When a run pauses for human approval, that attribute is not
|
|
25
|
-
checkpointed. Upon resuming, LangGraph restores the state without the shell
|
|
26
|
-
resources, so the next tool execution fails with
|
|
27
|
-
``Shell session resources are unavailable``.
|
|
28
|
-
|
|
29
|
-
This subclass lazily recreates the shell session the first time a resumed run
|
|
30
|
-
touches the shell tool again and only performs shutdown when a session is
|
|
31
|
-
actually active. This keeps behaviour identical for uninterrupted runs while
|
|
32
|
-
allowing HITL pauses to succeed.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
def wrap_tool_call(
|
|
36
|
-
self,
|
|
37
|
-
request: ToolCallRequest,
|
|
38
|
-
handler: Callable[[ToolCallRequest], ToolMessage | Command],
|
|
39
|
-
) -> ToolMessage | Command:
|
|
40
|
-
if isinstance(request.tool, _PersistentShellTool):
|
|
41
|
-
resources = self._get_or_create_resources(request.state)
|
|
42
|
-
return self._run_shell_tool(
|
|
43
|
-
resources,
|
|
44
|
-
request.tool_call["args"],
|
|
45
|
-
tool_call_id=request.tool_call.get("id"),
|
|
46
|
-
)
|
|
47
|
-
return super().wrap_tool_call(request, handler)
|
|
48
|
-
|
|
49
|
-
async def awrap_tool_call(
|
|
50
|
-
self,
|
|
51
|
-
request: ToolCallRequest,
|
|
52
|
-
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
|
|
53
|
-
) -> ToolMessage | Command:
|
|
54
|
-
if isinstance(request.tool, _PersistentShellTool):
|
|
55
|
-
resources = self._get_or_create_resources(request.state)
|
|
56
|
-
return self._run_shell_tool(
|
|
57
|
-
resources,
|
|
58
|
-
request.tool_call["args"],
|
|
59
|
-
tool_call_id=request.tool_call.get("id"),
|
|
60
|
-
)
|
|
61
|
-
return await super().awrap_tool_call(request, handler)
|
|
62
|
-
|
|
63
|
-
def after_agent(self, state: ShellToolState, runtime) -> None: # type: ignore[override]
|
|
64
|
-
if self._has_resources(state):
|
|
65
|
-
super().after_agent(state, runtime)
|
|
66
|
-
|
|
67
|
-
async def aafter_agent(self, state: ShellToolState, runtime) -> None: # type: ignore[override]
|
|
68
|
-
if self._has_resources(state):
|
|
69
|
-
await super().aafter_agent(state, runtime)
|
|
70
|
-
|
|
71
|
-
@staticmethod
|
|
72
|
-
def _has_resources(state: AgentState) -> bool:
|
|
73
|
-
resources = state.get("shell_session_resources")
|
|
74
|
-
return isinstance(resources, _SessionResources)
|
|
75
|
-
|
|
76
|
-
def _get_or_create_resources(self, state: AgentState) -> _SessionResources:
|
|
77
|
-
resources = state.get("shell_session_resources")
|
|
78
|
-
if isinstance(resources, _SessionResources):
|
|
79
|
-
return resources
|
|
80
|
-
|
|
81
|
-
new_resources = self._create_resources()
|
|
82
|
-
cast("dict[str, Any]", state)["shell_session_resources"] = new_resources
|
|
83
|
-
return new_resources
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
__all__ = ["ResumableShellToolMiddleware"]
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
deepagents/__init__.py,sha256=9BVNn4lfF5N8l2KY8Ttxi82zO609I-fGqoSIF7DAxiU,342
|
|
2
|
-
deepagents/graph.py,sha256=pwRKF9EsmBtc2LabAcqted92kIhG5b4SaEfACT2G4Ps,6094
|
|
3
|
-
deepagents/backends/__init__.py,sha256=BOKu2cQ1OdMyO_l2rLqZQiXppYFmQbx7OIQb7WYwvZc,457
|
|
4
|
-
deepagents/backends/composite.py,sha256=0jvuo2YBlr3LT3N10uE0p1-vVSCdkzK0uLciPRcUg8o,8431
|
|
5
|
-
deepagents/backends/filesystem.py,sha256=SsVDx__j_AARIwRzaDuokbxbkquJ_Lw3Qi7dLWaqRUs,18674
|
|
6
|
-
deepagents/backends/protocol.py,sha256=wmXk24BBN1vo3ALYTIZmCi5fjxNZeH8y06v1OXc5ABU,4555
|
|
7
|
-
deepagents/backends/state.py,sha256=ST_tUExPxArJaA3U8vc1dyzxuYl2BQH-HU7P0eu_Ty8,6518
|
|
8
|
-
deepagents/backends/store.py,sha256=HWGZ0euXp9ww4HvWYg1S-tEH17cuFy3F1lg3DiGN6CY,13232
|
|
9
|
-
deepagents/backends/utils.py,sha256=Iyk2jW-gfoLvMnz-W_2FRCoJW_j3r1zoumU9iww-jd0,13973
|
|
10
|
-
deepagents/middleware/__init__.py,sha256=x7UHqGcrKlhzORNdChPvnUwa_PIJCbFUHY6zTKVfloI,418
|
|
11
|
-
deepagents/middleware/filesystem.py,sha256=7O2GUeGPQBslNBU1Gdser-tkL2DO_f8nZ9yZsGC3INs,28294
|
|
12
|
-
deepagents/middleware/patch_tool_calls.py,sha256=PdNhxPaQqwnFkhEAZEE2kEzadTNAOO3_iJRA30WqpGE,1981
|
|
13
|
-
deepagents/middleware/resumable_shell.py,sha256=KjhafjKu28Nf_8pDmSk_aWRK7pgkXZoubvWQljIEv3w,3382
|
|
14
|
-
deepagents/middleware/subagents.py,sha256=dI9w_TXKb4I6-InHua993sICTQ4fw5GapgNkHc-q50I,23474
|
|
15
|
-
deepagents-0.2.5.dist-info/licenses/LICENSE,sha256=c__BaxUCK69leo2yEKynf8lWndu8iwYwge1CbyqAe-E,1071
|
|
16
|
-
deepagents-cli/README.md,sha256=LQksI_Z2qG3IMbIZ7ZRBQlWTufPZmIevGt7uya_HObg,49
|
|
17
|
-
deepagents-cli/deepagents_cli/README.md,sha256=ajP946mBaKQRn_k3vViWfQn8v1BKiaw58gqb0xKmp4o,7413
|
|
18
|
-
deepagents-cli/deepagents_cli/__init__.py,sha256=2W9tHzQianR_Q0ku9hc_ZI3kUYsilXQ6I_kUnpg9bzg,108
|
|
19
|
-
deepagents-cli/deepagents_cli/__main__.py,sha256=J9-RNZv_zxw4SyjWRI_N7k7m8G4--vclD8vKxYIiXPQ,128
|
|
20
|
-
deepagents-cli/deepagents_cli/agent.py,sha256=8GFi9XtfedYTL0J-f72ARb_glklIIFCyzI_9buAg3h4,11066
|
|
21
|
-
deepagents-cli/deepagents_cli/agent_memory.py,sha256=Ma4P8sFpZ84ytb8GtqRcTv9Uy5E15oXqJzuBV9vuKmU,9147
|
|
22
|
-
deepagents-cli/deepagents_cli/commands.py,sha256=rHsHij1xGs13N_m46YcBP2M2_MRGqRbSlnWaZaMnoco,2606
|
|
23
|
-
deepagents-cli/deepagents_cli/config.py,sha256=gcYPDPltyhgMIiTNXQOaoKGqsAc-izNGPOlAqf_o7I0,4403
|
|
24
|
-
deepagents-cli/deepagents_cli/default_agent_prompt.md,sha256=Pi9SvgOAa74qhgDUMexm2-S_OfUO_CTic3zdbUnuB4s,4964
|
|
25
|
-
deepagents-cli/deepagents_cli/execution.py,sha256=2VqjnrT_LQn0b7eLqIIPW6oB4GeLFSR1w2X_3kjU4Lg,26498
|
|
26
|
-
deepagents-cli/deepagents_cli/file_ops.py,sha256=LQ7NTXPWLwePbiTBDA-22_VHxEGil7NpBltHZx1C7r4,12362
|
|
27
|
-
deepagents-cli/deepagents_cli/input.py,sha256=KXv9Kq13V1fK5bSRLfJxheYn8FH6_xc_l4fpcQlwqNc,10177
|
|
28
|
-
deepagents-cli/deepagents_cli/main.py,sha256=cIyLfAU-eJrAf2i7aCEElVTO7xXPXW8jsje4REACfcg,7142
|
|
29
|
-
deepagents-cli/deepagents_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
deepagents-cli/deepagents_cli/token_utils.py,sha256=tEeghLW2-vsitc-ba9cklKYnjLgRpvK7rK7PRiUu8jA,2439
|
|
31
|
-
deepagents-cli/deepagents_cli/tools.py,sha256=Av92Luq-vGgUr25DqErGi7aI6y6DdFSXLigffhNLxYk,4287
|
|
32
|
-
deepagents-cli/deepagents_cli/ui.py,sha256=d5hMHuTYerAINehGHohTI0ln4u9vojPvK1fN4OrqEEI,18667
|
|
33
|
-
deepagents-cli/tests/test_file_ops.py,sha256=xtu1UCmVf6gPadK0H33CtscCon8GAChi5oCboZfq-hg,3008
|
|
34
|
-
deepagents-cli/tests/test_placeholder.py,sha256=awIMcOR8rWGq0p_kljsIkZKinVdDlyx3MqDMQgPSi6U,101
|
|
35
|
-
deepagents-0.2.5.dist-info/METADATA,sha256=PiLwTOCG0BRgMteCvhAf4JhOOYxZW3EPq5Qb0_95fec,18828
|
|
36
|
-
deepagents-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
37
|
-
deepagents-0.2.5.dist-info/top_level.txt,sha256=7lDw_vmqmbQPyEVg2wcsIMEM9rjFC8i4pDXlksJ7vao,26
|
|
38
|
-
deepagents-0.2.5.dist-info/RECORD,,
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Harrison Chase
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
deepagents-cli/README.md
DELETED