mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-19 22:25:44 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6fb2ef5ca | ||
|
|
2fb7e92879 | ||
|
|
46b2d08a8a | ||
|
|
649b742e0a | ||
|
|
fd4629e778 | ||
|
|
d14f98f01b | ||
|
|
c0b56bf60d | ||
|
|
e8b875906f | ||
|
|
d48faecd42 | ||
|
|
5e175e098b | ||
|
|
bcf335651e | ||
|
|
965849823a | ||
|
|
45e7101457 | ||
|
|
ecd75a8c4d | ||
|
|
edec5c055e | ||
|
|
ff310cc8d6 | ||
|
|
233bd78ee4 | ||
|
|
b818bf2fba | ||
|
|
c5ec568cfb | ||
|
|
29548b2e27 | ||
|
|
f2dc537696 | ||
|
|
31d21c8d24 | ||
|
|
4a03ed5915 | ||
|
|
dc083c6563 | ||
|
|
bfe005fef0 | ||
|
|
5388b7c74f | ||
|
|
0d4617817d | ||
|
|
5046ec4f43 | ||
|
|
c26bb9e156 | ||
|
|
3982090c6d | ||
|
|
6d3a2c59da | ||
|
|
79444dee9c | ||
|
|
07f8f87780 | ||
|
|
99bde8774e | ||
|
|
5946f4ff2b | ||
|
|
b99734d157 | ||
|
|
d1c29fc8be | ||
|
|
dc47c7b357 | ||
|
|
561aa3080e | ||
|
|
c6a450b857 | ||
|
|
0557fb03a4 | ||
|
|
c9adf995c2 | ||
|
|
fc20de5bba | ||
|
|
0822a287e3 | ||
|
|
66741ba071 | ||
|
|
e72c25873f | ||
|
|
739336516d | ||
|
|
ae6c793bdf | ||
|
|
d8954963b4 | ||
|
|
ea38ba9e29 | ||
|
|
76229ade66 | ||
|
|
4aad36947e | ||
|
|
cb7b667e6f | ||
|
|
822ddb5f48 | ||
|
|
de355ee2d2 | ||
|
|
9ca270d62d | ||
|
|
28b5105913 | ||
|
|
be47752f0e | ||
|
|
aa83f4a33e | ||
|
|
4d69331a52 | ||
|
|
fb8c386958 | ||
|
|
ac8b51f1f2 | ||
|
|
db0f508269 | ||
|
|
fe110ae145 | ||
|
|
fc276c5ac0 | ||
|
|
9360545659 | ||
|
|
26e30ad6af | ||
|
|
8df533b489 | ||
|
|
f9df0f4700 |
@@ -21,6 +21,7 @@ jobs:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
- "3.13"
|
||||
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
@@ -32,6 +33,11 @@ jobs:
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
cache-key: test-${{ inputs.working-directory }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
|
||||
@@ -16,9 +16,12 @@ jobs:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
- "3.13"
|
||||
core-version:
|
||||
- ">=0.2.39,<0.3.0"
|
||||
- "latest"
|
||||
include:
|
||||
- python-version: "3.11"
|
||||
core-version: ">=0.2.39,<0.3.0"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -32,6 +35,11 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: test-langgraph
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
|
||||
@@ -27,6 +27,11 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: test-scheduler-kafka
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
poetry install --with docs
|
||||
poetry install --with test
|
||||
poetry run pip install -U pytest pytest-check-links langsmith langchain GitPython
|
||||
|
||||
- name: Lint Docs
|
||||
|
||||
@@ -24,7 +24,7 @@ export -f execute_notebook
|
||||
|
||||
# Check if custom notebook paths are provided
|
||||
if [ $# -gt 0 ]; then
|
||||
notebooks="$@"
|
||||
notebooks=$(echo "$@" | tr ' ' '\n' | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
|
||||
else
|
||||
# Find all notebooks and filter out those in the skip list
|
||||
notebooks=$(find docs/docs/tutorials docs/docs/how-tos -name "*.ipynb" | grep -v ".ipynb_checkpoints" | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from typing import List, Literal, Optional
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
import nbformat
|
||||
from nbconvert.preprocessors import Preprocessor
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
# Base URL for all class documentation
|
||||
_LANGCHAIN_API_REFERENCE = "https://python.langchain.com/api_reference/"
|
||||
_LANGGRAPH_API_REFERENCE = "https://langchain-ai.github.io/langgraph/reference/"
|
||||
|
||||
|
||||
# (alias/re-exported modules, source module, class, docs namespace)
|
||||
MANUAL_API_REFERENCES_LANGGRAPH = [
|
||||
(
|
||||
["langgraph.prebuilt"],
|
||||
"langgraph.prebuilt.chat_agent_executor",
|
||||
"create_react_agent",
|
||||
"prebuilt",
|
||||
),
|
||||
(["langgraph.prebuilt"], "langgraph.prebuilt.tool_node", "ToolNode", "prebuilt"),
|
||||
(
|
||||
["langgraph.prebuilt"],
|
||||
"langgraph.prebuilt.tool_node",
|
||||
"tools_condition",
|
||||
"prebuilt",
|
||||
),
|
||||
(
|
||||
["langgraph.prebuilt"],
|
||||
"langgraph.prebuilt.tool_node",
|
||||
"InjectedState",
|
||||
"prebuilt",
|
||||
),
|
||||
# Graph
|
||||
(["langgraph.graph"], "langgraph.graph.message", "add_messages", "graphs"),
|
||||
(["langgraph.graph"], "langgraph.graph.state", "StateGraph", "graphs"),
|
||||
(["langgraph.graph"], "langgraph.graph.state", "CompiledStateGraph", "graphs"),
|
||||
([], "langgraph.types", "StreamMode", "types"),
|
||||
(["langgraph.graph"], "langgraph.constants", "START", "constants"),
|
||||
(["langgraph.graph"], "langgraph.constants", "END", "constants"),
|
||||
(["langgraph.constants"], "langgraph.types", "Send", "types"),
|
||||
(["langgraph.constants"], "langgraph.types", "Interrupt", "types"),
|
||||
([], "langgraph.types", "RetryPolicy", "types"),
|
||||
([], "langgraph.checkpoint.base", "Checkpoint", "checkpoints"),
|
||||
([], "langgraph.checkpoint.base", "CheckpointMetadata", "checkpoints"),
|
||||
([], "langgraph.checkpoint.base", "BaseCheckpointSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.base", "SerializerProtocol", "checkpoints"),
|
||||
([], "langgraph.checkpoint.serde.jsonplus", "JsonPlusSerializer", "checkpoints"),
|
||||
([], "langgraph.checkpoint.memory", "MemorySaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.sqlite.aio", "AsyncSqliteSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.sqlite", "SqliteSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.postgres.aio", "AsyncPostgresSaver", "checkpoints"),
|
||||
([], "langgraph.checkpoint.postgres", "PostgresSaver", "checkpoints"),
|
||||
]
|
||||
|
||||
WELL_KNOWN_LANGGRAPH_OBJECTS = {
|
||||
(module_, class_): (source_module, namespace)
|
||||
for (modules, source_module, class_, namespace) in MANUAL_API_REFERENCES_LANGGRAPH
|
||||
for module_ in modules + [source_module]
|
||||
}
|
||||
|
||||
|
||||
def _make_regular_expression(pkg_prefix: str) -> re.Pattern:
|
||||
if not pkg_prefix.isidentifier():
|
||||
raise ValueError(f"Invalid package prefix: {pkg_prefix}")
|
||||
return re.compile(
|
||||
r"from\s+(" + pkg_prefix + "(?:_\w+)?(?:\.\w+)*?)\s+import\s+"
|
||||
r"((?:\w+(?:,\s*)?)*" # Match zero or more words separated by a comma+optional ws
|
||||
r"(?:\s*\(.*?\))?)", # Match optional parentheses block
|
||||
re.DOTALL, # Match newlines as well
|
||||
)
|
||||
|
||||
|
||||
# Regular expression to match langchain import lines
|
||||
_IMPORT_LANGCHAIN_RE = _make_regular_expression("langchain")
|
||||
_IMPORT_LANGGRAPH_RE = _make_regular_expression("langgraph")
|
||||
|
||||
|
||||
def _get_full_module_name(module_path, class_name) -> Optional[str]:
|
||||
"""Get full module name using inspect"""
|
||||
try:
|
||||
module = importlib.import_module(module_path)
|
||||
class_ = getattr(module, class_name)
|
||||
module = inspect.getmodule(class_)
|
||||
if module is None:
|
||||
# For constants, inspect.getmodule() might return None
|
||||
# In this case, we'll return the original module_path
|
||||
return module_path
|
||||
return module.__name__
|
||||
except AttributeError as e:
|
||||
logger.warning(f"Could not find module for {class_name}, {e}")
|
||||
return None
|
||||
except ImportError as e:
|
||||
logger.warning(f"Failed to load for class {class_name}, {e}")
|
||||
return None
|
||||
|
||||
|
||||
def _get_doc_title(data: str, file_name: str) -> str:
|
||||
try:
|
||||
return re.findall(r"^#\s*(.*)", data, re.MULTILINE)[0]
|
||||
except IndexError:
|
||||
pass
|
||||
# Parse the rst-style titles
|
||||
try:
|
||||
return re.findall(r"^(.*)\n=+\n", data, re.MULTILINE)[0]
|
||||
except IndexError:
|
||||
return file_name
|
||||
|
||||
|
||||
class ImportInformation(TypedDict):
|
||||
imported: str # imported class name
|
||||
source: str # module path
|
||||
docs: str # URL to the documentation
|
||||
title: str # Title of the document
|
||||
|
||||
|
||||
def _get_imports(
|
||||
code: str, doc_title: str, package_ecosystem: Literal["langchain", "langgraph"]
|
||||
) -> List[ImportInformation]:
|
||||
"""Get imports from the given code block.
|
||||
|
||||
Args:
|
||||
code: Python code block from which to extract imports
|
||||
doc_title: Title of the document
|
||||
package_ecosystem: "langchain" or "langgraph". The two live in different
|
||||
repositories and have separate documentation sites.
|
||||
|
||||
Returns:
|
||||
List of import information for the given code block
|
||||
"""
|
||||
imports = []
|
||||
|
||||
if package_ecosystem == "langchain":
|
||||
pattern = _IMPORT_LANGCHAIN_RE
|
||||
elif package_ecosystem == "langgraph":
|
||||
pattern = _IMPORT_LANGGRAPH_RE
|
||||
else:
|
||||
raise ValueError(f"Invalid package ecosystem: {package_ecosystem}")
|
||||
|
||||
for import_match in pattern.finditer(code):
|
||||
module = import_match.group(1)
|
||||
if "pydantic_v1" in module:
|
||||
continue
|
||||
imports_str = (
|
||||
import_match.group(2).replace("(\n", "").replace("\n)", "")
|
||||
) # Handle newlines within parentheses
|
||||
# remove any newline and spaces, then split by comma
|
||||
imported_classes = [
|
||||
imp.strip()
|
||||
for imp in re.split(r",\s*", imports_str.replace("\n", ""))
|
||||
if imp.strip()
|
||||
]
|
||||
for class_name in imported_classes:
|
||||
module_path = _get_full_module_name(module, class_name)
|
||||
if not module_path:
|
||||
continue
|
||||
if len(module_path.split(".")) < 2:
|
||||
continue
|
||||
|
||||
if package_ecosystem == "langchain":
|
||||
pkg = module_path.split(".")[0].replace("langchain_", "")
|
||||
top_level_mod = module_path.split(".")[1]
|
||||
|
||||
url = (
|
||||
_LANGCHAIN_API_REFERENCE
|
||||
+ pkg
|
||||
+ "/"
|
||||
+ top_level_mod
|
||||
+ "/"
|
||||
+ module_path
|
||||
+ "."
|
||||
+ class_name
|
||||
+ ".html"
|
||||
)
|
||||
elif package_ecosystem == "langgraph":
|
||||
if (module, class_name) not in WELL_KNOWN_LANGGRAPH_OBJECTS:
|
||||
# Likely not documented yet
|
||||
continue
|
||||
|
||||
source_module, namespace = WELL_KNOWN_LANGGRAPH_OBJECTS[
|
||||
(module, class_name)
|
||||
]
|
||||
url = (
|
||||
_LANGGRAPH_API_REFERENCE
|
||||
+ namespace
|
||||
+ "/#"
|
||||
+ source_module
|
||||
+ "."
|
||||
+ class_name
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Invalid package ecosystem: {package_ecosystem}")
|
||||
|
||||
# Add the import information to our list
|
||||
imports.append(
|
||||
{
|
||||
"imported": class_name,
|
||||
"source": module,
|
||||
"docs": url,
|
||||
"title": doc_title,
|
||||
}
|
||||
)
|
||||
|
||||
return imports
|
||||
|
||||
|
||||
class ImportPreprocessor(Preprocessor):
|
||||
"""A preprocessor to replace imports in each Python code cell with links to their
|
||||
documentation and append the import info in a comment."""
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
self.all_imports = []
|
||||
file_name = os.path.basename(resources.get("metadata", {}).get("name", ""))
|
||||
_DOC_TITLE = _get_doc_title(nb.cells[0].source, file_name)
|
||||
|
||||
cells = []
|
||||
for cell in nb.cells:
|
||||
if cell.cell_type == "code":
|
||||
cells.append(cell)
|
||||
imports = _get_imports(
|
||||
cell.source, _DOC_TITLE, "langchain"
|
||||
) + _get_imports(cell.source, _DOC_TITLE, "langgraph")
|
||||
if not imports:
|
||||
continue
|
||||
|
||||
cells.append(
|
||||
nbformat.v4.new_markdown_cell(
|
||||
source=f"""
|
||||
<div>
|
||||
<b>API Reference:</b>
|
||||
{' | '.join(f'<a href="{imp["docs"]}">{imp["imported"]}</a>' for imp in imports)}
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
)
|
||||
else:
|
||||
cells.append(cell)
|
||||
nb.cells = cells
|
||||
return nb, resources
|
||||
@@ -0,0 +1,126 @@
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import nbformat
|
||||
from nbconvert.exporters import MarkdownExporter
|
||||
from nbconvert.preprocessors import Preprocessor
|
||||
|
||||
from generate_api_reference_links import ImportPreprocessor
|
||||
|
||||
|
||||
class EscapePreprocessor(Preprocessor):
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
if cell.cell_type == "markdown":
|
||||
# rewrite markdown links to html links (excluding image links)
|
||||
cell.source = re.sub(
|
||||
r"(?<!!)\[([^\]]*)\]\((?![^\)]*//)([^)]*)(?:\.ipynb)?\)",
|
||||
r'<a href="\2">\1</a>',
|
||||
cell.source,
|
||||
)
|
||||
# Fix image paths in <img> tags
|
||||
cell.source = re.sub(
|
||||
r'<img\s+src="\.?/img/([^"]+)"', r'<img src="../img/\1"', cell.source
|
||||
)
|
||||
|
||||
elif cell.cell_type == "code":
|
||||
# escape ``` in code
|
||||
cell.source = cell.source.replace("```", r"\`\`\`")
|
||||
# escape ``` in output
|
||||
if "outputs" in cell:
|
||||
filter_out = set()
|
||||
for i, output in enumerate(cell["outputs"]):
|
||||
if "text" in output:
|
||||
if not output["text"].strip():
|
||||
filter_out.add(i)
|
||||
continue
|
||||
|
||||
value = output["text"].replace("```", r"\`\`\`")
|
||||
# handle a funky case w/ references in text
|
||||
value = re.sub(r"\[(\d+)\](?=\[(\d+)\])", r"[\1]\\", value)
|
||||
output["text"] = value
|
||||
elif "data" in output:
|
||||
for key, value in output["data"].items():
|
||||
if isinstance(value, str):
|
||||
value = value.replace("```", r"\`\`\`")
|
||||
# handle a funky case w/ references in text
|
||||
output["data"][key] = re.sub(
|
||||
r"\[(\d+)\](?=\[(\d+)\])", r"[\1]\\", value
|
||||
)
|
||||
cell["outputs"] = [
|
||||
output
|
||||
for i, output in enumerate(cell["outputs"])
|
||||
if i not in filter_out
|
||||
]
|
||||
|
||||
return cell, resources
|
||||
|
||||
|
||||
class ExtractAttachmentsPreprocessor(Preprocessor):
|
||||
"""
|
||||
Extracts all of the outputs from the notebook file. The extracted
|
||||
outputs are returned in the 'resources' dictionary.
|
||||
"""
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Apply a transformation on each cell,
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
cell_index : int
|
||||
Index of the cell being processed (see base.py)
|
||||
"""
|
||||
|
||||
# Get files directory if it has been specified
|
||||
|
||||
# Make sure outputs key exists
|
||||
if not isinstance(resources["outputs"], dict):
|
||||
resources["outputs"] = {}
|
||||
|
||||
# Loop through all of the attachments in the cell
|
||||
for name, attach in cell.get("attachments", {}).items():
|
||||
for mime, data in attach.items():
|
||||
if mime not in {
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/svg+xml",
|
||||
"application/pdf",
|
||||
}:
|
||||
continue
|
||||
|
||||
# attachments are pre-rendered. Only replace markdown-formatted
|
||||
# images with the following logic
|
||||
attach_str = f"({name})"
|
||||
if attach_str in cell.source:
|
||||
data = f"(data:{mime};base64,{data})"
|
||||
cell.source = cell.source.replace(attach_str, data)
|
||||
|
||||
return cell, resources
|
||||
|
||||
|
||||
exporter = MarkdownExporter(
|
||||
preprocessors=[
|
||||
EscapePreprocessor,
|
||||
ExtractAttachmentsPreprocessor,
|
||||
ImportPreprocessor,
|
||||
],
|
||||
template_name="mdoutput",
|
||||
extra_template_basedirs=[
|
||||
os.path.join(os.path.dirname(__file__), "notebook_convert_templates")
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def convert_notebook(
|
||||
notebook_path: Path,
|
||||
) -> Path:
|
||||
with open(notebook_path) as f:
|
||||
nb = nbformat.read(f, as_version=4)
|
||||
|
||||
body, _ = exporter.from_notebook_node(nb)
|
||||
return body
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mimetypes": {
|
||||
"text/markdown": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends 'markdown/index.md.j2' %}
|
||||
|
||||
{%- block traceback_line -%}
|
||||
```output
|
||||
{{ line.rstrip() | strip_ansi }}
|
||||
```
|
||||
{%- endblock traceback_line -%}
|
||||
|
||||
{%- block stream -%}
|
||||
```output
|
||||
{{ output.text.rstrip() }}
|
||||
```
|
||||
{%- endblock stream -%}
|
||||
|
||||
{%- block data_text scoped -%}
|
||||
```output
|
||||
{{ output.data['text/plain'].rstrip() }}
|
||||
```
|
||||
{%- endblock data_text -%}
|
||||
|
||||
{%- block data_html scoped -%}
|
||||
```html
|
||||
{{ output.data['text/html'] | safe }}
|
||||
```
|
||||
{%- endblock data_html -%}
|
||||
|
||||
{%- block data_jpg scoped -%}
|
||||

|
||||
{%- endblock data_jpg -%}
|
||||
|
||||
{%- block data_png scoped -%}
|
||||

|
||||
{%- endblock data_png -%}
|
||||
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from mkdocs.structure.pages import Page
|
||||
from mkdocs.structure.files import Files, File
|
||||
from notebook_convert import convert_notebook
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
class NotebookFile(File):
|
||||
def is_documentation_page(self):
|
||||
return True
|
||||
|
||||
|
||||
def on_files(files: Files, **kwargs: Dict[str, Any]):
|
||||
new_files = Files([])
|
||||
for file in files:
|
||||
if file.src_path.endswith(".ipynb"):
|
||||
new_file = NotebookFile(
|
||||
path=file.src_path,
|
||||
src_dir=file.src_dir,
|
||||
dest_dir=file.dest_dir,
|
||||
use_directory_urls=file.use_directory_urls,
|
||||
)
|
||||
new_files.append(new_file)
|
||||
else:
|
||||
new_files.append(file)
|
||||
return new_files
|
||||
|
||||
|
||||
def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
|
||||
if page.file.src_path.endswith(".ipynb"):
|
||||
logger.info("Processing Jupyter notebook: %s", page.file.src_path)
|
||||
body = convert_notebook(page.file.abs_src_path)
|
||||
return body
|
||||
|
||||
return markdown
|
||||
@@ -40,7 +40,6 @@ NOTEBOOKS_NO_EXECUTION = [
|
||||
"docs/docs/tutorials/storm/storm.ipynb", # issues only when running with VCR
|
||||
"docs/docs/tutorials/lats/lats.ipynb", # issues only when running with VCR
|
||||
"docs/docs/tutorials/multi_agent/hierarchical_agent_teams.ipynb", # taking a very long time to run
|
||||
"docs/docs/tutorials/customer-support/customer-support.ipynb", # user input - update
|
||||
"docs/docs/tutorials/rag/langgraph_crag.ipynb", # flakiness from tavily
|
||||
"docs/docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # Cannot create a consistent method resolution error from VCR
|
||||
"docs/docs/how-tos/map-reduce.ipynb" # flakiness from structured output, only when running with VCR
|
||||
@@ -70,10 +69,13 @@ def is_comment(code: str) -> bool:
|
||||
return code.strip().startswith("#")
|
||||
|
||||
|
||||
def has_blocklisted_command(code: str) -> bool:
|
||||
def has_blocklisted_command(code: str, metadata: dict) -> bool:
|
||||
if 'hide_from_vcr' in metadata:
|
||||
return True
|
||||
|
||||
code = code.strip()
|
||||
for blocklisted_command in BLOCKLIST_COMMANDS:
|
||||
if blocklisted_command in code:
|
||||
for blocklisted_pattern in BLOCKLIST_COMMANDS:
|
||||
if blocklisted_pattern in code:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -108,7 +110,7 @@ def add_vcr_to_notebook(
|
||||
if all(is_comment(line) or not line.strip() for line in lines):
|
||||
continue
|
||||
|
||||
if has_blocklisted_command(cell.source):
|
||||
if has_blocklisted_command(cell.source, cell.metadata):
|
||||
continue
|
||||
|
||||
cell_id = cell.get("id", idx)
|
||||
@@ -125,6 +127,8 @@ def add_vcr_to_notebook(
|
||||
"import msgpack",
|
||||
"import base64",
|
||||
"import zlib",
|
||||
"import os",
|
||||
"os.environ.pop(\"LANGCHAIN_TRACING_V2\", None)",
|
||||
"custom_vcr = vcr.VCR()",
|
||||
"",
|
||||
"def compress_data(data, compression_level=9):",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ This can be in several ways, but the primary supported way is to add an "interru
|
||||
|
||||
## Setup
|
||||
|
||||
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/edit-graph-state.ipynb#build-the-agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
|
||||
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/edit-graph-state.ipynb#agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
|
||||
|
||||
### SDK initialization
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Luckily, LangGraph makes it possible to do similar things in a production way. T
|
||||
|
||||
## Setup
|
||||
|
||||
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/wait-user-input.ipynb#build-the-agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
|
||||
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/wait-user-input.ipynb#agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
|
||||
|
||||
### SDK initialization
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
" <p>\n",
|
||||
" In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the <code>create_react_agent(model, tools=tool)</code> (<a href=\"https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent\">API doc</a>) constructor. This may be more appropriate if you are used to LangChain’s <a href=\"https://python.langchain.com/v0.1/docs/modules/agents/concepts/#agentexecutor\">AgentExecutor</a> class.\n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -107,7 +107,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdin",
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ANTHROPIC_API_KEY: ········\n"
|
||||
@@ -91,12 +91,9 @@
|
||||
"id": "51b6817d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div class=\"admonition tip\">\n",
|
||||
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"!!! tip \"Set up [LangSmith](https://smith.langchain.com) for LangGraph development\"\n",
|
||||
"\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started [here](https://docs.smith.langchain.com)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -103,7 +103,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"<div class=\"admonition tip\">\n",
|
||||
" <p class=\"admonition-title\">Note</p>\n",
|
||||
" <p>\n",
|
||||
" If you need memory that is <b>shared</b> across multiple conversations or users (cross-thread persistence), check out this <a href=\"https://langchain-ai.github.io/langgraph/how-tos/cross-thread-persistence/\">how-to guide</a>).\n",
|
||||
" If you need memory that is <b>shared</b> across multiple conversations or users (cross-thread persistence), check out this <a href=\"https://langchain-ai.github.io/langgraph/how-tos/cross-thread-persistence/\">how-to guide</a>.\n",
|
||||
" </p>\n",
|
||||
"</div>"
|
||||
]
|
||||
@@ -88,7 +88,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdin",
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ANTHROPIC_API_KEY: ········\n"
|
||||
@@ -118,7 +118,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -61,7 +61,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -70,12 +70,12 @@
|
||||
"source": [
|
||||
"## Without returning state\n",
|
||||
"\n",
|
||||
"We are going to define a dummy graph in this example that will always hit the recursion limit. First, we will implement it without returning the state and show that it hits the recursion limit. This graph is based on the ReACT architecture, but instead of actually making decisions and taking actions it just loops forever."
|
||||
"We are going to define a dummy graph in this example that will always hit the recursion limit. First, we will implement it without returning the state and show that it hits the recursion limit. This graph is based on the ReAct architecture, but instead of actually making decisions and taking actions it just loops forever."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -145,7 +145,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -171,18 +171,18 @@
|
||||
"source": [
|
||||
"## With returning state\n",
|
||||
"\n",
|
||||
"If we wanted to actually return the state, what we are going to do is introduce a new key to our state called `is_last_step` which keeps track of if we are on the last step of our recursion limit. If so, we will bypass all other graph decisions and simply terminate the graph, returning the state to the user without causing an error.\n",
|
||||
"To avoid hitting the recursion limit, we can introduce a new key to our state called `remaining_steps`. It will keep track of number of steps until reaching the recursion limit. We can then check the value of `remaining_steps` to determine whether we should terminate the graph execution and return the state to the user without causing the `RecursionError`.\n",
|
||||
"\n",
|
||||
"We are going to use a `ManagedValue` channel to do this. A `ManagedValue` channel is a state channel that will exist for the duration of our graph run and no longer. Since our `action` node is going to always induce at least 2 extra steps to our graph (since the `action` node ALWAYS calls the `decision` node afterwards), we will use this channel to check if we are within 2 steps of the limit. See the implementation of `IsLastOrSecondToLastStepManager` below.\n",
|
||||
"To do so, we will use a special `RemainingSteps` annotation. Under the hood, it creates a special `ManagedValue` channel -- a state channel that will exist for the duration of our graph run and no longer.\n",
|
||||
"\n",
|
||||
"This implementation very closely mirrors the implementation of `isLastStep` (which you can use by calling `from langgraph.managed import IsLastStep` and then decorating state keys with the `isLastStep` type), but in this case we check if we are on the last OR second-to-last step, instead of just the last step.\n",
|
||||
"Since our `action` node is going to always induce at least 2 extra steps to our graph (since the `action` node ALWAYS calls the `decision` node afterwards), we will use this channel to check if we are within 2 steps of the limit.\n",
|
||||
"\n",
|
||||
"Now, when we run our graph we should receive no errors and instead get the last value of the state before the recursion limit was hit."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -190,24 +190,18 @@
|
||||
"from langgraph.graph import StateGraph\n",
|
||||
"from typing import Annotated\n",
|
||||
"\n",
|
||||
"from langgraph.managed.base import ManagedValue\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class IsLastOrSecondToLastStepManager(ManagedValue[bool]):\n",
|
||||
" def __call__(self, step: int) -> bool:\n",
|
||||
" limit = self.config.get(\"recursion_limit\", 0)\n",
|
||||
" return step >= limit - 2\n",
|
||||
"from langgraph.managed.is_last_step import RemainingSteps\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class State(TypedDict):\n",
|
||||
" value: str\n",
|
||||
" action_result: str\n",
|
||||
" is_last_step: Annotated[bool, IsLastOrSecondToLastStepManager]\n",
|
||||
" remaining_steps: RemainingSteps\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def router(state: State):\n",
|
||||
" # Force the agent to end if it is on the last step\n",
|
||||
" if state[\"is_last_step\"]:\n",
|
||||
" # Force the agent to end\n",
|
||||
" if state[\"remaining_steps\"] <= 2:\n",
|
||||
" return END\n",
|
||||
" if state[\"value\"] == \"end\":\n",
|
||||
" return END\n",
|
||||
@@ -235,7 +229,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -244,7 +238,7 @@
|
||||
"{'value': 'keep going!', 'action_result': 'what a great result!'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -277,7 +271,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
" <p>\n",
|
||||
" In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the <code>create_react_agent(model, tools=tool)</code> (<a href=\"https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent\">API doc</a>) constructor. This may be more appropriate if you are used to LangChain’s <a href=\"https://python.langchain.com/docs/how_to/agent_executor/#concepts\">AgentExecutor</a> class.\n",
|
||||
" </p>\n",
|
||||
"</div> \n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
"<div class=\"admonition warning\">\n",
|
||||
" <p class=\"admonition-title\">Note on Python < 3.11</p>\n",
|
||||
@@ -24,7 +24,7 @@
|
||||
" When using python 3.8, 3.9, or 3.10, please ensure you manually pass the RunnableConfig through to the llm when invoking it like so: <code>llm.ainvoke(..., config)</code>.\n",
|
||||
" The stream method collects all events from your nested code using a streaming tracer passed as a callback. In 3.11 and above, this is automatically handled via <a href=\"https://docs.python.org/3/library/contextvars.html\">contextvar</a>'s; prior to 3.11, <a href=\"https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task\">asyncio's tasks</a> lacked proper contextvar support, meaning that the callbacks will only propagate if you manually pass the config through. We do this in the <code>call_model</code> method below.\n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -85,7 +85,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -264,7 +264,7 @@
|
||||
" <p class=\"admonition-title\">Manual Callback Propagation</p>\n",
|
||||
" <p>\n",
|
||||
" Note that in <code>call_model(state: State, config: RunnableConfig):</code> below, we a) accept the <a href=\"https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.config.RunnableConfig.html#langchain_core.runnables.config.RunnableConfig\">RunnableConfig</a> in the node and b) pass this in as the second arg for <code>llm.ainvoke(..., config)</code>. This is optional for python 3.11 and later.</p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -171,7 +171,7 @@
|
||||
" <p>\n",
|
||||
" We're wrapping the <code>grandchild_graph</code> invocation in a separate function (<code>call_grandchild_graph</code>) that transforms the input state before calling the grandchild graph and then transforms the output of grandchild graph back to child graph state. If you just pass <code>grandchild_graph</code> directly to <code>.add_node</code> without the transformations, LangGraph will raise an error as there are no shared state channels (keys) between child and grandchild states.\n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -240,7 +240,7 @@
|
||||
" <p>\n",
|
||||
" We're wrapping the <code>child_graph</code> invocation in a separate function (<code>call_child_graph</code>) that transforms the input state before calling the child graph and then transforms the output of the child graph back to parent graph state. If you just pass <code>child_graph</code> directly to <code>.add_node</code> without the transformations, LangGraph will raise an error as there are no shared state channels (keys) between parent and child states.\n",
|
||||
" </p>\n",
|
||||
"</div> \n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
"Let's run the parent graph and make sure it correctly calls both the child and grandchild subgraphs:"
|
||||
]
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -85,7 +85,7 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "71638c2a-5038-439e-907a-de2bb548db34",
|
||||
"metadata": {},
|
||||
"metadata": {"hide_from_vcr": true},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
@@ -176,7 +176,7 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "654e2f81",
|
||||
"metadata": {},
|
||||
"metadata": {"hide_from_vcr": true},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
@@ -2340,10 +2340,13 @@
|
||||
" # We have an interrupt! The agent is trying to use a tool, and the user can approve or deny it\n",
|
||||
" # Note: This code is all outside of your graph. Typically, you would stream the output to a UI.\n",
|
||||
" # Then, you would have the frontend trigger a new run via an API call when the user has provided input.\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" try:\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" except:\n",
|
||||
" user_input = \"y\"\n",
|
||||
" if user_input.strip() == \"y\":\n",
|
||||
" # Just continue\n",
|
||||
" result = part_2_graph.invoke(\n",
|
||||
@@ -2958,10 +2961,13 @@
|
||||
" # We have an interrupt! The agent is trying to use a tool, and the user can approve or deny it\n",
|
||||
" # Note: This code is all outside of your graph. Typically, you would stream the output to a UI.\n",
|
||||
" # Then, you would have the frontend trigger a new run via an API call when the user has provided input.\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" try:\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" except:\n",
|
||||
" user_input = \"y\"\n",
|
||||
" if user_input.strip() == \"y\":\n",
|
||||
" # Just continue\n",
|
||||
" result = part_3_graph.invoke(\n",
|
||||
@@ -4373,10 +4379,13 @@
|
||||
" # We have an interrupt! The agent is trying to use a tool, and the user can approve or deny it\n",
|
||||
" # Note: This code is all outside of your graph. Typically, you would stream the output to a UI.\n",
|
||||
" # Then, you would have the frontend trigger a new run via an API call when the user has provided input.\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" try:\n",
|
||||
" user_input = input(\n",
|
||||
" \"Do you approve of the above actions? Type 'y' to continue;\"\n",
|
||||
" \" otherwise, explain your requested changed.\\n\\n\"\n",
|
||||
" )\n",
|
||||
" except:\n",
|
||||
" user_input = \"y\"\n",
|
||||
" if user_input.strip() == \"y\":\n",
|
||||
" # Just continue\n",
|
||||
" result = part_4_graph.invoke(\n",
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"To simplify the code in each agent node, we will use the AgentExecutor class from LangChain. This and other \"advanced agent\" notebooks are designed to show how you can implement certain design patterns in LangGraph. If the pattern suits your needs, we recommend combining it with some of the other fundamental patterns described elsewhere in the docs for best performance.\n",
|
||||
"To simplify the code in each agent node, we will use LangGraph's prebuilt [create_react_agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent). This and other \"advanced agent\" notebooks are designed to show how you can implement certain design patterns in LangGraph. If the pattern suits your needs, we recommend combining it with some of the other fundamental patterns described elsewhere in the docs for best performance.\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
@@ -68,7 +68,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+7
-13
@@ -77,15 +77,6 @@ plugins:
|
||||
options:
|
||||
filters:
|
||||
- "!^_"
|
||||
- mkdocs-jupyter:
|
||||
ignore_h1_titles: true
|
||||
execute: false
|
||||
include_source: True
|
||||
include_requirejs: true
|
||||
- exclude:
|
||||
glob:
|
||||
- tutorials/llm-compiler/math_tools.py
|
||||
- tutorials/llm-compiler/output_parser.py
|
||||
nav:
|
||||
- "index.md"
|
||||
- Tutorials:
|
||||
@@ -326,9 +317,8 @@ markdown_extensions:
|
||||
- markdown_include.include:
|
||||
base_path: ./
|
||||
- github-callouts
|
||||
extra_css:
|
||||
- css/mkdocstrings.css
|
||||
|
||||
hooks:
|
||||
- _scripts/notebook_hooks.py
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/js
|
||||
@@ -357,4 +347,8 @@ validation:
|
||||
omitted_files: warn
|
||||
absolute_links: warn
|
||||
unrecognized_links: warn
|
||||
anchors: warn
|
||||
# TODO: figure out how to enable 'warn' for this
|
||||
# it's only an issue for tutorials/storm/storm.ipynb
|
||||
# because it creates anchors in the generated report
|
||||
# and those anchors are not available in the actual doc
|
||||
anchors: info
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8a63cfe9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/tutorials/tool-calling-agent-local.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Generated
+79
-68
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
@@ -580,103 +580,114 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg"
|
||||
version = "3.2.1"
|
||||
version = "3.2.3"
|
||||
description = "PostgreSQL database adapter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175"},
|
||||
{file = "psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7"},
|
||||
{file = "psycopg-3.2.3-py3-none-any.whl", hash = "sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907"},
|
||||
{file = "psycopg-3.2.3.tar.gz", hash = "sha256:a5764f67c27bec8bfac85764d23c534af2c27b893550377e37ce59c12aac47a2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
psycopg-binary = {version = "3.2.1", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""}
|
||||
typing-extensions = ">=4.4"
|
||||
psycopg-binary = {version = "3.2.3", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""}
|
||||
typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""}
|
||||
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
binary = ["psycopg-binary (==3.2.1)"]
|
||||
c = ["psycopg-c (==3.2.1)"]
|
||||
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.6)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
|
||||
binary = ["psycopg-binary (==3.2.3)"]
|
||||
c = ["psycopg-c (==3.2.3)"]
|
||||
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.11)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
|
||||
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
|
||||
pool = ["psycopg-pool"]
|
||||
test = ["anyio (>=4.0)", "mypy (>=1.6)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
||||
test = ["anyio (>=4.0)", "mypy (>=1.11)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-binary"
|
||||
version = "3.2.1"
|
||||
version = "3.2.3"
|
||||
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:cad2de17804c4cfee8640ae2b279d616bb9e4734ac3c17c13db5e40982bd710d"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:592b27d6c46a40f9eeaaeea7c1fef6f3c60b02c634365eb649b2d880669f149f"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a997efbaadb5e1a294fb5760e2f5643d7b8e4e3fe6cb6f09e6d605fd28e0291"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1d2b6438fb83376f43ebb798bf0ad5e57bc56c03c9c29c85bc15405c8c0ac5a"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1f087bd84bdcac78bf9f024ebdbfacd07fc0a23ec8191448a50679e2ac4a19e"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:415c3b72ea32119163255c6504085f374e47ae7345f14bc3f0ef1f6e0976a879"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f092114f10f81fb6bae544a0ec027eb720e2d9c74a4fcdaa9dd3899873136935"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06a7aae34edfe179ddc04da005e083ff6c6b0020000399a2cbf0a7121a8a22ea"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0b018631e5c80ce9bc210b71ea885932f9cca6db131e4df505653d7e3873a938"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8a509aeaac364fa965454e80cd110fe6d48ba2c80f56c9b8563423f0b5c3cfd"},
|
||||
{file = "psycopg_binary-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:413977d18412ff83486eeb5875eb00b185a9391c57febac45b8993bf9c0ff489"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:62b1b7b07e00ee490afb39c0a47d8282a9c2822c7cfed9553a04b0058adf7e7f"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f8afb07114ea9b924a4a0305ceb15354ccf0ef3c0e14d54b8dbeb03e50182dd7"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40bb515d042f6a345714ec0403df68ccf13f73b05e567837d80c886c7c9d3805"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6418712ba63cebb0c88c050b3997185b0ef54173b36568522d5634ac06153040"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:101472468d59c74bb8565fab603e032803fd533d16be4b2d13da1bab8deb32a3"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa3931f308ab4a479d0ee22dc04bea867a6365cac0172e5ddcba359da043854b"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc314a47d44fe1a8069b075a64abffad347a3a1d8652fed1bab5d3baea37acb2"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cc304a46be1e291031148d9d95c12451ffe783ff0cc72f18e2cc7ec43cdb8c68"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f9e13600647087df5928875559f0eb8f496f53e6278b7da9511b4b3d0aff960"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b140182830c76c74d17eba27df3755a46442ce8d4fb299e7f1cf2f74a87c877b"},
|
||||
{file = "psycopg_binary-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:3c838806eeb99af39f934b7999e35f947a8e577997cc892c12b5053a97a9057f"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7066d3dca196ed0dc6172f9777b2d62e4f138705886be656cccff2d555234d60"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:28ada5f610468c57d8a4a055a8ea915d0085a43d794266c4f3b9d02f4288f4db"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e8213bf50af073b1aa8dc3cff123bfeedac86332a16c1b7274910bc88a847c7"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74d623261655a169bc84a9669890975c229f2fa6e19a7f2d10a77675dcf1a707"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42781ba94e8842ee98bca5a7d0c44cc9d067500fedca2d6a90fa3609b6d16b42"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e6669091d09f8ba36e10ce678a6d9916e110446236a9b92346464a3565635e"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b09e8a576a2ac69d695032ee76f31e03b30781828b5dd6d18c6a009e5a3d1c35"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8f28ff0cb9f1defdc4a6f8c958bf6787274247e7dfeca811f6e2f56602695fb1"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4c84fcac8a3a3479ac14673095cc4e1fdba2935499f72c436785ac679bec0d1a"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:950fd666ec9e9fe6a8eeb2b5a8f17301790e518953730ad44d715b59ffdbc67f"},
|
||||
{file = "psycopg_binary-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:334046a937bb086c36e2c6889fe327f9f29bfc085d678f70fac0b0618949f674"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:1d6833f607f3fc7b22226a9e121235d3b84c0eda1d3caab174673ef698f63788"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d353e028b8f848b9784450fc2abf149d53a738d451eab3ee4c85703438128b9"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f34e369891f77d0738e5d25727c307d06d5344948771e5379ea29c76c6d84555"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ab58213cc976a1666f66bc1cb2e602315cd753b7981a8e17237ac2a185bd4a1"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0104a72a17aa84b3b7dcab6c84826c595355bf54bb6ea6d284dcb06d99c6801"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:059cbd4e6da2337e17707178fe49464ed01de867dc86c677b30751755ec1dc51"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:73f9c9b984be9c322b5ec1515b12df1ee5896029f5e72d46160eb6517438659c"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:af0469c00f24c4bec18c3d2ede124bf62688d88d1b8a5f3c3edc2f61046fe0d7"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:463d55345f73ff391df8177a185ad57b552915ad33f5cc2b31b930500c068b22"},
|
||||
{file = "psycopg_binary-3.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:302b86f92c0d76e99fe1b5c22c492ae519ce8b98b88d37ef74fda4c9e24c6b46"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0879b5d76b7d48678d31278242aaf951bc2d69ca4e4d7cef117e4bbf7bfefda9"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f99e59f8a5f4dcd9cbdec445f3d8ac950a492fc0e211032384d6992ed3c17eb7"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84837e99353d16c6980603b362d0f03302d4b06c71672a6651f38df8a482923d"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ce965caf618061817f66c0906f0452aef966c293ae0933d4fa5a16ea6eaf5bb"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78c2007caf3c90f08685c5378e3ceb142bafd5636be7495f7d86ec8a977eaeef"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7a84b5eb194a258116154b2a4ff2962ea60ea52de089508db23a51d3d6b1c7d1"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4a42b8f9ab39affcd5249b45cac763ac3cf12df962b67e23fd15a2ee2932afe5"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:788ffc43d7517c13e624c83e0e553b7b8823c9655e18296566d36a829bfb373f"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:21927f41c4d722ae8eb30d62a6ce732c398eac230509af5ba1749a337f8a63e2"},
|
||||
{file = "psycopg_binary-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:921f0c7f39590763d64a619de84d1b142587acc70fd11cbb5ba8fa39786f3073"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:965455eac8547f32b3181d5ec9ad8b9be500c10fe06193543efaaebe3e4ce70c"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:71adcc8bc80a65b776510bc39992edf942ace35b153ed7a9c6c573a6849ce308"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f73adc05452fb85e7a12ed3f69c81540a8875960739082e6ea5e28c373a30774"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8630943143c6d6ca9aefc88bbe5e76c90553f4e1a3b2dc339e67dc34aa86f7e"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bffb61e198a91f712cc3d7f2d176a697cb05b284b2ad150fb8edb308eba9002"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4fa2240c9fceddaa815a58f29212826fafe43ce80ff666d38c4a03fb036955"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:192a5f8496e6e1243fdd9ac20e117e667c0712f148c5f9343483b84435854c78"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64dc6e9ec64f592f19dc01a784e87267a64a743d34f68488924251253da3c818"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:79498df398970abcee3d326edd1d4655de7d77aa9aecd578154f8af35ce7bbd2"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:949551752930d5e478817e0b49956350d866b26578ced0042a61967e3fcccdea"},
|
||||
{file = "psycopg_binary-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:80a2337e2dfb26950894c8301358961430a0304f7bfe729d34cc036474e9c9b1"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:6d8f2144e0d5808c2e2aed40fbebe13869cd00c2ae745aca4b3b16a435edb056"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94253be2b57ef2fea7ffe08996067aabf56a1eb9648342c9e3bad9e10c46e045"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fda0162b0dbfa5eaed6cdc708179fa27e148cb8490c7d62e5cf30713909658ea"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c0419cdad8c70eaeb3116bb28e7b42d546f91baf5179d7556f230d40942dc78"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74fbf5dd3ef09beafd3557631e282f00f8af4e7a78fbfce8ab06d9cd5a789aae"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d784f614e4d53050cbe8abf2ae9d1aaacf8ed31ce57b42ce3bf2a48a66c3a5c"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4e76ce2475ed4885fe13b8254058be710ec0de74ebd8ef8224cf44a9a3358e5f"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5938b257b04c851c2d1e6cb2f8c18318f06017f35be9a5fe761ee1e2e344dfb7"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:257c4aea6f70a9aef39b2a77d0658a41bf05c243e2bf41895eb02220ac6306f3"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:06b5cc915e57621eebf2393f4173793ed7e3387295f07fed93ed3fb6a6ccf585"},
|
||||
{file = "psycopg_binary-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:09baa041856b35598d335b1a74e19a49da8500acedf78164600694c0ba8ce21b"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:48f8ca6ee8939bab760225b2ab82934d54330eec10afe4394a92d3f2a0c37dd6"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5361ea13c241d4f0ec3f95e0bf976c15e2e451e9cc7ef2e5ccfc9d170b197a40"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb987f14af7da7c24f803111dbc7392f5070fd350146af3345103f76ea82e339"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0463a11b1cace5a6aeffaf167920707b912b8986a9c7920341c75e3686277920"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b7be9a6c06518967b641fb15032b1ed682fd3b0443f64078899c61034a0bca6"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64a607e630d9f4b2797f641884e52b9f8e239d35943f51bef817a384ec1678fe"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fa33ead69ed133210d96af0c63448b1385df48b9c0247eda735c5896b9e6dbbf"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1f8b0d0e99d8e19923e6e07379fa00570be5182c201a8c0b5aaa9a4d4a4ea20b"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:709447bd7203b0b2debab1acec23123eb80b386f6c29e7604a5d4326a11e5bd6"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5e37d5027e297a627da3551a1e962316d0f88ee4ada74c768f6c9234e26346d9"},
|
||||
{file = "psycopg_binary-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:261f0031ee6074765096a19b27ed0f75498a8338c3dcd7f4f0d831e38adf12d1"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:41fdec0182efac66b27478ac15ef54c9ebcecf0e26ed467eb7d6f262a913318b"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:07d019a786eb020c0f984691aa1b994cb79430061065a694cf6f94056c603d26"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c57615791a337378fe5381143259a6c432cdcbb1d3e6428bfb7ce59fff3fb5c"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8eb9a4e394926b93ad919cad1b0a918e9b4c846609e8c1cfb6b743683f64da0"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5905729668ef1418bd36fbe876322dcb0f90b46811bba96d505af89e6fbdce2f"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd65774ed7d65101b314808b6893e1a75b7664f680c3ef18d2e5c84d570fa393"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:700679c02f9348a0d0a2adcd33a0275717cd0d0aee9d4482b47d935023629505"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96334bb64d054e36fed346c50c4190bad9d7c586376204f50bede21a913bf942"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9099e443d4cc24ac6872e6a05f93205ba1a231b1a8917317b07c9ef2b955f1f4"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1985ab05e9abebfbdf3163a16ebb37fbc5d49aff2bf5b3d7375ff0920bbb54cd"},
|
||||
{file = "psycopg_binary-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:e90352d7b610b4693fad0feea48549d4315d10f1eba5605421c92bb834e90170"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:69320f05de8cdf4077ecd7fefdec223890eea232af0d58f2530cbda2871244a0"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4926ea5c46da30bec4a85907aa3f7e4ea6313145b2aa9469fdb861798daf1502"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c64c4cd0d50d5b2288ab1bcb26c7126c772bbdebdfadcd77225a77df01c4a57e"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05a1bdce30356e70a05428928717765f4a9229999421013f41338d9680d03a63"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad357e426b0ea5c3043b8ec905546fa44b734bf11d33b3da3959f6e4447d350"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:967b47a0fd237aa17c2748fdb7425015c394a6fb57cdad1562e46a6eb070f96d"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:71db8896b942770ed7ab4efa59b22eee5203be2dfdee3c5258d60e57605d688c"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2773f850a778575dd7158a6dd072f7925b67f3ba305e2003538e8831fec77a1d"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aeddf7b3b3f6e24ccf7d0edfe2d94094ea76b40e831c16eff5230e040ce3b76b"},
|
||||
{file = "psycopg_binary-3.2.3-cp38-cp38-win_amd64.whl", hash = "sha256:824c867a38521d61d62b60aca7db7ca013a2b479e428a0db47d25d8ca5067410"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:9994f7db390c17fc2bd4c09dca722fd792ff8a49bb3bdace0c50a83f22f1767d"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1303bf8347d6be7ad26d1362af2c38b3a90b8293e8d56244296488ee8591058e"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:842da42a63ecb32612bb7f5b9e9f8617eab9bc23bd58679a441f4150fcc51c96"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bb342a01c76f38a12432848e6013c57eb630103e7556cf79b705b53814c3949"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd40af959173ea0d087b6b232b855cfeaa6738f47cb2a0fd10a7f4fa8b74293f"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9b60b465773a52c7d4705b0a751f7f1cdccf81dd12aee3b921b31a6e76b07b0e"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fc6d87a1c44df8d493ef44988a3ded751e284e02cdf785f746c2d357e99782a6"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f0b018e37608c3bfc6039a1dc4eb461e89334465a19916be0153c757a78ea426"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a29f5294b0b6360bfda69653697eff70aaf2908f58d1073b0acd6f6ab5b5a4f"},
|
||||
{file = "psycopg_binary-3.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:e56b1fd529e5dde2d1452a7d72907b37ed1b4f07fdced5d8fb1e963acfff6749"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-pool"
|
||||
version = "3.2.2"
|
||||
version = "3.2.3"
|
||||
description = "Connection Pool for Psycopg"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "psycopg_pool-3.2.2-py3-none-any.whl", hash = "sha256:273081d0fbfaced4f35e69200c89cb8fbddfe277c38cc86c235b90a2ec2c8153"},
|
||||
{file = "psycopg_pool-3.2.2.tar.gz", hash = "sha256:9e22c370045f6d7f2666a5ad1b0caf345f9f1912195b0b25d0d3bcc4f3a7389c"},
|
||||
{file = "psycopg_pool-3.2.3-py3-none-any.whl", hash = "sha256:53bd8e640625e01b2927b2ad96df8ed8e8f91caea4597d45e7673fc7bbb85eb1"},
|
||||
{file = "psycopg_pool-3.2.3.tar.gz", hash = "sha256:bb942f123bef4b7fbe4d55421bd3fb01829903c95c0f33fd42b7e94e5ac9b52a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.4"
|
||||
typing-extensions = ">=4.6"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
|
||||
@@ -206,7 +206,9 @@ class JsonPlusSerializer(SerializerProtocol):
|
||||
elif type_ == "json":
|
||||
return self.loads(data_)
|
||||
elif type_ == "msgpack":
|
||||
return msgpack.unpackb(data_, ext_hook=_msgpack_ext_hook)
|
||||
return msgpack.unpackb(
|
||||
data_, ext_hook=_msgpack_ext_hook, strict_map_key=False
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError(f"Unknown serialization type: {type_}")
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
from langgraph.managed.is_last_step import IsLastStep
|
||||
from langgraph.managed.is_last_step import IsLastStep, RemainingSteps
|
||||
|
||||
__all__ = ["IsLastStep"]
|
||||
__all__ = ["IsLastStep", "RemainingSteps"]
|
||||
|
||||
@@ -13,22 +13,23 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from typing_extensions import Self, TypeGuard
|
||||
|
||||
from langgraph.types import LoopProtocol
|
||||
|
||||
V = TypeVar("V")
|
||||
U = TypeVar("U")
|
||||
|
||||
|
||||
class ManagedValue(ABC, Generic[V]):
|
||||
def __init__(self, config: RunnableConfig) -> None:
|
||||
self.config = config
|
||||
def __init__(self, loop: LoopProtocol) -> None:
|
||||
self.loop = loop
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def enter(cls, config: RunnableConfig, **kwargs: Any) -> Iterator[Self]:
|
||||
def enter(cls, loop: LoopProtocol, **kwargs: Any) -> Iterator[Self]:
|
||||
try:
|
||||
value = cls(config, **kwargs)
|
||||
value = cls(loop, **kwargs)
|
||||
yield value
|
||||
finally:
|
||||
# because managed value and Pregel have reference to each other
|
||||
@@ -40,9 +41,9 @@ class ManagedValue(ABC, Generic[V]):
|
||||
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def aenter(cls, config: RunnableConfig, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
async def aenter(cls, loop: LoopProtocol, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
try:
|
||||
value = cls(config, **kwargs)
|
||||
value = cls(loop, **kwargs)
|
||||
yield value
|
||||
finally:
|
||||
# because managed value and Pregel have reference to each other
|
||||
@@ -53,7 +54,7 @@ class ManagedValue(ABC, Generic[V]):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self, step: int) -> V: ...
|
||||
def __call__(self) -> V: ...
|
||||
|
||||
|
||||
class WritableManagedValue(Generic[V, U], ManagedValue[V], ABC):
|
||||
|
||||
@@ -13,10 +13,10 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from typing_extensions import Self
|
||||
|
||||
from langgraph.managed.base import ConfiguredManagedValue, ManagedValue, V
|
||||
from langgraph.types import LoopProtocol
|
||||
|
||||
|
||||
class Context(ManagedValue[V], Generic[V]):
|
||||
@@ -46,14 +46,14 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def enter(cls, config: RunnableConfig, **kwargs: Any) -> Iterator[Self]:
|
||||
with super().enter(config, **kwargs) as self:
|
||||
def enter(cls, loop: LoopProtocol, **kwargs: Any) -> Iterator[Self]:
|
||||
with super().enter(loop, **kwargs) as self:
|
||||
if self.ctx is None:
|
||||
raise ValueError(
|
||||
"Synchronous context manager not found. Please initialize Context value with a sync context manager, or invoke your graph asynchronously."
|
||||
)
|
||||
ctx = (
|
||||
self.ctx(config) # type: ignore[call-arg]
|
||||
self.ctx(loop.config) # type: ignore[call-arg]
|
||||
if signature(self.ctx).parameters.get("config")
|
||||
else self.ctx()
|
||||
)
|
||||
@@ -63,17 +63,17 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def aenter(cls, config: RunnableConfig, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
async with super().aenter(config, **kwargs) as self:
|
||||
async def aenter(cls, loop: LoopProtocol, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
async with super().aenter(loop, **kwargs) as self:
|
||||
if self.actx is not None:
|
||||
ctx = (
|
||||
self.actx(config) # type: ignore[call-arg]
|
||||
self.actx(loop.config) # type: ignore[call-arg]
|
||||
if signature(self.actx).parameters.get("config")
|
||||
else self.actx()
|
||||
)
|
||||
elif self.ctx is not None:
|
||||
ctx = (
|
||||
self.ctx(config) # type: ignore
|
||||
self.ctx(loop.config) # type: ignore
|
||||
if signature(self.ctx).parameters.get("config")
|
||||
else self.ctx()
|
||||
)
|
||||
@@ -96,7 +96,7 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
loop: LoopProtocol,
|
||||
*,
|
||||
ctx: Union[None, Type[ContextManager[V]], Type[AsyncContextManager[V]]] = None,
|
||||
actx: Optional[Type[AsyncContextManager[V]]] = None,
|
||||
@@ -104,5 +104,5 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
self.ctx = ctx
|
||||
self.actx = actx
|
||||
|
||||
def __call__(self, step: int) -> V:
|
||||
def __call__(self) -> V:
|
||||
return self.value
|
||||
|
||||
@@ -4,8 +4,16 @@ from langgraph.managed.base import ManagedValue
|
||||
|
||||
|
||||
class IsLastStepManager(ManagedValue[bool]):
|
||||
def __call__(self, step: int) -> bool:
|
||||
return step == self.config.get("recursion_limit", 0) - 1
|
||||
def __call__(self) -> bool:
|
||||
return self.loop.step == self.loop.stop - 1
|
||||
|
||||
|
||||
IsLastStep = Annotated[bool, IsLastStepManager]
|
||||
|
||||
|
||||
class RemainingStepsManager(ManagedValue[int]):
|
||||
def __call__(self) -> int:
|
||||
return self.loop.stop - self.loop.step
|
||||
|
||||
|
||||
RemainingSteps = Annotated[int, RemainingStepsManager]
|
||||
|
||||
@@ -7,13 +7,11 @@ from typing import (
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
cast,
|
||||
)
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from typing_extensions import NotRequired, Required, Self
|
||||
|
||||
from langgraph.constants import CONF, CONFIG_KEY_STORE
|
||||
from langgraph.constants import CONF
|
||||
from langgraph.errors import InvalidUpdateError
|
||||
from langgraph.managed.base import (
|
||||
ChannelKeyPlaceholder,
|
||||
@@ -21,7 +19,8 @@ from langgraph.managed.base import (
|
||||
ConfiguredManagedValue,
|
||||
WritableManagedValue,
|
||||
)
|
||||
from langgraph.store.base import BaseStore, PutOp
|
||||
from langgraph.store.base import PutOp
|
||||
from langgraph.types import LoopProtocol
|
||||
|
||||
V = dict[str, Any]
|
||||
|
||||
@@ -55,25 +54,26 @@ class SharedValue(WritableManagedValue[Value, Update]):
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def enter(cls, config: RunnableConfig, **kwargs: Any) -> Iterator[Self]:
|
||||
with super().enter(config, **kwargs) as value:
|
||||
if value.store is not None:
|
||||
saved = value.store.search(value.ns)
|
||||
def enter(cls, loop: LoopProtocol, **kwargs: Any) -> Iterator[Self]:
|
||||
with super().enter(loop, **kwargs) as value:
|
||||
if loop.store is not None:
|
||||
saved = loop.store.search(value.ns)
|
||||
value.value = {it.key: it.value for it in saved}
|
||||
yield value
|
||||
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def aenter(cls, config: RunnableConfig, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
async with super().aenter(config, **kwargs) as value:
|
||||
if value.store is not None:
|
||||
saved = await value.store.asearch(value.ns)
|
||||
async def aenter(cls, loop: LoopProtocol, **kwargs: Any) -> AsyncIterator[Self]:
|
||||
async with super().aenter(loop, **kwargs) as value:
|
||||
if loop.store is not None:
|
||||
saved = await loop.store.asearch(value.ns)
|
||||
value.value = {it.key: it.value for it in saved}
|
||||
yield value
|
||||
|
||||
def __init__(
|
||||
self, config: RunnableConfig, *, typ: Type[Any], scope: str, key: str
|
||||
self, loop: LoopProtocol, *, typ: Type[Any], scope: str, key: str
|
||||
) -> None:
|
||||
super().__init__(loop)
|
||||
if typ := _strip_extras(typ):
|
||||
if typ not in (
|
||||
dict,
|
||||
@@ -83,18 +83,17 @@ class SharedValue(WritableManagedValue[Value, Update]):
|
||||
raise ValueError("SharedValue must be a dict")
|
||||
self.scope = scope
|
||||
self.value: Value = {}
|
||||
self.store = cast(BaseStore, config[CONF].get(CONFIG_KEY_STORE))
|
||||
if self.store is None:
|
||||
if self.loop.store is None:
|
||||
pass
|
||||
elif scope_value := config[CONF].get(self.scope):
|
||||
elif scope_value := self.loop.config[CONF].get(self.scope):
|
||||
self.ns = ("scoped", scope, key, scope_value)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Scope {scope} for shared state key not in config.configurable"
|
||||
)
|
||||
|
||||
def __call__(self, step: int) -> Value:
|
||||
return self.value.copy()
|
||||
def __call__(self) -> Value:
|
||||
return self.value
|
||||
|
||||
def _process_update(self, values: Sequence[Update]) -> list[PutOp]:
|
||||
writes: list[PutOp] = []
|
||||
@@ -112,13 +111,13 @@ class SharedValue(WritableManagedValue[Value, Update]):
|
||||
return writes
|
||||
|
||||
def update(self, values: Sequence[Update]) -> None:
|
||||
if self.store is None:
|
||||
if self.loop.store is None:
|
||||
self._process_update(values)
|
||||
else:
|
||||
return self.store.batch(self._process_update(values))
|
||||
return self.loop.store.batch(self._process_update(values))
|
||||
|
||||
async def aupdate(self, writes: Sequence[Update]) -> None:
|
||||
if self.store is None:
|
||||
if self.loop.store is None:
|
||||
self._process_update(writes)
|
||||
else:
|
||||
return await self.store.abatch(self._process_update(writes))
|
||||
return await self.loop.store.abatch(self._process_update(writes))
|
||||
|
||||
@@ -14,7 +14,7 @@ from langgraph._api.deprecation import deprecated_parameter
|
||||
from langgraph.graph import StateGraph
|
||||
from langgraph.graph.graph import CompiledGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.managed import IsLastStep
|
||||
from langgraph.managed import IsLastStep, RemainingSteps
|
||||
from langgraph.prebuilt.tool_executor import ToolExecutor
|
||||
from langgraph.prebuilt.tool_node import ToolNode
|
||||
from langgraph.store.base import BaseStore
|
||||
@@ -33,6 +33,8 @@ class AgentState(TypedDict):
|
||||
|
||||
is_last_step: IsLastStep
|
||||
|
||||
remaining_steps: RemainingSteps
|
||||
|
||||
|
||||
StateSchema = TypeVar("StateSchema", bound=AgentState)
|
||||
StateSchemaType = Type[StateSchema]
|
||||
@@ -529,10 +531,28 @@ def create_react_agent(
|
||||
# Define the function that calls the model
|
||||
def call_model(state: AgentState, config: RunnableConfig) -> AgentState:
|
||||
response = model_runnable.invoke(state, config)
|
||||
has_tool_calls = isinstance(response, AIMessage) and response.tool_calls
|
||||
all_tools_return_direct = (
|
||||
all(call["name"] in should_return_direct for call in response.tool_calls)
|
||||
if isinstance(response, AIMessage)
|
||||
else False
|
||||
)
|
||||
if (
|
||||
state["is_last_step"]
|
||||
and isinstance(response, AIMessage)
|
||||
and response.tool_calls
|
||||
(
|
||||
"remaining_steps" not in state
|
||||
and state["is_last_step"]
|
||||
and has_tool_calls
|
||||
)
|
||||
or (
|
||||
"remaining_steps" in state
|
||||
and state["remaining_steps"] < 1
|
||||
and all_tools_return_direct
|
||||
)
|
||||
or (
|
||||
"remaining_steps" in state
|
||||
and state["remaining_steps"] < 2
|
||||
and has_tool_calls
|
||||
)
|
||||
):
|
||||
return {
|
||||
"messages": [
|
||||
@@ -547,10 +567,28 @@ def create_react_agent(
|
||||
|
||||
async def acall_model(state: AgentState, config: RunnableConfig) -> AgentState:
|
||||
response = await model_runnable.ainvoke(state, config)
|
||||
has_tool_calls = isinstance(response, AIMessage) and response.tool_calls
|
||||
all_tools_return_direct = (
|
||||
all(call["name"] in should_return_direct for call in response.tool_calls)
|
||||
if isinstance(response, AIMessage)
|
||||
else False
|
||||
)
|
||||
if (
|
||||
state["is_last_step"]
|
||||
and isinstance(response, AIMessage)
|
||||
and response.tool_calls
|
||||
(
|
||||
"remaining_steps" not in state
|
||||
and state["is_last_step"]
|
||||
and has_tool_calls
|
||||
)
|
||||
or (
|
||||
"remaining_steps" in state
|
||||
and state["remaining_steps"] < 1
|
||||
and all_tools_return_direct
|
||||
)
|
||||
or (
|
||||
"remaining_steps" in state
|
||||
and state["remaining_steps"] < 2
|
||||
and has_tool_calls
|
||||
)
|
||||
):
|
||||
return {
|
||||
"messages": [
|
||||
|
||||
@@ -88,7 +88,7 @@ from langgraph.pregel.utils import find_subgraph_pregel, get_new_channel_version
|
||||
from langgraph.pregel.validate import validate_graph, validate_keys
|
||||
from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry
|
||||
from langgraph.store.base import BaseStore
|
||||
from langgraph.types import All, Checkpointer, StateSnapshot, StreamMode
|
||||
from langgraph.types import All, Checkpointer, LoopProtocol, StateSnapshot, StreamMode
|
||||
from langgraph.utils.config import (
|
||||
ensure_config,
|
||||
merge_configs,
|
||||
@@ -252,8 +252,8 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
if auto_validate:
|
||||
self.validate()
|
||||
|
||||
def copy(self, update: dict[str, Any]) -> Self:
|
||||
attrs = {**self.__dict__, **update}
|
||||
def copy(self, update: dict[str, Any] | None = None) -> Self:
|
||||
attrs = {**self.__dict__, **(update or {})}
|
||||
return self.__class__(**attrs)
|
||||
|
||||
def with_config(self, config: RunnableConfig | None = None, **kwargs: Any) -> Self:
|
||||
@@ -433,7 +433,14 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
)
|
||||
|
||||
with ChannelsManager(
|
||||
self.channels, saved.checkpoint, saved.config, skip_context=True
|
||||
self.channels,
|
||||
saved.checkpoint,
|
||||
LoopProtocol(
|
||||
config=saved.config,
|
||||
step=saved.metadata.get("step", -1) + 1,
|
||||
stop=saved.metadata.get("step", -1) + 2,
|
||||
),
|
||||
skip_context=True,
|
||||
) as (channels, managed):
|
||||
# tasks for this checkpoint
|
||||
next_tasks = prepare_next_tasks(
|
||||
@@ -484,8 +491,13 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
patch_checkpoint_map(saved.config, saved.metadata),
|
||||
saved.metadata,
|
||||
saved.checkpoint["ts"],
|
||||
saved.parent_config,
|
||||
tasks_w_writes(next_tasks.values(), saved.pending_writes, task_states),
|
||||
patch_checkpoint_map(saved.parent_config, saved.metadata),
|
||||
tasks_w_writes(
|
||||
next_tasks.values(),
|
||||
saved.pending_writes,
|
||||
task_states,
|
||||
self.stream_channels_asis,
|
||||
),
|
||||
)
|
||||
|
||||
async def _aprepare_state_snapshot(
|
||||
@@ -506,7 +518,14 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
)
|
||||
|
||||
async with AsyncChannelsManager(
|
||||
self.channels, saved.checkpoint, saved.config, skip_context=True
|
||||
self.channels,
|
||||
saved.checkpoint,
|
||||
LoopProtocol(
|
||||
config=saved.config,
|
||||
step=saved.metadata.get("step", -1) + 1,
|
||||
stop=saved.metadata.get("step", -1) + 2,
|
||||
),
|
||||
skip_context=True,
|
||||
) as (
|
||||
channels,
|
||||
managed,
|
||||
@@ -560,8 +579,13 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
patch_checkpoint_map(saved.config, saved.metadata),
|
||||
saved.metadata,
|
||||
saved.checkpoint["ts"],
|
||||
saved.parent_config,
|
||||
tasks_w_writes(next_tasks.values(), saved.pending_writes, task_states),
|
||||
patch_checkpoint_map(saved.parent_config, saved.metadata),
|
||||
tasks_w_writes(
|
||||
next_tasks.values(),
|
||||
saved.pending_writes,
|
||||
task_states,
|
||||
self.stream_channels_asis,
|
||||
),
|
||||
)
|
||||
|
||||
def get_state(
|
||||
@@ -825,7 +849,11 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
if as_node not in self.nodes:
|
||||
raise InvalidUpdateError(f"Node {as_node} does not exist")
|
||||
# update channels
|
||||
with ChannelsManager(self.channels, checkpoint, config) as (
|
||||
with ChannelsManager(
|
||||
self.channels,
|
||||
checkpoint,
|
||||
LoopProtocol(config=config, step=step + 1, stop=step + 2),
|
||||
) as (
|
||||
channels,
|
||||
managed,
|
||||
):
|
||||
@@ -866,9 +894,10 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
if saved:
|
||||
checkpointer.put_writes(checkpoint_config, task.writes, task_id)
|
||||
# apply to checkpoint and save
|
||||
assert not apply_writes(
|
||||
mv_writes = apply_writes(
|
||||
checkpoint, channels, [task], checkpointer.get_next_version
|
||||
), "Can't write to SharedValues from update_state"
|
||||
)
|
||||
assert not mv_writes, "Can't write to SharedValues from update_state"
|
||||
checkpoint = create_checkpoint(checkpoint, channels, step + 1)
|
||||
next_config = checkpointer.put(
|
||||
checkpoint_config,
|
||||
@@ -970,7 +999,11 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
if as_node not in self.nodes:
|
||||
raise InvalidUpdateError(f"Node {as_node} does not exist")
|
||||
# update channels, acting as the chosen node
|
||||
async with AsyncChannelsManager(self.channels, checkpoint, config) as (
|
||||
async with AsyncChannelsManager(
|
||||
self.channels,
|
||||
checkpoint,
|
||||
LoopProtocol(config=config, step=step + 1, stop=step + 2),
|
||||
) as (
|
||||
channels,
|
||||
managed,
|
||||
):
|
||||
@@ -1011,9 +1044,10 @@ class Pregel(Runnable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]):
|
||||
if saved:
|
||||
await checkpointer.aput_writes(checkpoint_config, writes, task_id)
|
||||
# apply to checkpoint and save
|
||||
assert not apply_writes(
|
||||
mv_writes = apply_writes(
|
||||
checkpoint, channels, [task], checkpointer.get_next_version
|
||||
), "Can't write to SharedValues from update_state"
|
||||
)
|
||||
assert not mv_writes, "Can't write to SharedValues from update_state"
|
||||
checkpoint = create_checkpoint(checkpoint, channels, step + 1)
|
||||
next_config = await checkpointer.aput(
|
||||
checkpoint_config,
|
||||
|
||||
@@ -57,7 +57,7 @@ from langgraph.pregel.log import logger
|
||||
from langgraph.pregel.manager import ChannelsManager
|
||||
from langgraph.pregel.read import PregelNode
|
||||
from langgraph.store.base import BaseStore
|
||||
from langgraph.types import All, PregelExecutableTask, PregelTask
|
||||
from langgraph.types import All, LoopProtocol, PregelExecutableTask, PregelTask
|
||||
from langgraph.utils.config import merge_configs, patch_config
|
||||
|
||||
GetNextVersion = Callable[[Optional[V], BaseChannel], V]
|
||||
@@ -148,7 +148,7 @@ def local_read(
|
||||
with ChannelsManager(
|
||||
{k: v for k, v in channels.items() if k in updated},
|
||||
checkpoint,
|
||||
config,
|
||||
LoopProtocol(config=config, step=step, stop=step + 1),
|
||||
skip_context=True,
|
||||
) as (local_channels, _):
|
||||
apply_writes(copy_checkpoint(checkpoint), local_channels, [task], None)
|
||||
@@ -156,7 +156,7 @@ def local_read(
|
||||
else:
|
||||
values = read_channels(channels, select)
|
||||
if managed_keys:
|
||||
values.update({k: managed[k](step) for k in managed_keys})
|
||||
values.update({k: managed[k]() for k in managed_keys})
|
||||
return values
|
||||
|
||||
|
||||
@@ -493,9 +493,7 @@ def prepare_single_task(
|
||||
):
|
||||
try:
|
||||
val = next(
|
||||
_proc_input(
|
||||
step, proc, managed, channels, for_execution=for_execution
|
||||
)
|
||||
_proc_input(proc, managed, channels, for_execution=for_execution)
|
||||
)
|
||||
except StopIteration:
|
||||
return
|
||||
@@ -583,7 +581,6 @@ def prepare_single_task(
|
||||
|
||||
|
||||
def _proc_input(
|
||||
step: int,
|
||||
proc: PregelNode,
|
||||
managed: ManagedValueMapping,
|
||||
channels: Mapping[str, BaseChannel],
|
||||
@@ -605,7 +602,7 @@ def _proc_input(
|
||||
except EmptyChannelError:
|
||||
continue
|
||||
else:
|
||||
val[k] = managed[k](step)
|
||||
val[k] = managed[k]()
|
||||
except EmptyChannelError:
|
||||
return
|
||||
elif isinstance(proc.channels, list):
|
||||
|
||||
@@ -32,6 +32,7 @@ from langgraph.constants import (
|
||||
from langgraph.pregel.io import read_channels
|
||||
from langgraph.pregel.utils import find_subgraph_pregel
|
||||
from langgraph.types import PregelExecutableTask, PregelTask, StateSnapshot
|
||||
from langgraph.utils.config import patch_checkpoint_map
|
||||
|
||||
|
||||
class TaskPayload(TypedDict):
|
||||
@@ -148,6 +149,7 @@ def map_debug_checkpoint(
|
||||
tasks: Iterable[PregelExecutableTask],
|
||||
pending_writes: list[PendingWrite],
|
||||
parent_config: Optional[RunnableConfig],
|
||||
output_keys: Union[str, Sequence[str]],
|
||||
) -> Iterator[DebugOutputCheckpoint]:
|
||||
"""Produce "checkpoint" events for stream_mode=debug."""
|
||||
|
||||
@@ -176,8 +178,8 @@ def map_debug_checkpoint(
|
||||
"timestamp": checkpoint["ts"],
|
||||
"step": step,
|
||||
"payload": {
|
||||
"config": config,
|
||||
"parent_config": parent_config,
|
||||
"config": patch_checkpoint_map(config, metadata),
|
||||
"parent_config": patch_checkpoint_map(parent_config, metadata),
|
||||
"values": read_channels(channels, stream_channels),
|
||||
"metadata": metadata,
|
||||
"next": [t.name for t in tasks],
|
||||
@@ -195,7 +197,7 @@ def map_debug_checkpoint(
|
||||
"interrupts": tuple(asdict(i) for i in t.interrupts),
|
||||
"state": t.state,
|
||||
}
|
||||
for t in tasks_w_writes(tasks, pending_writes, task_states)
|
||||
for t in tasks_w_writes(tasks, pending_writes, task_states, output_keys)
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -251,6 +253,7 @@ def tasks_w_writes(
|
||||
tasks: Iterable[Union[PregelTask, PregelExecutableTask]],
|
||||
pending_writes: Optional[list[PendingWrite]],
|
||||
states: Optional[dict[str, Union[RunnableConfig, StateSnapshot]]],
|
||||
output_keys: Union[str, Sequence[str]],
|
||||
) -> tuple[PregelTask, ...]:
|
||||
"""Apply writes / subgraph states to tasks to be returned in a StateSnapshot."""
|
||||
pending_writes = pending_writes or []
|
||||
@@ -271,6 +274,32 @@ def tasks_w_writes(
|
||||
v for tid, n, v in pending_writes if tid == task.id and n == INTERRUPT
|
||||
),
|
||||
states.get(task.id) if states else None,
|
||||
(
|
||||
next(
|
||||
(
|
||||
val
|
||||
for tid, chan, val in pending_writes
|
||||
if tid == task.id and chan == output_keys
|
||||
),
|
||||
None,
|
||||
)
|
||||
if isinstance(output_keys, str)
|
||||
else {
|
||||
chan: val
|
||||
for tid, chan, val in pending_writes
|
||||
if tid == task.id
|
||||
and (
|
||||
chan == output_keys
|
||||
if isinstance(output_keys, str)
|
||||
else chan in output_keys
|
||||
)
|
||||
}
|
||||
)
|
||||
if any(
|
||||
w[0] == task.id and w[1] not in (ERROR, INTERRUPT)
|
||||
for w in pending_writes
|
||||
)
|
||||
else None,
|
||||
)
|
||||
for task in tasks
|
||||
)
|
||||
|
||||
@@ -123,7 +123,7 @@ def map_output_updates(
|
||||
updated = (
|
||||
(
|
||||
task.name,
|
||||
{chan: value for chan, value in task.writes if chan in output_channels},
|
||||
{chan: value for chan, value in writes if chan in output_channels},
|
||||
)
|
||||
for task, writes in output_tasks
|
||||
if any(chan in output_channels for chan, _ in writes)
|
||||
|
||||
@@ -54,7 +54,6 @@ from langgraph.constants import (
|
||||
NS_SEP,
|
||||
SCHEDULED,
|
||||
TAG_HIDDEN,
|
||||
TASKS,
|
||||
)
|
||||
from langgraph.errors import (
|
||||
_SEEN_CHECKPOINT_NS,
|
||||
@@ -101,7 +100,7 @@ from langgraph.pregel.manager import AsyncChannelsManager, ChannelsManager
|
||||
from langgraph.pregel.read import PregelNode
|
||||
from langgraph.pregel.utils import get_new_channel_versions
|
||||
from langgraph.store.base import BaseStore
|
||||
from langgraph.types import All, PregelExecutableTask, StreamMode
|
||||
from langgraph.types import All, LoopProtocol, PregelExecutableTask, StreamProtocol
|
||||
from langgraph.utils.config import patch_configurable
|
||||
|
||||
V = TypeVar("V")
|
||||
@@ -113,22 +112,6 @@ INPUT_RESUMING = object()
|
||||
SPECIAL_CHANNELS = (ERROR, INTERRUPT, SCHEDULED)
|
||||
|
||||
|
||||
class StreamProtocol:
|
||||
__slots__ = ("modes", "__call__")
|
||||
|
||||
modes: set[StreamMode]
|
||||
|
||||
__call__: Callable[[StreamChunk], None]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
__call__: Callable[[StreamChunk], None],
|
||||
modes: set[StreamMode],
|
||||
) -> None:
|
||||
self.__call__ = __call__
|
||||
self.modes = modes
|
||||
|
||||
|
||||
def DuplexStream(*streams: StreamProtocol) -> StreamProtocol:
|
||||
def __call__(value: StreamChunk) -> None:
|
||||
for stream in streams:
|
||||
@@ -138,16 +121,13 @@ def DuplexStream(*streams: StreamProtocol) -> StreamProtocol:
|
||||
return StreamProtocol(__call__, {mode for s in streams for mode in s.modes})
|
||||
|
||||
|
||||
class PregelLoop:
|
||||
class PregelLoop(LoopProtocol):
|
||||
input: Optional[Any]
|
||||
config: RunnableConfig
|
||||
store: Optional[BaseStore]
|
||||
checkpointer: Optional[BaseCheckpointSaver]
|
||||
nodes: Mapping[str, PregelNode]
|
||||
specs: Mapping[str, Union[BaseChannel, ManagedValueSpec]]
|
||||
output_keys: Union[str, Sequence[str]]
|
||||
stream_keys: Union[str, Sequence[str]]
|
||||
stream: Optional[StreamProtocol]
|
||||
skip_done_tasks: bool
|
||||
is_nested: bool
|
||||
|
||||
@@ -178,8 +158,6 @@ class PregelLoop:
|
||||
checkpoint_previous_versions: dict[str, Union[str, float, int]]
|
||||
prev_checkpoint_config: Optional[RunnableConfig]
|
||||
|
||||
step: int
|
||||
stop: int
|
||||
status: Literal[
|
||||
"pending", "done", "interrupt_before", "interrupt_after", "out_of_steps"
|
||||
]
|
||||
@@ -203,10 +181,14 @@ class PregelLoop:
|
||||
check_subgraphs: bool = True,
|
||||
debug: bool = False,
|
||||
) -> None:
|
||||
self.stream = stream
|
||||
super().__init__(
|
||||
step=0,
|
||||
stop=0,
|
||||
config=config,
|
||||
stream=stream,
|
||||
store=store,
|
||||
)
|
||||
self.input = input
|
||||
self.config = config
|
||||
self.store = store
|
||||
self.checkpointer = checkpointer
|
||||
self.nodes = nodes
|
||||
self.specs = specs
|
||||
@@ -250,30 +232,12 @@ class PregelLoop:
|
||||
if self.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS)
|
||||
else ()
|
||||
)
|
||||
self.prev_checkpoint_config = (
|
||||
self.checkpoint_config
|
||||
if self.checkpoint_config
|
||||
and CONF in self.checkpoint_config
|
||||
and CONFIG_KEY_CHECKPOINT_ID in self.checkpoint_config[CONF]
|
||||
else None
|
||||
)
|
||||
self.prev_checkpoint_config = None
|
||||
|
||||
def put_writes(self, task_id: str, writes: Sequence[tuple[str, Any]]) -> None:
|
||||
"""Put writes for a task, to be read by the next tick."""
|
||||
if not writes:
|
||||
return
|
||||
# adjust task_writes_left
|
||||
first_channel = writes[0][0]
|
||||
any_channel_is_send = any(k == TASKS for k, _ in writes)
|
||||
always_save = any_channel_is_send or first_channel in SPECIAL_CHANNELS
|
||||
if not always_save and not self.task_writes_left:
|
||||
return self._output_writes(task_id, writes)
|
||||
elif first_channel == INTERRUPT:
|
||||
# INTERRUPT makes us want to save the last task's writes
|
||||
# so we don't decrement task_writes_left
|
||||
pass
|
||||
else:
|
||||
self.task_writes_left -= 1
|
||||
# save writes
|
||||
self.checkpoint_pending_writes.extend((task_id, k, v) for k, v in writes)
|
||||
if self.checkpointer_put_writes is not None:
|
||||
@@ -352,10 +316,7 @@ class PregelLoop:
|
||||
# after execution, check if we should interrupt
|
||||
if should_interrupt(self.checkpoint, interrupt_after, self.tasks.values()):
|
||||
self.status = "interrupt_after"
|
||||
if self.is_nested:
|
||||
raise GraphInterrupt()
|
||||
else:
|
||||
return False
|
||||
raise GraphInterrupt()
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -377,9 +338,6 @@ class PregelLoop:
|
||||
store=self.store,
|
||||
checkpointer=self.checkpointer,
|
||||
)
|
||||
# we don't need to save the writes for the last task that completes
|
||||
# unless in special conditions handled by self.put_writes()
|
||||
self.task_writes_left = len(self.tasks) - 1
|
||||
|
||||
# produce debug output
|
||||
if self._checkpointer_put_after_previous is not None:
|
||||
@@ -395,6 +353,7 @@ class PregelLoop:
|
||||
self.tasks.values(),
|
||||
self.checkpoint_pending_writes,
|
||||
self.prev_checkpoint_config,
|
||||
self.output_keys,
|
||||
)
|
||||
|
||||
# if no more tasks, we're done
|
||||
@@ -447,10 +406,7 @@ class PregelLoop:
|
||||
# before execution, check if we should interrupt
|
||||
if should_interrupt(self.checkpoint, interrupt_before, self.tasks.values()):
|
||||
self.status = "interrupt_before"
|
||||
if self.is_nested:
|
||||
raise GraphInterrupt()
|
||||
else:
|
||||
return False
|
||||
raise GraphInterrupt()
|
||||
|
||||
# produce debug output
|
||||
self._emit("debug", map_debug_tasks, self.step, self.tasks.values())
|
||||
@@ -509,12 +465,13 @@ class PregelLoop:
|
||||
manager=None,
|
||||
)
|
||||
# apply input writes
|
||||
assert not apply_writes(
|
||||
mv_writes = apply_writes(
|
||||
self.checkpoint,
|
||||
self.channels,
|
||||
[*discard_tasks.values(), PregelTaskWrites(INPUT, input_writes, [])],
|
||||
self.checkpointer_get_next_version,
|
||||
), "Can't write to SharedValues in graph input"
|
||||
)
|
||||
assert not mv_writes, "Can't write to SharedValues in graph input"
|
||||
# save input checkpoint
|
||||
self._put_checkpoint({"source": "input", "writes": dict(input_writes)})
|
||||
elif CONFIG_KEY_RESUMING not in configurable:
|
||||
@@ -528,7 +485,7 @@ class PregelLoop:
|
||||
)
|
||||
|
||||
def _put_checkpoint(self, metadata: CheckpointMetadata) -> None:
|
||||
# assign step
|
||||
# assign step and parents
|
||||
metadata["step"] = self.step
|
||||
metadata["parents"] = self.config[CONF].get(CONFIG_KEY_CHECKPOINT_MAP, {})
|
||||
# debug flag
|
||||
@@ -544,13 +501,14 @@ class PregelLoop:
|
||||
self.checkpoint = create_checkpoint(self.checkpoint, self.channels, self.step)
|
||||
# bail if no checkpointer
|
||||
if self._checkpointer_put_after_previous is not None:
|
||||
self.checkpoint_metadata = metadata
|
||||
|
||||
self.prev_checkpoint_config = (
|
||||
self.checkpoint_config
|
||||
if CONFIG_KEY_CHECKPOINT_ID in self.checkpoint_config[CONF]
|
||||
and self.checkpoint_config[CONF][CONFIG_KEY_CHECKPOINT_ID]
|
||||
else None
|
||||
)
|
||||
self.checkpoint_metadata = metadata
|
||||
self.checkpoint_config = {
|
||||
**self.checkpoint_config,
|
||||
CONF: {
|
||||
@@ -603,6 +561,10 @@ class PregelLoop:
|
||||
self.output = read_channels(self.channels, self.output_keys)
|
||||
if suppress:
|
||||
# suppress interrupt
|
||||
self._emit(
|
||||
"updates",
|
||||
lambda: iter([{INTERRUPT: cast(GraphInterrupt, exc_value).args[0]}]),
|
||||
)
|
||||
return True
|
||||
|
||||
def _emit(
|
||||
@@ -740,6 +702,7 @@ class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
**saved.config.get(CONF, {}),
|
||||
},
|
||||
}
|
||||
self.prev_checkpoint_config = saved.parent_config
|
||||
self.checkpoint = saved.checkpoint
|
||||
self.checkpoint_metadata = saved.metadata
|
||||
self.checkpoint_pending_writes = (
|
||||
@@ -750,7 +713,7 @@ class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
|
||||
self.submit = self.stack.enter_context(BackgroundExecutor(self.config))
|
||||
self.channels, self.managed = self.stack.enter_context(
|
||||
ChannelsManager(self.specs, self.checkpoint, self.config, self.store)
|
||||
ChannelsManager(self.specs, self.checkpoint, self)
|
||||
)
|
||||
self.stack.push(self._suppress_interrupt)
|
||||
self.status = "pending"
|
||||
@@ -867,6 +830,7 @@ class AsyncPregelLoop(PregelLoop, AsyncContextManager):
|
||||
**saved.config.get(CONF, {}),
|
||||
},
|
||||
}
|
||||
self.prev_checkpoint_config = saved.parent_config
|
||||
self.checkpoint = saved.checkpoint
|
||||
self.checkpoint_metadata = saved.metadata
|
||||
self.checkpoint_pending_writes = (
|
||||
@@ -877,7 +841,7 @@ class AsyncPregelLoop(PregelLoop, AsyncContextManager):
|
||||
|
||||
self.submit = await self.stack.enter_async_context(AsyncBackgroundExecutor())
|
||||
self.channels, self.managed = await self.stack.enter_async_context(
|
||||
AsyncChannelsManager(self.specs, self.checkpoint, self.config, self.store)
|
||||
AsyncChannelsManager(self.specs, self.checkpoint, self)
|
||||
)
|
||||
self.stack.push(self._suppress_interrupt)
|
||||
self.status = "pending"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user