langgraph-prebuilt 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/PKG-INFO +1 -1
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/chat_agent_executor.py +69 -69
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/pyproject.toml +1 -1
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/LICENSE +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/README.md +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/__init__.py +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/interrupt.py +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/py.typed +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/tool_node.py +0 -0
- {langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/tool_validator.py +0 -0
{langgraph_prebuilt-0.1.2 → langgraph_prebuilt-0.1.4}/langgraph/prebuilt/chat_agent_executor.py
RENAMED
|
@@ -10,6 +10,7 @@ from typing import (
|
|
|
10
10
|
TypeVar,
|
|
11
11
|
Union,
|
|
12
12
|
cast,
|
|
13
|
+
get_type_hints,
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
from langchain_core.language_models import (
|
|
@@ -57,13 +58,27 @@ class AgentState(TypedDict):
|
|
|
57
58
|
remaining_steps: RemainingSteps
|
|
58
59
|
|
|
59
60
|
|
|
61
|
+
class AgentStatePydantic(BaseModel):
|
|
62
|
+
"""The state of the agent."""
|
|
63
|
+
|
|
64
|
+
messages: Annotated[Sequence[BaseMessage], add_messages]
|
|
65
|
+
|
|
66
|
+
remaining_steps: RemainingSteps = 25
|
|
67
|
+
|
|
68
|
+
|
|
60
69
|
class AgentStateWithStructuredResponse(AgentState):
|
|
61
70
|
"""The state of the agent with a structured response."""
|
|
62
71
|
|
|
63
72
|
structured_response: StructuredResponse
|
|
64
73
|
|
|
65
74
|
|
|
66
|
-
|
|
75
|
+
class AgentStateWithStructuredResponsePydantic(AgentStatePydantic):
|
|
76
|
+
"""The state of the agent with a structured response."""
|
|
77
|
+
|
|
78
|
+
structured_response: StructuredResponse
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
StateSchema = TypeVar("StateSchema", bound=Union[AgentState, AgentStatePydantic])
|
|
67
82
|
StateSchemaType = Type[StateSchema]
|
|
68
83
|
|
|
69
84
|
PROMPT_RUNNABLE_NAME = "Prompt"
|
|
@@ -76,21 +91,29 @@ Prompt = Union[
|
|
|
76
91
|
]
|
|
77
92
|
|
|
78
93
|
|
|
94
|
+
def _get_state_value(state: StateSchema, key: str, default: Any = None) -> Any:
|
|
95
|
+
return (
|
|
96
|
+
state.get(key, default)
|
|
97
|
+
if isinstance(state, dict)
|
|
98
|
+
else getattr(state, key, default)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
79
102
|
def _get_prompt_runnable(prompt: Optional[Prompt]) -> Runnable:
|
|
80
103
|
prompt_runnable: Runnable
|
|
81
104
|
if prompt is None:
|
|
82
105
|
prompt_runnable = RunnableCallable(
|
|
83
|
-
lambda state: state
|
|
106
|
+
lambda state: _get_state_value(state, "messages"), name=PROMPT_RUNNABLE_NAME
|
|
84
107
|
)
|
|
85
108
|
elif isinstance(prompt, str):
|
|
86
109
|
_system_message: BaseMessage = SystemMessage(content=prompt)
|
|
87
110
|
prompt_runnable = RunnableCallable(
|
|
88
|
-
lambda state: [_system_message] + state
|
|
111
|
+
lambda state: [_system_message] + _get_state_value(state, "messages"),
|
|
89
112
|
name=PROMPT_RUNNABLE_NAME,
|
|
90
113
|
)
|
|
91
114
|
elif isinstance(prompt, SystemMessage):
|
|
92
115
|
prompt_runnable = RunnableCallable(
|
|
93
|
-
lambda state: [prompt] + state
|
|
116
|
+
lambda state: [prompt] + _get_state_value(state, "messages"),
|
|
94
117
|
name=PROMPT_RUNNABLE_NAME,
|
|
95
118
|
)
|
|
96
119
|
elif inspect.iscoroutinefunction(prompt):
|
|
@@ -234,7 +257,7 @@ def _validate_chat_history(
|
|
|
234
257
|
@_convert_modifier_to_prompt
|
|
235
258
|
def create_react_agent(
|
|
236
259
|
model: Union[str, LanguageModelLike],
|
|
237
|
-
tools: Union[Sequence[BaseTool], ToolNode],
|
|
260
|
+
tools: Union[Sequence[Union[BaseTool, Callable]], ToolNode],
|
|
238
261
|
*,
|
|
239
262
|
prompt: Optional[Prompt] = None,
|
|
240
263
|
response_format: Optional[
|
|
@@ -283,7 +306,7 @@ def create_react_agent(
|
|
|
283
306
|
The graph will make a separate call to the LLM to generate the structured response after the agent loop is finished.
|
|
284
307
|
This is not the only strategy to get structured responses, see more options in [this guide](https://langchain-ai.github.io/langgraph/how-tos/react-agent-structured-output/).
|
|
285
308
|
state_schema: An optional state schema that defines graph state.
|
|
286
|
-
Must have `messages` and `
|
|
309
|
+
Must have `messages` and `remaining_steps` keys.
|
|
287
310
|
Defaults to `AgentState` that defines those two keys.
|
|
288
311
|
config_schema: An optional schema for configuration.
|
|
289
312
|
Use this to expose configurable parameters via agent.config_specs.
|
|
@@ -359,12 +382,11 @@ def create_react_agent(
|
|
|
359
382
|
Use with a simple tool:
|
|
360
383
|
|
|
361
384
|
```pycon
|
|
362
|
-
>>> from datetime import datetime
|
|
363
385
|
>>> from langchain_openai import ChatOpenAI
|
|
364
386
|
>>> from langgraph.prebuilt import create_react_agent
|
|
365
387
|
|
|
366
388
|
|
|
367
|
-
... def check_weather(location: str
|
|
389
|
+
... def check_weather(location: str) -> str:
|
|
368
390
|
... '''Return the weather forecast for the specified location.'''
|
|
369
391
|
... return f"It's always sunny in {location}"
|
|
370
392
|
>>>
|
|
@@ -572,7 +594,7 @@ def create_react_agent(
|
|
|
572
594
|
|
|
573
595
|
```pycon
|
|
574
596
|
>>> import time
|
|
575
|
-
... def check_weather(location: str
|
|
597
|
+
... def check_weather(location: str) -> str:
|
|
576
598
|
... '''Return the weather forecast for the specified location.'''
|
|
577
599
|
... time.sleep(2)
|
|
578
600
|
... return f"It's always sunny in {location}"
|
|
@@ -595,7 +617,8 @@ def create_react_agent(
|
|
|
595
617
|
if response_format is not None:
|
|
596
618
|
required_keys.add("structured_response")
|
|
597
619
|
|
|
598
|
-
|
|
620
|
+
schema_keys = set(get_type_hints(state_schema))
|
|
621
|
+
if missing_keys := required_keys - set(schema_keys):
|
|
599
622
|
raise ValueError(f"Missing required key(s) {missing_keys} in state_schema")
|
|
600
623
|
|
|
601
624
|
if state_schema is None:
|
|
@@ -636,35 +659,34 @@ def create_react_agent(
|
|
|
636
659
|
# our graph needs to check if these were called
|
|
637
660
|
should_return_direct = {t.name for t in tool_classes if t.return_direct}
|
|
638
661
|
|
|
639
|
-
|
|
640
|
-
def call_model(state: AgentState, config: RunnableConfig) -> AgentState:
|
|
641
|
-
_validate_chat_history(state["messages"])
|
|
642
|
-
response = cast(AIMessage, model_runnable.invoke(state, config))
|
|
643
|
-
# add agent name to the AIMessage
|
|
644
|
-
response.name = name
|
|
662
|
+
def _are_more_steps_needed(state: StateSchema, response: BaseMessage) -> bool:
|
|
645
663
|
has_tool_calls = isinstance(response, AIMessage) and response.tool_calls
|
|
646
664
|
all_tools_return_direct = (
|
|
647
665
|
all(call["name"] in should_return_direct for call in response.tool_calls)
|
|
648
666
|
if isinstance(response, AIMessage)
|
|
649
667
|
else False
|
|
650
668
|
)
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
and has_tool_calls
|
|
656
|
-
)
|
|
669
|
+
remaining_steps = _get_state_value(state, "remaining_steps", None)
|
|
670
|
+
is_last_step = _get_state_value(state, "is_last_step", False)
|
|
671
|
+
return (
|
|
672
|
+
(remaining_steps is None and is_last_step and has_tool_calls)
|
|
657
673
|
or (
|
|
658
|
-
|
|
659
|
-
and
|
|
674
|
+
remaining_steps is not None
|
|
675
|
+
and remaining_steps < 1
|
|
660
676
|
and all_tools_return_direct
|
|
661
677
|
)
|
|
662
|
-
or (
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
)
|
|
678
|
+
or (remaining_steps is not None and remaining_steps < 2 and has_tool_calls)
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
# Define the function that calls the model
|
|
682
|
+
def call_model(state: StateSchema, config: RunnableConfig) -> StateSchema:
|
|
683
|
+
messages = _get_state_value(state, "messages")
|
|
684
|
+
_validate_chat_history(messages)
|
|
685
|
+
response = cast(AIMessage, model_runnable.invoke(state, config))
|
|
686
|
+
# add agent name to the AIMessage
|
|
687
|
+
response.name = name
|
|
688
|
+
|
|
689
|
+
if _are_more_steps_needed(state, response):
|
|
668
690
|
return {
|
|
669
691
|
"messages": [
|
|
670
692
|
AIMessage(
|
|
@@ -676,34 +698,13 @@ def create_react_agent(
|
|
|
676
698
|
# We return a list, because this will get added to the existing list
|
|
677
699
|
return {"messages": [response]}
|
|
678
700
|
|
|
679
|
-
async def acall_model(state:
|
|
680
|
-
|
|
701
|
+
async def acall_model(state: StateSchema, config: RunnableConfig) -> StateSchema:
|
|
702
|
+
messages = _get_state_value(state, "messages")
|
|
703
|
+
_validate_chat_history(messages)
|
|
681
704
|
response = cast(AIMessage, await model_runnable.ainvoke(state, config))
|
|
682
705
|
# add agent name to the AIMessage
|
|
683
706
|
response.name = name
|
|
684
|
-
|
|
685
|
-
all_tools_return_direct = (
|
|
686
|
-
all(call["name"] in should_return_direct for call in response.tool_calls)
|
|
687
|
-
if isinstance(response, AIMessage)
|
|
688
|
-
else False
|
|
689
|
-
)
|
|
690
|
-
if (
|
|
691
|
-
(
|
|
692
|
-
"remaining_steps" not in state
|
|
693
|
-
and state.get("is_last_step", False)
|
|
694
|
-
and has_tool_calls
|
|
695
|
-
)
|
|
696
|
-
or (
|
|
697
|
-
"remaining_steps" in state
|
|
698
|
-
and state["remaining_steps"] < 1
|
|
699
|
-
and all_tools_return_direct
|
|
700
|
-
)
|
|
701
|
-
or (
|
|
702
|
-
"remaining_steps" in state
|
|
703
|
-
and state["remaining_steps"] < 2
|
|
704
|
-
and has_tool_calls
|
|
705
|
-
)
|
|
706
|
-
):
|
|
707
|
+
if _are_more_steps_needed(state, response):
|
|
707
708
|
return {
|
|
708
709
|
"messages": [
|
|
709
710
|
AIMessage(
|
|
@@ -716,11 +717,9 @@ def create_react_agent(
|
|
|
716
717
|
return {"messages": [response]}
|
|
717
718
|
|
|
718
719
|
def generate_structured_response(
|
|
719
|
-
state:
|
|
720
|
-
) ->
|
|
721
|
-
|
|
722
|
-
# for the LLM to generate the structured response
|
|
723
|
-
messages = state["messages"][:-1]
|
|
720
|
+
state: StateSchema, config: RunnableConfig
|
|
721
|
+
) -> StateSchema:
|
|
722
|
+
messages = _get_state_value(state, "messages")
|
|
724
723
|
structured_response_schema = response_format
|
|
725
724
|
if isinstance(response_format, tuple):
|
|
726
725
|
system_prompt, structured_response_schema = response_format
|
|
@@ -733,11 +732,9 @@ def create_react_agent(
|
|
|
733
732
|
return {"structured_response": response}
|
|
734
733
|
|
|
735
734
|
async def agenerate_structured_response(
|
|
736
|
-
state:
|
|
737
|
-
) ->
|
|
738
|
-
|
|
739
|
-
# for the LLM to generate the structured response
|
|
740
|
-
messages = state["messages"][:-1]
|
|
735
|
+
state: StateSchema, config: RunnableConfig
|
|
736
|
+
) -> StateSchema:
|
|
737
|
+
messages = _get_state_value(state, "messages")
|
|
741
738
|
structured_response_schema = response_format
|
|
742
739
|
if isinstance(response_format, tuple):
|
|
743
740
|
system_prompt, structured_response_schema = response_format
|
|
@@ -773,8 +770,8 @@ def create_react_agent(
|
|
|
773
770
|
)
|
|
774
771
|
|
|
775
772
|
# Define the function that determines whether to continue or not
|
|
776
|
-
def should_continue(state:
|
|
777
|
-
messages = state
|
|
773
|
+
def should_continue(state: StateSchema) -> Union[str, list]:
|
|
774
|
+
messages = _get_state_value(state, "messages")
|
|
778
775
|
last_message = messages[-1]
|
|
779
776
|
# If there is no function call, then we finish
|
|
780
777
|
if not isinstance(last_message, AIMessage) or not last_message.tool_calls:
|
|
@@ -824,8 +821,8 @@ def create_react_agent(
|
|
|
824
821
|
path_map=should_continue_destinations,
|
|
825
822
|
)
|
|
826
823
|
|
|
827
|
-
def route_tool_responses(state:
|
|
828
|
-
for m in reversed(state
|
|
824
|
+
def route_tool_responses(state: StateSchema) -> Literal["agent", "__end__"]:
|
|
825
|
+
for m in reversed(_get_state_value(state, "messages")):
|
|
829
826
|
if not isinstance(m, ToolMessage):
|
|
830
827
|
break
|
|
831
828
|
if m.name in should_return_direct:
|
|
@@ -857,4 +854,7 @@ __all__ = [
|
|
|
857
854
|
"create_react_agent",
|
|
858
855
|
"create_tool_calling_executor",
|
|
859
856
|
"AgentState",
|
|
857
|
+
"AgentStatePydantic",
|
|
858
|
+
"AgentStateWithStructuredResponse",
|
|
859
|
+
"AgentStateWithStructuredResponsePydantic",
|
|
860
860
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|