deepagents 0.0.6rc1__py3-none-any.whl → 0.0.6rc3__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/interrupt.py DELETED
@@ -1,122 +0,0 @@
1
- """Interrupt configuration functionality for deep agents using LangGraph prebuilts."""
2
-
3
- from typing import Dict, Any, List, Optional, Union
4
- from langgraph.types import interrupt
5
- from langgraph.prebuilt.interrupt import (
6
- HumanInterruptConfig,
7
- ActionRequest,
8
- HumanInterrupt,
9
- HumanResponse,
10
- )
11
-
12
- ToolInterruptConfig = Dict[str, Union[HumanInterruptConfig, bool]]
13
-
14
-
15
- def create_interrupt_hook(
16
- tool_configs: ToolInterruptConfig,
17
- message_prefix: str = "Tool execution requires approval",
18
- ) -> callable:
19
- """Create a post model hook that handles interrupts using native LangGraph schemas.
20
-
21
- Args:
22
- tool_configs: Dict mapping tool names to HumanInterruptConfig objects or boolean values.
23
- If True, uses default HumanInterruptConfig. If False, no interrupt.
24
- message_prefix: Optional message prefix for interrupt descriptions
25
- """
26
- # Right now we don't properly handle `ignore`
27
- for tool, interrupt_config in tool_configs.items():
28
- if isinstance(interrupt_config, dict):
29
- if "allow_ignore" in interrupt_config and interrupt_config["allow_ignore"]:
30
- raise ValueError(
31
- f"For {tool} we get `allow_ignore = True` - we currently don't support `ignore`."
32
- )
33
-
34
- def interrupt_hook(state: Dict[str, Any]) -> Dict[str, Any]:
35
- """Post model hook that checks for tool calls and triggers interrupts if needed."""
36
- messages = state.get("messages", [])
37
- if not messages:
38
- return
39
-
40
- last_message = messages[-1]
41
-
42
- if not hasattr(last_message, "tool_calls") or not last_message.tool_calls:
43
- return
44
-
45
- # Separate tool calls that need interrupts from those that don't
46
- interrupt_tool_calls = []
47
- auto_approved_tool_calls = []
48
-
49
- for tool_call in last_message.tool_calls:
50
- tool_name = tool_call["name"]
51
- if tool_name in tool_configs and tool_configs[tool_name]:
52
- interrupt_tool_calls.append(tool_call)
53
- else:
54
- auto_approved_tool_calls.append(tool_call)
55
-
56
- # If no interrupts needed, return early
57
- if not interrupt_tool_calls:
58
- return
59
-
60
- # Right now, no easy handling for when multiple tools need interrupt
61
- if len(interrupt_tool_calls) > 1:
62
- raise ValueError(
63
- "Right now, interrupt hook only works when one tool requires interrupts"
64
- )
65
- tool_call = interrupt_tool_calls[0]
66
-
67
- approved_tool_calls = auto_approved_tool_calls.copy()
68
-
69
- tool_name = tool_call["name"]
70
- tool_args = tool_call["args"]
71
- description = f"{message_prefix}\n\nTool: {tool_name}\nArgs: {tool_args}"
72
- tool_config = tool_configs[tool_name]
73
- default_tool_config: HumanInterruptConfig = {
74
- "allow_accept": True,
75
- "allow_edit": True,
76
- "allow_respond": True,
77
- "allow_ignore": False,
78
- }
79
-
80
- request: HumanInterrupt = {
81
- "action_request": ActionRequest(
82
- action=tool_name,
83
- args=tool_args,
84
- ),
85
- "config": tool_config
86
- if isinstance(tool_config, dict)
87
- else default_tool_config,
88
- "description": description,
89
- }
90
-
91
- responses: List[HumanResponse] = interrupt([request])
92
-
93
- if len(responses) != 1:
94
- raise ValueError(f"Expected a list of one response, got {responses}")
95
- response = responses[0]
96
-
97
- if response["type"] == "accept":
98
- approved_tool_calls.append(tool_call)
99
- elif response["type"] == "edit":
100
- edited: ActionRequest = response["args"]
101
- new_tool_call = {
102
- "type": "tool_call",
103
- "name": edited["action"],
104
- "args": edited["args"],
105
- "id": tool_call["id"],
106
- }
107
- approved_tool_calls.append(new_tool_call)
108
- elif response["type"] == "response":
109
- response_message = {
110
- "type": "tool",
111
- "tool_call_id": tool_call["id"],
112
- "content": response["args"],
113
- }
114
- return {"messages": [response_message]}
115
- else:
116
- raise ValueError(f"Unknown response type: {response['type']}")
117
-
118
- last_message.tool_calls = approved_tool_calls
119
-
120
- return {"messages": [last_message]}
121
-
122
- return interrupt_hook
deepagents/sub_agent.py DELETED
@@ -1,169 +0,0 @@
1
- from deepagents.prompts import TASK_TOOL_DESCRIPTION
2
- from deepagents.state import DeepAgentState
3
- from langgraph.prebuilt import create_react_agent
4
- from langchain_core.tools import BaseTool
5
- from typing_extensions import TypedDict
6
- from langchain_core.tools import tool, InjectedToolCallId
7
- from langchain_core.messages import ToolMessage
8
- from langchain_core.language_models import LanguageModelLike
9
- from langchain.chat_models import init_chat_model
10
- from typing import Annotated, NotRequired, Any, Union, Optional, Callable
11
- from langgraph.types import Command
12
- from langchain_core.runnables import Runnable
13
-
14
- from langgraph.prebuilt import InjectedState
15
-
16
-
17
- class SubAgent(TypedDict):
18
- name: str
19
- description: str
20
- prompt: str
21
- tools: NotRequired[list[str]]
22
- # Optional per-subagent model: can be either a model instance OR dict settings
23
- model: NotRequired[Union[LanguageModelLike, dict[str, Any]]]
24
-
25
-
26
- class CustomSubAgent(TypedDict):
27
- name: str
28
- description: str
29
- graph: Runnable
30
-
31
-
32
- def _get_agents(
33
- tools,
34
- instructions,
35
- subagents: list[SubAgent | CustomSubAgent],
36
- model,
37
- state_schema,
38
- post_model_hook: Optional[Callable] = None,
39
- ):
40
- agents = {
41
- "general-purpose": create_react_agent(
42
- model,
43
- prompt=instructions,
44
- tools=tools,
45
- checkpointer=False,
46
- post_model_hook=post_model_hook,
47
- state_schema=state_schema,
48
- )
49
- }
50
- tools_by_name = {}
51
- for tool_ in tools:
52
- if not isinstance(tool_, BaseTool):
53
- tool_ = tool(tool_)
54
- tools_by_name[tool_.name] = tool_
55
- for _agent in subagents:
56
- if "graph" in _agent:
57
- agents[_agent["name"]] = _agent["graph"]
58
- continue
59
- if "tools" in _agent:
60
- _tools = [tools_by_name[t] for t in _agent["tools"]]
61
- else:
62
- _tools = tools
63
- # Resolve per-subagent model: can be instance or dict
64
- if "model" in _agent:
65
- agent_model = _agent["model"]
66
- if isinstance(agent_model, dict):
67
- # Dictionary settings - create model from config
68
- sub_model = init_chat_model(**agent_model)
69
- else:
70
- # Model instance - use directly
71
- sub_model = agent_model
72
- else:
73
- # Fallback to main model
74
- sub_model = model
75
- agents[_agent["name"]] = create_react_agent(
76
- sub_model,
77
- prompt=_agent["prompt"],
78
- tools=_tools,
79
- state_schema=state_schema,
80
- checkpointer=False,
81
- post_model_hook=post_model_hook,
82
- )
83
- return agents
84
-
85
-
86
- def _get_subagent_description(subagents: list[SubAgent | CustomSubAgent]):
87
- return [f"- {_agent['name']}: {_agent['description']}" for _agent in subagents]
88
-
89
-
90
- def _create_task_tool(
91
- tools,
92
- instructions,
93
- subagents: list[SubAgent | CustomSubAgent],
94
- model,
95
- state_schema,
96
- post_model_hook: Optional[Callable] = None,
97
- ):
98
- agents = _get_agents(
99
- tools, instructions, subagents, model, state_schema, post_model_hook
100
- )
101
- other_agents_string = _get_subagent_description(subagents)
102
-
103
- @tool(
104
- description=TASK_TOOL_DESCRIPTION.format(other_agents=other_agents_string)
105
- )
106
- async def task(
107
- description: str,
108
- subagent_type: str,
109
- state: Annotated[DeepAgentState, InjectedState],
110
- tool_call_id: Annotated[str, InjectedToolCallId],
111
- ):
112
- if subagent_type not in agents:
113
- return f"Error: invoked agent of type {subagent_type}, the only allowed types are {[f'`{k}`' for k in agents]}"
114
- sub_agent = agents[subagent_type]
115
- state["messages"] = [{"role": "user", "content": description}]
116
- result = await sub_agent.ainvoke(state)
117
- return Command(
118
- update={
119
- "files": result.get("files", {}),
120
- "messages": [
121
- ToolMessage(
122
- result["messages"][-1].content, tool_call_id=tool_call_id
123
- )
124
- ],
125
- }
126
- )
127
-
128
- return task
129
-
130
-
131
- def _create_sync_task_tool(
132
- tools,
133
- instructions,
134
- subagents: list[SubAgent | CustomSubAgent],
135
- model,
136
- state_schema,
137
- post_model_hook: Optional[Callable] = None,
138
- ):
139
- agents = _get_agents(
140
- tools, instructions, subagents, model, state_schema, post_model_hook
141
- )
142
- other_agents_string = _get_subagent_description(subagents)
143
-
144
- @tool(
145
- description=TASK_TOOL_DESCRIPTION.format(other_agents=other_agents_string)
146
- )
147
- def task(
148
- description: str,
149
- subagent_type: str,
150
- state: Annotated[DeepAgentState, InjectedState],
151
- tool_call_id: Annotated[str, InjectedToolCallId],
152
- ):
153
- if subagent_type not in agents:
154
- return f"Error: invoked agent of type {subagent_type}, the only allowed types are {[f'`{k}`' for k in agents]}"
155
- sub_agent = agents[subagent_type]
156
- state["messages"] = [{"role": "user", "content": description}]
157
- result = sub_agent.invoke(state)
158
- return Command(
159
- update={
160
- "files": result.get("files", {}),
161
- "messages": [
162
- ToolMessage(
163
- result["messages"][-1].content, tool_call_id=tool_call_id
164
- )
165
- ],
166
- }
167
- )
168
-
169
- return task
@@ -1,14 +0,0 @@
1
- deepagents/__init__.py,sha256=_u6fBfAwHlIeMsu5n95nMHz0WPhEglVlZ3ulm31WlwY,361
2
- deepagents/builder.py,sha256=CTjEZpkubpGr2pippATsQaENxHGZgzddrUrsattU-QU,2794
3
- deepagents/graph.py,sha256=tEdZ4WQHN0tsnXr60Lv9LIp55s6LYq0R-v2czAbmHlg,8035
4
- deepagents/interrupt.py,sha256=08W7Zjk6S8HBoNepBvzSAXvpywM5sCDl0oGNim8YCJA,4449
5
- deepagents/model.py,sha256=VyRIkdeXJH8HqLrudTKucHpBTtrwMFTQGRlXBj0kUyo,155
6
- deepagents/prompts.py,sha256=PzHgIwy4gzHyiKHR3fCS7RtJR15NyGshCtPKuOS7XTQ,24743
7
- deepagents/state.py,sha256=sCihH_OgYKibf6zGc2MdORFJqBqKNO60bWVjAenQ_xc,567
8
- deepagents/sub_agent.py,sha256=4Q84OxoAznXKVVLbd6Y2RYDOnTF7THdYwtOLvCxaED4,5404
9
- deepagents/tools.py,sha256=LFu6T1qP0DDtUBBKZUiZJt4O1rfpZ69BbdV1jx7IrK8,4765
10
- deepagents-0.0.6rc1.dist-info/licenses/LICENSE,sha256=c__BaxUCK69leo2yEKynf8lWndu8iwYwge1CbyqAe-E,1071
11
- deepagents-0.0.6rc1.dist-info/METADATA,sha256=9t_eBM4TTKxVCGXdz1M9v86rSSGeSOQ15-Yc7PJZxz0,17180
12
- deepagents-0.0.6rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- deepagents-0.0.6rc1.dist-info/top_level.txt,sha256=drAzchOzPNePwpb3_pbPuvLuayXkN7SNqeIKMBWJoAo,11
14
- deepagents-0.0.6rc1.dist-info/RECORD,,