mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-19 22:25:44 +02:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0139e11ae5 | ||
|
|
bfbe55ab64 | ||
|
|
36478eb745 | ||
|
|
8fb91569b9 | ||
|
|
9bf6728354 | ||
|
|
126a8f5bc6 | ||
|
|
9170f636d0 | ||
|
|
8207d3fefb | ||
|
|
ade3f372a5 | ||
|
|
3a55d1137b | ||
|
|
bece43dc67 | ||
|
|
913b8d5e95 | ||
|
|
29f6ea7f61 | ||
|
|
3c0d9346c2 | ||
|
|
0ebb78d9b2 | ||
|
|
70153ceba2 | ||
|
|
a9c87ed8b6 | ||
|
|
837fe59e24 | ||
|
|
596a26461c |
@@ -280,7 +280,21 @@ LangGraph allows access to short-term and long-term memory from tools. See [Memo
|
||||
|
||||
## Prebuilt tools
|
||||
|
||||
LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
|
||||
You can use prebuilt tools from model providers by passing a dictionary with tool specs to the `tools` parameter of `create_react_agent`. For example, to use the `web_search_preview` tool from OpenAI:
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
agent = create_react_agent(
|
||||
model="openai:gpt-4o-mini",
|
||||
tools=[{"type": "web_search_preview"}]
|
||||
)
|
||||
response = agent.invoke(
|
||||
{"messages": ["What was a positive news story from today?"]}
|
||||
)
|
||||
```
|
||||
|
||||
Additionally, LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
|
||||
|
||||
You can browse the full list of available integrations in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/tools/).
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployment option.
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) deployment option.
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ For more information, please see:
|
||||
|
||||
## Self-Hosted Data Plane
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Data Plane](./langgraph_self_hosted_data_plane.md) deployment option is a "hybrid" model for deployment where we manage the [control plane](./langgraph_control_plane.md) in our cloud and you manage the [data plane](./langgraph_data_plane.md) in your cloud. This option provides a way to securely manage your data plane infrastructure, while offloading control plane management to us.
|
||||
|
||||
@@ -56,8 +56,8 @@ For more information, please see:
|
||||
|
||||
## Self-Hosted Control Plane
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Control Plane](./langgraph_self_hosted_control_plane.md) deployment option is a fully self-hosted model for deployment where you manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in your cloud. This option give you full control and responsibility of the control plane and data plane infrastructure.
|
||||
|
||||
|
||||
@@ -47,17 +47,22 @@ This section describes various features of the control plane.
|
||||
|
||||
For simplicity, the control plane offers two deployment types with different resource allocations: `Development` and `Production`.
|
||||
|
||||
| **Deployment Type** | **CPU** | **Memory** | **Scaling** |
|
||||
|---------------------|---------|------------|---------------------|
|
||||
| Development | 1 CPU | 1 GB | Up to 1 container |
|
||||
| Production | 2 CPU | 2 GB | Up to 10 containers |
|
||||
| **Deployment Type** | **CPU/Memory** | **Scaling** | **Database** |
|
||||
|---------------------|-----------------|---------------------|----------------------------------------------------------------------------------|
|
||||
| Development | 1 CPU, 1 GB RAM | Up to 1 container | 10 GB disk, no backups |
|
||||
| Production | 2 CPU, 2 GB RAM | Up to 10 containers | Autoscaling disk, automatic backups, highly available (multi-zone configuration) |
|
||||
|
||||
CPU and memory resources are per container.
|
||||
|
||||
!!! info "For [Cloud SaaS](../concepts/langgraph_cloud.md)"
|
||||
!!! warning "Immutable Deployment Type"
|
||||
|
||||
Once a deployment is created, the deployment type cannot be changed.
|
||||
|
||||
!!! info "Resource Customization"
|
||||
For `Production` type deployments, resources can be manually increased on a case-by-case basis depending on use case and capacity constraints. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
!!! info
|
||||
For `Development` types deployments, database disk size can be manually increased on a case-by-case basis depending on use case and capacity constraints. For most use cases, [TTLs](../how-tos/ttl/configure_ttl.md) should be configured to manage disk usage. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
Resources for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments can be fully customized.
|
||||
|
||||
### Database Provisioning
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
There are two versions of the self-hosted deployment: [Self-Hosted Data Plane](./deployment_options.md#self-hosted-data-plane) and [Self-Hosted Control Plane](./deployment_options.md#self-hosted-control-plane).
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ search:
|
||||
|
||||
There are two versions of the self-hosted deployment: [Self-Hosted Data Plane](./deployment_options.md#self-hosted-data-plane) and [Self-Hosted Control Plane](./deployment_options.md#self-hosted-control-plane).
|
||||
|
||||
!!! important "Beta"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage.
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -2891,7 +2891,7 @@ test = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = { editable = "../libs/prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
@@ -2214,12 +2214,14 @@ class Pregel(PregelProtocol):
|
||||
validate_keys(output_keys, self.channels)
|
||||
interrupt_before = interrupt_before or self.interrupt_before_nodes
|
||||
interrupt_after = interrupt_after or self.interrupt_after_nodes
|
||||
stream_mode = stream_mode if stream_mode is not None else self.stream_mode
|
||||
if stream_mode is None and CONFIG_KEY_TASK_ID in config.get(CONF, {}):
|
||||
# if being called as a node in another graph, default to values mode
|
||||
# but don't overwrite stream_mode arg if provided
|
||||
stream_mode = ["values"]
|
||||
elif stream_mode is None:
|
||||
stream_mode = self.stream_mode
|
||||
if not isinstance(stream_mode, list):
|
||||
stream_mode = [stream_mode]
|
||||
if CONFIG_KEY_TASK_ID in config.get(CONF, {}):
|
||||
# if being called as a node in another graph, always use values mode
|
||||
stream_mode = ["values"]
|
||||
if self.checkpointer is False:
|
||||
checkpointer: BaseCheckpointSaver | None = None
|
||||
elif CONFIG_KEY_CHECKPOINTER in config.get(CONF, {}):
|
||||
|
||||
@@ -135,7 +135,6 @@ P = ParamSpec("P")
|
||||
INPUT_DONE = object()
|
||||
INPUT_RESUMING = object()
|
||||
INPUT_SHOULD_VALIDATE = object()
|
||||
SPECIAL_CHANNELS = (ERROR, INTERRUPT, SCHEDULED)
|
||||
WritesT = Sequence[tuple[str, Any]]
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ EXCLUDED_FRAME_FNAMES = (
|
||||
"concurrent/futures/_base.py",
|
||||
)
|
||||
|
||||
SKIP_RERAISE_SET: weakref.WeakSet[Union[concurrent.futures.Future, asyncio.Future]] = (
|
||||
weakref.WeakSet()
|
||||
)
|
||||
|
||||
|
||||
class FuturesDict(Generic[F, E], dict[F, Optional[PregelExecutableTask]]):
|
||||
event: E
|
||||
@@ -165,7 +169,6 @@ class PregelRunner:
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -207,7 +210,6 @@ class PregelRunner:
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
__reraise_on_exit__=reraise,
|
||||
@@ -302,7 +304,6 @@ class PregelRunner:
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
loop=loop,
|
||||
),
|
||||
},
|
||||
@@ -349,7 +350,6 @@ class PregelRunner:
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
loop=loop,
|
||||
),
|
||||
},
|
||||
@@ -434,7 +434,8 @@ class PregelRunner:
|
||||
raise exception
|
||||
else:
|
||||
# save error to checkpointer
|
||||
self.put_writes()(task.id, [(ERROR, exception)]) # type: ignore[misc]
|
||||
task.writes.append((ERROR, exception))
|
||||
self.put_writes()(task.id, task.writes) # type: ignore[misc]
|
||||
else:
|
||||
if self.node_finished and (
|
||||
task.config is None or TAG_HIDDEN not in task.config.get("tags", [])
|
||||
@@ -456,7 +457,7 @@ def _should_stop_others(
|
||||
if fut.cancelled():
|
||||
continue
|
||||
elif exc := fut.exception():
|
||||
if not isinstance(exc, GraphBubbleUp):
|
||||
if not isinstance(exc, GraphBubbleUp) and fut not in SKIP_RERAISE_SET:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -494,7 +495,8 @@ def _panic_or_proceed(
|
||||
interrupts: list[GraphInterrupt] = []
|
||||
while done:
|
||||
# if any task failed
|
||||
if exc := _exception(done.pop()):
|
||||
fut = done.pop()
|
||||
if exc := _exception(fut):
|
||||
# cancel all pending tasks
|
||||
while inflight:
|
||||
inflight.pop().cancel()
|
||||
@@ -503,7 +505,7 @@ def _panic_or_proceed(
|
||||
if isinstance(exc, GraphInterrupt):
|
||||
# collect interrupts
|
||||
interrupts.append(exc)
|
||||
else:
|
||||
elif fut not in SKIP_RERAISE_SET:
|
||||
raise exc
|
||||
# raise combined interrupts
|
||||
if interrupts:
|
||||
@@ -530,7 +532,6 @@ def _call(
|
||||
[PregelExecutableTask, int, Optional[Call]], Optional[PregelExecutableTask]
|
||||
],
|
||||
submit: weakref.ref[Submit],
|
||||
reraise: bool,
|
||||
) -> concurrent.futures.Future[Any]:
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
raise RuntimeError("In an sync context async tasks cannot be called")
|
||||
@@ -582,14 +583,16 @@ def _call(
|
||||
callbacks=callbacks,
|
||||
schedule_task=schedule_task,
|
||||
submit=submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
__reraise_on_exit__=reraise,
|
||||
__reraise_on_exit__=False,
|
||||
# starting a new task in the next tick ensures
|
||||
# updates from this tick are committed/streamed first
|
||||
__next_tick__=True,
|
||||
)
|
||||
# exceptions for call() tasks are raised into the parent task
|
||||
# so we should not re-raise at the end of the tick
|
||||
SKIP_RERAISE_SET.add(fut)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
fut = cast(Union[asyncio.Future, concurrent.futures.Future], fut)
|
||||
# return a chained future to ensure commit() callback is called
|
||||
@@ -613,7 +616,6 @@ def _acall(
|
||||
],
|
||||
submit: weakref.ref[Submit],
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
reraise: bool = False,
|
||||
stream: bool = False,
|
||||
) -> Union[asyncio.Future[Any], concurrent.futures.Future[Any]]:
|
||||
# return a chained future to ensure commit() callback is called
|
||||
@@ -643,7 +645,6 @@ def _acall(
|
||||
schedule_task=schedule_task,
|
||||
submit=submit,
|
||||
loop=loop,
|
||||
reraise=reraise,
|
||||
stream=stream,
|
||||
),
|
||||
loop,
|
||||
@@ -669,7 +670,6 @@ async def _acall_impl(
|
||||
],
|
||||
submit: weakref.ref[Submit],
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
reraise: bool = False,
|
||||
stream: bool = False,
|
||||
) -> None:
|
||||
try:
|
||||
@@ -726,17 +726,19 @@ async def _acall_impl(
|
||||
schedule_task=schedule_task,
|
||||
submit=submit,
|
||||
loop=loop,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
__name__=task().name, # type: ignore[union-attr]
|
||||
__name__=next_task.name,
|
||||
__cancel_on_exit__=True,
|
||||
__reraise_on_exit__=reraise,
|
||||
__reraise_on_exit__=False,
|
||||
# starting a new task in the next tick ensures
|
||||
# updates from this tick are committed/streamed first
|
||||
__next_tick__=True,
|
||||
),
|
||||
)
|
||||
# exceptions for call() tasks are raised into the parent task
|
||||
# so we should not re-raise at the end of the tick
|
||||
SKIP_RERAISE_SET.add(fut)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
if fut is not None:
|
||||
chain_future(fut, destination)
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.4.5"
|
||||
version = "0.4.7"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -6873,7 +6873,7 @@ def test_sync_streaming_with_functional_api() -> None:
|
||||
should be greater than the time delay between the two tasks.
|
||||
"""
|
||||
|
||||
time_delay = 0.01
|
||||
time_delay = 0.05
|
||||
|
||||
@task()
|
||||
def slow() -> dict:
|
||||
@@ -8769,3 +8769,36 @@ def test_get_graph_root_channel(snapshot: SnapshotAssertion) -> None:
|
||||
|
||||
assert json.dumps(graph.get_graph().to_json(), indent=2) == snapshot
|
||||
assert graph.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
|
||||
def test_imp_exception(
|
||||
sync_checkpointer: BaseCheckpointSaver,
|
||||
) -> None:
|
||||
@task()
|
||||
def my_task(number: int):
|
||||
time.sleep(0.1)
|
||||
return number * 2
|
||||
|
||||
@task()
|
||||
def task_with_exception(number: int):
|
||||
time.sleep(0.1)
|
||||
raise Exception("This is a test exception")
|
||||
|
||||
@entrypoint(checkpointer=sync_checkpointer)
|
||||
def my_workflow(number: int):
|
||||
my_task(number).result()
|
||||
try:
|
||||
task_with_exception(number).result()
|
||||
except Exception as e:
|
||||
print(f"Exception caught: {e}")
|
||||
my_task(number).result()
|
||||
return "done"
|
||||
|
||||
thread1 = {"configurable": {"thread_id": "1"}}
|
||||
assert my_workflow.invoke(1, thread1) == "done"
|
||||
|
||||
assert [c for c in my_workflow.stream(1, thread1)] == [
|
||||
{"my_task": 2},
|
||||
{"my_task": 2},
|
||||
{"my_workflow": "done"},
|
||||
]
|
||||
|
||||
@@ -9148,3 +9148,301 @@ async def test_draw_invalid():
|
||||
{"source": "nothing", "target": "__end__"},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@NEEDS_CONTEXTVARS
|
||||
async def test_imp_exception(
|
||||
async_checkpointer: BaseCheckpointSaver,
|
||||
) -> None:
|
||||
@task()
|
||||
async def my_task(number: int):
|
||||
await asyncio.sleep(0.1)
|
||||
return number * 2
|
||||
|
||||
@task()
|
||||
async def task_with_exception(number: int):
|
||||
await asyncio.sleep(0.1)
|
||||
raise Exception("This is a test exception")
|
||||
|
||||
@entrypoint(checkpointer=async_checkpointer)
|
||||
async def my_workflow(number: int):
|
||||
await my_task(number)
|
||||
try:
|
||||
await task_with_exception(number)
|
||||
except Exception as e:
|
||||
print(f"Exception caught: {e}")
|
||||
await my_task(number)
|
||||
return "done"
|
||||
|
||||
thread1 = {"configurable": {"thread_id": "1"}}
|
||||
assert await my_workflow.ainvoke(1, thread1) == "done"
|
||||
|
||||
assert [c async for c in my_workflow.astream(1, thread1)] == [
|
||||
{"my_task": 2},
|
||||
{"my_task": 2},
|
||||
{"my_workflow": "done"},
|
||||
]
|
||||
|
||||
assert [c async for c in my_workflow.astream_events(1, thread1)] == [
|
||||
{
|
||||
"event": "on_chain_start",
|
||||
"data": {"input": 1},
|
||||
"name": "LangGraph",
|
||||
"tags": [],
|
||||
"run_id": AnyStr(),
|
||||
"metadata": {"thread_id": "1"},
|
||||
"parent_ids": [],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_start",
|
||||
"data": {"input": 1},
|
||||
"name": "my_workflow",
|
||||
"tags": ["graph:step:4"],
|
||||
"run_id": AnyStr(),
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_workflow",
|
||||
"langgraph_triggers": ("__start__",),
|
||||
"langgraph_path": ("__pregel_pull", "my_workflow"),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [AnyStr()],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_start",
|
||||
"data": {"input": {"number": 1}},
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"run_id": AnyStr(),
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"data": {"chunk": 2},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_end",
|
||||
"data": {"output": 2, "input": {"number": 1}},
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "LangGraph",
|
||||
"tags": [],
|
||||
"metadata": {"thread_id": "1"},
|
||||
"data": {"chunk": {"my_task": 2}},
|
||||
"parent_ids": [],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_start",
|
||||
"data": {"input": {"number": 1}},
|
||||
"name": "task_with_exception",
|
||||
"tags": ["seq:step:1"],
|
||||
"run_id": AnyStr(),
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_start",
|
||||
"data": {"input": {"number": 1}},
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"run_id": AnyStr(),
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"data": {"chunk": 2},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_end",
|
||||
"data": {"output": 2, "input": {"number": 1}},
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_task",
|
||||
"tags": ["seq:step:1"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_task",
|
||||
"langgraph_triggers": ("__pregel_push",),
|
||||
"langgraph_path": (
|
||||
"__pregel_push",
|
||||
("__pregel_pull", "my_workflow"),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [
|
||||
AnyStr(),
|
||||
AnyStr(),
|
||||
],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_workflow",
|
||||
"tags": ["graph:step:4"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_workflow",
|
||||
"langgraph_triggers": ("__start__",),
|
||||
"langgraph_path": ("__pregel_pull", "my_workflow"),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"data": {"chunk": "done"},
|
||||
"parent_ids": [AnyStr()],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "LangGraph",
|
||||
"tags": [],
|
||||
"metadata": {"thread_id": "1"},
|
||||
"data": {"chunk": {"my_task": 2}},
|
||||
"parent_ids": [],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_end",
|
||||
"data": {"output": "done", "input": 1},
|
||||
"run_id": AnyStr(),
|
||||
"name": "my_workflow",
|
||||
"tags": ["graph:step:4"],
|
||||
"metadata": {
|
||||
"thread_id": "1",
|
||||
"langgraph_step": 4,
|
||||
"langgraph_node": "my_workflow",
|
||||
"langgraph_triggers": ("__start__",),
|
||||
"langgraph_path": ("__pregel_pull", "my_workflow"),
|
||||
"langgraph_checkpoint_ns": AnyStr(),
|
||||
},
|
||||
"parent_ids": [AnyStr()],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_stream",
|
||||
"run_id": AnyStr(),
|
||||
"name": "LangGraph",
|
||||
"tags": [],
|
||||
"metadata": {"thread_id": "1"},
|
||||
"data": {"chunk": {"my_workflow": "done"}},
|
||||
"parent_ids": [],
|
||||
},
|
||||
{
|
||||
"event": "on_chain_end",
|
||||
"data": {"output": "done"},
|
||||
"run_id": AnyStr(),
|
||||
"name": "LangGraph",
|
||||
"tags": [],
|
||||
"metadata": {"thread_id": "1"},
|
||||
"parent_ids": [],
|
||||
},
|
||||
]
|
||||
|
||||
Generated
+4
-3
@@ -1,5 +1,4 @@
|
||||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.9"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13' and python_full_version < '4.0'",
|
||||
@@ -1197,7 +1196,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.4.5"
|
||||
version = "0.4.7"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -1416,7 +1415,7 @@ inmem = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = { editable = "../prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -2123,6 +2122,8 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403 },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/13/16d3a83b07f949a686f6cfd7cfc60e57a769ff502151ea140ad67b118e26/pycryptodome-3.22.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:98fd9da809d5675f3a65dcd9ed384b9dc67edab6a4cda150c5870a8122ec961d", size = 1700779 },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/af/16d26f7dfc5fd7696ea2c91448f937b51b55312b5bed44f777563e32a4fe/pycryptodome-3.22.0-pp27-pypy_73-win32.whl", hash = "sha256:37ddcd18284e6b36b0a71ea495a4c4dca35bb09ccc9bfd5b91bfaf2321f131c1", size = 1775230 },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/c3/e3423e72669ca09f141aae493e1feaa8b8475859898b04f57078280a61c4/pycryptodome-3.22.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4bdce34af16c1dcc7f8c66185684be15f5818afd2a82b75a4ce6b55f9783e13", size = 1618698 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/b7/35eec0b3919cafea362dcb68bb0654d9cb3cde6da6b7a9d8480ce0bf203a/pycryptodome-3.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2988ffcd5137dc2d27eb51cd18c0f0f68e5b009d5fec56fbccb638f90934f333", size = 1666957 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1f/f49bccdd8d61f1da4278eb0d6aee7f988f1a6ec4056b0c2dc51eda45ae27/pycryptodome-3.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e653519dedcd1532788547f00eeb6108cc7ce9efdf5cc9996abce0d53f95d5a9", size = 1659242 },
|
||||
|
||||
@@ -240,7 +240,7 @@ def _validate_chat_history(
|
||||
|
||||
def create_react_agent(
|
||||
model: Union[str, LanguageModelLike],
|
||||
tools: Union[Sequence[Union[BaseTool, Callable]], ToolNode],
|
||||
tools: Union[Sequence[Union[BaseTool, Callable, dict[str, Any]]], ToolNode],
|
||||
*,
|
||||
prompt: Optional[Prompt] = None,
|
||||
response_format: Optional[
|
||||
@@ -420,12 +420,13 @@ def create_react_agent(
|
||||
else AgentState
|
||||
)
|
||||
|
||||
llm_builtin_tools: list[dict] = []
|
||||
if isinstance(tools, ToolNode):
|
||||
tool_classes = list(tools.tools_by_name.values())
|
||||
tool_node = tools
|
||||
else:
|
||||
tool_node = ToolNode(tools)
|
||||
# get the tool functions wrapped in a tool class from the ToolNode
|
||||
llm_builtin_tools = [t for t in tools if isinstance(t, dict)]
|
||||
tool_node = ToolNode([t for t in tools if not isinstance(t, dict)])
|
||||
tool_classes = list(tool_node.tools_by_name.values())
|
||||
|
||||
if isinstance(model, str):
|
||||
@@ -442,8 +443,12 @@ def create_react_agent(
|
||||
|
||||
tool_calling_enabled = len(tool_classes) > 0
|
||||
|
||||
if _should_bind_tools(model, tool_classes) and tool_calling_enabled:
|
||||
model = cast(BaseChatModel, model).bind_tools(tool_classes)
|
||||
if (
|
||||
_should_bind_tools(model, tool_classes)
|
||||
and len(tool_classes) > 0
|
||||
or (len(llm_builtin_tools) > 0)
|
||||
):
|
||||
model = cast(BaseChatModel, model).bind_tools(tool_classes + llm_builtin_tools) # type: ignore[operator]
|
||||
|
||||
model_runnable = _get_prompt_runnable(prompt) | model
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
Generated
+2
-3
@@ -1,5 +1,4 @@
|
||||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.9"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
@@ -320,7 +319,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.4.5"
|
||||
version = "0.4.7"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -461,7 +460,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Generated
+2
-3
@@ -1,5 +1,4 @@
|
||||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.9"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
@@ -447,7 +446,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.4.5"
|
||||
version = "0.4.7"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -558,7 +557,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = { editable = "../prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Reference in New Issue
Block a user