Compare commits

..
Author SHA1 Message Date
William Fu-Hinthorn 2bcd48f497 format 2025-04-28 08:33:14 -07:00
William Fu-Hinthorn ed8c91c39a Add how-to 2025-04-28 08:24:27 -07:00
William Fu-Hinthorn 6ca126f94d Add schema updates for the configurable headers 2025-04-28 08:02:44 -07:00
149 changed files with 3282 additions and 2857 deletions
+1 -21
View File
@@ -22,12 +22,6 @@ MANUAL_API_REFERENCES_LANGGRAPH = [
"create_react_agent",
"prebuilt",
),
(
[],
"langgraph.prebuilt.chat_agent_executor",
"AgentState",
"prebuilt",
),
(["langgraph.prebuilt"], "langgraph.prebuilt.tool_node", "ToolNode", "prebuilt"),
(
["langgraph.prebuilt"],
@@ -69,18 +63,6 @@ MANUAL_API_REFERENCES_LANGGRAPH = [
([], "langgraph.checkpoint.sqlite", "SqliteSaver", "checkpoints"),
([], "langgraph.checkpoint.postgres.aio", "AsyncPostgresSaver", "checkpoints"),
([], "langgraph.checkpoint.postgres", "PostgresSaver", "checkpoints"),
# other prebuilts
(["langgraph_supervisor"], "langgraph_supervisor.supervisor", "create_supervisor", "supervisor"),
(["langgraph_supervisor"], "langgraph_supervisor.handoff", "create_handoff_tool", "supervisor"),
([], "langgraph_supervisor.handoff", "create_forward_message_tool", "supervisor"),
(["langgraph_swarm"], "langgraph_swarm.swarm", "create_swarm", "swarm"),
(["langgraph_swarm"], "langgraph_swarm.swarm", "add_active_agent_router", "swarm"),
(["langgraph_swarm"], "langgraph_swarm.swarm", "SwarmState", "swarm"),
(["langgraph_swarm"], "langgraph_swarm.handoff", "create_handoff_tool", "swarm"),
([], "langchain_mcp_adapters.client", "MultiServerMCPClient", "mcp"),
([], "langchain_mcp_adapters.tools", "load_mcp_tools", "mcp"),
([], "langchain_mcp_adapters.prompts", "load_mcp_prompt", "mcp"),
([], "langchain_mcp_adapters.resources", "load_mcp_resources", "mcp"),
]
WELL_KNOWN_LANGGRAPH_OBJECTS = {
@@ -162,9 +144,7 @@ def get_imports(code: str, path: str) -> List[ImportInformation]:
for found_import in found_imports:
module = found_import["source"]
if module.startswith("langchain_mcp_adapters"):
package_ecosystem = "langgraph"
elif module.startswith("langchain"):
if module.startswith("langchain"):
# Handles things like `langchain` or `langchain_anthropic`
package_ecosystem = "langchain"
elif module.startswith("langgraph"):
+14 -14
View File
@@ -1,6 +1,7 @@
import ast
import os
import re
from pathlib import Path
from typing import Literal
import nbformat
@@ -25,7 +26,7 @@ def _uses_input(source: str) -> bool:
def _rewrite_cell_magic(code: str) -> str:
"""Process a code block that uses cell magic.
"""Process a code block that uses cell magic.:w
- Lines starting with "%%capture" are ignored.
- Lines starting with "%pip" are rewritten by removing the leading "%" character.
@@ -51,14 +52,10 @@ def _rewrite_cell_magic(code: str) -> str:
if stripped.startswith("%%capture"):
continue
# Rewrite %pip lines by dropping the '%'
elif stripped.startswith("%") or stripped.startswith("!"):
# Drop the leading '%' character and then drop all leading whitespace
stripped = stripped.lstrip("%! \t")
# Check if the line starts with "pip"
if stripped.startswith("pip"):
rewritten_lines.append(stripped)
else:
raise NotImplementedError(f"Unhandled line: {line}")
elif stripped.startswith("%pip"):
# Drop the leading '%' character
rewritten_lines.append(stripped[1:])
# Anything else is not supported
else:
raise NotImplementedError(f"Unhandled line: {line}")
@@ -250,10 +247,13 @@ class EscapePreprocessor(Preprocessor):
)
cell.metadata["exec"] = is_exec
# For markdown exec migration we'll re-write cell magic as bash commands
if source.startswith("%%"):
cell.source = _rewrite_cell_magic(source)
cell.metadata["language"] = "shell"
if self.markdown_exec_migration:
# For markdown exec migration we'll re-write cell magic as bash commands
if source.startswith("%%"):
cell.source = _rewrite_cell_magic(source)
cell.metadata["language"] = "shell"
cell.metadata["has_output"] = _has_output(source)
# Remove noqa comments
cell.source = re.sub(r"#\s*noqa.*$", "", cell.source, flags=re.MULTILINE)
@@ -352,7 +352,7 @@ exporter = MarkdownExporter(
def convert_notebook(
notebook_path: str,
notebook_path: Path,
mode: Literal["markdown", "exec"] = "markdown",
) -> str:
with open(notebook_path) as f:
@@ -1,18 +1,5 @@
{% extends 'markdown/index.md.j2' %}
{% block input %}{# cell.metadata.language is an addition of our docs pipeline. #}
```{%- if 'language' in cell.metadata -%}
{{ cell.metadata.language }}
{%- elif 'magics_language' in cell.metadata -%}
{{ cell.metadata.magics_language }}
{%- elif 'name' in nb.metadata.get('language_info', {}) -%}
{{ nb.metadata.language_info.name }}
{%- endif %}
{{ cell.source }}
```
{% endblock input %}
{%- block traceback_line -%}
```output
{{ line.rstrip() | strip_ansi }}
@@ -21,13 +8,13 @@
{%- block stream -%}
```output
{{ output.text.rstrip() | strip_ansi }}
{{ output.text.rstrip() }}
```
{%- endblock stream -%}
{%- block data_text scoped -%}
```output
{{ output.data['text/plain'].rstrip() | strip_ansi }}
{{ output.data['text/plain'].rstrip() }}
```
{%- endblock data_text -%}
+1 -8
View File
@@ -31,15 +31,8 @@ REDIRECT_MAP = {
"cloud/concepts/api.md": "concepts/langgraph_server.md",
"cloud/concepts/cloud.md": "concepts/langgraph_cloud.md",
"cloud/faq/studio.md": "concepts/langgraph_studio.md#studio-faqs",
# prebuit redirects
"how-tos/create-react-agent.ipynb": "agents/agents.md#basic-configuration",
"how-tos/create-react-agent-memory.ipynb": "agents/memory.md",
"how-tos/create-react-agent-system-prompt.ipynb": "agents/context.md#prompts",
"how-tos/create-react-agent-hitl.ipynb": "agents/human-in-the-loop.md",
"how-tos/create-react-agent-structured-output.ipynb": "agents/agents.md#structured-output",
# misc
"prebuilt.md": "agents/prebuilt.md",
"reference/prebuilt.md": "reference/agents.md"
"prebuilt.md": "agents/prebuilt.md"
}
@@ -0,0 +1 @@
eNqNVgtsFNcVdSEttE1FpLYqoRCGJYnj4BnP7H/tWo7/WuPFn10X25hsZ2fe7o53fp43Y+/asQoGp1IpbaZKQgMtEWB2iesf5RPH1ClfQQpEJVXSGNOoakiDIQKnbUTalNA3+4F1QVVGsnffvHPvufe+c+/bvkQnUCAniV8a4kQVKDSjogXU+xIK6NAAVDfHBaCGJXagvs7r26Mp3NSTYVWVYWFBAS1zhCQDkeYIRhIKOqkCJkyrBei7zIOkm4GAxMYuzrvSYxIAhHQIQFMhtq7HxEiIS1TRwtSFTDAOYmoYYF2ARh8KxomYtyofKy8tMeVjJkXigYHUIFBMvflYtrmo8XwWhIaQgyqNttBLVZJ4P0PzfJpUjclJUFATk0kaGI413hggf4OjAQS7W11KTHQ313TW+aIwbGuvcRiwOyaFiF2khaSfEFD96YgNDK2ENAFFZbCZetpMvMTQhk0bWreZUvm0mXpNvb3r/ycJU6WiSEohVgpRhoZJcv1EbpMYEaUuEatNe8rNaxOxeh7QEGBBLorFJE3BBCPhCIBEdqmM1OeUwP9FEu1dj0wEiQV8Mj1Zxa2S4cVIm0KfUFUALaBFkOYhMNwDQUaSUTXFYCUJMk35fwr+RSrJAsgonJyGmZpQumrYkIiEIWSWSoKSIiQrgwUVScBoLMR1AhHLVD5ZEplWEAtSNkxSygpSLCoySC0z0OQiEy/KkxND6KCMiqIu4BRgVG/dXbRRqAxaCrQDRkVodK6JMKBZRPVezkMDYQmq+sjcHhmlGQagugKRkVjEoQ+Hujk5H2NBkKdVMIg0IYJkffTBCAAyTvMoo3jKSh+jZZnnUiEUtENJHEprCDdiuXd70OgYHHWdqOoH61AQpe6C+hhqZhGjCKuTIMeiOBIPJ/KoOXGeRvHE5eT+kewNmWYiyAmeHhR6PGU8ko2RoL7XQzN13jkuaYUJ63tpRbBbD2S/VzRR5QSgJ8rr76VLb96lsxAURbj2z3EMYyKj703q8NU5xkBVYjgjIR/6LjLOSFKEA/rU3/1+JugPCMWEVueLuYBY0eCwy14fQ3a7Wn2tnEXSgLOWN9eUt5Lf90QcTTJ045TD7LDYHC6LE6cIkqAICqddRLiitspX7Q60lDF2l9PBr2aoslCjAkVfTRcnRRsIP9la6alu5zi4xl8b5GiVUCr9waCngbQQdlpuCHmrBaUDsJU2RyigeWxkaRGGotM6ObZY1Vwi6JIcjdWuQHNno5ljNUK2eQAsD1PWSHSNv9PWEeBK661RT1Z4DieFk+kI7aTVSRrPSEYbPBBDaljf47Ba9ykAymgsg01xVDJVg30DSIfg3JlEejzvrlt9V8LfHqhAmtQnfWEtHzPbMS+QMTNptmKUpdDiKiRJrNrjGypP0/juK8H9PoUWYRDJsDIj+QQT1sQIYAfL7yv2SUPs6CSN8NE4w0FUliDA01HpQ814Y+piwt0VB1KdhUtKiBa57iSt/oohZHQRceLB9DbqecMlIscFqA9QDodjJL2VEdkgSozEKRInqdeM9mdQTxmRy5Ki4hAw6N5TY/pUvkBHjYYqtlA2ix1VuQhNIYbXWODVAhWSgEhhESYrgJdodiKKo9kIeE7g0Ckk/6fvVNQslHFG4/ciVCkC0PW7z0KmntezIQowGIw87jgacKHnt/cHZXyZDYzLZp+YC4MgK6A9dgGO37ufdrGbhEPRDBjnWH3qUbTwm4NBawCQtqDLbreZA0ELYIJWs9lip20MSwNmtLwKL6eZMMC9SbnpiYqWNaUed/nhZjxbN3idnPrVkRAlKHLBYNwLFHQ0+iDDSxqLZqMC4shXY2mLftDJuCy008oGXAzlYGgnXrm2cSzj7Y7KBozBmvz5sTGemuen5s1fvmVhTvKZj/5u31Ybp388TX7rmZtrl/7zlw+cdm8/KU0f3NX/1tdX07vyqvSfCbaJxTPj5/v/81mgaP/k2XcWlZ2LVkFiW86GN54/dbTimPtK49bl75098NnrF5+f/qOrtfji6PLdZRcSm9YX/JDlfas++nzR1yZ+/fjxq40r3JHWVuflzYeuqQ/joS0fdB8WFwpe36H8Au57r77/xE/+9r5M7XxTPH68ZuPOU7OrcnLGraFlFwaiLLV5T9HuCz07V2458nLOLw4MVi2++XT3X599d19uv21z3H3rpaMr2xf55L5lPZd++oN/jDFY8eLVf6l/99IZS/xosfzp2RdqS1uOXTPlzn75q9Z5r33T9/T2pfueOvPk3qPy1kMrHGPapYUz/cJk7ol8drjmgbWDo4duEZt6SpaM+H7zr/DL50qXhRI1l09LU96Jd248EipZcHj4k1f47ueaqtSVD88ue+R33Q9ueHtmx8fYY7ef8268/sLSDR3b1i75xtsXpsDp68LU/k/PHx/9qP+ELXrt854/LP1Rywexydx449Vn/UfeWLVj25vXb229MVu7ZGbLyRU3PdqJxxfc/NP2ruZPAlrzzBCRt6LnKe/0yIfDede+cyT3w7Pfdb84fvXjmWf+XdL0c1PH1LmJ6Sv2uDV246HOvB2Bl0piZb9i37p8pSZ3+s/nf//ousqpwHKi7FhfZPZB46Tn53xlwXDOSXTs/wUjQUsQ
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
eNqdVWtsHNUZdWQe+QFIbUiViiaebiI1JJ7xzL5m15ZBZv2og9ev3RDHeazuztzdHXtm7mTujL1rd2vlUShgQoa4DQalNLG9dhbjxBAguE4KqYBEKqWqGmiSKjQtlekjIqVVKUHUvbOPxFbyq/NjZ++9536vc75vdo33QB1LSF0yKakG1IFgkAW2do3rcIcJsbEno0AjgcTR1pZQeMTUpfPrEoah4cqKCqBJDNKgCiRGQEpFD1chJIBRQf5rMsyZGY0iMXVhyWC/Q4EYgzjEjkpqS79DQMSXapCFo5dcoSRMGQlI9UJAXjolqVSovpwK1DzoKKccOpKhjTQx1B3pbWRHQSKU7a24ZtBuZINUsuTIGxs6BApZxICMIdkwoKKRtAxTt42wDGvvISQXIjFSWs54zFRzmdu2rv+vpPodKlBygDg0IoX4bIwIsaBLWgHm2IghycBOA1EEuSCTGNIVYMOomI4UClBxqQeqlIyE3C5jG9OATryQ6uOcS00nVdUNCeaXRWhuUYyX5CmpcUc6bReIMCXpULQzuoG2C1VEo2gXFAyCTm9LjycgEImrp0cTCBvW1GIWjwJBgKSqUBWQSDxYL8X7JK2cEmFMBgbMEuZUmKuOle2GUKOBTPLJ5G9Zx4CmyVI+gIoujNTJAtO0HcnNx1mbU5roQjWs4y0kiJrGitYUkZtKcYzbx7DHkjQ2gKTKRD60DEg8GS13/rOFBxoQuokRuiBlK5O/PLUQg7A1FgRCS2iRSaALCWsM6IrX/crCfd1UDUmB1nig9WZ3hcMb7lwMxzH+6UWGcUoVrLGcCl9fdBkaeooWELFhHWKnivWRoRo3EtaIy++d0CHWSPPA3RlyzTDxrlHCBfzlmfFCEx1uebhI4qWSb4zWEl6sk+GEWU45vVQIapSTdbopzlXp8lV6fFRDMDwZKLgJ35KG6bAOVBwjVNQVaR8XEqbaDcVs4JaEn7QJJ9nY4ZP+pGFSQxjShaisyQ66PT8+6MbaV/LqopEeB6rUl3NrHbHJJONCUo8XjonqbZPEOa1gUgjeOVU4KdY5S/JiaY6lWe4NW/8CkZUduIZ0g8ZQIMPJSFnnyxWQtDVV7eI8Li/LslWkDQXZFGHIjNYihfjEVZSmQxkBcSZJk+EAZUmRCAm538LgI3rhyGX2xM0IA3VDMiMnXGz+ObUQokPbg53GdUOjfvLM3hpUtOW0MX7ePbMYhuGCgEa8Cj5x83nBxGEWTyaLYFoSrfNryCLi83l4AP1OvwdEPYDlfS7WH3MLAu8WfU7B5T0aqKcDQEhAOpRTmzVeu7m5JtgYyIaI7QBC3RJ85sKS0khEiEWiSjVjtoRTfqjWtvFeLRQW2D5/Z7hTciET+ppk54ZAJ/tIsJvfqOFGmuOdvMvD+10+mmNYhmM4GviZRG1TfbihMbr5IcHr9/HywwL3ULxdx2p4Q6+Ekm1MhO2sCzZ0SRJujjTFJGAwel0kFgu2sS7GC7S2eKhB0XdAsc7Dx6Nm0MPWED7JwK2uqKKIEsksxNWFfqBJP9B2N7gr2WI3VFFiTgXVzOLZV0V9l3zZWlQ5VUXaiMgJkjcZzCHJgNXNSIXnh0gNzB5JrDZMvwp7Ed/e4I929LQ7JdFkNE8Q4kCCc3cnmyM9nh1RqabVnQwuKALv42i2UAcv6/blxHMj9P8zqtc66IXtTbdo+U/4uIqwKsVimRDUSQtZWUFGpkjGuA4zhPP2ms3WcZ/gdwGf289HfT5eAD66blP7saK168Ng1P4G5L7lOzP5D8/bS35e9uTSktxTarRvQ8+z96bnNnV87g9/e4919cfmcxdmwxt5qWx435+2vvnS9HD67HP1X3y6bF/JU94HOz6ofmBuVdlHI++uOLNyzV21bzUO/eizTWV/vvjpuV8MHuj9cuyrU/PXzr734dt//Dds1m/f/yh47f21n5ye+Vpm8t5n3DO7f1DX/LuJO/5Rfs9I8ptM50/dVeG76aNnXrhvwHvwva+kj++8PNyx4v5f15z+ft8g1yN//s7hS5fXr5Er3whvMPbOLW0ceJw7Vu9Z/odfNTZsXdZ0R+uK4MR9H8480nXbPm7d8q1HDu14fe/IP/ct27QuPVjG9X85ffzO9LxycuA/n/31iU9SK5++Zv7msaxn6Fznv74YvHrqJ1s9wl3uii3q3qH/1jTffmF9pfCmsv3AxJHpe969e+3q5OoTO8XSlhe+8/eeV3cumx05ODtwcOCpr08/e27vizg8d+3I+9uHZ1+O/W3q8J6mrr888VtH75X975ztO6AqZRMfpVeFPxhaPiZub+Vfbvv946fZztIV3/PtFn/44qFVj82vHqtaw1ycv9KQxfebDyxt6t9/ZctbK78FhmPtc4n16y9e3jm5YffaS48+yZ5xXG2+FCFMzc+XliDt2sd1pSUl/wM+DOUE
@@ -0,0 +1 @@
eNqNVgl0E9UaLgfUshQqKAKiHVMXhE6ayZ5i4bRpiy20aZpgugB5k5mbZJrJTJiZtEkXl4JSSqGOPAG1INLSYi2FQh+U9YEgi0CVIz4oVoooR95BEERll3cnTbUs5x3nnCQzc7/7/dv3/zcVjUWA4ymW6ddMMQLgcEKAD7xY0ciB2X7AC3MbvEBws2R9tslirfNzVOd4tyD4+IT4eNxHyVkfYHBKTrDe+CIsnnDjQjy899EgRFPvYMngyf7jSmVewPO4C/CyBKSgVEaw0BYjwAeZDW55gUcEN0CKAQ5/OIRikKw842RZHCLjWBpIKD8POFl5HNJ3K+On6T4QnOcpXsDhEnwpsCxtJ3CaDhsUgr4QyOlnQgFKGIqU3kgge7pOk4lrLUFPpi8jg0tLeWW2KVn9skInwf7ckgCtM7g3xOMCgj3sroTBOZffC72SrMlKZ8gISgjOgPczZEyQmCErl5WXz7zHe1m6gHgpl1tAHAAhaNZPBqXAIb5v3FIcd8Vj/zte32vKel9y7zUtlz0wk/cShYtFCYiHYYsZxMlyDyjTTPjGy5KADmXKJ6BqVgJJGcTgLy9wAPfCBydO80AKDnh9UHmCn5NIFHJFOOD/U7u/UxQS8ARH+cIw2XQeQJVRUGosApF9EgKj8OISLJQFH85BPtgKfIjcx0GJcwIFeh6lyoZuAOOXYiiQhSvGO2VS3L3OwiApxgXrLmUHdhLFATIEDxH0RbKOQkDAVEsSaXQDnISWT0VE17tZXhBb7u6xdThBAJhQwBAsCfnFta4SyheHkMBJ4wJogqViQCgxYpMHAB+K01QRaOjZJa7HfT6aIkKhxhfyLNMcLi0q+XL/cpNUThR2LSOIbSboRFJ6fHYQDgMGweRqvVyxPoBCnVAMDZsbpXHoT4MvtL6t74IPJzyQBA0PGrGhZ3NLXwzLi6szccJkuYsS5wi3uBrnvFr1xr7vOT8jUF4gNhqz7zcXXvzLnEqOYXJD613EfJAhxNUhAW6+azMQuCBKsJBD/EjRQLCshwJi5y92O+G0O7yJApecp9Yp9HkBKlULiGkuQW9L5TIMxny/nnXZMKPVVOy1Kgt1fjOK6ZQ6lUan16pRTK6QY3IMdSVnWVRquV8V4FJyTPZsZ7rdm29QuvG82Ra5dbo8M0houEJ6mlGV4reYbfm5SbkEmUFOTSfSAnlOG2nw5oJcRYYti7Rpi6dnG5KdFpXZPBGB3vmLKDIxaCjK4bTJKic9lbZmJ6co1QGL35FiyMnPob0qfzqD5aelmwm/S+Hp454K06KKsIdahVqvkK6WXm3QgHEJbrFOr9Cv4QDvg2MdzGmAKRP8fEU91CE4fKAxPN5Xmab+JeHH61OgJsUdVrc/DlFqEQvwIUqFUo1gqgSVLkFtQKZkWpuNYTPWB0qw1crhDO+EMkztlXwj4fYzHkA2GR8o9h2S2GElJffhWEJBwMfyAA17JTbnojk9BxuanrKxp7NQlnPhDFUSMit+LAkZHmQU0xZehiNAooTGUS8v1qu1KkNLeKlXZE0wMAWKKVAFtkVqfQL2lOS5j+UElAcEPDeFoNgZ58UDUkMlqjCNSguzPBGOH4L2k0AqE+uFRvmJiI8DNIuTWwMoHIqAprwUrELoO3wmw2bBpBq1348QWA+Ax/calaLn2tkXwgHJghTHn0T1BnhtfzCol0spYQwa5da7YTzo41Cd1su3378eplil4JsDvWCUIsXOZ+GDXenQKDWYDjOQerXSQDhwldYBDFCPpN6g0aqU64xpqBEn3AC1hOQmNqbkZSVlphs35aJ9dYOafD3/WhoZlmcop7PBAjhYGrEpdLrB2ciBBsiVk5QntukJgwrX6oCe0Cl0pIJAU20563vZ/lRZvTRYQ39f3mjomeWf9T8RsyAyInT1h587dwTrN0xX2pDyq7boksKoYOusUu9+dlHp4Pwn+9Wuennbm9gY8/CNsR1z93T+K/qH7jni72ceO7cAefulH98e+449K2JQ04KCcbTr2O+7l3+7Y3W/7vKSLYWTZ95a+zPb8uqO3MO6QzXf7dn0SZUytuy3gWsvmlXnb57OPnjlh00VadjGFfb4lvglx3W2pXV1VzVbh3jmrJh5Y+NnZbODWvuK5fafXl/+woLW9jdfy4qI2FndXSs7fi2n69PtXQUl1UfbcruerogYdFleMGhEbKqmOTUo5H9R9ZLzdmXBtXMg6mq9ceBOM1l57BHd65oOPKom5uepKyM9vxe8tW/wqBOjnjnWr6w/mbj4IVf7xYXNxECtIdMxNnrKc5lbnmodsEdDrxm+Z1D1saJ3l3bcTOg4ffDqsi1txi8XtS/M0yzQNlx9cVHT3v+sHsPatp+5Mrp2xaSPj6rP/LFyUOq8r74qaJ+j//Xd4s6uSMeFm+Yt+04VYEXL1gbEozFr9keaPlz86OFd3cPOimlr5p7M2n2WjftubcDCHnlbpa7aMKExw/zU7MFppTHGeRUbH7eTN0rJI4kBlkw8e7a6LTJ9/J7o6ubduqjPK996JXr09vmPvRH4yWjyXxg6s7XecOXQtNt8zMvR3PVIdsSW99uf0gTujJmdmDLh+ZW7lrT133P6k8oN2HVsZdrNjEmFjo7JT37qvVVzRjnh4qEvZDEJc4cuPDfpUsuUZzyB7ieRFot19Oe22MWlJ83fLC/P2icOjd57/OCiOqzg8vRXrh/SP2IaX7n1icLKqALl0K3WCw/dIXOaG2+oriXMicnZe2z/kDLd0Az5uCmTV1YNOyWOeevF8SO+nb9k8Z1/lFzSDHINu3XpdMzFV2ufvr5tTM3RGueYX7rHNS3tavqgdr+pLWl+5NX5MizL1F33Va3pEnGc5vamPbxr6evJB0bGqt95YXrZyXRmR3vJpjO679UXbIWrGm83D+4cemzA4dunzJ7h1WDWAe17I4vrmhyFJzffLFuR/hrWGvvFCMv5MjBWvTiqeWD9vne/PPu1dVhtyT+3V2nSCkc/t6jtZqAL8TTMuszwR/RJK5cfPL8z6Y1z0ca4mjnzZkysfrQscpYae/G7tvPXhtcupXL3X1jtvv0wWLZjJ1GRWfX8h+asjgs/2YfNO3pdW7nuTMdr63V7H9vmShqR+GPsqK/nOX59NbHfteKXdv132wf5Ay6Xf+n6zfPDtMCkf793rer7g5NLnjikm7iuRHPxXHv71j8mn+AXFh74zFa8fVnOs0zNkCvGFcRHqWNXOe50RW8el+ROOJ8ZYJiRRza8H//jJv31kl+ipPbtH6E7sXBq98MREf8DqwE5Nw==
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
eNrtVwlYFMe2HgQVjQtgXCM6jr4Yl559BVFxQARkETCAiGPTU8M0zEyP3T3IgHzuYFzvqBi3XGRHBEQlRI1RXAOIxv0Jatwj1+1GiUpU9FYPQzJEv5e8+3nfu3nP/j6Y7qpTp845/zmn/lpQmARICicMDiW4gQYkitHwg1qzoJAEs0yAohcV6AGtJdR5oSHhEbkmEq8fqaVpI+XB46FGnEsYgQHFuRih5yUJeJgWpXnw3agDVjV5cYTa3OBgTuXoAUWh8YDieLBjUjkYAfcy0PCDEwmXDKfYtBawZwMU/pBs3MAOjlaO44xmc0hCBxgpEwVITlosHNETaqBjhuKNNCImGCGKJgGqh2MaVEcBOEADvRG6QptIZjGfy2fGCEJn2502G61KNSaD1VtGxy/vHuxUjgHVWwXiAa2yGcXIqAGFkbjRJsaZSgFoNg5tJ9hQ0s58DUHqUUaMyywzoiTUB2NLWZUbSRgzksZB6yeG02brCzCYGB9iOAYzZvVKw2H8bTMWOokb4jlpaUxUIDQ4CdRWcasCe0kiLgFgNJRMi00r1AJUDXdelaclKNpS1h6y7SiGARhGYMAINdRuKY1PwY2j2Wqg0aE0KIYwGYA1LJbiRACMCKrDk0BB6ypLOWo06nDM6igvgSIMJTZYEcaSN6eLGRARmAQG2lIRAo3w9ueFmmFuGdgCrkTBlZUnIxSN4gYdzBVEh0J7CozW+a/tJ4wolgiVILa8tRS0Li6zlyEoS34QioWEt1OJkpjWko+Seql4l/04aTLQuB5YCpWhb25nm/x1OxFXIOAqdrRTTJkNmCXfmn5ftVsMaNKMYATUYcnml7XFRwcM8bTWkiuSyYpIQBlhpYCFBXAZbaIW5EEsQF11oa1ickIC20D8ntU3zwfiYvkmwgTTXChgB6AGtpAvlLAFUg+hxEMkZ/sFRZQobdtEvBWGHREkaqA0EArfNtgLMa3JkAjUxcq3Av4NAzj0hjEfFiQCko0EBRCbVZaSKCSstVcg/j67WrMLIch41ICnWLe1bGXAhL0BN1TYpmERMCrh5oiesuSKRcIy20xbnIuhX3xEwEf4gr3JCCxmoMP1OIyd9b+tOUGYBXz47H5TgiYSgYGyFIn4rc9+exES6KExzO6/KMpTwGff24XadAkZGYVMure9GAXsDMqV6qndb87bVOTwqZLkNmEEV1vqh8EPlUCjEEvUMqFCJBSLNCINytcIpAI0TqGQaOQYiu1hqh+DWhjojARJIxTAYC+mzZb60Xo0makqL5FAIpJCTz1hB8J0JjUIN8X5EIwPlCfbSAIdgaq3KyciShTTAiTcmm2WQp/oYO8gf2VxODRSSRCJOFjd4OCoUmEaVZzeC+fzP52lJIP1qFYpxtQheirBfwJXOzkoMD4wThQYOUGfgvqY/SZNov0RgUwkE8ukMpEcEXD5XAFXgEi8E4VTQoWETO+vC00M89emoCBSwg1VmX3UOh1QSIPlYm6CVIZGBHonTzB7h+oifRI+TZH4KSRGidCf4lI+XNlkcqJQSEfLfDXaYPNslY839AZ2Wi+eJxtmIuyDlJetHhBYD0hrNUjaqsGTrbbGwIvbvvd5sifBYyzEoDN7ssOZYAL4C/t0OE4Dr2DCAOrXwhiYknC1F64UJE0gtYlKkpwSbRakhAWS/tP4yYSCNMr8RH6zBVODxEBoSlAI5XZB4EvFCN8WBylfLLdm4a+m/5NWVUYh9uWNhFiPI4ijgaAMuEZTEA5IWEKWYkxHmNSwjZOgAGIe5h1tqVDwJXJUIVZIFAo+XyOVIL6RYeVt2n5pBnnMGVCI6mCOJWGWXVqRF8dDLBZxPNl61EsuFfP51lN9fkHriXTUoXnwMmeW9XGEf69fLw+/tPIs3yXtu8iopstb6NQutTGzvTsMy4tdNH5JTnViiNtC4ssvuFPOz/XzbxiG+HJ6WiTxJzdsED0dwnFyyljO2qadWVubfPPv4/ZTY86/OPFwx9ZN3544/vL6vUuxxWtLZ14Q1jWFqK/XpRxdvtNtIX9vxgblgPKpXxQuozeUnfVtcKg1rkVKK4bM2NjM3RNzrOaePDprSaZrNy/1rVQf5yEZD69WFilepS8ftovyyX5W4VOdPmxCVf/6ifNSeHO2eFTpnDacdjBd+rIsfzPrYL8NopG3o3a/7rGmqbFXpjAkqClv0P4HWQ9b7oY/etWU8UP3sv33X+bN4F25WJadc0H3Y/yCno/uF/cbsmXr3d3/kf6yI150KvZ0F4e7tdOye+jvGS926JVdP6Gy45VyZOB1zut+Vzxer4+6Xl7klTl8VfCjmzP3ivNNP9SrzM6LTx2K3KO8+3V0wbqJdxJi3Yt4lZ0u9AfKQXW8c6X3Rz7wRfeFD5WrsqpmdsjExsTWGYpuD3TsfSUsI/XIJ9v+sqru0djTGz8QOeTU3JorowK/zprR+HjuLb95ggoHs8GdPMjtPOjswEreQxW+8NvYw9dOP+h4eIwz/UAq71OxrgVjXW2eN5K3iHs6evjevg6LT0R2yZ2zI8DY5bTguSMDryNrtfC8aYATi/UuOWKHnHfEEUez7ZcaTDqdnQgKjxx4zMIpGyNUYajuv6KFOMOxOIyQSkaYp+iV0UlTJlCSMI1/RIDZB0g+nUX9UfaIkvEmPbSK2Y2TOt3K26bD9+kM4ZvOSeMwfK299Rx/mq3H47U0Ow6wrQVtZhy3EcQ2pxg/2vmj+iNWvyfT/zSZ/p7l8p5O/+/T6QLMSlcs9Y//zdnKv4BHvHGVkMrE/72rRJ//o1cJkVjw57lKSOXv/CoBxCK1Ri2UizCxWCwVaKQagUwml4nlCpFAzRcI/vVXiXdAUdVSNUz0d0dRS39LUcOOGy7xXfbdG9VnTMz8G363nl/olOZ+92mok/PSCVtqMo8suuF2q1jv1nz1g4BKkIucWtZD5Jm74fZ+NeuQdodD9rSa4ibpXPerc543T/5u9lerBj+8mvbX7ZWekZe9zl16Rlzixa37sCJ1/FG/85k5J2PUSkPe1PE166dvP+67ZzRRezSF7XFGu6bk0WbeiKy+TWBGSdONH9i6mk+WxzleFbLmfX59E8djxYEjnZbWu/6NP7F+y8XN6Swf9iaXIZjYko8JNk3yXXm6+9YDlc7jppDLHJYvfNCC0AV4/yTn2rDtWWUnMx3uja398PKTC4N+bBmxNP9Y54ajjnMPJd6crNs2T7g68MCKyTO63R/r2Dw24iFvmvPgoqbpAq+cwPR7ZEWvsyuHP35e3uPgkTPBzdtyGw+Vnqk+czG5UpidlzAlpBY/sPM/008uuV/SlIW++sHrqbTs58EvPgvt6pVbNme8/hy4jo+eubdq7oovksY49XE93DO3PG7hkG8WHpv2NGlMD9csDhZ5/pik3xz+zcXn9k/rHXD3aHFVfvXTeoLVyit7fq7wG+n4bnml46j3vPLfgle2HU6qVj5n9agtMsxZoqJgK9GjjKj9JyNm9/ob1hhmVapmSCMDIoMU44iVSLbu00oaf8MS4RLcmiNv0xlpywQ7KSYION0aFWX74d8llL+Kx9qrse0SZosK503KyUCrbl2L6kLtPbASI2antox4i7LWLg8nadIErGb9PrG3I7iu7wnu/xDB3WM/oQU6CAnS2vUsX8cBGuUyfY1r19e48Ar0nhf/UV4sFwjeKS8WC/+cvDhPJBfw/18TY0ymkMilcjnMHoWYD9SYKA6ViaVAIEXjRFKN8M9AjCUCoRzi+M6IcYcTvyXGEZcMHym6pT2LdEnB+/b7aM2Rz2IO6B0/bIwjOu71XVbA134myo0a2uVDMNvtVtbz7i07Fx+bdT8uZqaqU79Bjxt/XHKuxWl8dO66LXRGzovGggMFkXNfPn2a1lw1O/7wi6Mvn514sG3u2P3mmAPHIxcWB/hGXV29Tnl+csZYD7ePXg66MPmTiKjv+vRd9vONhFmPSKfnM6szd0pr6UEFd578PGfagfUXIlctji3fuHraocIurKqWZsTtm2xeQheTaclPa90q89aF+LOWLhzlunhE/bBG36l+oZ15t/zOP5Pd/u4iR3Gl6PaKvSfrbh3t3nuSX/nQtSv7lWSMwzsPdiSuNdSypPOThmxySPrUY/wp95QglwHGygdO/Uozzi12n9UhY+fwEZs7ZfTfd8950fNJy1dv6DqYrP6+6vztr7Zvv/5z/TJCtX3u1h4/6V19yzU5nh3cU+7FaOU/Dc3vOfrKytV111aMCZynSexV5j7f6VT1GFZKdcvGGoXTpSqXx6KPG52D0K7RI3ef8F86acVnExt2ZGiffOy9MvVE7MPSEZ3kDatH7TweK+w69dTNAmP0/PRq7wErjI6NLgMFkzJSHUMDJ85ueNXtVYNbLtX74pRx1S6lEzt394gc6Lm+sLRbb58b6RXje+2OGvywdkiQMmLgqvIva7ouj3LrcXI+PZC8dFBiRukfZY81SbmDgu8s2FrT5XjU8/uXsZfX8VPTtUMzTClHsm6seCI1jfp48sHPswPccgaczuhSdWfWlqbZomJOxtjagEPpc4bM2XPx7NabndwP7no+L8CF9ru276+R467F9Her4H5RsuhMkafw2zM3X82USjez1qwpez3qjt/lFydLidc9W1xPbcz5S7Tvx9iTZ8jgs+trP2jaVTH8c7+DI12J0oLMqBtkr/QBdYYDL0d29y9SSS58dcTy4FVH7Y27Oy9oudzEjdtqDt9S2S4OazyMoGtHFusfp57SCg==
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
eNrtVgt0FNUZTogFaxGiGAgvWRcOFMhsZnf2mRAg5EE2IWzIJuRJ17szd3cnmZ0Z5pHsEoOSCFQ5UFZRC+ixQh40TcKbBuRNLSiPIlIwkYYjTyugVDi8tKR3NhtMxLZa7Wl76pyzO3Pv/e9//////v+/X1V9GRREmmPDG2lWggIgJTQQX6iqF+BsGYrSs3VeKHk4qibLZs9ZLQt06ziPJPFiXGws4GkNx0MW0BqS88aWaWNJD5Bi0TfPwKCaGidH+dvCn6lQe6EoAjcU1XGqogo1yaGzWAkN1OVoi4oWVZIHqsohQC9BRbMq0aWOUakFjoGKkCxCQV05C814OQoyypSblzA9pwixaKhFb1ESIPCigQswIkQTEvTyyCNJFhQluAZX5jiOCRkh+fmgcpfMBp1WdN37jlNVqFngDQq4oeQImabIUFAkBZoPialzRYiMVzzgVEiymxMuTvACRUyjbOOBgPShEItB5byAQidINOwckrTkD35AVlZ8KFKzflLZhgKh+N1lLHKSZt3qykolOgghWoBUUDyooLsk5yyBpIQkK2dV1nsgoNDJv6jxcKIUaO6J3FpAkhCFE7IkRyHtgSb3HJqPUVHQxQAJNiC0WBgMS6ChFEIeAwxdBus6dwXWAZ5naDLoaGyJyLGNIXQxxZL7lxsUMDGUC6wU2GRDRiRaY7P8KMVYlVajN2vwdT5MlADNMihlMAYge+r44Pqb3Rd4QJYiJVgofQN1nZubu8twYqA2E5A2ew+VQCA9gVogeI36jd3nBZmVaC8M1Cdl3X9caPHL4wiNVquxrO+hWPSzZKA2mH6/7bEZSoIfIzmkI/AG3twVHwaybskTWE2Y9WsEKPKoYGB1HdomyWJVDcICHjpQHyqcVbaMLhDbwwbWJCNcAjtyPHKMSmdU2SGv0uE6vUpLxBGmOK1BNTUzpzEpdEzO18KwPkcArOhCUKR0wV5PemS2FFINSV8L+A4FcOSNYj4qTAz6eE6EWMiqQGM+lt3ZMjBr8sbO7MI4wQ1Yek7w2MCvFTBRi6DZTaFlVASKSnQ45hUDq816U3NopSvODcgvHNPiGK7dquQ+idJKMZznBAkTIYkakuQPtMZ4gU/JqQRCayCMOI7Ho/ojGZmCdtmZzHnRmWK8ihcgwwFqmw9DXQEytJdGIAT/Q80O5YsWbcZb7peQuFLIioE1BN757OwuIkDlBMWNe4pqLOjZ/vVCXbp0iozFZNjWU0yE3QxabfSKLfevh1SswsVGX5cwRlOB1lFo4NAB4II6s0nrJCCpsxgsgMQJ4CS1hMFIufSGtUmpWBIgPRCzB7MtUJ9cMD0x05rUYEe6kziulIYvtIVHOByky+H0JujTrDOz9VKKZPDPpPNmzzDjlqnlOULJVHEGnudk5Jn5qURitmdaQUoupjXpTITBZCYMmFaDa7QaLUYnzc5LTElF9elLTXROZwiLkTPaAU4Vpmd7/PkFZY6MDCqdL9cJpIXDkxMFfRJhg4RNk5Fpz8yayfK5Vj1wZjtKHVbz1ByjRpNcosl3IzxRp02IjVehTER9UEwI1QOG6gFTqkEfh3dVQ7yKCmZBgqZn74tXpaHbzMYy/nhURiidIHqjPm2nJZgwnWNh6zIUA7mMphJ85XrI+3mKN2VS+b45JWWSOV2algNoN1GY4hJ8s71uq5BVmlqYRnYLgg4nMDwUByOuNweT50vT/0WrtuRj3csbswWvI4Qjy4ks7XLV2aGASijQQDKcTKE2LsA6hHl2YkFgk5m0EMDgdOlIC6m1mM1YSl72ui5t95pBjXIHBO/veXWdl85b4dtHLHowLPhEoF9Hh5S9Z8lKPHLHtagTr+4b98CjG05tSchpegxuvgL+ePihwe6w8sWLL7/xp1Hv7MqL7MUcv/NkwbUv2idM+sPbZN9zjz/ae9W5UQ2Ft/u/fu7D8D9HN52cRP3spQtX7l4qWz934sRK/zG8dPDoz+dXNTfGg4bCJ6zv7durHsPkVvseWXjQNzx1eNbyhIaXZx4/N/X3jkODApMcdxLfH5Ax8kdxae8v2h9NokBgHyfsTNp7YWNp7ZSLvnlL+74SHVGaUxT+yavS7wx7mAfmv/vsNrlpaO3osMGrs/e8NGSo4wQtPC6eSa9deUX7gX7XrjeFa9vv3Li7/KztVPOG/dZdi66/8Unf84sHJk0c8NSaMabD0RGHf7OkSFX79FL7czcWHInmi/RRJSVN++g+U46BPm/3K95ZTTzVMvfGXXnua6OAlhuWV5lc+dcJVPO0GUUvbl354BOvFG7TbT4w6+YyelX75qKTfVb0f733ZkNu5tjlpuNNl3U7f77XnD0yOjkhbUHLkEuzr07K3jb1zIbPT4JlGk2tJnXe7Sc3VIxvqR7ZeKz4415eLzTpl56kW/qezV33TlXk9o03BykQRYTl5fr2JSC8vk9G16vu+2F0MaruO1mZYbqJAHQxoMsQLYV4m4MEzD8ib7TChNSKkCPTNseZKZfZRU9mvjHdlwRgIbQZnVnflOMBwS17kVXKaeqK4iC7KkbfxYiPFasr1Qqp6mm82iqNEVWAKQd+USXKLOu/32nFiR7OOL6JyT/w3u/Me9vDIn9gvv955ltHBplFoPWz/3Ji8W+48u9j/UaT+dux/qh/wvrN/5usv0ZntJj/D2m/0fy9037ciRtII0np9VpCB3C906yFBifhclEWQkdQ8O/S/u9OJ0mjntTi345Ozv0qnVyaaDuFR87/9HaU/XXTT1sZI7pa5kVOwcadnzz+Mf9rVifvivngkYc9HfHpZa1HIwaNaL948/qFHSlhU/KcEVM269YcWXHrcnH/i7JcLo99uukzqbKD8B/gOoo7WrY+XRm16N09H4zJ+XCfvKplY+9qVxt5Pqv3MPt7zgLKP3vF+QEftUwh2oYMPRO1/yC19fkXJ0zfWL0eNh1d9PAXi8PC2uvKG9Oi7g4YmjZwRPrC9oO937pw68eTzcbV6pqbbX2siTGuNb/sdez4nfJFV6t0mz6aPOCUtSpjjJVeG72pz+kBD4/hE20Tdu/eUZ3e7/TEhZbGitj9RWtMF3716ZIrfU0n/nJixK0Zaw306Zf71bqHrLy1e+Y0aVD4hOnjr3cMH9149sgC3VXiWLRvW9v+5/w3Bte1hvfud+zQnZtnxtsLwNK2W7aM5ysnZR4etnRYxvS8hybdemZWQWnJ0YL6m/tObDn0WWN+/SXviuc8g3ZEdBLCM2N/Egl6hYX9DcY7sLU=
File diff suppressed because one or more lines are too long
+1 -10
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Agents
## What is an agent?
@@ -112,7 +103,7 @@ from langgraph.prebuilt import create_react_agent
# highlight-next-line
def prompt(state: AgentState, config: RunnableConfig) -> list[AnyMessage]: # (1)!
user_name = config["configurable"].get("user_name")
user_name = config.get("configurable", {}).get("user_name")
system_msg = f"You are a helpful assistant. Address the user as {user_name}."
return [{"role": "system", "content": system_msg}] + state["messages"]
+66 -15
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Context
Agents often require more than a list of messages to function effectively. They need **context**.
@@ -83,7 +74,7 @@ agent.invoke({
For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions). For more, see the [Memory guide](./memory.md).
## Customizing Prompts with Context { #prompts }
## Customizing Prompts with Context
Prompts define how the agent behaves. To incorporate runtime context, you can dynamically generate prompts based on the agent's state or config.
@@ -107,7 +98,7 @@ Common use cases:
config: RunnableConfig,
) -> list[AnyMessage]:
# highlight-next-line
user_name = config["configurable"].get("user_name")
user_name = config.get("configurable", {}).get("user_name")
system_msg = f"You are a helpful assistant. User's name is {user_name}"
return [{"role": "system", "content": system_msg}] + state["messages"]
@@ -162,7 +153,7 @@ Common use cases:
})
```
## Accessing Context in Tools { #tools }
## Tools
Tools can access context through special parameter **annotations**.
@@ -183,7 +174,7 @@ Tools can access context through special parameter **annotations**.
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = config["configurable"].get("user_id")
user_id = config.get("configurable", {}).get("user_id")
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent(
@@ -231,6 +222,66 @@ Tools can access context through special parameter **annotations**.
})
```
### Update Context from Tools
Tools can update agent's context (state and long-term memory) during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts. See [Memory](./memory.md#read-short-term) guide for more information.
## Update context from tools
Tools can modify the agent's state during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts.
```python
from typing import Annotated
from langchain_core.tools import InjectedToolCallId
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import InjectedState
from langgraph.types import Command
class CustomState(AgentState):
# highlight-next-line
user_name: str
def get_user_info(
# highlight-next-line
tool_call_id: Annotated[str, InjectedToolCallId],
# highlight-next-line
config: RunnableConfig
) -> Command:
"""Look up user info."""
# highlight-next-line
user_id = config.get("configurable", {}).get("user_id")
name = "John Smith" if user_id == "user_123" else "Unknown user"
return Command(update={
# highlight-next-line
"user_name": name,
# update the message history
# highlight-next-line
"messages": [
ToolMessage(
"Successfully looked up user information",
# highlight-next-line
tool_call_id=tool_call_id
)
]
})
def greet(
# highlight-next-line
state: Annotated[CustomState, InjectedState]
) -> str:
"""Use this to greet the user once you found their info."""
user_name = state["user_name"]
return f"Hello {user_name}!"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info, greet],
# highlight-next-line
state_schema=CustomState
)
agent.invoke(
{"messages": [{"role": "user", "content": "greet the user"}]},
# highlight-next-line
config={"configurable": {"user_id": "user_123"}}
)
```
For more details, see [how to update state from tools](../how-tos/update-state-from-tools.ipynb).
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Deployment
To deploy your LangGraph agent, create and configure a LangGraph app. This setup supports both local development and production deployments.
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Evals
To evaluate your agent's performance you can use `LangSmith` [evaluations](https://docs.smith.langchain.com/evaluation). You would need to first define an evaluator function to judge the results from an agent, such as final outputs or trajectory. Depending on your evaluation technique, this may or may not involve a reference output:
+1 -12
View File
@@ -1,17 +1,6 @@
---
search:
boost: 2
tags:
- human-in-the-loop
- hil
- agent
hide:
- tags
---
# Human-in-the-loop
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [Human-In-the-Loop (HIL)](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [human-in-the-loop](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received.
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# MCP Integration
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that standardizes how applications provide tools and context to language models. LangGraph agents can use tools defined on MCP servers through the `langchain-mcp-adapters` library.
+7 -168
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Memory
LangGraph supports two types of memory essential for building conversational agents:
@@ -92,26 +83,15 @@ When the agent is invoked the second time with the same `thread_id`, the origina
If you're using [LangGraph Platform](./deployment.md), during deployment your checkpointer will be automatically configured to use a production-ready database.
### Manage message history
Long conversations can exceed the LLM's context window. Common solutions are:
* [Summarization](#summarize-message-history): Maintain a running summary of the conversation
* [Trimming](#trim-message-history): Remove first or last N messages in the history
This allows the agent to keep track of the conversation without exceeding the LLM's context window.
To manage message history, specify `pre_model_hook` — a function ([node](../concepts/low_level.md#nodes)) that will always run before calling the language model.
#### Summarize message history
### Message history summarization
<figure markdown="1">
![image](./assets/summary.png){: style="max-height:400px"}
<figcaption>Long conversations can exceed the LLM's context window. A common solution is to maintain a running summary of the conversation. This allows the agent to keep track of the conversation without exceeding the LLM's context window.
<figcaption>Message history can grow quickly and exceed the LLM's context window. A common solution is to maintain a running summary of the conversation. This allows the agent to keep track of the conversation without exceeding the LLM's context window.
</figcaption>
</figure>
To summarize message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with a prebuilt [`SummarizationNode`](https://langchain-ai.github.io/langmem/reference/short_term/#langmem.short_term.SummarizationNode):
Long conversations can exceed the LLM's context window. To handle this, you can summarize older messages by specifying a [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent], such as the prebuilt [`SummarizationNode`](https://langchain-ai.github.io/langmem/reference/short_term/#langmem.short_term.SummarizationNode):
```python
from langchain_anthropic import ChatAnthropic
@@ -158,144 +138,8 @@ agent = create_react_agent(
4. The `pre_model_hook` is set to the `SummarizationNode`. This node will summarize the message history before sending it to the LLM. The summarization node will automatically handle the summarization process and update the agent's state with the new summary. You can replace this with a custom implementation if you prefer. Please see the [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent] API reference for more details.
5. The `state_schema` is set to the `State` class, which is the custom state that contains an extra `context` key.
#### Trim message history
To trim message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with [`trim_messages`](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) function:
```python
# highlight-next-line
from langchain_core.messages.utils import (
# highlight-next-line
trim_messages,
# highlight-next-line
count_tokens_approximately
# highlight-next-line
)
from langgraph.prebuilt import create_react_agent
# This function will be called every time before the node that calls LLM
def pre_model_hook(state):
trimmed_messages = trim_messages(
state["messages"],
strategy="last",
token_counter=count_tokens_approximately,
max_tokens=384,
start_on="human",
end_on=("human", "tool"),
)
# highlight-next-line
return {"llm_input_messages": trimmed_messages}
checkpointer = InMemorySaver()
agent = create_react_agent(
model,
tools,
# highlight-next-line
pre_model_hook=pre_model_hook,
checkpointer=checkpointer,
)
```
To learn more about using `pre_model_hook` for managing message history, see this [how-to guide](../how-tos/create-react-agent-manage-message-history.ipynb)
### Read in tools { #read-short-term }
LangGraph allows agent to access its short-term memory (state) inside the tools.
```python
from typing import Annotated
from langgraph.prebuilt import InjectedState, create_react_agent
class CustomState(AgentState):
# highlight-next-line
user_id: str
def get_user_info(
# highlight-next-line
state: Annotated[CustomState, InjectedState]
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = state["user_id"]
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({
"messages": "look up user information",
# highlight-next-line
"user_id": "user_123"
})
```
See the [Context](./context.md#__tabbed_2_2) guide for more information.
### Write from tools { #write-short-term }
To modify the agent's short-term memory (state) during execution, you can return state updates directly from the tools. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts.
```python
from typing import Annotated
from langchain_core.tools import InjectedToolCallId
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import InjectedState, create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.types import Command
class CustomState(AgentState):
# highlight-next-line
user_name: str
def update_user_info(
tool_call_id: Annotated[str, InjectedToolCallId],
config: RunnableConfig
) -> Command:
"""Look up and update user info."""
user_id = config["configurable"].get("user_id")
name = "John Smith" if user_id == "user_123" else "Unknown user"
# highlight-next-line
return Command(update={
# highlight-next-line
"user_name": name,
# update the message history
"messages": [
ToolMessage(
"Successfully looked up user information",
tool_call_id=tool_call_id
)
]
})
def greet(
# highlight-next-line
state: Annotated[CustomState, InjectedState]
) -> str:
"""Use this to greet the user once you found their info."""
user_name = state["user_name"]
return f"Hello {user_name}!"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info, greet],
# highlight-next-line
state_schema=CustomState
)
agent.invoke(
{"messages": [{"role": "user", "content": "greet the user"}]},
# highlight-next-line
config={"configurable": {"user_id": "user_123"}}
)
```
For more details, see [how to update state from tools](../how-tos/update-state-from-tools.ipynb).
## Long-term memory
Use long-term memory to store user-specific or application-specific data across conversations. This is useful for applications like chatbots, where you want to remember user preferences or other information.
@@ -305,10 +149,9 @@ To use long-term memory, you need to:
1. [Configure a store](../how-tos/cross-thread-persistence.ipynb) to persist data across invocations.
2. Use the [`get_store`][langgraph.config.get_store] function to access the store from within tools or prompts.
### Read { #read-long-term }
### Reading
```python title="A tool the agent can use to look up user information"
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore
@@ -331,7 +174,7 @@ def get_user_info(config: RunnableConfig) -> str:
# Same as that provided to `create_react_agent`
# highlight-next-line
store = get_store() # (6)!
user_id = config["configurable"].get("user_id")
user_id = config.get("configurable", {}).get("user_id")
# highlight-next-line
user_info = store.get(("users",), user_id) # (7)!
return str(user_info.value) if user_info else "Unknown user"
@@ -360,7 +203,7 @@ agent.invoke(
7. The `get` method is used to retrieve data from the store. The first argument is the namespace, and the second argument is the key. This will return a `StoreValue` object, which contains the value and metadata about the value.
8. The `store` is passed to the agent. This enables the agent to access the store when running tools. You can also use the `get_store` function to access the store from anywhere in your code.
### Write { #write-long-term }
### Writing
```python title="Example of a tool that updates user information"
from typing_extensions import TypedDict
@@ -379,7 +222,7 @@ def save_user_info(user_info: UserInfo, config: RunnableConfig) -> str: # (3)!
# Same as that provided to `create_react_agent`
# highlight-next-line
store = get_store() # (4)!
user_id = config["configurable"].get("user_id")
user_id = config.get("configurable", {}).get("user_id")
# highlight-next-line
store.put(("users",), user_id, user_info) # (5)!
return "Successfully saved user info."
@@ -409,10 +252,6 @@ store.get(("users",), "user_123").value
5. The `put` method is used to store data in the store. The first argument is the namespace, and the second argument is the key. This will store the user information in the store.
6. The `user_id` is passed in the config. This is used to identify the user whose information is being updated.
### Semantic search
LangGraph also allows you to [search](https://langchain-ai.github.io/langgraph/how-tos/memory/semantic-search/#using-in-create-react-agent) for items in long-term memory by semantic similarity.
### Prebuilt memory tools
**LangMem** is a LangChain-maintained library that offers tools for managing long-term memories in your agent. See the [LangMem documentation](https://langchain-ai.github.io/langmem/) for usage examples.
+1 -77
View File
@@ -1,14 +1,3 @@
---
search:
boost: 2
tags:
- anthropic
- openai
- agent
hide:
- tags
---
# Models
This page describes how to configure the chat model used by an agent.
@@ -74,72 +63,7 @@ agent = create_react_agent(
The example above uses `ChatAnthropic`, which is already supported by `init_chat_model`. This pattern is shown to illustrate how to manually instantiate a model not available through init_chat_model.
## Disable streaming
To disable streaming of the individual LLM tokens, set `disable_streaming=True` when initializing the model:
=== "`init_chat_model`"
```python
from langchain.chat_models import init_chat_model
model = init_chat_model(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
disable_streaming=True
)
```
=== "`ChatModel`"
```python
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(
model="claude-3-7-sonnet-latest",
# highlight-next-line
disable_streaming=True
)
```
Refer to the [API reference](https://python.langchain.com/api_reference/core/language_models/langchain_core.language_models.chat_models.BaseChatModel.html#langchain_core.language_models.chat_models.BaseChatModel.disable_streaming) for more information on `disable_streaming`
## Adding model fallbacks
You can add a fallback to a different model or a different LLM provider using `model.with_fallbacks([...])`:
=== "`init_chat_model`"
```python
from langchain.chat_models import init_chat_model
model_with_fallbacks = (
init_chat_model("anthropic:claude-3-5-haiku-latest")
# highlight-next-line
.with_fallbacks([
init_chat_model("openai:gpt-4.1-mini"),
])
)
```
=== "`ChatModel`"
```python
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI
model_with_fallbacks = (
ChatAnthropic(model="claude-3-5-haiku-latest")
# highlight-next-line
.with_fallbacks([
ChatOpenAI(model="gpt-4.1-mini"),
])
)
```
See this [guide](https://python.langchain.com/docs/how_to/fallbacks/#fallback-to-better-model) for more information on model fallbacks.
## Additional resources
- [Model integration directory](https://python.langchain.com/docs/integrations/chat/)
- [Universal initialization with `init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/)
- [Universal initialization with `init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/)
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Multi-agent
A single agent might struggle if it needs to specialize in multiple domains or manage many tools. To tackle this, you can break your agent into smaller, independent agents and composing them into a [multi-agent system](../concepts/multi_agent.md).
-6
View File
@@ -1,11 +1,5 @@
---
title: Overview
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Agent development with LangGraph
-7
View File
@@ -1,10 +1,3 @@
---
tags:
- agent
hide:
- tags
---
# Community Agents
To share your project, simply open a Pull Request adding an entry for your package in our [packages.yml](https://github.com/langchain-ai/langgraph/blob/main/docs/_scripts/third_party_page/packages.yml) file.
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Running agents
-15
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Streaming
Streaming is key to building responsive applications. There are a few types of data youll want to stream:
@@ -212,12 +203,6 @@ You can specify multiple streaming modes by passing stream mode as a list: `stre
print("\n")
```
## Disable streaming
In some applications you might need to disable streaming of individual tokens for a given model. This is useful in [multi-agent](./multi-agent.md) systems to control which agents stream their output.
See the [Models](./models.md#disable-streaming) guide to learn how to disable streaming.
## Additional resources
* [Streaming in LangGraph](https://langchain-ai.github.io/langgraph/how-tos/streaming)
-16
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Tools
[Tools](https://python.langchain.com/docs/concepts/tools/) are a way to encapsulate a function and its input schema in a way that can be passed to a chat model that supports tool calling. This allows the model to request the execution of this function with specific inputs.
@@ -271,13 +262,6 @@ By default, the agent will catch all exceptions raised during tool calls and wil
See [API reference][langgraph.prebuilt.tool_node.ToolNode] for more information on different tool error handling options.
## Working with memory
LangGraph allows access to short-term and long-term memory from tools. See [Memory](./memory.md) guide for more information on:
* how to [read](./memory.md#read-short-term) from and [write](./memory.md#write-short-term) to **short-term** memory
* how to [read](./memory.md#read-long-term) from and [write](./memory.md#write-long-term) to **long-term** memory
## Prebuilt tools
LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
-9
View File
@@ -1,12 +1,3 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# UI
You can use a prebuilt chat UI for interacting with any LangGraph agent through the [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui). Using the [deployed version](https://agentchat.vercel.app) is the quickest way to get started, and allows you to interact with both local and deployed graphs.
+10 -10
View File
@@ -107,13 +107,13 @@ After installing and authorizing LangChain's `hosted-langserve` GitHub app, repo
All traffic from `LangGraph Platform` deployments created after January 6th 2025 will come through a NAT gateway.
This NAT gateway will have several static ip addresses depending on the region you are deploying in. Refer to the table below for the list of IP addresses to whitelist:
| US | EU |
|----------------|-----------------|
| 35.197.29.146 | 34.90.213.236 |
| 34.145.102.123 | 34.13.244.114 |
| 34.169.45.153 | 34.32.180.189 |
| 34.82.222.17 | 34.34.69.108 |
| 35.227.171.135 | 34.32.145.240 |
| 34.169.88.30 | 34.90.157.44 |
| 34.19.93.202 | 34.141.242.180 |
| 34.19.34.50 | 34.32.141.108 |
| US | EU |
|----------------|----------------|
| 35.197.29.146 | 34.13.192.67 |
| 34.145.102.123 | 34.147.105.64 |
| 34.169.45.153 | 34.90.22.166 |
| 34.82.222.17 | 34.147.36.213 |
| 35.227.171.135 | 34.32.137.113 |
| 34.169.88.30 | 34.91.238.184 |
| 34.19.93.202 | 35.204.101.241 |
| 34.19.34.50 | 35.204.48.32 |
@@ -17,58 +17,8 @@ Here's how to customize the included and excluded headers:
}
```
The `include` and `exclude` lists accept exact header names or patterns using `*` to match any number of characters. For your security, no other regex patterns are supported.
## Using within your graph
You can access the included headers in your graph using the `config` argument of any node.
```python
def my_node(state, config):
organization_id = config["configurable"].get("x-organization-id")
...
```
Or by fetching from context (useful in tools and or within other nested functions).
```python
from langgraph.config import get_config
def search_everything(query: str):
organization_id = get_config()["configurable"].get("x-organization-id")
...
```
You can even use this to dynamically compile the graph.
```python
# my_graph.py.
import contextlib
@contextlib.asynccontextmanager
async def generate_agent(config):
organization_id = config["configurable"].get("x-organization-id")
if organization_id == "org1":
graph = ...
yield graph
else:
graph = ...
yield graph
```
```json
{
"graphs": {"agent": "my_grph.py:generate_agent"}
}
```
For more examples on how to use runtime configuration, check out the [configuration how-to](../../how-tos/configuration.ipynb).
### Opt-out of configurable headers
If you'd like to opt-out of configurable headers, you can simply set a wildcard pattern in the `exclude` list:
```json
@@ -81,6 +31,4 @@ If you'd like to opt-out of configurable headers, you can simply set a wildcard
}
```
This will exclude all headers from being added to your run's configuration.
Note that exclusions take precedence over inclusions.
This will exclude all headers from being added to your run's configuration.
+14 -170
View File
@@ -207,6 +207,18 @@ Behind the scenes, `LoadExternalComponent` will fetch the JS and CSS for the UI
## How-to guides
### Show loading UI when components are loading
You can provide a fallback UI to be rendered when the components are loading.
```tsx
<LoadExternalComponent
stream={thread}
message={ui}
fallback={<div>Loading...</div>}
/>
```
### Provide custom components on the client side
If you already have the components loaded in your client application, you can provide a map of such components to be rendered directly without fetching the UI code from LangGraph Platform.
@@ -223,18 +235,6 @@ const clientComponents = {
/>;
```
### Show loading UI when components are loading
You can provide a fallback UI to be rendered when the components are loading.
```tsx
<LoadExternalComponent
stream={thread}
message={ui}
fallback={<div>Loading...</div>}
/>
```
### Customise the namespace of UI components.
By default `LoadExternalComponent` will use the `assistantId` from `useStream()` hook to fetch the code for UI components. You can customise this by providing a `namespace` prop to the `LoadExternalComponent` component.
@@ -316,9 +316,9 @@ const WeatherComponent = (props: { city: string }) => {
};
```
### Streaming UI messages from the server
### Streaming UI updates before the node execution is finished
You can stream UI messages before the node execution is finished by using the `onCustomEvent` callback of the `useStream()` hook. This is especially useful when updating the UI component as the LLM is generating the response.
You can stream UI updates before the node execution is finished by using the `onCustomEvent` callback of the `useStream()` hook.
```tsx
import { uiMessageReducer } from "@langchain/langgraph-sdk/react-ui";
@@ -335,162 +335,6 @@ const { thread, submit } = useStream({
});
```
Then you can pushing updates to the UI component by calling `ui.push()` / `push_ui_message()` with the same ID as the UI message you wish to update.
=== "Python"
```python
from typing import Annotated, Sequence, TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.graph.ui import AnyUIMessage, push_ui_message, ui_message_reducer
class AgentState(TypedDict): # noqa: D101
messages: Annotated[Sequence[BaseMessage], add_messages]
ui: Annotated[Sequence[AnyUIMessage], ui_message_reducer]
class CreateTextDocument(TypedDict):
"""Prepare a document heading for the user."""
title: str
async def writer_node(state: AgentState):
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
message: AIMessage = await model.bind_tools(
tools=[CreateTextDocument],
tool_choice={"type": "tool", "name": "CreateTextDocument"},
).ainvoke(state["messages"])
tool_call = next(
(x["args"] for x in message.tool_calls if x["name"] == "CreateTextDocument"),
None,
)
if tool_call:
ui_message = push_ui_message("writer", tool_call, message=message)
ui_message_id = ui_message["id"]
# We're already streaming the LLM response to the client through UI messages
# so we don't need to stream it again to the `messages` stream mode.
content_stream = model.with_config({"tags": ["nostream"]}).astream(
f"Create a document with the title: {tool_call['title']}"
)
content: AIMessageChunk | None = None
async for chunk in content_stream:
content = content + chunk if content else chunk
push_ui_message(
"writer",
{"content": content.text()},
id=ui_message_id,
message=message,
# Use `merge=rue` to merge props with the existing UI message
merge=True,
)
return {"messages": [message]}
```
=== "JS"
```tsx
import {
Annotation,
MessagesAnnotation,
type LangGraphRunnableConfig,
} from "@langchain/langgraph";
import { z } from "zod";
import { ChatAnthropic } from "@langchain/anthropic";
import {
typedUi,
uiMessageReducer,
} from "@langchain/langgraph-sdk/react-ui/server";
import type { AIMessageChunk } from "@langchain/core/messages";
import type ComponentMap from "./ui";
const AgentState = Annotation.Root({
...MessagesAnnotation.spec,
ui: Annotation({ reducer: uiMessageReducer, default: () => [] }),
});
async function writerNode(
state: typeof AgentState.State,
config: LangGraphRunnableConfig
): Promise<typeof AgentState.Update> {
const ui = typedUi<typeof ComponentMap>(config);
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-latest" });
const message = await model
.bindTools(
[
{
name: "create_text_document",
description: "Prepare a document heading for the user.",
schema: z.object({ title: z.string() }),
},
],
{ tool_choice: { type: "tool", name: "create_text_document" } }
)
.invoke(state.messages);
type ToolCall = { name: "create_text_document"; args: { title: string } };
const toolCall = message.tool_calls?.find(
(tool): tool is ToolCall => tool.name === "create_text_document"
);
if (toolCall) {
const { id, name } = ui.push(
{ name: "writer", props: { title: toolCall.args.title } },
{ message }
);
const contentStream = await model
// We're already streaming the LLM response to the client through UI messages
// so we don't need to stream it again to the `messages` stream mode.
.withConfig({ tags: ["nostream"] })
.stream(`Create a short poem with the topic: ${message.text}`);
let content: AIMessageChunk | undefined;
for await (const chunk of contentStream) {
content = content?.concat(chunk) ?? chunk;
ui.push(
{ id, name, props: { content: content?.text } },
// Use `merge: true` to merge props with the existing UI message
{ message, merge: true }
);
}
}
return { messages: [message] };
}
```
=== "`ui.tsx`"
```tsx
function WriterComponent(props: { title: string; content?: string }) {
return (
<article>
<h2>{props.title}</h2>
<p style={{ whiteSpace: "pre-wrap" }}>{props.content}</p>
</article>
);
}
export default {
weather: WriterComponent,
};
```
### Remove UI messages from state
Similar to how messages can be removed from the state by appending a RemoveMessage you can remove an UI message from the state by calling `remove_ui_message` / `ui.delete` with the ID of the UI message.
@@ -1,4 +1,4 @@
# How to use the interrupt option
# Interrupt
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
@@ -1,5 +1,4 @@
# How to use the Rollback option
# Rollback
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Agent architectures
Many LLM applications implement a particular control flow of steps before and / or after LLM calls. As an example, [RAG](https://github.com/langchain-ai/rag-from-scratch) performs retrieval of documents relevant to a user question, and passes those documents to an LLM in order to ground the model's response in the provided document context.
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Application Structure
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Assistants
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Authentication & Access Control
LangGraph Platform provides a flexible authentication and authorization system that can integrate with most authentication schemes.
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Breakpoints
Breakpoints pause graph execution at specific points and enable stepping through execution step by step. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step. Breakpoints can also be used to enable [**human-in-the-loop**](./human_in_the_loop.md) workflows, though we recommend using the [`interrupt` function](./human_in_the_loop.md#interrupt) for this purpose.
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Bring Your Own Cloud (BYOC)
!!! note Prerequisites
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Deployment Options
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Double Texting
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Durable Execution
**Durable execution** is a technique in which a process or workflow saves its progress at key points, allowing it to pause and later resume exactly where it left off. This is particularly useful in scenarios that require [human-in-the-loop](./human_in_the_loop.md), where users can inspect, validate, or modify the process before continuing, and in long-running tasks that might encounter interruptions or errors (e.g., calls to an LLM timing out). By preserving completed work, durable execution enables a process to resume without reprocessing previous steps -- even after a significant delay (e.g., a week later).
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# FAQ
Common questions and their answers!
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Functional API
## Overview
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Why LangGraph?
## LLM applications
-26
View File
@@ -1,13 +1,3 @@
---
search:
boost: 2
tags:
- human-in-the-loop
- hil
hide:
- tags
---
# Human-in-the-loop
!!! tip "This guide uses the new `interrupt` function."
@@ -450,22 +440,6 @@ Upon **resuming** the graph, the counter will be incremented a second time, resu
The value of counter is: 2
```
### Resuming multiple interrupts with one invocation
If you have multiple interrupts in the task queue, you can use `Command.resume` with a dictionary mapping
of interrupt ids to resume values to resume multiple interrupts with a single `invoke` / `stream` call.
For example, once your graph has been interrupted (multiple times, theoretically) and is stalled:
```python
resume_map = {
i.interrupt_id: f"human input for prompt {i.value}"
for i in parent.get_state(thread_config).interrupts
}
parent_graph.invoke(Command(resume=resume_map), config=thread_config)
```
## Common Pitfalls
### Side-effects
-2
View File
@@ -1,8 +1,6 @@
---
title: Concepts
description: Conceptual Guide for LangGraph
search:
boost: 0.5
---
# Conceptual Guide
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph CLI
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Cloud SaaS (Beta)
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy to Cloud SaaS](../cloud/deployment/cloud.md).
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Control Plane
The term "control plane" is used broadly to refer to the Control Plane UI where users create and update [LangGraph Servers](./langgraph_server.md) (deployments) and the Control Plane APIs that support the UI experience.
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Data Plane
The term "data plane" is used broadly to refer to [LangGraph Servers](./langgraph_server.md) (deployments), the corresponding infrastructure for each server, and the "listener" application that continuously polls for updates from the [LangGraph Control Plane](./langgraph_control_plane.md).
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Self-Hosted Control Plane (Beta)
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy the Self-Hosted Control Plane](../cloud/deployment/self_hosted_control_plane.md).
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Self-Hosted Data Plane (Beta)
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy the Self-Hosted Data Plane](../cloud/deployment/self_hosted_data_plane.md).
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Server
!!! info "Prerequisites"
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Standalone Container
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy a Standalone Container](../cloud/deployment/standalone_container.md).
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Studio
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Glossary
## Graphs
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Memory
## What is Memory?
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Multi-agent Systems
An [agent](./agentic_concepts.md#agent-architectures) is _a system that uses an LLM to decide the control flow of an application_. As you develop these systems, they might grow more complex over time, making them harder to manage and scale. For example, you might run into the following problems:
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Persistence
LangGraph has a built-in persistence layer, implemented through checkpointers. When you compile graph with a checkpointer, the checkpointer saves a `checkpoint` of the graph state at every super-step. Those checkpoints are saved to a `thread`, which can be accessed after graph execution. Because `threads` allow access to graph's state after execution, several powerful capabilities including human-in-the-loop, memory, time travel, and fault-tolerance are all possible. See [this how-to guide](../how-tos/persistence.ipynb) for an end-to-end example on how to add and use checkpointers with your graph. Below, we'll discuss each of these concepts in more detail.
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Platform Plans
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Platform Architecture
![](img/langgraph_platform_deployment_architecture.png)
+1 -6
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph's Runtime (Pregel)
[Pregel][langgraph.pregel.Pregel] implements LangGraph's runtime, managing the execution of LangGraph applications.
@@ -27,7 +22,7 @@ Repeat until no **actors** are selected for execution, or a maximum number of st
## Actors
An **actor** is a `PregelNode`. It subscribes to channels, reads data from them, and writes data to them. It can be thought of as an **actor** in the Pregel algorithm. `PregelNodes` implement LangChain's Runnable interface.
An **actor** is a [PregelNode][langgraph.pregel.read.PregelNode]. It subscribes to channels, reads data from them, and writes data to them. It can be thought of as an **actor** in the Pregel algorithm. [PregelNodes][langgraph.pregel.read.PregelNode] implement LangChain's Runnable interface.
## Channels
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph Platform: Scalability & Resilience
LangGraph Platform is designed to scale horizontally with your workload. Each instance of the service is stateless, and keeps no resources in memory. The service is designed to gracefully handle new instances being added or removed, including hard shutdown cases.
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# LangGraph SDK
!!! info "Prerequisites"
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Self-Hosted
!!! note Prerequisites
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Streaming
Building a responsive app for end-users? Real-time updates are key to keeping users engaged as your app progresses.
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Template Applications
Templates are open source reference applications designed to help you get started quickly when building with LangGraph. They provide working examples of common agentic workflows that can be customized to your needs.
-5
View File
@@ -1,8 +1,3 @@
---
search:
boost: 2
---
# Time Travel ⏱️
!!! note "Prerequisites"
@@ -0,0 +1,376 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add human-in-the-loop processes to the prebuilt ReAct agent\n",
"\n",
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Prerequisites</p>\n",
" <p>\n",
" This guide assumes familiarity with the following:\n",
" <ul>\n",
" <li> \n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/\">\n",
" Human-in-the-loop\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/\">\n",
" Agent Architectures\n",
" </a> \n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/chat_models/\">\n",
" Chat Models\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/tools/\">\n",
" Tools\n",
" </a>\n",
" </li> \n",
" </ul>\n",
" </p>\n",
"</div> \n",
"\n",
"This guide will show how to add human-in-the-loop processes to the prebuilt ReAct agent. Please see [this tutorial](../create-react-agent) for how to get started with the prebuilt ReAct agent\n",
"\n",
"You can add a a breakpoint before tools are called by passing `interrupt_before=[\"tools\"]` to `create_react_agent`. Note that you need to be using a checkpointer for this to work."
]
},
{
"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": 2,
"id": "a213e11a-5c62-4ddb-a707-490d91add383",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-openai"
]
},
{
"cell_type": "code",
"execution_count": 3,
"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": "d4c5c054",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 4,
"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",
"from typing import Literal\n",
"\n",
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(location: str):\n",
" \"\"\"Use this to get weather information from a given location.\"\"\"\n",
" if location.lower() in [\"nyc\", \"new york\"]:\n",
" return \"It might be cloudy in nyc\"\n",
" elif location.lower() in [\"sf\", \"san francisco\"]:\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" raise AssertionError(\"Unknown Location\")\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We need a checkpointer to enable human-in-the-loop patterns\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(\n",
" model, tools=tools, interrupt_before=[\"tools\"], checkpointer=memory\n",
")"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
"metadata": {},
"outputs": [],
"source": [
"def print_stream(stream):\n",
" \"\"\"A utility to pretty print the 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": "code",
"execution_count": 8,
"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, CA?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_YjOKDkgMGgUZUpKIasYk1AdK)\n",
" Call ID: call_YjOKDkgMGgUZUpKIasYk1AdK\n",
" Args:\n",
" location: SF, CA\n"
]
}
],
"source": [
"from langchain_core.messages import HumanMessage\n",
"\n",
"config = {\"configurable\": {\"thread_id\": \"42\"}}\n",
"inputs = {\"messages\": [(\"user\", \"what is the weather in SF, CA?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "ca40a719",
"metadata": {},
"source": [
"We can verify that our graph stopped at the right place:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Next step: ('tools',)\n"
]
}
],
"source": [
"snapshot = graph.get_state(config)\n",
"print(\"Next step: \", snapshot.next)"
]
},
{
"cell_type": "markdown",
"id": "7de6ca78",
"metadata": {},
"source": [
"Now we can either approve or edit the tool call before proceeding to the next node. If we wanted to approve the tool call, we would simply continue streaming the graph with `None` input. If we wanted to edit the tool call we need to update the state to have the correct tool call, and then after the update has been applied we can continue.\n",
"\n",
"We can try resuming and we will see an error arise:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "740bbaeb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_YjOKDkgMGgUZUpKIasYk1AdK)\n",
" Call ID: call_YjOKDkgMGgUZUpKIasYk1AdK\n",
" Args:\n",
" location: SF, CA\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"Error: AssertionError('Unknown Location')\n",
" Please fix your mistakes.\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_CLu9ofeBhtWF2oheBspxXkfE)\n",
" Call ID: call_CLu9ofeBhtWF2oheBspxXkfE\n",
" Args:\n",
" location: San Francisco, CA\n"
]
}
],
"source": [
"print_stream(graph.stream(None, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "c1cf5950",
"metadata": {},
"source": [
"This error arose because our tool argument of \"San Francisco, CA\" is not a location our tool recognizes.\n",
"\n",
"Let's show how we would edit the tool call to search for \"San Francisco\" instead of \"San Francisco, CA\" - since our tool as written treats \"San Francisco, CA\" as an unknown location. We will update the state and then resume streaming the graph and should see no errors arise:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "1c81ed9f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'configurable': {'thread_id': '42',\n",
" 'checkpoint_ns': '',\n",
" 'checkpoint_id': '1ef801d1-5b93-6bb9-8004-a088af1f9cec'}}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"state = graph.get_state(config)\n",
"\n",
"last_message = state.values[\"messages\"][-1]\n",
"last_message.tool_calls[0][\"args\"] = {\"location\": \"San Francisco\"}\n",
"\n",
"graph.update_state(config, {\"messages\": [last_message]})"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "83148e08-63e8-49e5-a08b-02dc907bed1d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_CLu9ofeBhtWF2oheBspxXkfE)\n",
" Call ID: call_CLu9ofeBhtWF2oheBspxXkfE\n",
" Args:\n",
" location: San Francisco\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": [
"print_stream(graph.stream(None, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "8202a5f9",
"metadata": {},
"source": [
"Fantastic! Our graph updated properly to query the weather in San Francisco and got the correct \"It's always sunny in sf\" response from the tool, and then responded to the user accordingly."
]
}
],
"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
}
@@ -17,8 +17,8 @@
"\n",
"Message history can grow quickly and exceed LLM context window size, whether you're building chatbots with many conversation turns or agentic systems with numerous tool calls. There are several strategies for managing the message history:\n",
"\n",
"* [message trimming](#keep-the-original-message-history-unmodified) remove first or last N messages in the history\n",
"* [summarization](#summarizing-message-history) summarize earlier messages in the history and replace them with a summary\n",
"* [message trimming](#keep-the-original-message-history-unmodified) - remove first or last N messages in the history\n",
"* [summarization](#summarizing-message-history) - summarize earlier messages in the history and replace them with a summary\n",
"* custom strategies (e.g., message filtering, etc.)\n",
"\n",
"To manage message history in `create_react_agent`, you need to define a `pre_model_hook` function or [runnable](https://python.langchain.com/docs/concepts/runnables/) that takes graph state an returns a state update:\n",
@@ -691,8 +691,7 @@
"\n",
"checkpointer = InMemorySaver()\n",
"graph = create_react_agent(\n",
" # limit the output size to ensure consistent behavior\n",
" model.bind(max_tokens=256),\n",
" model,\n",
" tools,\n",
" # highlight-next-line\n",
" pre_model_hook=summarization_node,\n",
@@ -0,0 +1,291 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add thread-level memory to a ReAct Agent\n",
"\n",
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Prerequisites</p>\n",
" <p>\n",
" This guide assumes familiarity with the following:\n",
" <ul>\n",
" <li> \n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/\">\n",
" LangGraph Persistence\n",
" </a>\n",
" </li>\n",
" <li> \n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/#checkpointer-interface\">\n",
" Checkpointer interface\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/\">\n",
" Agent Architectures\n",
" </a> \n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/chat_models/\">\n",
" Chat Models\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/tools/\">\n",
" Tools\n",
" </a>\n",
" </li>\n",
" </ul>\n",
" </p>\n",
"</div> \n",
"\n",
"This guide will show how to add memory to the prebuilt ReAct agent. Please see [this tutorial](../create-react-agent) for how to get started with the prebuilt ReAct agent\n",
"\n",
"We can add memory to the agent, by passing a [checkpointer](https://langchain-ai.github.io/langgraph/reference/checkpoints/) to the [create_react_agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent) function."
]
},
{
"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": 2,
"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": "87a00ce9",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"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 langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(location: str) -> str:\n",
" \"\"\"Use this to get weather information.\"\"\"\n",
" if any([city in location.lower() for city in [\"nyc\", \"new york city\"]]):\n",
" return \"It might be cloudy in nyc\"\n",
" elif any([city in location.lower() for city in [\"sf\", \"san francisco\"]]):\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" return f\"I am not sure what the weather is in {location}\"\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We can add \"chat memory\" to the graph with LangGraph's checkpointer\n",
"# to retain the chat context between interactions\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(model, tools=tools, checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n",
"\n",
"Let's interact with it multiple times to show that it can remember"
]
},
{
"cell_type": "code",
"execution_count": 5,
"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": "code",
"execution_count": 6,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in NYC?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_xM1suIq26KXvRFqJIvLVGfqG)\n",
" Call ID: call_xM1suIq26KXvRFqJIvLVGfqG\n",
" Args:\n",
" city: nyc\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It might be cloudy in nyc\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"The weather in NYC might be cloudy.\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "838a043f-90ad-4e69-9d1d-6e22db2c346c",
"metadata": {},
"source": [
"Notice that when we pass the same thread ID, the chat history is preserved."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "187479f9-32fa-4611-9487-cf816ba2e147",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's it known for?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"New York City (NYC) is known for a variety of iconic landmarks, cultural institutions, and vibrant neighborhoods. Some of the most notable aspects include:\n",
"\n",
"1. **Statue of Liberty**: A symbol of freedom and democracy.\n",
"2. **Times Square**: Known for its bright lights, Broadway theaters, and bustling atmosphere.\n",
"3. **Central Park**: A large urban park offering a green oasis in the middle of the city.\n",
"4. **Empire State Building**: An iconic skyscraper with an observation deck offering panoramic views of the city.\n",
"5. **Broadway**: Famous for its world-class theater productions.\n",
"6. **Wall Street**: The financial hub of the United States.\n",
"7. **Museums**: Including the Metropolitan Museum of Art, the Museum of Modern Art (MoMA), and the American Museum of Natural History.\n",
"8. **Diverse Cuisine**: A melting pot of culinary experiences from around the world.\n",
"9. **Cultural Diversity**: A rich tapestry of cultures, languages, and traditions.\n",
"10. **Fashion**: A global fashion capital, home to New York Fashion Week.\n",
"\n",
"These are just a few highlights of what makes NYC a unique and vibrant city.\n"
]
}
],
"source": [
"inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c461eb47-b4f9-406f-8923-c68db7c5687f",
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}
@@ -0,0 +1,287 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to return structured output from the prebuilt ReAct agent\n",
"\n",
"!!! info \"Prerequisites\"\n",
" This guide assumes familiarity with the following:\n",
" \n",
" - [Agent Architectures](../../concepts/agentic_concepts/)\n",
" - [Chat Models](https://python.langchain.com/docs/concepts/chat_models/)\n",
" - [Tools](https://python.langchain.com/docs/concepts/tools/)\n",
" - [Structured Output](https://python.langchain.com/docs/concepts/structured_outputs/)\n",
"\n",
"To return structured output from the prebuilt ReAct agent you can provide a `response_format` parameter with the desired output schema to [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent]:\n",
"\n",
"```python\n",
"class ResponseFormat(BaseModel):\n",
" \"\"\"Respond to the user in this format.\"\"\"\n",
" my_special_output: str\n",
"\n",
"\n",
"graph = create_react_agent(\n",
" model,\n",
" tools=tools,\n",
" # specify the schema for the structured output using `response_format` parameter\n",
" response_format=ResponseFormat\n",
")\n",
"```\n",
"\n",
"Prebuilt ReAct makes an additional LLM call at the end of the ReAct loop to produce a structured output response. Please see [this guide](../react-agent-structured-output) to learn about other strategies for returning structured outputs from a tool-calling agent."
]
},
{
"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": null,
"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": "87a00ce9",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"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",
"# 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",
"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",
"# Define the structured output schema\n",
"\n",
"from pydantic import BaseModel, Field\n",
"\n",
"\n",
"class WeatherResponse(BaseModel):\n",
" \"\"\"Respond to the user in this format.\"\"\"\n",
"\n",
" conditions: str = Field(description=\"Weather conditions\")\n",
"\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(\n",
" model,\n",
" tools=tools,\n",
" # specify the schema for the structured output using `response_format` parameter\n",
" response_format=WeatherResponse,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n",
"\n",
"Let's now test our agent:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [],
"source": [
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"response = graph.invoke(inputs)"
]
},
{
"cell_type": "markdown",
"id": "50e273a0-fbdb-4eee-89ca-580fbfb52daf",
"metadata": {},
"source": [
"You can see that the agent output contains a `structured_response` key with the structured output conforming to the specified `WeatherResponse` schema, in addition to the message history under `messages` key."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "300748d4-0ed2-470d-8dbc-7c14231e73b8",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"WeatherResponse(conditions='cloudy')"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"response[\"structured_response\"]"
]
},
{
"cell_type": "markdown",
"id": "bd9e3487-2cec-44cf-9472-0a51eebeddff",
"metadata": {},
"source": [
"### Customizing prompt"
]
},
{
"cell_type": "markdown",
"id": "a608548d-77fc-4d7a-845c-32ae9ec0489a",
"metadata": {},
"source": [
"You might need to further customize the second LLM call for the structured output generation and provide a system prompt. To do so, you can pass a tuple (prompt, schema):"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "d1386f99-ffd1-4b36-86ec-cabb3357d929",
"metadata": {},
"outputs": [],
"source": [
"graph = create_react_agent(\n",
" model,\n",
" tools=tools,\n",
" # specify both the system prompt and the schema for the structured output\n",
" response_format=(\"Always return capitalized weather conditions\", WeatherResponse),\n",
")\n",
"\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"response = graph.invoke(inputs)"
]
},
{
"cell_type": "markdown",
"id": "91f34991-b406-4fd2-a776-4dd03e3dc3dd",
"metadata": {},
"source": [
"You can verify that the structured response now contains a capitalized value:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ba43a67f-127c-45e7-982c-a8210d97a3ed",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"WeatherResponse(conditions='Cloudy')"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"response[\"structured_response\"]"
]
}
],
"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
}
@@ -0,0 +1,231 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add a custom system prompt to the prebuilt ReAct agent\n",
"\n",
"\n",
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Prerequisites</p>\n",
" <p>\n",
" This guide assumes familiarity with the following:\n",
" <ul>\n",
" <li> \n",
" <a href=\"https://python.langchain.com/docs/concepts/messages/#systemmessage\">\n",
" SystemMessage\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/\">\n",
" Agent Architectures\n",
" </a> \n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/chat_models/\">\n",
" Chat Models\n",
" </a>\n",
" </li>\n",
" <li>\n",
" <a href=\"https://python.langchain.com/docs/concepts/tools/\">\n",
" Tools\n",
" </a>\n",
" </li>\n",
" </ul>\n",
" </p>\n",
"</div> \n",
"\n",
"This tutorial will show how to add a custom system prompt to the [prebuilt ReAct agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent). Please see [this tutorial](../create-react-agent) for how to get started with the prebuilt ReAct agent\n",
"\n",
"You can add a custom system prompt by passing a string to the `prompt` param.\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": null,
"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": "715867c6",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"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",
"# We can add our system prompt here\n",
"\n",
"prompt = \"Respond in Italian\"\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(model, tools=tools, prompt=prompt)"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"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": "code",
"execution_count": 4,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in NYC?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_b02uzBRrIm2uciJa8zDXCDxT)\n",
" Call ID: call_b02uzBRrIm2uciJa8zDXCDxT\n",
" Args:\n",
" city: nyc\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It might be cloudy in nyc\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"A New York potrebbe essere nuvoloso.\n"
]
}
],
"source": [
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"\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
}
File diff suppressed because one or more lines are too long
+12 -6
View File
@@ -1,8 +1,6 @@
---
title: How-to Guides
description: How to accomplish common tasks in LangGraph
search:
boost: 0.5
---
# How-to Guides
@@ -151,13 +149,21 @@ See the below guide for how to integrate with other frameworks using the [Functi
- [How to integrate LangGraph (functional API) with AutoGen, CrewAI, and other frameworks](autogen-integration-functional.ipynb)
### Prebuilt Agent
### Prebuilt ReAct Agent
LangGraph comes with a [prebuilt][langgraph.prebuilt.chat_agent_executor.create_react_agent] implementation of a [tool calling agent](../concepts/agentic_concepts.md#tool-calling-agent). See [Agents](../agents/overview.md) guides for more information.
The LangGraph [prebuilt ReAct agent](../reference/prebuilt.md#langgraph.prebuilt.chat_agent_executor.create_react_agent) is pre-built implementation of a [tool calling agent](../concepts/agentic_concepts.md#tool-calling-agent).
!!! tip
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.
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.
These guides show how to use the prebuilt ReAct agent:
- [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)
- [How to return structured output from a ReAct agent](create-react-agent-structured-output.ipynb)
- [How to add semantic search for long-term memory to a ReAct agent](memory/semantic-search.ipynb#using-in-create-react-agent)
- [How to manage message history in a ReAct agent](create-react-agent-manage-message-history.ipynb)
Interested in further customizing the ReAct agent? This guide provides an
overview of its underlying implementation to help you customize for your own needs:
-39
View File
@@ -1,39 +0,0 @@
# Agents
::: langgraph.prebuilt.chat_agent_executor
options:
members:
- AgentState
- create_react_agent
::: langgraph.prebuilt.tool_node.ToolNode
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
inherited_members: false
members:
- inject_tool_args
::: langgraph.prebuilt.tool_node
options:
members:
- InjectedState
- InjectedStore
- tools_condition
::: langgraph.prebuilt.tool_validator.ValidationNode
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
inherited_members: false
members: false
::: langgraph.prebuilt.interrupt
options:
members:
- HumanInterruptConfig
- ActionRequest
- HumanInterrupt
- HumanResponse
+1 -7
View File
@@ -25,11 +25,5 @@
::: langgraph.checkpoint.sqlite.aio
::: langgraph.checkpoint.postgres
options:
members:
- PostgresSaver
::: langgraph.checkpoint.postgres.aio
options:
members:
- AsyncPostgresSaver
::: langgraph.checkpoint.postgres.aio
+6 -65
View File
@@ -1,75 +1,16 @@
# Graph Definitions
::: langgraph.graph.state.StateGraph
::: langgraph.graph.graph
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
members:
- add_node
- add_edge
- add_conditional_edges
- add_sequence
- compile
- Graph
- CompiledGraph
::: langgraph.graph.state.CompiledStateGraph
::: langgraph.graph.state
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
members:
- stream
- astream
- invoke
- ainvoke
- get_state
- aget_state
- get_state_history
- aget_state_history
- update_state
- aupdate_state
- bulk_update_state
- abulk_update_state
- get_graph
- aget_graph
- get_subgraphs
- aget_subgraphs
- with_config
::: langgraph.graph.graph.Graph
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
members:
- add_node
- add_edge
- add_conditional_edges
- compile
::: langgraph.graph.graph.CompiledGraph
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
members:
- stream
- astream
- invoke
- ainvoke
- get_state
- aget_state
- get_state_history
- aget_state_history
- update_state
- aupdate_state
- bulk_update_state
- abulk_update_state
- get_graph
- aget_graph
- get_subgraphs
- aget_subgraphs
- with_config
- StateGraph
- CompiledStateGraph
::: langgraph.graph.message
options:
-2
View File
@@ -1,8 +1,6 @@
---
title: Reference
description: API reference for LangGraph
search:
boost: 0.5
---
<style>
-21
View File
@@ -1,21 +0,0 @@
# LangChain Model Context Protocol (MCP) Adapters
::: langchain_mcp_adapters.client
options:
members:
- MultiServerMCPClient
::: langchain_mcp_adapters.tools
options:
members:
- load_mcp_tools
::: langchain_mcp_adapters.prompts
options:
members:
- load_mcp_prompt
::: langchain_mcp_adapters.resources
options:
members:
- load_mcp_resources
+28
View File
@@ -0,0 +1,28 @@
# Prebuilt
::: langgraph.prebuilt.chat_agent_executor
options:
members:
- create_react_agent
::: langgraph.prebuilt.tool_node
options:
members:
- ToolNode
- InjectedState
- InjectedStore
- tools_condition
::: langgraph.prebuilt.tool_validator
options:
members:
- ValidationNode
::: langgraph.prebuilt.interrupt
options:
members:
- HumanInterruptConfig
- ActionRequest
- HumanInterrupt
- HumanResponse
+3 -21
View File
@@ -1,25 +1,7 @@
# Pregel
::: langgraph.pregel.Pregel
::: langgraph.pregel
options:
show_if_no_docstring: true
show_root_heading: true
show_root_full_path: false
members:
- stream
- astream
- invoke
- ainvoke
- get_state
- aget_state
- get_state_history
- aget_state_history
- update_state
- aupdate_state
- bulk_update_state
- abulk_update_state
- get_graph
- aget_graph
- get_subgraphs
- aget_subgraphs
- with_config
- Pregel
- PregelNode
-12
View File
@@ -1,12 +0,0 @@
# LangGraph Supervisor
::: langgraph_supervisor.supervisor
options:
members:
- create_supervisor
::: langgraph_supervisor.handoff
options:
members:
- create_handoff_tool
- create_forward_message_tool
-13
View File
@@ -1,13 +0,0 @@
# LangGraph Swarm
::: langgraph_swarm.swarm
options:
members:
- SwarmState
- create_swarm
- add_active_agent_router
::: langgraph_swarm.handoff
options:
members:
- create_handoff_tool
+1
View File
@@ -10,6 +10,7 @@
- CachePolicy
- Interrupt
- PregelTask
- PregelExecutableTask
- StateSnapshot
- Send
- Command
@@ -1,7 +1,3 @@
---
search:
boost: 0.5
---
# Error reference
This page contains guides around resolving common errors you may find while building with LangGraph.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

+6 -47
View File
@@ -2,17 +2,17 @@
## :fontawesome-brands-safari:{ .safari } Safari connection error with local dev server
Safari blocks plainHTTP traffic on localhost. If you start Studio with a vanilla `langgraph dev`, the page may report a "Failed to load assistants" error and the browser DevTools will show network errors.
Safari blocks plainHTTP traffic on localhost. If you start Studio with a vanilla
`langgraph dev`, the page may report a "Failed to load assistants" error (or something similar) and the browser DevTools will show network errors.
#### Quick fix — run Studio through a secure Cloudflare tunnel
=== "Python"
```shell
pip install -U langgraph-cli>=0.2.6
pip install -U langgraph-cli>=0.2.6 # Python
langgraph dev --tunnel
```
=== "JS"
```shell
@@ -25,18 +25,17 @@ The command prints a URL like:
```shell
https://smith.langchain.com/studio/?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
```
where
```shell
?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
```
indicates the endpoint where your agent server is exposed.
indicates the endpoint where your agent server is exposed. Open that URL in Safari and Studio should load immediately.
Open that URL in Safari and Studio should load immediately.
#### Alternative — use a Chromiumbased browser
Chrome and other Chromiumbased browsers allow HTTP on localhost, so a plain `langgraph dev` should work without extra steps.
Chrome, Edge, and Brave allow HTTP on localhost, so a plain `langgraph dev` should work without extra steps.
#### If its still not loading
@@ -44,43 +43,3 @@ Chrome and other Chromiumbased browsers allow HTTP on localhost, so a plain
2. Confirm your CLI version with `langgraph --version`.
No other configuration, certificates, or CORS tweaks are required.
## :fontawesome-brands-brave:{ .brave } Brave connection error with local dev server
By default, Brave blocks plainHTTP traffic on localhost if Brave Shields are enabled. If you start Studio with a vanilla `langgraph dev`, the page may report a "Failed to load assistants" error and the browser DevTools will show network errors.
#### Quick fix — disable Brave Shields for LangSmith
Click the Brave icon next to the URL bar and turn off the Brave Shields in the popover.
![Brave Shields](./img/brave-shields.png)
#### Alternative — run Studio through a secure Cloudflare tunnel
=== "Python"
```shell
pip install -U langgraph-cli>=0.2.6
langgraph dev --tunnel
```
=== "JS"
```shell
# Requires @langchain/langgraph-cli>=0.0.26
npx @langchain/langgraph-cli dev
```
The command prints a URL like:
```shell
https://smith.langchain.com/studio/?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
```
where
```shell
?baseUrl=https://hamilton-praise-heart-costumes.trycloudflare.com
```
indicates the endpoint where your agent server is exposed. Open that URL in Brave and Studio should load immediately.
-2
View File
@@ -1,7 +1,5 @@
---
title: Tutorials
search:
boost: 0.5
---
# Tutorials
-4
View File
@@ -1,7 +1,3 @@
---
search:
boost: 2
---
# Workflows and Agents
This guide reviews common patterns for agentic systems. In describing these systems, it can be useful to make a distinction between "workflows" and "agents". One way to think about this difference is nicely explained in [Anthropic's](https://python.langchain.com/docs/integrations/providers/anthropic/) `Building Effective Agents` blog post:
+7 -12
View File
@@ -56,7 +56,6 @@ plugins:
- search:
separator: '[\s\u200b\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;'
- autorefs
- tags
- mkdocstrings:
custom_templates: templates
handlers:
@@ -384,24 +383,20 @@ nav:
- Resources:
# NOTE: prebuilt.md is auto-generated by `make build-prebuilt`
- agents/prebuilt.md
- Reference:
- API reference:
- reference/index.md
- LangGraph:
- Library:
- Graphs: reference/graphs.md
- Checkpointing: reference/checkpoints.md
- Storage: reference/store.md
- Types: reference/types.md
- Config: reference/config.md
- Functional API: reference/func.md
- Prebuilt components: reference/prebuilt.md
- Channels: reference/channels.md
- Errors: reference/errors.md
- Types: reference/types.md
- Constants: reference/constants.md
- Pregel: reference/pregel.md
- Channels: reference/channels.md
- Prebuilt:
- Agents: reference/agents.md
- Supervisor: reference/supervisor.md
- Swarm: reference/swarm.md
- MCP Adapters: reference/mcp.md
- Config: reference/config.md
- Functional API: reference/func.md
- LangGraph Platform:
- Server API: "cloud/reference/api/api_ref.md"
- CLI: "cloud/reference/cli.md"
+399 -223
View File
File diff suppressed because it is too large Load Diff
+2 -7
View File
@@ -1,11 +1,10 @@
[tool.poetry]
name = "langgraph-docs"
name = "langgraph-monorepo"
version = "0.0.1"
description = "LangGraph docs"
description = "LangGraph monorepo"
authors = []
license = "MIT"
readme = "README.md"
package-mode = false
[tool.poetry.dependencies]
python = "^3.10"
@@ -21,10 +20,6 @@ langgraph-checkpoint = { path = "../libs/checkpoint/", develop = true }
langgraph-checkpoint-sqlite = { path = "../libs/checkpoint-sqlite", develop = true }
langgraph-checkpoint-postgres = { path = "../libs/checkpoint-postgres", develop = true }
langgraph-sdk = {path = "../libs/sdk-py", develop = true}
# TODO: switch these to published versions
langgraph-supervisor = { git = "https://github.com/langchain-ai/langgraph-supervisor-py" }
langgraph-swarm = { git = "https://github.com/langchain-ai/langgraph-swarm-py" }
langchain-mcp-adapters = { git = "https://github.com/langchain-ai/langchain-mcp-adapters" }
langchain-ollama = "^0.2.3"
mkdocs = "*"
mkdocs-autorefs = "*"
@@ -1,13 +1,8 @@
import os
import tempfile
import nbformat
import pytest
from _scripts.notebook_convert import (
_convert_links_in_markdown,
_has_output,
convert_notebook,
)
@@ -37,41 +32,3 @@ def test_has_output() -> None:
def test_link_conversion(source: str, expected: str) -> None:
"""Test logic to convert links in markdown cells."""
assert _convert_links_in_markdown(source) == expected
EXPECTED_OUTPUT = """\
```shell
pip install -U langgraph
```
```python
print('Hello')
```\
"""
def test_converting_cell_magic() -> None:
"""Test converting cell magic to code blocks."""
with tempfile.TemporaryDirectory() as tmpdir:
nb_path = os.path.join(tmpdir, "test_notebook.ipynb")
# Create a minimal notebook object
nb = nbformat.v4.new_notebook()
nb.cells = [
nbformat.v4.new_code_cell(
"%%capture --no-stderr\n"
"%pip install -U langgraph"
),
nbformat.v4.new_code_cell("print('Hello')"),
]
nb.metadata["language_info"] = {"name": "python"}
# Write to file
with open(nb_path, "w", encoding="utf-8") as f:
nbformat.write(nb, f)
# Run the conversion
converted = convert_notebook(nb_path)
assert converted == EXPECTED_OUTPUT
@@ -27,8 +27,6 @@ Conn = _internal.Conn # For backward compatibility
class PostgresSaver(BasePostgresSaver):
"""Checkpointer that stores checkpoints in a Postgres database."""
lock: threading.Lock
def __init__(
@@ -27,8 +27,6 @@ Conn = _ainternal.Conn # For backward compatibility
class AsyncPostgresSaver(BasePostgresSaver):
"""Asynchronous checkpointer that stores checkpoints in a Postgres database."""
lock: asyncio.Lock
def __init__(
+1 -1
View File
@@ -425,7 +425,7 @@ typing-extensions = ">=4.7"
[[package]]
name = "langgraph-checkpoint"
version = "2.0.25"
version = "2.0.24"
description = "Library with base interfaces for LangGraph checkpoint savers."
optional = false
python-versions = ">=3.9"

Some files were not shown because too many files have changed in this diff Show More