From f38784a2915b354585490652c9a66ae553269d63 Mon Sep 17 00:00:00 2001 From: Eugene Yurtsev Date: Tue, 18 Feb 2025 16:16:40 -0500 Subject: [PATCH] ci: remove markddown-exec from docs pipeline (#3482) This PR removes the following changes: * notebooks that were converted to markdown * mkdocs.yml file to reference the ipython notebooks rather than the markdown files * Makefile install vercel reverted * hooks for markdown-exec * notebook conversion jinja2 templates (for converting notebooks to markdown exec format) --- docs/Makefile | 8 +- docs/_scripts/assets/nock_setup_preamble.ts | 75 --- docs/_scripts/assets/vcr_setup_preamble.py | 107 ---- docs/_scripts/notebook_convert.py | 111 +--- .../md_executable/conf.json | 5 - .../md_executable/index.md.j2 | 38 -- docs/_scripts/notebook_hooks.py | 119 +--- docs/_scripts/setup_vcr.py | 77 --- docs/docs/how-tos/branching.ipynb | 541 ++++++++++++++++++ docs/docs/how-tos/branching.md | 286 --------- docs/docs/how-tos/create-react-agent.ipynb | 300 ++++++++++ docs/docs/how-tos/create-react-agent.md | 146 ----- docs/docs/how-tos/index.md | 8 +- docs/docs/how-tos/sequence.ipynb | 355 ++++++++++++ docs/docs/how-tos/sequence.md | 223 -------- docs/docs/how-tos/state-reducers.ipynb | 430 ++++++++++++++ docs/docs/how-tos/state-reducers.md | 219 ------- docs/mkdocs.yml | 31 +- .../unit_tests/test_notebook_conversion.py | 71 --- 19 files changed, 1638 insertions(+), 1512 deletions(-) delete mode 100644 docs/_scripts/assets/nock_setup_preamble.ts delete mode 100644 docs/_scripts/assets/vcr_setup_preamble.py delete mode 100644 docs/_scripts/notebook_convert_templates/md_executable/conf.json delete mode 100644 docs/_scripts/notebook_convert_templates/md_executable/index.md.j2 delete mode 100644 docs/_scripts/setup_vcr.py create mode 100644 docs/docs/how-tos/branching.ipynb delete mode 100644 docs/docs/how-tos/branching.md create mode 100644 docs/docs/how-tos/create-react-agent.ipynb delete mode 100644 docs/docs/how-tos/create-react-agent.md create mode 100644 docs/docs/how-tos/sequence.ipynb delete mode 100644 docs/docs/how-tos/sequence.md create mode 100644 docs/docs/how-tos/state-reducers.ipynb delete mode 100644 docs/docs/how-tos/state-reducers.md diff --git a/docs/Makefile b/docs/Makefile index 11b2519fb..d8df63efd 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -26,15 +26,9 @@ install-vercel-deps: # don't use vercel's python - it wasn't compiled with sqlite support, and it fails when installing ipython's kernel poetry env use /usr/bin/python3.11 poetry install --with docs --with test --no-root - poetry run pip install "git+https://github.com/benjamincburns/markdown-exec.git@cc0d39d737e5ffd4b83d23cd8729d7ea16e363c8" - poetry run python3 -m ipykernel install --name=python3 - npm install -g tslab - poetry run tslab install --python=python3 - poetry run jupyter kernelspec list - tests: - # RUn unit tests + # Run unit tests poetry run pytest tests/unit_tests diff --git a/docs/_scripts/assets/nock_setup_preamble.ts b/docs/_scripts/assets/nock_setup_preamble.ts deleted file mode 100644 index 3a1c0a9d6..000000000 --- a/docs/_scripts/assets/nock_setup_preamble.ts +++ /dev/null @@ -1,75 +0,0 @@ -import nock, { Definition } from "nock"; -import msgpack from "msgpack-lite"; -import zlib from "node:zlib"; -import fs from "node:fs/promises"; -import { Buffer } from "node:buffer"; - -// deno style imports here because we're running this in the deno jupyter kernel - -interface NockCassetteData { - hash: string; - entries: Definition[]; -} - -// Utility functions for compression & serialization -function compressData(data: NockCassetteData, compressionLevel = 9): string { - const packed = msgpack.encode(data); - const compressed = zlib.deflateSync(packed, { level: compressionLevel }); - return compressed.toString("base64"); -} - -function decompressData(compressedString: string): NockCassetteData { - const decoded = Buffer.from(compressedString, "base64"); - const decompressed = zlib.inflateSync(decoded); - return msgpack.decode(decompressed) as NockCassetteData; -} - -// deno-lint-ignore no-unused-vars -class HashedCassette { - private recording = true; - - constructor( - private readonly cassettePath: string, - private readonly hash: string - ) {} - - async enter() { - try { - const rawCassette = await fs.readFile(this.cassettePath, "utf-8"); - const data = decompressData(rawCassette); - if (data.hash === this.hash) { - this.recording = false; - nock.disableNetConnect(); - nock.define(data.entries); - return; - } - } catch (error) { - if (error instanceof Error && error.message.includes("ENOENT")) { - this.recording = true; - } else { - throw error; - } - } - - nock.recorder.rec({ - dont_print: true, - output_objects: true, - }); - } - - async exit() { - if (this.recording) { - const entries = nock.recorder.play() as Definition[]; - const data = { - hash: this.hash, - entries, - }; - const compressed = compressData(data); - await fs.writeFile(this.cassettePath, compressed); - } else { - nock.enableNetConnect(); - nock.restore(); - nock.cleanAll(); - } - } -} diff --git a/docs/_scripts/assets/vcr_setup_preamble.py b/docs/_scripts/assets/vcr_setup_preamble.py deleted file mode 100644 index efa9c7109..000000000 --- a/docs/_scripts/assets/vcr_setup_preamble.py +++ /dev/null @@ -1,107 +0,0 @@ -import base64 -import os -import zlib -from types import TracebackType -from typing import Optional, Any, Type - -import msgpack -import vcr - -os.environ.pop("LANGCHAIN_TRACING_V2", None) -custom_vcr = vcr.VCR() - - -def compress_data(data: Any, compression_level: int = 9) -> str: - packed = msgpack.packb(data, use_bin_type=True) - compressed = zlib.compress(packed, level=compression_level) - return base64.b64encode(compressed).decode("utf-8") - - -def decompress_data(compressed_string: str) -> Any: - decoded = base64.b64decode(compressed_string) - decompressed = zlib.decompress(decoded) - return msgpack.unpackb(decompressed, raw=False) - - -class AdvancedCompressedSerializer: - def serialize(self, cassette_dict: Any) -> str: - return compress_data(cassette_dict) - - def deserialize(self, cassette_string: str) -> Any: - return decompress_data(cassette_string) - - -custom_vcr.register_serializer("advanced_compressed", AdvancedCompressedSerializer()) -custom_vcr.serializer = "advanced_compressed" - - -class HashedCassette: - def __init__(self, cassette_path: str, hash_value: str) -> None: - """A context manager for using VCR cassettes with an embedded hash value. - - Args: - cassette_path (str): The file path of the cassette (independent of hash). - hash_value (str): The expected hash value (e.g. a uuid string). - - This class provides a context manager for using VCR cassettes with an embedded hash value. - The hash value is used to ensure that the cassette matches the expected state, and if not, - the cassette is removed or updated with the new hash value. - """ - self.cassette_path: str = cassette_path - self.hash_value: str = hash_value - self.vcr: vcr.VCR = custom_vcr - self.cassette_context: Optional[Any] = None - self.exited: bool = False - - def __enter__(self) -> Any: - self.exited: bool = False - # Get the serializer instance from the VCR instance. - serializer = self.vcr.serializers[self.vcr.serializer] - # If the cassette file exists, check its embedded hash. - if os.path.exists(self.cassette_path): - with open(self.cassette_path, "r") as f: - content = f.read() - try: - cassette_data = serializer.deserialize(content) - except Exception as e: - os.remove(self.cassette_path) - else: - existing_hash = cassette_data.get("cassette_hash") - if existing_hash != self.hash_value: - os.remove(self.cassette_path) - # Now enter the VCR cassette context. - self.cassette_context = custom_vcr.use_cassette( - self.cassette_path, - filter_headers=["x-api-key", "authorization"], - record_mode="once", - serializer="advanced_compressed", - ) - return self.cassette_context.__enter__() - - def __exit__( - self, - exc_type: Optional[Type[BaseException]] = None, - exc_val: Optional[BaseException] = None, - exc_tb: Optional[TracebackType] = None, - ) -> Optional[bool]: - if self.exited: - return - self.exited = True - # Exit the VCR cassette context. - result = self.cassette_context.__exit__(exc_type, exc_val, exc_tb) - serializer = self.vcr.serializers[self.vcr.serializer] - # If a cassette was recorded (or updated), open and update its hash. - if os.path.exists(self.cassette_path): - with open(self.cassette_path, "r") as f: - content = f.read() - try: - cassette_data = serializer.deserialize(content) - except Exception as e: - return result - # Update the cassette data with the expected hash. - if cassette_data.get("cassette_hash") != self.hash_value: - cassette_data["cassette_hash"] = self.hash_value - serialized_data = serializer.serialize(cassette_data) - with open(self.cassette_path, "w") as f: - f.write(serialized_data) - return result diff --git a/docs/_scripts/notebook_convert.py b/docs/_scripts/notebook_convert.py index 66a56ff88..06badf25b 100644 --- a/docs/_scripts/notebook_convert.py +++ b/docs/_scripts/notebook_convert.py @@ -1,10 +1,8 @@ -import argparse import ast -import glob import os import re from pathlib import Path -from typing import Literal, Optional +from typing import Literal import nbformat from nbconvert.exporters import MarkdownExporter @@ -352,17 +350,6 @@ exporter = MarkdownExporter( ], ) -md_executable = MarkdownExporter( - preprocessors=[ - ExtractAttachmentsPreprocessor, - EscapePreprocessor(markdown_exec_migration=True), - ], - template_name="md_executable", - extra_template_basedirs=[ - os.path.join(os.path.dirname(__file__), "notebook_convert_templates") - ], -) - def convert_notebook( notebook_path: Path, @@ -372,99 +359,5 @@ def convert_notebook( nb = nbformat.read(f, as_version=4) nb.metadata.mode = mode - if mode == "markdown": - body, _ = exporter.from_notebook_node(nb) - else: - body, _ = md_executable.from_notebook_node(nb) + body, _ = exporter.from_notebook_node(nb) return body - - -HERE = Path(__file__).parent -DOCS = HERE.parent / "docs" - - -# Convert notebooks to markdown -def _convert_notebooks( - *, - output_dir: Optional[Path] = None, - replace: bool = False, - pattern: str = "*.ipynb", -) -> None: - """Converting notebooks.""" - if not output_dir and not replace: - raise ValueError("Either --output_dir or --replace must be specified") - - output_dir_path = DOCS if replace else Path(output_dir) - - # Get the directory where the script was executed - base_dir = os.getcwd() - # Build the full search pattern using the current working directory as the base - full_pattern = os.path.join(base_dir, args.pattern) - - # Use glob with recursive search enabled - matching_files = glob.glob(full_pattern, recursive=True) - paths = [Path(file) for file in matching_files] - - file_names = [notebook.name for notebook in paths] - - for notebook in paths: - markdown = convert_notebook(notebook, mode="exec") - markdown_path = output_dir_path / notebook.relative_to(DOCS).with_suffix(".md") - markdown_path.parent.mkdir(parents=True, exist_ok=True) - with open(markdown_path, "w") as f: - f.write(markdown) - if replace: - notebook.unlink(missing_ok=False) - - if replace: - # The regex will match markdown links that point to *.ipynb files. - # It captures: - # group(1): the link text (inside the square brackets) - # group(2): the file path (without the trailing .ipynb) - link_pattern = r"(? str: - link_text = match.group(1) - link_target = match.group(2) - # Reconstruct the file name with the .ipynb extension. - # For example, if link_target is "foo/bar", then linked_file becomes "bar.ipynb". - linked_file = Path(link_target).name + ".ipynb" - # Only update if the notebook was among those converted. - if linked_file in file_names: - # Change the extension from .ipynb to .md - return f"[{link_text}]({link_target}.md)" - # Otherwise, leave the original link intact. - return match.group(0) - - # Process all markdown files in the output directory. - for path in output_dir_path.rglob("**/*.md"): - with open(path, "r", encoding="utf-8") as f: - content = f.read() - new_content = re.sub(link_pattern, replace_link, content) - with open(path, "w", encoding="utf-8") as f: - f.write(new_content) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Convert notebooks to markdown") - parser.add_argument( - "--output_dir", - default=None, - help="Directory to output markdown files", - ) - parser.add_argument( - "--replace", - action="store_true", - help="Replace original notebooks with markdown files", - ) - parser.add_argument( - "--pattern", - default="*.ipynb", - help="Glob pattern to match notebooks to convert", - ) - args = parser.parse_args() - _convert_notebooks( - replace=args.replace, - output_dir=args.output_dir, - pattern=args.pattern, - ) diff --git a/docs/_scripts/notebook_convert_templates/md_executable/conf.json b/docs/_scripts/notebook_convert_templates/md_executable/conf.json deleted file mode 100644 index 7adab7c92..000000000 --- a/docs/_scripts/notebook_convert_templates/md_executable/conf.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mimetypes": { - "text/markdown": true - } -} \ No newline at end of file diff --git a/docs/_scripts/notebook_convert_templates/md_executable/index.md.j2 b/docs/_scripts/notebook_convert_templates/md_executable/index.md.j2 deleted file mode 100644 index 58e687fb4..000000000 --- a/docs/_scripts/notebook_convert_templates/md_executable/index.md.j2 +++ /dev/null @@ -1,38 +0,0 @@ -{#https://github.com/rdbisme/nbconvert/blob/master/share/jupyter/nbconvert/templates/markdown/index.md.j2#} -{% extends 'markdown/index.md.j2' %} - -{% block input %} -``` -{%- if 'magics_language' in cell.metadata -%} - {{ cell.metadata.magics_language}} -{%- elif cell.metadata.get('language') == "shell" -%} - shell -{%- elif 'name' in nb.metadata.get('language_info', {}) -%} - {{ nb.metadata.language_info.name }}{% if cell.metadata.exec|default(false) %} exec="on" source="above" session="1"{% if cell.metadata.has_output|default(false) %} result="ansi"{% endif %}{% endif %} -{%- endif %} -{{ cell.source}} -``` -{% endblock input %} - -{%- block traceback_line -%} -{%- endblock traceback_line -%} - -{%- block stream -%} -{%- endblock stream -%} - -{%- block data_text scoped -%} -{%- endblock data_text -%} - -{%- block data_html scoped -%} -```html -{{ output.data['text/html'] | safe }} -``` -{%- endblock data_html -%} - -{%- block data_jpg scoped -%} -![](data:image/jpg;base64,{{ output.data['image/jpeg'] }}) -{%- endblock data_jpg -%} - -{%- block data_png scoped -%} -![](data:image/png;base64,{{ output.data['image/png'] }}) -{%- endblock data_png -%} diff --git a/docs/_scripts/notebook_hooks.py b/docs/_scripts/notebook_hooks.py index dfbc9eef1..4a468c489 100644 --- a/docs/_scripts/notebook_hooks.py +++ b/docs/_scripts/notebook_hooks.py @@ -2,18 +2,13 @@ import logging import os import posixpath import re -import traceback -from typing import Any, Callable, Dict +from typing import Any, Dict -from markdown import Markdown -from markdown_exec.hooks import SessionHistoryEntry from mkdocs.structure.files import Files, File from mkdocs.structure.pages import Page -from pymdownx.superfences import SuperFencesException from _scripts.generate_api_reference_links import update_markdown_with_imports from _scripts.notebook_convert import convert_notebook -from _scripts.setup_vcr import load_postamble, load_preamble, _hash_string logger = logging.getLogger(__name__) logging.basicConfig() @@ -163,118 +158,6 @@ def _highlight_code_blocks(markdown: str) -> str: return markdown -def handle_vcr_setup( - *, - formatter: Callable, - language: str, - code: str, - session: str, - id: str, - md: Markdown, - **kwargs: Dict[str, Any], -) -> Dict[str, Any]: - """Handle VCR setup in markdown content if necessary.""" - try: - if kwargs.get("extra", None) is None: - raise SuperFencesException( - f"error while processing {language} block: extra dict is required" - ) - - if kwargs["extra"].get("path", None) is None: - raise SuperFencesException( - f"error while processing {language} block: path is required" - ) - - document_filename = kwargs["extra"]["path"] - - if session is None or session == "" and id is None or id == "": - id = _hash_string(code) - - if session is not None and session != "": - logger.info(f"new {language} session {session} on page {document_filename}") - - cassette_prefix = document_filename.replace(".md", "").replace(os.path.sep, "_") - - cassette_dir = os.path.abspath( - os.path.join(os.path.dirname(os.path.dirname(__file__)), "cassettes") - ) - os.makedirs(cassette_dir, exist_ok=True) - - # Build a unique cassette name. - cassette_name = os.path.join( - cassette_dir, - f"{cassette_prefix}_{session if session else id}_{language}.msgpack.zlib", - ) - - # Add context manager at start with explicit __enter__ and __exit__ calls - - wrapped_lines = [ - load_preamble(language, code, cassette_name), - code, - ] - - if session is None or session == "": - logger.info( - f"no session, adding postamble for {language} in {document_filename}" - ) - wrapped_lines.append(load_postamble(language)) - - transformed_source = "\n".join(wrapped_lines) - - # Propagate extras - keep_extras = { - key: value - for key, value in kwargs["extra"].items() - if key - in { - "hl_lines", - } - } - - return dict( - transform_source=lambda code: (transformed_source, code), - id=id, - extra=keep_extras, - ) - except Exception as e: - raise SuperFencesException(traceback.format_exc()) from e - - -def handle_vcr_teardown( - *, - formatter: Callable, - language: str, - session: str, - history: list[SessionHistoryEntry], -): - last_inputs = dict(history[-1].inputs) - code = load_postamble(language) - md = last_inputs["md"] - html = False - update_toc = False - - document_filename = last_inputs.get("extra", {}).get("path", None) - - if document_filename is None: - logger.warning(f"no document filename found while tearing down {session}!") - else: - logger.info(f"tearing down {language} {session} on {document_filename}") - - kwargs = dict( - code=code, - session=session, - id=f"{id}_vcr_end", - md=md, - html=html, - update_toc=update_toc, - extra={}, - ) - - # This doesn't actually render anything, we just call the formatter so it - # executes in the same context as the session of which we're disposing. - formatter(**kwargs) - - def _on_page_markdown_with_config( markdown: str, page: Page, diff --git a/docs/_scripts/setup_vcr.py b/docs/_scripts/setup_vcr.py deleted file mode 100644 index efd0283a3..000000000 --- a/docs/_scripts/setup_vcr.py +++ /dev/null @@ -1,77 +0,0 @@ -# A list of patterns that, if found in a code block, will cause us to leave that block unchanged. -import hashlib -import os -from textwrap import dedent - -preambles = { - "python": "vcr_setup_preamble.py", - "typescript": "nock_setup_preamble.ts", -} - - -def _get_python_cassette_init(cassette_name: str, hash_: str) -> str: - return dedent( - f""" - _cassette = HashedCassette('{cassette_name}', '{hash_}') - _cassette.__enter__() - """ - ) - - -def _get_typescript_cassette_init(cassette_name: str, hash_: str) -> str: - return dedent( - f""" - const _cassette = new HashedCassette("{cassette_name}", "{hash_}"); - await _cassette.enter(); - """ - ) - - -def _get_python_cassette_cleanup() -> str: - return "_cassette.__exit__()" - - -def _get_typescript_cassette_cleanup() -> str: - return "await _cassette.exit();" - - -preamble_inits = { - "python": _get_python_cassette_init, - "py": _get_python_cassette_init, - "typescript": _get_typescript_cassette_init, - "ts": _get_typescript_cassette_init, -} - -preamble_cleanups = { - "python": _get_python_cassette_cleanup, - "py": _get_python_cassette_cleanup, - "typescript": _get_typescript_cassette_cleanup, - "ts": _get_typescript_cassette_cleanup, -} - - -def load_preamble(language: str, code: str, cassette_name: str) -> str: - """Load the source code for the preamble for a given language.""" - _assets_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets") - - preamble_path = os.path.join(_assets_dir, preambles[language]) - with open(preamble_path, "r") as f: - lines = f.readlines() - hash_ = _hash_string(code) - lines.append(preamble_inits[language](cassette_name, hash_)) - return "\n".join(lines).strip() - - -def load_postamble(language: str) -> str: - """Load the source code for the postamble for a given language.""" - - return preamble_cleanups[language]() - - -def _hash_string(input_string: str) -> str: - # Encode the input string to bytes - encoded_string = input_string.encode("utf-8") - # Create a SHA-256 hash object - sha256_hash = hashlib.sha256(encoded_string) - # Get the hexadecimal digest of the hash - return sha256_hash.hexdigest() diff --git a/docs/docs/how-tos/branching.ipynb b/docs/docs/how-tos/branching.ipynb new file mode 100644 index 000000000..d1d6dd494 --- /dev/null +++ b/docs/docs/how-tos/branching.ipynb @@ -0,0 +1,541 @@ +{ + "cells": [ + { + "attachments": { + "51f122de-b2ce-4c21-a5a7-c3be70c28a91.png": { + "image/png": "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LyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj41NDI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+OTA0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClCweXwAAAAcaURPVAAAAAIAAAAAAAABDwAAACgAAAEPAAABDwAARV8dcCWfAABAAElEQVR4AezdB3wURfvA8UcgdAgdqaElNAEVFBSk96agf1REFJSiooAFRXxtiAooShHEBkhRULEjRTpIB0MTCC0JJEBooZcA/5mJt97mLqSQS+4uv30/8XZnd2dnvjcvnzyZ2ZmbrqlN2BBAAAEEEEAAAQQQQAABBDK9wE0EiJm+DQCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIICARwXOnDkjW7ZssT2jWrVqEhgYaEvjAAEEEEAAAQQyXoAAMeO/A0qAAAII+LVA+/btXQJEXeHy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Ujl++pycOH1eTkSfl7PnzsvFC+cl5qL5MZ8XL56TwsUjpFSZslK2TBkpXz5C8uUQyZczixTKncV82llyggACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAApaKqg4AAAVzSURBVAgggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuC/w/OsDdHSiHP+MAAAAASUVORK5CYII=" + } + }, + "cell_type": "markdown", + "id": "710dc4f0-1c88-4386-9e9d-fec3de6bb774", + "metadata": {}, + "source": [ + "# How to create branches for parallel node execution\n", + "\n", + "
\n", + "

Prerequisites

\n", + "

\n", + " This guide assumes familiarity with the following:\n", + "

\n", + "

\n", + "
\n", + "\n", + "Parallel execution of nodes is essential to speed up overall graph operation. LangGraph offers native support for parallel execution of nodes, which can significantly enhance the performance of graph-based workflows. This parallelization is achieved through fan-out and fan-in mechanisms, utilizing both standard edges and [conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.MessageGraph.add_conditional_edges). Below are some examples showing how to add create branching dataflows that work for you. \n", + "\n", + "![Screenshot 2024-07-09 at 2.55.56 PM.png](attachment:51f122de-b2ce-4c21-a5a7-c3be70c28a91.png)" + ] + }, + { + "cell_type": "markdown", + "id": "66b6b42d", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First, let's install the required packages" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bb54e2d0", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph" + ] + }, + { + "cell_type": "markdown", + "id": "73bac559", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith 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. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "d6c05fc4-ecd8-483f-a9fd-b1a055f922d9", + "metadata": {}, + "source": [ + "## How to run graph nodes in parallel\n", + "\n", + "In this example, we fan out from `Node A` to `B and C` and then fan in to `D`. With our state, [we specify the reducer add operation](https://langchain-ai.github.io/langgraph/concepts/low_level/#reducers). This will combine or accumulate values for the specific key in the State, rather than simply overwriting the existing value. For lists, this means concatenating the new list with the existing list. See [this guide](../../how-tos/state-reducers) for more detail on updating state with reducers." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "09372b8b-edea-4b9d-9ec3-3d93ce1ba819", + "metadata": {}, + "outputs": [], + "source": [ + "import operator\n", + "from typing import Annotated, Any\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph, START, END\n", + "\n", + "\n", + "class State(TypedDict):\n", + " # The operator.add reducer fn makes this append-only\n", + " aggregate: Annotated[list, operator.add]\n", + "\n", + "\n", + "def a(state: State):\n", + " print(f'Adding \"A\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", + "\n", + "\n", + "def b(state: State):\n", + " print(f'Adding \"B\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", + "\n", + "\n", + "def c(state: State):\n", + " print(f'Adding \"C\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"C\"]}\n", + "\n", + "\n", + "def d(state: State):\n", + " print(f'Adding \"D\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"D\"]}\n", + "\n", + "\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", + "builder.add_edge(START, \"a\")\n", + "builder.add_edge(\"a\", \"b\")\n", + "builder.add_edge(\"a\", \"c\")\n", + "builder.add_edge(\"b\", \"d\")\n", + "builder.add_edge(\"c\", \"d\")\n", + "builder.add_edge(\"d\", END)\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "66f52a20", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "id": "74dd577b-0474-44c4-b4bc-9113090e3121", + "metadata": {}, + "source": [ + "With the reducer, you can see that the values added in each node are accumulated." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "81646784-5e7d-4096-980d-9fdfafd6e7a3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"D\" to ['A', 'B', 'C']\n" + ] + }, + { + "data": { + "text/plain": [ + "{'aggregate': ['A', 'B', 'C', 'D']}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"aggregate\": []}, {\"configurable\": {\"thread_id\": \"foo\"}})" + ] + }, + { + "cell_type": "markdown", + "id": "ea5495cf-9564-40c6-bc2d-0b2a8f72a5df", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + " In the above example, nodes `\"b\"` and `\"c\"` are executed concurrently in the same [superstep](../../concepts/low_level/#graphs). Because they are in the same step, node `\"d\"` executes after both `\"b\"` and `\"c\"` are finished.\n", + "\n", + " Importantly, updates from a parallel superstep may not be ordered consistently. If you need a consistent, predetermined ordering of updates from a parallel superstep, you should write the outputs to a separate field in the state together with a value with which to order them." + ] + }, + { + "cell_type": "markdown", + "id": "c392b3d2", + "metadata": {}, + "source": [ + "
Exception handling?\n", + "

LangGraph executes nodes within \"supersteps\", meaning that while parallel branches are executed in parallel, the entire superstep is transactional. If any of these branches raises an exception, none of the updates are applied to the state (the entire superstep errors).

\n", + "

Importantly, when using a checkpointer, results from successful nodes within a superstep are saved, and don't repeat when resumed.

\n", + " If you have error-prone (perhaps want to handle flakey API calls), LangGraph provides two ways to address this:
\n", + "
    \n", + "
  1. You can write regular python code within your node to catch and handle exceptions.
  2. \n", + "
  3. You can set a retry_policy to direct the graph to retry nodes that raise certain types of exceptions. Only failing branches are retried, so you needn't worry about performing redundant work.
  4. \n", + "

\n", + "Together, these let you perform parallel execution and fully control exception handling.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "08d8162e-1785-4ae1-993f-6d2ed48c22ae", + "metadata": {}, + "source": [ + "## Parallel node fan-out and fan-in with extra steps\n", + "\n", + "The above example showed how to fan-out and fan-in when each path was only one step. But what if one path had more than one step? Let's add a node `b_2` in the \"b\" branch:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "259a7704-5aa0-4e4c-aeef-cca04e8be0ff", + "metadata": {}, + "outputs": [], + "source": [ + "def b_2(state: State):\n", + " print(f'Adding \"B_2\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B_2\"]}\n", + "\n", + "\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(b_2)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", + "builder.add_edge(START, \"a\")\n", + "builder.add_edge(\"a\", \"b\")\n", + "builder.add_edge(\"a\", \"c\")\n", + "builder.add_edge(\"b\", \"b_2\")\n", + "# highlight-next-line\n", + "builder.add_edge([\"b_2\", \"c\"], \"d\")\n", + "builder.add_edge(\"d\", END)\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "83320227-8ab3-44c0-b6cf-064a7a425b9f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2b8659a9-bacd-4620-a160-8a08d10f7192", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"B_2\" to ['A', 'B', 'C']\n", + "Adding \"D\" to ['A', 'B', 'C', 'B_2']\n" + ] + }, + { + "data": { + "text/plain": [ + "{'aggregate': ['A', 'B', 'C', 'B_2', 'D']}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"aggregate\": []})" + ] + }, + { + "cell_type": "markdown", + "id": "00d33cb0-5a47-4057-bc55-0fc14c6034fc", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + "

In the above example, nodes `\"b\"` and `\"c\"` are executed concurrently in the same [superstep](../../concepts/low_level/#graphs). What happens in the next step?

\n", + "

We use `add_edge([\"b_2\", \"c\"], \"d\")` here to force node `\"d\"` to only run when both nodes `\"b_2\"` and `\"c\"` have finished execution. If we added two separate edges,\n", + " node `\"d\"` would run twice: after node `b2` finishes and once again after node `c` (in whichever order those nodes finish).

" + ] + }, + { + "cell_type": "markdown", + "id": "d45f4477", + "metadata": {}, + "source": [ + "## Conditional Branching\n", + "\n", + "If your fan-out is not deterministic, you can use [add_conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_conditional_edges) directly." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "95f5e026", + "metadata": {}, + "outputs": [], + "source": [ + "import operator\n", + "from typing import Annotated, Sequence\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph, START, END\n", + "\n", + "\n", + "class State(TypedDict):\n", + " aggregate: Annotated[list, operator.add]\n", + " # Add a key to the state. We will set this key to determine\n", + " # how we branch.\n", + " which: str\n", + "\n", + "\n", + "def a(state: State):\n", + " print(f'Adding \"A\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", + "\n", + "\n", + "def b(state: State):\n", + " print(f'Adding \"B\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", + "\n", + "\n", + "def c(state: State):\n", + " print(f'Adding \"C\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"C\"]}\n", + "\n", + "\n", + "def d(state: State):\n", + " print(f'Adding \"D\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"D\"]}\n", + "\n", + "\n", + "def e(state: State):\n", + " print(f'Adding \"E\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"E\"]}\n", + "\n", + "\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", + "builder.add_node(e)\n", + "builder.add_edge(START, \"a\")\n", + "\n", + "\n", + "def route_bc_or_cd(state: State) -> Sequence[str]:\n", + " if state[\"which\"] == \"cd\":\n", + " return [\"c\", \"d\"]\n", + " return [\"b\", \"c\"]\n", + "\n", + "\n", + "intermediates = [\"b\", \"c\", \"d\"]\n", + "builder.add_conditional_edges(\n", + " \"a\",\n", + " route_bc_or_cd,\n", + " intermediates,\n", + ")\n", + "for node in intermediates:\n", + " builder.add_edge(node, \"e\")\n", + "\n", + "builder.add_edge(\"e\", END)\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "264da3f8-f5de-499b-8287-73797c1e3511", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOgAAAGwCAIAAAAsYb4BAAAAAXNSR0IArs4c6QAAIABJREFUeJzt3XdgFEX/P/C53u9yyaX3BAlFQgjBCIbQi4TeIgRQDIKo4IOKD/oFRAR9jIqKCI+NhyoPYghKk6qEmlCVEgKBFEi9y12u5vr+/jh/kQcDOWBn53ZvXn/p5W72w+adudnd2VkWQRAAw+iGjboADHsYOLgYLeHgYrSEg4vREg4uRks4uBgtcVEXgFJdhdVidFqMLpeTsDW7UZfjFYGIzRewxXKOWM4NjhSgLgcZfwzu1TOG8ovm8kvm2M4SQACxjKMM5QOanM522gl1dbPF4BJK2LevNcc/LonvIo7rKEVdF9VYfnUB4o9jTUV7G+M6SuO7SOIfl3A4LNQVPRKzwVl+yVxfaW24Zes1Iii2owR1RdTxl+A23LLuXVcX11HSa0QQT8C0kb2mxnZiZ6NAxB4yLQx1LRTxi+BeKTJcPKbPyg2XBjB5aFRb0Zz/efWk+dFBEcwf+zI/uGW/myqvmAdMCkVdCEW+z6sa8UK4TMlDXQhcDA/u6f3apgb7oCn+8gXqseWjqj7jgiMSRKgLgYhpo707lV8y11dZ/S21AIBJ82N2fl1jt9LjBN/DYWxw9Y32kmLD8BkRqAtBI2dBzP5NdairgIixwT22o7FDDxnqKpCRBvDkQbzfjzShLgQWZgbXc0ksoYvfnZa/01MjVMd3alBXAQszg3v5pL73aBXqKhDjcFkZo1QXfmNmp8vA4FotrpsXzWFxFB1Tm0ymq1evovr4/UUkikqKDZAaR4uBwS2/ZI5/nLqLn88888xPP/2E6uP3p4oQ2K1ug9YBqX2EGBjcugprYlfqRrd2u/3hPug5g/7QH/dShx6yqqsWqJtAgoHBra2wypVQLu2uW7du2LBhGRkZubm5xcXFAIDhw4drtdpt27alpaUNHz7cE8Qvv/xy5MiR6enpWVlZq1evdrlcno9/+OGHgwcPLiwsHDNmTFpa2unTp//+cdKJpJzGWrh/G0gw8Nq9xeAUy8n/dxUXF69atWro0KG9evU6ceKExWIBAOTl5b3yyivdu3fPycnh8/kAAA6HU1RUlJmZGRUVVVpaunbtWrlcPmXKFE8jJpNp9erVCxYsaG5u7tGjx98/TjqxnFtd1gyjZbSYFlyXi7A3u0VSDukt19TUAAAmTpyYnJw8bNgwz4udOnXicrkqlSolJcXzCofDWb9+PYv154TJ27dvHz58uCW4drt94cKFjz/++L0+TjqJnGM2uCA1jhDTgut2ukVy8lMLAMjIyJDL5YsWLZo/f35GRsZ93qnVar/55ptTp04ZDAYAgEz213UQoVDYklpqcLgsLo/e045bxbQxLk/AcVgJWzP5fYxKpVq7dm1sbOw//vGP3NzchoaGVt/W2NiYk5NTXFw8e/bsL774omPHji1jXACAWCwmvbD7MzU5mTf/mIHBBQCI5RwLnC/HuLi4lStXrlmzpqysbMmSJS2v3znDLj8/X6vVrl69esiQIZ07dw4La3uKD9QJehaDSwznKwgtBgY3MlFkMTphtOw5ddWjR4/evXu3XDUQiUQazV9XVpuampRKZUtem5qa7p/Luz5OOofdHRQG5bAPLc6dPQczGLWOmpvWuE4kX4O4fPnyCy+84HQ6r1+/vn379k6dOnkO0UpLSw8fPszlcm/evMnj8SQSyc8//+xyuRwOx/r16w8dOmQ2mydMmCAUCo8fP15eXj516tQ7m73r44GBgeSW/esP6m79AsQyph3MMDC4Ihnn1O7GlD4B5Dar1+uvXbu2f//+4uLi1NTUt99+WyqVAgCSk5NLS0v37Nlz9erVzp079+/f3+12b9u27dChQ9HR0YsWLTp//rzFYklLS2s1uHd9PD4+nsSaDVrH5ZOGnsMZOG2DmXdA7F1Xm/50UGAoA78iH0hJscGodTwxNAh1IeRj2jeIR1J32cldjVm54fd6w7Jlyw4ePPj310NDQ+vr6//+ukKhgDejoMWxY8cWLlzY6o+ioqJu377999c3b94cGRl5rwaPFmieXRxLao2+gpk9LgBg22e3eo8ODosTtvpTnU7X3NzK9SSHw8HjtXKbIZvN9ub8wCOyWq1arbbVH7FYrf+mQkJCuNzWe5+zB3U2q6sXE8cJTA5uzc3mq6eN/bNDUBeCzPZVt8e8HNlyDY9hGHg6zCMiQaQM5R3bwdhbAO5v68e3MkarmJpaJgcXANCtr9JqcZ091PqXL4Pt/q42OVMREtX6MIkZGDtUaFG0t5HHZ6cOUKIuhCJ71tYm91ZEPUb1tWWKMbnH9Uh/OshsdB78vpVzBQxjt7q//7CqXYqU8an1ix7Xo6TYcHSHuleW6vGnFKhrIR/hJo7vbKyvtPadEBwUzvyFw/wouJ4O6fhOze1rzZ17yuM7S5SMuDxRW95cXdZ8aq/2qRFB3fr5y3DIv4LrYdDaLx4zlF82AwLEdZZweSyJgisP5LlctNkPxkaHSe9kscHlkwZlCL9diiSljx9F1sPvgttC12Cvq7CampxmvZPNYRl1JE8oq6iokEqlKhXJ5/+lCg6Lw5IquDIlN7q9WChh4JRFbzDzkq83lCF8ZQjE0cKSJV/FdOqeNaILvE34M+afVcAYCQcXoyUcXFgCAwNbna+DkQIHFxatVutwMHDtIx+BgwuLQCBgs/HuhQXvWVhsNpvbzeTF7NHCwYVFKpXea4o39uhwcGExmUxOJ5S75DEcXIiCgoIEAr+Y74IEDi4sjY2NNpsNdRWMhYOL0RIOLiwikQifDoMH71lYmpub8ekweHBwYRGLxRyOn845pAAOLiwWi+XOlXExcuHgYrSEgwuLQqHAs8PgwcGFRa/X49lh8ODgYrSEgwtLUFAQpEeXYTi4EDU2NsJ+3Kk/w8HFaAkHFxaVSoVnh8GDgwuLRqPBs8PgwcHFaAkHFxZ8ezpUOLiw4NvTocLBxWgJBxcWvK4CVHjPwoLXVYAKBxcWpVKJL/nCg4MLi06nw5d84cHBxWgJBxcWsViMl2CCBwcXFovFgpdgggcHFxaVSiUUMvmhpGjh4MKi0WisVivqKhgLBxcWfAcEVDi4sOA7IKDCwYVFLpfj2WHw+O+TJSEZNGiQUChksVh6vZ7H44lEIhaLxeFwCgoKUJfGKPhEI8mUSuWNGzdYLJbnf5uamgAAI0aMQF0X0+ChAsmmTp16161mISEhU6dORVcRM+HgkmzEiBHR0dEt/0sQRFpaWkJCAtKiGAgHl3w5OTktJ8LCwsKmT5+OuiIGwsEl38iRI2NjY1u62/j4eNQVMRAOLhSTJ0/m8/mhoaHTpk1DXQsz4bMKD8BidDbW2h32tk8gdo7v3zm+KDY2ltUcdvOSuc33iyRsVYSAJ8D9iLfweVyvWIzOwz801FXYYjtKmo3krzPucrrrK63tUqQDJ4eS3jgj4eC2zWxw7viyOmNsWGAY3CWVrp83VJUYR70Y0XIaGLsXHNy2ffXPGxNej6fme7ziirHionHEzAgKtkVreFDVhjMHtKkDgigbfcZ1kvFFnKrStofFfg4Htw215VaJktK5MjwBR1ODp5W1AQe3DS4nkFEb3IAQvhXC8R/D4OC2wWJwEtQu6+FyEA4HPvBoAw4uRks4uBgt4eBitISDi9ESDi5GSzi4GC3h4GK0hIOL0RIOLkZLOLgYLeHgYrSEg4vREg4uRks4uBgt4bt8SWa32zds/Obw4X0N6vqgINXgQVnPPTuLw+GgrotpcHBJxuFwzp4t6tkrMyI8qqysdNPmtTKZfOKEKajrYhocXJJxOJzVX65vuU23pvZ24dHDOLikw8Eln06n3bDxm9NnThmNBgCATCpDXRED4eCSTKttnPlijkgkfn767IiIqLVrV9+6XYm6KAbCwSXZzzvzdTrtl1+sCw0NAwCEhITh4MKAT4eRzGBoCghQelILANAbmvCSKzDgHpdkKSlpBTt+WPufNZ07dz169HBR0XG32202myUSCerSGAX3uCTL7N1/2tQZO37atnz5/zmcji9XrYuJiSsqPo66LqbBa4e14ft/VWWMDVOGUveovavFeovB3mdcMGVbpCPc42K0hIOL0RIOLkZLOLgYLeHg3k9hYaHBYKB+u6dPn759+zb126URHNzW2Ww2k8lUUFAgkUqp33pcXPyKFSsAAC4XXm+0dTi4d3M4HEuXLq2trRUKhZ9++imHjWAXBQerPMH98ccf161bR30Bvg8H925r1qzp2rVrXFwcl4v+smJ2drbRaDxx4gTqQnwODu6fdu3a9X//938AgLlz544aNQp1OX+ZM2dOWloaAGDWrFnXrl1DXY6vwMEFZrPZarWePn160aJFqGtpnefJwK+//vp///tfAEBzczPqitDz6+CaTKbFixdrNBo+n//uu+8KhULUFd1P+/btFy9eDADYs2cPHvj6dXD37NkzaNCg2NhYNoojsIc2btw4mUx24cIFq9WKuhZk6PQLI8vWrVvHjx8PAJg4cWLv3r1Rl/Mwxo0bl5KS4nK5Ro0adfnyZdTlIOBfwdVoNACAurq6H3/80cuPBITxCUDpBDo2hyWWenU7u0Qi+fLLL48ePQoA0Gq18EvzIf4SXL1eP2vWLLVaDQB49dVXvf8gn89qrLHBLO1u9ZXNsiBvz8RFRUW9+OKLnrMi77//vv9MUmV+cG02GwDg1KlTM2fO7Nix44N+PP5xsa6O0uBajI7o9uIH/dS0adOSkpLKy8vtdr94KiXDg7tly5ZZs2YBAIYMGdK9e/eHaCExWcbhgLMHNRCqa8WhLbVdeikk8oe59jFu3LiEhASCIJ5++ulLly5BqM6HMPYOiNra2vDw8K+//nrmzJmP3lrhdrXDDlRRwuBIIZvDIqPA/2G1uDTV1pKipoxRqvjOj3p3WkNDw+7du6dPn65Wq4ODmXknBQODa7Va33jjjcmTJ/fq1YvEZssumG78YbLbCC+HvA6Hg81me7lqmEzJCwzlde0bEEjqPUIrVqxwOp1vvvkmiW36CAYG9/z581artWfPnmjLWLJkSffu3UeMGIG2jK1bt44ZM0av1zOt6yWY4tSpUz179kRdxV/Onz9fVVWFuoo/VVZWZmdn19fXoy6ENEzocRsbG4OCgjZs2JCdnS0QCFCX46OuX79eWlo6fPhwp9PpCxPfHhHtg5uXlxcbG5udnY26kLvt2bMnKioqOTkZdSF3e/HFF/v16+eDe+yB0Pt02Llz53wztQCA4uLiykpfXDXs3//+t6cwi8WCupaHR8seV61Wv/POO6tXryYIomUlWl9TU1MjFosDAgJQF3JPN27c2LJly8KFC1EX8jBo2eN+9tlnnrOzPptaAEBERIQvpxYAkJiY2Llz52+//RZ1IQ+DTj1uUVFRSUnJc889h7oQrxQUFMTFxXXr1g11IW1wu91sNjsvL+/5559XqVSoy/EWbXrcurq69evX++ZwtlW///47LW4x98xFHjVq1OzZs1HX8gBo0OPu27evW7duQqFQLpejruUB3Lx5Uy6X06gP8ygsLAwNDU1KSkJdSBt8vcfdsWPHkSNHQkJC6JVaAEBCQgLtUgsASE1Nfffdd2/evIm6kDb4bo9bWFiYmZl548aNxMRE1LU8jC1btiQmJj7xxBOoC3kYVVVVYWFhpaWlXbp0QV1L63y0x122bNmNGzc8R76oa3lIpaWl9fX1qKt4SDExMXw+/5NPPjl06BDqWlrncz2up4u9cOFCSkoK6loeydWrVwMCAsLCwlAX8kiKiorS09Pr6up87R/iW8FdvHhxRkbG4MGDUReC/Y+FCxempKR47jD1Eb4yVLBYLFVVVenp6YxJ7caNG0+dOoW6CnIsW7asqanJc9IXdS1/8ongrlu37vbt21FRUVlZWahrIc2NGzc892Yyw4wZMwAAGzZsOHbsGOpagE8E9/z580ajsX379vRalaNN06ZNQz6ZnXTPPffctm3bzGYz6kKQjnFv3rwZGxur1+sDAwNR1YA9BKvVWl5e/hC3TJMIWSd39erVf/7znxwOh6mp3bRpE2PGuHcRCoXR0dG9evVCODESWXArKyu3bduGausUKCsrY9IY9y5SqfTXX38tKSnxHLRRD0FwFyxY4FnogPpNU2nixImedW2ZSiAQdO/eXa/Xb968mfqtUx3czZs3Mz6yHp06dQoPD0ddBXSxsbH19fVlZWUUb5fqg7Pq6urIyEgqt4jKnj17IiMju3btiroQKlRVVcXExFC5Rep63Pnz51dWVvpJaj33nFVVVaGugiIxMTF79+6lcrlpinrc9evXDxo0KCIigoJt+YjCwsLw8PDHHnsMdSHUOXXqlNVq7du3LwXb8q25ChjmJehDhfXr1//www+wt+KDzpw5U1FRgboKBBYtWnT27FnYW4Eb3EuXLrHZ7IkTJ0Ldim/atWvXxYsXUVeBwHvvvbdz507YjwbCQwVYduzYERcXR/dZxT4LYnB37dqVkpISFRUFqX3MlxUVFfF4vNTUVEjtwxoqHDly5PDhw/6cWr8d43qkp6fPmzfPZDJBah9WcCMiIvLy8iA1Tgt+O8Zt8fPPP+v1ekiNQwmuXq+XyWQMWMvyUQwcOBDtxD/kFAqF3W53OBwwGocS3DfeeKOmpgZGyzSSkZHRrl071FUgdvDgwe+++w5Gy+QHV61WCwQCeKNyujhw4MCVK1dQV4HYmDFjII2X8OkwWHzkGRBMRX6Pe/XqVX97PGer8BjXo6Kiora2lvRmyQ/uO++8g4OLx7gtioqKNm7cSHqz5Ac3NjaW4qmZvungwYMlJSWoq0CvS5cuMBa4xmNcWPAYFypygvvyyy9rtVoej+d2u5uamuRyOZfLdTqd33//PRlF0skzzzzjWePfarXy+XzW/+dvu+KFF16w2WwEQdjt9ubm5oCAAIIgLBZLfn4+Ke2Tc42gT58+n3/+uec55Z7Vwz2P/iOlcXphsVjXr1+/8xW3203uw1lpoVOnTps2bWp5SIfnvH5ISAhZ7ZMzxp04ceLf78mh6dKwj2j48OFCofDOVxQKRW5uLrqK0MjJybnrhheCINLT08lqn7SDsylTptz5VEe5XD5p0iSyGqeRcePG3XVs2qlTJ99/hAnpQkJCBg4ceOe3bmhoaE5ODlntkxbckSNH3tnptmvXLjMzk6zGaUQoFGZlZbU8NF0mk02fPh11UWhMmjSp5QZ9giDS0tJIPD9I5umwyZMnezpdhUJB4t8W7YwdOzY6Otrz38nJycxeFuQ+PJ2u57/DwsKmTJlCYuNkBnf06NGeTjchIaFPnz4ktkwvIpFo5MiRXC43KCiILk9lg2TSpEmxsbEEQaSmprZv357Elr06q+B0uJtNXq3omz3uubVr1z4zfrpR52zzzQRBSBVcNsd3nw75d3ab22Zpe1cMHThm90+H4+Pj28V1aXNXEG4gD6LZFFBbs9tubXs/iPlBfTOePtB8IHvcc15Fwk3Ig3jeFNDGedySYsMfR/XaOrtIyvGmuQfCFbD1antEvKhrH0VCFynp7ZPrj6NNF47oXU6C9MewiuWchipbTAdxav+AqMfEJLdOtjMHtJdPGngCtjfBfVDyIF7tzeb4xyXdBypDY4T3eef9glu8X6upcaT0CZQFevVH8HAMWvvpXzSPpUg691TA28ojKtyutluJjj0D5IF8SJvQa+wndzak9g9ITPbdv+Ff1tdJA3mJyXJpAKxIuN2EodF+dHt95pjgqMdE93rbPYNb9IvW0Oh8cjhpZ4zv78i2utiOoi5P+WJ2f9umZvHYqf2CKNjWgY3VyRmKdim+mN296+oCwwWdnlRSs7nd39zKGK2Katd6dls/ONM12DXVNspSCwDoMyHsxu9mm8VF2Ra9VFvebLO6qUktAGDglIjfj6JZcfb+Kq6Y+SIOZakFAAyYHH7ukO5eP209uJpqG0FQfczkdBCaGjvFG22TptpO5eEji8WymtyNtTbKtuilhls2noDSRWmFEq76ts1saP2QrvVSTHpXcPT9hsYwhMWL9BooN9Y9CrPRqYqkdFdEthM3NfjcfrBZXKpwgRdvJFNMB4murvW+rPXgOmxuB4Rjxvuzml1Oh8/Ny7FZ3A4bpVWZjU63z42YgNngclL+12TUOQjQ+tcdo57QhPkPHFyMlnBwMVrCwcVoCQcXoyUcXIyWcHAxWsLBxWgJBxejJRxcjJZwcDFaIi24I0b1XfPvz8hqDWOeCdlPr/j0fbJawz0uRks4uBgtkXlz6c2b1+e8mnv9+tXg4NCJE6aMGD6WxMbpZc/en7YX/LeqqkIqlfXqmfnCjFcUCvKX2vRxLpdrw8Zvdu0usFqbU1LSbFYriY2TGdyyG9eyJ04d0H/o/gO7V3z6vtXaPGG8Py4Lsm79V+s3fNO3z8AJ43J0TdrTp09yODS7+5wUn6/8cOeu7U8PHdk1ObX49AmjyUhi42Tu0MGDsp7JngYAGDF87JxXc9et/2p41liR6J43ajKSWt2wafPaQYOGvb1gqecVzz7xN9euX925a/uUnOdzn38JADBkyPALv5P5ZGooY1wOhzNqxHiLxVJa6nePnTl7rsjlco0aMR51IYgdPXoYADD+jq9cNpvMsME6OAtSBQMAzGZYT8T0WVptIwAgODgUdSGI1TfUSaVShRzWegOwgtvUpAMABAZSdFe375BKZQAAra4RdSGIBSiUJpPJbod12za8h1AflMnkiYlkrnNGC91S0gAAe/bsaHnF6Wx7zSzmad++IwDg0OFfILVP5sHZvv27AgODhEJRUfHxkyePzp3zJp8Pa8EinxUdHTs8a8zOXdsNBn2PHj31+qadO/M/+/Sb0NAw1KVRql/fQRs3fbvi0/fLy2881i7p8pU/NBo1ie2TFlw+X5A9ceq+/btu3aoMD4+c/8aiYU+PIqtxepn3j7fCwiJ27dp+/MSRYFVIjx49/fB53BwO58MPvvj8iw9/3vmjRCLtkzmA3DPZpO3Q/G37AAATJ5C5eC9NsdnsnMnTcyb76ULkLcLCwj9Y/tf0lblz3iSxcXzJF6MlHFyMlnBwMVrCwcVoCQcXoyUcXIyWcHAxWsLBxWgJBxejJRxcjJZwcDFawsHFaAkHF6Ol1meH8YUs9z2edgKPSMLh8X3ugdRCCYcvoLQqiZzL9r1ZkBIFlwPxwbitkyl5rHt0ra2/LFPy1JXNcIv6m+obFkUw5fumLRIFp+EWmQsCtOlWqTkw1Ocm4IskbE011Y8NrLhiCgprfVe0HtyQaAHpzwhvE5fPComm+hFwbQqNFrhd1D3yzeFwS5Vcpe8FNzRW6LBR+vg1c5MjIl4kknJa/ek9e9zIdsLC/DrItf3l4Obqzk/KuTyfG3MHRwnlgbyiPQ3UbO7A+urU/tQ9L9d70e3FLBY4f5i6m0APbq7pMfSeu+KeT08HAFw+qb9+wdS1T5AylM/hQomUw+ZuUtvO7G/sMTggvrMvPjLc48wBbX2VreOTyqAIAZtN/peRrdmlV9tP7Vb3mxgckeC7S6gUblc7HERisjwoAtZjYq0Wl15tO1bQMPyFcFXEPb+B7xdcAED5ZfOFI0115VYO16vfFgGA2+3isFvv3u/CF7FtFldUe3G3vgG+/NvyuHbOeOFIk1HrdDm9ekKqm3ADwGJ7MeSSBnBNemdsB3H3gcr7/Kp8xKWT+ssnDDaLy2rxagRFAMLtJjjerQaiDOXp1Y74xyU9BgfKg+53wNNGcFvYmr2q0mq1jh49+pdfvLspmSAEYq8i7kMIYPPuKcf/+te/UlJShg4d2naTBCGk234gCGD3bj9cu3bt448//vrrr71q1g2EEq8i7u15F4HIq+bcgOVwWbx8My2xvN0VBMvO5rqYuitYXu8HLp9wEVbS9wMzdyvGeOQHNykpifQ26UihUPB4PndamnpsNjsyMpL8ZsltjsViXb16ldw2aUqv1zscDtRVoOd0Omtra0lvluyRB5udnJxMbps0pVKpBAJfP0VAjYSEBNLbJDm4fD7/4sWLzc1UXy72QRqNxmaj+hqpD9LpdE1NTaQ3S/4Y94knnjAYDKQ3Szu4x/WwWq2JiYmkN0t+cG02W3l5OenN0g7ucT2uXr0qlZJ/TZT84CYkJNy8eZP0ZmmHz+ezqJ+p5Htu3rxJgzEuACA5ObmkpIT0ZmnHbrd7eVWS2crKyrp06UJ6s+QHt2fPnoWFhaQ3i9FRaWmpVCoNDAwkvWXygyuVSlNTU69c8bvn7dxFpVL54YLsdzl//ny/fv1gtAzlkm9mZub27dthtEwjGo0G3qM76GLr1q1DhgyB0TKU4I4ZM2bPnj34mNrPnThxIioqKiYmBkbjsCbZPPvss/n5+ZAapwU8V2Hnzp2TJ0+G1Dis4L7wwguffvoppMZpwc/nKpw/f16tVvfs2RNS+7CCy2az33jjjby8PEjtYz7uX//614IFC+C1D3E+bnZ2dlFRUUVFBbxN+DKBQEDu02tpJD8/v2vXru3atYO3Cbh7dunSpatWrYK6CZ9ls9ncburua/cddrs9Pz//7bffhroVuMHt3Llzt27dVqxYAXUrvslvr/dOnz598eLFsLcC/bssJyentrb28OHDsDfka/zzem9eXt7IkSM7dOgAe0NUDMI++uij77//Xq/XU7AtDKHCwkK3252dnU3Btig6evj2228HDBhAzbZ8hFAo5HBodtP5ozh79uymTZugnkm4E3WHvYWFhb1796Zsc8hZrVaXi9LFthAqKSn59NNPvVw8gRTUBVcsFhcUFEC6co0hdO3atYULF27atInKjVJ6olGlUv3www8zZ850Op1UbhcJP7nke/LkyQ0bNlB/eZ/qM+QKhSIvL++pp55i/IUJf7jku2vXrm+//XbZsmXUbxrBpZ2AgICioqLXX3/92LFj1G8dI8uaNWtOnz793XffIdk6smuS+fn527Zt++GHH1AVABuz7/J9++23eTzeu+++i6oAlBfTP//88+rqagqusiDB1Lv02FTFAAARpElEQVR8jUbj3Llz+/TpM2PGDIRlIJ4FMm/evPT09OHDh9fVUbf6OfbQjhw5MmLEiFdffRX52SH0j3fJyspKTU3Nzc196aWXsrKyUJdDGubNDluxYsXt27d/++031IUA9D2uR3h4+O7du4uKit577z3UtZCGSbPDrFbr1KlTQ0NDfWe+lE8E12Pp0qVdunSZO3duWVkZ6lpIEBQUxIyDs3379r322mtvvfVWTk4O6lr+gn6ocKfRo0f36NHjtddeGzRoENqx/6NrbGxkwMHZggUL2Gz26tWrURdyNx/qcT0iIyO3bt3qcDhycnJofcQmk8m4XN/qFx7I8ePHn3zyyQEDBrz//vuoa2mFj+7Z2bNn9+vXLzc3d9q0adRMkyOd0Wik75Xt5cuX19fXHz161GevWvtcj9uiQ4cOu3fvrqysfO211+jY9UokEjpOazx58uScOXM6duy4cuVKn02t7/a4Ld58880LFy7k5uaOHz9++vTpqMt5AGazmXbTGhcvXqzVapctWxYQEIC6ljb4bo/bIiUlZffu3WazecKECTR6wIRKpRIKYT1+kXS//PJLWlpaenr6qlWrfD+1NOhxW7zyyivDhg1btGhRWlravHnzUJfTNo1GExsbi7qKthkMhoULF8pkstOnT9PoBk9vnyzpOzZt2vTrr79OmzatT58+qGtpxdixYysrK1vu8iUIgiCITp06UTzP2ksbNmw4c+ZMdnb2U089hbqWB0ODocJdpkyZ8vHHH//000/z5s1rbKTuYd5e6tu3L4vFaum6WCyWUql8/vnnUdd1t3Pnzo0dO1an061cuZJ2qaVlj9uisLBw2bJl2dnZubm5qGv5S319/UsvveTpdD26d+/+1VdfIS3qfzgcjvfee6+2tnbhwoW0GMy0in49bovMzMz9+/fbbLYRI0acPXsWdTl/Cg0NvXMpY4VC8cwzzyCt6H/s2rWrd+/e6enp33zzDX1TS+/gerz00ktfffXV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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ffe8e4aa-55c1-43b4-ba64-74583359a35b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"E\" to ['A', 'B', 'C']\n" + ] + }, + { + "data": { + "text/plain": [ + "{'aggregate': ['A', 'B', 'C', 'E'], 'which': 'bc'}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"aggregate\": [], \"which\": \"bc\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3bc6b3b4-a0c8-471a-9029-ddf2472c385c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adding \"A\" to []\n", + "Adding \"C\" to ['A']\n", + "Adding \"D\" to ['A']\n", + "Adding \"E\" to ['A', 'C', 'D']\n" + ] + }, + { + "data": { + "text/plain": [ + "{'aggregate': ['A', 'C', 'D', 'E'], 'which': 'cd'}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"aggregate\": [], \"which\": \"cd\"})" + ] + }, + { + "cell_type": "markdown", + "id": "639a3653-0fa3-4b6d-bf53-63479c6b00fe", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "- Continue with the [Graph API Basics](../../how-tos/#graph-api-basics) guides.\n", + "- Learn how to create [map-reduce](../../how-tos/map-reduce/) branches in which different states can be distributed to multiple instances of a node." + ] + } + ], + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/how-tos/branching.md b/docs/docs/how-tos/branching.md deleted file mode 100644 index 8cbdf939d..000000000 --- a/docs/docs/how-tos/branching.md +++ /dev/null @@ -1,286 +0,0 @@ -# How to create branches for parallel node execution - -
-

Prerequisites

-

- This guide assumes familiarity with the following: -

-

-
- -Parallel execution of nodes is essential to speed up overall graph operation. LangGraph offers native support for parallel execution of nodes, which can significantly enhance the performance of graph-based workflows. This parallelization is achieved through fan-out and fan-in mechanisms, utilizing both standard edges and [conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.MessageGraph.add_conditional_edges). Below are some examples showing how to add create branching dataflows that work for you. - -![Screenshot 2024-07-09 at 2.55.56 PM.png](data:image/png;base64,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LyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj41NDI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+OTA0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClCweXwAAAAcaURPVAAAAAIAAAAAAAABDwAAACgAAAEPAAABDwAARV8dcCWfAABAAElEQVR4AezdB3wURfvA8UcgdAgdqaElNAEVFBSk96agf1REFJSiooAFRXxtiAooShHEBkhRULEjRTpIB0MTCC0JJEBooZcA/5mJt97mLqSQS+4uv30/8XZnd2dnvjcvnzyZ2ZmbrqlN2BBAAAEEEEAAAQQQQAABBDK9wE0EiJm+DQCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQaAgIIIICARwXOnDkjW7ZssT2jWrVqEhgYaEvjAAEEEEAAAQQyXoAAMeO/A0qAAAII+LVA+/btXQJEXeHy5ctL9erVJTg4WJo1ayY1atTwawcqhwACCCCAgC8IECD6wrdEGRFAAAEfFnjooYdk1apVSdbgrrvuMoFi8+bNTfCY5A1cgAACCCCAAAJpLkCAmOakZIgAAggg4Cxw8uRJmTRpknz88ccmOTw83HwGBQU5X2bb79q1q9x7772mVzFPnjy2cxwggAACCCCAgOcECBA9Z0vOCCCAQKYRuBAncuLsJQnbFykHDx2Wc+cvyN6wXbJ31zY5Gn1AjkRHysljMSn2qF27tjz99NOiexXZEEAAAQQQQMDzAgSInjfmCQgggIBfCcTFxcmmTZtk2fKVsnNvhOyPiJDDB1UAePSQx+rZqEkz6fZIV2nZgkDRY8hkjAACCCCAgBIgQKQZIIAAAggkKbB06VIJDQ2VjRs3yoqVK+XypUtJ3qMvuClLFilYtKQULFbC+syTr6DkzhsoOfPmV5/xP7nUca5/94+q3sZdm/6S3VvWyPpFP9ue065rHxnw/IsSUjS7LZ0DBBBAAAEEEEgbAQLEtHEkFwQQQMDvBJYsWSKLFy8W/bl///7r1k8HdyWCguXmoBApG3yLlChf2QSEBYoUv+591zs5dlBXCQtdI2WCq0tk2Dbr0oo17pBXBr0kLRvWtdLYQQABBBBAAIG0ESBATBtHckEAAQT8RuCnn36SyZMnm2Gk16tUtTsaSdU6jaWSCthKVax6vUtTde6PaWNk9+bV0qRzLzV8NVp+/uJ9uXj+rMkrW7YAeemlF6Vv376pypubEEAAAQQQQMC9AAGiexdSEUAAgUwnEBUVJYMHDzY9holVvlDx0ipge0J0cFi0VOKzkCZ2/42knzp+VOZ9M06W/zLVyqZpq3Yy6bPx1jE7CCCAAAIIIHBjAgSIN+bH3QgggIDfCOilJVaq9wsT2+q2vF9ad+svhYuXSuySdEn/e/k8+XrEQIm7dNE8r0HztjL9ywnp8mweggACCCCAgL8LECD6+zdM/RBAAIFkCLz66qsyffp0t1fqXsM2KjCs27Kz2/MZkbhn63r54q2+cvbUCfP4O5u0lZmTJkiWmzKiNDwTAQQQQAAB/xEgQPSf75KaIIAAAqkSGD9+vAwfPtztvd7Sa+iucDEHw2Xc4EflxOGD5vRtDdvK5M/HS4GcRInuvEhDAAEEEEAgOQIEiMlR4hoEEEDATwV+/vlnee6559zW7pEXRnpVr6G7Ql44d1ZGDbhfDoWHmdM6SPxpKsNN3VmRhgACCCCAQHIECBCTo8Q1CCCAgB8K6AXvO3bsKNu2/beEhKOaDw94T+5q08Vx6PWfHzzXSSJ2bjblvLtZW/nmK4JEr//SKCACCCCAgFcKECB65ddCoRBAAAHPC+ilLN544w2XB3V84mVp3qW3S7q3Jwx/qp0c3LvDFPPlt4bL048/5O1FpnwIIIAAAgh4nQABotd9JRQIAQQQ8LzA2bNnpUOHDrJnzx7bw5o/2Ec69hxkS/OVg9MnjsnHL3SRmIP7TZG///1PueOWYF8pPuVEAAEEEEDAKwQIEL3ia6AQCCCAQPoKTJw4Ud59913bQ+u37yoPPjvUluZrBxuXzpHJ7z5ril2hai1ZPPcXX6sC5UUAAQQQQCBDBQgQM5SfhyOAAALpL3D8+HHTe3jgwAHr4bc3bi+PDx5tHfvyzsyxr8vK3+KX7OjS7QkZOex1X64OZUcAAQQQQCBdBQgQ05WbhyGAAAIZLzBmzBj58MMPrYLkypNPBn70vdwcVMlK8+WdQxF7ZMKrj8uJmChTjdeHvidPdO/qy1Wi7AgggAACCKSbAAFiulHzIAQQQCDjBa5evSqNGzeW8PBwqzBtuw+Q1o/ED8u0En18Z9nPU+X78W+aWhS7uZQsXjhf8ubN6+O1ovgIIIAAAgh4XoAA0fPGPAEBBBDwGoEFCxbIk08+aZWnZPnKMvDjHyRHzlxWmr/sfDL4Mdm5cYWpzptD35Ue3R/xl6pRDwQQQAABBDwmQIDoMVoyRgABBLxPYPDgwTJjxgyrYN1eHCl3tuhsHfvTzsalv6sJa54zVapxe1357cdZ/lQ96oKAzwksWrRIPv/8c8mXL59UqFBBGjRoIMHBwVK0aFHJkiWLz9WHAiPgrwIEiP76zVIvBBBAIIGAXtqicZMmcuTwYXOmer2m0uetzxNc5T+HV+Li5N1eLSUmKn447cSvpkrrZg39p4LUBAEfE3jzzTdl0qRJbkutA8Vq1apJ1apVJSQkRCpVqiSlS5eWrFmzur2eRAQQ8JwAAaLnbMkZAQQQ8CqBX3/9Vfr162eV6dkR0yW4Vj3r2B93fv7ifVn4XXwQ3LLD/fL5uFH+WE3q5CUCG/edlDnrD8nCjYfl/IU4l1LVCikkbW4vLu3rlJBsWW9yOe/vCe+//75MmDAh2dXMkyePdOnSRe6991657bbbkn0fFyKAwI0JECDemB93I4AAAj4jMGDAAPnxxx9Nee9odp88Oui/mUx9phIpLOi+7Rvlo4H/Z+7Kmi1AFv65QMqXL5/CXLgcgZQJRJ+4IHM2HJJ5KlCMiD7jcnOxwrmkVe3i0qV+aSmaP4fLeX9N0KMYpkyZImFhYXLp0iVZvHix6LTkbE2bNpVXXnlFKleunJzLuSadBc6dOydjx46VXbt2ycsvv2x6gdO5CDwuDQUIENMQk6wQQAABbxbQs5fu27fPFPHxwWPk9sbtvLm4aVa2sYMekbDQ1Sa/Pv0GyKsvDUyzvMkIgaQElmw9Kku2xshf24/KqdOXXC5vUKuYNKtZTBrfUlRyZc9c7+F1795dli5dKvXq1ZPhw4dLTEyM7N69WzZv3iyrV6+WvXv3unjpZXp0jyKb9wicP39eHnzwQQkNDTWF+uKLL6RFixbeU0BKkmIBAsQUk3EDAggg4HsCx44dk9tvv90q+MiftkqOXP43c6lVQaedpT99LT9MeMuk3NOsrUz7KvlD3JyyYReBGxI4c+GKLNpyRJZtOyqrVbAYd/mqLb/cuQOkYc2i0rBaEWlUvYhkzeL/Q1CbqHeidRB4yy23yO+//27z0Af63Pjx4+W7776znRs3bpx06NDBlpZZDy5cuGB6Vhs2bCidO2fMhGMDBw6U2bNnW18BAaJF4bM7BIg++9VRcAQQQCD5AqtWrZKHHnrI3HDrPW2k52vjkn+zj1958uhhGfZkC7l4/qyUDKokq5Yt9PEaUXxfF4hSQ1CXql7FFf8ckw3qJ+FWtFAuubt6YWlcvajcVblQwtN+cxwUFGTqUqJECdNjmFjFdM9Ujx49RP+hy7Hpnsdy5co5DjPtp/Mf/zLC5KuvvpK33or/A5zjSyBAdEj47icBou9+d5QcAQQQSLbA119/Lf/73//M9U+8/qnUqp+5hv98OfRpCV0xz9R/ydotUr54/mTbcSECnhTYpd5R1O8rLlDvKx5TgWPCrVypfNKgWmFpUqOoVC/jP+326tWr1vvAejKa7du3J6y67VgPPe3YsaP1zuJjjz0mb7/9tu2azHjgHCB+/PHH0qlTp3Rj2LRpk9x3330uzyNAdCHxuQQCRJ/7yigwAgggkHKB1157TaZOnWpuHP1HmNyUydYcmzt9nMz5+qP4+k/6Tu5remfKEbkDAQ8KnFaznupAceHmI7J51wm3T6pRqaA0vKWIeWexZMGcbq/xlUQ9SY1e2sKxhYeHO3YT/Zw1a5a89NJL5rx+b3HmzJmJXptZTsTGxkrNmjVNdZ988knrD4Gerr+elKZVq1YSERHh8igCRBcSn0sgQPS5r4wCI4AAAikX6KImEFijJn0oERQsgz+bm/IMfPyObWsWy8TXnzS1eOqVYfLKU918skYvTt4iJ89eTnbZs6o5T3IEZJXs2bKYn4BsN5njALXEQo6ALGqpBc9NiqLfoQtQ+eu/RehPvayDTnN8mjR1UhdBl0OXzXFt/HX6nM7DcZ/K499rdXr8uf/uvybqf9dE/VwT1TklV9X+Vb3vSLM+49N0D5bzNfoefa/Ow3Gf/ow/Vmnmen2vc9p/+ZvrVIbxeTqu/7c8Jk9H2eLzcJTLyl/tXFGJugwHjp2XXQfPyL7os3L6jOvENlmVSdkSeaV+1cLSo1k5yZPD99YK1AGGXvPQse3Zs0eyZcvmOHT7+ccff0jfvn3NucTeW3R7ox8nOjvec889Mm3atHSp7aJFi0QP+3W3ESC6U/GtNAJE3/q+KC0CCPi4gPo9UXYcPC071c9SNbvhOvX+UfHCOeWHV+7yaM26PNRV1qxaKUFVbpUXRv/g0Wd5Y+b6PcTXH7nbFK1F5+7yxUdDvbGY1y3Tk59slK273fcsXfdGTvq1QFDJvFJb9Sx2vqukVLo5r8/UVc98WaVKFau8eoipHmqa2Hb8+HF59NFHZevWreYSPZOpntE04abzXbFihezYsUNOnjwpgYGBot9xrFOnjjWkNeE93nwcFxd33cDZeahuhQoVzNIh6VUfPdtsVFSU6cHs1auX9d0QIKbXN+C55xAges6WnBFAIJMLxJ6LU8HgKfVzRvUGnJawqDMSoX7cbas/auYuOc3SnujVV/6c/4eUKBcigyf+kWb5+lJG/3ukvsQePSQhterKgl9m+VLRTVnX7j4un83bp4LEkz5XdgqcPgLvP1FLLZdRJH0edoNPuXz5slSqVMnKRQcbOphLuOkAacGCBTJkyBDbJDV6cpRmzez/buqA8IEHHjDrLCbMRx/rtRSHDRsmJUuWdHfao2nr1683ywzlz59fWrZsKTfddFOiz9PrROqZW7///nurznpIrV4H8rbbbnO5r1q1ata7mckZquuSQRok6N5Lx3BTAsQ0AM3gLAgQM/gL4PEIIOD7AucvXZE9h8/KbhX87VWfew+dkT3R5+TESdcJJ9zVNleubPLAPaXlmTYV3Z1Ok7SBLwyS2d/PlMI3l5E3pixJkzx9LZPRLz4ke7ask7KVa8qcX3+RfDkS/wXNW+sWHnNOnv9ysxw8clayqF8w9e+Y+hdN50+TroZg6nT1YX3qOunrsphz6tPpPufrblIHtuME+djvj3+2zkvfo8+pgZT/PlOn/Vc2x3lH3uqUOe84dpx3l27qooaXOp6T2C/XOv3KlasSp4d6qh89ZDN+X9S+Sr8SPwTUpKvr9PDOOPWpLou/1kr7dxioGnaq71PJZuhn/P067/hhqFd0fvpZegip+o++Vn+aYaM6/d+f9GpPL3SpIv93V6n0etwNPce550tn9OKLL4oeLlmoUCEpUKCACTZ0L+CGDRusIMnxwDZt2sinn37qODSfZ8+elYcffthai892MsGBXtBdT3iTcNOTruj3HCMjI+XUqVNSsGBBKVKkiFSvXt30XgYEBCS8xXase9O+/PJL0T15Xbp0Ecf1CWf67Nevn/UupS0DdfDjjz/KgAEDEiZbx7reuv7Om17CyDHD6759+9T/D9X/WdJ5cw5Sp0yZIo0bN07nEvC4tBQgQExLTfJCAAG/F9gWecr0BDoCwf2Hz8vR4+eTrLf+xTlXzmxy9lzi7499PaiuhKj3ijyx9Xuuv/z680+Sr0ARGTZzjSce4fV5jux3r0SGbZXsOXPJ+s3/SKAPBohej0wBUy1wOe6qXIy7Jhcux6kfta9+Lqg/Pl1S6TGxl0T3IG9SvccH1R+hnLfc6g9MpYvnMcNM+7fz3B+ZnJ+ZVvuOZS5Skp/uSdMBSM6c9kl69NqII0eOtGVVtmxZ0T120dHRVgDluEBP6KIn79J/VNAT5gwaNMgEZ47zCT/1tXoY5fU2PYvoRx/FT4blmFF03rx50rt3b5fbdCBat25dW/pnn31mejhtiW4ONm7cKIULF7bOaBNdR73t3LnTZrNavXuuXXTgqK975513TBBu3ZxGO87fpZ48SD+LzXcFCBB997uj5Agg4GEB3Su4NTxWtkWeln9UYLg74pTpGUjqsTlzZpUg9S5QJfVuUME8AebeDTuOJ3qb7kHs0qisPNWqfKLX3OiJHj17yqKFC1VwlFs++HnLjWbnk/cPe7KlHI7cI7ny5Jft27aYHi+frAiFzjQCi7bEyKyVB+Tvna7/ftRVQ0n10hdNaxST/OrfEF/cnIOK5JZ/+vTp0qBBA9vlehjqnXfeaQWB+l3GX375xRrCqif+2bZtm4wfP15+//13617dQ6l7Mvv06SPLly+30h07OsDUPZOO3rl27drJJ598YoJKxzXOnxMnTpR3333XJA0ePFhatGghHTp0sIZ/Ol+b8B1KHVTpINV50xPx6OGo69ats5Vv1KhRcv/991uXNmnSRPbu3WuOnYfqfvPNN2ZYqnWh2tGBmzZMakIg53uS2tf+FSv+98eJX3/91ZpZNal7Oe+dAgSI3vm9UCoEEEhHAT3EbGfUWTVxzCnZpYaJ7lYzB+5SweAl9df7pLZihXNJsFqnLKRUXqmsPiurzxIFcprhapMXhcuMxRHX7TVsX7+UdGtYVsoVy53Uo27o/INqFlP9l2S9jZm354by8tWbX+92j5yMiZJipcrJur+W+mo1KLcfC8TEXpQ5G9WaiH8fMX+QSljVkHKB6h3DoiowLCLliyU+oUvC+7z12HlYYkrKqAOfu++On3RK37ds2TIzBNSRh37PsFs39zMV62Gkq1atMkNIn376aXNdaGio41Z59tlnpXPnzqKDQx1EJewBdPfuo+Nm5/VmdQCoJ8txBJf6Gj1ZjqOnTx/rdw2zZ89uevcSDsnUdXjooYesQE73Xs6fP1/fJkOHDpXu3bubff0fHbg6Ju/RwWTRokVNT+Tnn39uXeO8M3z4cJO3c9qN7OvhuDVq1LCy+PPPP21LmFgn2PEZAQJEn/mqKCgCCKSFwLmLV+To6UsqCDwj8zYdNu8NJhyyldhzyqngL7hkPqlSOj4QrKICwrxq2GjCbb765W7qknAJCz+V8JR1rAPL1x+uKnUqFrTSPLnj/MtF36FfSrU7G3vycV6Z9+D/qy1nT52U4Oq3yZ9zfvLKMlKozCewbPtRWa5+1qm1Dw+pd0wTbiVVIHhX1ULSpGZRqVMhff69SFgGTx07D43UvWU6qNNDQnVQdeTIEfMeog42dC9ews15IpTJkyfLG2+8YS7R7//pSW2S00Ome+yc11LUa8U2bNjQ9qg333xTJk2aZKXppTnmzJnj9j0/5yGm1g3/7ug8dB3vuOMO69TcuXPNUh/6ncgPPvjAStf7//d//2cd6x3nRelnz54ttWvXts7ra9euXWuOdU+oDmKdy2xd+O9OWs92evjwYdOD63jOX3/9JaVK+ca7sI4y82kXIEC0e3CEAAJ+LPDJH3tk6vz9SdYwm1psraIOAtVPFRUUVi0TKFXVZ1LbTtX7qHsNF6vFrq+3lVC9hd+8UFdyZk+/iQT0X5L1uyd6a9DhUenS702zn5n+83yHahJ36aLc0aCZfD/9q8xUderqRQLH1B+oVu86pn5OyDo19PzkqYsupSukRiE0UENI29UpLrWCCric95cEPVGMo/cusUXe9RBQ/V6d7p2bMWOGVXU9jFT3lunPESNGmKGf+qTuidPvKCa17d+/Xxo1amRdlrBXTp/QS2bUr1/f1guo0/VQVd1rl3B7/fXX3T5bT1zTvHlzc7l+H1H3SurNEZA69wB27dpV3nvvPXM+4X90oJw1a1bbO4b6Gt2buHRp/KgIXf8lS5ZYt+qeTB086xlRHcNf9Undi5pWs7nqmVOdA2sdrBYvXtwqAzu+J0CA6HvfGSVGAIFUCjz5yQaXJQKKFMol5YrnkgrqnUE9RLSqCgorqAkfUrLpWUwnqcDwWzWcNKlhqXnyZJeh3arJ3VX+m2AgJc9K7bX6lzDHrH2FipeWN7/OXEMsz8SekFe71DF8be/rIhNG2yezSK0r9yGQHIHtB07Lml3HZYNaxzJU/VxWE9Ak3LJnzyr1qqv3CvUQUhUc5lTH/r498cQTonsI9ZbwnTx3dXfuRdPnHUGdXgLDsUC8fmcvsaGVznnqSWJeeuklk6SXxvjwww+dT5t9PRRTB4MJNz1UVAdhCSfK6du3r/zxh30ZIT07qx626tich6E6egqdZyFNbIZVx/3uPp17EJ3P63Uj3377bdPbmXDdSXezoTrfm5J9HcBrd8emZ57Vs7+y+a4AAaLvfneUHAEEUiiwZGuMbFeTzZRUQaGeQKaiCgRz3eAvYb+tj5apKjAMT2R9Q90bmUM9wzF7ad+OleTxJkEpLHnaXO78S0j/D2dKxVviA6a0yd27c/lrzrfy7eghppC9eveV14YM9u4CUzqfF1itAsKl22JkQ9hJiVBD2hPbbq1cSBrpwFBNOHOz6jnMTJueyMXRK6gXsv/hhx+SrL6eTVT3vOnNEVQOHDhQ9LBLveleQR2EJbXpwE8HgHp7+eWXRb+P6LzpXkjdI5jYppei0M913u677z4zFNSRpt+T1IGr7vVzbCtXrhTdS6i3xx57zARwzmsI6sl29LNz507+e+mtW7eWf/75x/EI8+luMhrnVw0S67G1ZZLMAz0xjp6Mx7HpP0jqpUrYfFeAANF3vztKjgACGSiwWc1u+rUKDFeEHkm0FG3VemT71Uyo2/eeNNe0rldS3nywaqLXe/qE8y8HrR7pJ+2623+58fTzMzL/CUN6yD/rl5kiOL+7lJFl4tkIZHYB53fv9LINevmGpDbnYamO4Zg6uHPMTprc9+uc31vUw1R1UBYcHCx6ZlMdPDqGbOry6PcOda+k7iF0TAaj05966ikz86hj3cGEk+7oSWrKlCmjL7U25/f19PN0D6ruCdX/Ljk2HSy///77yZ7oxfmPfzoPXZ/Fixe7DPPUs5e++uqr5jHJdXKU6XqfusdQT+zj2PSMsXnzJv1ahuN6Pr1PgADR+74TSoQAAl4sEHsuTg0n3S+zVHCoF8F2t+np5x9pVEbmbDgsc1dHmUtqhRSUcb1ulQDVo5hRm/MU7KUrVpPnPvhWcuZO2XDajCr7jTz3RMwheaNbfZNF8RKlZO3qv24kO+5FAIFUCMTGxpoeQr0cQ0hIiJlkZc+ePbbhl7t27ZIcOXK4zV3PlKnfNXT0HuqLHLNxOv/xS6frIC9Xrlx6N9FNzyaanLX6dI+efodQT55z6NAhadu2re2dRP3+oH5nMDAwUJyX7dC9i4kteO88Oc+aNWvkwoULJt+Ek/Hcdttt0qxZM7nrrruMmS5Dwk271qxZ05asg139LmLCTU/+o4NJx5ZW7wrq4ba6N9SxJVyL0ZHOp+8IECD6zndFSRFAIIMFflwTJVMXRUjUEdcZ9XTRQoLyy8NqyYo2txeXMb/vlhl/hpsSl1ST0ozufauUUTOXZuS2fv1629pZzR/sIx172tfdysjyeerZK36bIbPG/s9k/9TT/eSVl+PfO/LU88gXAQRcBZzfE3Sc1b2GzstAjBkzxizRoIPEK1euyIkTJ+TgwYOi/+3SQYhzAKV7rPR6gHqh+4Tv/iV3Aha9gPy4ceMcxXH5bNWqlegyOb9rqAPc9u3b28qie+x0D+Zvv/1mZiPVxzr4SqwXbfTo0abs+oGOCW90UKt7RJ09EhZIv/uog0sd5OllJfSsqMePH7fNIKqHqzrex0x4vz52ntAn4XIh7q5PTprze5X6eh34J2cW2eTkzTUZI0CAmDHuPBUBBHxIYP2eEzJFTUKzbvsxt6XWMw52bVxGuqnF7vX21cJw+ey33WY/R46sMvLJWnJnJe+Ynl7/9VsP/3Fs/YZPk5Bb73Ic+uXnxNd7ybY1i0zd3A358stKUykEvEygR48esmhR/P8Pb7RoOkjSPYl6DUG96fUBdS+i3lIydPLatWvy3XffWZPVmAzUf/SQ0ueee070u32O4aOOc/pTv++n6+NY01AHhHotRj0xi14Co0qVKqYczvc471+8eNHMghoVFSV6qQu95qLedEA8YcIE0aM9krPpOut3Mh3vZepyOOfnLg/d46h7JHWwrZfl6NSpk7vLUpTmvFakLsP27dtTdD8Xe58AAaL3fSeUCAEEvETgSOwFmaKGkv6wNDLREj3cLEgebVxWCuWN/0Xl2xUH5OMfdlrXD3mkmnSoU8I6zuidhH/p1cGhDhL9dftn3VKZ8FpPU732He+VT8aO8deqUi8EvFrAudcstQXVgVvPnj3NhCgJh5DqWUl1sKcnnNHv8KVku3z5suhlLy5duiTlypUz7/Aldb8eFqp7NU+fPm2Gc+rF6VOy6SBR97I5T2DjuD8mJsa8m6jfydS9p7rX0t3mPPuqDlp1D2NyJodZuHChWWpDv7up63ujm7bQS2joXlPdC6qX3WDzbQECRN/+/ig9Agh4SODbFZEyfXGkxBw/7/YJreqWNO8ZhpT470X8+X8fkdenbLGu79WukjzRPGNmLLUKkWDH8ZfrsLAw64weZqqHm/rbdjQ6Qj5/s49E799lqqYXjm7atKm/VZP6IOATAnrIqJ74RS9VceDAATM0UvdmnTlzxvzodwx1r5bugdJr6OneOB106WGo+li/C6h/MuOmnfTIDx0E6qGoERERZohp//79JV++fJmRhDp7WIAA0cPAZI8AAr4lsGrncbXY/X4JVYtYu9vuqFZYBYZlpV5IIdvp3YfOyqMj1ogesqS3DvVLyZAHqtiu8ZYDPRvfO++8YxUnZ+68asKab0RPXONP26gBD8j+fzaZKr300iDp1+8Zf6oedUEAAQQQQMAjAgSIHmElUwQQ8DWBA6qncIqagObXlQfcFr1cqbzSTQ0lbe9muOgVNZtp89eWyfnzcebeO9WaZmPUe4feuukhUfpdRP1XaMd2c1Cw9Bn6pRQuXsqR5NOfM8e+Lit/m27qUKFSZVm8cL5P14fCI4AAAgggkF4CBIjpJc1zEEDAawW+XhIhM5ZEykn1zmHCLU/uAOmuhonqCWiyZrkp4Wlz3ObNFXIi9qLZL186n4zpVUuK5nc/VbvbDDIgUc/cp2fwc950kPjqZ3Odk3xyf9oHg2Ttgv8W3E5synefrByFRgABBBBAwMMCBIgeBiZ7BBDwXoEl247K1MXhsm1P/EL2CUvapUlZMwHN9YK9x8eslx37Ys2t+dRENaN61ZQaZQMTZuV1x0ePHjWz6Ol1vZw3Xw4ST8REyxdv9ZXIsK1WlR5++GGz4LSVwA4CCCCAAAIIXFeAAPG6PJxEAAF/FNin1jGcpJaimL822m31WtxZQh5R6xlWUcNKr7e9q2Yr/UXNWurYhj5eQ1rUKuY49PrPX375xbZItaPAFarXlgGjZjkOfeJzy6qFMmlYP4m7fMkqr546fvbs2WaiCyuRHQQQQAABBBC4rgAB4nV5OIkAAv4koN8VnKzWM5yhlq44e+6yS9VqBheUx5oESf2qhV3OJUz4ZnmkjJ4dPzumPvdsp2ATVCa8ztuP9bTwL774oksxi5UqLy+M/Uly5bl+kOxyYzonXLt6VebOGCd/TB1te3JAQICMHTtW2rRpY0vnAAEEEEAAAQSuL0CAeH0fziKAgJ8I6CUopi4Jl7DwUy41urlobunZIkg63lHS5Zy7hB/XRMnwb/+xTj3YNEgGdqhkHfvazrRp02TIkCEuxc4WkEOeHTlDyle91eWcNyTs2LBc5qngcM/W9bbi5A8MlLFjxpi1yWwnOEAAAQQQQACBJAUIEJMk4gIEEPBlgZ1RZ0yv4eIN9nftdJ2yBWSRJ1pXkG4Ny0hAtizJquYfGw/LW1P/e8etae2b5d1u1ZN1rzdf9OWXX8rbb7/ttoiPPD9C6ra63+25jEi8eP6c6jUcKwtnfeby+JtLlJQxoz+WunXrupwjAQEEEEAAAQSSFiBATNqIKxBAwAcFzl+6IpPVUNJv1JDSS2o/4fZA4zJqApogKR6Y/NlGtx84Lf0n/i2nz8S/51ajUkH5/JnbE2bts8effPKJjBgxwm35G3fqIa269pM8+Qu4PZ9eiZv/+lP1Go61TUTjeHb5ChVNcFizZk1HEp8IIIAAAgggkEIBAsQUgnE5Agh4v8Bv66PV7KQREq56DxNuDW8rLo+rIaHV1HIUKdkux12Vp1RwuHX3CXObHpY6uX8dKZAnICXZeP21n3/+uYxQy19cuhi/bIdzgUuUC1FB4rNye6O2zsnpsr9p6RxZPf87+Wf9MrfPa9ailbzxvyESFBTk9jyJCCCAAAIIIJA8AQLE5DlxFQII+IDA5vBY+VoFhitCj7iUNqRcoDzZopw0rFbE5VxyEoZ9v0N+XXnQXJo9exb5vP8dUrmkd0/gkpx6ubtm/fr18s57I2XT+tXuTstdbR+S1ipQLFj0Zrfn0zJxzfzZKjCcJXu2rEs02wEDBsrAgQMSPc8JBBBAAAEEEEi+AAFi8q24EgEEvFQg9lycTFq0X2ap4PCqmqnUeSuohpD2blNBOtVN3gQ0zvc69mevPigjZu5wHMqIXrVSHWhamXj5TlxcnAxVQeLkLz51W9IiJcpK485PyK33tJb8BVMXdLvNWCXu3rxWdmxaoXoLl0rkrv/e90x4fekyQfK66jVs1apVwlMcI4AAAggggEAqBQgQUwnHbQgg4B0CekbRqYsiJEqtbZhw69WuonRrVFZyqMloUrvtPnRGnpnwt8Seih9y+WKXKvLAXaVSm53P3Td37lzVmzhcIvfvdVv23PkKSC0VJN5av5VUrdPQ7TXJSQwLXSOhK+fJzo0r5HDkniRvadmqtbw25FWGlCYpxQUIIIAAAgikTIAAMWVeXI0AAl4isH7PCTOcdO22oy4l6lC/lPRoVk5KFszpci6lCQO+DJXVW+Of0b1VeXlazXqa2bZDhw7JmHHjZf6CBRJzKCrR6petXFP0e4rFSpaXYmUqSsnyIVK0pP2dwHOnT8mJmCg5GRMtx49ESXR4mGxWgeGp467Dgt09qGXrdvLIw11YwsIdDmkIIIAAAgikgQABYhogkgUCCKSfwFHVkzdJzUz6w9JIl4feUa2w9FZBXI2ygS7nUptw//ur5eDhs9KgVjH54PEaqc3GL+47f/68fP/bfJk3b4GsWf6nXLpwPsl65ciVW4qrYPHShQsmMLx43rWnN8lM1AUd7rtfuqnAsF69esm5nGsQQAABBBBAIJUCBIiphOM2BBBIf4FvV0TK9MWREnPcHpgElcwnT6n3DBvfkrbvwuka6iGmx9WyFndWKpT+FfbiJ+6PiJLZc+bL+vUbZPeObckaFprS6pSrGCL1GzSQLp3vlVtvvTWlt3M9AggggAACCKRCgAAxFWjcggAC6Suwaudxtdj9fgndFb/EhOPpuXMHyDPtK8r9meidQEfdvenzxPlrsnNfpGzZslU2h/4t20PXy+6tG1JVxKAKwdK2XXtp0eQeqV27dqry4CYEEEAAAQQQSL0AAWLq7bgTAQQ8LHBA9RROURPQ/LrygMuTHm1ZTno0LSe5c2R1OUdCxgtcuiISFh4lUdHRcuiQ+lGfh9XnEfU+4xn1HmKhQoWlcOFCUqRIYSmmfooXLSwVK5SX4ODgjC88JUAAAQQQQCATCxAgZuIvn6oj4M0CXy+JUMNJI6zZQx1lbXFnCendsryUKZzLkcQnAggggAACCCCAQBoJECCmESTZIIBA2ggs3hoj01RwuG3PSVuGNYMLyjNtK0otteA9GwIIIIAAAggggIBnBAgQPeNKrgggkEKBfWodw0kLw2X+2mjbncWL5JL+HYOlaY2itnQOEEAAAQQQQAABBNJegAAx7U3JEQEEUiBw5eo1NQFNuMxQw0nPnrts3Zk1603ytAoMH2lYxkpjBwEEEEAAAQQQQMCzAgSInvUldwQQuI7A/L+PyNQl4Woyk1O2qx5oXEb6tKwg+XJls6VzgAACCCCAAAIIIOBZAQJEz/qSOwIIuBHYGXXG9Bou3nDIdlYvRv+cWraibJHctnQOEEAAAQQQQAABBNJHgAAxfZx5CgIIKIHzau2DSWo46bdqOOklvQ7Cv1tIUKB6z7Ci1K5Q0JHEJwIIIIAAAggggEAGCBAgZgA6j0QgMwr8tj5apqrAMFz1Hjq2AoE5pJ/qMWxfp4QjiU8EEEAAAQQQQACBDBQgQMxAfB6NQGYQ2BweK1+rwHBF6BFbdXu1qyRPNA+ypXGAAAIIIIAAAgggkLECBIgZ68/TEfBbgdhzcWo46X6ZpYLDq2qmUsfWvn4peVatZxiYO8CRxCcCCCCAAAIIIICAlwgQIHrJF0ExEPAngdmrD8q0xZESpdY2dGx3VCssA9WyFRWK53Ek8YkAAggggAACCCDgZQIEiF72hVAcBHxZYPWuY2rZikjZ8M8xqxplS+RVE9BUkvpVCltp7CCAAAIIIIAAAgh4pwABond+L5QKAZ8SiDh6XqYsDpff/zpolTtXzqzyjOoxfOCuUlYaOwgggAACCCCAAALeLUCA6N3fD6VDwCcECBB94muikAgggAACCCCAQJICBIhJEnEBAggkJhB35ZrpOfx2aaScPnPJuqxbi3LST01Ew4YAAggggAACCCDgWwIEiL71fVFaBLxG4I+Nh2XaknDZE3naKlOzOjfLC/cGS6G82a00dhBAAAEEEEAAAQR8R4AA0Xe+K0qKgFcIhO6PX9dw5eb/1jW8pWIBGagCw+pl8ntFGSkEAggggAACCCCAQOoECBBT58ZdCGQ6gagTF1SPYYTMXhZp1b14kdzybPuK0rxWMSuNHQQQQAABBBBAAAHfFSBA9N3vjpIjkC4Cl+OuytSlETJTvWcYe/q/9wx7t68kPZsFpUsZeAgCCCCAAAIIIIBA+ggQIKaPM09BwCcFflkXLTNUYLj/oP09w5c7V5b8ubL5ZJ0oNAIIIIAAAggggEDiAgSIidtwBoFMK/DXjmMyfVmEWvD+uGVQoXQ+ebFziNxevoCVxg4CCCCAAAIIIICAfwkQIPrX90ltELghgZ1RZ1RgGCnz10RZ+eTIoRe8ryRd7i5tpbGDAAIIIIAAAggg4J8CBIj++b1SKwRSJHDizGX1nmG4fKeGk16+fNW6994GpUUPJ81yk5XEDgIIIIAAAggggIAfCxAg+vGXS9UQSI7ADNVjOHP5ATl89Jx1ec3ggvJipxAJKZHXSmMHAQQQQAABBBBAwP8FCBD9/zumhgi4FZj/9xGZod4z3LEv1jpfsEBO6d8xWFrfxrIVFgo7CCCAAAIIIIBAJhIgQMxEXzZVRUALbNx3UqYviRTnhe51+qMty8kzbSrqXTYEEEAAAQQQQACBTCpAgJhJv3iqnfkEIo+dl6lqoftfVhywVf7umkVlkBpOerPqPWRDAAEEEEAAAQQQyNwCBIiZ+/un9plA4PylKyYwnKXeNTxz9rJV4zI35zHDSRtULWylsYMAAggggAACCCCQuQUIEDP390/t/VzgR7VcxTdqZtKI6DNWTbNnzyI9W1eQx5sEWWnsIIAAAggggAACCCCgBQgQaQcI+KHAsu1HZfrSCAnddcJWu5Z1S8qg+0Ikb86stnQOEEAAAQQQQAABBBDQAgSItAME/Ehg+4HTKjCMlIXro221qlo+UAaoxe5rlStgS+cAAQQQQAABBBBAAAFnAQJEZw32EfBRgZhTF9VC9xHygwoOr1y5ZtWiYGAO6dW6vHSuV8pKYwcBBBBAAAEEEEAAgcQECBATkyEdAR8QuKZiQR0Yzlx2QI6dOG8r8f2NyshLajgpGwIIIIAAAggggAACyRUgQEyuFNch4GUCf2w8bBa6Dws/ZSvZndWLyIAOlaRC8Ty2dA4QQAABBBBAAAEEEEhKgAAxKSHOI+BlAmt3HzfvGa7ZetRWstJq2YrerSpIy1uL2dI5QAABBBBAAAEEEEAguQIEiMmV4joEMlhg/5FzZjjp738dtJUkICCLdG1WTp5qVd6WzgECCCCAAAIIIIAAAikVIEBMqRjXI5DOAqcvxJmF7r9ffkDOnftvoXtdjGZ1SsiA9hWlqJqMhg0BBBBAAAEEEEAAgRsVIEC8UUHuR8CDAt+p3sJvl0XKwcNnbU+pWqGA9FE9hvVCCtnSOUAAAQQQQAABBBBA4EYECBBvRI97EfCQwOKtMTJDzU66ZfdJ2xMKBuaUR5qUkW6NytrSOUAAAQQQQAABBBBAIC0ECBDTQpE8EEgjgS0RsWoCmgOyZOMhlxzvvae0DOwQLDnVO4dsCCCAAAIIIIAAAgh4QoAA0ROq5IlACgUOnbxg3jOcrYaT6rUNnbc7qhVWw0kryC1l8zsns48AAggggAACCCCAQJoLECCmOSkZIpB8gbgr12SaCgpnLo2UE7EXbDfqZSu6NS4r99UtaUvnAAEEEEAAAQQQQAABTwkQIHpKlnwRSELg1/XR8o0KDPceOG27Mnv2rPJg4zLyTJuKtnQOEEAAAQQQQAABBBDwtAABoqeFyR+BBAKrdx2TGcsOyNpt9oXu9WVNa98sfVtXkLJFciW4i0MEEEAAAQQQQAABBDwvQIDoeWOegIAR2H3orExTM5POXR3lIlKlfKB0bxIkTWsUdTlHAgIIIIAAAggggAAC6SVAgJhe0jwn0wqcPHtZBYbh8p3qNbx48YrNoYBa4P5htWTFY01YtsIGwwECCCCAAAIIIIBAhggQIGYIOw/NLALfrjhgFro/FHPOpcodG5SWvmqx+0J5s7ucIwEBBBBAAAEEEEAAgYwQIEDMCHWe6fcCf4YeUe8ZRsj2vbEuddXLVjyqegzvrFTI5RwJCCCAAAIIIIAAAghkpAABYkbq82y/EwjdH6uGk0bK8r8Pu9StVHG9bEUZ6VSvlMs5EhBAAAEEEEAAAQQQ8AYBAkRv+BYog88LHDh+XqYtiZCflh9wqUtAQBa1bEVZ6dOyvARky+JyngQEEEAAAQQQQAABBLxFgADRW74JyuGTAhcvXzU9hjPVYvenTl90qUMTtWxFdzWctGqpfC7nSEAAAQQQQAABBBBAwNsECBC97RuhPD4j8NPaKLPQfXjUGZcyVy4XKI+qXsPmtYq5nCMBAQQQQAABBBBAAAFvFSBA9NZvhnJ5rcCew2flw5/CZOOOYy5lLJA/hzyk3jN8XK1pyIYAAggggAACCCCAgK8JECD62jdGeTNc4LkvQmXttqMu5ehQv5Q83rSclCqU0+UcCQgggAACCCCAAAII+IIAAaIvfEuU0WsE/th4WEb9uEtOn7lklalO1ULSXQWGd1YqaKWxgwACCCCAAAIIIICALwoQIPrit0aZM0Rgwrx9MmXuXuvZetmKR9Rw0s4sW2GZsIMAAggggAACCCDg2wIEiL79/VH6dBR4e9Y/MmdVlGRTS1U8qN8zVL2G+XJlS8cS8CgEEEAAAQQQQAABBDwrQIDoWV9y9zOBtbtPqKAwQC1bkdfPakZ1EEAAAQQQQAABBBAQIUCkFSCAAAIIIIAAAggggAACCBgBAkQaAgIIIIAAAggggAACCCCAgBEgQKQhIIAAAggggAACCCCAAAIIGAECRBoCAggggAACCCCAAAIIIICAESBApCEggAACCCCAAAIIIIAAAggYAQJEGgICCCCAAAIIIIAAAggggIARIECkISCAAAIIIIAAAggggAACCBgBAkQagovA5cuXZcuWLbJp0yZZs2aNnDp1yrom5ugxiY6Oljh1Tf78+SV/YKAEBuaXwPyB6jif2tef+aV06dISFBQkZcqUkVKlSkmWLFmsPNhBAAEEEEAAAQQQQAAB7xQgQPTO7yVDSrVixQr56quvZN26dbag8EYLExAQYIJFR8Cog0f9U7t2bSlWrNiNZs/9CCCAAAIIIIAAAgggkEYCBIhpBOmr2cTFxcnatWvlu+++k9mzZydajZy5ckvBoqUkb8FCEhm2TS6cO5Potck9Ubx4cenatasMGDAgubdwHQIIIIAAAggggAACCHhQgADRg7jenHVUVJR8+umn8uNPP8up2JO2ombJklVur3eP1Lq7uRSvWEvyFSkpefIXsF0TvT9MInaGSlT4Lonev1N2bvpLrl29artGH1S+7W5p3+MlyZk7j2xa9ocs/O4zuXj+rO06HSAOHDjQlsYBAggggAACCCCAAAIIpL8AAWL6m2f4E6dMmSLjJ3wqh6KjbGWp27C53N6kk1Sq01QCsme3nUvq4NihA7Jjw3L5R/3oz0sXzrncUqtBK7mtYXu5vVFbmTTsWRUwzjHXECC6UJGAAAIIIIAAAggggECGCBAgZgh7xjx0+fLlMnHiRNGfzltQlVrS+L6eUrtJe+fkVO+fOn7UBIk7Ni6XbWsWy/mz/01yozMtUqKsHI2OsOWvexAZamoj4QABBBBAAAEEEEAAgXQXIEBMd/L0f6BjOKnuOUy4Nbn/CenU+9WEyWl2fPrEMdm6eqFsXaN+Vi+Sa9dch6E6HtawcVP535DBEhIS4kjiEwEEEEAAAQQQQAABBNJRgAAxHbEz4lHh4eHSq1cv2blzp8vjuz4/XOq1esAl3VMJMQfDrWAxLHS128dUrBQioz8eJTVq1HB7nkQEEEAAAQQQQAABBBDwnAABoudsMzznw4cPS+/eveXvv/92Kcvzo2dLOTW0NKO2yLCtqldxkWxft0TCd4S6FEPPbvryyy9LgQL2yXFcLiQBAQQQQAABBBBAAAEE0kyAADHNKL0ro9jYWOnTp4+sWrXKpWAjf94qOXLmcknPqIT9KkD8Z/1SWb/oZ4k5uN8qRs2aNeWdd96RWrUyLpC1CsMOAggggAACCCCAAAKZQIAA0Q+/5AsXLpjgcMmSpRX7IAAABR9JREFUJS61e/Pr5VKoeEmXdG9J+HXyB7LgmwlWcfLkySPvvfee3HvvvVYaOwgggAACCCCAAAIIIOAZAQJEz7hmaK6653Du3LkuZXjqna+k6h2NXNK9LWHWuDdkxa/TbMV64YUX5LnnnrOlcYAAAggggAACCCCAAAJpK0CAmLaeGZ7b1KlT5bXXXnMpx/1PvS6N7nvMJd1bE74e/rwZcupcvgceeEA+/PBD5yT2EUAAAQQQQAABBBBAIA0FCBDTEDOjszp37py069BR9u4OsxWlY89B0vzBPrY0bz+4dPGCTBjyuOzZss5W1BYtWsgXX3xhS+MAAQQQQAABBBBAAAEE0kaAADFtHL0il9GfTJRRI961laV1t+ek7aP9bWm+cqBnOp0wpIeciT1uK/LMmTOlXr16tjQOEEAAAQQQQAABBBBA4MYFCBBv3NArcgg/dEzu69hRjh8+YJUnuGZdeXbkDOvYF3fWLfxZpo543qXo27dvFz2BDRsCCCCAAAIIIIAAAgiknQABYtpZZlhOV6+JPP/WR/LjpI9tZXh2xAwJrlXXluaLB79N/lDmfzPeVvTu3bvL0KFDbWkcIIAAAggggAACCCCAwI0JECDemJ9X3L37+FXp+fB9tgXnfXloqTvUz97oLVtXL7SdGjlypHTp0sWWxgECCCCAAAIIIIAAAgikXoAAMfV2XnHnpSsiX/+5RYb2bm+Vxx+GllqV+Xfnn/XLzPuIzumFCxeW6dOnS9WqVZ2T2UcAAQQQQAABBBBAAIFUChAgphLOW24Lj70m744YJXOnjbGK5C9DS60K/bsz+b3+snHJb7Zk1ke0cXCAAAIIIIAAAggggMANCRAg3hBfxt+8+sAVGfREJ2t4qb8NLXUW1ktejH7xIeckqVOnjvzwww+2NA4QQAABBBBAAAEEEEAgdQIEiKlz84q79PDSGX9FyhvdGpjyZM+ZW179bJ4UKl7SK8rniULM+GiwrJ47y5b1smXLJCgoyJbGAQIIIIAAAggggAACCKRcgAAx5WZec8e+k9dk3MTJ8v34N02ZGnfqIZ37vuY15fNEQfbvCJVR/Tvbsn7jjTekZ8+etjQOEEAAAQQQQAABBBBAIOUCBIgpN/OaO1ZGXJFPhr8mK3+LX+vwlU/nSMnylb2mfJ4qyMfPd5G92zZY2Tdo0MBMVmMlsIMAAggggAACCCCAAAKpEiBATBWbd9w0b/cVGTXoMdmxYblUrdNQnho2yTsK5uFSLPh2gvw66QPbU8LCwiR79uy2NA4QQAABBBBAAAEEEEAgZQIEiCnz8pqrr10T+eS3v2Vkv3tNmRp37imd+wzxmvJ5siBRe3fI+0+1sz3im2++kbvvvtuWxgECCCCAAAIIIIAAAgikTIAAMWVeXnP12csiw8bPlOmjBpkyPdR/mNzd1j7Dp9cU1gMFGdmvo0SGbbNy/uqrr6RZs2bWMTsIIIAAAggggAACCCCQcgECxJSbecUdMeeuyeC33pcF335qytP/g2+lYo07vKJs6VGIeTPGy+9TPrQeNX78eGnXzt6raJ1kBwEEEEAAAQQQQAABBJIlQICYLCbvu0jPYNr/mb4SumKuKdy7s9ZL3sCC3ldQD5XoUMQeebdXSyv3UaNGyf33328ds4MAAggggAACCCCAAAIpFyBATLmZV9yx8+hVefyB1hK1b6cpz5h5e7yiXOlZiEGdasmFc2fMI4cNGybdunVLz8fzLAQQQAABBBBAAAEE/E7g/wEAAP//MpHkVwAAQABJREFU7d0HfBVV2sDhl14DodfQO1IFAYXQREBExALiooC4qOzCig0VWd1dG+IHooCLoEhTWVQEG6h0CyAKSA+9N+mEGsJ33ol3uJNGQu6dTG7+89t4Z87MPeUZF3lzWpbL5hCODCew9Vis3NK4usRcvGDV/a25WzNcG9Ja4ZcfukUO7o5r99ChQ+Whhx5Ka5Z8HwEEEEAAAQQQQACBTC2QhQAxY77/n9bukB6dWtqVz4wB4ujBPSVq1c+WwZNPPikDBgywPThBAAEEEEAAAQQQQACB1AsQIKbezBPf+HLBMvlb7252XTJjgDj59SdkxbzPLYNRo0bJHXfcYXtwggACCCCAAAIIIIAAAqkXIEBMvZknvrFw+Rrpdc9tdl0yY4A4671hMu9/71oGq1evlvDwcNuDEwQQQAABBBBAAAEEEEi9AAFi6s088Y1f1m6Ruzu1tesy7LNVkidfmH2dGU6mjXhGls2dYTV12bJlUrJkyczQbNqIAAIIIIAAAggggEDQBAgQg0Yb3Iy37d4nrZs3swvp96/xcl3TNvZ1Zjh5vX9n2bN1vdXUjz/+WJo1u+KRGdpPGxFAAAEEEEAAAQQQCLQAAWKgRV3K79ixY1K/fn27tLbd+kmXvoPt61A/iT51Qp69u6HdzLVr10pYWObqQbUbzwkCCCCAAAIIIIAAAgESIEAMEKTb2Zw/f16qVatmF1uhZn15/M1P7etQP4la+ZOMfuZ+q5kVK1aUhQsXhnqTaR8CCCCAAAIIIIAAAkEXIEAMOnHwCqhRq7acjT5tF/DSR0ulQOFi9nUon8z/ZIJ8Pv5Vq4mdO3eW0aNHh3JzaRsCCCCAAAIIIIAAAq4IECC6whycQu6+t6f88vMSO/MHnx8j9Vt0sK9D+WTUE/fK1rW/WE0cPHiw9O/fP5SbS9sQQAABBBBAAAEEEHBFgADRFebgFPL68BEyZvQoO/NmHbtLj8desa9D9WTDL4vknecftJs3adIkadWqlX3NCQIIIIAAAggggAACCFybAAHitbl54lvz58+XPn36OOoyYPiHUrVuE0daqF1MfeMpWf7dZ3azVqxYIcWKZY6htXajOUEAAQQQQAABBBBAIAgCBIhBQHUrS13JtGHDhhIbG2sXWa95e+k7dKx9HWonB/dsl9ce7iiXYi5aTbvrrrtkxIgRodZM2oMAAggggAACCCCAQLoIECCmC3vgCu3atav89ttvjgz7Dn1H6jW/xZEWKhdfTxklc6a+ZTdn2rRp0rx5c/uaEwQQQAABBBBAAAEEELh2AQLEa7fzxDcnT54sQ4cOddSlar0mMuD1Dx1poXCx+fdl8vZT99lN0XmHOv+QAwEEEEAAAQQQQAABBAIjQIAYGMd0y0X3Q7z11ltly5Ytjjr0eOxVadaxmyMto1+8/fR9snn1MrsZb7/9ttx+++32NScIIIAAAggggAACCCCQNgECxLT5eeLb48aNk1deca5emjVbdmsuYp1mbT1Rx7RW4hszrPQbM7zUd9StW1e++OIL3yWfCCCAAAIIIIAAAgggEAABAsQAIKZ3FrpYjfYi7tu3z6pKRNXrZPfmtZIrTz556J9jpXrDjD1HL/7QUm3klClTJDIyMr3pKR8BBBBAAAEEEEAAgZASIEAMkdc5cuRIefPNN63WvDV3qzx7TyOJPnlM8ocXMUHiO1Kp9vUZsqVHDuyR8S/2k33bN9n1HzJkiPTr18++5gQBBBBAAAEEEEAAAQQCI0CAGBhHT+Ty5JNPyowZM6y6aJA4sH1l67xQ8TLy1xf+K2Wr1PJEPVNTiRGP3SU7Nqyyv3LnnXeKBsMcCCCAAAIIIIAAAgggEHgBAsTAm6ZbjmfOnBFd2fPgwYNWHfyDxBIRlaXHoFczVE/i6ME9JWrVz7ZnzZo1ZerUqVK0aFE7jRMEEEAAAQQQQAABBBAInAABYuAsPZFTVFSUtGvXzq6Lf5CYI1ce6fLQYIm8/X77vldPJg97XFbMn+WoHvMOHRxcIIAAAggggAACCCAQcAECxICTpn+Gie2N6F+rph26mUDxGckXVtA/2TPns99/Xb6fPs5Rn7Fjx0qnTp0caVwggAACCCCAAAIIIIBAYAUIEAPr6Znc5s6dm+xCLrrSaZe+g6Vagxs9U2etyMSXB8jKxV876kRw6ODgAgEEEEAAAQQQQACBoAkQIAaNNv0zPnnypNzf56+yasXSRCuTJUvWuCGnXXpJ9hw5En3GrcS92zbKhyMGW9tz+JdJcOivwTkCCCCAAAIIIIAAAsEVIEAMrq8nch9hVv0c9ecWGIlVSBewadSmizRqe4cUKVEmsUeClnbuTLQ11/DLiW/ImdMnHOUQHDo4uEAAAQQQQAABBBBAIOgCBIhBJ/ZGAatWrZIx734g3341M8kK5ckXZgWJGixWrNkgyecCcePArq2yYsHnsmLebDl6cI8jy0qVKsngwYOlQ4cOjnQuEEAAAQQQQAABBBBAILgCBIjB9fVc7l9/t0jGvTdRVv28INm61b2pvdRq1FLKV68rZSrXTPbZ1Nzc9NuPJjCcJb/MmyWxl2ISfPWOO+6wgsPSpUsnuEcCAggggAACCCCAAAIIBFeAADG4vp7NfeL0WfLhlEkStebXq9axSMkIKVetrlSs1VCq1m2S4oDx0J7t1pzCvds3yP4dm+XAzs1y5MDuRMvLlSuXFRj27ds30fskIoAAAggggAACCCCAQPAFCBCDb+zpEj746FOZ8+33snzJ93Lp4oUU1TVv/oJSplKNZJ/ds3W9nI0+lewzvpsdO3YUDQwbN27sS+ITAQQQQAABBBBAAAEE0kGAADEd0L1Y5Lotu+TLOd/L4gXfydoVPwW9iiVKlJDbb79dunTpInXq1Al6eRSAAAIIIIAAAggggAACVxcgQLy6UaZ74pdVa+WrOSZQXLdetmxcJ8cO7Q2YQaNGjaygUIPD8PDwgOVLRggggAACCCCAAAIIIJB2AQLEtBuGdA4XLomsidoha9dvkLWrV8n6NavklNlf8fSpk9a2FEkNIw0rEC7lyleQypUqSPlyEVK2bFnrp3nz5iHtReMQQAABBBBAAAEEEMjIAgSIGfnteaDusbGxcuLECTlpgkb9iYmJkfLly0vhwoU9UDuqgAACCCCAAAIIIIAAAqkRIEBMjRbPIoAAAggggAACCCCAAAIhLECAGMIvl6YFTmDrwWgZPjNKbqlfXO5sWiZwGZMTAggggAACCCCAAAIeEiBA9NDLoCreFXh03EpZufGoVcFGNQvLA20qyA1VCnm3wtQMAQQQQAABBBBAAIFrECBAvAY0vpL5BLYcOC2vfRola7ccsxt/e/Oy0qt1OSlTOI+dxgkCCCCAAAIIIIAAAhlZgAAxI7896u6qgAaJPYctc5QZXjCX9GhZzgoUHTe4QAABBBBAAAEEEEAgAwoQIGbAl0aV008g+vwleXjsb7Jl10lHJWpULCgPtC4vbeoUc6RzgQACCCCAAAIIIIBARhIgQMxIb4u6ekZg2eajMui/qyQ29rKjTm0blbQCxeql8zvSuUAAAQQQQAABBBBAICMIECBmhLdEHT0rMOH7HTLhq62O+uXMmU26t4qQ3mYhm3y5sjnucYEAAggggAACCCCAgJcFCBC9/HaoW4YReOz932XpmsOO+kaUzCc9zbDTLjeUcqRzgQACCCCAAAIIIICAVwUIEL36ZqhXhhPYZvZK/Ns7q+TYiXOOuje5rqjc36qcNKrMthgOGC4QQAABBBBAAAEEPCdAgOi5V0KFMrrArOX75NWPNiRoRtdI3RajvJQMz53gHgkIIIAAAggggAACCHhBgADRC2+BOoSkwMufbJQvftzraFthExz+xeyd+JfICEc6FwgggAACCCCAAAIIeEGAANELb4E6hKzAqbMx8sg7v8nW3accbaxdOdwMOy0vrczwUw4EEEAAAQQQQAABBLwiQIDolTdBPUJaYGnUURkyaa1En7noaGe7xqXMthjlpGoptsVwwHCBAAIIIIAAAgggkC4CBIjpwk6hmVVg/Hc75L2vndti5M6t22KUlz5tykvuHFkzKw3tRgABBBBAAAEEEPCAAAGiB14CVch8Av94b7UsW/uHo+EVyoTJX8z+iZ0bsS2GA4YLBBBAAAEEEEAAAdcECBBdo6YgBJwCW822GE9PXCN7zaf/0axOMWvYaYOK4f7JnCOAAAIIIIAAAgggEHQBAsSgE1MAAskLfL5sn4ycGSXnz19yPHhXywjpbYadFiuQy5HOBQIIIIAAAggggAACwRIgQAyWLPkikEqBl2ZslC9/cm6LUbSw2RbDzE/s0aJsKnPjcQQQQAABBBBAAAEEUi9AgJh6M76BQNAEdFuMx99fLWu2HHeUUbdqIenZqpxE1mJbDAcMFwgggAACCCCAAAIBFSBADCgnmSEQGIGfNh6RYZ9ukoN/nHVk2L5JKTPstIJULJ7Xkc4FAggggAACCCCAAAKBECBADIQieSAQJIF3zbYYk+Zul0uXYu0S8ubJLj1al5feZv/EHNnZFsOG4QQBBBBAAAEEEEAgzQIEiGkmJAMEgi/w9OS1snjlQUdBlcqGWcNOb72+pCOdCwQQQAABBBBAAAEErlWAAPFa5fgeAi4LbDkQLf+ZvkE27TjhKLl5veLyQJtyUrdcQUc6FwgggAACCCCAAAIIpFaAADG1YjyPQDoL6LYY4+Zsl2PHz9k1yZJF5B4z5LS3GXpaOH9OO50TBBBAAAEEEEAAAQRSI0CAmBotnkXAQwLDZm6SmYv3OGpUomheua9VhHS/iW0xHDBcIIAAAggggAACCKRIgAAxRUw8hIA3BU6eiZEXPl4vP6857KhgvWqFpFfrCnJjjcKOdC4QQAABBBBAAAEEEEhOgAAxOR3uIZBBBH7ccERGf71Vtu855ahxpxvLyANm/8TyxdgWwwHDBQIIIIAAAggggECiAgSIibKQiEDGFJjw/Q75eOFuOR19wW5AmJmT2MMEib3MHMVsWc1kRQ4EEEAAAQQQQAABBJIQIEBMAoZkBDKywH9mbJSvftrraEKVcgWsbTE6NCjhSOcCAQQQQAABBBBAAAGfAAGiT4JPBEJMYLPZFuPNWVHy68ajjpZFmgCxl1nttHZEmCOdCwQQQAABBBBAAAEECBD5dwCBEBeYuXSvTFu0W/aYgNF3ZMueVbq1jLACxfB8OXzJfCKAAAIIIIAAAghkcgECxEz+LwDNzzwCY+dsk+kLd8n585fsRpcqnlf+YuYn3t2sjJ3GCQIIIIAAAggggEDmFSBAzLzvnpZnQoETZluMNz6Pku9+2e9ofUOzHUavNuWlSVW2xXDAcIEAAggggAACCGQyAQLETPbCaS4CKvCD2RZj8oKd8vvmYw6QzjeZbTHM/MSIInkc6VwggAACCCCAAAIIZA4BAsTM8Z5pJQKJCkz/cY98aLbFOPjHGft+wbBccm+rCOljehQ5EEAAAQQQQAABBDKXAAFi5nrftBaBRAVGfrFZZpiFbGIvXbbvV6tQQB5oVUFurlfMTuMEAQQQQAABBBBAILQFCBBD+/3SOgRSLBC1/7S89/0OWfTbQcd3Wl9f0gw7LSc1y7AthgOGCwQQQAABBBBAIAQFCBBD8KXSJATSIvD96sMyddFO2bj9hJ1NjhxZpbsZdtq7TUXJnzubnc4JAggggAACCCCAQGgJECCG1vukNQgETGCy2RLjIzPs9Njxc3aeZUrkk/tNb+IdTUrbaZwggAACCCCAAAIIhI4AAWLovEtagkDABY5HX5R3v90uny3e7ci7ca0iJlAsLzdUKeRI5wIBBBBAAAEEEEAgYwsQIGbs90ftEXBFYOX24zLFrHb60++HHOV1aVHWDDstL6XCczvSuUAAAQQQQAABBBDImAIEiBnzvVFrBNJF4Ktf98s0Eyhu23PKLr9QwVxyn+lNvL9lhJ3GCQIIIIAAAggggEDGFCBAzJjvjVojkK4Cutrpx2Z+4qnTF+x61KoULj1blZM2ddgWw0bhBAEEEEAAAQQQyGACBIgZ7IVRXQS8IrDrj7MyeeFO+fLHvY4qtW2k22KUl+ql8zvSuUAAAQQQQAABBBDwvgABovffETVEwNMCyzYflSkLdsmKDUfseubKlc1si1FOeptAMa8550AAAQQQQAABBBDIGAIEiBnjPVFLBDwv8PmyfTLNDDvdvf+0XdfyphfxPjM3scsNbItho3CCAAIIIIAAAgh4WIAA0cMvh6ohkNEEos9fMsNOd8nHC3bKeXPuO5qaeYkPtIqQhpXYFsNnwicCCCCAAAIIIOBFAQJEL74V6oRABheIMr2Ik+bvlHkrDjhacrcJEnuZoafFCrIthgOGCwQQQAABBBBAwCMCBIgeeRFUA4FQFFiw9g8zP3GHrN92wm5e0cJ5pGfrcnJv87J2GicIIIAAAggggAAC3hAgQPTGe6AWCIS0wFQzN3GaGXZ67MR5u511qxaS+1uVlxa1ithpnCCAAAIIIIAAAgikrwABYvr6UzoCmUbgwPFzMsmsdjpz8W5Hmzs0LW2tdlqheF5HOhcIIIAAAggggAAC7gsQILpvTokIZGqBFduOWdtiLDPDT31Hvrw5pIcZdvrQzRV8SXwigAACCCCAAAIIpIMAAWI6oFMkAgiIzFputsVYuFt2+W2LUTkiTO43eyd2aFACIgQQQAABBBBAAIF0ECBATAd0ikQAgTgB3RZjkpmbON1sjeG/LUaL+sWllwkUrytXACoEEEAAAQQQQAABFwUIEF3EpigEEEhcIGqf2RbDzE+ct2K//UC2bFnkHrMtxmO3VbXTOEEAAQQQQAABBBAIrgABYnB9yR0BBFIhMH/NYZlqehPXbztuf6tU8XzyFxMo3t2sjJ3GCQIIIIAAAggggEBwBAgQg+NKrgggkAaBKYt2yYdmfuIxs/Kp77i+ZhHp3ba8NK5cyJfEJwIIIIAAAggggECABQgQAwxKdgggEBiB/cd0W4yd8vmSPY4Mb29eVp6+o5pkN0NQORBAAAEEEEAAAQQCK0CAGFhPckMAgQAL/LJVt8XYKcvXHbFzDi+Qy9oWo1ercnYaJwgggAACCCCAAAJpFyBATLshOSCAgAsCny8z22Is2i27/bbFqFGxoBl2WkFa1S7qQg0oAgEEEEAAAQQQCH0BAsTQf8e0EIGQETh9LubPbTF2y4ULl+x2tW1UUgZ3rS4F8ma30wJ1csZsxfHctHVSOH8O+We3moHKlnwQQAABBBBAAAFPChAgevK1UCkEEEhOYNO+UzLZ2hbjgP1YzpxZ5V6zd2L/DpXstECcjJ2zTSbP3W5l1enGMjL0nhqByJY8EEAAAQQQQAABTwoQIHrytVApBBBIicC833VbjJ2yYfsJ+/FypfJLn5srSMeGJey0tJxEmSGtg8avliNm0Rw9HmhfMeBBaFrqx3cRQAABBBBAAIFAChAgBlKTvBBAIF0EJpu9Ez8yP8dOnLfLb3JdUXnu7hpSomAuO+1aTxat+0MGT1htf33Q3dWl+01l7WtOEEAAAQQQQACBUBEgQAyVN0k7EMjkAvvNnokfzN8ps+Jti3FXywh5ymyLkdZj+o97ZOQnm+xsXu5TV9rWLWZfc5I6gTMXRbYciZWCebJIvhwiRfOybUnqBHkaAQQQQACB4AgQIAbHlVwRQCCdBHRbjMkmUPxl/ZVtMYoWziMPtqsgdzYtnaZavfnlFvl43k4rj/z5csr/PVRH6lUIT1OeofLlszEip85flpPm5+KV9YNEt6vMnk0kpznJZdYQyp5VJJc513S9Xrk/VmIvixyOviwlw7JIqfxZpIT54UAAAQQQQACB9BEgQEwfd0pFAIEgC8xctlemLdwtew5E2yXVqRIuz5lFZioWz2enpfbk6clrZfHKg9bXIkrmkxEP1ZOIInlSm02Gf37evHmya9cu2bB1t2zdsUtOnDyZ+jaZwDB3vjDJk6+A5Mkf9zNn6ltSrc710qZNW7mlZVMpUqSIFCtWTPbt2ydHjsQF/U2bNk19WXwDAQQQQAABBFIkQICYIiYeQgCBjChw+twlM+x0h0w38xMvXoy1m9C+SSn517217OvUnByPvigDzaI1UTvjFsapU6WQjOxbV/LnDvwWG6mpl1vPzpw5Uz6YNElWrVzpVpEJyqlTp45MmDBBSpYsmeAeCQgggAACCCCQNgECxLT58W0EEMgAApv2nZZJZtjp/F+vbIuRL28O6XNLBenZslyqW7B+zykZ9O5qOXEqblGc5vWKyxu966Q6n4z0hblz58rkyZPlhx9+SHG1s2bLJoWLl5FCxUvJqWNH5OihfXLh3JkUfz+5B6dPny70JCYnxD0EEEAAAQSuTYAA8drc+BYCCGRAgflrDpv9E3fKRr9tMSpHFDDDTqtLbfOZmuP71Yfl+Q9+t79ym9kj8fkQ3SPxpZdekvHjx9ttTeykZPmqElGltlSo2UDKmk8NDAsWKZ7g0eiTx+WYCRQ1WDx6cI/1c+TPz6MH98rZ0ykbqvrYY49Jjx496EVMIEwCAggggAACaRMgQEybH99GAIEMKKDbYny4YJccP3llW4wW9UvI8F7Xpao1Hy3ZLaM+i7K/c7/pkfxbx8r2dUY/WbhwoXz66acye/ZsR1PymvmCVes3kzKVapqfWlKxVgPJX7Cw45lrvTh7+pQVNB7cs032bt8g+7ZtlN1b1snJI4cSzbJXr14yZMgQyZUr7duZJFoAiQgggAACCGQyAQLETPbCaS4CCMQJ7DMb308yvYn+22JkzZpFerevKP1uqZhiplFmZdOP/lzZVL80sGs1uS8yIsXf9+KDP/30k0wy8wznzJljVy9b9hxSu0kbue7Pn/zhgQkI7QJScPLrwi9l0qv/SPCkDjXVIaccCCCAAAIIIJB2AQLEtBuSAwIIZGCB5VuOyRQTKPpvi1GyWF4Z0r2GNK5cKEUtG/rRevlu+X772Rfuv046NixhX2eUk5Vm4RmdZ/jZZ585qnx3/xes4LBIybKO9PS4iD55Qj6f8Iosm/uJXXylSpVk+PDh0qhRIzuNEwQQQAABBBC4NgECxGtz41sIIBBiAp8v2ydTrW0xTtsta1ijsLz91/qSzfQsJnfEXLosfx+/SlZtOmo9FpY/p4x6uL7UKhuW3Nc8dW/kyJHy5ptvJqjTkAnfSokI7w2bXfLFNJkx+p+O+r788svSs2dPRxoXCCCAAAIIIJA6AQLE1HnxNAIIhLDAqXMx1iI2002geOHCld3ee9xcXv7RqUqyLd979KwMmvC77NofF2DWqhQu7zzSQHLlMDvDe/xIKjh85X8rzNzClPWipkcTf13whUx67TFH0bp4zaBBgxxpXCCAAAIIIIBAygUIEFNuxZMIIJBJBDbuPW0Fiv7bYhQMyynPda8pLWsXTVJh5fbj8oTZI/HM2RjrmU5mZdOhHl/ZNKngsPvAl+SmTj2SbKtXbiw1Q00/HDHYUR2CRAcHFwgggAACCKRKgAAxVVw8jAACmUlg3u+HZcpC57YY1SsUlNH96ktYnuyJUsxZeUhenLzGvjega1X5S2Tq91q0MwjiycSJE+XFF19MUELT9vfIfY+/liDdqwmJDTfVXkQNFDkQQAABBBBAIHUCBIip8+JpBBDIhAKTzLYYH5mf4yeubIvRpUVZefbO6olqTDJbaLwze7N1T1dGHWHmIzat5v6qn4lW7s/Ebdu2yT333CN//PGH47HqDW6Sv7022ZGWES7mf/KefD7+FUdVCRIdHFwggAACCCCQIgECxBQx8RACCGR2AZ1jqIHf7B/22BQ5c2aTZ82w08RWLH1jVpR8YuYy6hFRMp+MeaS+FC+Y2/5uep88/vjj1h6H/vUIL1ZK/j5sqhQvU8E/OcOcz/1wjHw1aYSjvrrwTteuXR1pXCCAAAIIIIBA0gIEiEnbcAcBBBBIILB8y1GzLcYux7YY5Urll9Gml7B4Qedm7U9NWiNLVsVt8N6ifgkZ3uu6BPmlR8KsWbNk4MCBCYru2m+ItL7rwQTpGSlh8utPyIp5n9tVrlKlisyYMUMKF/ZWD65dQU4QQAABBBDwmAABosdeCNVBAIGMITBz6V5rW4y9B6PtCre7oZT8p0ct+/qkWaxmoNn+YuP2E1Zarw6V5NH2Fe376XFy/Phxa2hpVFSUo/gKNRvI429e2VvQcTMDXRzYtVVGPdFdok8es2v90EMPydChQ+1rThBAAAEEEEAgaQECxKRtuIMAAggkK3DKBICTFuyU6WZ+4sWLsfazg++tKV2blLauo8y2F4+9u1qOHj9nXb/6YF1pXaeY/azbJ1OmTJHnn38+QbF9hoyWBpEdE6RnxIQFn74vM9992VF1bXdkZKQjjQsEEEAAAQQQSChAgJjQhBQEEAhhgQdGrZCoHSckX94ckt/8hOXJJmHms0C+HFLQfBbMm92kxX0WzJtTiplho9VL55fs2bIkqbJh7ymzLcYuWfDrAfuZIoXyWPMOKxTPKwvWHpZn3/vduheWP6dMfaKxlAhPn/mIjz76qHz99dd2PfWkQeSt0mfI2460jH4x9rnesvHXJXYzmjZtKtOnT7evOUEAAQQQQACBxAUIEBN3IRUBBEJUoOmgedfUsrJmoZlKZq6hBovVzE+NsmFSrIBzzuH3qw+ZbTF2ySYTgPoO39zDqYt2yejP41Y2rVutkLz7aEPfI659nj59Who2bCjnz19ZjVULH2SGllY0Q0xD6djy+3J56ynnPo6DBw+W/v37h1IzaQsCCCCAAAIBFyBADDgpGSKAgJcFdG/DFVuPyd4jZ2XXoTNy4PCZa66u9gZWMsFilVL5pHoZEzyWKWAFkB+YYacfmxVMj5+8EogN6FpN9pgyZy6OW9n01mal5Z/dal5z2dfyxblz50q/fv0cX63foqM8+PxoR1qoXHwxcbh89/F/7ebkz5/fWrCmVq0r80Ttm5wggAACCCCAgCVAgMi/CAggkKkFLsTEyk4TJG7ZHy1bDpySzftOy7b9Z+QPs63FtR6NahaRWuUKyG6T74LfDtrZ5M2TXSqWDpN1JkDV47G7qsm9zSPs+8E+efrppxMMs+z97FvSsFWnYBedLvmfO3tG3jIL1uzZut4uX4fYPvPMM/Y1Jwgg4I7A/PnzZfz48RIWFiaVKlWS5s2bS9WqVaVYsWKSNWtWdypBKQggkCIBAsQUMfEQAghkNoHT52Jks1lgZtPe0xK175Rs3H1Ktu05lWqGwmauYbasWeSwX8CZK1c2M8zzkmTPnlXefLS+NKpUKNX5XssX6tWrJ7qKqe8oXraSPPfuHMmaLZsvKeQ+F8+eIp+MedFuV8WKFWXhwoX2NScIIOCOwIsvvigTJ05MtDANFLVnv2bNmlKtWjXR7WnKli0r2UL4z6ZEIUhEwCMCBIgeeRFUAwEEMobAehMkrtt1UjbsOZn6oFHXubnsbGfliAIyxuyhGG4WyQnmsXTpUunevbujiHY9HpXOvZ90pIXaxXnTi/h6/85yeN8Ou2njxo2TDh062NecIIBA8AVee+01eeedd1JcUL58+aRbt27SpUsXadAgtOZIpxiBBxFIJwECxHSCp1gEEAgdAQ0a1+8+IRv3aI/jKdlufmJi4kWCyTS3vJnHOP2pJsk8kfZbn332mQwaNMiR0dNjZkvZKrUdaaF4MffDMfLVpBF202677TYZM2aMfc0JAikRWLn9uOhCVAvNPOYjx+K2rYn/vVqVCkrzWsWk5XVFpXKJfPFvZ+rr6OhomTRpkmzevFkuXLggCxYsEE1LydGmTRtraHj16tVT8jjPIIBAGgUIENMIyNcRQACBxAS2HoiO62U0Q1Q3mgBy8+6T1rDSxJ7VtJoVw2XiwOuTup3m9LffflveeOMNO5/aTdvIw/8ab1+H8snxPw5YvYinTxy1mpk9e3ZZt26d5M6dPluNhLJ1ZmjbGTM8fKHZumbZ5mOyIupoksFigxqFJbJWUbm5XvEEKx5nBqertfGBBx6QRYsWiW5BM2zYMDl8+LBs2bJFfv/9d9ERD9u2bUuQxVtvvWX1KCa4QYJnBNauXSv/+9//ZNWqVXLo0CHZv3+/aG9wnTp1pH379tZIFr3m8LYAAaK33w+1QwCBEBLQxXDWmUBxvZnPqEHjxh3H7Z7GFvWLy/BedYLW2ueee06mTZtm59/zyeFyQ7s77etQP/l8/Ksy/5MJdjP1L6T33nuvfc0JAtciEGsGCizddESWbj4qKzYfl23m/9/xjzxmcarIusWlbd1iVsAY/35mvW7durUVBF533XXy1VdfJWDQAHHs2LHWysP+N0ePHi2dO3f2T8q05+fOnbN6ViMjI+XOO9P3z/OjR4+K/nfmm2++SfZ9FClSRN577z2GDSerlP43CRDT/x1QAwQQyMQCGjQuWHNYercpH1SF3r17W0O6fIU8N/5bKVmusu8y5D/374iSYWYuYuylGKutzZo1k48//jjk200D3RVYvfO4/LD+iPy44YgJFhMualXBbIfTpl4JudkEi5Uy+RDU8uXj/swrVaqU1WOY1JtavXq19OnTR44cOWI/oj2PFSpUsK8z64ma6N62eqSnycGDB61fuCXW65vYu9EexDlz5ki5cuUSu02aBwQIED3wEqgCAgggEGyBW265RTZt2mQVkyNXbhk+8/eQXr00Mc/xLz4sa37+3rpVo0YN0X0hORAIlsCv247JEhMs/myCxZ1m+5z4x40mSGxrehbbmSGoOc2KxpnpiI2NFV1RWA8NFtavv7IVTWIOOvT09ttvt+cs9urVS/79738n9mimSvMPEN98803p2rVrurR/4MCBMmvWLEfZuija9ddfLyVKlLCGDf/3v/+1358+2LdvX/nnP//p+A4X3hEgQPTOu6AmCCCAQNAEdBjXqVNxPRqlK1aXZ/77ddDK8mrGs94bJvP+965VPf1Ly/Lly71aVeoVYgLLzBDUJev/MMHiUdl70LkwS7HCeaRdw+Jye+NSUqF45pibpYvU6NYWvmPnzp2+0yQ/dV7bU089Zd3XeYvTp09P8tnMcuPEiRNSt25dq7kPPfSQDB061PWmx8TESOXKztEoGgx27NjRUZd9+/ZZ8w937dplpWvv4ZIlSxzPcOEdAQJE77wLaoIAAggETcA3nEsLqNe8vfQdOjZoZXk146VzZsiHI5+xqpczZ05rNUWv1pV6ha6ADj9dsuEPWbLmDzly/MpqqLov6i2NS0pnEyg2MItWhfJx5swZa89DXxu3bt1q9oXN7rtM9FPntj3yyCPWvaTmLSb6xRBO9Hds0aKFTJ06NV1a65tPqoW3atXKWq02sYro3ENfz29Keo4Ty4M0dwQIEN1xphQEEEAgXQX8A8Sbu/WT2/sOTtf6pEfhu6LWyBsD7rCL1mFtXltNb+ychCs32hXOYCdZs2SR7FmzmM3OzY/51POs+vnndTYzqjJ71qzWfes5fdZ33zwjoj9pPy5fvizmf3Lp0mW5ZE50YZlY8w8rzXxess7j7mma3tM08z/rU2ug1740x3fNFzR/HTLpe966/+d3NM2Xn688k2Slnb8YKwfMdhkHj56TU6cvaDH2USAspxQukFMK5M1hPXubCRrvaFLavp/RT86ePSs6zNt3XO3/i7oAyv333y+6QqYeujeirmga/9B8f/jhB9m4caMcP35cChYsKDrHsVGjRvaQ1vjf8fK19s4lFzj7D9WtVKmSY565m+36+eef5emnnxbtHdQVs3U4cGLH+PHj5aWXXrJu6WI1v/32W2KPkeYBAQJED7wEqoAAAggEW8A/QOzx2KvSrGO3YBfpufwvmb9sDep0ZR+1n376ScqUKeOZeg79aL18t3y/Z+pDRbwlsHRkW29VKA21uXjxolSpUsXOQbe20GAu/qEB0nfffSdDhgxxLFLz/vvvS9u2Tg8NCO++++4kRwboXoovv/yylC7tfqC9YsUK2b59uxQoUEB0PngW88uTpA7dJ3LGjBnyySef2G3WIbXPPPNMoit/1qpVy57bl5KhukmVm9Z03y9KsmXLlmRWOnd04cKF1v30DGiTrCA3bAECRJuCEwQQQCB0BfwDxAHDP5SqdZuEbmOTadlr/drLvp1brCd0aX0dquaV4/Nl++Tt2Vsk+sxFr1SJenhEoEq5AjJ1UGOP1Cbt1fDv+dLcnnzySdHhkoULF5bw8HCrJ0p7AX/99Vc7SPKVqnPbdI6b/xEdHS09evQQXfH0akdSPVwrV6609u/bvXu3nDx5UgoVKiRFixaV2rVrW72XOXLkSDZrnWOnQyg18OnWrZv4ntdg9l//+pf93b///e/2XEo78c+TmTNnymOPPRY/2b5ObG6frmLqW+FVg9Csplfei4f/fEmtn27LMXLkSC9WlToZAQJE/jVAAAEEMoGAf4D44PNjpH6LDpmg1QmbOOzhDrJ3x2brxuzZs6VevXoJH0rHlOVbjsnAMQEYdmU6KHS4pg7p9P1k0+Gcem0P8Ywb0ql/n9R06yfePWt4qMkrbmhotivP6fP67J/l6Hn8YaLaS6JpOqTU+r4p6Mq5SdPvWt+Lq5f/PR1+qveu9dDhnBdjLkvMpVgzNNScm+GlMZcumZ+4oaIxZuynnl80N83oUOtTn03NoT0mFzRfU47mr3ldjImVC3+Wq/ldMNdJZav3T0bHyOmzMXLm7EWTT9LlP353Nel2U0Rqquf5Z/3/TEppZbUnbdKkSZI7d27HV3RvxOHDhzvSdBEU7bHTjdp9AZTvAV3Q5fnnn7d68nTBHB0eqcFZUoc++9e//jWp21a6riLqC3h8K4rqSsn9+vVL8D1dcKdJE+cv6d59912rhzPBw/ESdFimDs/0HWqibdRDV6r2t1m6dKnlooGjPqfDOzUIT49D35v/qqVqld57N6aHQ0YpkwAxo7wp6okAAgikQcD/L2PdB74kN3XqkYbcMu5XB3WqIZdi4nrooqKiJFeuXJ5rzMa9pyT6vJl7ZAVhccGTFdyZeMk/iNLzuMDO94zO6YsLxjSdw3sC+82cwx83mq0vNh6VFZuOyPnzlxKtZBmzR+L1VQvJDVUKSZNqhSUsT/ILuCSaiccT/f9MSmlVp02bJs2bN3c8rsNQb7jhBjsI1HnF+ssf3xBWDeTXrVsnY8eOFR014Du0h1J7Mh9++OFEV9PUAFN7Jn3BZadOnWTMmDFJDg8dN26cvPLKK1b2zz77rLRr1046d+5sD//0lauf8edQ6oqsGqT6Hzq6QYej/vLLL476jRgxQu666y770datW4tv/0H/obofffSRNSzVftCcaJCohsnNa/R/PlDn58+fl5YtW9qBrOa7Zs0aK4APVBnkE1gBAsTAepIbAggg4EkBnZujf9HQ47beT8gtPfp7sp7BrNT+nZvl1X5xPae6xP7338ftiRjMMskbAQ0KF649LD+ZwPDXTUcl1vQ2xj+0h7WuCQgbmYCwcdVwqVc+tFcx1fb7z52L75HctQY+N954o/3I4sWLrSGgvgSdZ9izZ0/fpeNTh5Hqgio6hLR///7Wc/7DUgcMGGD1amlwqEFU/B7AxOY++gqYPHmyvc2EBoC6WI4vuNRndLEcX0+fXutcQ11NWXv3WrVqpUn2oW2499577UBOey+//fZb6/5//vMfeeCBB+xnNXD1Ld6jf8YXK1bM6onUBWESO4YNG2blndi9YKX5L06jZeh2JTrUlsO7AgSI3n031AwBBBAImID/8KXWdz4oXR8eErC8M0pGq5bMlfdfiguMb7vtNqs3IKPUnXpmPIE1u07IrGX7Zd5vB+XsuZgEDSgcnlsamqCwcZVwq5ewpLnOTIf/0EjtLdOgToeEalB16NAhax6i/hJHe/HiHxMmTLB66DT9gw8+kBdeeMF6ROf/6aI2Kekh0x47/70Up0yZIpGRkY6iXnzxRZk4caKdVrNmTfn666/NkOmE8/z8h5jaX/jzRPPQNjZufGUe6Zw5c6ytPnRO5BtvvGF/Rc/vuece+1pPNLC94464FZg/++wzawN63wP6rG9PV91XUINY/zr7nvN9BnNxGF1Fds+ePXL69GnRcx1mruc6pNd36PDYH3/8UfLkyeNL4tODAgSIHnwpVAkBBBAItIAOp2rfvr2VbeObu8r9T135C0mgy/Jqft9PHyez33/dqt4TTzwhAwcO9GpVqVcGFli49g/59Oc98sv6IwlaUbV8AauX8AYdPlq1sDVEOMFDmSRBt0Lw9d4ltcm7DgHVeXXaO/fhhx/aMjqMVHvL9PP111+3f9mjPXE61+1qx44dO6whj77n4vfKaboGODfddJOjF1DTdaiq9trFP3R+XWJl68I1N998s/W4zkfUXkk9fAGpfw/gfffdJ6+++qp1P/4/NFDWFUL95xjqM9qbuGjRIutxbb9vlVBN0J5MDZ51RVTf8FdN117UQK7mevDgQWvbEZ3HmVhAr2X6Dp33rXM6dVgwh3cFCBC9+26oGQIIIBBQAf1Lig5rqnVDK3nkP+8FNG+vZ3bu7BkZ+dhdsn9HlFVV7VH1Bcxerzv1877A8i1H5YcNR2SJCQ73HzrjqHCNigWlRa2i0qxGEalVNsxxLzNf9O3b1x7mHX9OXmIu/r1oet8X1OkWGL4N4nXOXlJDK/3z1EVidJijHjr8/v/+7//8b1vnOhRTg8H4hw4V1SAsfqD2yCOPyDfffON4XFdn1WGrvsN/GKqvp9B/FdKkVlj1fT+xT/8eRP/7um+kbkqvvZ0a7PrvO5nYaqj+303N+YEDB6zeTf/hsyn5foMGDawFdHS4P4f3BAgQvfdOqBECCCAQFIHu3buLrmpXqFhJ+dfUH4NShlcznTNttHw9OW5Jdf3NuQ5d094HDgSuVeCXrcdk3upDsszMK4wfFJYomle6NC0tresUlYrF+fcsMWNdyMXXK6gb2X/66aeJPeZI054n7XnTwxdUDho0SHTYpR66EIoGYVc7NPDTAFCPwYMHW/MR/b8Tf8VN/3t6rltRaLn+hw4B1SDWd+g8SQ1c/fcF1KGV2kuoh+4JqAFcixYtrOG0mqa9alp23rx59TJFR4cOHWTDhg2OZxNbjMZ/HmNSPbaOTFJ4oXM5/Rf/0a/p+zx69Ki9eE5yWekWIL17907uEe6lgwABYjqgUyQCCCCQHgL6lyjfX2oeemGc1L0xbthTetTFzTIP7d1h9R5GnzxuFat/UdK/aHIgkFqBE2aPyjkrD1qB4e+bjyX4evUKBaXzDaXk7mZlEtwjwSngP/dO56Xp9g1XO/yHpfqGY/oHKCmdX+c/b1F/UaRBmfZk6VB8DR59Qza1PjrvUHsltYfQtxiMpj/66KPWyqO++YjxF93RRWoiIpxbk+hQTN/QSt9CWdoTqnMqfYcGV6+99ppVH19acp/+PZD6nLZnwYIFUqJECcfXdPXS5557zkpLqZMjgyQu4rdbFxHSOujqsP69ilqmb7XV+FnpLy91YR7f3pHx73PtvgABovvmlIgAAgiki4AuNa7/Mddl4ctWrikDXv9I8uQP/SFv/xv9gvzwxVTbPP4iD/YNThBAICgCukm69hBqgFCtWjVrkZWtW7c6hl8mt+2Mrjqqcw19vYdaSd9qnP49Y5quQd7VFkDRwEV72a52aDCncwh18RwdSnnrrbc65iTq/EGdM1iwYEHx37ZDfxGX1Ib3/ovzLFu2TM6dO2flG3/ung7BbNu2rTRr1swy0zrEP+JvPq/3NdjVuYjxD138R4NJ36EL28QPIn33UvPpP4cyqe/pAj2zZs2yFq3R+Z/ay6tBuv+hq5r6hv36p3OePgIEiOnjTqkIIIBAugjob5D1N8l6tOraR+58JLR70pbN/USmjRhsW+uiE75hbXYiJwggEFQB/3mCvoK019B/G4i33nrL2qJB9ya9dOmSHDt2TPbu3SsrVqyw5vz5B1C6wbruB6irZMaf+5fSBViGDx8uo0eP9lUnwafOUdY6+c811ABXV0D2r4v2lukQyy+//NJajVSvNfjKnz9/gjw1YdSoUVbd9dy34I0Gtdoj6u+h9/0PnfuowaUGeXXq1LFWRdVhnL4eSX1Wh6v65mP6f9d37r+gT/ztQnzPpPbTfz5nYt/VLUNmzJghJUuWdNzWXxD87W9/s4fHahAZf6iq4wtcuCpAgOgqN4UhgAAC6SsQf8+wUB5qun3DSjO09G4HeHJ7pDke5AIBBAIm0KdPH5k/f35A8tMgSXsSdQ9BPXR/QO1F1CM1QycvX75sBS7xe610SKmucKxz+3zDR63M//yHzvfT9viGT2pAqH+uFi1a1NoCQxeD0XokdehIDu1127dvn+hWFxpA6aEB8TvvvCPjxo1L6quOdN9Qed+8TK2Hf36Oh/+80B5H7ZHUAFe35ejatWtij6U6TYfTjhw50grm/b+siwbpcFn9ZUBih9ZH52Lq3E0NdDWQ5PCGAAGiN94DtUAAAQRcE9D/aOvS8XqUMUNNB4bgUNOz0adkSPcmEnPxvO2qi9Pob/mT+suK/SAnCCAQUAH/XrNrzVgDtwcffFA6d+6cYAip9mJpcKELzugcvtQcFy9eFB32eOHCBalQoUKKFq/SYaG6kumpU6es4Zy6OX1qDg0Sda9G/wVsfN8/fPiwtYiWzsnU3tOk5u35r76qQav2MIaHh/uySfJz3rx51lYbOndT2xvIQwNPDXS1FzgsLMzR+5pUOTp8WNup0x/i9zIm9R3Sgy9AgBh8Y0pAAAEEPCWgy6vrAhG+o0HkrdJnyJVrX3pG/nzpwZvl0N7tjibQe+jg4AIB1wR0yKgu/KI9RbqRug6N1N4j3URdfzRI0OBCe8F0Xpz2xmnQpb/M0WvtXfIfSulaxT1QkDqtW7fOGoqpQ1F37dplDTH9xz/+YQVhHqgiVQhBAQLEEHypNAkBBBBITkB/29yjRw/rN72+50IpSBz1RHfZunaFr2nWpy728P777zvSuEAAAQQQQACBhAIEiAlNSEEAAQRCXkDn8MTf6iEUgsRX+nWQAzs3O96fziP65JNPrJUTHTe4QAABBBBAAIEEAgSICUhIQAABBDKHgP8G074WN2x1m/R+dpTvMsN8nj5xTN56qkeC4FAbMGDAAHnyySczTFuoKAIIIIAAAukpQICYnvqUjQACCKSzQGRkpOzcudNRi9Z39ZWu/eI2VHbc8OjFgZ1bZOLLA2T/zqgENaxdu7a1eIXObeJAAAEEEEAAgasLECBe3YgnEEAAgZAV0AUPdO+s+MeNne6Tewf+J36y566jVv0sn479d6LBYUREhLz77rvW6nieqzgVQgABBBBAwKMCBIgefTFUCwEEEHBLQJeHT2wIZrnqdeXB58dI4eKl3apKqspZPHuqzBz3klyKuZjge7r64fjx45l3mECGBAQQQAABBJIXIEBM3oe7CCCAQKYQSCpIzJotu/QaPFIatLzVMw6XYmLks3Evy5LZkxOtU968eWXChAly0003JXqfRAQQQAABBBBIWoAAMWkb7iCAAAKZSmD9+vXSsWPHRNsc2eUBaXtPPylUrFSi991K3L15rcyaMEyiVv2UZJEaHLZr1y7J+9xAAAEEEEAAgaQFCBCTtuEOAgggkOkEDh06JI8++qisWOHcR1AhChUrbQWJkV3ud91lv9m64ocvp8kPX0yTy5djEy2/cePG8sQTT0izZs0SvU8iAggggAACCFxdgADx6kY8gQACCGQqgdjYWHnmmWdk+vTpiba7esPmcvM9fxX9DPZxeN8u+fHLqbLEBIcXz59Lsrj+/ftbwWH27NmTfIYbCCCAAAIIIHB1AQLEqxvxBAIIIJApBYYPHy6jR49Osu1NbrlbGkbeKjUbt0zymWu9cezwAfnxq7gewzOnTySZTY1a18ngp56QNm3aJPkMNxBAAAEEEEAg5QIEiCm34kkEEEAg0wmsWrVKpk6dau0lmFTjK9ZqKA0iO0nDlrdJgcJFk3osRenrf1kkG1YskpWLv5GTRw8l+Z2IilWl211dpU/vByQsLCzJ57iBAAIIIIAAAqkTIEBMnRdPI4AAAplSYNmyZfLeB1Nl7tezk2x/vgKFzGqnnaRC9fpSpnJNKVOpRpLP+t9Yv3yRrFn6vaz5+ftkg0L9Tt3GN8ldd3aVnt26CsNJ/RU5RwABBBBAIDACBIiBcSQXBBBAIFMILFy0RN55f4osXTj3qu3Nm7+glKpQTXQ/xWxmbuCFc+fkwvmz5vOM+dHPs7J17XKzj2HMVfNq2aGr3GN6DDvfEvjhrFctnAcQQAABBBDIRAIEiJnoZdNUBBBAIFACX3y7UBYs/lFWLPtZdkatCVS2dj6FS5SV2o0jpfENN0irG2+QelXL2Pc4QQABBBBAAIHgCRAgBs+WnBFAAIGQF4i9LLJ07Tb5bsFiWf7jYmt/Qu0ZTO2hAWGRkmWkVuPW0uTGFhLZuLaUypdFsmdLbU48jwACCCCAAAJpESBATIse30UAAQQQsAUuXBI5deGy/PDTUjl++pycOH1eTkSfl7PnzsvFC+cl5qL5MZ8XL56TwsUjpFSZslK2TBkpXz5C8uUQyZczixTKncV82llyggACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAApaKqg4AAAVzSURBVAgggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuCxAgugxOcQgggAACCCCAAAIIIICAVwUIEL36ZqgXAggggAACCCCAAAIIIOCyAAGiy+AUhwACCCCAAAIIIIAAAgh4VYAA0atvhnohgAACCCCAAAIIIIAAAi4LECC6DE5xCCCAAAIIIIAAAggggIBXBQgQvfpmqBcCCCCAAAIIIIAAAggg4LIAAaLL4BSHAAIIIIAAAggggAACCHhVgADRq2+GeiGAAAIIIIAAAggggAACLgsQILoMTnEIIIAAAggggAACCCCAgFcFCBC9+maoFwIIIIAAAggggAACCCDgsgABosvgFIcAAggggAACCCCAAAIIeFWAANGrb4Z6IYAAAggggAACCCCAAAIuC/w/OsDdHSiHP+MAAAAASUVORK5CYII=) - -## Setup - -First, let's install the required packages - - -```shell -pip install -U langgraph -``` - -
-

Set up LangSmith for LangGraph development

-

- 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. -

-
- -## How to run graph nodes in parallel - -In this example, we fan out from `Node A` to `B and C` and then fan in to `D`. With our state, [we specify the reducer add operation](../concepts/low_level.md#reducers). This will combine or accumulate values for the specific key in the State, rather than simply overwriting the existing value. For lists, this means concatenating the new list with the existing list. See [this guide](../how-tos/state-reducers.md) for more detail on updating state with reducers. - - -```python exec="on" source="above" session="1" -import operator -from typing import Annotated, Any - -from typing_extensions import TypedDict - -from langgraph.graph import StateGraph, START, END - - -class State(TypedDict): - # The operator.add reducer fn makes this append-only - aggregate: Annotated[list, operator.add] - - -def a(state: State): - print(f'Adding "A" to {state["aggregate"]}') - return {"aggregate": ["A"]} - - -def b(state: State): - print(f'Adding "B" to {state["aggregate"]}') - return {"aggregate": ["B"]} - - -def c(state: State): - print(f'Adding "C" to {state["aggregate"]}') - return {"aggregate": ["C"]} - - -def d(state: State): - print(f'Adding "D" to {state["aggregate"]}') - return {"aggregate": ["D"]} - - -builder = StateGraph(State) -builder.add_node(a) -builder.add_node(b) -builder.add_node(c) -builder.add_node(d) -builder.add_edge(START, "a") -builder.add_edge("a", "b") -builder.add_edge("a", "c") -builder.add_edge("b", "d") -builder.add_edge("c", "d") -builder.add_edge("d", END) -graph = builder.compile() -``` - - -```python exec="on" source="above" session="1" result="ansi" -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI8AAAGwCAIAAAAfWqEIAAAAAXNSR0IArs4c6QAAIABJREFUeJztnXlgFEW6wKtnJnMfyeTO5E44AkkIkAgEEBAiBJIQ7gAilyg8cX2y4qrL032rixz7BFeU3cUVFeKyggiCYMAFCcQQwn0kBHKRO5kjM5OZyUzP0e+P2Y0sBBikqi/691fozHzfR//S3dXV1VUYQRCAgyHwqC6A4yHgbDEJzhaT4GwxCc4Wk+BsMQkBJVlxh1vXjNu63Dazy+0CTtxDSRkPi1DEk8j5UiVf7i8ICBGSXwBG5v2W3eq+cb6r9opV22RXh4mkCr5UKVAF+uF2ZthyOQmLyWkzu4ViXmc7Hpcii0+RhcVISCuAPFulB/VN1baQKHF8iiyqr5ScpOgwtON1V6ydHbjd6s7MDVKHkXGokWHr+lnzD4Udw6eo0yeoUecin7pr1p8O6OIGyjJzg1DnQm7r1H6dx0OMzg/CMAxpImqpvmQpLzLMfS0aaRa0tor3ahUBgsHjAtCloA+6FseujY0r/pjA56P6u0Ro67u/tYbHiYc89Vio6uGjX1ev2JDAQyMMla3Th/R8AZbxNAsvVPfH0I4f/rR1/hsxKIIjuTuuvWJxOT2PoSoAgDpUmJkbePIbLYrgSGyd+FqbNubxOgHeTlyyvK3e3nbLDj0yfFuXTxrjU+Ryf2p6SWhCZm7QTwd00MPCt1V71ZqZFwg9LLPQJEoCw0QNVTa4YSHbarhuwzDg50dSZ3Fra2tLSwtVX78/QRph9UUL3JiQd2vtVUt8shxuzHvR1NSUl5dXUVFBydcfSFyyrO6qFW5MyLYMbXh8qgxuzHvhcrl+2e2H91u/+Os+IlUINInitnqYbQ2Y91su3LNtTd2KDQmwAvZgt9vXrVtXXFwMABg8ePCrr75KEEReXl7PB3Jycn73u9+1t7d//PHHJSUlFoslJiZm8eLFkyZN8n5g9uzZCQkJCQkJu3btstvt27dvnzt37h1fh1720cL2qL6S/hlKWAFhttxsXW6pgg8xYA/bt28/ePDg8uXLg4KCDh48KJFIpFLpu+++u2bNmuXLl6enp6vVau/hcu3atZkzZ/r7+x87dmzNmjVRUVEDBw70BiktLbXb7Zs2bbLZbDExMXd/HToyJd9qdkMMCNOWtcslUyBpuLe0tEgkkkWLFgkEgvz8fO/G/v37AwBiY2PT0tK8WzQaze7du73dx1OnTp0wYcKPP/7YY0sgEKxdu1Yikdzr69CRqQQmnRNiQJjXLY8LiGRIWoPZ2dl2u/2ll16qrq6+/ydv3LixatWqSZMmTZs2ze126/X6nl8lJyf3qCIHgR/kBw8wd65UyTdpYf4p9ZCZmfnBBx/o9fqCgoJ3333X5XL1+rHy8vKFCxfiOP72229v2LBBpVJ5PD8/lSZZFQCgq9MllsG8NMA8ccmUAqu59/346GRmZg4fPvzvf//7pk2bwsPDly5devdnPvnkk8jIyM2bNwsEAkr03IHV7AqPhVkDzGNLKOaFxohxB8zrqhccxwEAPB5v/vz5wcHB169fBwCIxWIAgFb7c/+p0Wjs27evVxWO4zab7fZj6w7u/jp0eHxMoYZ5PEBuFEgV/Lortn7pCrhhd+3adeLEicmTJ2u1Wq1WO2DAAABAaGioRqPZuXOnRCIxmUwFBQXp6ekHDhzYv3+/SqUqLCw0m801NTUEQfR69bj76yKRCGLNLqfn+pmucbNCIMaE3CiIT5HXXoHc3QIAiIyMxHF806ZN+/btKygoWLBgAQAAw7C1a9fKZLI//vGPBw4cMBgMK1asGDFixMaNGzds2DBs2LD169frdLqzZ8/2GvPur8Otue6qNS4ZckcB5KeRLqfnwF9apq2MhBiToZR8qwuNEScOgtkPB/lMKPDjhcVJzh41pGfd835z7NixvW5PTU29fPny3dtVKtX+/fuhltkLW7Zs2bNnz93bFQpFV1fX3dsxDDt+/Pi9onV24HVXrSPzII+CQvKk//5jEx6225vH44WFhUEq7Z6YTCar9eE6YSMiIu71q+/+1pr0hCI+BXIHNxJbV38yOmzE0AmP6ePjjkb7pWJj1nz4f2FIuh6SM/11LY4b53s5gbAet5vYs7kJhSqE75hMfDbs7NHOltpuRPFpS+G6W+jGgKId/bn3w6b0LHV0f8aPevcFwkMUrmuY/pJGiqZrm4yR1fv/3ByXLEsd5Y80C+XoWuy7/tg0d3VUYDjMW+w7IOOthbLD+upLlsycIOh3i3TAbHD+dEDP44GnFyBvuJL0RpChDf/poE7gx4vsK4lPlqE7V5BJ3TVr+y171dmuzNzAPoMhd7b1Cqlv27XUdleVd9VetfoH+wWGC2UqgVTJl6v83G5mzLDidHisJpfV7PJ4wJVTptgkaZ/B8n7p0B7kPxBSbfXQVt+tbcatJpfN7ObxAdzH4QCAa9euxcfHQ39iIpTwpHK+TClQBQtik2QYj+x3nKixhZo5c+b84Q9/SExMpLoQyHDv9DMJzhaTYKetmJgYHo+F/zUW/pcAALdu3brPM37mwk5bcjlJY/FJhp22LBb4ow3oADttBQWxc8IHdtrS6XSsvI9kp634+HiuTcgYamtruTYhB8Ww05ZKpeJaGYzBZDJxrQzG4O/vzx1bjMFoNHLHFgfFsNNWZGQkd7/FGJqamrj7LQ6KYaetuLg47kzIGOrq6rgzIQfFsNNWQkICdyZkDDU1NdyZkINi2GmLG6HGJLgRahzUw05b3HhCJsGNJ2QSUVFRXCuDMTQ2NnKtDA6KYacttVrNjctgDAaDgRuXwRi4kdVMghtZzSTi4+O56xZjqK2t5a5bjCEkJISV1y1WzW4yceJEkUhEEITBYFAoFEKhkCAIsVi8e/duqkuDAxtmx+pBoVDU19d7f3Y4HAAAPp//yiuvUF0XNFh1uhg9evQdjQuNRjNnzhzqKoIMq2zNmDEjJubnZaH5fP6sWbPY1Dhkla3IyMjMzMyef0ZHR9++gB0LYJUt74qDGo0GACAUCtl0DvTCNluRkZEjR44kCCIqKmrmzJlUlwMZxrQJzXpnZwfu9mHayaeGz604q58wfkKtDwvYYoCQ+/upw4R8AQMubwy432qu7j571NCpdUb3l1k6Ia/GJhRhhg6cIEC/oYp02i8OQXdbbfXdx3frsp6NEImRrPbaQ/n3HWIpPzOX1mvW0/q61dmOH9nZnvN8FGpVAICMSSH2bk/5EcircMGF1rbOHu0ckQdzbbj7kzExuP6arduKaunLR4fWthqqbKpAIakpMdDZhmRZWSjQ15YLJ8QynkROaqs1MFzcZeCOrYcH4wGTjuwdhzvcHho3u+hri+NuOFtMgrPFJDhbTIKzxSQ4W0yCs8UkOFtMgrPFJDhbTIKzxSQ4W0yCs8UkOFtMgjFjnnyho6P9b9s/LisrsVotUVEx8+YunjB+EtVFwYRVtlxu1/Xr16bmzVQp/YtPHfvD2jUaTVRS/4FU1wUNVtmKCNd89ulu78D37Oyp02ZMKCn5kbNFX6prbnz2+V+qqioAAG6322DQU10RTFjVyjh/ofy/XlzoxPHXVr/9v29vUCpVHoJV74qz6tjaseOTiIjItX/YLBAIAAASsYTqiiDDqmPLZDYmJvT1qsJx3NZtY9k8DKw6ttLS0ouKDhw6vF+pUO3+urCry1xfV0N1UTBhla0li1YY9LoPt2xUKJQ5U6bPnvnM+5vXtrW1hoWFU10aHFhlSy6X/+7t9bdvGTlyDHXlwIdV1y3Ww9liEpwtJsHZYhKcLSbB2WISnC0mwdliEpwtJsHZYhKcLSbB2WISnC0mQV9bPD4WHCUiOalIyheKaLxPqC7gnmAYcNo9hnYHmUkbq6zqcHLnU3kY6GurqanJ7K7SNnaTltFicgrEzp/Kj5KW8WGhqS29Xr9kyZLlb2bVXOxquE7SQnXH/946cX50RUXFrl27yMn4sNB0xrsnnniitLSUz+cTHuKrTU0xA+QKtV9guBh6IgwjzAaX2YCfPqh95o0YVZAfAGD16tXZ2dlPPfUU9HSPCB1t5eTkbNu2LTz858EUl08aG6q6CQD0zZAvY2IZ30+IRSRIhk1S8/g/T//57LPP/uY3vxk4kGbjfAmasXTp0kuXLlFdBUEQRHZ2tlarpbqK/4Bex9brr78+fvz4rKwsqgsBAACPxzNs2LDy8nKqC/kZGrUy/vSnP6WmptJEFQCAx+N9++23M2bMoLqQn6GLrV27djkcjnnz5lFdyH8QHh6+Zs2a5557jupC/gUtbBUXF5eVla1evZrqQnph8ODB06ZNe+utt6guBNDCVl1d3b59+zZt2kR1IfdkypQpffv23blzJ9WFUN2Cd7lcI0eOLCsro7AGH3nzzTfHjBkzceJEKougtkk6d+7c5uZmamvwnRdeeKGiooLCAqi09fLLLxcXF1NYwC9g+PDhDoeDquyUXbe2bt2anJw8evRoqgr4ZXz55ZcUNlypsVVcXGwwGOjTMvaduLi4FStWbNiwgZLsFLQydDrd/Pnzi4qKSM4LkQ0bNsTExFCwvhf5J9+8vLzGxkby88Jl3rx5lZWVJCcl29b69euPHDlCclIUOByOOXPmkJyU1OvWoUOHurq66NMT+CgIhcIVK1asWrWKzKTkXbdMJtO0adOOHTtGTjpyWLt2bb9+/cjr+SXtKH7++eevXr1KWjrSmDlzJmk3+CSdCb/44osBAwbQ7lEsDN555x3S+qPJsNXe3r5r166XX36ZhFzk079//6FDhxYWFpKRjITjd8mSJRcuXCAhEYU8/fTTJAwLQH5sfffdd4mJiWlpaagTUcvbb7+9ceNG1FmQ29q4cePKlStRZ6GczMxMs9l85swZpFnQ2vrkk0/mzJmjUCiQZqEJq1atev/995GmQGjL6XQeOXJkxYoV6FLQij59+qSlpf3www/oUiC0tXPnzieffBJdfBqSn5+/fft2dPER2tqxY8eCBQvQxach/fv3l8vlZ8+eRRQfla2jR49mZ2erVCpE8WnLggULDh8+jCg4KlvffvvtyJEjEQWnM6NGjSoqKuruRvIiExJbRqOxoqIiMzMTRXD6M3ny5EOHDqGIjMTW8ePHKR7JRSlZWVknT55EERmJrZKSkoyMDBSRGUFGRkZJSQmKCZiR2Gpvb39sT4NecnJyUPRrwLdVXV2N47hIRPbr+LQiIiLi4sWL0MPCt1VZWZmUlAQ9LLNISkqqrKyEHha+rdbW1kGDBkEPyyz69evndDqhh4Vv6/r162q1GnpYZhEcHHzu3DmXywU3LHxbYrE4KioKeljGMWbMmNbWVrgx4ds6d+6cUqmEHpZx6HQ6g8EANyZ8W9HR0f7+/tDDMo5+/fpZrVa4MSHbIgjiwoUL3lV6HnMMBoPNZoMbE7ItHMfT09PhxmQoERER3jURIQJnrO6LL75oMBj8/PzcbndNTU18fLxAIHC5XF9++SWMIplEQUEBAADDMK1WK5PJJBIJhmEYhkHZFXBOWWPGjPnggw8cjn9N63Pjxg3vWRFKcGaBYdjNmze9PxuNRu8sKbD64eCcCWfPnq3RaO7Y+MQTT0AJzixycnLE4v+YPUylUi1duhRKcGjXrWeeeeb2vkGlUjl37lxYwRnEjBkzoqOjb98yYMCAwYMHQwkOzVZeXt7th1diYuLjNoTGi1gsnjJlCp/P9/5ToVAsXrwYVnCYbcJ58+Z5Dy+VSjV//nyIkZnF9OnTe3pzUlNTITaSYdrKz8/3Hl7x8fFjxrBqCcCHQiKR5OXlCQSCwMDARYsWQYzsU5vQ5fR0W3x6EjpnxqJPP/20YObirs4Hd2gSBCFXCW6fw5H+4A6Pw/bgXTFpwrTv9h+Li4tLjE154K4gPEAZ6JOIB9xvVZ4xXz5pMrThEjnfl3APhUDEM2nxiDjJoDGq+BQ59PhwuXzSePGEye0iYN/yAqmS39HgiO4vHfKUf2Qf6X0+eT9bZ44YdC3OtDFqhdoPcoG3YTbg5d/r+qTJBo6g7+DD4r1a3E4kjfBXqlHNP27S4aUHOoY85Z+Qes8/3HvaKvveYNa7hueEICruDk7sbotJkqSMpKOwH3drMT/ekHGBJOQ6uqM5dZQqMa13Yb23Mjo7cF2zgzRVAIAxs8JqLlkdNjdpGX2kta7bYfeQowoAMOGZiEsnjff6be+2dM0OgiD74u9yEroWnOSkD0TXjJPZDsIwzG7x6Ft7n5q7d1sWkzs4Cv7k6/cnLE5i0sEfy/CIWLtcQRpSd4UmUWrs6H0/9N5wdDo8Tjviou7CbnW7nPBbno+Iw+bh8UntnrZ2uTz3uCBQP1Mrh+9wtpgEZ4tJcLaYBGeLSXC2mARni0lwtpgEZ4tJcLaYBGeLSUCzlTt17NY/b4YVjaNXuGOLSXC2mATMV3dqa2++9PLSmzevBweHzp71TG7OdIjBmcWhw/v3frOroaFeLldkjnhy2XMrVSoI77TBtFVdc2PO7AXjn5p05Oh3729aa7d3z5r5OI4B/ezzv3z+xbaxYybMmjG/02goLy/l8+HsZ5i2ns6aUjDnWQBAbs70l15e+tnnf8mZMl0ikUBMQX+02o6dhZ9mZU1+8/Xfe7d49wkUkFy3+Hz+1NyZNputqqoCRXw6c+58mdvtnpo7E0VwVK2MwKBgAIDVakEUn7YYDHoAQHBwKIrgqGwZjZ0AALWapIFd9EEuVwAADJ16FMFR2Tpx4geFQpmQ0BdRfNoyOC0dAHDo0L6eLRDnOIHZyig6clCtDhSLJWVnSkpLT/7qpdeEQlTjkGlLVFRMzpRpBw7uNZtNGRkjTCbjgQNfb960LTQ07NGDQ7MlFIrmzF5QdORgY+Ot8HDN6lf/Z3L2VFjBmcUr//1GWFjEwYN7S346ERwUkpExAtaUFNBsfb27CAAwe9YzsAIyFx6PN3/e4vnzoL0S+XNk6BE50MHZYhKcLSbB2WISnC0mwdliEpwtJsHZYhKcLSbB2WISnC0mwdliEpwtJtF7H7xQjHkA2fNlSGR8PyHtZugSy/hCEalVyZQC3j0ejfR+bCkC/LS3kKyldx+aa2yqYIQTSv0yZCp+RyOps1E0VlnVob0/xe3dVkiUCPpMYQ9EIMRComi3DlRolMjjhr/u2b1wOj3yAEHAQ9lSBPhpEsXFX7chru1nfihsHjhcKfCj3XU0OFKsVPuVHeogJ93Rz5uHPBVwr9/eb8a7a6Wmmxctg8YEBoQK+QIk+9Hp8Bi1jrNH9BlP+8cNpO8UhWePGtobHEnDAwIjRDwe/NOOo9tt0uKnv9OOmx0cEX/P8bIPmE2y7pr14gljW52dL/CpRAIAj8fN5/k0AZBQwnPY3JF9pYPH+t+nRJpw43zXxRPGLoPL7fJpIiEP4QEA4/lwRZH7CywmV0x/6dAJAUER97sW+LrWgqPbp3O33W7Pz8///vvvffkwIAiRlHYTOz0AAjjsPu2KdevWpaWlTZo06cEhCULs237wdRSNSOLTmdADMKfb5uOHGQnm664gMJwncMPdFezdrWwEvq2+fR+78bm9olKp/Pwg3z7Ct+VdcobDZDJBX+gTsi0Mw1JTU+HGZChBQUF3LD/z6EC2JRAIzp07BzcmQ2lpaYG++Dt8W2lpaSiWqGccAQEBcjnk+334162qqiroq1sykdraWga0MoKDgzlb3te2GHBsCYVC7wp8jznt7e0qFeSlI5AcW1qtFnpYxqHVaoODg+HGhG+rX79+Fstj93L4HXR1daWnp0N/NRS+LaVSWVlZCT0ss6itrUVx8YZvKy4urq6uDnpYZlFfXx8bGws9LHxbCQkJ3Mrver0+JSUFelj4tkJDQysqKnQ6HfTIDOLUqVPx8fHQwyJ5YpKcnHz16lUUkZnC1atXk5OToYdFYmvEiBH19fUoIjOCysrKrKysniWPIYLEVnp6+oEDB1BEZgTHjx9HcRpEZSs2Ntbj8TQ0NKAITn+Ki4sRraOO6kn/1KlTy8vLEQWnM62trRqNpk+fPiiCo7I1bty4wsJCRMHpzN69ewcOHIgoOCpbMTExgYGB58+fRxSftnzzzTfTpk1DFBzhmKc5c+aUlJSgi09DSktLJ06cGBBwz6HRjwhCWxMmTDhy5EhLSwu6FHTjo48+ysnJQRcf7XjCZcuWbdu2DWkK+lBSUqJWq5OSktClQGsrLy/PYDA8Jo+7ioqKXnjhBaQpkI/VnT59+nvvvYc6C+UcOnSIIAh0rUEvyG2NGTMGx/HS0lLUiajlvffee+ONN1BnIWMc/BtvvPGPf/yDhERU8dlnn/3qV7+SSqWoE5FhS6PRpKamfvTRRyTkIp/r168fPXp01qxZZCQjyKKgoKCqqoq0dKQxderUhoYGcnKR90bQ+vXr//rXv5KWjhy++OKLefPmRUVFkZOOPFvR0dHDhg1bt24daRlRc/bs2ZKSktmzZ5OW0dc3WWHx6quvTpkyZdy4cWQmRURGRkZZWRmPR+Ibi+SccG/nxRdftFgs5OeFy7vvvlteXk5yUgreZH311VeffRbaklSU8PHHH4eFhaWnp5OclwJbsbGxS5Yseeutt8hPDYXi4uKbN28uXbqU/NRkX7d62LRpU3h4eEFBASXZfzEdHR3Lli3bv38/NelJPvPezsqVK0tKSigs4BcwcuRIm81GVXYqbREE8corr7S0tFBbg+/89re/vX79OoUFUHYm9OLxeIYNG8aI8Tavv/76+PHjs7KyKKyB4tlNeDze3r17X3vtNWrLeCA7duwYOnQotaqotwUAiIqKmjVr1vLly3u2jBo1auvWrZQWBSZOnNjz8549e5qamkjqt70v1Nvydgrk5+e///77AIAnn3yyu7v7zJkzFNZTWFhoNBqHDh3qHRhz9epVEp5d+QJdXt2ZNGlSc3NzRkYGQRAYhun1+vb29tBQJOvQPpCysjK3241hWHp6ukAgOH36NCVl3A0tji0vW7du7WnymM1mql6wtFgst79y4XK5srOzKankbmhha/r06UOGDLl9S1dX16lTpygp5tq1a3e8N63VaseOHUtJMXdAizPhwIEDMQxraWnBcRzDMO98UVeuXPH+Fnd4Th/SN1d3Yxhm1kOe5woAoAryk6kEqaNV0f2kAIDy8nKTyYT9e85OgiD8/f0jIiKg5/0F0MLWO++809ra+s9//vPw4cOdnZ0dHR0AAKvVWl1dHaKO+XJ9w8j80OgkpSpQ6PHAvzvEHR59i/38MaNZ70rOVJ4+fdp7QhaJRMHBwRkZGXl5eTSZaoziu+O7KS0tLSoqunLlSmtr63+/+NvumoHTX4b/unWvnNzbJlHh7/35OaFQqNFoJk6cmJWVpVAoyMnuC7Sz5aWlpaWoqCjINSk9K0geQN6U/sV7Wq80752cP5omB9Md0KKVcTcRERGzZyzoaLSTqQoAIJIKssfOp6cq+toCAOhb8ZgksmeID4kWW80ukpP6Dn1tedzAYoLfAnxAUhewmdwkJ/Ud+triuBvOFpPgbDEJzhaT4GwxCc4Wk+BsMQnOFpPgbDEJzhaT4GwxCc4Wk2Czre8O7Rs3Pl2vZ88Mv2y2xT44W0yCFqNoIHKzuurDLRurqioC1UFRUTFUlwMZVtlqaKh/ZdXzKqX/sudW8vmCL3awbfo2Vtn6818/4GG8j7Z85u8f4H2BZfMH7JnwgVXXLRzHy8tLs56e4lXlXRaR6qIgwx5bJpPR5XKFh9FiUC0i2GNLoVACADo7DVQXghD22BKLxRpN1I8nfoC+JjR9YI8tAMDCZ59vaWla+dLib/Z9tf/bPf/4agfVFUGGVdfhrAnZFkvXV1/t+MtfP4iNiR8wIKWx8RbVRcGEpuPgAQD1FbaLxcbxc0ltNdRc7NI12SbMp+adzAfCqjMh6+FsMQnOFpPgbDEJzhaT4GwxCc4Wk+BsMQnOFpPgbDEJzhaT4GwxCTrbIqRysh8R8ARAKKHvPqFvZaogv/Zb3SQn7WzHJXI+yUl9h9a2JAq+x03qAx2nwx2sEZGZ8aGgry0eD0seoTqxp420jDWXzHaLO3agjLSMDwt9n0Z6qSgz37xoGZUfKhQjPEF5PMSNc6bWGlveC7QeMkV3WwCAG+e7rpSYTDpnaLSk2+rTtD4et5vH44F/zwj5ADDQXt+dOlI1enrwo9aKGAbY8k7AaTW5jTqnb7sf/P73v1+6dKlGo/Hlw2IpLzCCvteq22HGKBoMw+T+Arm/r9UaHbVqDdAkShDXRTb0bWVw3A07bclk9G3XPQrstGW1WqkuAQnstBUTE0Pq4ptkwcL/EgDg1q1bHo+H6irgw05bGo2GO7YYQ3NzM3dscVAMO23J5WRPTU4O7LR1xyI/rIGdtqKjo7lWBmNoaGjgWhkcFMNOW/Hx8dyZkDHU1tZyZ0IOimGnrcjISO5MyBiampq4MyEHxbDTVlBQEObjgCdGwU5bOp2OEWO5HhZ22mIr7LQllUq5MyFjsNls3JmQMXAj1JgEN0KNg3rYaYsbT8gkuPGEHNTDTlvc6E8mwY3+ZBLc8y0mwT3fYhIYhnH9hIyBIAiun5CDYjhbTIKdtsLCwrg2IWNoa2tjZZuQGXPR+MiQIUPuaAoSBJGZmbllyxbqioIJq46t/v379zTfvQQFBT3//PNU1wUNVtmaO3euWCzu+SdBEIMGDUpNTaW0KJiwylZubm50dHTPPwMDAxcuXEhpRZBhlS0AwLx580QikffASklJSU5OproimLDNVm5ubkxMjPfAWrRoEdXlQIZttgAACxcuFIvFKSkpKSkpVNcCGYpb8N1Wd0OlVd/qtJjcVrPLhbuh/AHdargVGhoqFol9+OwDUAQICIKQqQQBIYKIOAm1805SZuvySdO1MrNJ51RHKgDGEwj5AhGfL6DdsU4QhMvuduFugiC6OiyAIPoMlg8e6+/71JYQocDW5VOmnw7og+NUEpVY6g/hz59McJuzS99tuGWMT5GPmqoWSUidPJ5UW902z3d/a3c6eSGJAXw/+k6S7wv6BrMsMcQnAAAFjklEQVS53Tw8OzApg7x5b8iz1VrX/c1HLQkjNCKpHzkZSaDpSnv8AFFmTiA56Uiy1dmB79vaFveET5NIM4uOm/qEZOHQp/xJyEWGrY5G+8FP2+OfiESdiCraq/URUbzR+UGoEyFvg3k8xFebmlisCgAQmhjYVOOsOteFOhFyW4c+bYsfRuvlJqAQPiDkYnGXWe9EmgWtrZrLFrORkCqZsZLBIyL2l536Vo80BVpbJ/fpA2PVSFPQB1WYvL0R17U40KVAaOvGhS5pgFgko2N7vXD3W+s/mA09bGBswIUfTdDD9oDQVvUFq1DGsK6KR0QRKLlxzowuPkJbtyqtyhApuvg0BONhqhDJrUpU79Gi6ppsrukOiZXz+Ej+GgydLd8e3nyj5oyfQKSJ6Jc9YXmUZgAAYHvh6uCgGD5fUHZ2n8vtTOo7cnruaxLxv3qGLl45euT4J53G1tDgeIJANSJKHiRrreuOSULymjqqY8tidOEOJHvEbNZt2bbMZjNPnbxqysSVbrfzo09eaG2v8f72REmhobNlyTP/lz951eWr//znj9u9289fKtr51RqlPDB/8q/79Rne0nYTRW0AAJ6A19GIIwqO6tiymV08AZJ+26MnPpXL1C8s3sLnCwAAQwdlr9s8o+zs/vwpqwAAwYHR82b+L4Zh0ZEDL1ccr6o+nQNecjod+w+9Hx8zeNnCD/l8PgBAp29EJEwgEpjafVqA75cERxTXbvUIREhag9dv/GQ0tb/5ztieLW6302hu9/7s5yfuGVKo9g+vb7gMAKi7dclqM47OLPCqAgDweKieAPiJ+B4Pqs48VLYIQHhcSM6EXRb9gH6jpjz94u0bxaJeHlvw+X4ejxsA0Glq88pDUc8deDyEE80lAKEtuUrgdiG5T5RKlFabKSQ49iGKkQUAACw2I4p67sDlcEsUqPYqqlaGVCnwOJGcvvvEZ9Q3XGpsruzZ4sAfsOR4RFgfDOOdv/Q9inruwOVwyVWobKGKGxDqR6B5byBr3HOVN0q2ff6rJ0fOU8jU12+WejzuxfM33q8Y/7AnhuSWndvvcjn69Rlh7tJV3ihRyJE8QnQ5XJGJQhSREdoKDBM5u10OmxP6k+KgwMiVy7YdKPrTsROfAQyLDO8/cvisB34rf8qvBQLhhctFVdVlcdGDIsL6dlmQ9MB2dVhjckNRREb7NLJ4r7ajnR8Uq0IUn4bg3a6mi61Lfv8Q19SHAuEwq35DFS17O+/zAXOXfsOfeulaJQgCAALDermm5kx8aXh6PqwKK6tKCve81euvgtSROkPT3dufHvfck5lz7xXQorcNGKGAVd7doH3Sv//PrZhYpgztvRvG7Xab/n2fdDsej4cgiJ57o9uRSlRiMbROHRy3W6yGe/wSA6CXPSORKHu6su7m6pG6F/8vAeOhmk4ArS2jFt/zYUviiCh0KehDR7Uhpg9/2CSEz/PQPo30DxYmZciNrcgHLFCO0+ECbhypKjLGZYzMDcJNFovhAbdETKemtDlvWRjqLGSMO5/9SmTHDZ29C1XPNOXUn23JfT5MLEM++pik0Z8EQXyypj6sf5AiiFXPJwkPUXumeery8KBwVHfEt0PqOPivP2zmiaUBkUrSMiLForfdOt9esDoqMJykQV1kv2NSVmS4cMwYkqhWRyK8L0GNzWjX1nYGhgpynkN+rbodCt4IslvdP36t79S5ACZQhkhlagnJBfxiHFanWWt1mO0Y8IydEaRJJLtyyt62M+nw6ku26osWpxPgdo9AxOf78TE+7Waq4/H5uM3hxt1+Yj5uc8YNlPUdLItIoOYvjPq5aBzdbrPBZTO7rCY37nADQC9bIglfKMakSr5MIVAGUjw2knpbHL5Du/d8Oe4DZ4tJcLaYBGeLSXC2mARni0n8P9I5HBy1G647AAAAAElFTkSuQmCC) - -With the reducer, you can see that the values added in each node are accumulated. - - -```python exec="on" source="above" session="1" result="ansi" -graph.invoke({"aggregate": []}, {"configurable": {"thread_id": "foo"}}) -``` - - - - - - -!!! note - - In the above example, nodes `"b"` and `"c"` are executed concurrently in the same [superstep](../concepts/low_level.md#graphs). Because they are in the same step, node `"d"` executes after both `"b"` and `"c"` are finished. - - Importantly, updates from a parallel superstep may not be ordered consistently. If you need a consistent, predetermined ordering of updates from a parallel superstep, you should write the outputs to a separate field in the state together with a value with which to order them. - -
Exception handling? -

LangGraph executes nodes within "supersteps", meaning that while parallel branches are executed in parallel, the entire superstep is transactional. If any of these branches raises an exception, none of the updates are applied to the state (the entire superstep errors).

-

Importantly, when using a checkpointer, results from successful nodes within a superstep are saved, and don't repeat when resumed.

- If you have error-prone (perhaps want to handle flakey API calls), LangGraph provides two ways to address this:
-
    -
  1. You can write regular python code within your node to catch and handle exceptions.
  2. -
  3. You can set a retry_policy to direct the graph to retry nodes that raise certain types of exceptions. Only failing branches are retried, so you needn't worry about performing redundant work.
  4. -

-Together, these let you perform parallel execution and fully control exception handling. -
- -## Parallel node fan-out and fan-in with extra steps - -The above example showed how to fan-out and fan-in when each path was only one step. But what if one path had more than one step? Let's add a node `b_2` in the "b" branch: - - -```python exec="on" source="above" session="1" -def b_2(state: State): - print(f'Adding "B_2" to {state["aggregate"]}') - return {"aggregate": ["B_2"]} - - -builder = StateGraph(State) -builder.add_node(a) -builder.add_node(b) -builder.add_node(b_2) -builder.add_node(c) -builder.add_node(d) -builder.add_edge(START, "a") -builder.add_edge("a", "b") -builder.add_edge("a", "c") -builder.add_edge("b", "b_2") -# highlight-next-line -builder.add_edge(["b_2", "c"], "d") -builder.add_edge("d", END) -graph = builder.compile() -``` - - -```python exec="on" source="above" session="1" -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/png;base64,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) - - -```python exec="on" source="above" session="1" result="ansi" -graph.invoke({"aggregate": []}) -``` - - - - - - -!!! note - -

In the above example, nodes `"b"` and `"c"` are executed concurrently in the same [superstep](../concepts/low_level.md#graphs). What happens in the next step?

-

We use `add_edge(["b_2", "c"], "d")` here to force node `"d"` to only run when both nodes `"b_2"` and `"c"` have finished execution. If we added two separate edges, - node `"d"` would run twice: after node `b2` finishes and once again after node `c` (in whichever order those nodes finish).

- -## Conditional Branching - -If your fan-out is not deterministic, you can use [add_conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_conditional_edges) directly. - - -```python exec="on" source="above" session="1" -import operator -from typing import Annotated, Sequence - -from typing_extensions import TypedDict - -from langgraph.graph import StateGraph, START, END - - -class State(TypedDict): - aggregate: Annotated[list, operator.add] - # Add a key to the state. We will set this key to determine - # how we branch. - which: str - - -def a(state: State): - print(f'Adding "A" to {state["aggregate"]}') - return {"aggregate": ["A"]} - - -def b(state: State): - print(f'Adding "B" to {state["aggregate"]}') - return {"aggregate": ["B"]} - - -def c(state: State): - print(f'Adding "C" to {state["aggregate"]}') - return {"aggregate": ["C"]} - - -def d(state: State): - print(f'Adding "D" to {state["aggregate"]}') - return {"aggregate": ["D"]} - - -def e(state: State): - print(f'Adding "E" to {state["aggregate"]}') - return {"aggregate": ["E"]} - - -builder = StateGraph(State) -builder.add_node(a) -builder.add_node(b) -builder.add_node(c) -builder.add_node(d) -builder.add_node(e) -builder.add_edge(START, "a") - - -def route_bc_or_cd(state: State) -> Sequence[str]: - if state["which"] == "cd": - return ["c", "d"] - return ["b", "c"] - - -intermediates = ["b", "c", "d"] -builder.add_conditional_edges( - "a", - route_bc_or_cd, - intermediates, -) -for node in intermediates: - builder.add_edge(node, "e") - -builder.add_edge("e", END) -graph = builder.compile() -``` - - -```python exec="on" source="above" session="1" -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/png;base64,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Uv3www8zZ850Op1UbhcJP7nke/LkyQ0bNlB/eZ/qM+QKhSIvL++pp55i/IUJf7jku2vXrm+//XbZsmXUbxrBpZ2AgICioqLXX3/92LFj1G8dI8uaNWtOnz793XffIdk6smuS+fn527Zt++GHH1AVABuz7/J9++23eTzeu+++i6oAlBfTP//88+rqagqusiDB1Lv02FTFAAARpElEQVR8jUbj3Llz+/TpM2PGDIRlIJ4FMm/evPT09OHDh9fVUbf6OfbQjhw5MmLEiFdffRX52SH0j3fJyspKTU3Nzc196aWXsrKyUJdDGubNDluxYsXt27d/++031IUA9D2uR3h4+O7du4uKit577z3UtZCGSbPDrFbr1KlTQ0NDfWe+lE8E12Pp0qVdunSZO3duWVkZ6lpIEBQUxIyDs3379r322mtvvfVWTk4O6lr+gn6ocKfRo0f36NHjtddeGzRoENqx/6NrbGxkwMHZggUL2Gz26tWrURdyNx/qcT0iIyO3bt3qcDhycnJofcQmk8m4XN/qFx7I8ePHn3zyyQEDBrz//vuoa2mFj+7Z2bNn9+vXLzc3d9q0adRMkyOd0Wik75Xt5cuX19fXHz161GevWvtcj9uiQ4cOu3fvrqysfO211+jY9UokEjpOazx58uScOXM6duy4cuVKn02t7/a4Ld58880LFy7k5uaOHz9++vTpqMt5AGazmXbTGhcvXqzVapctWxYQEIC6ljb4bo/bIiUlZffu3WazecKECTR6wIRKpRIKYT1+kXS//PJLWlpaenr6qlWrfD+1NOhxW7zyyivDhg1btGhRWlravHnzUJfTNo1GExsbi7qKthkMhoULF8pkstOnT9PoBk9vnyzpOzZt2vTrr79OmzatT58+qGtpxdixYysrK1vu8iUIgiCITp06UTzP2ksbNmw4c+ZMdnb2U089hbqWB0ODocJdpkyZ8vHHH//000/z5s1rbKTuYd5e6tu3L4vFaum6WCyWUql8/vnnUdd1t3Pnzo0dO1an061cuZJ2qaVlj9uisLBw2bJl2dnZubm5qGv5S319/UsvveTpdD26d+/+1VdfIS3qfzgcjvfee6+2tnbhwoW0GMy0in49bovMzMz9+/fbbLYRI0acPXsWdTl/Cg0NvXMpY4VC8cwzzyCt6H/s2rWrd+/e6enp33zzDX1TS+/gerz00ktfffXV3r1758+f7yMjhwkTJrRkol27dpCW5H5QFy5cyM7Orq6uPnXqFANm4XGWLFmCuoZHJZPJMjMzuVzuvHnzLBZLWloa2nqkUqlarb5w4YJCoXj55Zfj4uLQ1mMymZYsWXL48OF33nln8ODBaIshC+173Bb9+/ffv38/i8UaOHDgoUOH0BYzbty4qKiohISEvn37oq3kP//5T1ZWVp8+fb777juoyydSjMYHZ/ei0+k++OADDoeTm5tL4q/q7EFdRYmFy2XVV1m9eb/T5WKxWBzv5pKrIgRcPispTZbUXfbIlf7pt99++/nnnxMSEl555RWy2vQdDAyux7lz5z788MMuXbrMnz//EefFEgSx+YOqpCcUAcGCwDA+AOSfpXc6iMZaa/V1s1jKeWpk0CO2VlZW9tFHH0ml0jfffDM0NJSkGn0LY4PrUVBQ8NFHH82aNevZZ5996EY2Lq94YmhIRDsxqaW17swBDeFy988OebiP22y2vLy8S5cuzZ8/H/lYHyqGB9dj5cqVFRUVWVlZD7Hw3ukDWhaHk9RdAae0Vpza3ZDUTRLTUfKgH1y3bt2BAwcmTJgwevRoOKX5EOYcnN3H3Llz33rrrX379uXm5j7oNJ3yi+bAMErvwJEG8G5da36gjxw4cGDIkCFGo3Hz5s3+kFo6TbJ5RMHBwXl5eRcuXHjvvfcSExPnz58vk3l1GMTls4OoDW5wlKDiisnLN5eUlOTl5YWGhm7evFmlUkEuzYf4S3A9UlJSNm/evHv37mnTpg0ePHj27NltfqS2vBlQO2eKIFgGdduLjmm12k8++cSzNkVycjIlpfkQvxgq3CUrK6ugoIDH42VmZv700093/XTAgAG//PILotJa8cEHHwwdOvSuF1etWpWdnd27d+/ly5f7YWr9NLgeM2bM2Lt37++//56dnV1cXNzyuk6nW7NmjU6nQ1rdn44fP3748OE7L2Xn5+f36tVLIpEcOHDg74H2H/4bXM9tYYsXL16+fPl//vOfV1999datWxkZGWw2u7q6evny5airAwRB5OXl6XQ6giCysrJOnDgxbty40tLSX3/9lV53McHgX2PcVrVr127NmjXHjh0bP358y11ixcXFP/744/jx4xEWtmTJkurqas9/19XVbdmy5ZNPPkE+88FH+HWPe6eMjIw77220WCzr169Xq9Wo6tm/f39hYWHL/7JYrJKSEpzaFji4f/r7tKna2tqlS5ciKcZNEGvWrDEajXe+6CPDbh+Bg/unxsZG4n+53e5z584hubJYU1Nz69atllvWWmpIT0+nvhjfhMe4fzp79uyOHTuMRqPFYrFarXa73WQymc1mFoHgxlcuhzNgwAChUCiVSkUiEZ/Pl0gkMpls5MiR1Bfjm3Bw/9LqxdIvX0ewdGRYWNhLcz6kfrs0gocKGC3h4GK0hIOL0RIOLkZLOLgYLeHgYrSET4eRr7auZvXqFWfPFfH5gvaPdXj++Zc6JHVCXRTT4B6XZI2NmjlznzcY9a+8/MasmXMdDser/5hRXn4DdV1Mg3tckm3c9K0yIPCTj9Z4nlwyaOCwKdNG79pTMOflN1CXxig4uCQrKjreoK4fNrx3yysOh0PdUI+0KAbCwSWZVtfYs2fvmTPm3PmiRCJFVxEz4eCSTCaT6/VNMTF44ixc+OCMZKmpT1y69HvptZKWV5qbH2yRBMwbuMcl2bPTZp46dWz+my9PnDBFqQwsLj7hcruWLf0EdV1Mg4NLssiIqFUr16756rPN369lsViPPdZhzGhaPhnTx+Hgki8mJu6D5Z+hroLh8BgXoyUcXIyWcHAxWsLBxWgJBxejJRxcjJZwcDFawsHFaAkHF6MlHFyMlnBwMVrCwcVoCQf3fgg3ERQuoHi5RjaHJZZzqN0m/eDg3g+LzXLa3QatncqNNjXY+EL8e2kD3kFtiE4SUxxci8kVFkfpIwHpCAe3DT2zgo7mU3ePrvq29XapqVM6dY8Opim/eAj1I9JrHfmf3x40NTIgmA91Q5Ulpj+OaCfOi+LycYfSBhxcr+g1jpO7GyuvmOO7yAzatp9XCgBwu90sFovl3eNUhWJOxWVTpyfl/bNDHrlYv4CD+wDsVndjrd3t8mqPrVu3LikpqWfPnt68mctnhUQLvEw5hu85ezB8ITs8Xujlmx3cekFAVGQ7EeSi/BQeS2G0hIMLC5/Px1/98ODgwmK32/HxAzw4uLAolUoej4e6CsbCwYVFp9M5HF6dOMMeAg4uLAEBAXw+3AsW/gwHF5ampia7ndJJDn4FBxejJRxcWAQCAZuNdy8seM/CYrPZ3G436ioYCwcXFqVSiQ/O4MHBhUWn0+GDM3hwcDFawsGFRaVSCYXeTiXDHhQOLiwajcZqtaKugrFwcDFawsGFJSAgAE+ygQcHF5ampiY8yQYeHFyMlnBwYZFKpVwuvqUPFhxcWEwmk9PpRF0FY+HgYrSEgwsLnh0GFd6zsODZYVDh4MKCb0+HCgcXFnx7OlQ4uBgt4eDCgtdVgAoHFxa8rgJUOLgYLeHgwoIXBIEKBxcWvCAIVDi4sCgUCnxwBg8OLix6vR4fnMGDgwsLvmwGFQ4uLPiyGVQ4uBgt4eBitISDCws+qwAVDi4s+KwCVPjJkiQbNGiQTqcjCKLlrAJBEDExMQUFBahLYxTc45KsV69ed6bWcw/P1KlTkRbFQDi4JJs8eXJoaOidr8TExIwdOxZdRcyEg0uypKSkHj16tAzABALBxIkTURfFQDi45Luz042IiMDdLQw4uORLSkpKTU0lCILP50+aNAl1OcyEgwvF1KlTw8PDIyMjcXcLCT4dBtxuovySSVPjMOmcZoOLxQZWMwnrIdTUVIvF4oAA5aM3JVPynE63RM4JUHFDY4QRiaJHb5Pu/Dq4184bL50w1tywBEZKOXwuV8Dh8TlcPsfX9giLBRxWp8PmcjvdzfrmZoMjtqMkpY88PN5/E+ynwa24bC4saBQFCIVykSxYjLqcB+NyuAxqi0ltksrZfceplKH+eIOQ3wWXIMDutfXaBmdIYqBQRu9fuaHBrL6ha99d2ntUEOpaqOZfwbXb3BuXV4U8FiRT0ayXvQ91uU7AdYycGY66EEr5UXDtNtfG5beiU8L5Iqatt6yvM7EczSNnhqEuhDp+dDrs6wXlCU9GMS+1AABFmJTgi7d9Xo26EOr4S3A3fVCV+GQEg+8DU4RKuGLR4R/UqAuhiF8E98QujTxcIZILUBcClzJKodMQN/4woS6ECswPrlHnuHLKKA+Voi6ECrJQeeF2DeoqqMD84BYWNKoSAlFXQRG+mCcKEF08rkddCHQMD26T2q5vdAWE+2J3W3TmpzcWpRsMJHeQQbEBV4qYP1pgeHDLL5nZfP+6Y5En5FpMLvVtG+pC4GJ4cK9fMEsZdK3BSxKl+MZFhne6DDyp2cJudxOAJQ2EMhPFbrfuPbjm/B/7HA5bsCq2b0ZOSpdBAIDCE1suXDyY2WvS3oNrjEZNZESHCaPeCgmO83yquqZ0x54Vt6qvyGWq4KAYGIUBAGTB4sZaA6TGfQSTg2vRO81NUG4Qd7vdaze/rtPV9s98VioNvHHz7KYfFtrszendRwIAqm5fOnJ884RRb7tczh9//uC/25fOnbUWAFCvrlizdrZEHDBs0EscNvfAb9/BqA0AwBVwKi81Q2rcRzA5uGaDiyeE8g+8eOXX8ooLb7++QyEPBgCkJg+x2S3HTm71BBcAMD3nY7ksCACQ8eTEnb98brboJWLF7n1fsFjsObO+k0qUAAAWm719Zx6M8rgCjtXsgtGy72BycJuNToEEypFZSelxl9v5/ooxLa+43S6R8K9zFwL+n+MTZUA4AMBgUPO4gtKyUz17jPOkFgDAYcPa+SwWS6bim/ROqYKxv1/G/sMAAGwOy2GD0vEYTY1ymerF6V/+z+ZaCyKXw/PE2mDUuFzOQCVFc7ia9Q4en7HXtxkeXImc64QTXLFIbjLrlAHhPJ63l5E9Ha3JpINRz13cLrfbDQQiDgXbQoXJp8MkCq692Qmj5XaJPdxu14ni/JZXbPY2DoaEQokqKPr3y4ecTugLijlsLpGUyalleI8rDeAKRGy3y83mkPz32b3r00Vnduza94WuqTYyPKmm7vrFK7+9OXcrny+8z6cG95vx/Y/vfPH1jCdSh7PY7KMnt5JbVQu7xRHG9NvRmBxcAEBwlMDQYCH9ki+Xy3vh2ZV79n95/o/9J08XBAfF9HpiLIfTxs5M7Tq0udn42/HNu/Z/ERqcEBv9uFpTSW5hHmaNpcuT9/sTYgCG3wFx/bzx9CFTROcQ1IVQ6lph1ZS3o8UyJvdKTP63AQASkiXF+5vu8waCIBa9P7DVH0nFASZLK5/t3CFz0rh3yKqw2Wpa/smoVn8UG92l8tbFv78eooqbO+ueFy8sTdaIdiJmp5b5PS4AoOgXbWWZKyTxnjMbtbqaVl93Oh1cbiungfl8Ucu52Efndrub9HWt/4xgAVYrvx0Oh+e58NGqyrM1g3NUjF9ygfnBBQD8+80bj/WO4XCZfArFw9BgdltMo2dHoC4EOub/LgEAfScGN92m4gQqcmaNsX/2PTtjJvGL4HZIk6tCWY1V9xvsMsDtP+qeHKqQB/rF/GO/CC4AoM/YYC6wayoZe09L9WV1xzRxfGdfvNcDBr8Y47bY+W2d3cUPilGgLoRkNZcbUnpLO6XLUBdCHf8KLgDgt21qdQMRFKsk/XIaElaTveZyQ6/hgR3S/Ci1/hhcAEDJacOvW9WqOEVIImlntajntLkayhqdVvvwF8ICQxm+ZMTf+WNwPU7s1t68aGFxubJgsSxYTJdFbpw2l0FtMWnMLpvjyWGBHZ+Qo64IDf8NruemtLLzptKzJk21jc1lc/kcLp/DE/FcThJWJCcRl8e1mW1Ou5PFAjaTI6aDNKm7JP5xCeq6UPLr4LYgCEJbZ7cYXWaD02EjXE7f2id8AZsnYInlXImcExBM7zV9yYKDi9ESE46sMT+Eg4vREg4uRks4uBgt4eBitISDi9HS/wO9G1W+OHiJ2QAAAABJRU5ErkJggg==) - - -```python exec="on" source="above" session="1" result="ansi" -graph.invoke({"aggregate": [], "which": "bc"}) -``` - - -```python exec="on" source="above" session="1" result="ansi" -graph.invoke({"aggregate": [], "which": "cd"}) -``` - -## Next steps - -- Continue with the [Graph API Basics](../how-tos/index.md#graph-api-basics) guides. -- Learn how to create [map-reduce](../how-tos/map-reduce.ipynb) branches in which different states can be distributed to multiple instances of a node. diff --git a/docs/docs/how-tos/create-react-agent.ipynb b/docs/docs/how-tos/create-react-agent.ipynb new file mode 100644 index 000000000..90d2875cb --- /dev/null +++ b/docs/docs/how-tos/create-react-agent.ipynb @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", + "metadata": {}, + "source": [ + "# How to use the pre-built ReAct agent" + ] + }, + { + "cell_type": "markdown", + "id": "e0fcced0-9767-412f-90f9-7f3cd618ff90", + "metadata": {}, + "source": [ + "
\n", + "

Prerequisites

\n", + "

\n", + " This guide assumes familiarity with the following:\n", + "

\n", + "

\n", + "
\n", + "\n", + "In this how-to we'll create a simple [ReAct](https://arxiv.org/abs/2210.03629) agent app that can check the weather. The app consists of an agent (LLM) and tools. As we interact with the app, we will first call the agent (LLM) to decide if we should use tools. Then we will run a loop: \n", + "\n", + "1. If the agent said to take an action (i.e. call tool), we'll run the tools and pass the results back to the agent\n", + "2. If the agent did not ask to run tools, we will finish (respond to the user)\n", + "\n", + "
\n", + "

Prebuilt Agent

\n", + "

\n", + "Please note that here will we use a prebuilt agent. One of the big benefits of LangGraph is that you can easily create your own agent architectures. So while it's fine to start here to build an agent quickly, we would strongly recommend learning how to build your own agent so that you can take full advantage of LangGraph.\n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First let's install the required packages and set our API keys" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a213e11a-5c62-4ddb-a707-490d91add383", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "23a1885c-04ab-4750-aefa-105891fddf3e", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "id": "035b920d", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith 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. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7a154152-973e-4b5d-aa13-48c617744a4c", + "metadata": {}, + "outputs": [], + "source": [ + "# First we initialize the model we want to use.\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", + "\n", + "\n", + "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", + "\n", + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", + "\n", + "@tool\n", + "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", + " \"\"\"Use this to get weather information.\"\"\"\n", + " if city == \"nyc\":\n", + " return \"It might be cloudy in nyc\"\n", + " elif city == \"sf\":\n", + " return \"It's always sunny in sf\"\n", + " else:\n", + " raise AssertionError(\"Unknown city\")\n", + "\n", + "\n", + "tools = [get_weather]\n", + "\n", + "\n", + "# Define the graph\n", + "\n", + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "graph = create_react_agent(model, tools=tools)" + ] + }, + { + "cell_type": "markdown", + "id": "00407425-506d-4ffd-9c86-987921d8c844", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "First, let's visualize the graph we just created" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fa16de4c-aac0-4ff4-ab69-60d399f75423", + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", + "metadata": {}, + "outputs": [], + "source": [ + "def print_stream(stream):\n", + " for s in stream:\n", + " message = s[\"messages\"][-1]\n", + " if isinstance(message, tuple):\n", + " print(message)\n", + " else:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "id": "9d187d6b-0fb6-4860-8771-160c3cf403c6", + "metadata": {}, + "source": [ + "Let's run the app with an input that needs a tool call" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what is the weather in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_zVvnU9DKr6jsNnluFIl59mHb)\n", + " Call ID: call_zVvnU9DKr6jsNnluFIl59mHb\n", + " Args:\n", + " city: sf\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in San Francisco is currently sunny.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"what is the weather in sf\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "838a043f-90ad-4e69-9d1d-6e22db2c346c", + "metadata": {}, + "source": [ + "Now let's try a question that doesn't need tools" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "187479f9-32fa-4611-9487-cf816ba2e147", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "who built you?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"who built you?\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + } + ], + "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.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/how-tos/create-react-agent.md b/docs/docs/how-tos/create-react-agent.md deleted file mode 100644 index a2fc4006c..000000000 --- a/docs/docs/how-tos/create-react-agent.md +++ /dev/null @@ -1,146 +0,0 @@ -# How to use the pre-built ReAct agent - -
-

Prerequisites

-

- This guide assumes familiarity with the following: -

-

-
- -In this how-to we'll create a simple [ReAct](https://arxiv.org/abs/2210.03629) agent app that can check the weather. The app consists of an agent (LLM) and tools. As we interact with the app, we will first call the agent (LLM) to decide if we should use tools. Then we will run a loop: - -1. If the agent said to take an action (i.e. call tool), we'll run the tools and pass the results back to the agent -2. If the agent did not ask to run tools, we will finish (respond to the user) - -
-

Prebuilt Agent

-

-Please note that here will we use a prebuilt agent. One of the big benefits of LangGraph is that you can easily create your own agent architectures. So while it's fine to start here to build an agent quickly, we would strongly recommend learning how to build your own agent so that you can take full advantage of LangGraph. -

-
- -## Setup - -First let's install the required packages and set our API keys - - -```python -%%capture --no-stderr -%pip install -U langgraph langchain-openai -``` - - -```python -import getpass -import os - - -def _set_env(var: str): - if not os.environ.get(var): - os.environ[var] = getpass.getpass(f"{var}: ") - - -_set_env("OPENAI_API_KEY") -``` - -
-

Set up LangSmith for LangGraph development

-

- 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. -

-
- -## Code - - -```python exec="on" source="above" session="1" -# First we initialize the model we want to use. -from langchain_openai import ChatOpenAI - -model = ChatOpenAI(model="gpt-4o", temperature=0) - - -# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF) - -from typing import Literal - -from langchain_core.tools import tool - - -@tool -def get_weather(city: Literal["nyc", "sf"]): - """Use this to get weather information.""" - if city == "nyc": - return "It might be cloudy in nyc" - elif city == "sf": - return "It's always sunny in sf" - else: - raise AssertionError("Unknown city") - - -tools = [get_weather] - - -# Define the graph - -from langgraph.prebuilt import create_react_agent - -graph = create_react_agent(model, tools=tools) -``` - -## Usage - -First, let's visualize the graph we just created - - -```python exec="on" source="above" session="1" -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/jpg;base64,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) - - -```python exec="on" source="above" session="1" -def print_stream(stream): - for s in stream: - message = s["messages"][-1] - if isinstance(message, tuple): - print(message) - else: - message.pretty_print() -``` - -Let's run the app with an input that needs a tool call - - -```python exec="on" source="above" session="1" result="ansi" -inputs = {"messages": [("user", "what is the weather in sf")]} -print_stream(graph.stream(inputs, stream_mode="values")) -``` - -Now let's try a question that doesn't need tools - - -```python exec="on" source="above" session="1" result="ansi" -inputs = {"messages": [("user", "who built you?")]} -print_stream(graph.stream(inputs, stream_mode="values")) -``` diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index 6e007b5b2..0887af667 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -11,9 +11,9 @@ Here you’ll find answers to “How do I...?” types of questions. These guide ### Graph API Basics -- [How to update graph state from nodes](state-reducers.md) -- [How to create a sequence of steps](sequence.md) -- [How to create branches for parallel execution](branching.md) +- [How to update graph state from nodes](state-reducers.ipynb) +- [How to create a sequence of steps](sequence.ipynb) +- [How to create branches for parallel execution](branching.ipynb) - [How to create and control loops with recursion limits](recursion-limit.ipynb) - [How to visualize your graph](visualization.ipynb) @@ -159,7 +159,7 @@ One of the big benefits of LangGraph is that you can easily create your own agen These guides show how to use the prebuilt ReAct agent: -- [How to use the pre-built ReAct agent](create-react-agent.md) +- [How to use the pre-built ReAct agent](create-react-agent.ipynb) - [How to add thread-level memory to a ReAct Agent](create-react-agent-memory.ipynb) - [How to add a custom system prompt to a ReAct agent](create-react-agent-system-prompt.ipynb) - [How to add human-in-the-loop processes to a ReAct agent](create-react-agent-hitl.ipynb) diff --git a/docs/docs/how-tos/sequence.ipynb b/docs/docs/how-tos/sequence.ipynb new file mode 100644 index 000000000..1b805a521 --- /dev/null +++ b/docs/docs/how-tos/sequence.ipynb @@ -0,0 +1,355 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to create a sequence of steps\n", + "\n", + "!!! info \"Prerequisites\"\n", + " This guide assumes familiarity with the following:\n", + "\n", + " - [How to define and update graph state](../../how-tos/state-reducers)\n", + "\n", + "This guide demonstrates how to construct a simple sequence of steps. We will demonstrate:\n", + "\n", + "1. How to build a sequential graph\n", + "2. Built-in short-hand for constructing similar graphs.\n", + "\n", + "\n", + "# Summary\n", + "\n", + "To add a sequence of nodes, we use the `.add_node` and `.add_edge` methods of our [graph](../../concepts/low_level/#stategraph):\n", + "```python\n", + "from langgraph.graph import START, StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "graph_builder.add_node(step_1)\n", + "graph_builder.add_node(step_2)\n", + "graph_builder.add_node(step_3)\n", + "\n", + "# Add edges\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "graph_builder.add_edge(\"step_1\", \"step_2\")\n", + "graph_builder.add_edge(\"step_2\", \"step_3\")\n", + "```\n", + "\n", + "We can also use the built-in shorthand `.add_sequence`:\n", + "```python\n", + "graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3])\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "```\n", + "\n", + "\n", + "
\n", + "Why split application steps into a sequence with LangGraph?\n", + "\n", + "LangGraph makes it easy to add an underlying persistence layer to your application.\n", + "This allows state to be checkpointed in between the execution of nodes, so your LangGraph nodes govern:\n", + "\n", + "
    \n", + "
  • How state updates are [checkpointed](../../concepts/persistence/)
  • \n", + "
  • How interruptions are resumed in [human-in-the-loop](../../concepts/human_in_the_loop/) workflows
  • \n", + "
  • How we can \"rewind\" and branch-off executions using LangGraph's [time travel](../../concepts/time-travel/) features
  • \n", + "
\n", + "\n", + "They also determine how execution steps are [streamed](../../concepts/streaming/), and how your application is visualized\n", + "and debugged using [LangGraph Studio](../../concepts/langgraph_studio/).\n", + "\n", + "
\n", + "\n", + "## Setup\n", + "\n", + "First, let's install langgraph:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith for better debugging

\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 aps built with LangGraph — read more about how to get started in the docs. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Build the graph\n", + "\n", + "Let's demonstrate a simple usage example. We will create a sequence of three steps:\n", + "\n", + "1. Populate a value in a key of the state\n", + "2. Update the same value\n", + "3. Populate a different value\n", + "\n", + "### Define state\n", + "\n", + "Let's first define our [state](../../concepts/low_level/#state). This governs the [schema of the graph](../../concepts/low_level/#schema), and can also specify how to apply updates. See [this guide](../../how-tos/state-reducers) for more detail.\n", + "\n", + "In our case, we will just keep track of two values:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import TypedDict\n", + "\n", + "\n", + "class State(TypedDict):\n", + " value_1: str\n", + " value_2: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define nodes\n", + "\n", + "Our [nodes](../../concepts/low_level/#nodes) are just Python functions that read our graph's state and make updates to it. The first argument to this function will always be the state:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def step_1(state: State):\n", + " return {\"value_1\": \"a\"}\n", + "\n", + "\n", + "def step_2(state: State):\n", + " current_value_1 = state[\"value_1\"]\n", + " return {\"value_1\": f\"{current_value_1} b\"}\n", + "\n", + "\n", + "def step_3(state: State):\n", + " return {\"value_2\": 10}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + " Note that when issuing updates to the state, each node can just specify the value of the key it wishes to update.\n", + "\n", + "By default, this will **overwrite** the value of the corresponding key. You can also use [reducers](../../concepts/low_level/#reducers) to control how updates are processed— for example, you can append successive updates to a key instead. See [this guide](../../how-tos/state-reducers) for more detail.\n", + "\n", + "### Define graph\n", + "\n", + "We use [StateGraph](../../concepts/low_level/#stategraph) to define a graph that operates on this state.\n", + "\n", + "We will then use [add_node](../../concepts/low_level/#messagesstate) and [add_edge](../../concepts/low_level/#edges) to populate our graph and define its control flow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import START, StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "graph_builder.add_node(step_1)\n", + "graph_builder.add_node(step_2)\n", + "graph_builder.add_node(step_3)\n", + "\n", + "# Add edges\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "graph_builder.add_edge(\"step_1\", \"step_2\")\n", + "graph_builder.add_edge(\"step_2\", \"step_3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "!!! tip \"Specifying custom names\"\n", + "\n", + " You can specify custom names for nodes using `.add_node`:\n", + "\n", + " ```python\n", + " graph_builder.add_node(\"my_node\", step_1)\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- `.add_edge` takes the names of nodes, which for functions defaults to `node.__name__`.\n", + "- We must specify the entry point of the graph. For this we add an edge with the [START node](../../concepts/low_level/#start-node).\n", + "- The graph halts when there are no more nodes to execute.\n", + "\n", + "We next [compile](../../concepts/low_level/#compiling-your-graph) our graph. This provides a few basic checks on the structure of the graph (e.g., identifying orphaned nodes). If we were adding persistence to our application via a [checkpointer](../../concepts/persistence/), it would also be passed in here." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "graph = graph_builder.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LangGraph provides built-in utilities for visualizing your graph. Let's inspect our sequence. See [this guide](../../how-tos/visualization) for detail on visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGsAAAFNCAIAAACIXwbEAAAAAXNSR0IArs4c6QAAG5NJREFUeJztnXlcE2fewJ/cdwIkEO5L5VZUPKiiUsWqVFG88MCq27p16/bSd3uyvde61lbbravdqtuttR61WhfXqrUeFdFWqlZAKXJ4QMIRQsh9zeT9I36oH801MxnykM73L53M8cuXJzPPPNeP5nA4AAUB6IEOoN9DGSQKZZAolEGiUAaJQhkkCpPg8Tq1rafLZtQhRi1itzlQtB/UjRhMwGTS+WIGX8QMjWTxhYQk0PDVB7uUlsarhuZqA5tPAw4aX8Tgixk8ARNF+oFBJoum19qNWsSos1tMKItNTx4sGJgtFEtZOM6G2aBeY68sVzkACJGxkgYLImK5OK4KFcpmU1O1obvdKgxljpkuY3Ox3dmwGbx4XF1T2TNmhiw1R4Q9VNipruipPKzKfVSaPS7E96MwGDy0pXXgMGFmrgRvhP2Dn0+ou9qsj5RG+ri/ryV2+1+bh00MDXp9AICcgrCENMGhLa2+HuDwgW1lTSqF2Zc9g4YbV3R7Ntz2ZU/vv+JDW1qHTQyNT+X74e/br7j+o7a1yVSwUO55Ny8Gq75T84SMzIeC/8frkqoTap7Ay9f3dB/Ua+zV53p+t/oAACMKwk7t6/S8jyeDleWqMTNk/o6qn/HQdGllucrDDm4NdiktDgCCst6HiZxJoSqFxWywu9vBrcHGq4YQGZ63HHzU1NRYLJZAHe4ZgZjZVGN096lbg83VhqTBApJiuo/y8vJly5aZTKaAHO6V5MHCpmq9u09dG9SqbRw+vc/eeXEXH2dFgrzS5yQpS6DvtrtrdnJjsMtGUhferVu3Vq5cmZeXV1hYuHbtWhRFy8vL161bBwAoKCgYMWJEeXk5AKC9vf31118vKCjIzc0tKSk5evSo83CNRjNixIidO3eWlZXl5eWtWLHC5eF+x25z9KhsLj9y3TRm1CF8EYOMUN5+++2bN2+uWbPGYDBUVVXR6fSxY8eWlpZ+8cUXmzZtEgqF8fHxAAC73V5bWzt37tyQkJCTJ0+WlZXFxcVlZmY6T7J9+/Z58+Zt3bqVwWDI5fIHD/c7fDHDqEVCI1x85MagFuGLSTGoUCjS0tKKi4sBAKWlpQCAsLCw2NhYAEBWVlZIyN1GkZiYmK+++opGowEAZs6cWVBQcPr06V6DgwcPXrVqVe85Hzzc7wjETIPW9ePY7ZOExSalA6CwsPDChQvr169Xq9We96yvr1+9evXUqVOLi4sRBOnq6ur9aNSoUWTE5gE2l+7u5c21Jq6Arut2WwMiwqpVq1avXn38+PGioqJ9+/a52+3ixYtLly61Wq2vv/76+vXrJRIJiqK9n/J4PDJi80CPysYXuf69ut7KFzGNOlIM0mi0RYsWzZw5c+3atevXr09JSRk6dKjzo3v/yNu2bYuNjd20aROTyfRRGanDVzw8GFyXQWEog8Mj5VfsrHkIBIKVK1cCAOrq6noFdXb+9gaq0WhSUlKc+qxWq9FovLcM3seDh/sdgYQhCnX9fuG6DIbJOZ0tVk2nNSSc7d9QXnzxRaFQmJubW1FRAQBIT08HAGRnZzMYjA0bNhQVFVksljlz5jjrJYcOHZJIJLt27dJqtY2Nje5K2YOH+zfm1gYTagfu+k8Yb7zxhssPdN12Q489KsnPd5yWlpaKioqjR4+aTKann346Pz8fACAWi+Vy+XfffXf27FmtVjt9+vTs7OympqY9e/ZUVVVNnjy5pKTk2LFjaWlpUqn0888/z8vLy8jI6D3ng4f7N+ZfzmjkidzIRNfvF27bBxVNpus/aid5a1/8PfC/7cq8mTKJm1YCt53N0cm8n46q79Qb41Jct05rtdqioiKXH8XGxra0tDy4fcKECW+++abPkePkiSeeaGhoeHB7enr69evXH9yelZX18ccfuzvb9Z+0HB7dnT4vbdQdd8yn9nWWrIlz+SmKom1tba5PSnN9Wh6PFxoa6u5y/qKzs9Nmc/EG5i4qNpstk7ltBt3+1+aFL8S5q8p4b+X/4WBnfAo/MbOPGmlgo/ZCj1GLjHwkzMM+Xqos44vDzxzo1Ha5fqkObhSNprqLOs/6gC+9nRYzsvWFBn/0IPYnTAbbJy81+rKnT/3FVgvyycsN+h4b4cD6Bx0t5u2vNdntqC87+zrqw6RHdq+/PeUxeczAIO84bvhFV3W8e8FffG0lwzby6NTeDm23bewMmSyGgzdCeGltNJ0v75IncMYVh/t+FObRb7frjOfKVfFpfHkcNylLwGDSsIcKF1Yz2lSjb7tpViutD82QRiView3DOQKz8aq+/pKuucaQmiNicegCMVMgYXD5jP4whBUw6DSjzm7Q2g1aRN9ja6k3JWcJU0YIE9LwVNpwGuzldp2xu8Nq0NoNPQiKOuxWfypEEKS6urq3+ctfcPh0Z7OzQMyQRrEJ3tmJGiQVvV4/ffr006dPBzoQT1Bj+YlCGSQK7AadTbAwA7tBl+1RUAG7QfK6gP0F7AY1Gk2gQ/AC7Aajo6MDHYIXYDeoUCgCHYIXYDc4ePDgQIfgBdgNVldXBzoEL8BuEH5gN+ihFw0SYDeoUnmaiQADsBsMD8fQXBwQYDdI6ogsvwC7QfiB3eDAgQMDHYIXYDfocgwRVMBuEH5gN3jvSEs4gd3gtWvXAh2CF2A3CD+wG6TaZohCtc0EP7AbpHo7iUL1dgY/sBuk+ouJQvUXE2XQoEGBDsELsBu8ceNGoEPwAuwG4Qd2g5GRvq5FGShgN+hu8iM8wG4wKysr0CF4AXaDNTU1gQ7BC7AbpMogUagySJS4ONcz7OEBxhk5K1asUCgUTCYTRVGVSiWTyeh0us1mO3LkSKBDcwGMZXDx4sVarba1tVWpVNpsNqVS2draymCQspIacWA0mJ+ff9/rsMPhgLbDBEaDAIAlS5bw+b9NGIyKilqwYEFAI3ILpAYffvjhpKSk3nt0dnb2kCFDAh2UayA1CABYvny5s3lVJpNBWwChNpifn5+cnOzsMob2Jog/T5PFhKhaLRYzuTWhWY88aeneW5i/vKnGQOqFeAK6NJrN5uB53OOpDx79XHn7uil6AL9fZGXyBcSOtt82DxommrTA1WK1HsFm0GZB93/Ukp0fFpcixHol+Km/1HOnTj9zZbRzBV0fwWZwz4Y7uY+GS6P7fXYrdzTX6m5f009/Isr3QzA8SeovaSMTeUGsDwCQlClismh36t2uwv8gGAx23LFyBJC+WvkRFpfRpbD6vj8GgxYTIpb6eVlWCAmVc4xuFu92CQaDVrMjaB6+HkBsDpsNw9eEt0bdX6AMEoUySBTKIFEog0ShDBKFMkgUyiBRKINEoQwShTJIlAAYbGtTKttIXwvKbre/+tfVdb+SPjW0rw22KloWlRb9SvIX0+l1r5Y9X1n5A6lXcYKzpwk3iN1O9kidS5cvvvfeW52qDlKv0guJBs1m86aP1jkLwpAhw/781P85gGPp8rkAgDffeulNAKZMmf7SC28499y2ffP3J49arZa42IT585dMfPgRAMD+r7/c/M8PZs9ecObMCb1el5E++Mknn01N8TLT7uDBvaNHj01KGrjpw3XkfbteSDT45e5/Hzt2ePmylVKp7Njxwzwej8fjv/rKO39bW7Z82cphQ0eEhoY5M8W8WvZ8W5ti8aLlISFhV65Uvf3OK2azqXDaTOd5bFbr229u6FR1fPafT1aveXLbp3uiIj0tSvjcsy9JpbLvvuujgV4kGlS2KXg83qKFy5hM5qOFs5wbUwalAQDi4xMHD767TvcPZ09erb68e1e5TBYOACiYNNVkMn59YHevwZVPPsfn89MBSE3JKH1s1sGDe5/60/MeriuV9ulSXSQaLJg07fvvj7740tOrnlqTnOx22ZgLFyrsdvui0t9SPiEIIhC46E2VyyPj4xOv18E1qpVEg6NHjXl37YdbP9n0+IoFjxbOeu7Zl5wZ/O6ju7tLKpV9sGHrvRsZrvYEAIhEYp1OS1rIeCD3WTx61JiRI3K/PrD7n1s2yuVRS0off3AfkUis0XTL5VEcjveMHarOjrj4RHKCxQmJ9UGr1QoAoNPp8+YulsnCb9yoAwBwOFwAQJfqt/XIhg8fhSDIf8v3925xl0/8ypWfWxUtmRlwDYMjsQweOLjnXOWZyQWFXV2dKlVnamoGACAiQh4dFbNv/xdcHk+r7ZldvGByQWH54QNbP/lQ2aZIGZTW0FBfce7UZzv2c7l3u/Y3blqbkzNaoWj5+sDusDBp8awS8mLGAYkGo6NjbVbrlq0bBQLh7NkLSuYvcSaNKytbu/69Nz/evCEiIvLh/EciI6Pe+/vmT7f94+TJY4cPH4iNjS+aMffeO6bdbt/6yYdWqyU7O+dPTz4nEMCV+g3DuJlvP2uLTRUmZvTdmCNnjfp/5T/cOyKYbOp+6jFqrRPm+LpyZF+/1fmFZ557ornZxZpwY8ZMePlF0rNa3ke/NPha2bs2u4skhDxuX+fWht3g3DmL5s5Z9OB259sLJFAtrEShDBKFMkgUyiBRKINEoQwShTJIFMogUSiDRKEMEgWDQWEIk07v99mKvUJn0PhCDNNmMBgUiBkdt123HgcT7TeNYhnL9/0xGIxL5em7XbSIBBlGnT0uBUMbDwaD4THcmEHcioPtuALrH3z/pWLIOAlfhKHJCvP84ppzPTeuGBIyhbJoLpsbJA8isxHpUphrz2vGzZIlZWLrRcAzQ1vRZLp2QavvQTQdGGbw4cHhsFitvvSCEkQUygqLZA3NDwmNwDxxEMY1j3qhspD/LqAMEgV2gzCvk+IEdoNUdg2iUNnWiEJlWyMKlZ+EKFR+EqJQ90GiUPfB4Ad2g6mpqYEOwQuwG/z1118DHYIXYDcIP7Ab7B2PDi2wGzSbzYEOwQuwG5RIJIEOwQuwG+zp6Ql0CF6A3SD8wG4wNjY20CF4AXaDLS0tgQ7BC7AbhB/YDVJZJ4lCZZ0MfmA3SPV2EoXq7Qx+YDdI9ZMQheonIUpoaGigQ/AC7Aa7u7sDHYIXYDcIP7AbpEZ9EIUa9UGUjIyMQIfgBdgNXrtG+lK0BIHdIFUGiUKVQaJkZmYGOgQvwDgjZ9WqVWq1msViIQjS2NiYnJzMZDIRBNm1a1egQ3MBjKtGTZgw4f3330cQxPnf+vp6ZxrtQMflGhh/xfPnz4+Li7tv46hRowIUjhdgNAgAKC0tvXdColgsXrhwYUAjcgukBmfNmhUTE9P730GDBo0fPz6gEbkFUoMAgIULFzqLoUQiKS0tDXQ4boHXYHFxsbMYDhgwYNy4cYEOxy34n8VmA2qzon4N5n5K5izbvn17yZxlum4MqUhxwOHT2RychQlPffDid+raSi2Hz7AYEXxXhQ2HAzBZIHtCyJC8EKzHYjZ45N/KkAhOUpZIGIJhSRH40alttZXdPCE9bya2xAjYDB7ZoZTF8dJHYf5D9RcunVABmmPCbAzrvGL48TfX6nlCZhDrAwAML5CZ9Gj7LQyDtzEYbL9lYXGDPws5g0HrbLH4vj+WHNomNCyK9IVLAk54HNdAUhZygw5B7JC+3vsRm8VhNmKopcFbo+4vUAaJQhkkCmWQKJRBolAGiUIZJAplkCiUQaJQBolCGSRKAHrc29qUDuDwnEEXNwaDYcvWjRXnTtts1vj4pCWLHx8zhtxOvmDLQv6vTz868f23Ux6Z/odlfwIOR9lra6qrr5B0LSfBloX8sSUrCiZNcyZHHj9+0vwFhRXnTvfmSiaDYMtCLpXKejNA83h8AIDNRu6Sz0GYhbyXny5WAgByho8m7zsGZxZyJyiK7t79WVxcQm5uHnnfMZizkH9z6KuGxvq172xkMMjt2wnOLOQdHe3bd2weO3bCQw+RPlwkOLOQf/TxehRFn171F7yBYyAIs5CfPnPi3Lkzjy1ZIZdH+u/buCXYspDr9fqPN29gMplms+nznducGwsKpkVHxXg4igjBloX8s/980tWlAgD06gMApKdnkWeQykJ+P1QWcioLuQ9QWch9hcpC/ruAMkgUyiBRKINEoQwShTJIFMogUSiDRKEMEoUySBRsObQZzOA3zmLRuXwMXxPDrlwBQ9X6O8hCfsckDMXQXIDBYGQCx2YOksmcHkARVB6PISkKBoPxaQIH6rhyWo0rsP7BhcMdoREsWTSGqVuYZ8ee3t+BOkDyYLE0CvbsNb6Doo4upeXa+e6oJG7ORGyrbuKZoV1zrqfmvNZqRs0Gcn/UDgBQFGHQSZ8OyWDSJDJW9njJoGEirMfiX/PI4QBWM7mrBBgMhpKSksOHD5N6FQAAh0sHNJzH4m+jptEAh0du5caG0GyIkeyrEATq4PoFsBukVvQmCrWiN1Go3BBEoXJDECUrKyvQIXgBdoM1NT6NWA0gsBuksk4Shco6GfzAbpCqzRCFqs0EP7AbTEz0PvchsMBu8ObNm4EOwQuwG4Qf2A2GhMC+YCTsBjUaTaBD8ALsBul06CMMdABeQFFyO7OIA7tB+IHdIJV1kihU1sngB3aDVG8nUajezuAHdoNUCytRqBbW4Ad2gyIR5iGRfQzsBnU6XaBD8ALsBqknCVGoJwlRYmNjAx2CF2A32NLSEugQvAC7wXuzd8IJ7AZbW1sDHYIXYDdIjcAkCvwjMGHM475jx46tW7eiKIqiKJ1OdzgcNBoNRdFLly4FOjQXwFgG58+fHx8f39vVSaPRHA4HtE2tMBoUCoWFhYX3ruHL5XKhTQINo0EAwNy5cxMSEnr/GxsbW1RU5PGIgAGpQbFYPHXqVOevWCAQLF68ONARuQVSgwCAefPmOQcPwlwAoTYoEommTZvG4/EWLFgQ6Fg8QWJtRnnT1FRt7LhjMekQkwFhsmgmrHPiHcButzFZmJN1C0OYVhPKEzJ4QmZkImfAEEF4DFlZb/1v0GpGfzzaff3HHhafJQoXsHlMJpvB5DCZbHrfVT0dALEhditisyAWg1XfaUQRJCNXMubRML9fys8GzxzounZBE5kqE8l4TDZE6aJtZru206i83jVyinT0VH+OxfGbwbZbthO72zkiXngy1KNO2+rVqNU6449RIol/ngH+Mdh4VX9ynyp5dDSDCVG5c4fVaGs43zr3uZiIWD8s+OIHg8qb5uO7VAnDo4hH05fcuqQoekIeKmcTPA/RkqxoNh3b2dnv9AEAEoZH7/+oVa/BkLLdJYQM2qzoN5sViSNIyTzXBySPjtm17jbBkxD6FR/4p4IbKuGH9OPlo3raDVymaUqpHPcZ8JfB5lq9Qevo1/oAABK5QNFoUbVacJ8Bv8GzB7vCB/i/gtr3yJJDT3+twn04zlWjbtUZmDwWV4jnQabuVgLgCAsl5e7pcDhOVez8seqQVqeShydOHL9sSObDng8RyfjddzRdSos0Cs+bH84y2PiLgSPEk8pCpW55d2Pxndbr+K7rCw1NVVnpE6YVrKTTGZ/veamu/rzXQ9hCbnOtAd/lcBpsrjWKwvHkrUERcvN20mi0FY99OGPqM+PHLHx8yUYAwKWrR70eJQ7nN1zBaRDPr7i7wyoIYbF5Xo61Ws0HDq+/VncWAJCUMHRm4WoAHOs/KgEA7Nz7ys69YMSwRxfMfs2557cntly+esxms4TLEvLzFg8dPBkA8EPl7v9+uykvt+Rq7fcmsy4hNuvRKU/HxXjK2+mU6PwHjytiMtkMuvfvyA/hau7QrFaUzcZcpPAYNOkQq9l7OTr5w3+qLv9vyqQ/ioWyqitH2Gweh8NfNO+tL796bcrEPw5MzhEK7ubt3LFrTXe3cuL4pUJhWGPTz1/sK7NYTaNz7raq2u3WpQv/3qPtPH7y0607nlrz512+3EM1Pe2VP32NokjuyGJfvpRRZzfrELa0TwwatHZf2l3UGgWbzZs4bimDwRw94m4SztioVABARHhiUsLdvJ3V104137zyyppvJOJwAMDwIVMsVmPF+b29BmdMfYbD4QMA4mLS122aU3Hhq6Jpz3q9+rsb5yCIrXj6XxLifOqzZ3MZBq1dLMXcFonHoNWEsgXen8LDh0y9fPXYp58/O7Pw+Si5277K67+eQ1D72g9+KykoivC4LjKShYZERsgSb7fU+hLk0gXrfv7lyKEjH0jEEVnp3tNo8yQcox7Poqh4DDJYNKvRRaKp+0hLeejx0o3lxz56/+PFo3Nmzp7xAoPh4nI6fZdYJFu5fPO9G+lubl48nthk8p63EwCQkZaXnjr2H/964uDh93wxaNJaOa7+bF7BY5AvZiJWn17I01IeShk4+uz5PeVHPwwNiSzI/4OLs/HEekN3aEgUi+W9Otaj7YgIT/C6mxMajRYfm1FxocZk0vF4XsZj2y0IX4zHBp7ajEDMsNu8T/u12e/m7ZwwdpFYFN6i/BUAwGJxAQBa3W95OwcOGImiSOVPX/dusVhdr7ze0Pxzl7olIc7LuGCTWd/775bWOiaTzWZ7r7pazXaBGE/jJh7r0iiOqceKog463dMCuhXn99bWnc3JntqjU2l1nXHR6QCAEIlcGhpz5tyXbBbPYOoZl1uSkz3tx6pvDh/7R7dGGROVqmi7UX3t9AvP7GWz775x7//vupQBo7rUrWfP7xEJpXmj53u4aJe6ddOWpcOzp4aGRDXe/PnmnatjR89zefe4F4vRxuExODw8BhlvvPEGjsPa71jMJprntzqdXt3YfOny1aPtnc0jh8+YMnEFnU6n0WgJcVl1Ny5crj7erVFmpU8QCCRDsiaZTLpfak5cvXbKbDaMypmRlDCUTqffulPza8MFeXjihapvmm9fGZA0fNG8t0IkER4uSqcxjCZt9bWTdTcqAaBNGr90cv7jXpca0Cj0MYnMxAxPGUHdgbN1q65Ke+mMITrD05chjrNG/beyU87aDHncvqycOC8sdhCeq+BsWUgdLqo8rHaOS8N3BiJs3vakst3FnM/MtPEL57yO9WxWs53JdODTh98gjU4bkidpuq6OGCjFdwYilM5/B0FcVKd8eWI8SGejemQB/nUiCbVR/+vlpuTcWKj6hbFi0lq6mlSlL8fjPgOhfpLJSyJUTfjbJmGg66Z66lL8TfxEDSZlCJMzuZ2N/TVjifJ6x4iJYkzZSB6EaG/nqEfCYhIZbTe6CJ6n71Fe70wfwU8bKSZ4Hj+MfBgzPSw8ArTf6E8/Z0Vt+6AhnKHj/bDQsN/GzVw+rWmoNovkEq6I6CgAUjF0mzWt3SMnSVKG+2fmsj/HbimaTN/v6WSwWeEDwlhc6LJzm/XWzkY1i+WYvDhcGum34YT+Hz9Y/7Pu6jmdXmMXSAXiCD5bwApIrduJA3WYdBZdh9GgNkqkrJxJEnyvbh4gawxr+y3zjV8MikZzx20Ti0tnc5kcARPxoUXHL7D4TJPGYjUhdhsqjeYmZfIHDBEQfOa6oy/mNBl1dqMWsZgQgDsZEkacGZD4EiZPQHptH8ZZYf0LeMfy9xcog0ShDBKFMkgUyiBRKINE+X82OL629pwN6gAAAABJRU5ErkJggg==", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Usage\n", + "\n", + "Let's proceed with a simple invocation:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'value_1': 'a b', 'value_2': 10}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"value_1\": \"c\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- We kicked off invocation by providing a value for a single state key. We must always provide a value for at least one key.\n", + "- The value we passed in was overwritten by the first node.\n", + "- The second node updated the value.\n", + "- The third node populated a different value." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Built-in shorthand\n", + "\n", + "!!! info \"Prerequisites\"\n", + " `.add_sequence` requires `langgraph>=0.2.46`\n", + "\n", + "\n", + "LangGraph includes a built-in shorthand `.add_sequence` for convenience:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'value_1': 'a b', 'value_2': 10}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# highlight-next-line\n", + "graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3])\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "\n", + "graph = graph_builder.compile()\n", + "\n", + "graph.invoke({\"value_1\": \"c\"})" + ] + } + ], + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/docs/how-tos/sequence.md b/docs/docs/how-tos/sequence.md deleted file mode 100644 index eafe39086..000000000 --- a/docs/docs/how-tos/sequence.md +++ /dev/null @@ -1,223 +0,0 @@ -# How to create a sequence of steps - -!!! info "Prerequisites" - This guide assumes familiarity with the following: - - - [How to define and update graph state](../how-tos/state-reducers.md) - -This guide demonstrates how to construct a simple sequence of steps. We will demonstrate: - -1. How to build a sequential graph -2. Built-in short-hand for constructing similar graphs. - - -# Summary - -To add a sequence of nodes, we use the `.add_node` and `.add_edge` methods of our [graph](../concepts/low_level.md#stategraph): - -```python -from langgraph.graph import START, StateGraph - -graph_builder = StateGraph(State) - -# Add nodes -graph_builder.add_node(step_1) -graph_builder.add_node(step_2) -graph_builder.add_node(step_3) - -# Add edges -graph_builder.add_edge(START, "step_1") -graph_builder.add_edge("step_1", "step_2") -graph_builder.add_edge("step_2", "step_3") -``` - -We can also use the built-in shorthand `.add_sequence`: - -```python -graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3]) -graph_builder.add_edge(START, "step_1") -``` - - -
-Why split application steps into a sequence with LangGraph? - -LangGraph makes it easy to add an underlying persistence layer to your application. -This allows state to be checkpointed in between the execution of nodes, so your LangGraph nodes govern: - -
    -
  • How state updates are [checkpointed](../concepts/persistence/.md)
  • -
  • How interruptions are resumed in [human-in-the-loop](../concepts/human_in_the_loop/.md) workflows
  • -
  • How we can "rewind" and branch-off executions using LangGraph's [time travel](../concepts/time-travel/.md) features
  • -
- -They also determine how execution steps are [streamed](../concepts/streaming.md), and how your application is visualized -and debugged using [LangGraph Studio](../concepts/langgraph_studio.md). - -
- -## Setup - -First, let's install langgraph: - - -```python -%%capture --no-stderr -%pip install -U langgraph -``` - -
-

Set up LangSmith for better debugging

-

- 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 aps built with LangGraph — read more about how to get started in the docs. -

-
- -## Build the graph - -Let's demonstrate a simple usage example. We will create a sequence of three steps: - -1. Populate a value in a key of the state -2. Update the same value -3. Populate a different value - -### Define state - -Let's first define our [state](../concepts/low_level.md#state). This governs the [schema of the graph](../concepts/low_level.md#schema), and can also specify how to apply updates. See [this guide](../how-tos/state-reducers.md) for more detail. - -In our case, we will just keep track of two values: - - -```python exec="on" source="above" session="1" -from typing_extensions import TypedDict - - -class State(TypedDict): - value_1: str - value_2: int -``` - -### Define nodes - -Our [nodes](../concepts/low_level.md#nodes) are just Python functions that read our graph's state and make updates to it. The first argument to this function will always be the state: - - -```python exec="on" source="above" session="1" -def step_1(state: State): - return {"value_1": "a"} - - -def step_2(state: State): - current_value_1 = state["value_1"] - return {"value_1": f"{current_value_1} b"} - - -def step_3(state: State): - return {"value_2": 10} -``` - -!!! note - - Note that when issuing updates to the state, each node can just specify the value of the key it wishes to update. - -By default, this will **overwrite** the value of the corresponding key. You can also use [reducers](../concepts/low_level.md#reducers) to control how updates are processed— for example, you can append successive updates to a key instead. See [this guide](../how-tos/state-reducers.md) for more detail. - -### Define graph - -We use [StateGraph](../concepts/low_level.md#stategraph) to define a graph that operates on this state. - -We will then use [add_node](../concepts/low_level.md#messagesstate) and [add_edge](../concepts/low_level.md#edges) to populate our graph and define its control flow. - - -```python exec="on" source="above" session="1" -from langgraph.graph import START, StateGraph - -graph_builder = StateGraph(State) - -# Add nodes -graph_builder.add_node(step_1) -graph_builder.add_node(step_2) -graph_builder.add_node(step_3) - -# Add edges -graph_builder.add_edge(START, "step_1") -graph_builder.add_edge("step_1", "step_2") -graph_builder.add_edge("step_2", "step_3") -``` - -!!! tip "Specifying custom names" - - You can specify custom names for nodes using `.add_node`: - - ```python - graph_builder.add_node("my_node", step_1) - ``` - -Note that: - -- `.add_edge` takes the names of nodes, which for functions defaults to `node.__name__`. -- We must specify the entry point of the graph. For this we add an edge with the [START node](../concepts/low_level.md#start-node). -- The graph halts when there are no more nodes to execute. - -We next [compile](../concepts/low_level.md#compiling-your-graph) our graph. This provides a few basic checks on the structure of the graph (e.g., identifying orphaned nodes). If we were adding persistence to our application via a [checkpointer](../concepts/persistence.md), it would also be passed in here. - - -```python exec="on" source="above" session="1" -graph = graph_builder.compile() -``` - -LangGraph provides built-in utilities for visualizing your graph. Let's inspect our sequence. See [this guide](../how-tos/visualization.ipynb) for detail on visualization. - - -```python exec="on" source="above" session="1" -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/png;base64,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) - -### Usage - -Let's proceed with a simple invocation: - - -```python exec="on" source="above" session="1" result="ansi" -graph.invoke({"value_1": "c"}) -``` - - - - - - -Note that: - -- We kicked off invocation by providing a value for a single state key. We must always provide a value for at least one key. -- The value we passed in was overwritten by the first node. -- The second node updated the value. -- The third node populated a different value. - -## Built-in shorthand - -!!! info "Prerequisites" - `.add_sequence` requires `langgraph>=0.2.46` - - -LangGraph includes a built-in shorthand `.add_sequence` for convenience: - - -```python exec="on" source="above" session="1" result="ansi" -# highlight-next-line -graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3]) -graph_builder.add_edge(START, "step_1") - -graph = graph_builder.compile() - -graph.invoke({"value_1": "c"}) -``` - - - - - diff --git a/docs/docs/how-tos/state-reducers.ipynb b/docs/docs/how-tos/state-reducers.ipynb new file mode 100644 index 000000000..dd66a15c0 --- /dev/null +++ b/docs/docs/how-tos/state-reducers.ipynb @@ -0,0 +1,430 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to update graph state from nodes\n", + "\n", + "This guide demonstrates how to define and update [state](../../concepts/low_level/#state) in LangGraph. We will demonstrate:\n", + "\n", + "1. How to use state to define a graph's [schema](../../concepts/low_level/#schema)\n", + "2. How to use [reducers](../../concepts/low_level/#reducers) to control how state updates are processed.\n", + "\n", + "We will use [messages](../../concepts/low_level/#messagesstate) in our examples. This represents a versatile formulation of state for many LLM applications. See our [concepts page](../../concepts/low_level/#working-with-messages-in-graph-state) for more detail.\n", + "\n", + "## Setup\n", + "\n", + "First, let's install langgraph:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith for better debugging

\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 aps built with LangGraph — read more about how to get started in the docs. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example graph\n", + "\n", + "### Define state\n", + "[State](../../concepts/low_level/#state) in LangGraph can be a `TypedDict`, `Pydantic` model, or dataclass. Below we will use `TypedDict`. See [this guide](../../how-tos/state-model) for detail on using Pydantic.\n", + "\n", + "By default, graphs will have the same input and output schema, and the state determines that schema. See [this guide](../../how-tos/input_output_schema/) for how to define distinct input and output schemas.\n", + "\n", + "Let's consider a simple example:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import AnyMessage\n", + "from typing_extensions import TypedDict\n", + "\n", + "\n", + "class State(TypedDict):\n", + " messages: list[AnyMessage]\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This state tracks a list of [message](https://python.langchain.com/docs/concepts/messages/) objects, as well as an extra integer field.\n", + "\n", + "### Define graph structure\n", + "\n", + "Let's build an example graph with a single node. Our [node](../../concepts/low_level/#nodes) is just a Python function that reads our graph's state and makes updates to it. The first argument to this function will always be the state:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import AIMessage\n", + "\n", + "\n", + "def node(state: State):\n", + " messages = state[\"messages\"]\n", + " new_message = AIMessage(\"Hello!\")\n", + "\n", + " return {\"messages\": messages + [new_message], \"extra_field\": 10}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This node simply appends a message to our message list, and populates an extra field.\n", + "\n", + "!!! important\n", + "\n", + " Nodes should return updates to the state directly, instead of mutating the state.\n", + "\n", + "Let's next define a simple graph containing this node. We use [StateGraph](../../concepts/low_level/#stategraph) to define a graph that operates on this state. We then use [add_node](../../concepts/low_level/#messagesstate) populate our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "graph_builder.add_node(node)\n", + "graph_builder.set_entry_point(\"node\")\n", + "graph = graph_builder.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LangGraph provides built-in utilities for visualizing your graph. Let's inspect our graph. See [this guide](../../how-tos/visualization) for detail on visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, our graph just executes a single node." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use graph\n", + "\n", + "Let's proceed with a simple invocation:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='Hi', additional_kwargs={}, response_metadata={}),\n", + " AIMessage(content='Hello!', additional_kwargs={}, response_metadata={})],\n", + " 'extra_field': 10}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.messages import HumanMessage\n", + "\n", + "result = graph.invoke({\"messages\": [HumanMessage(\"Hi\")]})\n", + "result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- We kicked off invocation by updating a single key of the state.\n", + "- We receive the entire state in the invocation result.\n", + "\n", + "For convenience, we frequently inspect the content of [message objects](https://python.langchain.com/docs/concepts/messages/) via pretty-print:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Process state updates with reducers\n", + "\n", + "Each key in the state can have its own independent [reducer](../../concepts/low_level/#reducers) function, which controls how updates from nodes are applied. If no reducer function is explicitly specified then it is assumed that all updates to the key should override it.\n", + "\n", + "For `TypedDict` state schemas, we can define reducers by annotating the corresponding field of the state with a reducer function.\n", + "\n", + "In the earlier example, our node updated the `\"messages\"` key in the state by appending a message to it. Below, we add a reducer to this key, such that updates are automatically appended:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import Annotated\n", + "\n", + "\n", + "def add(left, right):\n", + " \"\"\"Can also import `add` from the `operator` built-in.\"\"\"\n", + " return left + right\n", + "\n", + "\n", + "class State(TypedDict):\n", + " # highlight-next-line\n", + " messages: Annotated[list[AnyMessage], add]\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now our node can be simplified:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def node(state: State):\n", + " new_message = AIMessage(\"Hello!\")\n", + " # highlight-next-line\n", + " return {\"messages\": [new_message], \"extra_field\": 10}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "from langgraph.graph import START\n", + "\n", + "\n", + "graph = StateGraph(State).add_node(node).add_edge(START, \"node\").compile()\n", + "\n", + "result = graph.invoke({\"messages\": [HumanMessage(\"Hi\")]})\n", + "\n", + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MessagesState\n", + "\n", + "In practice, there are additional considerations for updating lists of messages:\n", + "\n", + "- We may wish to update an existing message in the state.\n", + "- We may want to accept short-hands for [message formats](../../concepts/low_level/#using-messages-in-your-graph), such as [OpenAI format](https://python.langchain.com/docs/concepts/messages/#openai-format).\n", + "\n", + "LangGraph includes a built-in reducer `add_messages` that handles these considerations:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph.message import add_messages\n", + "\n", + "\n", + "class State(TypedDict):\n", + " # highlight-next-line\n", + " messages: Annotated[list[AnyMessage], add_messages]\n", + " extra_field: int\n", + "\n", + "\n", + "def node(state: State):\n", + " new_message = AIMessage(\"Hello!\")\n", + " return {\"messages\": [new_message], \"extra_field\": 10}\n", + "\n", + "\n", + "graph = StateGraph(State).add_node(node).set_entry_point(\"node\").compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "# highlight-next-line\n", + "input_message = {\"role\": \"user\", \"content\": \"Hi\"}\n", + "\n", + "result = graph.invoke({\"messages\": [input_message]})\n", + "\n", + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a versatile representation of state for applications involving [chat models](https://python.langchain.com/docs/concepts/chat_models/). LangGraph includes a pre-built `MessagesState` for convenience, so that we can have:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import MessagesState\n", + "\n", + "\n", + "class State(MessagesState):\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "- Continue with the [Graph API Basics](../../how-tos/#graph-api-basics) guides.\n", + "- See more detail on [state management](../../how-tos/#state-management)." + ] + } + ], + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/docs/how-tos/state-reducers.md b/docs/docs/how-tos/state-reducers.md deleted file mode 100644 index 30920eab9..000000000 --- a/docs/docs/how-tos/state-reducers.md +++ /dev/null @@ -1,219 +0,0 @@ -# How to update graph state from nodes - -This guide demonstrates how to define and update [state](../concepts/low_level.md/#state) in LangGraph. We will demonstrate: - -1. How to use state to define a graph's [schema](../concepts/low_level.md/#schema) -2. How to use [reducers](../concepts/low_level.md/#reducers) to control how state updates are processed. - -We will use [messages](../concepts/low_level.md/#messagesstate) in our examples. This represents a versatile formulation of state for many LLM applications. See our [concepts page](../concepts/low_level.md/#working-with-messages-in-graph-state) for more detail. - -## Setup - -First, let's install langgraph: - -```python -%%capture --no-stderr -%pip install -U langgraph -``` - -
-

Set up LangSmith for better debugging

-

- 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 aps built with LangGraph — read more about how to get started in the docs. -

-
- -## Example graph - -### Define state -[State](../concepts/low_level.md/#state) in LangGraph can be a `TypedDict`, `Pydantic` model, or dataclass. Below we will use `TypedDict`. See [this guide](../how-tos/state-model.ipynb) for detail on using Pydantic. - -By default, graphs will have the same input and output schema, and the state determines that schema. See [this guide](../how-tos/input_output_schema.ipynb) for how to define distinct input and output schemas. - -Let's consider a simple example: - - -```python exec="on" source="above" session="1" -from langchain_core.messages import AnyMessage -from typing_extensions import TypedDict - - -class State(TypedDict): - messages: list[AnyMessage] - extra_field: int -``` - -This state tracks a list of [message](https://python.langchain.com/docs/concepts/messages/) objects, as well as an extra integer field. - -### Define graph structure - -Let's build an example graph with a single node. Our [node](../concepts/low_level.md#nodes) is just a Python function that reads our graph's state and makes updates to it. The first argument to this function will always be the state: - -```python exec="on" source="above" session="1" -from langchain_core.messages import AIMessage - - -def node(state: State): - messages = state["messages"] - new_message = AIMessage("Hello!") - - return {"messages": messages + [new_message], "extra_field": 10} -``` - -This node simply appends a message to our message list, and populates an extra field. - -!!! important - - Nodes should return updates to the state directly, instead of mutating the state. - -Let's next define a simple graph containing this node. We use [StateGraph](../concepts/low_level.md#stategraph) to define a graph that operates on this state. We then use [add_node](../concepts/low_level.md#messagesstate) populate our graph. - - -```python exec="on" source="above" session="1" -from langgraph.graph import StateGraph - -graph_builder = StateGraph(State) -graph_builder.add_node(node) -graph_builder.set_entry_point("node") -graph = graph_builder.compile() -``` - -LangGraph provides built-in utilities for visualizing your graph. Let's inspect our graph. See [this guide](../how-tos/visualization.ipynb) for detail on visualization. - - -```python -from IPython.display import Image, display - -display(Image(graph.get_graph().draw_mermaid_png())) -``` - -![](data:image/png;base64,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) - -In this case, our graph just executes a single node. - -### Use graph - -Let's proceed with a simple invocation: - - -```python exec="on" source="above" session="1" result="ansi" -from langchain_core.messages import HumanMessage - -result = graph.invoke({"messages": [HumanMessage("Hi")]}) -result -``` - -Note that: - -- We kicked off invocation by updating a single key of the state. -- We receive the entire state in the invocation result. - -For convenience, we frequently inspect the content of [message objects](https://python.langchain.com/docs/concepts/messages/) via pretty-print: - - -```python exec="on" source="above" session="1" result="ansi" -for message in result["messages"]: - message.pretty_print() -``` - -## Process state updates with reducers - -Each key in the state can have its own independent [reducer](../concepts/low_level.md#reducers) function, which controls how updates from nodes are applied. If no reducer function is explicitly specified then it is assumed that all updates to the key should override it. - -For `TypedDict` state schemas, we can define reducers by annotating the corresponding field of the state with a reducer function. - -In the earlier example, our node updated the `"messages"` key in the state by appending a message to it. Below, we add a reducer to this key, such that updates are automatically appended: - - -```python exec="on" source="above" session="1" -from typing_extensions import Annotated - - -def add(left, right): - """Can also import `add` from the `operator` built-in.""" - return left + right - - -class State(TypedDict): - # highlight-next-line - messages: Annotated[list[AnyMessage], add] - extra_field: int -``` - -Now our node can be simplified: - - -```python exec="on" source="above" session="1" -def node(state: State): - new_message = AIMessage("Hello!") - # highlight-next-line - return {"messages": [new_message], "extra_field": 10} -``` - - -```python exec="on" source="above" session="1" result="ansi" -from langgraph.graph import START - - -graph = StateGraph(State).add_node(node).add_edge(START, "node").compile() - -result = graph.invoke({"messages": [HumanMessage("Hi")]}) - -for message in result["messages"]: - message.pretty_print() -``` - -### MessagesState - -In practice, there are additional considerations for updating lists of messages: - -- We may wish to update an existing message in the state. -- We may want to accept short-hands for [message formats](../concepts/low_level.md#using-messages-in-your-graph), such as [OpenAI format](https://python.langchain.com/docs/concepts/messages/#openai-format). - -LangGraph includes a built-in reducer `add_messages` that handles these considerations: - - -```python exec="on" source="above" session="1" -from langgraph.graph.message import add_messages - - -class State(TypedDict): - # highlight-next-line - messages: Annotated[list[AnyMessage], add_messages] - extra_field: int - - -def node(state: State): - new_message = AIMessage("Hello!") - return {"messages": [new_message], "extra_field": 10} - - -graph = StateGraph(State).add_node(node).set_entry_point("node").compile() -``` - - -```python exec="on" source="above" session="1" result="ansi" -# highlight-next-line -input_message = {"role": "user", "content": "Hi"} - -result = graph.invoke({"messages": [input_message]}) - -for message in result["messages"]: - message.pretty_print() -``` - -This is a versatile representation of state for applications involving [chat models](https://python.langchain.com/docs/concepts/chat_models/). LangGraph includes a pre-built `MessagesState` for convenience, so that we can have: - - -```python exec="on" source="above" session="1" -from langgraph.graph import MessagesState - - -class State(MessagesState): - extra_field: int -``` - -## Next steps - -- Continue with the [Graph API Basics](index.md#graph-api-basics) guides. -- See more detail on [state management](index.md#state-management). diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 15b1c4e8e..ac73e9e34 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -56,29 +56,6 @@ plugins: - search: separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' - autorefs - - markdown-exec: - ansi: required - hooks: - python: - pre_session: - - _scripts.notebook_hooks:handle_vcr_setup - post_session: - - _scripts.notebook_hooks:handle_vcr_teardown - py: - pre_session: - - _scripts.notebook_hooks:handle_vcr_setup - post_session: - - _scripts.notebook_hooks:handle_vcr_teardown - typescript: - pre_session: - - _scripts.notebook_hooks:handle_vcr_setup - post_session: - - _scripts.notebook_hooks:handle_vcr_teardown - ts: - pre_session: - - _scripts.notebook_hooks:handle_vcr_setup - post_session: - - _scripts.notebook_hooks:handle_vcr_teardown - mkdocstrings: handlers: python: @@ -123,9 +100,9 @@ nav: - LangGraph: how-tos#langgraph - Graph API Basics: - Graph API Basics: how-tos#graph-api-basics - - how-tos/state-reducers.md - - how-tos/sequence.md - - how-tos/branching.md + - how-tos/state-reducers.ipynb + - how-tos/sequence.ipynb + - how-tos/branching.ipynb - how-tos/recursion-limit.ipynb - how-tos/visualization.ipynb - Controllability: @@ -203,7 +180,7 @@ nav: - how-tos/autogen-integration-functional.ipynb - Prebuilt ReAct Agent: - Prebuilt ReAct Agent: how-tos#prebuilt-react-agent - - how-tos/create-react-agent.md + - how-tos/create-react-agent.ipynb - how-tos/create-react-agent-memory.ipynb - how-tos/create-react-agent-system-prompt.ipynb - how-tos/create-react-agent-hitl.ipynb diff --git a/docs/tests/unit_tests/test_notebook_conversion.py b/docs/tests/unit_tests/test_notebook_conversion.py index 61740b37d..46f8ef60c 100644 --- a/docs/tests/unit_tests/test_notebook_conversion.py +++ b/docs/tests/unit_tests/test_notebook_conversion.py @@ -1,81 +1,10 @@ -import nbformat import pytest from _scripts.notebook_convert import ( _convert_links_in_markdown, - md_executable, _has_output, ) -EXPECTED_OUTPUT = """\ -```python exec="on" source="above" session="1" result="ansi" -print("Hello, world!") -``` -""" - - -def test_convert_normal_code_block() -> None: - notebook = nbformat.v4.new_notebook() - notebook.metadata.language_info = {"name": "python", "version": "3.11"} - notebook.cells.append(nbformat.v4.new_code_cell('print("Hello, world!")')) - markdown, _ = md_executable.from_notebook_node(notebook) - assert markdown == EXPECTED_OUTPUT - - -# We treat cell magic as a non-executable code block. -CELL_MAGIC_INPUT = """\ -%%capture -%pip install numpy -""" - -CELL_MAGIC_OUTPUT = """\ -```shell -pip install numpy -``` -""" - - -def test_convert_cell_magic() -> None: - notebook = nbformat.v4.new_notebook() - notebook.metadata.language_info = {"name": "python", "version": "3.11"} - notebook.cells.append(nbformat.v4.new_code_cell(CELL_MAGIC_INPUT)) - markdown, _ = md_executable.from_notebook_node(notebook) - assert markdown == CELL_MAGIC_OUTPUT - - -STDIN_INPUT = """\ -input("Enter your name: ")\ -""" - -STDIN_OUTPUT = """\ -```python -input("Enter your name: ") -``` -""" - - -def test_convert_input_cell() -> None: - notebook = nbformat.v4.new_notebook() - notebook.metadata.language_info = {"name": "python", "version": "3.11"} - notebook.cells.append(nbformat.v4.new_code_cell(STDIN_INPUT)) - markdown, _ = md_executable.from_notebook_node(notebook) - assert markdown == STDIN_OUTPUT - - -NO_STDOUT_EXPECTED = """\ -```python exec="on" source="above" session="1" -display(x) -``` -""" - - -def test_convert_block_without_output() -> None: - notebook = nbformat.v4.new_notebook() - notebook.metadata.language_info = {"name": "python", "version": "3.11"} - notebook.cells.append(nbformat.v4.new_code_cell("display(x)")) - markdown, _ = md_executable.from_notebook_node(notebook) - assert markdown == NO_STDOUT_EXPECTED - def test_has_output() -> None: """Test if a given code block is expected to have output."""