mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-06 17:57:49 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03776f058e |
@@ -22,12 +22,6 @@ MANUAL_API_REFERENCES_LANGGRAPH = [
|
||||
"create_react_agent",
|
||||
"prebuilt",
|
||||
),
|
||||
(
|
||||
[],
|
||||
"langgraph.prebuilt.chat_agent_executor",
|
||||
"AgentState",
|
||||
"prebuilt",
|
||||
),
|
||||
(["langgraph.prebuilt"], "langgraph.prebuilt.tool_node", "ToolNode", "prebuilt"),
|
||||
(
|
||||
["langgraph.prebuilt"],
|
||||
@@ -69,18 +63,6 @@ MANUAL_API_REFERENCES_LANGGRAPH = [
|
||||
([], "langgraph.checkpoint.sqlite", "SqliteSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.postgres.aio", "AsyncPostgresSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.postgres", "PostgresSaver", "checkpoints"),
|
||||
# other prebuilts
|
||||
(["langgraph_supervisor"], "langgraph_supervisor.supervisor", "create_supervisor", "supervisor"),
|
||||
(["langgraph_supervisor"], "langgraph_supervisor.handoff", "create_handoff_tool", "supervisor"),
|
||||
([], "langgraph_supervisor.handoff", "create_forward_message_tool", "supervisor"),
|
||||
(["langgraph_swarm"], "langgraph_swarm.swarm", "create_swarm", "swarm"),
|
||||
(["langgraph_swarm"], "langgraph_swarm.swarm", "add_active_agent_router", "swarm"),
|
||||
(["langgraph_swarm"], "langgraph_swarm.swarm", "SwarmState", "swarm"),
|
||||
(["langgraph_swarm"], "langgraph_swarm.handoff", "create_handoff_tool", "swarm"),
|
||||
([], "langchain_mcp_adapters.client", "MultiServerMCPClient", "mcp"),
|
||||
([], "langchain_mcp_adapters.tools", "load_mcp_tools", "mcp"),
|
||||
([], "langchain_mcp_adapters.prompts", "load_mcp_prompt", "mcp"),
|
||||
([], "langchain_mcp_adapters.resources", "load_mcp_resources", "mcp"),
|
||||
]
|
||||
|
||||
WELL_KNOWN_LANGGRAPH_OBJECTS = {
|
||||
@@ -162,9 +144,7 @@ def get_imports(code: str, path: str) -> List[ImportInformation]:
|
||||
for found_import in found_imports:
|
||||
module = found_import["source"]
|
||||
|
||||
if module.startswith("langchain_mcp_adapters"):
|
||||
package_ecosystem = "langgraph"
|
||||
elif module.startswith("langchain"):
|
||||
if module.startswith("langchain"):
|
||||
# Handles things like `langchain` or `langchain_anthropic`
|
||||
package_ecosystem = "langchain"
|
||||
elif module.startswith("langgraph"):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
import nbformat
|
||||
@@ -25,7 +26,7 @@ def _uses_input(source: str) -> bool:
|
||||
|
||||
|
||||
def _rewrite_cell_magic(code: str) -> str:
|
||||
"""Process a code block that uses cell magic.
|
||||
"""Process a code block that uses cell magic.:w
|
||||
|
||||
- Lines starting with "%%capture" are ignored.
|
||||
- Lines starting with "%pip" are rewritten by removing the leading "%" character.
|
||||
@@ -51,14 +52,10 @@ def _rewrite_cell_magic(code: str) -> str:
|
||||
if stripped.startswith("%%capture"):
|
||||
continue
|
||||
# Rewrite %pip lines by dropping the '%'
|
||||
elif stripped.startswith("%") or stripped.startswith("!"):
|
||||
# Drop the leading '%' character and then drop all leading whitespace
|
||||
stripped = stripped.lstrip("%! \t")
|
||||
# Check if the line starts with "pip"
|
||||
if stripped.startswith("pip"):
|
||||
rewritten_lines.append(stripped)
|
||||
else:
|
||||
raise NotImplementedError(f"Unhandled line: {line}")
|
||||
elif stripped.startswith("%pip"):
|
||||
# Drop the leading '%' character
|
||||
rewritten_lines.append(stripped[1:])
|
||||
# Anything else is not supported
|
||||
else:
|
||||
raise NotImplementedError(f"Unhandled line: {line}")
|
||||
|
||||
@@ -250,10 +247,13 @@ class EscapePreprocessor(Preprocessor):
|
||||
)
|
||||
cell.metadata["exec"] = is_exec
|
||||
|
||||
# For markdown exec migration we'll re-write cell magic as bash commands
|
||||
if source.startswith("%%"):
|
||||
cell.source = _rewrite_cell_magic(source)
|
||||
cell.metadata["language"] = "shell"
|
||||
if self.markdown_exec_migration:
|
||||
# For markdown exec migration we'll re-write cell magic as bash commands
|
||||
if source.startswith("%%"):
|
||||
cell.source = _rewrite_cell_magic(source)
|
||||
cell.metadata["language"] = "shell"
|
||||
|
||||
cell.metadata["has_output"] = _has_output(source)
|
||||
|
||||
# Remove noqa comments
|
||||
cell.source = re.sub(r"#\s*noqa.*$", "", cell.source, flags=re.MULTILINE)
|
||||
@@ -352,7 +352,7 @@ exporter = MarkdownExporter(
|
||||
|
||||
|
||||
def convert_notebook(
|
||||
notebook_path: str,
|
||||
notebook_path: Path,
|
||||
mode: Literal["markdown", "exec"] = "markdown",
|
||||
) -> str:
|
||||
with open(notebook_path) as f:
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
{% extends 'markdown/index.md.j2' %}
|
||||
|
||||
{% block input %}{# cell.metadata.language is an addition of our docs pipeline. #}
|
||||
```{%- if 'language' in cell.metadata -%}
|
||||
{{ cell.metadata.language }}
|
||||
{%- elif 'magics_language' in cell.metadata -%}
|
||||
{{ cell.metadata.magics_language }}
|
||||
{%- elif 'name' in nb.metadata.get('language_info', {}) -%}
|
||||
{{ nb.metadata.language_info.name }}
|
||||
{%- endif %}
|
||||
{{ cell.source }}
|
||||
```
|
||||
{% endblock input %}
|
||||
|
||||
|
||||
{%- block traceback_line -%}
|
||||
```output
|
||||
{{ line.rstrip() | strip_ansi }}
|
||||
@@ -21,13 +8,13 @@
|
||||
|
||||
{%- block stream -%}
|
||||
```output
|
||||
{{ output.text.rstrip() | strip_ansi }}
|
||||
{{ output.text.rstrip() }}
|
||||
```
|
||||
{%- endblock stream -%}
|
||||
|
||||
{%- block data_text scoped -%}
|
||||
```output
|
||||
{{ output.data['text/plain'].rstrip() | strip_ansi }}
|
||||
{{ output.data['text/plain'].rstrip() }}
|
||||
```
|
||||
{%- endblock data_text -%}
|
||||
|
||||
|
||||
@@ -32,8 +32,7 @@ REDIRECT_MAP = {
|
||||
"cloud/concepts/cloud.md": "concepts/langgraph_cloud.md",
|
||||
"cloud/faq/studio.md": "concepts/langgraph_studio.md#studio-faqs",
|
||||
# misc
|
||||
"prebuilt.md": "agents/prebuilt.md",
|
||||
"reference/prebuilt.md": "reference/agents.md"
|
||||
"prebuilt.md": "agents/prebuilt.md"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Agents
|
||||
|
||||
## What is an agent?
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Context
|
||||
|
||||
Agents often require more than a list of messages to function effectively. They need **context**.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Deployment
|
||||
|
||||
To deploy your LangGraph agent, create and configure a LangGraph app. This setup supports both local development and production deployments.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Evals
|
||||
|
||||
To evaluate your agent's performance you can use `LangSmith` [evaluations](https://docs.smith.langchain.com/evaluation). You would need to first define an evaluator function to judge the results from an agent, such as final outputs or trajectory. Depending on your evaluation technique, this may or may not involve a reference output:
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- human-in-the-loop
|
||||
- hil
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Human-in-the-loop
|
||||
|
||||
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [Human-In-the-Loop (HIL)](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
|
||||
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [human-in-the-loop](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
|
||||
|
||||
LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received.
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# MCP Integration
|
||||
|
||||
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that standardizes how applications provide tools and context to language models. LangGraph agents can use tools defined on MCP servers through the `langchain-mcp-adapters` library.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Memory
|
||||
|
||||
LangGraph supports two types of memory essential for building conversational agents:
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- anthropic
|
||||
- openai
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Models
|
||||
|
||||
This page describes how to configure the chat model used by an agent.
|
||||
@@ -77,4 +66,4 @@ agent = create_react_agent(
|
||||
## Additional resources
|
||||
|
||||
- [Model integration directory](https://python.langchain.com/docs/integrations/chat/)
|
||||
- [Universal initialization with `init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/)
|
||||
- [Universal initialization with `init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/)
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Multi-agent
|
||||
|
||||
A single agent might struggle if it needs to specialize in multiple domains or manage many tools. To tackle this, you can break your agent into smaller, independent agents and composing them into a [multi-agent system](../concepts/multi_agent.md).
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: Overview
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Agent development with LangGraph
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
---
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Community Agents
|
||||
|
||||
To share your project, simply open a Pull Request adding an entry for your package in our [packages.yml](https://github.com/langchain-ai/langgraph/blob/main/docs/_scripts/third_party_page/packages.yml) file.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Running agents
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Streaming
|
||||
|
||||
Streaming is key to building responsive applications. There are a few types of data you’ll want to stream:
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Tools
|
||||
|
||||
[Tools](https://python.langchain.com/docs/concepts/tools/) are a way to encapsulate a function and its input schema in a way that can be passed to a chat model that supports tool calling. This allows the model to request the execution of this function with specific inputs.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# UI
|
||||
|
||||
You can use a prebuilt chat UI for interacting with any LangGraph agent through the [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui). Using the [deployed version](https://agentchat.vercel.app) is the quickest way to get started, and allows you to interact with both local and deployed graphs.
|
||||
|
||||
@@ -107,13 +107,13 @@ After installing and authorizing LangChain's `hosted-langserve` GitHub app, repo
|
||||
All traffic from `LangGraph Platform` deployments created after January 6th 2025 will come through a NAT gateway.
|
||||
This NAT gateway will have several static ip addresses depending on the region you are deploying in. Refer to the table below for the list of IP addresses to whitelist:
|
||||
|
||||
| US | EU |
|
||||
|----------------|-----------------|
|
||||
| 35.197.29.146 | 34.90.213.236 |
|
||||
| 34.145.102.123 | 34.13.244.114 |
|
||||
| 34.169.45.153 | 34.32.180.189 |
|
||||
| 34.82.222.17 | 34.34.69.108 |
|
||||
| 35.227.171.135 | 34.32.145.240 |
|
||||
| 34.169.88.30 | 34.90.157.44 |
|
||||
| 34.19.93.202 | 34.141.242.180 |
|
||||
| 34.19.34.50 | 34.32.141.108 |
|
||||
| US | EU |
|
||||
|----------------|----------------|
|
||||
| 35.197.29.146 | 34.13.192.67 |
|
||||
| 34.145.102.123 | 34.147.105.64 |
|
||||
| 34.169.45.153 | 34.90.22.166 |
|
||||
| 34.82.222.17 | 34.147.36.213 |
|
||||
| 35.227.171.135 | 34.32.137.113 |
|
||||
| 34.169.88.30 | 34.91.238.184 |
|
||||
| 34.19.93.202 | 35.204.101.241 |
|
||||
| 34.19.34.50 | 35.204.48.32 |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# How to use the interrupt option
|
||||
# Interrupt
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# How to use the Rollback option
|
||||
|
||||
# Rollback
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Agent architectures
|
||||
|
||||
Many LLM applications implement a particular control flow of steps before and / or after LLM calls. As an example, [RAG](https://github.com/langchain-ai/rag-from-scratch) performs retrieval of documents relevant to a user question, and passes those documents to an LLM in order to ground the model's response in the provided document context.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Application Structure
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Assistants
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Authentication & Access Control
|
||||
|
||||
LangGraph Platform provides a flexible authentication and authorization system that can integrate with most authentication schemes.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Breakpoints
|
||||
|
||||
Breakpoints pause graph execution at specific points and enable stepping through execution step by step. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step. Breakpoints can also be used to enable [**human-in-the-loop**](./human_in_the_loop.md) workflows, though we recommend using the [`interrupt` function](./human_in_the_loop.md#interrupt) for this purpose.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Bring Your Own Cloud (BYOC)
|
||||
|
||||
!!! note Prerequisites
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Deployment Options
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Double Texting
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Durable Execution
|
||||
|
||||
**Durable execution** is a technique in which a process or workflow saves its progress at key points, allowing it to pause and later resume exactly where it left off. This is particularly useful in scenarios that require [human-in-the-loop](./human_in_the_loop.md), where users can inspect, validate, or modify the process before continuing, and in long-running tasks that might encounter interruptions or errors (e.g., calls to an LLM timing out). By preserving completed work, durable execution enables a process to resume without reprocessing previous steps -- even after a significant delay (e.g., a week later).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# FAQ
|
||||
|
||||
Common questions and their answers!
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Functional API
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Why LangGraph?
|
||||
|
||||
## LLM applications
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- human-in-the-loop
|
||||
- hil
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Human-in-the-loop
|
||||
|
||||
!!! tip "This guide uses the new `interrupt` function."
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: Conceptual Guide for LangGraph
|
||||
search:
|
||||
boost: 0.5
|
||||
---
|
||||
|
||||
# Conceptual Guide
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph CLI
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Cloud SaaS (Beta)
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy to Cloud SaaS](../cloud/deployment/cloud.md).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Control Plane
|
||||
|
||||
The term "control plane" is used broadly to refer to the Control Plane UI where users create and update [LangGraph Servers](./langgraph_server.md) (deployments) and the Control Plane APIs that support the UI experience.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Data Plane
|
||||
|
||||
The term "data plane" is used broadly to refer to [LangGraph Servers](./langgraph_server.md) (deployments), the corresponding infrastructure for each server, and the "listener" application that continuously polls for updates from the [LangGraph Control Plane](./langgraph_control_plane.md).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Self-Hosted Control Plane (Beta)
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy the Self-Hosted Control Plane](../cloud/deployment/self_hosted_control_plane.md).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Self-Hosted Data Plane (Beta)
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy the Self-Hosted Data Plane](../cloud/deployment/self_hosted_data_plane.md).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Server
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Standalone Container
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy a Standalone Container](../cloud/deployment/standalone_container.md).
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Studio
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Glossary
|
||||
|
||||
## Graphs
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Memory
|
||||
|
||||
## What is Memory?
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Multi-agent Systems
|
||||
|
||||
An [agent](./agentic_concepts.md#agent-architectures) is _a system that uses an LLM to decide the control flow of an application_. As you develop these systems, they might grow more complex over time, making them harder to manage and scale. For example, you might run into the following problems:
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Persistence
|
||||
|
||||
LangGraph has a built-in persistence layer, implemented through checkpointers. When you compile graph with a checkpointer, the checkpointer saves a `checkpoint` of the graph state at every super-step. Those checkpoints are saved to a `thread`, which can be accessed after graph execution. Because `threads` allow access to graph's state after execution, several powerful capabilities including human-in-the-loop, memory, time travel, and fault-tolerance are all possible. See [this how-to guide](../how-tos/persistence.ipynb) for an end-to-end example on how to add and use checkpointers with your graph. Below, we'll discuss each of these concepts in more detail.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Platform Plans
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Platform Architecture
|
||||
|
||||

|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph's Runtime (Pregel)
|
||||
|
||||
[Pregel][langgraph.pregel.Pregel] implements LangGraph's runtime, managing the execution of LangGraph applications.
|
||||
@@ -27,7 +22,7 @@ Repeat until no **actors** are selected for execution, or a maximum number of st
|
||||
|
||||
## Actors
|
||||
|
||||
An **actor** is a `PregelNode`. It subscribes to channels, reads data from them, and writes data to them. It can be thought of as an **actor** in the Pregel algorithm. `PregelNodes` implement LangChain's Runnable interface.
|
||||
An **actor** is a [PregelNode][langgraph.pregel.read.PregelNode]. It subscribes to channels, reads data from them, and writes data to them. It can be thought of as an **actor** in the Pregel algorithm. [PregelNodes][langgraph.pregel.read.PregelNode] implement LangChain's Runnable interface.
|
||||
|
||||
## Channels
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Platform: Scalability & Resilience
|
||||
|
||||
LangGraph Platform is designed to scale horizontally with your workload. Each instance of the service is stateless, and keeps no resources in memory. The service is designed to gracefully handle new instances being added or removed, including hard shutdown cases.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph SDK
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Self-Hosted
|
||||
|
||||
!!! note Prerequisites
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Streaming
|
||||
|
||||
Building a responsive app for end-users? Real-time updates are key to keeping users engaged as your app progresses.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Template Applications
|
||||
|
||||
Templates are open source reference applications designed to help you get started quickly when building with LangGraph. They provide working examples of common agentic workflows that can be customized to your needs.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Time Travel ⏱️
|
||||
|
||||
!!! note "Prerequisites"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
title: How-to Guides
|
||||
description: How to accomplish common tasks in LangGraph
|
||||
search:
|
||||
boost: 0.5
|
||||
---
|
||||
|
||||
# How-to Guides
|
||||
@@ -153,7 +151,7 @@ See the below guide for how to integrate with other frameworks using the [Functi
|
||||
|
||||
### Prebuilt ReAct Agent
|
||||
|
||||
The LangGraph [prebuilt ReAct agent](../reference/agents.md#langgraph.prebuilt.chat_agent_executor.create_react_agent) is pre-built implementation of a [tool calling agent](../concepts/agentic_concepts.md#tool-calling-agent).
|
||||
The LangGraph [prebuilt ReAct agent](../reference/prebuilt.md#langgraph.prebuilt.chat_agent_executor.create_react_agent) is pre-built implementation of a [tool calling agent](../concepts/agentic_concepts.md#tool-calling-agent).
|
||||
|
||||
One of the big benefits of LangGraph is that you can easily create your own agent architectures. So while it's fine to start here to build an agent quickly, we would strongly recommend learning how to build your own agent so that you can take full advantage of LangGraph.
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# Agents
|
||||
|
||||
::: langgraph.prebuilt.chat_agent_executor
|
||||
options:
|
||||
members:
|
||||
- AgentState
|
||||
- create_react_agent
|
||||
|
||||
::: langgraph.prebuilt.tool_node.ToolNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
inherited_members: false
|
||||
members:
|
||||
- inject_tool_args
|
||||
|
||||
::: langgraph.prebuilt.tool_node
|
||||
options:
|
||||
members:
|
||||
- InjectedState
|
||||
- InjectedStore
|
||||
- tools_condition
|
||||
|
||||
::: langgraph.prebuilt.tool_validator.ValidationNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
inherited_members: false
|
||||
members: false
|
||||
|
||||
::: langgraph.prebuilt.interrupt
|
||||
options:
|
||||
members:
|
||||
- HumanInterruptConfig
|
||||
- ActionRequest
|
||||
- HumanInterrupt
|
||||
- HumanResponse
|
||||
@@ -25,11 +25,5 @@
|
||||
::: langgraph.checkpoint.sqlite.aio
|
||||
|
||||
::: langgraph.checkpoint.postgres
|
||||
options:
|
||||
members:
|
||||
- PostgresSaver
|
||||
|
||||
::: langgraph.checkpoint.postgres.aio
|
||||
options:
|
||||
members:
|
||||
- AsyncPostgresSaver
|
||||
::: langgraph.checkpoint.postgres.aio
|
||||
@@ -1,75 +1,16 @@
|
||||
# Graph Definitions
|
||||
|
||||
::: langgraph.graph.state.StateGraph
|
||||
::: langgraph.graph.graph
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
members:
|
||||
- add_node
|
||||
- add_edge
|
||||
- add_conditional_edges
|
||||
- add_sequence
|
||||
- compile
|
||||
- Graph
|
||||
- CompiledGraph
|
||||
|
||||
::: langgraph.graph.state.CompiledStateGraph
|
||||
::: langgraph.graph.state
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
members:
|
||||
- stream
|
||||
- astream
|
||||
- invoke
|
||||
- ainvoke
|
||||
- get_state
|
||||
- aget_state
|
||||
- get_state_history
|
||||
- aget_state_history
|
||||
- update_state
|
||||
- aupdate_state
|
||||
- bulk_update_state
|
||||
- abulk_update_state
|
||||
- get_graph
|
||||
- aget_graph
|
||||
- get_subgraphs
|
||||
- aget_subgraphs
|
||||
- with_config
|
||||
|
||||
::: langgraph.graph.graph.Graph
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
members:
|
||||
- add_node
|
||||
- add_edge
|
||||
- add_conditional_edges
|
||||
- compile
|
||||
|
||||
::: langgraph.graph.graph.CompiledGraph
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
members:
|
||||
- stream
|
||||
- astream
|
||||
- invoke
|
||||
- ainvoke
|
||||
- get_state
|
||||
- aget_state
|
||||
- get_state_history
|
||||
- aget_state_history
|
||||
- update_state
|
||||
- aupdate_state
|
||||
- bulk_update_state
|
||||
- abulk_update_state
|
||||
- get_graph
|
||||
- aget_graph
|
||||
- get_subgraphs
|
||||
- aget_subgraphs
|
||||
- with_config
|
||||
- StateGraph
|
||||
- CompiledStateGraph
|
||||
|
||||
::: langgraph.graph.message
|
||||
options:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
title: Reference
|
||||
description: API reference for LangGraph
|
||||
search:
|
||||
boost: 0.5
|
||||
---
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# LangChain Model Context Protocol (MCP) Adapters
|
||||
|
||||
::: langchain_mcp_adapters.client
|
||||
options:
|
||||
members:
|
||||
- MultiServerMCPClient
|
||||
|
||||
::: langchain_mcp_adapters.tools
|
||||
options:
|
||||
members:
|
||||
- load_mcp_tools
|
||||
|
||||
::: langchain_mcp_adapters.prompts
|
||||
options:
|
||||
members:
|
||||
- load_mcp_prompt
|
||||
|
||||
::: langchain_mcp_adapters.resources
|
||||
options:
|
||||
members:
|
||||
- load_mcp_resources
|
||||
@@ -0,0 +1,28 @@
|
||||
# Prebuilt
|
||||
|
||||
::: langgraph.prebuilt.chat_agent_executor
|
||||
options:
|
||||
members:
|
||||
- create_react_agent
|
||||
|
||||
::: langgraph.prebuilt.tool_node
|
||||
options:
|
||||
members:
|
||||
- ToolNode
|
||||
- InjectedState
|
||||
- InjectedStore
|
||||
- tools_condition
|
||||
|
||||
::: langgraph.prebuilt.tool_validator
|
||||
options:
|
||||
members:
|
||||
- ValidationNode
|
||||
|
||||
|
||||
::: langgraph.prebuilt.interrupt
|
||||
options:
|
||||
members:
|
||||
- HumanInterruptConfig
|
||||
- ActionRequest
|
||||
- HumanInterrupt
|
||||
- HumanResponse
|
||||
@@ -1,25 +1,7 @@
|
||||
# Pregel
|
||||
|
||||
::: langgraph.pregel.Pregel
|
||||
::: langgraph.pregel
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
show_root_heading: true
|
||||
show_root_full_path: false
|
||||
members:
|
||||
- stream
|
||||
- astream
|
||||
- invoke
|
||||
- ainvoke
|
||||
- get_state
|
||||
- aget_state
|
||||
- get_state_history
|
||||
- aget_state_history
|
||||
- update_state
|
||||
- aupdate_state
|
||||
- bulk_update_state
|
||||
- abulk_update_state
|
||||
- get_graph
|
||||
- aget_graph
|
||||
- get_subgraphs
|
||||
- aget_subgraphs
|
||||
- with_config
|
||||
- Pregel
|
||||
- PregelNode
|
||||
@@ -1,12 +0,0 @@
|
||||
# LangGraph Supervisor
|
||||
|
||||
::: langgraph_supervisor.supervisor
|
||||
options:
|
||||
members:
|
||||
- create_supervisor
|
||||
|
||||
::: langgraph_supervisor.handoff
|
||||
options:
|
||||
members:
|
||||
- create_handoff_tool
|
||||
- create_forward_message_tool
|
||||
@@ -1,13 +0,0 @@
|
||||
# LangGraph Swarm
|
||||
|
||||
::: langgraph_swarm.swarm
|
||||
options:
|
||||
members:
|
||||
- SwarmState
|
||||
- create_swarm
|
||||
- add_active_agent_router
|
||||
|
||||
::: langgraph_swarm.handoff
|
||||
options:
|
||||
members:
|
||||
- create_handoff_tool
|
||||
@@ -10,6 +10,7 @@
|
||||
- CachePolicy
|
||||
- Interrupt
|
||||
- PregelTask
|
||||
- PregelExecutableTask
|
||||
- StateSnapshot
|
||||
- Send
|
||||
- Command
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 0.5
|
||||
---
|
||||
# Error reference
|
||||
|
||||
This page contains guides around resolving common errors you may find while building with LangGraph.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
@@ -2,17 +2,17 @@
|
||||
|
||||
## :fontawesome-brands-safari:{ .safari } Safari connection error with local dev server
|
||||
|
||||
Safari blocks plain‑HTTP traffic on localhost. If you start Studio with a vanilla `langgraph dev`, the page may report a "Failed to load assistants" error and the browser DevTools will show network errors.
|
||||
Safari blocks plain‑HTTP traffic on localhost. If you start Studio with a vanilla
|
||||
`langgraph dev`, the page may report a "Failed to load assistants" error (or something similar) and the browser DevTools will show network errors.
|
||||
|
||||
#### Quick fix — run Studio through a secure Cloudflare tunnel
|
||||
|
||||
=== "Python"
|
||||
|
||||
```shell
|
||||
pip install -U langgraph-cli>=0.2.6
|
||||
pip install -U langgraph-cli>=0.2.6 # Python
|
||||
langgraph dev --tunnel
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```shell
|
||||
@@ -25,18 +25,17 @@ The command prints a URL like:
|
||||
```shell
|
||||
https://smith.langchain.com/studio/?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
```shell
|
||||
?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
|
||||
```
|
||||
indicates the endpoint where your agent server is exposed.
|
||||
|
||||
indicates the endpoint where your agent server is exposed. Open that URL in Safari and Studio should load immediately.
|
||||
Open that URL in Safari and Studio should load immediately.
|
||||
|
||||
#### Alternative — use a Chromium‑based browser
|
||||
|
||||
Chrome and other Chromium‑based browsers allow HTTP on localhost, so a plain `langgraph dev` should work without extra steps.
|
||||
Chrome, Edge, and Brave allow HTTP on localhost, so a plain `langgraph dev` should work without extra steps.
|
||||
|
||||
#### If it’s still not loading
|
||||
|
||||
@@ -44,43 +43,3 @@ Chrome and other Chromium‑based browsers allow HTTP on localhost, so a plain
|
||||
2. Confirm your CLI version with `langgraph --version`.
|
||||
|
||||
No other configuration, certificates, or CORS tweaks are required.
|
||||
|
||||
## :fontawesome-brands-brave:{ .brave } Brave connection error with local dev server
|
||||
|
||||
By default, Brave blocks plain‑HTTP traffic on localhost if Brave Shields are enabled. If you start Studio with a vanilla `langgraph dev`, the page may report a "Failed to load assistants" error and the browser DevTools will show network errors.
|
||||
|
||||
#### Quick fix — disable Brave Shields for LangSmith
|
||||
|
||||
Click the Brave icon next to the URL bar and turn off the Brave Shields in the popover.
|
||||
|
||||

|
||||
|
||||
#### Alternative — run Studio through a secure Cloudflare tunnel
|
||||
|
||||
=== "Python"
|
||||
|
||||
```shell
|
||||
pip install -U langgraph-cli>=0.2.6
|
||||
langgraph dev --tunnel
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```shell
|
||||
# Requires @langchain/langgraph-cli>=0.0.26
|
||||
npx @langchain/langgraph-cli dev
|
||||
```
|
||||
|
||||
The command prints a URL like:
|
||||
|
||||
```shell
|
||||
https://smith.langchain.com/studio/?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
```shell
|
||||
?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
|
||||
```
|
||||
|
||||
indicates the endpoint where your agent server is exposed. Open that URL in Brave and Studio should load immediately.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
title: Tutorials
|
||||
search:
|
||||
boost: 0.5
|
||||
---
|
||||
|
||||
# Tutorials
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
# Workflows and Agents
|
||||
|
||||
This guide reviews common patterns for agentic systems. In describing these systems, it can be useful to make a distinction between "workflows" and "agents". One way to think about this difference is nicely explained in [Anthropic's](https://python.langchain.com/docs/integrations/providers/anthropic/) `Building Effective Agents` blog post:
|
||||
|
||||
+7
-12
@@ -56,7 +56,6 @@ plugins:
|
||||
- search:
|
||||
separator: '[\s\u200b\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;'
|
||||
- autorefs
|
||||
- tags
|
||||
- mkdocstrings:
|
||||
custom_templates: templates
|
||||
handlers:
|
||||
@@ -384,24 +383,20 @@ nav:
|
||||
- Resources:
|
||||
# NOTE: prebuilt.md is auto-generated by `make build-prebuilt`
|
||||
- agents/prebuilt.md
|
||||
- Reference:
|
||||
- API reference:
|
||||
- reference/index.md
|
||||
- LangGraph:
|
||||
- Library:
|
||||
- Graphs: reference/graphs.md
|
||||
- Checkpointing: reference/checkpoints.md
|
||||
- Storage: reference/store.md
|
||||
- Types: reference/types.md
|
||||
- Config: reference/config.md
|
||||
- Functional API: reference/func.md
|
||||
- Prebuilt components: reference/prebuilt.md
|
||||
- Channels: reference/channels.md
|
||||
- Errors: reference/errors.md
|
||||
- Types: reference/types.md
|
||||
- Constants: reference/constants.md
|
||||
- Pregel: reference/pregel.md
|
||||
- Channels: reference/channels.md
|
||||
- Prebuilt:
|
||||
- Agents: reference/agents.md
|
||||
- Supervisor: reference/supervisor.md
|
||||
- Swarm: reference/swarm.md
|
||||
- MCP Adapters: reference/mcp.md
|
||||
- Config: reference/config.md
|
||||
- Functional API: reference/func.md
|
||||
- LangGraph Platform:
|
||||
- Server API: "cloud/reference/api/api_ref.md"
|
||||
- CLI: "cloud/reference/cli.md"
|
||||
|
||||
Generated
+399
-223
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,6 @@ langgraph-checkpoint = { path = "../libs/checkpoint/", develop = true }
|
||||
langgraph-checkpoint-sqlite = { path = "../libs/checkpoint-sqlite", develop = true }
|
||||
langgraph-checkpoint-postgres = { path = "../libs/checkpoint-postgres", develop = true }
|
||||
langgraph-sdk = {path = "../libs/sdk-py", develop = true}
|
||||
# TODO: switch these to published versions
|
||||
langgraph-supervisor = { git = "https://github.com/langchain-ai/langgraph-supervisor-py" }
|
||||
langgraph-swarm = { git = "https://github.com/langchain-ai/langgraph-swarm-py" }
|
||||
langchain-mcp-adapters = { git = "https://github.com/langchain-ai/langchain-mcp-adapters" }
|
||||
langchain-ollama = "^0.2.3"
|
||||
mkdocs = "*"
|
||||
mkdocs-autorefs = "*"
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import nbformat
|
||||
import pytest
|
||||
|
||||
from _scripts.notebook_convert import (
|
||||
_convert_links_in_markdown,
|
||||
_has_output,
|
||||
convert_notebook,
|
||||
)
|
||||
|
||||
|
||||
@@ -37,41 +32,3 @@ def test_has_output() -> None:
|
||||
def test_link_conversion(source: str, expected: str) -> None:
|
||||
"""Test logic to convert links in markdown cells."""
|
||||
assert _convert_links_in_markdown(source) == expected
|
||||
|
||||
|
||||
EXPECTED_OUTPUT = """\
|
||||
```shell
|
||||
pip install -U langgraph
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print('Hello')
|
||||
```\
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def test_converting_cell_magic() -> None:
|
||||
"""Test converting cell magic to code blocks."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
nb_path = os.path.join(tmpdir, "test_notebook.ipynb")
|
||||
|
||||
# Create a minimal notebook object
|
||||
nb = nbformat.v4.new_notebook()
|
||||
nb.cells = [
|
||||
nbformat.v4.new_code_cell(
|
||||
"%%capture --no-stderr\n"
|
||||
"%pip install -U langgraph"
|
||||
),
|
||||
nbformat.v4.new_code_cell("print('Hello')"),
|
||||
]
|
||||
nb.metadata["language_info"] = {"name": "python"}
|
||||
|
||||
# Write to file
|
||||
with open(nb_path, "w", encoding="utf-8") as f:
|
||||
nbformat.write(nb, f)
|
||||
|
||||
# Run the conversion
|
||||
converted = convert_notebook(nb_path)
|
||||
assert converted == EXPECTED_OUTPUT
|
||||
|
||||
@@ -27,8 +27,6 @@ Conn = _internal.Conn # For backward compatibility
|
||||
|
||||
|
||||
class PostgresSaver(BasePostgresSaver):
|
||||
"""Checkpointer that stores checkpoints in a Postgres database."""
|
||||
|
||||
lock: threading.Lock
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -27,8 +27,6 @@ Conn = _ainternal.Conn # For backward compatibility
|
||||
|
||||
|
||||
class AsyncPostgresSaver(BasePostgresSaver):
|
||||
"""Asynchronous checkpointer that stores checkpoints in a Postgres database."""
|
||||
|
||||
lock: asyncio.Lock
|
||||
|
||||
def __init__(
|
||||
|
||||
Generated
+1
-1
@@ -425,7 +425,7 @@ typing-extensions = ">=4.7"
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-checkpoint"
|
||||
version = "2.0.25"
|
||||
version = "2.0.24"
|
||||
description = "Library with base interfaces for LangGraph checkpoint savers."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
|
||||
Generated
+1
-1
@@ -357,7 +357,7 @@ typing-extensions = ">=4.7"
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-checkpoint"
|
||||
version = "2.0.25"
|
||||
version = "2.0.24"
|
||||
description = "Library with base interfaces for LangGraph checkpoint savers."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
|
||||
@@ -34,8 +34,6 @@ EMPTY_BYTES = b""
|
||||
|
||||
|
||||
class JsonPlusSerializer(SerializerProtocol):
|
||||
"""Serializer that uses ormsgpack, with a fallback to extended JSON serializer."""
|
||||
|
||||
def __init__(
|
||||
self, *, __unpack_ext_hook__: Optional[Callable[[int, bytes], Any]] = None
|
||||
) -> None:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"""Utilities for batching operations in a background task."""
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
import weakref
|
||||
|
||||
@@ -13,8 +13,6 @@ C = TypeVar("C")
|
||||
|
||||
|
||||
class BaseChannel(Generic[Value, Update, C], ABC):
|
||||
"""Base class for all channels."""
|
||||
|
||||
__slots__ = ("key", "typ")
|
||||
|
||||
def __init__(self, typ: Any, key: str = "") -> None:
|
||||
|
||||
@@ -76,15 +76,6 @@ class Graph:
|
||||
*,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
) -> Self:
|
||||
"""Add a new node to the graph.
|
||||
|
||||
Args:
|
||||
node (Union[str, RunnableLike]): The function or runnable this node will run.
|
||||
If a string is provided, it will be used as the node name, and action will be used as the function or runnable.
|
||||
action (Optional[RunnableLike]): The action associated with the node. (default: None)
|
||||
Will be used as the node function or runnable if `node` is a string (node name).
|
||||
metadata (Optional[dict[str, Any]]): The metadata associated with the node. (default: None)
|
||||
"""
|
||||
if isinstance(node, str):
|
||||
for character in (NS_SEP, NS_END):
|
||||
if character in node:
|
||||
@@ -119,12 +110,6 @@ class Graph:
|
||||
return self
|
||||
|
||||
def add_edge(self, start_key: str, end_key: str) -> Self:
|
||||
"""Add a directed edge from the start node to the end node.
|
||||
|
||||
Args:
|
||||
start_key (str): The key of the start node of the edge.
|
||||
end_key (str): The key of the end node of the edge.
|
||||
"""
|
||||
if self.compiled:
|
||||
logger.warning(
|
||||
"Adding an edge to a graph that has already been compiled. This will "
|
||||
@@ -317,25 +302,6 @@ class Graph:
|
||||
debug: bool = False,
|
||||
name: Optional[str] = None,
|
||||
) -> "CompiledGraph":
|
||||
"""Compiles the graph into a `CompiledGraph` object.
|
||||
|
||||
The compiled graph implements the `Runnable` interface and can be invoked,
|
||||
streamed, batched, and run asynchronously.
|
||||
|
||||
Args:
|
||||
checkpointer (Optional[Union[Checkpointer, Literal[False]]]): A checkpoint saver object or flag.
|
||||
If provided, this Checkpointer serves as a fully versioned "short-term memory" for the graph,
|
||||
allowing it to be paused, resumed, and replayed from any point.
|
||||
If None, it may inherit the parent graph's checkpointer when used as a subgraph.
|
||||
If False, it will not use or inherit any checkpointer.
|
||||
interrupt_before (Optional[Sequence[str]]): An optional list of node names to interrupt before.
|
||||
interrupt_after (Optional[Sequence[str]]): An optional list of node names to interrupt after.
|
||||
debug (bool): A flag indicating whether to enable debug mode.
|
||||
name (Optional[str]): The name to use for the compiled graph.
|
||||
|
||||
Returns:
|
||||
CompiledGraph: The compiled graph.
|
||||
"""
|
||||
# assign default values
|
||||
interrupt_before = interrupt_before or []
|
||||
interrupt_after = interrupt_after or []
|
||||
|
||||
@@ -123,44 +123,42 @@ class StateGraph(Graph):
|
||||
config_schema (Optional[Type[Any]]): The schema class that defines the configuration.
|
||||
Use this to expose configurable parameters in your API.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
def reducer(a: list, b: int | None) -> list:
|
||||
if b is not None:
|
||||
return a + [b]
|
||||
return a
|
||||
|
||||
class State(TypedDict):
|
||||
x: Annotated[list, reducer]
|
||||
|
||||
class ConfigSchema(TypedDict):
|
||||
r: float
|
||||
|
||||
graph = StateGraph(State, config_schema=ConfigSchema)
|
||||
|
||||
def node(state: State, config: RunnableConfig) -> dict:
|
||||
r = config["configurable"].get("r", 1.0)
|
||||
x = state["x"][-1]
|
||||
next_value = x * r * (1 - x)
|
||||
return {"x": next_value}
|
||||
|
||||
graph.add_node("A", node)
|
||||
graph.set_entry_point("A")
|
||||
graph.set_finish_point("A")
|
||||
compiled = graph.compile()
|
||||
|
||||
print(compiled.config_specs)
|
||||
# [ConfigurableFieldSpec(id='r', annotation=<class 'float'>, name=None, description=None, default=None, is_shared=False, dependencies=None)]
|
||||
|
||||
step1 = compiled.invoke({"x": 0.5}, {"configurable": {"r": 3.0}})
|
||||
# {'x': [0.5, 0.75]}
|
||||
```
|
||||
"""
|
||||
Examples:
|
||||
>>> from langchain_core.runnables import RunnableConfig
|
||||
>>> from typing_extensions import Annotated, TypedDict
|
||||
>>> from langgraph.checkpoint.memory import MemorySaver
|
||||
>>> from langgraph.graph import StateGraph
|
||||
>>>
|
||||
>>> def reducer(a: list, b: int | None) -> list:
|
||||
... if b is not None:
|
||||
... return a + [b]
|
||||
... return a
|
||||
>>>
|
||||
>>> class State(TypedDict):
|
||||
... x: Annotated[list, reducer]
|
||||
>>>
|
||||
>>> class ConfigSchema(TypedDict):
|
||||
... r: float
|
||||
>>>
|
||||
>>> graph = StateGraph(State, config_schema=ConfigSchema)
|
||||
>>>
|
||||
>>> def node(state: State, config: RunnableConfig) -> dict:
|
||||
... r = config["configurable"].get("r", 1.0)
|
||||
... x = state["x"][-1]
|
||||
... next_value = x * r * (1 - x)
|
||||
... return {"x": next_value}
|
||||
>>>
|
||||
>>> graph.add_node("A", node)
|
||||
>>> graph.set_entry_point("A")
|
||||
>>> graph.set_finish_point("A")
|
||||
>>> compiled = graph.compile()
|
||||
>>>
|
||||
>>> print(compiled.config_specs)
|
||||
[ConfigurableFieldSpec(id='r', annotation=<class 'float'>, name=None, description=None, default=None, is_shared=False, dependencies=None)]
|
||||
>>>
|
||||
>>> step1 = compiled.invoke({"x": 0.5}, {"configurable": {"r": 3.0}})
|
||||
>>> print(step1)
|
||||
{'x': [0.5, 0.75]}"""
|
||||
|
||||
nodes: dict[str, StateNodeSpec] # type: ignore[assignment]
|
||||
channels: dict[str, BaseChannel]
|
||||
@@ -253,8 +251,17 @@ class StateGraph(Graph):
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
"""Add a new node to the state graph.
|
||||
"""Adds a new node to the state graph.
|
||||
Will take the name of the function/runnable as the node name.
|
||||
|
||||
Args:
|
||||
node (RunnableLike): The function or runnable this node will run.
|
||||
|
||||
Raises:
|
||||
ValueError: If the key is already being used as a state key.
|
||||
|
||||
Returns:
|
||||
Self: The instance of the state graph, allowing for method chaining.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -269,7 +276,18 @@ class StateGraph(Graph):
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
"""Add a new node to the state graph."""
|
||||
"""Adds a new node to the state graph.
|
||||
|
||||
Args:
|
||||
node (str): The key of the node.
|
||||
action (RunnableLike): The action associated with the node.
|
||||
|
||||
Raises:
|
||||
ValueError: If the key is already being used as a state key.
|
||||
|
||||
Returns:
|
||||
Self: The instance of the state graph, allowing for method chaining.
|
||||
"""
|
||||
...
|
||||
|
||||
def add_node(
|
||||
@@ -282,13 +300,13 @@ class StateGraph(Graph):
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
"""Add a new node to the state graph.
|
||||
"""Adds a new node to the state graph.
|
||||
|
||||
Will take the name of the function/runnable as the node name.
|
||||
|
||||
Args:
|
||||
node (Union[str, RunnableLike]): The function or runnable this node will run.
|
||||
If a string is provided, it will be used as the node name, and action will be used as the function or runnable.
|
||||
action (Optional[RunnableLike]): The action associated with the node. (default: None)
|
||||
Will be used as the node function or runnable if `node` is a string (node name).
|
||||
metadata (Optional[dict[str, Any]]): The metadata associated with the node. (default: None)
|
||||
input (Optional[Type[Any]]): The input schema for the node. (default: the graph's input schema)
|
||||
retry (Optional[Union[RetryPolicy, Sequence[RetryPolicy]]]): The policy for retrying the node. (default: None)
|
||||
@@ -301,29 +319,29 @@ class StateGraph(Graph):
|
||||
Raises:
|
||||
ValueError: If the key is already being used as a state key.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from langgraph.graph import START, StateGraph
|
||||
|
||||
def my_node(state, config):
|
||||
return {"x": state["x"] + 1}
|
||||
|
||||
builder = StateGraph(dict)
|
||||
builder.add_node(my_node) # node name will be 'my_node'
|
||||
builder.add_edge(START, "my_node")
|
||||
graph = builder.compile()
|
||||
graph.invoke({"x": 1})
|
||||
# {'x': 2}
|
||||
Examples:
|
||||
```pycon
|
||||
>>> from langgraph.graph import START, StateGraph
|
||||
...
|
||||
>>> def my_node(state, config):
|
||||
... return {"x": state["x"] + 1}
|
||||
...
|
||||
>>> builder = StateGraph(dict)
|
||||
>>> builder.add_node(my_node) # node name will be 'my_node'
|
||||
>>> builder.add_edge(START, "my_node")
|
||||
>>> graph = builder.compile()
|
||||
>>> graph.invoke({"x": 1})
|
||||
{'x': 2}
|
||||
```
|
||||
Customize the name:
|
||||
|
||||
Example: Customize the name:
|
||||
```python
|
||||
builder = StateGraph(dict)
|
||||
builder.add_node("my_fair_node", my_node)
|
||||
builder.add_edge(START, "my_fair_node")
|
||||
graph = builder.compile()
|
||||
graph.invoke({"x": 1})
|
||||
# {'x': 2}
|
||||
```pycon
|
||||
>>> builder = StateGraph(dict)
|
||||
>>> builder.add_node("my_fair_node", my_node)
|
||||
>>> builder.add_edge(START, "my_fair_node")
|
||||
>>> graph = builder.compile()
|
||||
>>> graph.invoke({"x": 1})
|
||||
{'x': 2}
|
||||
```
|
||||
|
||||
Returns:
|
||||
@@ -426,7 +444,7 @@ class StateGraph(Graph):
|
||||
return self
|
||||
|
||||
def add_edge(self, start_key: Union[str, list[str]], end_key: str) -> Self:
|
||||
"""Add a directed edge from the start node (or list of start nodes) to the end node.
|
||||
"""Adds a directed edge from the start node (or list of start nodes) to the end node.
|
||||
|
||||
When a single start node is provided, the graph will wait for that node to complete
|
||||
before executing the end node. When multiple start nodes are provided,
|
||||
@@ -566,7 +584,7 @@ class StateGraph(Graph):
|
||||
debug: bool = False,
|
||||
name: Optional[str] = None,
|
||||
) -> "CompiledStateGraph":
|
||||
"""Compiles the state graph into a `CompiledStateGraph` object.
|
||||
"""Compiles the state graph into a `CompiledGraph` object.
|
||||
|
||||
The compiled graph implements the `Runnable` interface and can be invoked,
|
||||
streamed, batched, and run asynchronously.
|
||||
@@ -580,7 +598,6 @@ class StateGraph(Graph):
|
||||
interrupt_before (Optional[Sequence[str]]): An optional list of node names to interrupt before.
|
||||
interrupt_after (Optional[Sequence[str]]): An optional list of node names to interrupt after.
|
||||
debug (bool): A flag indicating whether to enable debug mode.
|
||||
name (Optional[str]): The name to use for the compiled graph.
|
||||
|
||||
Returns:
|
||||
CompiledStateGraph: The compiled state graph.
|
||||
@@ -830,9 +847,7 @@ class CompiledStateGraph(CompiledGraph):
|
||||
) -> Sequence[Union[ChannelWriteEntry, Send]]:
|
||||
writes = [
|
||||
(
|
||||
ChannelWriteEntry(
|
||||
p if p == END else CHANNEL_BRANCH_TO.format(p), None
|
||||
)
|
||||
ChannelWriteEntry(CHANNEL_BRANCH_TO.format(p), None)
|
||||
if not isinstance(p, Send)
|
||||
else p
|
||||
)
|
||||
@@ -1052,14 +1067,9 @@ def _control_static(
|
||||
ends: Union[tuple[str, ...], dict[str, str]],
|
||||
) -> Sequence[tuple[str, Any, Optional[str]]]:
|
||||
if isinstance(ends, dict):
|
||||
return [
|
||||
(k if k == END else CHANNEL_BRANCH_TO.format(k), None, label)
|
||||
for k, label in ends.items()
|
||||
]
|
||||
return [(CHANNEL_BRANCH_TO.format(k), None, label) for k, label in ends.items()]
|
||||
else:
|
||||
return [
|
||||
(e if e == END else CHANNEL_BRANCH_TO.format(e), None, None) for e in ends
|
||||
]
|
||||
return [(CHANNEL_BRANCH_TO.format(e), None, None) for e in ends]
|
||||
|
||||
|
||||
def _get_root(input: Any) -> Optional[Sequence[tuple[str, Any]]]:
|
||||
|
||||
@@ -100,9 +100,10 @@ def push_ui_message(
|
||||
"name": name,
|
||||
"props": props,
|
||||
"metadata": {
|
||||
"run_id": config.get("run_id", None),
|
||||
**(config.get("metadata") or {}),
|
||||
"tags": config.get("tags", None),
|
||||
"name": config.get("run_name", None),
|
||||
"run_id": config.get("run_id", None),
|
||||
**(metadata or {}),
|
||||
**({"message_id": message_id} if message_id else {}),
|
||||
},
|
||||
|
||||
@@ -230,15 +230,15 @@ class Pregel(PregelProtocol):
|
||||
|
||||
## Actors
|
||||
|
||||
An **actor** is a `PregelNode`.
|
||||
An **actor** is a [PregelNode][langgraph.pregel.read.PregelNode].
|
||||
It subscribes to channels, reads data from them, and writes data to them.
|
||||
It can be thought of as an **actor** in the Pregel algorithm.
|
||||
`PregelNodes` implement LangChain's
|
||||
[PregelNodes][langgraph.pregel.read.PregelNode] implement LangChain's
|
||||
Runnable interface.
|
||||
|
||||
## Channels
|
||||
|
||||
Channels are used to communicate between actors (`PregelNodes`).
|
||||
Channels are used to communicate between actors (PregelNodes).
|
||||
Each channel has a value type, an update type, and an update function – which
|
||||
takes a sequence of updates and
|
||||
modifies the stored value. Channels can be used to send data from one chain to
|
||||
@@ -560,7 +560,7 @@ class Pregel(PregelProtocol):
|
||||
def get_graph(
|
||||
self, config: RunnableConfig | None = None, *, xray: int | bool = False
|
||||
) -> Graph:
|
||||
"""Return a drawable representation of the computation graph."""
|
||||
"""Returns a drawable representation of the computation graph."""
|
||||
# gather subgraphs
|
||||
if xray:
|
||||
subgraphs = {
|
||||
@@ -588,7 +588,7 @@ class Pregel(PregelProtocol):
|
||||
async def aget_graph(
|
||||
self, config: RunnableConfig | None = None, *, xray: int | bool = False
|
||||
) -> Graph:
|
||||
"""Return a drawable representation of the computation graph."""
|
||||
"""Returns a drawable representation of the computation graph."""
|
||||
|
||||
# gather subgraphs
|
||||
if xray:
|
||||
@@ -639,7 +639,6 @@ class Pregel(PregelProtocol):
|
||||
return self.__class__(**attrs)
|
||||
|
||||
def with_config(self, config: RunnableConfig | None = None, **kwargs: Any) -> Self:
|
||||
"""Create a copy of the Pregel object with an updated config."""
|
||||
return self.copy(
|
||||
{"config": merge_configs(self.config, config, cast(RunnableConfig, kwargs))}
|
||||
)
|
||||
@@ -802,16 +801,6 @@ class Pregel(PregelProtocol):
|
||||
def get_subgraphs(
|
||||
self, *, namespace: str | None = None, recurse: bool = False
|
||||
) -> Iterator[tuple[str, PregelProtocol]]:
|
||||
"""Get the subgraphs of the graph.
|
||||
|
||||
Args:
|
||||
namespace (Optional[str]): The namespace to filter the subgraphs by.
|
||||
recurse (bool): Whether to recurse into the subgraphs.
|
||||
If False, only the immediate subgraphs will be returned.
|
||||
|
||||
Returns:
|
||||
Iterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs.
|
||||
"""
|
||||
for name, node in self.nodes.items():
|
||||
# filter by prefix
|
||||
if namespace is not None:
|
||||
@@ -841,16 +830,6 @@ class Pregel(PregelProtocol):
|
||||
async def aget_subgraphs(
|
||||
self, *, namespace: str | None = None, recurse: bool = False
|
||||
) -> AsyncIterator[tuple[str, PregelProtocol]]:
|
||||
"""Get the subgraphs of the graph.
|
||||
|
||||
Args:
|
||||
namespace (Optional[str]): The namespace to filter the subgraphs by.
|
||||
recurse (bool): Whether to recurse into the subgraphs.
|
||||
If False, only the immediate subgraphs will be returned.
|
||||
|
||||
Returns:
|
||||
AsyncIterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs.
|
||||
"""
|
||||
for name, node in self.get_subgraphs(namespace=namespace, recurse=recurse):
|
||||
yield name, node
|
||||
|
||||
@@ -1192,8 +1171,8 @@ class Pregel(PregelProtocol):
|
||||
before: RunnableConfig | None = None,
|
||||
limit: int | None = None,
|
||||
) -> Iterator[StateSnapshot]:
|
||||
"""Get the history of the state of the graph."""
|
||||
config = ensure_config(config)
|
||||
"""Get the history of the state of the graph."""
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
@@ -1243,8 +1222,8 @@ class Pregel(PregelProtocol):
|
||||
before: RunnableConfig | None = None,
|
||||
limit: int | None = None,
|
||||
) -> AsyncIterator[StateSnapshot]:
|
||||
"""Asynchronously get the history of the state of the graph."""
|
||||
config = ensure_config(config)
|
||||
"""Get the history of the state of the graph."""
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
@@ -1709,7 +1688,7 @@ class Pregel(PregelProtocol):
|
||||
config: RunnableConfig,
|
||||
supersteps: Sequence[Sequence[StateUpdate]],
|
||||
) -> RunnableConfig:
|
||||
"""Asynchronously apply updates to the graph state in bulk. Requires a checkpointer to be set.
|
||||
"""Apply updates to the graph state in bulk. Requires a checkpointer to be set.
|
||||
|
||||
Args:
|
||||
config: The config to apply the updates to.
|
||||
@@ -2134,7 +2113,7 @@ class Pregel(PregelProtocol):
|
||||
values: dict[str, Any] | Any,
|
||||
as_node: str | None = None,
|
||||
) -> RunnableConfig:
|
||||
"""Asynchronously update the state of the graph with the given values, as if they came from
|
||||
"""Update the state of the graph asynchronously with the given values, as if they came from
|
||||
node `as_node`. If `as_node` is not provided, it will be set to the last node
|
||||
that updated the state, if not ambiguous.
|
||||
"""
|
||||
@@ -2237,100 +2216,101 @@ class Pregel(PregelProtocol):
|
||||
Yields:
|
||||
The output of each step in the graph. The output shape depends on the stream_mode.
|
||||
|
||||
Example: Using stream_mode="values":
|
||||
```python
|
||||
import operator
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
Examples:
|
||||
Using different stream modes with a graph:
|
||||
```pycon
|
||||
>>> import operator
|
||||
>>> from typing_extensions import Annotated, TypedDict
|
||||
>>> from langgraph.graph import StateGraph, START
|
||||
...
|
||||
>>> class State(TypedDict):
|
||||
... alist: Annotated[list, operator.add]
|
||||
... another_list: Annotated[list, operator.add]
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", lambda _state: {"another_list": ["hi"]})
|
||||
>>> builder.add_node("b", lambda _state: {"alist": ["there"]})
|
||||
>>> builder.add_edge("a", "b")
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
```
|
||||
With stream_mode="values":
|
||||
|
||||
class State(TypedDict):
|
||||
alist: Annotated[list, operator.add]
|
||||
another_list: Annotated[list, operator.add]
|
||||
```pycon
|
||||
>>> for event in graph.stream({"alist": ['Ex for stream_mode="values"']}, stream_mode="values"):
|
||||
... print(event)
|
||||
{'alist': ['Ex for stream_mode="values"'], 'another_list': []}
|
||||
{'alist': ['Ex for stream_mode="values"'], 'another_list': ['hi']}
|
||||
{'alist': ['Ex for stream_mode="values"', 'there'], 'another_list': ['hi']}
|
||||
```
|
||||
With stream_mode="updates":
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", lambda _state: {"another_list": ["hi"]})
|
||||
builder.add_node("b", lambda _state: {"alist": ["there"]})
|
||||
builder.add_edge("a", "b")
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
```pycon
|
||||
>>> for event in graph.stream({"alist": ['Ex for stream_mode="updates"']}, stream_mode="updates"):
|
||||
... print(event)
|
||||
{'a': {'another_list': ['hi']}}
|
||||
{'b': {'alist': ['there']}}
|
||||
```
|
||||
With stream_mode="debug":
|
||||
|
||||
for event in graph.stream({"alist": ['Ex for stream_mode="values"']}, stream_mode="values"):
|
||||
print(event)
|
||||
|
||||
# {'alist': ['Ex for stream_mode="values"'], 'another_list': []}
|
||||
# {'alist': ['Ex for stream_mode="values"'], 'another_list': ['hi']}
|
||||
# {'alist': ['Ex for stream_mode="values"', 'there'], 'another_list': ['hi']}
|
||||
```pycon
|
||||
>>> for event in graph.stream({"alist": ['Ex for stream_mode="debug"']}, stream_mode="debug"):
|
||||
... print(event)
|
||||
{'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': []}, 'triggers': ['start:a']}}
|
||||
{'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'result': [('another_list', ['hi'])]}}
|
||||
{'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': ['hi']}, 'triggers': ['a']}}
|
||||
{'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'result': [('alist', ['there'])]}}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="updates":
|
||||
```python
|
||||
for event in graph.stream({"alist": ['Ex for stream_mode="updates"']}, stream_mode="updates"):
|
||||
print(event)
|
||||
With stream_mode="custom":
|
||||
|
||||
# {'a': {'another_list': ['hi']}}
|
||||
# {'b': {'alist': ['there']}}
|
||||
```pycon
|
||||
>>> from langgraph.types import StreamWriter
|
||||
...
|
||||
>>> def node_a(state: State, writer: StreamWriter):
|
||||
... writer({"custom_data": "foo"})
|
||||
... return {"alist": ["hi"]}
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", node_a)
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
...
|
||||
>>> for event in graph.stream({"alist": ['Ex for stream_mode="custom"']}, stream_mode="custom"):
|
||||
... print(event)
|
||||
{'custom_data': 'foo'}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="debug":
|
||||
```python
|
||||
for event in graph.stream({"alist": ['Ex for stream_mode="debug"']}, stream_mode="debug"):
|
||||
print(event)
|
||||
With stream_mode="messages":
|
||||
|
||||
# {'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': []}, 'triggers': ['start:a']}}
|
||||
# {'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'result': [('another_list', ['hi'])]}}
|
||||
# {'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': ['hi']}, 'triggers': ['a']}}
|
||||
# {'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'result': [('alist', ['there'])]}}
|
||||
```
|
||||
```pycon
|
||||
>>> from typing_extensions import Annotated, TypedDict
|
||||
>>> from langgraph.graph import StateGraph, START
|
||||
>>> from langchain_openai import ChatOpenAI
|
||||
...
|
||||
>>> llm = ChatOpenAI(model="gpt-4o-mini")
|
||||
...
|
||||
>>> class State(TypedDict):
|
||||
... question: str
|
||||
... answer: str
|
||||
...
|
||||
>>> def node_a(state: State):
|
||||
... response = llm.invoke(state["question"])
|
||||
... return {"answer": response.content}
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", node_a)
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
|
||||
Example: Using stream_mode="custom":
|
||||
```python
|
||||
from langgraph.types import StreamWriter
|
||||
|
||||
def node_a(state: State, writer: StreamWriter):
|
||||
writer({"custom_data": "foo"})
|
||||
return {"alist": ["hi"]}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", node_a)
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
|
||||
for event in graph.stream({"alist": ['Ex for stream_mode="custom"']}, stream_mode="custom"):
|
||||
print(event)
|
||||
|
||||
# {'custom_data': 'foo'}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="messages":
|
||||
```python
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||
|
||||
class State(TypedDict):
|
||||
question: str
|
||||
answer: str
|
||||
|
||||
def node_a(state: State):
|
||||
response = llm.invoke(state["question"])
|
||||
return {"answer": response.content}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", node_a)
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
|
||||
for event in graph.stream({"question": "What is the capital of France?"}, stream_mode="messages"):
|
||||
print(event)
|
||||
|
||||
# (AIMessageChunk(content='The', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], 'langgraph_path': ('__pregel_pull', 'a'), 'langgraph_checkpoint_ns': '...', 'checkpoint_ns': '...', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.7})
|
||||
# (AIMessageChunk(content=' capital', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], ...})
|
||||
# (AIMessageChunk(content=' of', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' France', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' is', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' Paris', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
>>> for event in graph.stream({"question": "What is the capital of France?"}, stream_mode="messages"):
|
||||
... print(event)
|
||||
(AIMessageChunk(content='The', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], 'langgraph_path': ('__pregel_pull', 'a'), 'langgraph_checkpoint_ns': '...', 'checkpoint_ns': '...', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.7})
|
||||
(AIMessageChunk(content=' capital', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], ...})
|
||||
(AIMessageChunk(content=' of', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' France', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' is', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' Paris', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -2498,7 +2478,7 @@ class Pregel(PregelProtocol):
|
||||
debug: bool | None = None,
|
||||
subgraphs: bool = False,
|
||||
) -> AsyncIterator[dict[str, Any] | Any]:
|
||||
"""Asynchronously stream graph steps for a single input.
|
||||
"""Stream graph steps for a single input.
|
||||
|
||||
Args:
|
||||
input: The input to the graph.
|
||||
@@ -2523,100 +2503,101 @@ class Pregel(PregelProtocol):
|
||||
Yields:
|
||||
The output of each step in the graph. The output shape depends on the stream_mode.
|
||||
|
||||
Example: Using stream_mode="values":
|
||||
```python
|
||||
import operator
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
Examples:
|
||||
Using different stream modes with a graph:
|
||||
```pycon
|
||||
>>> import operator
|
||||
>>> from typing_extensions import Annotated, TypedDict
|
||||
>>> from langgraph.graph import StateGraph, START
|
||||
...
|
||||
>>> class State(TypedDict):
|
||||
... alist: Annotated[list, operator.add]
|
||||
... another_list: Annotated[list, operator.add]
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", lambda _state: {"another_list": ["hi"]})
|
||||
>>> builder.add_node("b", lambda _state: {"alist": ["there"]})
|
||||
>>> builder.add_edge("a", "b")
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
```
|
||||
With stream_mode="values":
|
||||
|
||||
class State(TypedDict):
|
||||
alist: Annotated[list, operator.add]
|
||||
another_list: Annotated[list, operator.add]
|
||||
```pycon
|
||||
>>> async for event in graph.astream({"alist": ['Ex for stream_mode="values"']}, stream_mode="values"):
|
||||
... print(event)
|
||||
{'alist': ['Ex for stream_mode="values"'], 'another_list': []}
|
||||
{'alist': ['Ex for stream_mode="values"'], 'another_list': ['hi']}
|
||||
{'alist': ['Ex for stream_mode="values"', 'there'], 'another_list': ['hi']}
|
||||
```
|
||||
With stream_mode="updates":
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", lambda _state: {"another_list": ["hi"]})
|
||||
builder.add_node("b", lambda _state: {"alist": ["there"]})
|
||||
builder.add_edge("a", "b")
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
```pycon
|
||||
>>> async for event in graph.astream({"alist": ['Ex for stream_mode="updates"']}, stream_mode="updates"):
|
||||
... print(event)
|
||||
{'a': {'another_list': ['hi']}}
|
||||
{'b': {'alist': ['there']}}
|
||||
```
|
||||
With stream_mode="debug":
|
||||
|
||||
async for event in graph.astream({"alist": ['Ex for stream_mode="values"']}, stream_mode="values"):
|
||||
print(event)
|
||||
|
||||
# {'alist': ['Ex for stream_mode="values"'], 'another_list': []}
|
||||
# {'alist': ['Ex for stream_mode="values"'], 'another_list': ['hi']}
|
||||
# {'alist': ['Ex for stream_mode="values"', 'there'], 'another_list': ['hi']}
|
||||
```pycon
|
||||
>>> async for event in graph.astream({"alist": ['Ex for stream_mode="debug"']}, stream_mode="debug"):
|
||||
... print(event)
|
||||
{'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': []}, 'triggers': ['start:a']}}
|
||||
{'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'result': [('another_list', ['hi'])]}}
|
||||
{'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': ['hi']}, 'triggers': ['a']}}
|
||||
{'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'result': [('alist', ['there'])]}}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="updates":
|
||||
```python
|
||||
async for event in graph.astream({"alist": ['Ex for stream_mode="updates"']}, stream_mode="updates"):
|
||||
print(event)
|
||||
With stream_mode="custom":
|
||||
|
||||
# {'a': {'another_list': ['hi']}}
|
||||
# {'b': {'alist': ['there']}}
|
||||
```pycon
|
||||
>>> from langgraph.types import StreamWriter
|
||||
...
|
||||
>>> async def node_a(state: State, writer: StreamWriter):
|
||||
... writer({"custom_data": "foo"})
|
||||
... return {"alist": ["hi"]}
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", node_a)
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
...
|
||||
>>> async for event in graph.astream({"alist": ['Ex for stream_mode="custom"']}, stream_mode="custom"):
|
||||
... print(event)
|
||||
{'custom_data': 'foo'}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="debug":
|
||||
```python
|
||||
async for event in graph.astream({"alist": ['Ex for stream_mode="debug"']}, stream_mode="debug"):
|
||||
print(event)
|
||||
With stream_mode="messages":
|
||||
|
||||
# {'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': []}, 'triggers': ['start:a']}}
|
||||
# {'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 1, 'payload': {'id': '...', 'name': 'a', 'result': [('another_list', ['hi'])]}}
|
||||
# {'type': 'task', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'input': {'alist': ['Ex for stream_mode="debug"'], 'another_list': ['hi']}, 'triggers': ['a']}}
|
||||
# {'type': 'task_result', 'timestamp': '2024-06-23T...+00:00', 'step': 2, 'payload': {'id': '...', 'name': 'b', 'result': [('alist', ['there'])]}}
|
||||
```
|
||||
```pycon
|
||||
>>> from typing_extensions import Annotated, TypedDict
|
||||
>>> from langgraph.graph import StateGraph, START
|
||||
>>> from langchain_openai import ChatOpenAI
|
||||
...
|
||||
>>> llm = ChatOpenAI(model="gpt-4o-mini")
|
||||
...
|
||||
>>> class State(TypedDict):
|
||||
... question: str
|
||||
... answer: str
|
||||
...
|
||||
>>> async def node_a(state: State):
|
||||
... response = await llm.ainvoke(state["question"])
|
||||
... return {"answer": response.content}
|
||||
...
|
||||
>>> builder = StateGraph(State)
|
||||
>>> builder.add_node("a", node_a)
|
||||
>>> builder.add_edge(START, "a")
|
||||
>>> graph = builder.compile()
|
||||
|
||||
Example: Using stream_mode="custom":
|
||||
```python
|
||||
from langgraph.types import StreamWriter
|
||||
|
||||
async def node_a(state: State, writer: StreamWriter):
|
||||
writer({"custom_data": "foo"})
|
||||
return {"alist": ["hi"]}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", node_a)
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
|
||||
async for event in graph.astream({"alist": ['Ex for stream_mode="custom"']}, stream_mode="custom"):
|
||||
print(event)
|
||||
|
||||
# {'custom_data': 'foo'}
|
||||
```
|
||||
|
||||
Example: Using stream_mode="messages":
|
||||
```python
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||
|
||||
class State(TypedDict):
|
||||
question: str
|
||||
answer: str
|
||||
|
||||
async def node_a(state: State):
|
||||
response = await llm.ainvoke(state["question"])
|
||||
return {"answer": response.content}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", node_a)
|
||||
builder.add_edge(START, "a")
|
||||
graph = builder.compile()
|
||||
|
||||
async for event in graph.astream({"question": "What is the capital of France?"}, stream_mode="messages"):
|
||||
print(event)
|
||||
|
||||
# (AIMessageChunk(content='The', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], 'langgraph_path': ('__pregel_pull', 'a'), 'langgraph_checkpoint_ns': '...', 'checkpoint_ns': '...', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.7})
|
||||
# (AIMessageChunk(content=' capital', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], ...})
|
||||
# (AIMessageChunk(content=' of', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' France', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' is', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
# (AIMessageChunk(content=' Paris', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
>>> for event in graph.stream({"question": "What is the capital of France?"}, stream_mode="messages"):
|
||||
... print(event)
|
||||
(AIMessageChunk(content='The', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], 'langgraph_path': ('__pregel_pull', 'a'), 'langgraph_checkpoint_ns': '...', 'checkpoint_ns': '...', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.7})
|
||||
(AIMessageChunk(content=' capital', additional_kwargs={}, response_metadata={}, id='...'), {'langgraph_step': 1, 'langgraph_node': 'a', 'langgraph_triggers': ['start:a'], ...})
|
||||
(AIMessageChunk(content=' of', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' France', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' is', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
(AIMessageChunk(content=' Paris', additional_kwargs={}, response_metadata={}, id='...'), {...})
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
@@ -665,7 +665,7 @@ class PregelLoop(LoopProtocol):
|
||||
for tid, c, v in map_command(cmd=self.input):
|
||||
if not (c == RESUME and resume_is_map):
|
||||
writes[tid].append((c, v))
|
||||
if not writes and not resume_is_map:
|
||||
if not writes:
|
||||
raise EmptyInputError("Received empty Command input")
|
||||
# save writes
|
||||
for tid, ws in writes.items():
|
||||
|
||||
@@ -133,8 +133,7 @@ class CachePolicy(NamedTuple):
|
||||
|
||||
@dataclasses.dataclass(**_DC_KWARGS)
|
||||
class Interrupt:
|
||||
"""Information about an interrupt that occurred in a node.
|
||||
|
||||
"""
|
||||
!!! version-added "Added in version 0.2.24."
|
||||
"""
|
||||
|
||||
@@ -157,8 +156,6 @@ class StateUpdate(NamedTuple):
|
||||
|
||||
|
||||
class PregelTask(NamedTuple):
|
||||
"""A Pregel task."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
path: tuple[Union[str, int, tuple], ...]
|
||||
|
||||
Generated
+1
-1
@@ -1367,7 +1367,7 @@ typing-extensions = ">=4.7"
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-checkpoint"
|
||||
version = "2.0.25"
|
||||
version = "2.0.24"
|
||||
description = "Library with base interfaces for LangGraph checkpoint savers."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langgraph"
|
||||
version = "0.4.0"
|
||||
version = "0.3.34"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
|
||||
@@ -198,12 +198,6 @@
|
||||
"source": "__start__",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"data": "exit",
|
||||
"conditional": true
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "tools",
|
||||
@@ -213,6 +207,11 @@
|
||||
{
|
||||
"source": "tools",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -222,9 +221,9 @@
|
||||
'''
|
||||
graph TD;
|
||||
__start__ --> agent;
|
||||
agent -. exit .-> __end__;
|
||||
agent -. continue .-> tools;
|
||||
tools --> agent;
|
||||
agent -.-> __end__;
|
||||
|
||||
'''
|
||||
# ---
|
||||
@@ -285,12 +284,6 @@
|
||||
"source": "__start__",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"data": "end",
|
||||
"conditional": true
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "tools",
|
||||
@@ -300,6 +293,11 @@
|
||||
{
|
||||
"source": "tools",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -309,9 +307,9 @@
|
||||
'''
|
||||
graph TD;
|
||||
__start__ --> agent;
|
||||
agent -. end .-> __end__;
|
||||
agent -. continue .-> tools;
|
||||
tools --> agent;
|
||||
agent -.-> __end__;
|
||||
|
||||
'''
|
||||
# ---
|
||||
@@ -373,11 +371,6 @@
|
||||
"source": "__start__",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "tools",
|
||||
@@ -386,6 +379,11 @@
|
||||
{
|
||||
"source": "tools",
|
||||
"target": "agent"
|
||||
},
|
||||
{
|
||||
"source": "agent",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -395,9 +393,9 @@
|
||||
'''
|
||||
graph TD;
|
||||
__start__ --> agent;
|
||||
agent -.-> __end__;
|
||||
agent -.-> tools;
|
||||
tools --> agent;
|
||||
agent -.-> __end__;
|
||||
|
||||
'''
|
||||
# ---
|
||||
|
||||
@@ -156,8 +156,7 @@
|
||||
},
|
||||
{
|
||||
"source": "left",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
"target": "__end__"
|
||||
},
|
||||
{
|
||||
"source": "right",
|
||||
@@ -172,7 +171,7 @@
|
||||
graph TD;
|
||||
__start__ -. go-left .-> left;
|
||||
__start__ -. go-right .-> right;
|
||||
left -.-> __end__;
|
||||
left --> __end__;
|
||||
right --> __end__;
|
||||
|
||||
'''
|
||||
@@ -737,10 +736,8 @@
|
||||
Call_Tool -.-> Researcher;
|
||||
Chart_Generator -. call_tool .-> Call_Tool;
|
||||
Chart_Generator -. continue .-> Researcher;
|
||||
Chart_Generator -. end .-> __end__;
|
||||
Researcher -. call_tool .-> Call_Tool;
|
||||
Researcher -. continue .-> Chart_Generator;
|
||||
Researcher -. end .-> __end__;
|
||||
__start__ --> Researcher;
|
||||
Researcher -. redo .-> Researcher;
|
||||
|
||||
@@ -780,26 +777,26 @@
|
||||
gp_one(gp_one)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
__start__ --> gp_one;
|
||||
gp_one -. 1 .-> __end__;
|
||||
gp_one -. 0 .-> gp_two___start__;
|
||||
gp_two___end__ --> gp_one;
|
||||
gp_one -.-> __end__;
|
||||
subgraph gp_two
|
||||
gp_two___start__(<p>__start__</p>)
|
||||
gp_two_p_one(p_one)
|
||||
gp_two___end__(<p>__end__</p>)
|
||||
gp_two___start__ --> gp_two_p_one;
|
||||
gp_two_p_one -. 1 .-> gp_two___end__;
|
||||
gp_two_p_one -. 0 .-> gp_two_p_two___start__;
|
||||
gp_two_p_two___end__ --> gp_two_p_one;
|
||||
gp_two_p_one -.-> gp_two___end__;
|
||||
subgraph p_two
|
||||
gp_two_p_two___start__(<p>__start__</p>)
|
||||
gp_two_p_two_c_one(c_one)
|
||||
gp_two_p_two_c_two(c_two)
|
||||
gp_two_p_two___end__(<p>__end__</p>)
|
||||
gp_two_p_two___start__ --> gp_two_p_two_c_one;
|
||||
gp_two_p_two_c_one -. 1 .-> gp_two_p_two___end__;
|
||||
gp_two_p_two_c_one -. 0 .-> gp_two_p_two_c_two;
|
||||
gp_two_p_two_c_two --> gp_two_p_two_c_one;
|
||||
gp_two_p_two_c_one -.-> gp_two_p_two___end__;
|
||||
end
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
@@ -820,18 +817,18 @@
|
||||
p_one(p_one)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
__start__ --> p_one;
|
||||
p_one -. 1 .-> __end__;
|
||||
p_one -. 0 .-> p_two___start__;
|
||||
p_two___end__ --> p_one;
|
||||
p_one -.-> __end__;
|
||||
subgraph p_two
|
||||
p_two___start__(<p>__start__</p>)
|
||||
p_two_c_one(c_one)
|
||||
p_two_c_two(c_two)
|
||||
p_two___end__(<p>__end__</p>)
|
||||
p_two___start__ --> p_two_c_one;
|
||||
p_two_c_one -. 1 .-> p_two___end__;
|
||||
p_two_c_one -. 0 .-> p_two_c_two;
|
||||
p_two_c_two --> p_two_c_one;
|
||||
p_two_c_one -.-> p_two___end__;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
@@ -846,11 +843,6 @@
|
||||
'source': '__start__',
|
||||
'target': 'ask_question',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'answer_question',
|
||||
'target': '__end__',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'answer_question',
|
||||
@@ -860,6 +852,11 @@
|
||||
'source': 'ask_question',
|
||||
'target': 'answer_question',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'answer_question',
|
||||
'target': '__end__',
|
||||
}),
|
||||
]),
|
||||
'nodes': list([
|
||||
dict({
|
||||
@@ -1011,11 +1008,6 @@
|
||||
'source': 'conduct_interview:__start__',
|
||||
'target': 'conduct_interview:ask_question',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'conduct_interview:answer_question',
|
||||
'target': 'conduct_interview:__end__',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'conduct_interview:answer_question',
|
||||
@@ -1025,6 +1017,11 @@
|
||||
'source': 'conduct_interview:ask_question',
|
||||
'target': 'conduct_interview:answer_question',
|
||||
}),
|
||||
dict({
|
||||
'conditional': True,
|
||||
'source': 'conduct_interview:answer_question',
|
||||
'target': 'conduct_interview:__end__',
|
||||
}),
|
||||
]),
|
||||
'nodes': list([
|
||||
dict({
|
||||
|
||||
@@ -6350,92 +6350,6 @@ def test_double_interrupt_subgraph(
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
|
||||
def test_multi_resume(
|
||||
request: pytest.FixtureRequest, checkpointer_name: str
|
||||
) -> None:
|
||||
checkpointer = request.getfixturevalue(f"checkpointer_{checkpointer_name}")
|
||||
|
||||
class ChildState(TypedDict):
|
||||
prompt: str
|
||||
human_input: str
|
||||
human_inputs: list[str]
|
||||
|
||||
def get_human_input(state: ChildState):
|
||||
human_input = interrupt(state['prompt'])
|
||||
|
||||
return {
|
||||
'human_input': human_input,
|
||||
'human_inputs': [human_input],
|
||||
}
|
||||
|
||||
child_graph = (
|
||||
StateGraph(ChildState)
|
||||
.add_node("get_human_input", get_human_input)
|
||||
.add_edge(START, "get_human_input")
|
||||
.add_edge("get_human_input", END)
|
||||
.compile(checkpointer=checkpointer)
|
||||
)
|
||||
|
||||
class ParentState(TypedDict):
|
||||
prompts: list[str]
|
||||
human_inputs: Annotated[list[str], operator.add]
|
||||
|
||||
def assign_workers(state: ParentState) -> list[Send]:
|
||||
return [
|
||||
Send(
|
||||
"child_graph",
|
||||
{'prompt': prompt},
|
||||
)
|
||||
for prompt in state['prompts']
|
||||
]
|
||||
|
||||
def cleanup(state: ParentState):
|
||||
assert len(state['human_inputs']) == len(state["prompts"])
|
||||
|
||||
parent_graph = (
|
||||
StateGraph(ParentState)
|
||||
.add_node("child_graph", child_graph)
|
||||
.add_node("cleanup", cleanup)
|
||||
.add_conditional_edges(START, assign_workers, ["child_graph"])
|
||||
.add_edge("child_graph", "cleanup")
|
||||
.add_edge("cleanup", END)
|
||||
.compile(checkpointer=checkpointer)
|
||||
)
|
||||
|
||||
thread_config: RunnableConfig = {
|
||||
'configurable': {
|
||||
'thread_id': uuid.uuid4(),
|
||||
},
|
||||
}
|
||||
|
||||
prompts = ['a', 'b', 'c', 'd', 'e']
|
||||
|
||||
events = parent_graph.invoke(
|
||||
{'prompts': prompts},
|
||||
thread_config,
|
||||
stream_mode='values'
|
||||
)
|
||||
|
||||
assert len(events['__interrupt__']) == len(prompts)
|
||||
interrupt_values = {i.value for i in events['__interrupt__']}
|
||||
assert interrupt_values == set(prompts)
|
||||
|
||||
resume_map: dict[str, str] = {
|
||||
i.interrupt_id: f"human input for prompt {i.value}"
|
||||
for i in parent_graph.get_state(thread_config).interrupts
|
||||
}
|
||||
|
||||
result = parent_graph.invoke(Command(resume=resume_map), thread_config)
|
||||
assert result == {
|
||||
'prompts': prompts,
|
||||
'human_inputs': [
|
||||
f"human input for prompt {prompt}"
|
||||
for prompt in prompts
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_sync_streaming_with_functional_api() -> None:
|
||||
"""Test streaming with functional API.
|
||||
|
||||
|
||||
@@ -8703,88 +8703,3 @@ async def test_batch_update_as_input(checkpointer_name: str) -> None:
|
||||
]
|
||||
|
||||
assert new_history == history
|
||||
|
||||
|
||||
async def test_draw_invalid():
|
||||
from langchain_core.messages import BaseMessage
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[list[BaseMessage], add_messages]
|
||||
|
||||
workflow = StateGraph(AgentState)
|
||||
|
||||
async def call_model(state: AgentState) -> AgentState:
|
||||
return state
|
||||
|
||||
async def call_tool(state: AgentState) -> AgentState:
|
||||
return state
|
||||
|
||||
async def do_nothing(state: AgentState) -> AgentState:
|
||||
return state
|
||||
|
||||
def should_continue(state):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
if last_message.content.startswith("end"):
|
||||
return END
|
||||
else:
|
||||
return [Send("tool", last_message), Send("nothing", last_message)]
|
||||
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("tool", call_tool)
|
||||
workflow.add_node("nothing", do_nothing)
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
path_map=["tool", "nothing", END],
|
||||
)
|
||||
workflow.add_edge("tool", "agent")
|
||||
|
||||
graph = workflow.compile()
|
||||
|
||||
assert graph.get_graph().to_json() == {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "__start__",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": ["langchain", "schema", "runnable", "RunnablePassthrough"],
|
||||
"name": "__start__",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "agent",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
|
||||
"name": "agent",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "tool",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
|
||||
"name": "tool",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "nothing",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
|
||||
"name": "nothing",
|
||||
},
|
||||
},
|
||||
{"id": "__end__"},
|
||||
],
|
||||
"edges": [
|
||||
{"source": "__start__", "target": "agent"},
|
||||
{"source": "agent", "target": "__end__", "conditional": True},
|
||||
{"source": "agent", "target": "nothing", "conditional": True},
|
||||
{"source": "agent", "target": "tool", "conditional": True},
|
||||
{"source": "tool", "target": "agent"},
|
||||
{"source": "nothing", "target": "__end__"},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -280,9 +280,7 @@ def create_react_agent(
|
||||
version: Literal["v1", "v2"] = "v1",
|
||||
name: Optional[str] = None,
|
||||
) -> CompiledGraph:
|
||||
"""Creates an agent graph that calls tools in a loop until a stopping condition is met.
|
||||
|
||||
For more details on using `create_react_agent`, visit [Agents](https://langchain-ai.github.io/langgraph/agents/overview/) documentation.
|
||||
"""Creates a graph that works with a chat model that utilizes tool calling.
|
||||
|
||||
Args:
|
||||
model: The `LangChain` chat model that supports tool calling.
|
||||
@@ -376,7 +374,27 @@ def create_react_agent(
|
||||
Returns:
|
||||
A compiled LangChain runnable that can be used for chat interactions.
|
||||
|
||||
The "agent" node calls the language model with the messages list (after applying the prompt).
|
||||
The resulting graph looks like this:
|
||||
|
||||
``` mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Start
|
||||
Start --> Agent
|
||||
Agent --> Tools : continue
|
||||
Tools --> Agent
|
||||
Agent --> End : end
|
||||
End --> [*]
|
||||
|
||||
classDef startClass fill:#ffdfba;
|
||||
classDef endClass fill:#baffc9;
|
||||
classDef otherClass fill:#fad7de;
|
||||
|
||||
class Start startClass
|
||||
class End endClass
|
||||
class Agent,Tools otherClass
|
||||
```
|
||||
|
||||
The "agent" node calls the language model with the messages list (after applying the messages modifier).
|
||||
If the resulting AIMessage contains `tool_calls`, the graph will then call the ["tools"][langgraph.prebuilt.tool_node.ToolNode].
|
||||
The "tools" node executes the tools (1 tool per `tool_call`) and adds the responses to the messages list
|
||||
as `ToolMessage` objects. The agent node then calls the language model again.
|
||||
@@ -386,10 +404,10 @@ def create_react_agent(
|
||||
``` mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant A as LLM
|
||||
participant A as Agent (LLM)
|
||||
participant T as Tools
|
||||
U->>A: Initial input
|
||||
Note over A: Prompt + LLM
|
||||
Note over A: Messages modifier + LLM
|
||||
loop while tool_calls present
|
||||
A->>T: Execute tools
|
||||
T-->>A: ToolMessage for each tool_calls
|
||||
@@ -397,22 +415,233 @@ def create_react_agent(
|
||||
A->>U: Return final state
|
||||
```
|
||||
|
||||
Example:
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
Examples:
|
||||
Use with a simple tool:
|
||||
|
||||
def check_weather(location: str) -> str:
|
||||
'''Return the weather forecast for the specified location.'''
|
||||
return f"It's always sunny in {location}"
|
||||
```pycon
|
||||
>>> from langchain_openai import ChatOpenAI
|
||||
>>> from langgraph.prebuilt import create_react_agent
|
||||
|
||||
graph = create_react_agent(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[check_weather],
|
||||
prompt="You are a helpful assistant",
|
||||
)
|
||||
inputs = {"messages": [{"role": "user", "content": "what is the weather in sf"}]}
|
||||
for chunk in graph.stream(inputs, stream_mode="updates"):
|
||||
print(chunk)
|
||||
|
||||
... def check_weather(location: str) -> str:
|
||||
... '''Return the weather forecast for the specified location.'''
|
||||
... return f"It's always sunny in {location}"
|
||||
>>>
|
||||
>>> tools = [check_weather]
|
||||
>>> model = ChatOpenAI(model="gpt-4o")
|
||||
>>> graph = create_react_agent(model, tools=tools)
|
||||
>>> inputs = {"messages": [("user", "what is the weather in sf")]}
|
||||
>>> for s in graph.stream(inputs, stream_mode="values"):
|
||||
... message = s["messages"][-1]
|
||||
... if isinstance(message, tuple):
|
||||
... print(message)
|
||||
... else:
|
||||
... message.pretty_print()
|
||||
('user', 'what is the weather in sf')
|
||||
================================== Ai Message ==================================
|
||||
Tool Calls:
|
||||
check_weather (call_LUzFvKJRuaWQPeXvBOzwhQOu)
|
||||
Call ID: call_LUzFvKJRuaWQPeXvBOzwhQOu
|
||||
Args:
|
||||
location: San Francisco
|
||||
================================= Tool Message =================================
|
||||
Name: check_weather
|
||||
It's always sunny in San Francisco
|
||||
================================== Ai Message ==================================
|
||||
The weather in San Francisco is sunny.
|
||||
```
|
||||
Add a system prompt for the LLM:
|
||||
|
||||
```pycon
|
||||
>>> system_prompt = "You are a helpful bot named Fred."
|
||||
>>> graph = create_react_agent(model, tools, prompt=system_prompt)
|
||||
>>> inputs = {"messages": [("user", "What's your name? And what's the weather in SF?")]}
|
||||
>>> for s in graph.stream(inputs, stream_mode="values"):
|
||||
... message = s["messages"][-1]
|
||||
... if isinstance(message, tuple):
|
||||
... print(message)
|
||||
... else:
|
||||
... message.pretty_print()
|
||||
('user', "What's your name? And what's the weather in SF?")
|
||||
================================== Ai Message ==================================
|
||||
Hi, my name is Fred. Let me check the weather in San Francisco for you.
|
||||
Tool Calls:
|
||||
check_weather (call_lqhj4O0hXYkW9eknB4S41EXk)
|
||||
Call ID: call_lqhj4O0hXYkW9eknB4S41EXk
|
||||
Args:
|
||||
location: San Francisco
|
||||
================================= Tool Message =================================
|
||||
Name: check_weather
|
||||
It's always sunny in San Francisco
|
||||
================================== Ai Message ==================================
|
||||
The weather in San Francisco is currently sunny. If you need any more details or have other questions, feel free to ask!
|
||||
```
|
||||
|
||||
Add a more complex prompt for the LLM:
|
||||
|
||||
```pycon
|
||||
>>> from langchain_core.prompts import ChatPromptTemplate
|
||||
>>> prompt = ChatPromptTemplate.from_messages([
|
||||
... ("system", "You are a helpful bot named Fred."),
|
||||
... ("placeholder", "{messages}"),
|
||||
... ("user", "Remember, always be polite!"),
|
||||
... ])
|
||||
>>>
|
||||
>>> graph = create_react_agent(model, tools, prompt=prompt)
|
||||
>>> inputs = {"messages": [("user", "What's your name? And what's the weather in SF?")]}
|
||||
>>> for s in graph.stream(inputs, stream_mode="values"):
|
||||
... message = s["messages"][-1]
|
||||
... if isinstance(message, tuple):
|
||||
... print(message)
|
||||
... else:
|
||||
... message.pretty_print()
|
||||
```
|
||||
|
||||
Add complex prompt with custom graph state:
|
||||
|
||||
```pycon
|
||||
>>> from typing_extensions import TypedDict
|
||||
>>>
|
||||
>>> from langgraph.managed import IsLastStep
|
||||
>>> prompt = ChatPromptTemplate.from_messages(
|
||||
... [
|
||||
... ("system", "Today is {today}"),
|
||||
... ("placeholder", "{messages}"),
|
||||
... ]
|
||||
... )
|
||||
>>>
|
||||
>>> class CustomState(TypedDict):
|
||||
... today: str
|
||||
... messages: Annotated[list[BaseMessage], add_messages]
|
||||
... is_last_step: IsLastStep
|
||||
>>>
|
||||
>>> graph = create_react_agent(model, tools, state_schema=CustomState, prompt=prompt)
|
||||
>>> inputs = {"messages": [("user", "What's today's date? And what's the weather in SF?")], "today": "July 16, 2004"}
|
||||
>>> for s in graph.stream(inputs, stream_mode="values"):
|
||||
... message = s["messages"][-1]
|
||||
... if isinstance(message, tuple):
|
||||
... print(message)
|
||||
... else:
|
||||
... message.pretty_print()
|
||||
```
|
||||
|
||||
Add thread-level "chat memory" to the graph:
|
||||
|
||||
```pycon
|
||||
>>> from langgraph.checkpoint.memory import MemorySaver
|
||||
>>> graph = create_react_agent(model, tools, checkpointer=MemorySaver())
|
||||
>>> config = {"configurable": {"thread_id": "thread-1"}}
|
||||
>>> def print_stream(graph, inputs, config):
|
||||
... for s in graph.stream(inputs, config, stream_mode="values"):
|
||||
... message = s["messages"][-1]
|
||||
... if isinstance(message, tuple):
|
||||
... print(message)
|
||||
... else:
|
||||
... message.pretty_print()
|
||||
>>> inputs = {"messages": [("user", "What's the weather in SF?")]}
|
||||
>>> print_stream(graph, inputs, config)
|
||||
>>> inputs2 = {"messages": [("user", "Cool, so then should i go biking today?")]}
|
||||
>>> print_stream(graph, inputs2, config)
|
||||
('user', "What's the weather in SF?")
|
||||
================================== Ai Message ==================================
|
||||
Tool Calls:
|
||||
check_weather (call_ChndaktJxpr6EMPEB5JfOFYc)
|
||||
Call ID: call_ChndaktJxpr6EMPEB5JfOFYc
|
||||
Args:
|
||||
location: San Francisco
|
||||
================================= Tool Message =================================
|
||||
Name: check_weather
|
||||
It's always sunny in San Francisco
|
||||
================================== Ai Message ==================================
|
||||
The weather in San Francisco is sunny. Enjoy your day!
|
||||
================================ Human Message =================================
|
||||
Cool, so then should i go biking today?
|
||||
================================== Ai Message ==================================
|
||||
Since the weather in San Francisco is sunny, it sounds like a great day for biking! Enjoy your ride!
|
||||
```
|
||||
|
||||
Add an interrupt to let the user confirm before taking an action:
|
||||
|
||||
```pycon
|
||||
>>> graph = create_react_agent(
|
||||
... model, tools, interrupt_before=["tools"], checkpointer=MemorySaver()
|
||||
>>> )
|
||||
>>> config = {"configurable": {"thread_id": "thread-1"}}
|
||||
|
||||
>>> inputs = {"messages": [("user", "What's the weather in SF?")]}
|
||||
>>> print_stream(graph, inputs, config)
|
||||
>>> snapshot = graph.get_state(config)
|
||||
>>> print("Next step: ", snapshot.next)
|
||||
>>> print_stream(graph, None, config)
|
||||
```
|
||||
|
||||
Add cross-thread memory to the graph:
|
||||
|
||||
```pycon
|
||||
>>> from langgraph.prebuilt import InjectedStore
|
||||
>>> from langgraph.store.base import BaseStore
|
||||
|
||||
>>> def save_memory(memory: str, *, config: RunnableConfig, store: Annotated[BaseStore, InjectedStore()]) -> str:
|
||||
... '''Save the given memory for the current user.'''
|
||||
... # This is a **tool** the model can use to save memories to storage
|
||||
... user_id = config.get("configurable", {}).get("user_id")
|
||||
... namespace = ("memories", user_id)
|
||||
... store.put(namespace, f"memory_{len(store.search(namespace))}", {"data": memory})
|
||||
... return f"Saved memory: {memory}"
|
||||
|
||||
>>> def prepare_model_inputs(state: AgentState, config: RunnableConfig, store: BaseStore):
|
||||
... # Retrieve user memories and add them to the system message
|
||||
... # This function is called **every time** the model is prompted. It converts the state to a prompt
|
||||
... user_id = config.get("configurable", {}).get("user_id")
|
||||
... namespace = ("memories", user_id)
|
||||
... memories = [m.value["data"] for m in store.search(namespace)]
|
||||
... system_msg = f"User memories: {', '.join(memories)}"
|
||||
... return [{"role": "system", "content": system_msg)] + state["messages"]
|
||||
|
||||
>>> from langgraph.checkpoint.memory import MemorySaver
|
||||
>>> from langgraph.store.memory import InMemoryStore
|
||||
>>> store = InMemoryStore()
|
||||
>>> graph = create_react_agent(model, [save_memory], prompt=prepare_model_inputs, store=store, checkpointer=MemorySaver())
|
||||
>>> config = {"configurable": {"thread_id": "thread-1", "user_id": "1"}}
|
||||
|
||||
>>> inputs = {"messages": [("user", "Hey I'm Will, how's it going?")]}
|
||||
>>> print_stream(graph, inputs, config)
|
||||
('user', "Hey I'm Will, how's it going?")
|
||||
================================== Ai Message ==================================
|
||||
Hello Will! It's nice to meet you. I'm doing well, thank you for asking. How are you doing today?
|
||||
|
||||
>>> inputs2 = {"messages": [("user", "I like to bike")]}
|
||||
>>> print_stream(graph, inputs2, config)
|
||||
================================ Human Message =================================
|
||||
I like to bike
|
||||
================================== Ai Message ==================================
|
||||
That's great to hear, Will! Biking is an excellent hobby and form of exercise. It's a fun way to stay active and explore your surroundings. Do you have any favorite biking routes or trails you enjoy? Or perhaps you're into a specific type of biking, like mountain biking or road cycling?
|
||||
|
||||
>>> config = {"configurable": {"thread_id": "thread-2", "user_id": "1"}}
|
||||
>>> inputs3 = {"messages": [("user", "Hi there! Remember me?")]}
|
||||
>>> print_stream(graph, inputs3, config)
|
||||
================================ Human Message =================================
|
||||
Hi there! Remember me?
|
||||
================================== Ai Message ==================================
|
||||
User memories:
|
||||
Hello! Of course, I remember you, Will! You mentioned earlier that you like to bike. It's great to hear from you again. How have you been? Have you been on any interesting bike rides lately?
|
||||
```
|
||||
|
||||
Add a timeout for a given step:
|
||||
|
||||
```pycon
|
||||
>>> import time
|
||||
... def check_weather(location: str) -> str:
|
||||
... '''Return the weather forecast for the specified location.'''
|
||||
... time.sleep(2)
|
||||
... return f"It's always sunny in {location}"
|
||||
>>>
|
||||
>>> tools = [check_weather]
|
||||
>>> graph = create_react_agent(model, tools)
|
||||
>>> graph.step_timeout = 1 # Seconds
|
||||
>>> for s in graph.stream({"messages": [("user", "what is the weather in sf")]}):
|
||||
... print(s)
|
||||
TimeoutError: Timed out at step 2
|
||||
```
|
||||
"""
|
||||
if version not in ("v1", "v2"):
|
||||
|
||||
@@ -73,57 +73,87 @@ class ValidationNode(RunnableCallable):
|
||||
Returns:
|
||||
(Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of ToolMessages with the validated content or error messages.
|
||||
|
||||
Example:
|
||||
```python title="Example usage for re-prompting the model to generate a valid response:"
|
||||
from typing import Literal, Annotated
|
||||
from typing_extensions import TypedDict
|
||||
Examples:
|
||||
Example usage for re-prompting the model to generate a valid response:
|
||||
>>> from typing import Literal, Annotated
|
||||
>>> from typing_extensions import TypedDict
|
||||
...
|
||||
>>> from langchain_anthropic import ChatAnthropic
|
||||
>>> from pydantic import BaseModel, field_validator
|
||||
...
|
||||
>>> from langgraph.graph import END, START, StateGraph
|
||||
>>> from langgraph.prebuilt import ValidationNode
|
||||
>>> from langgraph.graph.message import add_messages
|
||||
...
|
||||
...
|
||||
>>> class SelectNumber(BaseModel):
|
||||
... a: int
|
||||
...
|
||||
... @field_validator("a")
|
||||
... def a_must_be_meaningful(cls, v):
|
||||
... if v != 37:
|
||||
... raise ValueError("Only 37 is allowed")
|
||||
... return v
|
||||
...
|
||||
...
|
||||
>>> builder = StateGraph(Annotated[list, add_messages])
|
||||
>>> llm = ChatAnthropic(model="claude-3-5-haiku-latest").bind_tools([SelectNumber])
|
||||
>>> builder.add_node("model", llm)
|
||||
>>> builder.add_node("validation", ValidationNode([SelectNumber]))
|
||||
>>> builder.add_edge(START, "model")
|
||||
...
|
||||
...
|
||||
>>> def should_validate(state: list) -> Literal["validation", "__end__"]:
|
||||
... if state[-1].tool_calls:
|
||||
... return "validation"
|
||||
... return END
|
||||
...
|
||||
...
|
||||
>>> builder.add_conditional_edges("model", should_validate)
|
||||
...
|
||||
...
|
||||
>>> def should_reprompt(state: list) -> Literal["model", "__end__"]:
|
||||
... for msg in state[::-1]:
|
||||
... # None of the tool calls were errors
|
||||
... if msg.type == "ai":
|
||||
... return END
|
||||
... if msg.additional_kwargs.get("is_error"):
|
||||
... return "model"
|
||||
... return END
|
||||
...
|
||||
...
|
||||
>>> builder.add_conditional_edges("validation", should_reprompt)
|
||||
...
|
||||
...
|
||||
>>> graph = builder.compile()
|
||||
>>> res = graph.invoke(("user", "Select a number, any number"))
|
||||
>>> # Show the retry logic
|
||||
>>> for msg in res:
|
||||
... msg.pretty_print()
|
||||
================================ Human Message =================================
|
||||
Select a number, any number
|
||||
================================== Ai Message ==================================
|
||||
[{'id': 'toolu_01JSjT9Pq8hGmTgmMPc6KnvM', 'input': {'a': 42}, 'name': 'SelectNumber', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
SelectNumber (toolu_01JSjT9Pq8hGmTgmMPc6KnvM)
|
||||
Call ID: toolu_01JSjT9Pq8hGmTgmMPc6KnvM
|
||||
Args:
|
||||
a: 42
|
||||
================================= Tool Message =================================
|
||||
Name: SelectNumber
|
||||
ValidationError(model='SelectNumber', errors=[{'loc': ('a',), 'msg': 'Only 37 is allowed', 'type': 'value_error'}])
|
||||
Respond after fixing all validation errors.
|
||||
================================== Ai Message ==================================
|
||||
[{'id': 'toolu_01PkxSVxNxc5wqwCPW1FiSmV', 'input': {'a': 37}, 'name': 'SelectNumber', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
SelectNumber (toolu_01PkxSVxNxc5wqwCPW1FiSmV)
|
||||
Call ID: toolu_01PkxSVxNxc5wqwCPW1FiSmV
|
||||
Args:
|
||||
a: 37
|
||||
================================= Tool Message =================================
|
||||
Name: SelectNumber
|
||||
{"a": 37}
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from pydantic import BaseModel, field_validator
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from langgraph.prebuilt import ValidationNode
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
class SelectNumber(BaseModel):
|
||||
a: int
|
||||
|
||||
@field_validator("a")
|
||||
def a_must_be_meaningful(cls, v):
|
||||
if v != 37:
|
||||
raise ValueError("Only 37 is allowed")
|
||||
return v
|
||||
|
||||
builder = StateGraph(Annotated[list, add_messages])
|
||||
llm = ChatAnthropic(model="claude-3-5-haiku-latest").bind_tools([SelectNumber])
|
||||
builder.add_node("model", llm)
|
||||
builder.add_node("validation", ValidationNode([SelectNumber]))
|
||||
builder.add_edge(START, "model")
|
||||
|
||||
def should_validate(state: list) -> Literal["validation", "__end__"]:
|
||||
if state[-1].tool_calls:
|
||||
return "validation"
|
||||
return END
|
||||
|
||||
builder.add_conditional_edges("model", should_validate)
|
||||
|
||||
def should_reprompt(state: list) -> Literal["model", "__end__"]:
|
||||
for msg in state[::-1]:
|
||||
# None of the tool calls were errors
|
||||
if msg.type == "ai":
|
||||
return END
|
||||
if msg.additional_kwargs.get("is_error"):
|
||||
return "model"
|
||||
return END
|
||||
|
||||
builder.add_conditional_edges("validation", should_reprompt)
|
||||
|
||||
graph = builder.compile()
|
||||
res = graph.invoke(("user", "Select a number, any number"))
|
||||
# Show the retry logic
|
||||
for msg in res:
|
||||
msg.pretty_print()
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
||||
Generated
+2
-2
@@ -446,7 +446,7 @@ typing-extensions = ">=4.7"
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.4.0"
|
||||
version = "0.3.34"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
@@ -467,7 +467,7 @@ url = "../langgraph"
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-checkpoint"
|
||||
version = "2.0.25"
|
||||
version = "2.0.24"
|
||||
description = "Library with base interfaces for LangGraph checkpoint savers."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user