Compare commits

..
Author SHA1 Message Date
William Fu-Hinthorn 396b3dc7f6 State modifier 2024-10-03 11:12:38 -07:00
vbarda 4fe00a138f lint 2024-10-03 12:17:40 -04:00
vbarda 1dcf33fc0e update 2024-10-03 12:16:46 -04:00
vbarda 97489f1386 langgraph: add support for passing store via state_modifier 2024-10-03 12:15:25 -04:00
397 changed files with 11218 additions and 40404 deletions
-115
View File
@@ -1,115 +0,0 @@
import asyncio
import json
import os
import pathlib
import sys
import langgraph_cli
import langgraph_cli.docker
import langgraph_cli.config
from langgraph_cli.exec import Runner, subp_exec
from langgraph_cli.progress import Progress
from langgraph_cli.constants import DEFAULT_PORT
def test(
config: pathlib.Path,
port: int,
tag: str,
verbose: bool,
):
with Runner() as runner, Progress(message="Pulling...") as set:
# check docker available
capabilities = langgraph_cli.docker.check_capabilities(runner)
# open config
config_json = langgraph_cli.config.validate_config_file(config)
set("Running...")
args = [
"run",
"--rm",
"-p",
f"{port}:8000",
]
if isinstance(config_json["env"], str):
args.extend(
[
"--env-file",
str(config.parent / config_json["env"]),
]
)
else:
for k, v in config_json["env"].items():
args.extend(
[
"-e",
f"{k}={v}",
]
)
if capabilities.healthcheck_start_interval:
args.extend(
[
"--health-interval",
"5s",
"--health-retries",
"1",
"--health-start-period",
"10s",
"--health-start-interval",
"1s",
]
)
else:
args.extend(
[
"--health-interval",
"5s",
"--health-retries",
"2",
]
)
_task = None
def on_stdout(line: str):
nonlocal _task
if "GET /ok" in line or "Uvicorn running on" in line:
set("")
sys.stdout.write(
f"""Ready!
- API: http://localhost:{port}
"""
)
sys.stdout.flush()
_task.cancel()
return True
return False
async def subp_exec_task(*args, **kwargs):
nonlocal _task
_task = asyncio.create_task(subp_exec(*args, **kwargs))
await _task
try:
runner.run(
subp_exec_task(
"docker",
*args,
tag,
verbose=verbose,
on_stdout=on_stdout,
)
)
except asyncio.CancelledError:
pass
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--tag", type=str)
parser.add_argument("-c", "--config", type=str, default="./langgraph.json")
parser.add_argument("-p", "--port", default=DEFAULT_PORT)
args = parser.parse_args()
test(pathlib.Path(args.config), args.port, args.tag, verbose=True)
+8 -22
View File
@@ -39,36 +39,22 @@ jobs:
- name: Install cli globally
if: steps.changed-files.outputs.all
run: pip install -e .
- name: Build and test service A
- name: Start service A
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples
run: |
# The build-arg isn't used; just testing that we accept other args
langgraph build -t langgraph-test-a --base-image "langchain/langgraph-trial"
cp .env.example .envg
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -c langgraph.json -t langgraph-test-a
- name: Build and test service B
timeout 60 langgraph test -c examples/langgraph.json --verbose || (exit "$(($? == 124 ? 0 : $?))")
- name: Start service B
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples/graphs
run: |
langgraph build -t langgraph-test-b --base-image "langchain/langgraph-trial"
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-b
- name: Build and test service C
timeout 60 langgraph test --verbose || (exit "$(($? == 124 ? 0 : $?))")
- name: Start service C
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples/graphs_reqs_a
run: |
langgraph build -t langgraph-test-c --base-image "langchain/langgraph-trial"
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-c
- name: Build and test service D
timeout 60 langgraph test --verbose || (exit "$(($? == 124 ? 0 : $?))")
- name: Start service D
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples/graphs_reqs_b
run: |
langgraph build -t langgraph-test-d --base-image "langchain/langgraph-trial"
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-d
- name: Build JS service
if: steps.changed-files.outputs.all
working-directory: libs/cli/js-examples
run: |
langgraph build -t langgraph-test-e
timeout 60 langgraph test --verbose || (exit "$(($? == 124 ? 0 : $?))")
-7
View File
@@ -21,7 +21,6 @@ jobs:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
name: "test #${{ matrix.python-version }}"
steps:
@@ -33,12 +32,6 @@ jobs:
poetry-version: ${{ env.POETRY_VERSION }}
working-directory: ${{ inputs.working-directory }}
cache-key: test-${{ inputs.working-directory }}
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
- name: Install dependencies
shell: bash
+2 -18
View File
@@ -16,22 +16,14 @@ jobs:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
core-version:
- ">=0.2.39,<0.3.0"
- "latest"
ff-send-v2:
- "false"
include:
- python-version: "3.11"
core-version: ">=0.2.42,<0.3.0"
- python-version: "3.11"
core-version: "latest"
ff-send-v2: "true"
defaults:
run:
working-directory: libs/langgraph
name: "test #${{ matrix.python-version }} (langchain-core: ${{ matrix.core-version }}, ff-send-v2: ${{ matrix.ff-send-v2 }})"
name: "test #${{ matrix.python-version }} (langchain-core: ${{ matrix.core-version }})"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
@@ -40,12 +32,6 @@ jobs:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: test-langgraph
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
- name: Install dependencies
shell: bash
@@ -57,8 +43,6 @@ jobs:
- name: Run tests
shell: bash
env:
LANGGRAPH_FF_SEND_V2: ${{ matrix.ff-send-v2 }}
run: |
make test
-2
View File
@@ -93,5 +93,3 @@ jobs:
# This is *only for CI use* and is *extremely dangerous* otherwise!
# https://github.com/pypa/gh-action-pypi-publish#tolerating-release-package-file-duplicates
skip-existing: true
# Temp workaround since attestations are on by default as of gh-action-pypi-publish v1.11.0
attestations: false
@@ -27,12 +27,6 @@ jobs:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: test-scheduler-kafka
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_RO_TOKEN }}
- name: Install dependencies
shell: bash
-2
View File
@@ -31,7 +31,6 @@ jobs:
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-duckdb",
"libs/checkpoint-postgres",
"libs/scheduler-kafka",
]
@@ -48,7 +47,6 @@ jobs:
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-duckdb",
"libs/checkpoint-postgres"
]
uses: ./.github/workflows/_test.yml
+4 -18
View File
@@ -25,7 +25,7 @@ jobs:
get-changed-files:
runs-on: ubuntu-latest
outputs:
changed-files: ${{ steps.changed-files.outputs.added_modified }}
changed-files: ${{ steps.changed-files.outputs.all }}
steps:
- uses: actions/checkout@v4
- name: Get changed files
@@ -44,8 +44,6 @@ jobs:
deploy:
# needs: run-changed-notebooks
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.MKDOCS_GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
@@ -60,14 +58,8 @@ jobs:
- name: Install dependencies
run: |
poetry install --with test --no-root
poetry run pip install -U \
pytest \
pytest-check-links \
langsmith \
langchain \
GitPython \
"git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
poetry install --with docs
poetry run pip install -U pytest pytest-check-links langsmith langchain GitPython
- name: Lint Docs
# This step lints the docs using the existing linting set up.
@@ -88,13 +80,8 @@ jobs:
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "https://x.com/.*" \
--check-links-ignore "https://github\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
--check-links-ignore "/.*\.(ipynb|html)$" \
--check-links-ignore "https://python\.langchain\.com/.*" \
--check-links-ignore "https://openai\.com/.*" \
--check-links-ignore "https://pepy\.tech/.*" \
--check-links $(find docs/site -name "index.html" | grep -v 'storm/index.html')
--check-links $(find docs/site -name "index.html" | grep -v 'storm/index.html')
else
echo "Fetching changes from origin/main..."
git fetch origin main
@@ -105,7 +92,6 @@ jobs:
echo "Running link check on HTML files matching changed notebook files..."
poetry run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
--check-links-ignore "https://x.com/.*" \
--check-links-ignore "https://github\.com/.*" \
--check-links-ignore "/.*\.(ipynb|html)$" \
-2
View File
@@ -270,8 +270,6 @@ jobs:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
print-hash: true
# Temp workaround since attestations are on by default as of gh-action-pypi-publish v1.11.0
attestations: false
mark-release:
needs:
+1 -13
View File
@@ -16,8 +16,6 @@
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
[LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform) is infrastructure for deploying LangGraph agents. It is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework. The LangGraph Platform consists of several components that work together to support the development, deployment, debugging, and monitoring of LangGraph applications: [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server) (APIs), [LangGraph SDKs](https://langchain-ai.github.io/langgraph/concepts/sdk) (clients for the APIs), [LangGraph CLI](https://langchain-ai.github.io/langgraph/concepts/langgraph_cli) (command line tool for building the server), [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio) (UI/debugger),
To learn more about LangGraph, check out our first LangChain Academy course, *Introduction to LangGraph*, available for free [here](https://academy.langchain.com/courses/intro-to-langgraph).
### Key Features
@@ -28,16 +26,6 @@ To learn more about LangGraph, check out our first LangChain Academy course, *In
- **Streaming Support**: Stream outputs as they are produced by each node (including token streaming).
- **Integration with LangChain**: LangGraph integrates seamlessly with [LangChain](https://github.com/langchain-ai/langchain/) and [LangSmith](https://docs.smith.langchain.com/) (but does not require them).
### LangGraph Platform
LangGraph Platform is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework.
Here are some common issues that arise in complex deployments, which LangGraph Platform addresses:
- **Streaming support**: LangGraph Server provides [multiple streaming modes](https://langchain-ai.github.io/langgraph/concepts/streaming) optimized for various application needs
- **Background runs**: Runs agents asynchronously in the background
- **Support for long running agents**: Infrastructure that can handle long running processes
- **[Double texting](https://langchain-ai.github.io/langgraph/concepts/double_texting)**: Handle the case where you get two messages from the user before the agent can respond
- **Handle burstiness**: Task queue for ensuring requests are handled consistently without loss, even under heavy loads
## Installation
@@ -238,7 +226,7 @@ final_state["messages"][-1].content
* [How-to Guides](https://langchain-ai.github.io/langgraph/how-tos/): Accomplish specific things within LangGraph, from streaming, to adding memory & persistence, to common design patterns (branching, subgraphs, etc.), these are the place to go if you want to copy and run a specific code snippet.
* [Conceptual Guides](https://langchain-ai.github.io/langgraph/concepts/high_level/): In-depth explanations of the key concepts and principles behind LangGraph, such as nodes, edges, state and more.
* [API Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more.
* [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform): LangGraph Platform is a commercial solution for deploying agentic applications in production, built on the open-source LangGraph framework.
* [Cloud (beta)](https://langchain-ai.github.io/langgraph/cloud/): With one click, deploy LangGraph applications to LangGraph Cloud.
## Contributing
+1 -1
View File
@@ -24,7 +24,7 @@ export -f execute_notebook
# Check if custom notebook paths are provided
if [ $# -gt 0 ]; then
notebooks=$(echo "$@" | tr ' ' '\n' | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
notebooks="$@"
else
# Find all notebooks and filter out those in the skip list
notebooks=$(find docs/docs/tutorials docs/docs/how-tos -name "*.ipynb" | grep -v ".ipynb_checkpoints" | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
@@ -1,246 +0,0 @@
import importlib
import inspect
import logging
import os
import re
from typing import List, Literal, Optional
from typing_extensions import TypedDict
import nbformat
from nbconvert.preprocessors import Preprocessor
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Base URL for all class documentation
_LANGCHAIN_API_REFERENCE = "https://python.langchain.com/api_reference/"
_LANGGRAPH_API_REFERENCE = "https://langchain-ai.github.io/langgraph/reference/"
# (alias/re-exported modules, source module, class, docs namespace)
MANUAL_API_REFERENCES_LANGGRAPH = [
(
["langgraph.prebuilt"],
"langgraph.prebuilt.chat_agent_executor",
"create_react_agent",
"prebuilt",
),
(["langgraph.prebuilt"], "langgraph.prebuilt.tool_node", "ToolNode", "prebuilt"),
(
["langgraph.prebuilt"],
"langgraph.prebuilt.tool_node",
"tools_condition",
"prebuilt",
),
(
["langgraph.prebuilt"],
"langgraph.prebuilt.tool_node",
"InjectedState",
"prebuilt",
),
# Graph
(["langgraph.graph"], "langgraph.graph.message", "add_messages", "graphs"),
(["langgraph.graph"], "langgraph.graph.state", "StateGraph", "graphs"),
(["langgraph.graph"], "langgraph.graph.state", "CompiledStateGraph", "graphs"),
([], "langgraph.types", "StreamMode", "types"),
(["langgraph.graph"], "langgraph.constants", "START", "constants"),
(["langgraph.graph"], "langgraph.constants", "END", "constants"),
(["langgraph.constants"], "langgraph.types", "Send", "types"),
(["langgraph.constants"], "langgraph.types", "Interrupt", "types"),
([], "langgraph.types", "RetryPolicy", "types"),
([], "langgraph.checkpoint.base", "Checkpoint", "checkpoints"),
([], "langgraph.checkpoint.base", "CheckpointMetadata", "checkpoints"),
([], "langgraph.checkpoint.base", "BaseCheckpointSaver", "checkpoints"),
([], "langgraph.checkpoint.base", "SerializerProtocol", "checkpoints"),
([], "langgraph.checkpoint.serde.jsonplus", "JsonPlusSerializer", "checkpoints"),
([], "langgraph.checkpoint.memory", "MemorySaver", "checkpoints"),
([], "langgraph.checkpoint.sqlite.aio", "AsyncSqliteSaver", "checkpoints"),
([], "langgraph.checkpoint.sqlite", "SqliteSaver", "checkpoints"),
([], "langgraph.checkpoint.postgres.aio", "AsyncPostgresSaver", "checkpoints"),
([], "langgraph.checkpoint.postgres", "PostgresSaver", "checkpoints"),
]
WELL_KNOWN_LANGGRAPH_OBJECTS = {
(module_, class_): (source_module, namespace)
for (modules, source_module, class_, namespace) in MANUAL_API_REFERENCES_LANGGRAPH
for module_ in modules + [source_module]
}
def _make_regular_expression(pkg_prefix: str) -> re.Pattern:
if not pkg_prefix.isidentifier():
raise ValueError(f"Invalid package prefix: {pkg_prefix}")
return re.compile(
r"from\s+(" + pkg_prefix + "(?:_\w+)?(?:\.\w+)*?)\s+import\s+"
r"((?:\w+(?:,\s*)?)*" # Match zero or more words separated by a comma+optional ws
r"(?:\s*\(.*?\))?)", # Match optional parentheses block
re.DOTALL, # Match newlines as well
)
# Regular expression to match langchain import lines
_IMPORT_LANGCHAIN_RE = _make_regular_expression("langchain")
_IMPORT_LANGGRAPH_RE = _make_regular_expression("langgraph")
def _get_full_module_name(module_path, class_name) -> Optional[str]:
"""Get full module name using inspect"""
try:
module = importlib.import_module(module_path)
class_ = getattr(module, class_name)
module = inspect.getmodule(class_)
if module is None:
# For constants, inspect.getmodule() might return None
# In this case, we'll return the original module_path
return module_path
return module.__name__
except AttributeError as e:
logger.warning(f"Could not find module for {class_name}, {e}")
return None
except ImportError as e:
logger.warning(f"Failed to load for class {class_name}, {e}")
return None
def _get_doc_title(data: str, file_name: str) -> str:
try:
return re.findall(r"^#\s*(.*)", data, re.MULTILINE)[0]
except IndexError:
pass
# Parse the rst-style titles
try:
return re.findall(r"^(.*)\n=+\n", data, re.MULTILINE)[0]
except IndexError:
return file_name
class ImportInformation(TypedDict):
imported: str # imported class name
source: str # module path
docs: str # URL to the documentation
title: str # Title of the document
def _get_imports(
code: str, doc_title: str, package_ecosystem: Literal["langchain", "langgraph"]
) -> List[ImportInformation]:
"""Get imports from the given code block.
Args:
code: Python code block from which to extract imports
doc_title: Title of the document
package_ecosystem: "langchain" or "langgraph". The two live in different
repositories and have separate documentation sites.
Returns:
List of import information for the given code block
"""
imports = []
if package_ecosystem == "langchain":
pattern = _IMPORT_LANGCHAIN_RE
elif package_ecosystem == "langgraph":
pattern = _IMPORT_LANGGRAPH_RE
else:
raise ValueError(f"Invalid package ecosystem: {package_ecosystem}")
for import_match in pattern.finditer(code):
module = import_match.group(1)
if "pydantic_v1" in module:
continue
imports_str = (
import_match.group(2).replace("(\n", "").replace("\n)", "")
) # Handle newlines within parentheses
# remove any newline and spaces, then split by comma
imported_classes = [
imp.strip()
for imp in re.split(r",\s*", imports_str.replace("\n", ""))
if imp.strip()
]
for class_name in imported_classes:
module_path = _get_full_module_name(module, class_name)
if not module_path:
continue
if len(module_path.split(".")) < 2:
continue
if package_ecosystem == "langchain":
pkg = module_path.split(".")[0].replace("langchain_", "")
top_level_mod = module_path.split(".")[1]
url = (
_LANGCHAIN_API_REFERENCE
+ pkg
+ "/"
+ top_level_mod
+ "/"
+ module_path
+ "."
+ class_name
+ ".html"
)
elif package_ecosystem == "langgraph":
if (module, class_name) not in WELL_KNOWN_LANGGRAPH_OBJECTS:
# Likely not documented yet
continue
source_module, namespace = WELL_KNOWN_LANGGRAPH_OBJECTS[
(module, class_name)
]
url = (
_LANGGRAPH_API_REFERENCE
+ namespace
+ "/#"
+ source_module
+ "."
+ class_name
)
else:
raise ValueError(f"Invalid package ecosystem: {package_ecosystem}")
# Add the import information to our list
imports.append(
{
"imported": class_name,
"source": module,
"docs": url,
"title": doc_title,
}
)
return imports
class ImportPreprocessor(Preprocessor):
"""A preprocessor to replace imports in each Python code cell with links to their
documentation and append the import info in a comment."""
def preprocess(self, nb, resources):
self.all_imports = []
file_name = os.path.basename(resources.get("metadata", {}).get("name", ""))
_DOC_TITLE = _get_doc_title(nb.cells[0].source, file_name)
cells = []
for cell in nb.cells:
if cell.cell_type == "code":
cells.append(cell)
imports = _get_imports(
cell.source, _DOC_TITLE, "langchain"
) + _get_imports(cell.source, _DOC_TITLE, "langgraph")
if not imports:
continue
cells.append(
nbformat.v4.new_markdown_cell(
source=f"""
<div>
<b>API Reference:</b>
{' | '.join(f'<a href="{imp["docs"]}">{imp["imported"]}</a>' for imp in imports)}
</div>
"""
)
)
else:
cells.append(cell)
nb.cells = cells
return nb, resources
-126
View File
@@ -1,126 +0,0 @@
import os
import re
from pathlib import Path
import nbformat
from nbconvert.exporters import MarkdownExporter
from nbconvert.preprocessors import Preprocessor
from generate_api_reference_links import ImportPreprocessor
class EscapePreprocessor(Preprocessor):
def preprocess_cell(self, cell, resources, cell_index):
if cell.cell_type == "markdown":
# rewrite markdown links to html links (excluding image links)
cell.source = re.sub(
r"(?<!!)\[([^\]]*)\]\((?![^\)]*//)([^)]*)(?:\.ipynb)?\)",
r'<a href="\2">\1</a>',
cell.source,
)
# Fix image paths in <img> tags
cell.source = re.sub(
r'<img\s+src="\.?/img/([^"]+)"', r'<img src="../img/\1"', cell.source
)
elif cell.cell_type == "code":
# escape ``` in code
cell.source = cell.source.replace("```", r"\`\`\`")
# escape ``` in output
if "outputs" in cell:
filter_out = set()
for i, output in enumerate(cell["outputs"]):
if "text" in output:
if not output["text"].strip():
filter_out.add(i)
continue
value = output["text"].replace("```", r"\`\`\`")
# handle a funky case w/ references in text
value = re.sub(r"\[(\d+)\](?=\[(\d+)\])", r"[\1]\\", value)
output["text"] = value
elif "data" in output:
for key, value in output["data"].items():
if isinstance(value, str):
value = value.replace("```", r"\`\`\`")
# handle a funky case w/ references in text
output["data"][key] = re.sub(
r"\[(\d+)\](?=\[(\d+)\])", r"[\1]\\", value
)
cell["outputs"] = [
output
for i, output in enumerate(cell["outputs"])
if i not in filter_out
]
return cell, resources
class ExtractAttachmentsPreprocessor(Preprocessor):
"""
Extracts all of the outputs from the notebook file. The extracted
outputs are returned in the 'resources' dictionary.
"""
def preprocess_cell(self, cell, resources, cell_index):
"""
Apply a transformation on each cell,
Parameters
----------
cell : NotebookNode cell
Notebook cell being processed
resources : dictionary
Additional resources used in the conversion process. Allows
preprocessors to pass variables into the Jinja engine.
cell_index : int
Index of the cell being processed (see base.py)
"""
# Get files directory if it has been specified
# Make sure outputs key exists
if not isinstance(resources["outputs"], dict):
resources["outputs"] = {}
# Loop through all of the attachments in the cell
for name, attach in cell.get("attachments", {}).items():
for mime, data in attach.items():
if mime not in {
"image/png",
"image/jpeg",
"image/svg+xml",
"application/pdf",
}:
continue
# attachments are pre-rendered. Only replace markdown-formatted
# images with the following logic
attach_str = f"({name})"
if attach_str in cell.source:
data = f"(data:{mime};base64,{data})"
cell.source = cell.source.replace(attach_str, data)
return cell, resources
exporter = MarkdownExporter(
preprocessors=[
EscapePreprocessor,
ExtractAttachmentsPreprocessor,
ImportPreprocessor,
],
template_name="mdoutput",
extra_template_basedirs=[
os.path.join(os.path.dirname(__file__), "notebook_convert_templates")
],
)
def convert_notebook(
notebook_path: Path,
) -> Path:
with open(notebook_path) as f:
nb = nbformat.read(f, as_version=4)
body, _ = exporter.from_notebook_node(nb)
return body
@@ -1,5 +0,0 @@
{
"mimetypes": {
"text/markdown": true
}
}
@@ -1,33 +0,0 @@
{% extends 'markdown/index.md.j2' %}
{%- block traceback_line -%}
```output
{{ line.rstrip() | strip_ansi }}
```
{%- endblock traceback_line -%}
{%- block stream -%}
```output
{{ output.text.rstrip() }}
```
{%- endblock stream -%}
{%- block data_text scoped -%}
```output
{{ output.data['text/plain'].rstrip() }}
```
{%- endblock data_text -%}
{%- block data_html scoped -%}
```html
{{ output.data['text/html'] | safe }}
```
{%- endblock data_html -%}
{%- block data_jpg scoped -%}
![](data:image/jpg;base64,{{ output.data['image/jpeg'] }})
{%- endblock data_jpg -%}
{%- block data_png scoped -%}
![](data:image/png;base64,{{ output.data['image/png'] }})
{%- endblock data_png -%}
-40
View File
@@ -1,40 +0,0 @@
import logging
from typing import Any, Dict
from mkdocs.structure.pages import Page
from mkdocs.structure.files import Files, File
from notebook_convert import convert_notebook
logger = logging.getLogger(__name__)
logging.basicConfig()
logger.setLevel(logging.INFO)
class NotebookFile(File):
def is_documentation_page(self):
return True
def on_files(files: Files, **kwargs: Dict[str, Any]):
new_files = Files([])
for file in files:
if file.src_path.endswith(".ipynb"):
new_file = NotebookFile(
path=file.src_path,
src_dir=file.src_dir,
dest_dir=file.dest_dir,
use_directory_urls=file.use_directory_urls,
)
new_files.append(new_file)
else:
new_files.append(file)
return new_files
def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
if page.file.src_path.endswith(".ipynb"):
logger.info("Processing Jupyter notebook: %s", page.file.src_path)
body = convert_notebook(page.file.abs_src_path)
return body
return markdown
+6 -11
View File
@@ -36,11 +36,11 @@ NOTEBOOKS_NO_EXECUTION = [
"docs/docs/tutorials/rag/langgraph_self_rag_local.ipynb",
# this loads a massive dataset from gcp
"docs/docs/tutorials/usaco/usaco.ipynb",
# TODO: figure out why autogen notebook is not runnable (they are just hanging. possible due to code execution?)
"docs/docs/how-tos/autogen-integration.ipynb",
# TODO: need to update these notebooks to make sure they are runnable in CI
"docs/docs/tutorials/storm/storm.ipynb", # issues only when running with VCR
"docs/docs/tutorials/lats/lats.ipynb", # issues only when running with VCR
"docs/docs/tutorials/multi_agent/hierarchical_agent_teams.ipynb", # taking a very long time to run
"docs/docs/tutorials/customer-support/customer-support.ipynb", # user input - update
"docs/docs/tutorials/rag/langgraph_crag.ipynb", # flakiness from tavily
"docs/docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # Cannot create a consistent method resolution error from VCR
"docs/docs/how-tos/map-reduce.ipynb" # flakiness from structured output, only when running with VCR
@@ -70,13 +70,10 @@ def is_comment(code: str) -> bool:
return code.strip().startswith("#")
def has_blocklisted_command(code: str, metadata: dict) -> bool:
if 'hide_from_vcr' in metadata:
return True
def has_blocklisted_command(code: str) -> bool:
code = code.strip()
for blocklisted_pattern in BLOCKLIST_COMMANDS:
if blocklisted_pattern in code:
for blocklisted_command in BLOCKLIST_COMMANDS:
if blocklisted_command in code:
return True
return False
@@ -111,7 +108,7 @@ def add_vcr_to_notebook(
if all(is_comment(line) or not line.strip() for line in lines):
continue
if has_blocklisted_command(cell.source, cell.metadata):
if has_blocklisted_command(cell.source):
continue
cell_id = cell.get("id", idx)
@@ -128,8 +125,6 @@ def add_vcr_to_notebook(
"import msgpack",
"import base64",
"import zlib",
"import os",
"os.environ.pop(\"LANGCHAIN_TRACING_V2\", None)",
"custom_vcr = vcr.VCR()",
"",
"def compress_data(data, compression_level=9):",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
eNqFVG1sU1UYLkIIGkA0GkkY7Fid8Qenve1tR9uQYO0Ax6gbW0GKEXJ27rnttfeLe07HPiTKwKAjkVxJGCrG6LqWlA26AGrAKYmQGPWHhhgsBELUGP/4C6KJBua5XcsgW2Z/NPee9+t5n+c5t7/QRSyqGPqcEUVnxEKY8Rdq9xcssjNLKNuX1whLG1KurbUjMZS1lHJDmjGTRrxeZCoepLO0ZZgK9mBD83b5vBqhFKUIzXUaUk/5hz63hrp3MCNDdOqOAJ/gD6wE7loSP3m5z20ZKuFP7iwllptHscGR6Mw5ekF5ErQTjWidxIoArQfoSCNAoeB5o9O9+xWnlSER1UnFKspKBIowCKmh64RBPx8mNPoFpyftoYxoTl7SyAJkEYBAmqimnFUBolShjG8CGFIzip4CzAAsTYADyAM283+g6LLBi3cX0gRJnLGDubRBmT02jYOTCGNiMkh0bEi8lz2a6lXMlUAisooYKWIHW4Vku5ghxIRIVbpIfrLKLiHTVBWMnLj3Vb7HSJUMyHpMMj1cdCBCTqXO7M+iNRzeth4umQ4Ejxjw+EvdkG+n6ConHaqIQ8qblfi5ewMmwhneB1btYOcni0/cm2NQeziOcGvHfS2RhdP2MLK0xsCpe8+trM4UjdiFWNv0cdXg1DjR4/N5wmP3NaY9OraHZaRSMnaX5LslRa6vCIVGKPhO1FhSiZ5iaXvIL4SOWYSa3Mpkb563ZFnan+OKkO+/KVTd90lrS03N665Hc01cHXt8naWsBEIAtGIGHP8AXyjiD0cEP1gfT4zEqmMSM4oxlrCQTmUuyNqa+AWczuoZIhVjM8pedk+tZfH5qqIpDFZvHhfLebVzAUEQys/MmmkRjbPmTMyJ4XD4f/pyZgizTzv7QZ8AhUBicstgYFsZzFQ5eX+rePIOHo7o6Vkyp/DUssGs2TPjEfzbilXQUJHsL/jzDsHXviqeifokRuM0bMbFYCiTNMPBM90Qq0ZWgox/wwisGKKb2WXQKRA50BiSRTEoiqIkBcOyLAeCZJUoyVK4kQx1Kcgu+jw+kDKMlEpOxtbBGMJpAjsqtrELTckXo/Hm2MhW2G50Gpy/BOI864ZO8h3E4na0i5XR/IJbJM/L26NJ+3QIS6tE7MeBkBwMiGEM177UXqoZ6K5Bcs7XofKt3MNtavGji3OW1B9Y4Kr85iYORlu+fm7JmxPfhga3N767Dp59IvrYlQMLBgbc6NDCIBM7dsmjw7e+aimuWL3otZvXfx1f/tfy3jqWHLx0NXmz+9LqNQmvcqb52q2bvXSC7V+snctvWltEzX8IzXXRtj9/emBZpC439NaKq0ut9Rebd245Jhx6fHPyw8Onik37R0uPLPvn8rUdx368nF5Td5AObLqx4f2ntv7ctOffw+fnzXfvHf0Nb93Yua9p7qJFDaW/zRsPhhbOG3/nOj7y3fwr50tvfDQwNG/j2dL4hdsfxILJy2/3PzS0/pcTnx5d+nr9hvr3Pm+4dRSzIw8fv724b3vqeHzLhYEvI7ueLd/5+MDtO8tcromJua7fPfHBjjku1385MZ/f
@@ -1 +0,0 @@
eNqFVHlsFFUcXiyIgTYg3gFkWO7j7c7sbrtsRcyy3KVsabdCMUJe37zdme7szHTe2x5UgpRLhEgmHiTGEAPbXdm00NoiIaAGFAE1QkgIbEgANQT/kIQQDFY5fLPd5QgNzh+Tmd/5/b7f915rqgEbRNbUAe2ySrEBEWU/xGxNGbg+jgndkIxhKmlioiJYFdodN+TMBIlSnZQ6nVCXHVClkqHpMnIgLeZsEJwxTAiMYJKo1cTmzPct9hhsWkW1KFaJvZQTeJdnOmfPBzHLWy12Q1Mw+7LHCTbszIs0hkSllqlRgpSTCRdr5lQYw2/Y17xtpWsiViw3UmBcxMANigHRVBVT4GIN+BIXb9UhzYTimBVXo8U5aGAOchJW9HBc4SAhMqEMPUehEpXVCEc1jkqYs0A4uGr25mQ1rLHkNSkJQ5GxtD0haYSaXY/NvQ8ihHUKsIo0kdUyOyKrZX06J+KwAilOIwtbllgzHcVYB1CRG3CyL8vshLquyAhafmcdm6M9RwCgzTp+3J22IAJGn0rNA/48DmdFM1uTyvEOt8fh6mwCbDpZVRjRQIEMUlLP+g897NAhirI6ICcBM9mXvPfhGI2YbeUQBaseKQkNJJlt0IiVeLofthtxlcoxbKYCFY+3yzkftHM7BMHh63qkMGlWkdkWhgrBXfdJvp+SZvt1A74E8MLePEsKViNUMncLvpIvDEx0Jl+8PslK0jhpTbCN4J9PpHKK2xUsy2/zom1EYg7bjvn1PEOezvEeLogoZ+mHE2aUunylzDK/PNQeyLUJ9buMrpABVRJmC5mbX34KSXE1isV0oN+1Z+wPxjJYf0WOyRTkThtblvVrJjw8z2cmPjHSwDHGmtUx4fb5fP9TlzGDqdljzQcEHvCeUN+UxZ4VGa6/zL4zm8OTtPAwROOfEPkATz6ae2J0/3h4z4p0DjSQRfMw+17FCxXRCEKLPdF6b039kmhZXXFj06Jo/f4mgBQtLgLK7i0MsoJoomaG87p8XkEQisPe8AyvKIphEYsC9kIXqnXBWgR3N8jQTAsOgYtoWkTB+wLzQAAiCYOqrGzM1JyaJf7yhYH25aBSq9UYfyHIeFY1FSersMHkaKazrdkBN3CSpVf6a8yeGUj0upHb40VY9Lh9CMxdVtmZF9B9gSSs2yF7P65jMjWY6diAyJitz9iyT4Fi+qMj/YUb771/+tiOev2r+OQWtw8c3DBqSGxKeOa4C4Fw28kFvzeg25d++jQJJkzakkoFh9Efnu86vnTYhbPXrg5effuj386du44O9M6+9c1rsz4uKu3Zc/pPe/Vaji6fcKp1WXndzqLRH4yYgraP/fdFuRceXJnmPwSHqm/Vx891nIbDS2q/u3uht6juJln57SevDr4+98tRZ6b5zhevH7Dr+tHLfwQH7py04PCO0tVlRy83nimcKL9wfjstGDnoVOcrl4OuW2PGDqwc9NzkUPX5q43vlu+Z6vmluzgz2z90Y0/wyNWZ/j3GItM+flMvHT5r6dlpvz696c4o6dmxqYhBhh1xv3ylcNydoqE7xxVu/QzO12+2LSbp5MCTF08IspR2v+S8NDq9MPPmjanbXh9xvGP/j3+3fH7zzHsF8BSIbL72T/vdbVvKujtOurtqL3WMXtS9tu3QX1duDLXZ7t0rsCWnrXtnyFM223+sv8iH
@@ -1 +0,0 @@
eNqFVG1oHEUYvlDQ+sMPqggGwfFoU5HM3e7d5i4XBY2XxLQ1HyZX8iGlndudvd3e7uy6O5fetUZqEm1sSdulCIKlBXO5K9eYJm3qB7Ui2B+KFhTqj6MqVBAs8YcW/KxtnL3cJSkJcVmW2Xm/nvd5n5mh/AC2bNUgVZMqodhCImU/tjOUt/DLKWzTkZyOqWJI2c6O7th4ylKLmxRKTbvB70em6kOEKpZhqqJPNHT/AO/XsW2jBLazcUPKFK/s8+oovZMaSUxsbwPguYBQC7wVJ7bz0j6vZWiYrbwpG1teZhUNhoRQd2uPgihQbaBnAEE6fsY7uMMNNySsuWZRQykJwyCsg7ZBCKYwwApwoQDn5rEzNsW669dnpACyMEBAwZoppzSAbFu1KUMPKNKSKkkAagCqYOCC8IHt7AtUIhsNC0u3uIvjOSPuHcwrGEmMtSNZxbCpM7OChzNIFLFJISaiIbHcznuJvapZCyQsa4jiguhiLRHtFJIYmxBp6gDOLUQ508g0NVVErt2/m/U1WSYE0oyJV5oLLmTI6CTU+aCxgsPfmWFjI4DzBQVfYDoNWbcq0RjxUEMMUs4s2S8sN5hITLI8sCwJJ7cQPLXcx7CdiTYkdnTfkRJZouJMIEsPCeeW71spQlUdO/lo58pyZeNSuaCP532RmTsS2xkiOhMy0mw8s0jyYkiBzTsIuRDk+KkKSxomCao44wE+cMrCtsnkjIdzLCVN2UNZNhH81ef5sgLf7dhWmeYPng3ZJjYd52KLpdYCTgAdIgWungBf3xCINHAB8HxbbDJaLhNbdRgzMQsRW2YDaa4MPy8qKZLEUiG66tiL3qW2LFZfU3WVwvLpY8Nyf52swHFcsWZNTwvrjDW3YjYYiUT+Jy9jBlNn1u0P8hzkhNhCl3VCfxGsFrlwhst4ci4ehmjjGp5LeCreYE3v1fFwgf5CGTRUJedjtt7J8W1Iifa0b+sVd0vp7e2tAdqeDCvy+TQUNSMlQcruMQxLgkhTpwjiclyuY28wGBLCKBQOcjyW5XBIkgUUiGBhfEBFToH38SBhGAkNn4m2wCgSFQy7S7Jx8k197Y1tW6KTvbDLiBuMvxhiPBOD4Fw3tpgcnUKpNDvgFs6x8K7GPme2XpTCQTHIx+vrOSEYEWFzT9d0RUCLAsm6t0PpvnyNydRiW5euPnZovaf0rHvh8FPJz559YGT+4Nv/PCQVM39t7al5/7TAPz56iOgjEzuaLyQ2jNzed3QL+PbT8P1y04PXvrn+0ZMPX50jF38+4c/fuvGr9f0vP8798Vt6//Dgn0K+te74PfcNzwbXF8b6dw1HNzv96St6q3MDv3ryNHfsJ/zi1MEDZ/dHoXbprvnQ7bHqwcu/v5N+dNO9jxw4fLbl3Ngr36G9ezqPfVL9xCnu6VERq7v+rd568s0Pa9Lt+txRsvnW6Bs3pbe+eL332pfnhzfePHIid/1uj2d+fp3n76+rL1dVeTz/AbjckGA=
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
eNrtVnlwE9cZN5BylHFKw5EEEliUMKbglXd1WqZukC9ssJHxBQg7YrX7JC1e7a73sCUbwYTYMRBKWAihgXAbG4wxR8wZIDRcBh+QTGvCkQBJGww0QElIIIXQt7IM9pB00hn6R9vsjLR67/u9736ffrOqCoEg0hzbpYZmJSAQpAQX4sJZVQIokIEolVZ6geThqIp0W2bWWlmgT4/wSBIvxkRFETyt5XjAErSW5LxRhXgU6SGkKPibZ0BQTYWTo/xnupSXaLxAFAk3EDUxyJQSDclBW6wEF5oieCRCRCQPQBhCggYRFhSJCOHkZAlJ4rg4QtBEIhqBY4AKl0UgaAJ5cMfLUYBRt9y8hBo4FcTCJQ7foiQAwgsXLoIRAdyQgJeHsUmyoCrBtJi6x3FMyB3JzweVu2Q2GL6q68HvGKREwxLeIMANJEfQd5+kYiggkgLNh2CaMUBCBMCAQoKVkBAMcXECQrBiERBo1h0MM5hWeESrquAJAeqGiReDhngBJlSQaNC2bIcGF+1ewuigKk0goKYFFokWAKXG8RCtpqcdzTmnAVKC6EBeoMoDCAqa+jSsT4WHEyWltnMJNxMkCWA2AUtyFLShbHIX03wkQgGXWptqGBMLgllRqvMB4FGCoQtBZdspZQvB8wxNEqo8aprIsTWhMqOqL4+Kq9VaorApWEmps0EnrClR6X7YayyCa42YFt/iQ0WJoFkG9g7KENCfSj4of6+jgCfIfKgEDfWxUtl2uLYjhhOVdWkEacvspJIQSI+yjhC8JsO7HfcFmZVoL1Cq4tMfNRcSPjSn1+K41rK1k2LRz5LKumD37ex0GEiCHyU5qENZjVWSHJdPA+X0TYeDdDmc3lhPZqYn2WHV+T2cP4EqYBx+X463wOwcp/U7xkTHTy6m7TY5LS0jqTgfxc26aMxs1Fv0KK6FAWtxVHTqJ6a6ea6AK84WxGnFOTkp8SaGMOlkMm2ygS10S6LOPX6S0zA2dVym11rE0zJmdmhTcVcWqYtOZfKTMwqyaf80q99niEvPxlyZXLK1aBQCvZMLaSrWaPelJxXYOHKaz0lhObZsz8QcyhWdjQvpcobe4/IlStGeOB2GJ2Ed3NOZDSgW8tCEGaIx9alt7w0GsG7Jo6zVW/D1AhB5ODXAq5UwZZIszqqAfQga66tC02ONbdzDFu5fkQB7UtmXJNCRCGZAbKSE6DCdAX7FGE0xehMyJi2rJj5kJusHW3BrlgDvpgu2YWJ7y1eRHpnNB1R1/A82+z612WElVffhTEKBj+dEgIa8UmomoRltcxNNSXi37WahnOAmWLo4aFbZoDYynJM0WxcSwzuvqoTGUa+orDWYsdqQpL3HqmFcMKMYiuG71dtPwiulOs5zgoSKgIRTWfIrpyO9hE+9T7F63Kg3wSSPQmiWZGQKZMrOBM4LbYqjEB6OKI6g9vhQOBABQ3tpWITgd2jiw7uCqyXa9ShC4vIBKyrr9Vjbs78jRACqBTWMB4oqLPDZ+8Ogdl06FWMx6/d0homgg0NrTV5x16PykIo1mFjjawejNKWcfhEuHC4LriMpl4kALtLgwgxOEtPhGOUymy0W+DZujk9C4wnSA9DMYLcpVQmTx1vTUuJ3TEI7tg1qC054KGc5kaVdrspMIMDSKNUkw8kUHI0CqIS6MqyTlbpokorGDUajCegNmIvQo4kTM7a0a3vQZBXqXA3+Ob5S2TbOD3c5M+T1nmHBpxv83L/PZDSwZ7E+e6+OnB07pexuek1rC9I81DOhLnyuQR6a/qcrRGZFXO2CJ167H3vy4MLPJvWY9fGHjX9rvnkB657YdWg3RXwKDPtk87K8bSV1M69du4cMWPb+UufdwHYHN9jRcp5owaS98daIlqdXN4zV9LfLW7D6XoP+fKf41778mf4vd47e0PfNcHRjfR5ddBHNqX1616fZtkZ56Mm4+u8t8/BCz4nKxMUXRpY2Lls+Ypi9QV/K356DY3ZT/wstA97apfnD71ednJtuzNtoP95rtL0hmf/NmM+vjUq9Ro7PPV5/9vwp26ebAs1DVpiOHYw6cXf4+5dX/mOOI+Jezvbwddufiv9d3+nry92uea98BhqaFqzZ8e3EmLX67ucoec7cpS0Xt5RdGuh9YVcZ33thX7vzl47Iz7fWzvmEDhxsGjD/hrW1NXfP/ZUUhW1ClwR8gXvRTPyVGl3MDvfFOPv6D75QrOzZoVWTR0XsbOlx5VdXS4+3blsz++vB2S9cjRo14ZkhZ3s2vzr4xbTc+bduTS+0omMX+D5kS3xTZ73+3CLlsmVRl5dKXl7Y+lpk1jcphy8vPrB/8ge6sb0Xr96Q+uH+pOSNPQO5eSeWPBP5R0vM9WABu4Vdvbvuq7dgNR8nmer60eMmU5FIRx2szDAdIAQcTHAYs0G+o1ImB0kw/4o30SoX0aggB5Y/VrZjCWKBJ8ueSGVlYILNm8pNyP+p9IoQ3LIXeqVa05TkPuA3uXCdq/mxAHM1AY1KdjqHpWkTImo+CdaPFBEipGYySwEKDkn4n4FbOmZGjbRTxI6fEtfPvPRnXvozL/3f4qVmne6x8lID/t/JSyuMBr35/5CYmh4/MdWRgDKbMRNhMhIGgw4jcb3RZDFbjEYdpqcs0f9JYmrBLdG6f4+YLn9ITJkJ1jZO2n/KinWjF3456Rv0FxmrYiuWDLb3KzuQPvJt02Gwu1/uCzfO9z4+96/I7hvYwbeXnw6khJWyhwaUnjm2qti9rfxoQLw9oT5i+kfnTx0NGGtmzBBnDpmZ3f+l2YYRrf43RuytvtbvVvPUk1RU3c3h3ZJOOfNy7hRv//jcGwm28KXDr+bWv/XmYv/uxpsjGq5HXJxfFzdgyu3lYWF753HzPIPuRuxYULU5+Y3GbfFb983o2mdFakIp1fJcRbhmeUqiadXzmTOHfhcZe+jcRysuDH/WPpB5NdzTG7zMXLuQ3zti4AGN6eSiMVPLx+60d/HfWH+y+xbfX/q9Yz3Ws3u3r3v2GHfUtlOM3dGtYdLZJ4+k3r5jP5R+6bexXx1y/D1nStN7Z9ZS23BDD8FJneh1YGUBcXDZ8z0vbSha/aQ0elD4xivOiC96N1G7l9aPti59Nmpu4nev5H0ydcGZG01HyhZOK2+KbFq0jL1ePoMquFzl/bZ52NvvPLEyeqpj55nC1uWnlgzaUXukv9Z8uOvGsuTmYzND3PJG2fcjGruGhf0TwS28Tw==
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
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
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
eNrtVnlwFFUaD2G5REIsBcQqoDPiwpL0pHuuzCRGyUEgIcmEXCaEMNvT/TrTSU93p49kJiEiuCiCizQrKsIWkgyTGMINMVyyGqIiN2ilIIAu4HLoLqyFQTn39WSCibhbbhX7x+7SVTPT733f+87f++Y3t74CiBLDc32aGE4GIkHKcCEtmVsvgnIFSPLv/G4gu3jKl2XPya1TROb4BJcsC1JsdDQhMHpeABzB6EneHV2BR5MuQo6G7wILAmZ8Tp7ynujzcrXODSSJKAGSLhYpqtaRPPTFyXChq4RHxkmI7AIIS8jQIcKBSgkhnLwiIyk8n0iIuihEJ/Is0NQVCYi6mmK44+YpwGpbJYKMmnhNiYNLHP5KsggIN1zQBCsBuCEDtwBzkxVRM4LpMW2P59lgOLJXCBinFS6Qvmbr7nssUq3jCHdAoQTIjkDsHlnToYBEiowQVNNNBjIiAhZUEJyMBNUQmhcRgpMqgchwJYE0A2WFR/SaCYEQoW1YeCngSBBhQUWZAV3LbtXAojtKmB00paup0coCm8SIgNLy+FFbK0+3Nu8sBaQMtWuKa+pdgKCgq9Mh4T4XL8nqut4tXE+QJIDVBBzJU9CHurakihGiEArQWm8aYU4cCFRFbSwDQEAJlqkA/q5T6gZCEFiGJDR5dKnEc03BNqNaLPeKG7VeohAUnKxuscMgElKjs7wQaxyC682YHt/gQSWZYDgWYgdlCRiPXwjId/QUCARZBo2gQRyr/q7D63rq8JK6OoMg7Tm9TBIi6VJXE6LbYtrcc19UOJlxA7U+Keted0Hhj+6MehzX2zb2Mix5OVJdHUDfe70OA1n0oiQPbairMD/J82UMUI9/63CQtMPpjnfl5LimOBIMXhfvTabKWYfXk+8uj3FO1Xsdk61JhVXMdLuSkZGdUlWG4jEGKxZjNtqMKK6HCetxVHIan0svEfhyvipPlEqr8vNTkywsYTEoZEahiasokSVDSWaB05SWPjXHnVApMAoW49Cn43QuabCms2VTssvzGG9pgtdjSszKw+gcfkpCZRwCo1MqGCrePN2TlVJu58lSj5PC8u15rufyKdqah4tZSrbRRXsmyVZXogHDU7Ae4RliTCgWjNCCmayY9qzrxgYLuBLZpdYZbXiDCCQBTg3woh+WTFakuT6IQ7D/k/rg9Ki1T/0RwsN8yRCT6q4UkYlCMBNiJ2XEgBlM8CvWbIk1mpDJGblNSUE3uT8LwY25IrybNIThpG7I15MuhSsDVGPSz4J9lwZ22EktfDiTUOAReAmgwajUpgI0u2tuoqnJm7tuFsqLJQTHVAXcqu9qQIZzkuG2BMXwzmsmoXPULal1sEDrgpJujDXCvGBFMRTDt2m3n4RXSgtc4EUZlQAJp7LsVY9HuQmPdp/ijbjZaIFFjkMYjmQVCuQozmTeDX1KcYgARxRPUNs9KByIgGXcDGxC4Ds48eFdwbUWtdyrIfNlgJPUBiPW9bzfU0UEmgctjbuGfDb47Px5pW5bBk3HFmPc3ltNAj0CqrO4pZZ75UETtZjU5OlWRhlKPT4WLhwmksZpK20iSIvNRtFGK4UbbSYLjZltZovNTK9PSkGTCNIF0JwA2tT65MLMhIzUpOYCtCdsUHtgwkM5x0scQ9P+HCDC1qiNJMsrFByNIvBDW9kJheoWK0lZcRN0TQMDRhNGdNJz2Ru6rd0FmU+bq4E/xzn+rnHe1ufEmIUDQwJPX/i5c4fN3sd1YOE7v46cH98xj41sris65ptS7Rrss5mUiMOfpdGJ5XlivzjzzcvzRjc/9nFs+J5FNF35xo2JAw//Zk/oZymbJqQZb3uurbjUtn7WD1e+n7f7C3bh7GvL6xeMPnnqUAf6amn/tSun/V39LmHatCeojOZ5S5P2FqzJHH4MtLS8XTx5SFbWAr//q6dee/IJeytjCnvzrHD6kRjfsKr5bZcnDozoPFD4nivnZELUh0c2Zr9zzTB5vv3ZQX0WpRf3ubirMwptbRgwrDClqNIdSfYPeXRZcipZdbazY+jfajKPvnXOsinOdKT90oDdTz9/3bLyWLzto/o5jshb0y1DwNK8+ln9v3939Mplg/rkDx8SMznz7dOVs1JGhPr2rQlF29e2Pt9vduTVrV8+dGbY43svFBacrIpoz934fbr85KmHwq6kXAifsf3OSOoctmanqdhTfEs48dKqnfKWzpI/J/rXdi7fs+rqgcWtTS/WFZ3t+/lyJfFiw3gbUZVQtG/mgenTLowpGpr5xqhP9hy9+l0bJSegqepqrrmm6rdpjUP88caH+/d9/9rOsUfC3nXfznzqwut/2l31kW7vlgNHlmbGrH9kk2EZPc3qcNh/bRwY9sHsQAP7hnSOao18A3bzfpKp0FP3m0xFIT1tcArL9lAh4GCCw5gL8B2NMjlIgv1XvInRuIhOU3IkmzgPYE1pSpZclpeUabHwU+DAAtN/Kb0ixBLFDaPSvOmqZ9zlNzPgeobunyU4Q1ej08hO77R0XUJEqyfBeZFSBZ4VCUYCFIIjiQzLQsMIxbPwNksRPYukJd0reccvSfEBRX1AUR9Q1P8tihpjNN5fimr+r6Wopv9DimrB7ztFNVNGinY6DcCJAYKkbARmMmBOJ4bRuAGLseD/QYpqxkiCsP57FHXJTynqtG6KOvzpojlU3KJvigaPvRYyaU7t+F8tmJEqj9d/ULwqLPROdcQPhWNHjKatm407xox8Qcz/68RlMxeOrD20O/7y2rI/nNrZ4hq6pvH6VzM/bu0833Gl5URDm+8d+rUUfeOApKSr4xd9+Ipl0NYvz01/Bl1z5mL51uJDeyYOvzX269pzZZ++2Sb+cUTezBU1GfaDyJpRNw+GhDzzl8oFj0eN/ixsQVu/kYsLKxsq2u0hE9SV4auXjVoYVjs85mhW+MPSpTHxc9vnRoVfiX6Rcz3mibi+7lFr/4tbX7g6MMc46vSq9odvqP2WVLw6PM214tqi0IJvZ457vmXl6xtGFdontV64NfLZmXXVl5y1Lx26OYwvFk62DzLnvOXclJVrrN4fcnt/5BN3ytY6ayPS0KsvEYam5nFxyo69v4+K9i12zX55xOdnBu84Ues6/wP38aeuY3W7Uhd+s67j/CpH4Yrr2w7XiHsPZi74olBoKBoRm45vO/b5iYLoDmUj0wy2ZR/HbwzoopY7Xnlz9fuhISH/APhftKQ=
@@ -1 +1 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +0,0 @@
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
@@ -0,0 +1 @@
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
@@ -1 +1 @@
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
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
eNqFVHtsFEUcLi2QWolGQAEhuJxFMXbvbvce7Z0xUA+EAqW1PVJbxDLsTm+X7u0uO7Ol11qTlkciNYGFiIomEHvclaOUK48QBGJFixoBK9jIGamgFCPYPwTFB9I62155hAb3j8nO/F7f7/t9Mw3RKqghUZFHtIgyhhrgMNkgoyGqwZU6RHhNJAixoPDhwoJif5OuiYnpAsYq8tpsQBWtQMaCpqgiZ+WUoK2KsQUhQiAAUXi5wocSn9ZagqC6HCuVUEYWL8XYWWcWZRlyIidLai2aIkHyZ9ER1CzEyikEiYzNo1UCwJSIqGCIkkEQzrTULTXDFR5KppmTgM5D2kG7aKTIMsQ0SwrY3azdzINCCMOg6Veq6BTQIAUoAUpqhS5RACERYYKewkCqFOUAhRUKC5AyQVipxWSlRLlCIcF1UQECnrC0ISwoCBtt9/S9B3AcVDENZU7hSS5jd6BGVLMoHlZIAMMYZ2IbINaIVUKo0kASq2BkMMqIA1WVRA6YdtsK0kdLkgAah1R4rzlmQqQJfTI2DuYO4bAVhsiYZMpudTitbLyaJt2JskSIpiVAIEXUAfvhOw0q4CpJHjopASMyGNx6p4+CjB35gCsovisl0DjB2AG0oNu5785zTZexGIRG1Fd4b7mk8XY5h5VhrJ62uxKjkMwZOyqAhGDbLZJvhcTIfB203U3bmdYhliQoB7BgNDEed7MGkUrkC1dHSEqso4YwmQg88Xk0qbgPChYMTbM7ZWx4NpmOcdQv6FmU3UEVcJgy9UMWr8vhdXqoufn+Fl+yjH/YYbT5NSCjCjKQOUPDj3KCLldCPuYbduwJy+22NFJfEoMippO3jQzL3Bphp91uTzx1X08NBglrZsWww+Px/E9ewgzExn6zP5qx03aHf7BLl7MsQQ0XOXhnk3giJh6CKPM+nrfxDHlT9/UeHo/TUxZLgqZF3jhC/svtTAmnqKzuy0H64nw0b25hkYt1hFwHqmlOUnSexuTdgvSAIKqxkaAYwPJ2JwNZxuF2uLPZHDbb6XKwHg8DuWwXzzZVicCIMVaGCihKQIJ7fC/SPsAJkC4ekI0RnV26KDc/z9fyMl2kLFcIf35AeJYVGUaKoUbkaMQGSpMLrsEICS/KLTX253BcBc9CJoeHIIfzuOk5JUXxIQHdEkjYfB0G3sd6IlONHHWM2P5EY3rKwJcmbcpVxuWOWdu/vmeB9b0ZIzO6umvSi9b4X2pNn3PqzX2TvJU2rmD+2J6Pms6lrnjk6QXXa7dtm4s+Ht/W0f1h79WTdczRm/921mWVn/rh2ys39iU+iT40+fcz7U3rTzZzUlyYOpnfciJvyuRfXdrxx3yRx0N9Va87sh8F2/Zs2bXqyz93TXdP9L/f0Xaz5u80vc/zXfT0j6OFzpalXpR2+HK8vplFOZ1G/cWRedfLaq6GU/efuayeLpvV/POo7ydtbD83ms0r2cm9s9zVv+yFZQ51bX3jzsK+h115Cz8b/yxsPt6xff7UpV8ERvu+5tZPmfbAX/XTmqccOnuxvH3rsRGbciZkjFt57Uh2Yl5P/fS91WmZvynxY4vyXu05O6EXzHhtYVlvV8aS9oqZ3c83bjzzpO+rn1xdE9Ol8xuvP7OwILWWboq/+83aC2+gzsAhdKrp2i+z+sZeynhu3SwPeuX8H0tWX/JKD27euawxa+WVA+l9Uyf2N2fVt721Fa3W/8nsHbV5TNfB1g1Z63ovR3J39+3Vb1youTkqJaW/Py0ls7Tk7YbUlJT/AFgF6ww=
@@ -1 +0,0 @@
eNqFVG1oHEUYTo0JDYq10j8WpcPRFsTM3e59hFwQJb3EKjVNTK42qbRhMvve7Ta7O5vduSTXWiSxFD8wZfOjFVGR5nIn17RJMLZCLFKtUGiLSqv1IPhDEQwitFb7wx/W2ctdk5IY98cyM+/X8z7vMzOc6wfb0Zi5ZkIzOdiEcrFx3OGcDX0pcPjhrAFcZUqmrbUjPpaytcIWlXPLaQgEiKX5iclVm1ka9VNmBPrlgAGOQ5LgZHqYki58f9BnkMFuznrBdHwNSJaC4VrkKzuJk5cP+mymg1j5Ug7YPmGlTCAxuXc0oBKONAcZaWQSA57xHdrrhTMFdM9MdZJSAIdwBDvMNIHjoCgg1QUlL4+TdjgYnl8XSyFiAyJIBd1KpHREHEdzuECPONF7NTOJOENcBeSB8KNd4o80M8EaFpZecQ9Ho65R8B3KqUAUwdvRjMoc7k4vY2KSUAoWx2BSpojs7qnkAc2qRQokdMIhTz20RardfC+AhYmu9UN2IcqdIpYlChHPHtgvOpsoUYJ52oLl5rwHGgtCTe6ebSzjCLSlxeBMJPlDYX9wahCLfjVTF9RjnQhIWaton11qsAjtFXlwSRRudiH49FIf5rjjLYS2dtyTkthUdceJbdSFP156bqdMrhng5mJty8uVjIvlQn5Z9ken70nspE3qjieI7sD0XZLvhuTFxENYqsOSfLrMkg5mkqvuWFAOf2SDYwlBw2tZkZKnnOGMmAhcvpgrafBE647yNH+seCTTJKbjnourqVokhVAr5chTlPg1REINEQltb4lPxEpl4isOYzpuE9NJiIE0l4efo2rK7AUlH1tx7AXfYlu2qK9rhsZx6f6JYXlbNxOWJKmwdVVPGwzBmlcxE4pGo/+TVzAD3J3x+sOyhKVQvNRleE8BrRS5cItLeLIeHoFo8yqei3jK3mhV7//AI+3Jl0BjTXE/E+tuSe6EoHWgOVq/bXd4G0vY27e92NXSEv5kEFOdpRTMxUsGuCiIQe4WEESVUDQSkaOJqBwJBykoSg/QHhqReuqjdYo81q8RNy/7ZZRkLKnDZOxZHCNUBdxRlI2ba+ra2djyfGyiE7ezHib4ixPBs8lMyHaALeTo5oulxQW3ISvC2xu73Jl6ShNKEIAqEbmeRutw8+72qbKA7gok470OxRdzSMjUFkcX5ja9tbai+FW+MHJl/5fSw4e7v7rc5R+5eqzp7OanN/wUax1qf3P97dHXLz55fXb4/p8HAg/hP8M3rqwLnR+Y+vWXuQ0n//40Pdnafurkdzffvn08ttf4q7ry3LqZzHMX3q+ppg8+YPn2XZv+4sOjj1d1zpwfH/kj9c/oDmXrlUsnXvHvu3RftaFseemHr+dZfP7MtU09N9dffWf6tydujf4+zOZrbh0fUh97132v6UhV33V146OfH/lmwGfWHOtYWz3fd3Xnjac2G+F9b1Sdmdt4IXPn28QHr4o27typrFgfmK2oXFNR8S83QI1c
@@ -1 +0,0 @@
eNqFVH9oG1Ucb90fDmR1DgTR4R5Bq2hfcpdLmqRs1Jqm1dW2oY2rrWh9uXvJXXP37nb30iXO/mE3HKJ03mAqxTFZ02SEbmtd58AfYGVDpYL7Y1IiTMU/RHAynMgQOuq7NGk7Wuv9cbz3vr8+38/3895oYRiblqKT2imFUGwikbKNZY8WTLw/jS16OK9hKutSLtrdG5tIm0rpUZlSw2ryeJChuBGhsqkbiugWdc0zzHs0bFkoia1cXJeypasHXRrKDFI9hYnlagI85/U1AFfViZ28eNBl6ipmK1fawqaLWUWdISHUOTogIwoUC2hZQJCGm10jLznhuoRVxyyqKC1hKEA/tHRCMIVeVoBr9HJOHitrUaw5fv16GiATAwRkrBqJtAqQZSkWZegBRWpKIUlAdUBlDBwQbvA8+wOFJPSm5aVT3MHxtB53jRRkjCTG2tGcrFvUnlnHwzkkitigEBNRl1hu+0zyVcVoABJOqIjiouhgLRNtF1MYGxCpyjDOL0fZ08gwVEVEjt0zxPqaqhACadbA681FBzJkdBJqX2yp4vBEs2xsBHBuwef2Tmcg61YhKiMeqohByhtl+6drDQYSUywPrEjCzi8Hn13ro1v2ZCcSu3vvSIlMUbYnkak1+s6vPTfThCoatgvh6PpyFeNqOcHN8+7QzB2JrSwR7ckEUi08s0LySkiRzVuAXCPk+LNVllRMklS2J7y897SJLYPJGR/Ks5Q0bY3m2ETwt18XKgo81d1RneaPNTtyrWw69ucxOd0AOAF0ixQ4emK/Jr/Q5OdAe2dsKlwpE9twGDMxExErwQYSqQ6/IMppksJSMbzh2Euu1bZMVl9VNIXCyu1jw3K2ds7HcVypflNPE2uMNadiTgiFQv+TlzGDqT3r9Ad5DnJCrNKlb6AENopcvsMVPHkHD0P0yCaeq3iq3mBT7//Aww0UK6ChItmfsfUgx++LdkXkwHBHsN3Xt5eIfDJMwpF9FzJQVPW0BCl7xzAsCyJD7RLgeMQn4qGgXwj4/VJc8kteMS7FuaAQ53FA8k4MK8gu8m4eJHU9qeJz4TYYRqKMYW9ZNnahtb+rpfPZ8NQLsEeP64y/GGI8E53gfC82mRztYrk0u+AmzrPwnpZ+ezYoignJi+PBkBAIiqFGGOnrma4KaEUgOed1KL+XrzOZmuzo0g+73tpaU/62PDfW0XHpqe2Hl/ae/qs1/tp32p6Ge0bbTz7zfuf4Ax/Qr+hRads/N45F5nrn+u4dalv4YmLh4ye6bjx8a7D7p2to6MDsNS1/5NYft+fv33mduzLu+v7Kjp1tbz65tXhi4JVDYc+2SOYqn3vj5vlFVx+qn58/tdvjnr+r7sL0fQ8t/r4YGPKd+Xsis7vjQaFuDz6RuPhR89zNXz+sD/t7oiizPXB80v4z9fhI/S/jP4cu//bO2GO33x47Ih3/5u6ZL98L1B273vzJy+/uYk0sLW2p6bu8sL+2tqbmX44/ioo=
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
+216
View File
@@ -0,0 +1,216 @@
# API Concepts
This page describes the high-level concepts of the LangGraph Cloud API. The conceptual guide of LangGraph (Python library) is [here](../../concepts/high_level.md).
## Data Models
The LangGraph Cloud API consists of a few core data models: [Assistants](#assistants), [Threads](#threads), [Runs](#runs), and [Cron Jobs](#cron-jobs).
### Assistants
When building agents, it is fairly common to make rapid changes that *do not* alter the graph logic. For example, simply changing prompts or the LLM selection can have significant impacts on the behavior of the agents. Assistants offer an easy way to make and save these types of changes to agent configuration. This can have at least two use-cases:
* Assistants give developers a quick and easy way to modify and version graph version for experimentation.
* Assistants can be modified via LangGraph Studio, offering a no-code way to configure agents (e.g., for business users).
#### Configuring Assistants
In practice, an assistant is just an *instance* of a graph with a specific configuration. Because of this, multiple assistants can reference the same graph but can contain different configurations, such as prompts, models, and other graph configuration options. The LangGraph Cloud API provides several endpoints for creating and managing assistants. See the [API reference](../reference/api/api_ref.html#tag/assistantscreate) and [this how-to](../how-tos/configuration_cloud.md) for more details on how to create assistants.
#### Versioning Assistants
![assistant versions](./assistant_version.png)
Once you've created an assistant, you can save and version it to track changes to the configuration over time. You can think about this at three levels:
1) The graph lays out the general agent application logic
2) The agent configuration options represent parameters that can be changed
3) Assistant versions save and track specific settings of the agent configuration options
For example, if you have an agent that helps for planning trips, you can create a new assistant *for each user* that passes specific user preferences (e.g., desired airline and car service). As each user interacts with their own assistant, assistant versions can be saved that track the specific desires of the user. Read [this how-to](../how-tos/assistant_versioning.md) to learn how you can use assistant versioning through both the [Studio](../how-tos/index.md/#langgraph-studio) and the SDK.
### Threads
A thread contains the accumulated state of a group of runs. If a run is executed on a thread, then the [state][state] of the underlying graph of the assistant will be persisted to the thread. A thread's current and historical state can be retrieved. To persist state, a thread must be created prior to executing a run.
The state of a thread at a particular point in time is called a checkpoint.
For more on threads and checkpoints, see this section of the [LangGraph conceptual guide](../../concepts/low_level.md#persistence).
The LangGraph Cloud API provides several endpoints for creating and managing threads and thread state. See the [API reference](../reference/api/api_ref.html#tag/threadscreate) for more details.
### Runs
A run is an invocation of an assistant. Each run may have its own input, configuration, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a thread.
The LangGraph Cloud API provides several endpoints for creating and managing runs. See the [API reference](../reference/api/api_ref.html#tag/runscreate) for more details.
### Cron Jobs
It's often useful to run graphs on some schedule. LangGraph Cloud supports cron jobs, which run on a user defined schedule. The user specifies a schedule, an assistant, and some input. After than, on the specified schedule LangGraph cloud will:
- Create a new thread with the specified assistant
- Send the specified input to that thread
Note that this sends the same input to the thread every time. See the [how-to guide](../how-tos/cron_jobs.md) for creating cron jobs.
The LangGraph Cloud API provides several endpoints for creating and managing cron jobs. See the [API reference](../reference/api/api_ref.html#tag/runscreate/POST/threads/{thread_id}/runs/crons) for more details.
## Features
The LangGraph Cloud API offers several features to support complex agent architectures.
### Streaming
Streaming is critical for making LLM applications feel responsive to end users. When creating a streaming run, the streaming mode determines what data is streamed back to the API client. The LangGraph Cloud API supports five streaming modes.
- `values`: Stream the full state of the graph after each [super-step](https://langchain-ai.github.io/langgraph/concepts/low_level/#graphs) is executed. See the [how-to guide](../how-tos/stream_values.md) for streaming values.
- `messages`: Stream complete messages (at the end of node execution) as well as tokens for any messages generated inside a node. This mode is primarily meant for powering chat applications. This is only an option if your graph contains a `messages` key. See the [how-to guide](../how-tos/stream_messages.md) for streaming messages.
- `updates`: Streams updates to the state of the graph after each node is executed. See the [how-to guide](../how-tos/stream_updates.md) for streaming updates.
- `events`: Stream all events (including the state of the graph) that occur during graph execution. See the [how-to guide](../how-tos/stream_events.md) for streaming events. This can be used to do token-by-token streaming for LLMs.
- `debug`: Stream debug events throughout graph execution. See the [how-to guide](../how-tos/stream_debug.md) for streaming debug events.
You can also specify multiple streaming modes at the same time. See the [how-to guide](../how-tos/stream_multiple.md) for configuring multiple streaming modes at the same time.
See the [API reference](../reference/api/api_ref.html#tag/runscreate/POST/threads/{thread_id}/runs/stream) for how to create streaming runs.
Streaming modes `values`, `updates`, and `debug` are very similar to modes available in the LangGraph library - for a deeper conceptual explanation of those, you can see the LangGraph library documentation [here](../../concepts/low_level.md#streaming).
Streaming mode `events` is the same as using `.astream_events` in the LangGraph library - for a deeper conceptual explanation of this, you can see the LangGraph library documentation [here](../../concepts/low_level.md#streaming).
#### `mode="messages"`
Streaming mode `messages` is a new streaming mode, currently only available in the API. What does this mode enable?
This mode is focused on streaming back messages. It currently assumes that you have a `messages` key in your graph that is a list of messages. Assuming we have a simple react agent deployed, what does this stream look like?
All events emitted have two attributes:
- `event`: This is the name of the event
- `data`: This is data associated with the event
Let's run it on a question that should trigger a tool call:
```python
thread = await client.threads.create()
input = {"messages": [{"role": "user", "content": "what's the weather in sf?"}]}
events = []
async for event in client.runs.stream(
thread["thread_id"],
assistant_id="agent", # This may need to change depending on the graph you deployed
input=input,
stream_mode="messages",
):
print(event.event)
```
```shell
metadata
messages/complete
messages/metadata
messages/partial
...
messages/partial
messages/complete
messages/complete
messages/metadata
messages/partial
...
messages/partial
messages/complete
end
```
We first get some `metadata` - this is metadata about the run.
```python
StreamPart(event='metadata', data={'run_id': '1ef657cf-ae55-6f65-97d4-f4ed1dbdabc6'})
```
We then get a `messages/complete` event - this a fully formed message getting emitted. In this case,
this was the just the input message we sent in.
```python
StreamPart(event='messages/complete', data=[{'content': 'hi!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '833c09a3-bb19-46c9-81d9-1e5954ec5f92', 'example': False}])
```
We then get a `messages/metadata` - this is just letting us know that a new message is starting.
```python
StreamPart(event='messages/metadata', data={'run-985c0f14-9f43-40d4-a505-4637fc58e333': {'metadata': {'created_by': 'system', 'run_id': '1ef657de-7594-66df-8eb2-31518e4a1ee2', 'graph_id': 'agent', 'thread_id': 'c178eab5-e293-423c-8e7d-1d113ffe7cd9', 'model_name': 'openai', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca', 'langgraph_step': 1, 'langgraph_node': 'agent', 'langgraph_triggers': ['start:agent'], 'langgraph_task_idx': 0, 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o', 'ls_model_type': 'chat', 'ls_temperature': 0.0}}})
```
We then get a BUNCH of `messages/partial` events - these are the individual tokens from the LLM! In the case below, we can see the START of a tool call.
```python
StreamPart(event='messages/partial', data=[{'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_w8Hr8dHGuZCPgRfd5FqRBArs', 'function': {'arguments': '', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-985c0f14-9f43-40d4-a505-4637fc58e333', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_w8Hr8dHGuZCPgRfd5FqRBArs', 'error': None}], 'usage_metadata': None}])
```
After that, we get a `messages/complete` event - this is the AIMessage finishing. It's now a complete tool call:
```python
StreamPart(event='messages/complete', data=[{'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_w8Hr8dHGuZCPgRfd5FqRBArs', 'function': {'arguments': '{"query":"current weather in San Francisco"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-985c0f14-9f43-40d4-a505-4637fc58e333', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_w8Hr8dHGuZCPgRfd5FqRBArs'}], 'invalid_tool_calls': [], 'usage_metadata': None}])
```
After that, we get ANOTHER `messages/complete` event. This is a tool message - our agent has called a tool, gotten a response, and now inserting it into the state in the form of a tool message.
```python
StreamPart(event='messages/complete', data=[{'content': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'San Francisco\', \'region\': \'California\', \'country\': \'United States of America\', \'lat\': 37.78, \'lon\': -122.42, \'tz_id\': \'America/Los_Angeles\', \'localtime_epoch\': 1724877689, \'localtime\': \'2024-08-28 13:41\'}, \'current\': {\'last_updated_epoch\': 1724877000, \'last_updated\': \'2024-08-28 13:30\', \'temp_c\': 23.3, \'temp_f\': 73.9, \'is_day\': 1, \'condition\': {\'text\': \'Partly cloudy\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/116.png\', \'code\': 1003}, \'wind_mph\': 15.0, \'wind_kph\': 24.1, \'wind_degree\': 310, \'wind_dir\': \'NW\', \'pressure_mb\': 1014.0, \'pressure_in\': 29.93, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 57, \'cloud\': 25, \'feelslike_c\': 25.0, \'feelslike_f\': 77.1, \'windchill_c\': 20.9, \'windchill_f\': 69.6, \'heatindex_c\': 23.3, \'heatindex_f\': 74.0, \'dewpoint_c\': 12.9, \'dewpoint_f\': 55.2, \'vis_km\': 16.0, \'vis_miles\': 9.0, \'uv\': 6.0, \'gust_mph\': 19.5, \'gust_kph\': 31.3}}"}]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'tavily_search_results_json', 'id': '0112eba5-7660-4375-9f24-c7a1d6777b97', 'tool_call_id': 'call_w8Hr8dHGuZCPgRfd5FqRBArs'}])
```
After that, we see the agent doing another LLM call and streaming back a response. We then get an `end` event:
```python
StreamPart(event='end', data=None)
```
And that's it! This is more focused streaming mode specifically focused on streaming back messages. See this [how-to guide](../how-tos/stream_messages.md) for more information.
### Human-in-the-Loop
There are many occasions where the graph cannot run completely autonomously. For instance, the user might need to input some additional arguments to a function call, or select the next edge for the graph to continue on. In these instances, we need to insert some human in the loop interaction, which you can learn about in the [human in the loop how-tos](../how-tos/index.md#human-in-the-loop).
### Double Texting
Many times users might interact with your graph in unintended ways. For instance, a user may send one message and before the graph has finished running send a second message. To solve this issue of "double-texting" (i.e. prompting the graph a second time before the first run has finished), LangGraph has provided four different solutions, all of which are covered in the [Double Texting how-tos](../how-tos/index.md#double-texting). These options are:
- `reject`: This is the simplest option, this just rejects any follow up runs and does not allow double texting. See the [how-to guide](../how-tos/reject_concurrent.md) for configuring the reject double text option.
- `enqueue`: This is a relatively simple option which continues the first run until it completes the whole run, then sends the new input as a separate run. See the [how-to guide](../how-tos/enqueue_concurrent.md) for configuring the enqueue double text option.
- `interrupt`: This option interrupts the current execution but saves all the work done up until that point. It then inserts the user input and continues from there. If you enable this option, your graph should be able to handle weird edge cases that may arise. See the [how-to guide](../how-tos/interrupt_concurrent.md) for configuring the interrupt double text option.
- `rollback`: This option rolls back all work done up until that point. It then sends the user input in, basically as if it just followed the original run input. See the [how-to guide](../how-tos/rollback_concurrent.md) for configuring the rollback double text option.
### Stateless Runs
All runs use the built-in checkpointer to store checkpoints for runs. However, it can often be useful to just kick off a run without worrying about explicitly creating a thread and without wanting to keep those checkpointers around. Stateless runs allow you to do this by exposing an endpoint that:
- Takes in user input
- Under the hood, creates a thread
- Runs the agent but skips all checkpointing steps
- Cleans up the thread afterwards
Stateless runs are still retried as regular retries are per node, while everything still in memory, so doesn't use checkpoints.
The only difference is in stateless background runs, if the task worker dies halfway (not because the run itself failed, for some external reason) then the whole run will be retried like any background run, but
- whereas a stateful background run would retry from the last successful checkpoint
- a stateless background run would retry from the beginning
See the [how-to guide](../how-tos/stateless_runs.md) for creating stateless runs.
### Webhooks
For all types of runs, langgraph cloud supports completion webhooks. When you create the run you can pass a webhook URL to be called when the completes (successfully or not). This is especially useful for background runs and cron jobs, as the webhook can give you an indication the run has completed and you can perform further actions for your appilcation.
See this [how-to guide](../how-tos/webhooks.md) to learn about how to use webhooks with LangGraph Cloud.
## Deployment
The LangGraph Cloud offers several features to support secure and robost deployments.
### Authentication
LangGraph applications deployed to LangGraph Cloud are automatically configured with LangSmith authentication. In order to call the API, a valid <a href="https://docs.smith.langchain.com/how_to_guides/setup/create_account_api_key#api-keys" target="_blank">LangSmith API key</a> is required.
### Local Testing
Before deploying your app in production to LangGraph Cloud, you may wish to test out your graph locally in order to ensure that everything is running as expected. Luckily, LangGraph makes this easy for you through use of the LangGraph CLI. Read more in this [how-to guide](../deployment/test_locally.md) or look at the [CLI reference](../reference/cli.md) to learn more.
Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

+28
View File
@@ -0,0 +1,28 @@
# Cloud Concepts
This page describes the high-level concepts of the LangGraph Cloud deployment.
## Deployment
A deployment is an instance of a LangGraph API. A single deployment can have many [revisions](#revision). When a deployment is created, all of the necessary infrastructure (e.g. database, containers, secrets store) are automatically provisioned. See the [architecture diagram](#architecture) below for more details.
See the [how-to guide](../deployment/cloud.md#create-new-deployment) for creating a new deployment.
## Revision
A revision is an iteration of a [deployment](#deployment). When a new deployment is created, an initial revision is automatically created. To deploy new code changes or update environment variable configurations for a deployment, a new revision must be created. When a revision is created, a new container image is built automatically.
See the [how-to guide](../deployment/cloud.md#create-new-revision) for creating a new revision.
## Asynchronous Deployment
Infrastructure for [deployments](#deployment) and [revisions](#revision) are provisioned and deployed asynchronously. They are not deployed immediately after submission. Currently, deployment can take up to several minutes.
## Architecture
!!! warning "Subject to Change"
The LangGraph Cloud deployment architecture may change in the future.
A high-level diagram of a LangGraph Cloud deployment.
![diagram](langgraph_cloud_architecture.png)

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 157 KiB

+6 -6
View File
@@ -11,7 +11,7 @@ LangGraph Cloud is available within <a href="https://www.langchain.com/langsmith
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
1. In the left-hand navigation panel, select `LangGraph Cloud`. The `LangGraph Cloud` view contains a list of existing LangGraph Cloud deployments.
1. In the left-hand navigation panel, select `Deployments`. The `Deployments` view contains a list of existing LangGraph Cloud deployments.
1. In the top-right corner, select `+ New Deployment` to create a new deployment.
1. In the `Create New Deployment` panel, fill out the required fields.
1. `Deployment details`
@@ -38,7 +38,7 @@ When [creating a new deployment](#create-new-deployment), a new revision is crea
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
1. In the left-hand navigation panel, select `LangGraph Cloud`. The `LangGraph Cloud` view contains a list of existing LangGraph Cloud deployments.
1. In the left-hand navigation panel, select `Deployments`. The `Deployments` view contains a list of existing LangGraph Cloud deployments.
1. Select an existing deployment to create a new revision for.
1. In the `Deployment` view, in the top-right corner, select `+ New Revision`.
1. In the `New Revision` modal, fill out the required fields.
@@ -56,7 +56,7 @@ Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmi
Build and deployment logs are available for each revision.
Starting from the `LangGraph Cloud` view...
Starting from the `Deployment` view...
1. Select the desired revision from the `Revisions` table. A panel slides open from the right-hand side and the `Build` tab is selected by default, which displays build logs for the revision.
1. In the panel, select the `Deploy` tab to view deployment logs for the revision.
@@ -69,7 +69,7 @@ Interrupting a revision will stop deployment of the revision.
!!! warning "Undefined Behavior"
Interrupted revisions have undefined behavior. This is only useful if you need to deploy a new revision and you already have a revision "stuck" in progress. In the future, this feature may be removed.
Starting from the `LangGraph Cloud` view...
Starting from the `Deployment` view...
1. Select the menu icon (three dots) on the right-hand side of the row for the desired revision from the `Revisions` table.
1. Select `Interrupt` from the menu.
@@ -79,13 +79,13 @@ Starting from the `LangGraph Cloud` view...
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
1. In the left-hand navigation panel, select `LangGraph Cloud`. The `LangGraph Cloud` view contains a list of existing LangGraph Cloud deployments.
1. In the left-hand navigation panel, select `Deployments`. The `Deployments` view contains a list of existing LangGraph Cloud deployments.
1. Select the menu icon (three dots) on the right-hand side of the row for the desired deployment and select `Delete`.
1. A `Confirmation` modal will appear. Select `Delete`.
## Deployment Settings
Starting from the `LangGraph Cloud` view...
Starting from the `Deployment` view...
1. In the top-right corner, select the gear icon (`Deployment Settings`).
1. Update the `Git Branch` to the desired branch.
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

+12 -18
View File
@@ -8,25 +8,19 @@ Testing locally ensures that there are no errors or conflicts with Python depend
Install the proper packages:
```shell
pip install langgraph-cli
```
=== "pip"
```bash
pip install -U langgraph-cli
```
=== "Homebrew (macOS only)"
```bash
brew install langgraph-cli
```
Ensure you have an API key, which you can create from the [LangSmith UI](https://smith.langchain.com) (Settings > API Keys). This is required to authenticate that you have LangGraph Cloud access. After you have saved the key to a safe place, place the following line in your `.env` file:
Ensure you have an API key, which you can create from the LangSmith UI (Settings > API Keys). This is required to authenticate that you have LangGraph Cloud access. After you have saved the key to a safe place, place the following line in your `.env` file:
```python
LANGSMITH_API_KEY = *********
LANGCHAIN_API_KEY = *********
```
## Start the API server
Once you have installed the CLI, you can run the following command to start the API server for local testing:
Once you have downloaded the CLI, you can run the following command to start the API server for local testing:
```shell
langgraph up
@@ -54,7 +48,7 @@ You can either initialize by passing authentication or by setting an environment
from langgraph_sdk import get_client
# only pass the url argument to get_client() if you changed the default port when calling langgraph up
client = get_client(url=<DEPLOYMENT_URL>,api_key=<LANGSMITH_API_KEY>)
client = get_client(url=<DEPLOYMENT_URL>,api_key=<LANGCHAIN_API_KEY>)
# Using the graph deployed with the name "agent"
assistant_id = "agent"
thread = await client.threads.create()
@@ -66,7 +60,7 @@ You can either initialize by passing authentication or by setting an environment
import { Client } from "@langchain/langgraph-sdk";
// only set the apiUrl if you changed the default port when calling langgraph up
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <LANGSMITH_API_KEY> });
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <LANGCHAIN_API_KEY> });
// Using the graph deployed with the name "agent"
const assistantId = "agent";
const thread = await client.threads.create();
@@ -78,13 +72,13 @@ You can either initialize by passing authentication or by setting an environment
curl --request POST \
--url <DEPLOYMENT_URL>/threads \
--header 'Content-Type: application/json'
--header 'x-api-key: <LANGSMITH_API_KEY>'
--header 'x-api-key: <LANGCHAIN_API_KEY>'
```
#### Initialize with environment variables
If you have a `LANGSMITH_API_KEY` set in your environment, you do not need to explicitly pass authentication to the client
If you have a `LANGCHAIN_API_KEY` set in your environment, you do not need to explicitly pass authentication to the client
=== "Python"
@@ -154,7 +148,7 @@ Now we can invoke our graph to ensure it is working. Make sure to change the inp
}
```
=== "CURL"
=== "CURL"
```bash
curl --request POST \
@@ -189,4 +183,4 @@ Now we can invoke our graph to ensure it is working. Make sure to change the inp
'
```
If your graph works correctly, you should see your graph output displayed in the console. Of course, there are many more ways you might need to test your graph, for a full list of commands you can send with the SDK, see the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) and [JS/TS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/) references.
If your graph works correctly, you should see your graph output displayed in the console. Of course, there are many more ways you might need to test your graph, for a full list of commands you can send with the SDK, see the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) and [JS/TS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/) references.
@@ -1,86 +1,39 @@
# LangGraph Studio
# Studio FAQs
!!! info "Prerequisites"
- [LangGraph Platform](./langgraph_platform.md)
- [LangGraph Server](./langgraph_server.md)
LangGraph Studio offers a new way to develop LLM applications by providing a specialized agent IDE that enables visualization, interaction, and debugging of complex agentic applications.
With visual graphs and the ability to edit state, you can better understand agent workflows and iterate faster. LangGraph Studio integrates with LangSmith allowing you to collaborate with teammates to debug failure modes.
![](img/lg_studio.png)
## Features
The key features of LangGraph Studio are:
- Visualizes your graph
- Test your graph by running it from the UI
- Debug your agent by [modifying its state and rerunning](human_in_the_loop.md)
- Create and manage [assistants](assistants.md)
- View and manage [threads](persistence.md#threads)
- View and manage [long term memory](memory.md)
- Add node input/outputs to [LangSmith](https://smith.langchain.com/) datasets for testing
## Types
### Desktop app
LangGraph Studio is available as a [desktop app](https://studio.langchain.com/) for MacOS users.
While in Beta, LangGraph Studio is available for free to all [LangSmith](https://smith.langchain.com/) users on any plan tier.
### Cloud studio
If you have deployed your LangGraph application on LangGraph Platform (Cloud), you can access the studio as part of that
### Development server
LangGraph CLI also contains a command for running an in-memory development server that can be used to connect a local LangGraph app with the studio.
See [instructions here](../cloud/reference/cli.md#dev) for more information.
The way this works is that it runs inside your local environment.
It will spin up an in-memory, development server to deploy the graph.
You can then connect to the studio via the Cloud hosted version of LangGraph Platform.
To be clear, the web studio will connect to your locally running server - your agent is still running locally and never leaves your device.
## Studio FAQs
### Why is my project failing to start?
## Why is my project failing to start?
There are a few reasons that your project might fail to start, here are some of the most common ones.
#### Docker issues (desktop only)
### Docker issues
LangGraph Studio (desktop) requires Docker Desktop version 4.24 or higher. Please make sure you have a version of Docker installed that satisfies that requirement and also make sure you have the Docker Desktop app up and running before trying to use LangGraph Studio. In addition, make sure you have docker-compose updated to version 2.22.0 or higher.
LangGraph Studio requires Docker Desktop version 4.24 or higher. Please make sure you have a version of Docker installed that satisfies that requirement and also make sure you have the Docker Desktop app up and running before trying to use LangGraph Studio. In addition, make sure you have docker-compose updated to version 2.22.0 or higher.
#### Configuration or environment issues
### Configuration or environment issues
Another reason your project might fail to start is because your configuration file is defined incorrectly, or you are missing required environment variables.
### How does interrupt work?
## How does interrupt work?
When you select the `Interrupts` dropdown and select a node to interrupt the graph will pause execution before and after (unless the node goes straight to `END`) that node has run. This means that you will be able to both edit the state before the node is ran and the state after the node has ran. This is intended to allow developers more fine-grained control over the behavior of a node and make it easier to observe how the node is behaving. You will not be able to edit the state after the node has ran if the node is the final node in the graph.
### How do I reload the app? (desktop only)
## How do I reload the app?
If you would like to reload the app, don't use Command+R as you might normally do. Instead, close and reopen the app for a full refresh.
### How does automatic rebuilding work? (desktop only)
## How does automatic rebuilding work?
One of the key features of LangGraph Studio is that it automatically rebuilds your image when you change the source code. This allows for a super fast development and testing cycle which makes it easy to iterate on your graph. There are two different ways that LangGraph rebuilds your image: either by editing the image or completely rebuilding it.
#### Rebuilds from source code changes
### Rebuilds from source code changes
If you modified the source code only (no configuration or dependency changes!) then the image does not require a full rebuild, and LangGraph Studio will only update the relevant parts. The UI status in the bottom left will switch from `Online` to `Stopping` temporarily while the image gets edited. The logs will be shown as this process is happening, and after the image has been edited the status will change back to `Online` and you will be able to run your graph with the modified code!
#### Rebuilds from configuration or dependency changes
### Rebuilds from configuration or dependency changes
If you edit your graph configuration file (`langgraph.json`) or the dependencies (either `pyproject.toml` or `requirements.txt`) then the entire image will be rebuilt. This will cause the UI to switch away from the graph view and start showing the logs of the new image building process. This can take a minute or two, and once it is done your updated image will be ready to use!
### Why is my graph taking so long to startup? (desktop only)
## Why is my graph taking so long to startup?
The LangGraph Studio interacts with a local LangGraph API server. To stay aligned with ongoing updates, the LangGraph API requires regular rebuilding. As a result, you may occasionally experience slight delays when starting up your project.
@@ -118,9 +71,3 @@ def routing_function(state: GraphState) -> Literal["node_b","node_c"]:
return "node_c"
```
## Related
For more information please see the following:
* [LangGraph Studio how-to guides](../how-tos/index.md#langgraph-studio)
+13 -13
View File
@@ -1,6 +1,6 @@
# How to version assistants
In this how-to guide we will walk through how you can create and manage different assistant versions. If you haven't already, you can read [this](../../concepts/assistants.md#versioning-assistants) conceptual guide to gain a better understanding of what assistant versioning is. This how-to assumes you have a graph that is configurable, which means you have defined a config schema and passed it to your graph as follows:
In this how-to guide we will walk through how you can create and manage different assistant versions. If you haven't already, you can read [this](../concepts/api.md/#versioning-assistants) conceptual guide to gain a better understanding of what assistant versioning is. This how-to assumes you have a graph that is configurable, which means you have defined a config schema and passed it to your graph as follows:
=== "Python"
@@ -86,19 +86,19 @@ To create an assistant using the studio do the following steps:
1. Click on the "Create New Assistant" button:
![click create](./img/click_create_assistant.png)
![click create](./img/click_create_assistant.png)
1. Use the create assistant pane to enter info for the assistant you wish to create, and then click create:
2. Use the create assistant pane to enter info for the assistant you wish to create, and then click create:
![create](./img/create_assistant.png)
![create](./img/create_assistant.png)
1. See that your assistant was created and is displayed in the Studio
3. See that your assistant was created and is displayed in the Studio
![view create](./img/create_assistant_view.png)
![view create](./img/create_assistant_view.png)
1. Click on the edit button next to the selected assistant to manage your created assistant:
4. Click on the edit button next to the selected assistant to manage your created assistant:
![create edit](./img/edit_created_assistant.png)
![create edit](./img/edit_created_assistant.png)
## Create a new version for your assistant
@@ -131,15 +131,15 @@ Let's now say we wanted to add a system prompt to our assistant. We can do this
1. First, click on the edit button next to the `openai_assistant`. Then, add a system prompt and click "Save New Version":
![create new version](./img/create_new_version.png)
![create new version](./img/create_new_version.png)
1. Then you can see it is selected in the assistant dropdown:
2. Then you can see it is selected in the assistant dropdown:
![see version dropdown](./img/see_new_version.png)
![see version dropdown](./img/see_new_version.png)
1. And you can see all the version history in the edit pane for the assistant:
3. And you can see all the version history in the edit pane for the assistant:
![see versions](./img/see_version_history.png)
![see versions](./img/see_version_history.png)
## Point your assistant to a different version
+1 -1
View File
@@ -4,7 +4,7 @@ You may wish to copy (i.e. "fork") an existing thread in order to keep the exist
## Setup
This code assumes you already have a thread to copy. You can read about what a thread is [here](../../concepts/langgraph_server.md#threads) and learn how to stream a run on a thread in [these how-to guides](../../how-tos/index.md#streaming_1).
This code assumes you already have a thread to copy. You can read about what a thread is [here](https://langchain-ai.github.io/langgraph/cloud/concepts/api/#threads) and learn how to stream a run on a thread in [these how-to guides](https://langchain-ai.github.io/langgraph/cloud/how-tos/#streaming).
### SDK initialization
@@ -1,6 +1,6 @@
# Enqueue
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../concepts/api.md#double-texting).
The guide covers the `enqueue` option for double texting, which adds the interruptions to a queue and executes them in the order they are received by the client. Below is a quick example of using the `enqueue` option.
@@ -6,7 +6,7 @@ This can be in several ways, but the primary supported way is to add an "interru
## Setup
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/edit-graph-state.ipynb#agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/edit-graph-state.ipynb#build-the-agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
### SDK initialization
@@ -14,7 +14,7 @@ Luckily, LangGraph makes it possible to do similar things in a production way. T
## Setup
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/wait-user-input.ipynb#agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/wait-user-input.ipynb#build-the-agent) if you want to. Once this graph is hosted, we are ready to invoke it and wait for user input.
### SDK initialization
+84
View File
@@ -0,0 +1,84 @@
---
hide:
- toc
---
# How-to Guides
Welcome to the LangGraph Cloud how-to guides! These guides provide practical, step-by-step instructions for accomplishing key tasks in LangGraph Cloud.
## Setup
LangGraph Cloud gives you best in class observability, testing, and hosting services. Learn how to setup your app for deployment to LangGraph Cloud in these how-to guides
- [How to set up app for deployment (requirements.txt)](../deployment/setup.md)
- [How to set up app for deployment (pyproject.toml)](../deployment/setup_pyproject.md)
- [How to set up app for deployment (JavaScript)](../deployment/setup_javascript.md)
- [How to customize Dockerfile](../deployment/custom_docker.md)
- [How to test locally](../deployment/test_locally.md)
## Deploy
Learn how to deploy your app to LangGraph Cloud in these how to guides:
- [How to deploy to LangGraph cloud](../deployment/cloud.md)
## Streaming
Streaming the results of your LLM application is vital for ensuring a good user experience, especially when your graph may call multiple models and take a long time to fully complete a run. Read about how to stream values from your graph in these how to guides:
- [How to stream values](./stream_values.md)
- [How to stream updates](./stream_updates.md)
- [How to stream messages](./stream_messages.md)
- [How to stream events](./stream_events.md)
- [How to stream in debug mode](./stream_debug.md)
- [How to stream multiple modes](./stream_multiple.md)
## Double-texting
Graph execution can take a while, and sometimes users may change their mind about the input they wanted to send before their original input has finished running. For example, a user might notice a typo in their original request and will edit the prompt and resend it. Deciding what to do in these cases is important for ensuring a smooth user experience and preventing your graphs from behaving in unexpected ways. The following how-to guides provide information on the various options LangGraph Cloud gives you for dealing with double-texting:
- [How to use the interrupt option](./interrupt_concurrent.md)
- [How to use the rollback option](./rollback_concurrent.md)
- [How to use the reject option](./reject_concurrent.md)
- [How to use the enqueue option](./enqueue_concurrent.md)
## Human-in-the-loop
When creating complex graphs, leaving every decision up to the LLM can be dangerous, especially when the decisions involve invoking certain tools or accessing specific documents. To remedy this, LangGraph allows you to insert human-in-the-loop behavior to ensure your graph does not have undesired outcomes. Read more about the different ways you can add human-in-the-loop capabilities to your LangGraph Cloud projects in these how-to guides:
- [How to add a breakpoint](./human_in_the_loop_breakpoint.md)
- [How to wait for user input](./human_in_the_loop_user_input.md)
- [How to edit graph state](./human_in_the_loop_edit_state.md)
- [How to replay and branch from prior states](./human_in_the_loop_time_travel.md)
- [How to review tool calls](./human_in_the_loop_review_tool_calls.md)
## LangGraph Studio
LangGraph Studio is a built-in UI for visualizing, testing, and debugging your agents.
- [How to enter LangGraph Studio](./test_deployment.md)
- [How to enter LangGraph Studio for local deployment](./test_local_deployment.md)
- [How to test your graph in LangGraph Studio](./invoke_studio.md)
- [Interact with threads in LangGraph Studio](./threads_studio.md)
## Different Types of Runs:
LangGraph Cloud supports multiple types of runs besides streaming runs.
- [How to run an agent in the background](./background_run.md)
- [How to run multiple agents in the same thread](./same-thread.md)
- [How to create cron jobs](./cron_jobs.md)
- [How to create stateless runs](./stateless_runs.md)
## Other
Other guides that may prove helpful!
- [How to configure agents](./configuration_cloud.md)
- [How to version assistants](./assistant_versioning.md)
- [How to convert LangGraph calls to LangGraph cloud calls](./langgraph_to_langgraph_cloud.ipynb)
- [How to integrate webhooks](./webhooks.md)
- [How to copy threads](./copy_threads.md)
- [How to check status of your threads](./check_thread_status.md)
@@ -1,6 +1,6 @@
# 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).
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../concepts/api.md#double-texting).
The guide covers the `interrupt` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option does not delete the first run, but rather keeps it in the database but sets its status to `interrupted`. Below is a quick example of using the `interrupt` option.
@@ -94,7 +94,6 @@ Now we can start our two runs and join the second on euntil it has completed:
assistant_id,
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
# sleep a bit to get partial outputs from the first run
await asyncio.sleep(2)
run = await client.runs.create(
thread["thread_id"],
@@ -115,7 +114,6 @@ Now we can start our two runs and join the second on euntil it has completed:
assistantId,
{ input: { messages: [{ role: "human", content: "what's the weather in sf?" }] } }
);
// sleep a bit to get partial outputs from the first run
await new Promise(resolve => setTimeout(resolve, 2000));
let run = await client.runs.create(
+1 -1
View File
@@ -1,6 +1,6 @@
# Reject
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide][double-texting].
The guide covers the `reject` option for double texting, which rejects the new run of the graph by throwing an error and continues with the original run until completion. Below is a quick example of using the `reject` option.
@@ -1,6 +1,6 @@
# 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).
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide][double-texting].
The guide covers the `rollback` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option is very similar to the `interrupt` option, but in this case the first run is completely deleted from the database and cannot be restarted. Below is a quick example of using the `rollback` option.
@@ -95,6 +95,7 @@ Now let's run a thread with the multitask parameter set to "rollback":
assistant_id,
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
await asyncio.sleep(2)
run = await client.runs.create(
thread["thread_id"],
assistant_id,
@@ -114,6 +115,7 @@ Now let's run a thread with the multitask parameter set to "rollback":
assistantId,
{ input: { messages: [{ role: "human", content: "what's the weather in sf?" }] } }
);
await new Promise(resolve => setTimeout(resolve, 2000));
let run = await client.runs.create(
thread["thread_id"],
@@ -137,7 +139,7 @@ Now let's run a thread with the multitask parameter set to "rollback":
--data "{
\"assistant_id\": \"agent\",
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in sf?\"}]},
}" && curl --request POST \
}" && sleep 2 && curl --request POST \
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
--header 'Content-Type: application/json' \
--data "{
-3
View File
@@ -1,8 +1,5 @@
# How to stream debug events
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md)
This guide covers how to stream debug events from your graph (`stream_mode="debug"`). Streaming debug events produces responses containing `type` and `timestamp` keys. Debug events correspond to different steps in the graph's execution, and there are three different types of steps that will get streamed back to you:
- `checkpoint`: These events will get streamed anytime the graph saves its state, which occurs after every super-step. Read more about checkpoints [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer)
+129 -5
View File
@@ -1,9 +1,6 @@
# How to stream events
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md#streaming-llm-tokens-and-events-astream_events)
This guide covers how to stream events from your graph (`stream_mode="events"`). Depending on the use case and user experience of your LangGraph application, your application may process event types differently.
This guide covers how to stream events from your graph (`stream_mode="events"`). Depending on the use case and user experience of your LangGraph application, your application may process event types differently. Read more about events in this [conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#astream_events-for-streaming-tokens-of-llm-calls).
## Setup
@@ -292,4 +289,131 @@ Output:
Receiving new event of type: end...
None
None
## Token-by-Token Streaming
Token-by-token streaming can be implemented with the `events` streaming mode. The `on_chat_model_stream` event type should be processed to stream LLM responses token-by-token.
=== "Python"
```python
llm_response = ""
# stream token-by-token
async for chunk in client.runs.stream(
thread_id=thread["thread_id"],
assistant_id=assistant_id,
input=input,
stream_mode="events",
):
if (
chunk.event == "events" and
chunk.data["event"] == "on_chat_model_stream" and
len(chunk.data["data"]["chunk"]["content"]) > 0 and
'text' in chunk.data["data"]["chunk"]["content"][0]
):
llm_response += chunk.data["data"]["chunk"]["content"][0]['text']
print(llm_response)
```
=== "Javascript"
```js
const llmResponse = "";
// stream events
const streamResponse = client.runs.stream(
thread["thread_id"],
assistantID,
{
input,
streamMode: "events"
}
);
for await (const chunk of streamResponse) {
if (chunk.event === "events" && chunk.data.event === "on_chat_model_stream" && chunk.data.chunk.content.length > 0 && 'text' in chunk.data.chunk.content[0]) {
llmResponse += chunk.data.data.chunk.content[0].text;
console.log(llmResponse);
}
}
```
=== "CURL"
```bash
curl --request POST \
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
--header 'Content-Type: application/json' \
--data "{
\"assistant_id\": \"agent\",
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"What's the weather in sf\"}]},
\"stream_mode\": [
\"events\"
]
}" | sed 's/\r$//' | awk '
/^event:/ { event = $2 }
/^data:/ {
json_data = substr($0, index($0, $2))
if (event == "events") {
print json_data
}
}' | jq -r '
select(.event == "on_chat_model_stream") |
.data.chunk.content[] | .text // empty
' | awk '
BEGIN { llm_response="" }
$0 != "" && $0 != "null" {
llm_response = llm_response $0
print llm_response
}'
```
Output:
The
The search
The search results provide
The search results provide the current weather conditions
The search results provide the current weather conditions in San Francisco.
The search results provide the current weather conditions in San Francisco. According
The search results provide the current weather conditions in San Francisco. According to the data,
The search results provide the current weather conditions in San Francisco. According to the data, as
The search results provide the current weather conditions in San Francisco. According to the data, as of 3
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12,
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024,
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C).
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The win
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is bl
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 k
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph).
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70%
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km).
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km). Overall
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km). Overall, it appears
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km). Overall, it appears to be a nice
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km). Overall, it appears to be a nice sunny day in San
The search results provide the current weather conditions in San Francisco. According to the data, as of 3:19 PM on August 12, 2024, the weather in San Francisco is sunny with a temperature of 60.8°F (16°C). The wind is blowing from the west-southwest at 13.4 mph (21.6 kph). The humidity is 70% and visibility is 6 miles (10 km). Overall, it appears to be a nice sunny day in San Francisco.
+392 -226
View File
@@ -1,9 +1,43 @@
# How to stream messages from your graph
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md)
This guide covers how to stream messages from your graph. In order to use this mode, the state of the graph you are interacting with MUST have a `messages` key that is a list of messages.
This guide covers how to stream messages from your graph. With `stream_mode="messages-tuple"`, messages (i.e. individual LLM tokens) from any chat model invocations inside your graph nodes will be streamed back.
E.g., the state should look something like:
=== "Python"
```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import add_messages
from langchain_core.messages import AnyMessage
class State(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
```
=== "Javascript"
```js
import { type BaseMessage } from "@langchain/core/messages";
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
export const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
});
```
Alternatively, you can use an instance or subclass of `from langgraph.graph import MessagesState` (`MessagesState` is equivalent to the implementation above). Or in Javascript: `import { MessagesAnnotation } from "@langchain/langgraph";`.
With `stream_mode="messages"` two things will be streamed back:
- It outputs messages produced by any chat model called inside (unless tagged in a special way)
- It outputs messages returned from nodes (to allow for nodes to return `ToolMessages` and the like)
Read more about how the `messages` streaming mode works [here](https://langchain-ai.github.io/langgraph/cloud/concepts/api/#modemessages)
## Setup
@@ -56,9 +90,101 @@ Output:
'values': None
}
Let's also define a helper function for better formatting of the tool calls in messages (for CURL we will define a helper script called `process_stream.sh`)
=== "Python"
```python
def format_tool_calls(tool_calls):
if tool_calls:
formatted_calls = []
for call in tool_calls:
formatted_calls.append(
f"Tool Call ID: {call['id']}, Function: {call['name']}, Arguments: {call['args']}"
)
return "\n".join(formatted_calls)
return "No tool calls"
```
=== "Javascript"
```js
function formatToolCalls(toolCalls) {
if (toolCalls && toolCalls.length > 0) {
const formattedCalls = toolCalls.map(call => {
return `Tool Call ID: ${call.id}, Function: ${call.name}, Arguments: ${call.args}`;
});
return formattedCalls.join("\n");
}
return "No tool calls";
}
```
=== "CURL"
```bash
# process_stream.sh
format_tool_calls() {
echo "$1" | jq -r 'map("Tool Call ID: \(.id), Function: \(.name), Arguments: \(.args)") | join("\n")'
}
process_data_item() {
local data_item="$1"
if echo "$data_item" | jq -e '.role == "user"' > /dev/null; then
echo "Human: $(echo "$data_item" | jq -r '.content')"
else
local tool_calls=$(echo "$data_item" | jq -r '.tool_calls // []')
local invalid_tool_calls=$(echo "$data_item" | jq -r '.invalid_tool_calls // []')
local content=$(echo "$data_item" | jq -r '.content // ""')
local response_metadata=$(echo "$data_item" | jq -r '.response_metadata // {}')
if [ -n "$content" ] && [ "$content" != "null" ]; then
echo "AI: $content"
fi
if [ "$tool_calls" != "[]" ]; then
echo "Tool Calls:"
format_tool_calls "$tool_calls"
fi
if [ "$invalid_tool_calls" != "[]" ]; then
echo "Invalid Tool Calls:"
format_tool_calls "$invalid_tool_calls"
fi
if [ "$response_metadata" != "{}" ]; then
local finish_reason=$(echo "$response_metadata" | jq -r '.finish_reason // "N/A"')
echo "Response Metadata: Finish Reason - $finish_reason"
fi
fi
}
while IFS=': ' read -r key value; do
case "$key" in
event)
event="$value"
;;
data)
if [ "$event" = "metadata" ]; then
run_id=$(echo "$value" | jq -r '.run_id')
echo "Metadata: Run ID - $run_id"
echo "------------------------------------------------"
elif [ "$event" = "messages/partial" ]; then
echo "$value" | jq -c '.[]' | while read -r data_item; do
process_data_item "$data_item"
done
echo "------------------------------------------------"
fi
;;
esac
done
```
## Stream graph in messages mode
Now we can stream LLM tokens for any messages generated inside a node in the form of tuples `(message, metadata)`. Metadata contains additional information that can be useful for filtering the streamed outputs to a specific node or LLM.
Now we can stream by messages, which will return complete messages (at the end of node execution) as well as tokens for any messages generated inside a node:
=== "Python"
@@ -66,16 +192,41 @@ Now we can stream LLM tokens for any messages generated inside a node in the for
input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
config = {"configurable": {"model_name": "openai"}}
async for chunk in client.runs.stream(
async for event in client.runs.stream(
thread["thread_id"],
assistant_id=assistant_id,
input=input,
config=config,
stream_mode="messages-tuple",
stream_mode="messages",
):
print(f"Receiving new event of type: {chunk.event}...")
print(chunk.data)
print("\n\n")
if event.event == "metadata":
print(f"Metadata: Run ID - {event.data['run_id']}")
print("-" * 50)
elif event.event == "messages/partial":
for data_item in event.data:
if "role" in data_item and data_item["role"] == "user":
print(f"Human: {data_item['content']}")
else:
tool_calls = data_item.get("tool_calls", [])
invalid_tool_calls = data_item.get("invalid_tool_calls", [])
content = data_item.get("content", "")
response_metadata = data_item.get("response_metadata", {})
if content:
print(f"AI: {content}")
if tool_calls:
print("Tool Calls:")
print(format_tool_calls(tool_calls))
if invalid_tool_calls:
print("Invalid Tool Calls:")
print(format_tool_calls(invalid_tool_calls))
if response_metadata:
finish_reason = response_metadata.get("finish_reason", "N/A")
print(f"Response Metadata: Finish Reason - {finish_reason}")
print("-" * 50)
```
=== "Javascript"
@@ -97,13 +248,46 @@ Now we can stream LLM tokens for any messages generated inside a node in the for
{
input,
config,
streamMode: "messages-tuple"
streamMode: "messages"
}
);
for await (const chunk of streamResponse) {
console.log(`Receiving new event of type: ${chunk.event}...`);
console.log(chunk.data);
console.log("\n\n");
for await (const event of streamResponse) {
if (event.event === "metadata") {
console.log(`Metadata: Run ID - ${event.data.run_id}`);
console.log("-".repeat(50));
} else if (event.event === "messages/partial") {
event.data.forEach(dataItem => {
if (dataItem.role && dataItem.role === "user") {
console.log(`Human: ${dataItem.content}`);
} else {
const toolCalls = dataItem.tool_calls || [];
const invalidToolCalls = dataItem.invalid_tool_calls || [];
const content = dataItem.content || "";
const responseMetadata = dataItem.response_metadata || {};
if (content) {
console.log(`AI: ${content}`);
}
if (toolCalls.length > 0) {
console.log("Tool Calls:");
console.log(formatToolCalls(toolCalls));
}
if (invalidToolCalls.length > 0) {
console.log("Invalid Tool Calls:");
console.log(formatToolCalls(invalidToolCalls));
}
if (responseMetadata) {
const finishReason = responseMetadata.finish_reason || "N/A";
console.log(`Response Metadata: Finish Reason - ${finishReason}`);
}
}
});
console.log("-".repeat(50));
}
}
```
@@ -111,221 +295,203 @@ Now we can stream LLM tokens for any messages generated inside a node in the for
```bash
curl --request POST \
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
--header 'Content-Type: application/json' \
--data "{
\"assistant_id\": \"agent\",
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what's the weather in la\"}]},
\"stream_mode\": [
\"messages-tuple\"
]
}" | \
sed 's/\r$//' | \
awk '
/^event:/ {
if (data_content != "") {
print data_content "\n"
}
sub(/^event: /, "Receiving event of type: ", $0)
printf "%s...\n", $0
data_content = ""
}
/^data:/ {
sub(/^data: /, "", $0)
data_content = $0
}
END {
if (data_content != "") {
print data_content "\n"
}
}
'
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
--header 'Content-Type: application/json' \
--data "{
\"assistant_id\": \"agent\",
\"config\":{\"configurable\":{\"model_name\":\"openai\"}},
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"What's the weather in sf\"}]},
\"stream_mode\": [
\"messages\"
]
}" | sed 's/\r$//' | ./process_stream.sh
```
Output:
Receiving new event of type: metadata...
{"run_id": "1ef971e0-9a84-6154-9047-247b4ce89c4d", "attempt": 1}
Metadata: Run ID - 1ef2fe5c-6a1d-6575-bc09-d7832711c17e
--------------------------------------------------
Invalid Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments:
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': ''}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather in'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather in San'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather in San Francisco'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather in San Francisco'}
--------------------------------------------------
Tool Calls:
Tool Call ID: call_cg14F20jMBqWYrNgEkdWHwB3, Function: tavily_search_results_json, Arguments: {'query': 'current weather in San Francisco'}
Response Metadata: Finish Reason - tool_calls
--------------------------------------------------
--------------------------------------------------
AI: The
--------------------------------------------------
AI: The current
--------------------------------------------------
AI: The current weather
--------------------------------------------------
AI: The current weather in
--------------------------------------------------
AI: The current weather in San
--------------------------------------------------
AI: The current weather in San Francisco
--------------------------------------------------
AI: The current weather in San Francisco is
--------------------------------------------------
AI: The current weather in San Francisco is over
--------------------------------------------------
AI: The current weather in San Francisco is overcast
--------------------------------------------------
AI: The current weather in San Francisco is overcast with
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F).
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-s
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-south
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 k
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph).
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%,
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles).
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index is
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index is 3
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index is 3.
--------------------------------------------------
AI: The current weather in San Francisco is overcast with a temperature of 13.9°C (57.0°F). The wind is blowing from the south-southwest at 6.9 mph (11.2 kph). The humidity is at 81%, and the visibility is 16 km (9 miles). The UV index is 3.
Response Metadata: Finish Reason - stop
--------------------------------------------------
...
Receiving new event of type: messages...
[
{
"type": "AIMessageChunk",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "weat"
},
"id": "toolu_0114XKXdNtHQEa3ozmY1uDdM",
"type": "tool_call"
}
],
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
Receiving new event of type: messages...
[
{
"type": "AIMessageChunk",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "her in san "
},
"id": "toolu_0114XKXdNtHQEa3ozmY1uDdM",
"type": "tool_call"
}
],
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
...
Receiving new event of type: messages...
[
{
"type": "AIMessageChunk",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "francisco"
},
"id": "toolu_0114XKXdNtHQEa3ozmY1uDdM",
"type": "tool_call"
}
],
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
...
Receiving new event of type: messages...
[
{
"content": "[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.775, 'lon': -122.4183, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1730475777, 'localtime': '2024-11-01 08:42'}, 'current': {'last_updated_epoch': 1730475000, 'last_updated': '2024-11-01 08:30', 'temp_c': 11.1, 'temp_f': 52.0, 'is_day': 1, 'condition': {'text': 'Partly cloudy', 'icon': '//cdn.weatherapi.com/weather/64x64/day/116.png', 'code': 1003}, 'wind_mph': 2.2, 'wind_kph': 3.6, 'wind_degree': 192, 'wind_dir': 'SSW', 'pressure_mb': 1018.0, 'pressure_in': 30.07, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 89, 'cloud': 75, 'feelslike_c': 11.5, 'feelslike_f': 52.6, 'windchill_c': 10.0, 'windchill_f': 50.1, 'heatindex_c': 10.4, 'heatindex_f': 50.7, 'dewpoint_c': 9.1, 'dewpoint_f': 48.5, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 3.0, 'gust_mph': 6.7, 'gust_kph': 10.8}}\"}]",
"type": "tool",
"tool_call_id": "toolu_0114XKXdNtHQEa3ozmY1uDdM",
...
},
{
"graph_id": "agent",
"langgraph_node": "action",
...
}
]
...
Receiving new event of type: messages...
[
{
"content": [
{
"text": "\n\nThe search",
"type": "text",
"index": 0
}
],
"type": "AIMessageChunk",
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
Receiving new event of type: messages...
[
{
"content": [
{
"text": " results provide",
"type": "text",
"index": 0
}
],
"type": "AIMessageChunk",
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
Receiving new event of type: messages...
[
{
"content": [
{
"text": " the current weather conditions",
"type": "text",
"index": 0
}
],
"type": "AIMessageChunk",
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
Receiving new event of type: messages...
[
{
"content": [
{
"text": " in San Francisco.",
"type": "text",
"index": 0
}
],
"type": "AIMessageChunk",
...
},
{
"graph_id": "agent",
"langgraph_node": "agent",
...
}
]
...
+16 -4
View File
@@ -1,8 +1,5 @@
# How to configure multiple streaming modes at the same time
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md)
This guide covers how to configure multiple streaming modes at the same time.
## Setup
@@ -178,6 +175,11 @@ Output:
Receiving new event of type: messages/complete...
[{'content': "What's the weather in SF?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '7da1bafa-f53c-4df8-ba63-8dd517140b9f', 'example': False}]
Receiving new event of type: debug...
{'type': 'checkpoint', 'timestamp': '2024-06-24T21:34:06.117924+00:00', 'step': 0, 'payload': {'config': {'tags': [], 'metadata': {'created_by': 'system', 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'callbacks': [None], 'recursion_limit': 25, 'configurable': {'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'thread_ts': '1ef32717-bc81-68c8-8000-4e18ae7d67a5', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25'}, 'values': {'messages': [{'content': "What's the weather in SF?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '7da1bafa-f53c-4df8-ba63-8dd517140b9f', 'example': False}]}, 'metadata': {'source': 'loop', 'step': 0, 'writes': None}}}
@@ -303,6 +305,11 @@ Output:
Receiving new event of type: messages/complete...
[{'content': 'begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-2424dd6d-5cf5-4244-8d98-357640ce6e12', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]
Receiving new event of type: debug...
{'type': 'checkpoint', 'timestamp': '2024-06-24T21:34:06.124510+00:00', 'step': 1, 'payload': {'config': {'tags': [], 'metadata': {'created_by': 'system', 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'callbacks': [None], 'recursion_limit': 25, 'configurable': {'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'thread_ts': '1ef32717-bc91-6a34-8001-26353c117c25', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25'}, 'values': {'some_bytes': 'c29tZV9ieXRlcw==', 'some_byte_array': 'c29tZV9ieXRlX2FycmF5', 'dict_with_bytes': {'more_bytes': 'bW9yZV9ieXRlcw=='}, 'messages': [{'content': "What's the weather in SF?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '7da1bafa-f53c-4df8-ba63-8dd517140b9f', 'example': False}, {'content': 'begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-2424dd6d-5cf5-4244-8d98-357640ce6e12', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}, 'metadata': {'source': 'loop', 'step': 1, 'writes': {'agent': {'some_bytes': 'c29tZV9ieXRlcw==', 'some_byte_array': 'c29tZV9ieXRlX2FycmF5', 'dict_with_bytes': {'more_bytes': 'bW9yZV9ieXRlcw=='}, 'messages': [{'content': 'begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-2424dd6d-5cf5-4244-8d98-357640ce6e12', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}}}}
@@ -462,7 +469,12 @@ Output:
{'event': 'on_chain_stream', 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'name': 'LangGraph', 'tags': [], 'metadata': {'created_by': 'system', 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'data': {'chunk': ['values', {'some_bytes': 'c29tZV9ieXRlcw==', 'some_byte_array': 'c29tZV9ieXRlX2FycmF5', 'dict_with_bytes': {'more_bytes': 'bW9yZV9ieXRlcw=='}, 'messages': [{'content': "What's the weather in SF?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '7da1bafa-f53c-4df8-ba63-8dd517140b9f', 'example': False}, {'content': 'begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-2424dd6d-5cf5-4244-8d98-357640ce6e12', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'tool_call__begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': '639ca779-403d-4915-a066-327e1f634c8b', 'tool_call_id': 'tool_call_id'}, {'content': 'end', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-0f2ef0a1-0fc7-445c-9df4-55e8bb284575', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}]}, 'parent_ids': []}
Receiving new event of type: messages/complete...
[{'content': 'end', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-0f2ef0a1-0fc7-445c-9df4-55e8bb284575', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]
Receiving new event of type: debug...
{'type': 'checkpoint', 'timestamp': '2024-06-24T21:34:06.134190+00:00', 'step': 3, 'payload': {'config': {'tags': [], 'metadata': {'created_by': 'system', 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'callbacks': [None], 'recursion_limit': 25, 'configurable': {'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25', 'user_id': '', 'graph_id': 'agent', 'thread_id': 'bfc68029-1f7b-400f-beab-6f9032a52da4', 'thread_ts': '1ef32717-bca9-6418-8003-8d0d0b06845c', 'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'}, 'run_id': '1ef32717-bc30-6cf2-8a26-33f63567bc25'}, 'values': {'some_bytes': 'c29tZV9ieXRlcw==', 'some_byte_array': 'c29tZV9ieXRlX2FycmF5', 'dict_with_bytes': {'more_bytes': 'bW9yZV9ieXRlcw=='}, 'messages': [{'content': "What's the weather in SF?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '7da1bafa-f53c-4df8-ba63-8dd517140b9f', 'example': False}, {'content': 'begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-2424dd6d-5cf5-4244-8d98-357640ce6e12', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'tool_call__begin', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': '639ca779-403d-4915-a066-327e1f634c8b', 'tool_call_id': 'tool_call_id'}, {'content': 'end', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-0f2ef0a1-0fc7-445c-9df4-55e8bb284575', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}, 'metadata': {'source': 'loop', 'step': 3, 'writes': {'agent': {'some_bytes': 'c29tZV9ieXRlcw==', 'some_byte_array': 'c29tZV9ieXRlX2FycmF5', 'dict_with_bytes': {'more_bytes': 'bW9yZV9ieXRlcw=='}, 'messages': [{'content': 'end', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-0f2ef0a1-0fc7-445c-9df4-55e8bb284575', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}}}}
+20 -68
View File
@@ -1,9 +1,6 @@
# How to stream state updates of your graph
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md)
This guide covers how to use `stream_mode="updates"` for your graph, which will stream the updates to the graph state that are made after each node is executed. This differs from using `stream_mode="values"`: instead of streaming the entire value of the state at each superstep, it only streams the updates from each of the nodes that made an update to the state at that superstep.
This guide covers how to use `stream_mode="updates"` for your graph, which will stream the updates to the graph state that are made after each node is executed. This differs from using `stream_mode="values"`: instead of streaming the entire value of the state at each superstep, it only streams the updates from each of the nodes that made an update to the state at that superstep. Read [this conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#stream-and-astream) to learn more.
## Setup
@@ -149,69 +146,24 @@ Now we can stream by updates, which outputs updates made to the state by each no
Output:
Receiving new event of type: metadata...
{"run_id": "cfc96c16-ed9a-44bd-b5bb-c30e3c0725f0"}
Receiving new event of type: updates...
{
"agent": {
"messages": [
{
"type": "ai",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "weather in los angeles"
},
"id": "toolu_0148tMmDK51iLQfG1yaNwRHM"
}
],
...
}
]
}
}
Receiving new event of type: updates...
{
"action": {
"messages": [
{
"content": [
{
"url": "https://www.weatherapi.com/",
"content": "{\"location\": {\"name\": \"Los Angeles\", \"region\": \"California\", \"country\": \"United States of America\", \"lat\": 34.05, \"lon\": -118.24, \"tz_id\": \"America/Los_Angeles\", \"localtime_epoch\": 1716062239, \"localtime\": \"2024-05-18 12:57\"}, \"current\": {\"last_updated_epoch\": 1716061500, \"last_updated\": \"2024-05-18 12:45\", \"temp_c\": 18.9, \"temp_f\": 66.0, \"is_day\": 1, \"condition\": {\"text\": \"Overcast\", \"icon\": \"//cdn.weatherapi.com/weather/64x64/day/122.png\", \"code\": 1009}, \"wind_mph\": 2.2, \"wind_kph\": 3.6, \"wind_degree\": 10, \"wind_dir\": \"N\", \"pressure_mb\": 1017.0, \"pressure_in\": 30.02, \"precip_mm\": 0.0, \"precip_in\": 0.0, \"humidity\": 65, \"cloud\": 100, \"feelslike_c\": 18.9, \"feelslike_f\": 66.0, \"vis_km\": 16.0, \"vis_miles\": 9.0, \"uv\": 6.0, \"gust_mph\": 7.5, \"gust_kph\": 12.0}}"
}
],
"type": "tool",
"name": "tavily_search_results_json",
"tool_call_id": "toolu_0148tMmDK51iLQfG1yaNwRHM",
...
}
]
}
}
Receiving new event of type: updates...
{
"agent": {
"messages": [
{
"content": "The weather in Los Angeles is currently overcast with a temperature of around 66°F (18.9°C). There are light winds from the north at around 2-3 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.",
"type": "ai",
...
}
]
}
}
{'run_id': 'cfc96c16-ed9a-44bd-b5bb-c30e3c0725f0'}
Receiving new event of type: data...
{'agent': {'messages': [{'content': [{'id': 'toolu_0148tMmDK51iLQfG1yaNwRHM', 'input': {'query': 'weather in los angeles'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-1a9d32b0-7007-4a36-abde-8df812a0ed94', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in los angeles'}, 'id': 'toolu_0148tMmDK51iLQfG1yaNwRHM'}], 'invalid_tool_calls': []}]}}
Receiving new event of type: data...
{'action': {'messages': [{'content': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Los Angeles\', \'region\': \'California\', \'country\': \'United States of America\', \'lat\': 34.05, \'lon\': -118.24, \'tz_id\': \'America/Los_Angeles\', \'localtime_epoch\': 1716062239, \'localtime\': \'2024-05-18 12:57\'}, \'current\': {\'last_updated_epoch\': 1716061500, \'last_updated\': \'2024-05-18 12:45\', \'temp_c\': 18.9, \'temp_f\': 66.0, \'is_day\': 1, \'condition\': {\'text\': \'Overcast\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/122.png\', \'code\': 1009}, \'wind_mph\': 2.2, \'wind_kph\': 3.6, \'wind_degree\': 10, \'wind_dir\': \'N\', \'pressure_mb\': 1017.0, \'pressure_in\': 30.02, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 65, \'cloud\': 100, \'feelslike_c\': 18.9, \'feelslike_f\': 66.0, \'vis_km\': 16.0, \'vis_miles\': 9.0, \'uv\': 6.0, \'gust_mph\': 7.5, \'gust_kph\': 12.0}}"}]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'tavily_search_results_json', 'id': 'a36e8cd1-0e96-4417-9c15-f10a945d2b42', 'tool_call_id': 'toolu_0148tMmDK51iLQfG1yaNwRHM'}]}}
Receiving new event of type: data...
{'agent': {'messages': [{'content': 'The weather in Los Angeles is currently overcast with a temperature of around 66°F (18.9°C). There are light winds from the north at around 2-3 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-d5c1c2f0-b12d-41ce-990b-f36570e7483d', 'example': False, 'tool_calls': [], 'invalid_tool_calls': []}]}}
Receiving new event of type: end...
None
+59 -127
View File
@@ -1,9 +1,6 @@
# How to stream full state of your graph
!!! info "Prerequisites"
* [Streaming](../../concepts/streaming.md)
This guide covers how to use `stream_mode="values"`, which streams the value of the state at each superstep. This differs from using `stream_mode="updates"`: instead of streaming just the updates to the state from each node, it streams the entire graph state at that superstep.
This guide covers how to use `stream_mode="values"`, which streams the value of the state at each superstep. This differs from using `stream_mode="updates"`: instead of streaming just the updates to the state from each node, it streams the entire graph state at that superstep. Read [this conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#stream-and-astream) to learn more.
## Setup
@@ -136,93 +133,30 @@ Now we can stream by values, which streams the full state of the graph after eac
Output:
Receiving new event of type: metadata...
{"run_id": "f08791ce-0a3d-44e0-836c-ff62cd2e2786"}
{'run_id': 'f08791ce-0a3d-44e0-836c-ff62cd2e2786'}
Receiving new event of type: values...
{
"messages": [
{
"role": "human",
"content": "what's the weather in la"
}
]
}
{'messages': [{'role': 'human', 'content': 'what's the weather in la'}]}
Receiving new event of type: values...
{
"messages": [
{
"content": "what's the weather in la",
"type": "human",
...
},
{
"content": "",
"type": "ai",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "weather in los angeles"
},
"id": "toolu_01E5mSaZWm5rWJnCqmt63v4g"
}
],
...
}
]
}
...
{'messages': [{'content': 'what's the weather in la', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'faa15565-8823-4aa1-87af-e21b40526fae', 'example': False}, {'content': [{'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g', 'input': {'query': 'weather in los angeles'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-3fe1db7a-6b8d-4d83-ba07-8657190ad811', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in los angeles'}, 'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g'}], 'invalid_tool_calls': []}]}
Receiving new event of type: values...
{
"messages": [
{
"content": "what's the weather in la",
"type": "human",
...
},
{
"content": "",
"type": "ai",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "weather in los angeles"
},
"id": "toolu_01E5mSaZWm5rWJnCqmt63v4g"
}
],
...
}
{
"content": [
{
"url": "https://www.weatherapi.com/",
"content": "{\"location\": {\"name\": \"Los Angeles\", \"region\": \"California\", \"country\": \"United States of America\", \"lat\": 34.05, \"lon\": -118.24, \"tz_id\": \"America/Los_Angeles\", \"localtime_epoch\": 1716310320, \"localtime\": \"2024-05-21 9:52\"}, \"current\": {\"last_updated_epoch\": 1716309900, \"last_updated\": \"2024-05-21 09:45\", \"temp_c\": 16.7, \"temp_f\": 62.1, \"is_day\": 1, \"condition\": {\"text\": \"Overcast\", \"icon\": \"//cdn.weatherapi.com/weather/64x64/day/122.png\", \"code\": 1009}, \"wind_mph\": 8.1, \"wind_kph\": 13.0, \"wind_degree\": 250, \"wind_dir\": \"WSW\", \"pressure_mb\": 1015.0, \"pressure_in\": 29.97, \"precip_mm\": 0.0, \"precip_in\": 0.0, \"humidity\": 65, \"cloud\": 100, \"feelslike_c\": 16.7, \"feelslike_f\": 62.1, \"vis_km\": 16.0, \"vis_miles\": 9.0, \"uv\": 5.0, \"gust_mph\": 12.5, \"gust_kph\": 20.2}}"
}
],
"type": "tool",
"name": "tavily_search_results_json",
"tool_call_id": "toolu_01E5mSaZWm5rWJnCqmt63v4g"
...
},
{
"content": "Based on the weather API results, the current weather in Los Angeles is overcast with a temperature of around 62°F (17°C). There are light winds from the west-southwest around 8-13 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.",
"type": "ai",
...
}
]
}
{'messages': [{'content': 'what's the weather in la', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'faa15565-8823-4aa1-87af-e21b40526fae', 'example': False}, {'content': [{'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g', 'input': {'query': 'weather in los angeles'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-3fe1db7a-6b8d-4d83-ba07-8657190ad811', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in los angeles'}, 'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g'}], 'invalid_tool_calls': []}, {'content': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Los Angeles\', \'region\': \'California\', \'country\': \'United States of America\', \'lat\': 34.05, \'lon\': -118.24, \'tz_id\': \'America/Los_Angeles\', \'localtime_epoch\': 1716310320, \'localtime\': \'2024-05-21 9:52\'}, \'current\': {\'last_updated_epoch\': 1716309900, \'last_updated\': \'2024-05-21 09:45\', \'temp_c\': 16.7, \'temp_f\': 62.1, \'is_day\': 1, \'condition\': {\'text\': \'Overcast\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/122.png\', \'code\': 1009}, \'wind_mph\': 8.1, \'wind_kph\': 13.0, \'wind_degree\': 250, \'wind_dir\': \'WSW\', \'pressure_mb\': 1015.0, \'pressure_in\': 29.97, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 65, \'cloud\': 100, \'feelslike_c\': 16.7, \'feelslike_f\': 62.1, \'vis_km\': 16.0, \'vis_miles\': 9.0, \'uv\': 5.0, \'gust_mph\': 12.5, \'gust_kph\': 20.2}}"}]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'tavily_search_results_json', 'id': '0d5dab31-5ff8-4ae2-a560-bc4bcba7c9d7', 'tool_call_id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g'}]}
Receiving new event of type: values...
{'messages': [{'content': 'what's the weather in la', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'faa15565-8823-4aa1-87af-e21b40526fae', 'example': False}, {'content': [{'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g', 'input': {'query': 'weather in los angeles'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-3fe1db7a-6b8d-4d83-ba07-8657190ad811', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in los angeles'}, 'id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g'}], 'invalid_tool_calls': []}, {'content': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Los Angeles\', \'region\': \'California\', \'country\': \'United States of America\', \'lat\': 34.05, \'lon\': -118.24, \'tz_id\': \'America/Los_Angeles\', \'localtime_epoch\': 1716310320, \'localtime\': \'2024-05-21 9:52\'}, \'current\': {\'last_updated_epoch\': 1716309900, \'last_updated\': \'2024-05-21 09:45\', \'temp_c\': 16.7, \'temp_f\': 62.1, \'is_day\': 1, \'condition\': {\'text\': \'Overcast\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/122.png\', \'code\': 1009}, \'wind_mph\': 8.1, \'wind_kph\': 13.0, \'wind_degree\': 250, \'wind_dir\': \'WSW\', \'pressure_mb\': 1015.0, \'pressure_in\': 29.97, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 65, \'cloud\': 100, \'feelslike_c\': 16.7, \'feelslike_f\': 62.1, \'vis_km\': 16.0, \'vis_miles\': 9.0, \'uv\': 5.0, \'gust_mph\': 12.5, \'gust_kph\': 20.2}}"}]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'tavily_search_results_json', 'id': '0d5dab31-5ff8-4ae2-a560-bc4bcba7c9d7', 'tool_call_id': 'toolu_01E5mSaZWm5rWJnCqmt63v4g'}, {'content': 'Based on the weather API results, the current weather in Los Angeles is overcast with a temperature of around 62°F (17°C). There are light winds from the west-southwest around 8-13 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-4d6d4c23-5aad-4042-b0d9-19407a9e08e3', 'example': False, 'tool_calls': [], 'invalid_tool_calls': []}]}
Receiving new event of type: end...
None
@@ -294,42 +228,40 @@ If we want to just get the final result, we can use this endpoint and just keep
Output:
{
"messages": [
{
"content": "what's the weather in la",
"type": "human",
...
},
{
"type": "ai",
"tool_calls": [
{
"name": "tavily_search_results_json",
"args": {
"query": "weather in los angeles"
},
"id": "toolu_01E5mSaZWm5rWJnCqmt63v4g"
}
],
...
}
{
"content": [
{
"url": "https://www.weatherapi.com/",
"content": "{\"location\": {\"name\": \"Los Angeles\", \"region\": \"California\", \"country\": \"United States of America\", \"lat\": 34.05, \"lon\": -118.24, \"tz_id\": \"America/Los_Angeles\", \"localtime_epoch\": 1716310320, \"localtime\": \"2024-05-21 9:52\"}, \"current\": {\"last_updated_epoch\": 1716309900, \"last_updated\": \"2024-05-21 09:45\", \"temp_c\": 16.7, \"temp_f\": 62.1, \"is_day\": 1, \"condition\": {\"text\": \"Overcast\", \"icon\": \"//cdn.weatherapi.com/weather/64x64/day/122.png\", \"code\": 1009}, \"wind_mph\": 8.1, \"wind_kph\": 13.0, \"wind_degree\": 250, \"wind_dir\": \"WSW\", \"pressure_mb\": 1015.0, \"pressure_in\": 29.97, \"precip_mm\": 0.0, \"precip_in\": 0.0, \"humidity\": 65, \"cloud\": 100, \"feelslike_c\": 16.7, \"feelslike_f\": 62.1, \"vis_km\": 16.0, \"vis_miles\": 9.0, \"uv\": 5.0, \"gust_mph\": 12.5, \"gust_kph\": 20.2}}"
}
],
"type": "tool",
"name": "tavily_search_results_json",
"tool_call_id": "toolu_01E5mSaZWm5rWJnCqmt63v4g"
...
},
{
"content": "Based on the weather API results, the current weather in Los Angeles is overcast with a temperature of around 62°F (17°C). There are light winds from the west-southwest around 8-13 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.",
"type": "ai",
...
}
]
}
{'messages': [{'content': 'what's the weather in la',
'additional_kwargs': {},
'response_metadata': {},
'type': 'human',
'name': None,
'id': 'e78c2f94-d810-42fc-a399-11f6bb1b1092',
'example': False},
{'content': [{'id': 'toolu_01SBMoAGr4U9x3ibztm2UUom',
'input': {'query': 'weather in los angeles'},
'name': 'tavily_search_results_json',
'type': 'tool_use'}],
'additional_kwargs': {},
'response_metadata': {},
'type': 'ai',
'name': None,
'id': 'run-80767ab8-09fc-40ec-9e45-657ddef5e0b1',
'example': False,
'tool_calls': [{'name': 'tavily_search_results_json',
'args': {'query': 'weather in los angeles'},
'id': 'toolu_01SBMoAGr4U9x3ibztm2UUom'}],
'invalid_tool_calls': []},
{'content': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Los Angeles\', \'region\': \'California\', \'country\': \'United States of America\', \'lat\': 34.05, \'lon\': -118.24, \'tz_id\': \'America/Los_Angeles\', \'localtime_epoch\': 1716310320, \'localtime\': \'2024-05-21 9:52\'}, \'current\': {\'last_updated_epoch\': 1716309900, \'last_updated\': \'2024-05-21 09:45\', \'temp_c\': 16.7, \'temp_f\': 62.1, \'is_day\': 1, \'condition\': {\'text\': \'Overcast\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/122.png\', \'code\': 1009}, \'wind_mph\': 8.1, \'wind_kph\': 13.0, \'wind_degree\': 250, \'wind_dir\': \'WSW\', \'pressure_mb\': 1015.0, \'pressure_in\': 29.97, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 65, \'cloud\': 100, \'feelslike_c\': 16.7, \'feelslike_f\': 62.1, \'vis_km\': 16.0, \'vis_miles\': 9.0, \'uv\': 5.0, \'gust_mph\': 12.5, \'gust_kph\': 20.2}}"}]',
'additional_kwargs': {},
'response_metadata': {},
'type': 'tool',
'name': 'tavily_search_results_json',
'id': 'af25e94a-c119-48c3-bbd3-096e42f472ac',
'tool_call_id': 'toolu_01SBMoAGr4U9x3ibztm2UUom'},
{'content': 'Based on the weather API results, the current weather in Los Angeles is overcast with a temperature of around 62°F (17°C). There are light winds from the west-southwest around 8-13 mph. The humidity is 65% and visibility is good at 9 miles. Overall, mild spring weather conditions in LA.',
'additional_kwargs': {},
'response_metadata': {},
'type': 'ai',
'name': None,
'id': 'run-b90f0037-e56a-4f3b-ad92-00d10d079a9e',
'example': False,
'tool_calls': [],
'invalid_tool_calls': []}]}
+1 -1
View File
@@ -4,7 +4,7 @@ The LangGraph Studio UI connects directly to LangGraph Cloud deployments.
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
1. In the left-hand navigation panel, select `LangGraph Cloud`. The `LangGraph Cloud` view contains a list of existing LangGraph Cloud deployments.
1. In the left-hand navigation panel, select `Deployments`. The `Deployments` view contains a list of existing LangGraph Cloud deployments.
1. Select an existing deployment to test with LangGraph Studio.
1. In the top-right corner, select `Open LangGraph Studio`.
1. [Invoke an assistant](./invoke_studio.md) or [view an existing thread](./threads_studio.md).
+5 -16
View File
@@ -76,9 +76,7 @@ Output:
## Use graph with a webhook
To invoke a run with a webhook, we specify the `webhook` parameter with the desired endpoint when creating a run. Webhook requests are triggered by the end of a run.
For example, if we can receive requests at `https://my-server.app/my-webhook-endpoint`, we can pass this to `stream`:
Now we can invoke a run with a webhook:
=== "Python"
@@ -91,7 +89,7 @@ For example, if we can receive requests at `https://my-server.app/my-webhook-end
assistant_id=assistant_id,
input=input,
stream_mode="events",
webhook="https://my-server.app/my-webhook-endpoint"
webhook="your-webhook"
):
# Do something with the stream output
pass
@@ -109,7 +107,7 @@ For example, if we can receive requests at `https://my-server.app/my-webhook-end
assistantID,
{
input: input,
webhook: "https://my-server.app/my-webhook-endpoint"
webhook: "your-webhook"
}
);
for await (const chunk of streamResponse) {
@@ -126,17 +124,8 @@ For example, if we can receive requests at `https://my-server.app/my-webhook-end
--data '{
"assistant_id": <ASSISTANT_ID>,
"input" : {"messages":[{"role": "user", "content": "Hello!"}]},
"webhook": "https://my-server.app/my-webhook-endpoint"
"webhook": <YOUR_WEBHOOK_URL>
}'
```
The schema for the payload sent to `my-webhook-endpoint` is that of a [run](../../concepts/langgraph_server.md/#runs). See [API Reference](https://langchain-ai.github.io/langgraph/cloud/reference/api/api_ref.html#model/run) for more detail. Note that the run input, configuration, etc. are included in the `kwargs` field.
### Signing webhook requests
To sign the webhook requests, we can specify a token parameter in the webhook URL, e.g.,
```
https://my-server.app/my-webhook-endpoint?token=...
```
The server should then extract the token from the request's parameters and validate it before processing the payload.
And that's it! Now you can trigger your custom webhooks whenever you want in your LangGraph applications!
Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB

+44
View File
@@ -0,0 +1,44 @@
# LangGraph Cloud (beta)
!!! tip
- LangGraph is an MIT-licensed open-source library, which we are committed to maintaining and growing for the community.
- LangGraph Cloud is an optional managed hosting service for LangGraph, which provides additional features geared towards production deployments.
- We are actively contributing improvements back to LangGraph informed by our work on LangGraph Cloud.
- You can always deploy LangGraph applications on your own infrastructure using the open-source LangGraph project.
!!! warning "Under Construction"
LangGraph Cloud documentation is under construction. Contents may change until general availability.
<video controls preload="auto" allowfullscreen="true" poster="how-tos/img/studio_forks_poster.png">
<source src="how-tos/img/studio_forks.mp4" type="video/mp4">
</video>
## Overview
LangGraph Cloud is a managed service for deploying and hosting LangGraph applications. Deploying applications with LangGraph Cloud shortens the time-to-market for developers. With one click, deploy a production-ready API with built-in persistence for your LangGraph application. LangGraph Cloud APIs are horizontally scalable and deployed with durable storage.
The LangGraph Cloud API exposes functionality of your LangGraph application through [Assistants](./concepts/api.md#assistants). An assistant abstracts the cognitive architecture of your graph. Invoke an assistant by calling the pre-built [API endpoints](./reference/api/api_ref.md).
LangGraph Cloud is seamlessly integrated with [LangSmith](https://www.langchain.com/langsmith) and is accessible from within the LangSmith UI.
LangGraph Cloud applications can be tested and debugged using the [LangGraph Studio Desktop](https://github.com/langchain-ai/langgraph-studio).
## Key Features
The LangGraph Cloud API supports key LangGraph features in addition to new functionality for enabling complex, agentic workflows.
- **Assistants and Threads**: Assistants abstract the cognitive architecture of graphs and threads track the state/history of graphs.
- **Streaming**: API support for [LangGraph streaming modes](../concepts/low_level.md#streaming) including setting multiple streaming modes at the same time.
- **Human-in-the-Loop**: API support for [LangGraph human-in-the-loop features](../concepts/agentic_concepts.md#human-in-the-loop).
- **Double Texting**: Configure how assistants respond when new input is received while processing a previous input. Interrupt, rollback, reject, or enqueue.
- **Background Runs/Cron Jobs**: A built-in task queue enables background runs and scheduled cron jobs.
- **Stateless Runs**: For simpler use cases, invoke an assistant without needing to create a thread.
## Documentation
- [Tutorials](./quick_start.md): Learn to build and deploy applications for LangGraph Cloud.
- [How-to Guides](./how-tos/index.md): Learn how to set up a LangGraph application for deployment and implement features of the LangGraph Cloud API such as streaming tokens, configuring double texting, and creating cron jobs. Go here if you want to copy and run a specific code snippet.
- [Conceptual Guides](./concepts/api.md): In-depth explanations of the core data models (e.g. assistants), key features of the LangGraph Cloud API (e.g. double texting), and the architecture of a LangGraph Cloud deployment.
- [Reference](./reference/api/api_ref.md): References for the LangGraph Cloud API, the corresponding Python and JS/TS SDKs, the LangGraph CLI, and deployment environment variables.
+203 -266
View File
@@ -1,194 +1,155 @@
# LangGraph Cloud Quick Start
# Quick Start
In this tutorial you will build and deploy a simple chatbot agent that can look things up on the internet. You will be using [LangGraph Cloud](../concepts/langgraph_cloud.md), [LangGraph Studio](../concepts/langgraph_studio.md) to visualize and test it out, and [LangGraph SDK](./reference/sdk/python_sdk_ref.md) to interact with the deployed agent.
If you want to learn how to build an agent like this from scratch, take a look at the [LangGraph Quick Start tutorial](../tutorials/introduction.ipynb).
This quick start guide will cover how to build a simple agent that can look up things on the internet. We will then deploy it to LangGraph Cloud, use the LangGraph Studio to visualize and test it out, and use the LangGraph SDK to interact with it.
## Set up requirements
This tutorial will use:
- Anthropic for the LLM - sign up and get an API key [here](https://console.anthropic.com/).
- Tavily for the search engine - sign up and get an API key [here](https://app.tavily.com/).
- LangSmith for hosting - sign up and get an API key [here](https://smith.langchain.com/).
- Anthropic for the LLM - sign up and get an API key [here](https://console.anthropic.com/)
- Tavily for the search engine - sign up and get an API key [here](https://app.tavily.com/)
- LangSmith for hosting - sign up and get an API key [here](https://smith.langchain.com/)
## Create and configure your app
## Set up local files
First, let's set create all of the necessary files for our LangGraph application.
1. Create a new application with the following directory and files:
1. __Create application directory and files__
=== "Python"
Create a new application `my-app` with the following file structure:
<my-app>/
|-- agent.py # code for your LangGraph agent
|-- requirements.txt # Python packages required for your graph
|-- langgraph.json # configuration file for LangGraph
|-- .env # environment files with API keys
```shell
mkdir my-app
=== "Javascript"
<my-app>/
|-- agent.ts # code for your LangGraph agent
|-- package.json # Javascript packages required for your graph
|-- langgraph.json # configuration file for LangGraph
|-- .env # environment files with API keys
2. The `agent.py`/`agent.ts` file should contain code for defining your graph. The following code is a simple example, the important thing is that at some point in your file you compile your graph and assign the compiled graph to a variable (in this case the `graph` variable). This example code uses `create_react_agent`, a prebuilt agent. You can read more about it [here](../concepts/agentic_concepts.md#react-implementation).
=== "Python"
```python
from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langgraph.prebuilt import create_react_agent
model = ChatAnthropic(model="claude-3-5-sonnet-20240620")
tools = [TavilySearchResults(max_results=2)]
graph = create_react_agent(model, tools)
```
=== "Python"
=== "Javascript"
my-app/
|-- agent.py # code for your LangGraph agent
|-- requirements.txt # Python packages required for your graph
|-- langgraph.json # configuration file for LangGraph
|-- .env # environment files with API keys
```ts
import { ChatAnthropic } from "@langchain/anthropic";
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
=== "Javascript"
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
});
my-app/
|-- agent.ts # code for your LangGraph agent
|-- package.json # Javascript packages required for your graph
|-- langgraph.json # configuration file for LangGraph
|-- .env # environment files with API keys
const tools = [
new TavilySearchResults({ maxResults: 3, }),
];
export const graph = createReactAgent({ llm: model, tools });
```
1. __Define your graph__
3. The `requirements.txt`/`package.json` file should contain any dependencies for your graph(s). In this case we only require four packages for our graph to run:
=== "Python"
The `agent.py` file should contain code with your graph.
=== "Python"
=== "Javascript"
The `agent.ts` file should contain code with your graph.
```python
langgraph
langchain_anthropic
tavily-python
langchain_community
```
The following code example is a simple chatbot agent (similar to the one in the [previous tutorial](../tutorials/introduction.ipynb)). Specifically, it uses [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent], a prebuilt [ReAct](../concepts/agentic_concepts.md#react-implementation)-style agent.
=== "Javascript"
The `agent` file needs to have a variable with a [CompiledGraph][langgraph.graph.graph.CompiledGraph] (in this case the `graph` variable).
=== "Python"
```python
# agent.py
from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langgraph.prebuilt import create_react_agent
model = ChatAnthropic(model="claude-3-5-sonnet-20240620")
tools = [TavilySearchResults(max_results=2)]
# compiled graph
graph = create_react_agent(model, tools)
```
=== "Javascript"
```ts
// agent.ts
import { ChatAnthropic } from "@langchain/anthropic";
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
});
const tools = [
new TavilySearchResults({ maxResults: 3, }),
];
// compiled graph
export const graph = createReactAgent({ llm: model, tools });
```
1. __Specify dependencies__
=== "Python"
You should add dependencies for your graph(s) to `requirements.txt`.
=== "Javascript"
You should add dependencies for your graph(s) to `package.json`.
In this case we only require four packages for our graph to run:
=== "Python"
```python
langgraph
langchain_anthropic
tavily-python
langchain_community
```
=== "Javascript"
```js
{
"name": "my-app",
"packageManager": "yarn@1.22.22",
"dependencies": {
"@langchain/community": "^0.3.11",
"@langchain/core": "^0.3.16",
"@langchain/langgraph": "0.2.18",
"@langchain/anthropic": "^0.3.7"
}
}
```
1. __Create LangGraph configuration file__
The [`langgraph.json`][langgraph.json] file is a configuration file that describes what graph(s) you are going to deploy. In this case we only have one graph: the compiled `graph` object from `agent.py` / `agent.ts`.
=== "Python"
```json
{
"dependencies": ["."],
"graphs": {
"agent": "./agent.py:graph"
},
"env": ".env"
```js
{
"name": "my-app",
"packageManager": "yarn@1.22.22",
"dependencies": {
"@langchain/community": "^0.2.31",
"@langchain/core": "^0.2.31",
"@langchain/langgraph": "0.2.0",
"@langchain/openai": "^0.2.8"
}
```
}
```
=== "Javascript"
4. The [`langgraph.json`][langgraph.json] file is a configuration file that describes what graph(s) you are going to host. In this case we only have one graph to host: the compiled `graph` object from `agent.py`/`agent.ts`.
```json
{
"node_version": "20",
"dockerfile_lines": [],
"dependencies": ["."],
"graphs": {
"agent": "./src/agent.ts:graph"
},
"env": ".env"
}
```
=== "Python"
Learn more about the LangGraph CLI configuration file [here](./reference/cli.md#configuration-file).
```json
{
"dependencies": ["."],
"graphs": {
"agent": "./agent.py:graph"
},
"env": ".env"
}
```
1. __Specify environment variables__
=== "Javascript"
The `.env` file should have any environment variables needed to run your graph. This will only be used for local testing, so if you are not testing locally you can skip this step.
```json
{
"node_version": "20",
"dockerfile_lines": [],
"dependencies": ["."],
"graphs": {
"agent": "./src/agent.ts:graph"
},
"env": ".env"
}
```
!!! warning
The `.env` file should NOT be included with the rest of source code in your Github repository. When creating a deployment using LangGraph Cloud, you will be able to specify the environment variables manually.
Learn more about the LangGraph CLI configuration file [here](./reference/cli.md#configuration-file).
For this graph, we need two environment variables:
5. The `.env` file should have any environment variables needed to run your graph. This will only be used for local testing, so if you are not testing locally you can skip this step. NOTE: if you do add this, you should NOT check this into git. For this graph, we need two environment variables:
```shell
ANTHROPIC_API_KEY=...
TAVILY_API_KEY=...
```
!!! tip
Learn more about different application structure options [here](../how-tos/index.md#application-structure).
Now that we have set everything up on our local file system, we are ready to host our graph.
Now that we have set everything up on our local file system, we are ready to test our graph locally.
## Test the graph build locally
## Test the app locally
### Using LangGraph Studio Desktop (recommended)
To test the LangGraph app before deploying it using LangGraph Cloud, you can start the [LangGraph server](../concepts/langgraph_server.md) locally or use [LangGraph Studio](../concepts/langgraph_studio.md).
![LangGraph Studio Desktop](./img/graph_video_poster.png)
## Using local server
Testing your graph locally is easy with LangGraph Studio Desktop. LangGraph Studio offers a new way to develop LLM applications by providing a specialized agent IDE that enables visualization, interaction, and debugging of complex agentic applications
You can test your app by running [LangGraph server](../concepts/langgraph_server.md) locally. This is useful to make sure you have configured our [CLI configuration file][langgraph.json] correctly and can interact with your graph.
With visual graphs and the ability to edit state, you can better understand agent workflows and iterate faster. LangGraph Studio integrates with [LangSmith](https://smith.langchain.com) so you can collaborate with teammates to debug failure modes.
To run the server locally, you need to first install the LangGraph CLI:
### Using the LangGraph CLI
Before deploying to the cloud, we probably want to test the building of our graph locally. This is useful to make sure we have configured our [CLI configuration file][langgraph.json] correctly and our graph runs.
In order to do this we can first install the LangGraph CLI
```shell
pip install langgraph-cli
```
You can then test our API server locally. In order to run the server locally, you will need to add your `LANGSMITH_API_KEY` to the `.env` file.
We can then test our API server locally. This requires access to LangGraph closed beta. In order to run the server locally, you will need to add your `LANGSMITH_API_KEY` to the .env file so we can validate you have access to LangGraph closed beta.
```shell
langgraph up
@@ -199,21 +160,10 @@ This will start up the LangGraph API server locally. If this runs successfully,
```shell
Ready!
- API: http://localhost:8123
2024-06-26 19:20:41,056:INFO:uvicorn.access 127.0.0.1:44138 - "GET /ok HTTP/1.1" 200
```
First, let's verify that the server is running correctly by calling `/ok` endpoint:
```shell
curl --request GET --url http://localhost:8123/ok
```
Output:
```
{"ok": "true"}
```
Now we're ready to test the app with the real inputs!
You can now test this out! **Note: this local server is intended SOLELY for local testing purposes and is not performant enough for production applications, so please do not use it as such.** To test it out, you can go to another terminal window and run:
```shell
curl --request POST \
@@ -225,57 +175,36 @@ curl --request POST \
"messages": [
{
"role": "user",
"content": "What is the weather in NYC?"
"content": "How are you?"
}
]
},
"stream_mode": "updates"
"metadata": {},
"config": {
"configurable": {}
},
"multitask_strategy": "reject",
"stream_mode": [
"values"
]
}'
```
Output:
If you get back a valid response, then all is functioning properly!
```
...
## Deploy to Cloud
data: {
"agent": {
"messages": [
{
"content": "The search results from Tavily provide the current weather conditions in New York City, including temperature, wind speed, precipitation, humidity, and cloud cover. According to the results, as of 3:00pm on October 30th, 2024, it is overcast in NYC with a temperature of around 66°F (19°C), light winds from the southwest around 8 mph (13 km/h), and 66% humidity.\n\nSo in summary, the current weather in NYC is overcast with mild temperatures in the mid 60sF and light winds, based on the search results. Let me know if you need any other details!",
"type": "ai",
...
}
]
}
}
```
### Push your code to GitHub
You can see that our agent responds with the up-to-date search results!
Turn the `<my-app>` directory into a GitHub repo. You can use the GitHub CLI if you like, or just create a repo manually (if unfamiliar, instructions [here](https://docs.github.com/en/migrations/importing-source-code/using-the-command-line-to-import-source-code/adding-locally-hosted-code-to-github)).
### Using LangGraph Studio Desktop
### Deploy from GitHub with LangGraph Cloud
You can also test your app locally with [LangGraph Studio](../concepts/langgraph_studio.md). LangGraph Studio offers a new way to develop LLM applications by providing a specialized agent IDE that enables visualization, interaction, and debugging of complex agentic applications.
Once you have created your github repository with a Python file containing your compiled graph as well as a `langgraph.json` file containing the configuration for hosting your graph, you can head over to LangSmith and click on the 🚀 icon on the left navbar to create a new deployment. Then click the `+ New Deployment` button.
With visual graphs and the ability to edit state, you can better understand agent workflows and iterate faster. LangGraph Studio integrates with LangSmith allowing you to collaborate with teammates to debug failure modes.
![Langsmith Workflow](./img/cloud_deployment.png)
LangGraph Studio is available as a [desktop app](https://studio.langchain.com/) for MacOS users. Once you have installed the app, you can select `my-app` directory, which will automatically start the server locally and load the graph in the UI.
To interact with your chatbot agent in LangGraph Studio, you can add a new message in the `Input` section and press `Submit`.
![LangGraph Studio Desktop](./deployment/img/quick_start_studio.png)
## Deploy to LangGraph Cloud
Once you've tested your graph locally and verified that it works as expected, you can deploy it to the LangGraph Cloud.
First, you'll need to turn the `my-app` directory into a GitHub repo and [push it to GitHub](https://docs.github.com/en/migrations/importing-source-code/using-the-command-line-to-import-source-code/adding-locally-hosted-code-to-github).
Once you have created your GitHub repository with a Python file containing your compiled graph as well as a `langgraph.json` with the configuration, you can head over to [LangSmith](https://smith.langchain.com/) and click on the graph icon (`LangGraph Cloud`) on the bottom of the left navbar. This will open the LangGraph deployments page. On this page, click the `+ New Deployment` button in the top right corner.
![Langsmith Workflow](./deployment/img/cloud_deployment.png)
**_If you have not deployed to LangGraph Cloud before:_** there will be a button that shows up saying `Import from GitHub`. Youll need to follow that flow to connect LangGraph Cloud to GitHub.
**_If you have not deployed to LangGraph Cloud before:_** there will be a button that shows up saying Import from GitHub. Youll need to follow that flow to connect LangGraph Cloud to GitHub.
**_Once you have set up your GitHub connection:_** the new deployment page will look as follows:
@@ -284,43 +213,53 @@ Once you have created your GitHub repository with a Python file containing your
To deploy your application, you should do the following:
1. Select your GitHub username or organization from the selector
1. Search for your repo to deploy in the search bar and select it
1. Choose a name for your deployment
1. In the `Git Branch` field, you can specify either the branch for the code you want to deploy, or the exact commit SHA.
1. In the `LangGraph API config file` field, enter the path to your `langgraph.json` file (which in this case is just `langgraph.json`)
1. If your application needs environment variables, add those in the `Environment Variables` section. They will be propagated to the underlying server so your code can access them. In this case, we will need `ANTHROPIC_API_KEY` and `TAVILY_API_KEY`.
2. Search for your repo to deploy in the search bar and select it
3. Choose any name
4. In the `LangGraph API config file` field, enter the path to your `langgraph.json` file (which in this case is just `langgraph.json`)
5. For Git Reference, you can select either the git branch for the code you want to deploy, or the exact commit SHA.
6. If your chain relies on environment variables, add those in. They will be propagated to the underlying server so your code can access them. In this case, we need `ANTHROPIC_API_KEY` and `TAVILY_API_KEY`.
Putting this all together, you should have something as follows for your deployment details:
![Deployment filled out](./deployment/img/deploy_filled_out.png)
Hit `Submit` and your application will start deploying!
## Inspect Traces + Monitor Service
### Deployments View
After your deployment is complete, your deployments page should look as follows:
![Deployed page](./deployment/img/deployed_page.png)
## Interact with your deployment
You can see that by default, you get access to the `Trace Count` monitoring chart and `Recent Traces` run view. These are powered by LangSmith.
### Using LangGraph Studio (Cloud)
You can click on `All Charts` to view all monitoring info for your server, or click on `See tracing project` to get more information on an individual trace.
On the deployment page for your application,, you should see a button in the top right corner that says `LangGraph Studio`. Clicking on this button will take you to the web version of LangGraph Studio. This is the same UI that you interacted with when [testing the app locally](#using-langgraph-studio-recommended), but instead of using a local LangGraph server, it uses the one from your LangGraph Cloud deployment.
### Access the Docs
You can access the docs by clicking on the API docs link, which should send you to a page that looks like this:
![API Docs page](./deployment/img/api_page.png)
You wont actually be able to test any of the API endpoints without authorizing first. To do so, grab your Langsmith API key and add it at the top where it says `API KEY (X-API-KEY)`. You should now be able to select any of the API endpoints, click `Test Request`, enter the parameters you would like to pass, and then click `Send` to view the results of the API call.
## Interact with your deployment via LangGraph Studio
If you click on your deployment you should see a blue button in the top right that says `LangGraph Studio`. Clicking on this button will take you to a page that looks like this:
![Studio UI before being run](./deployment/img/graph_visualization.png)
On this page you can test out your graph by passing in starting states and clicking `Start Run` (this should behave identically to calling `.invoke`). You will then be able to look into the execution thread for each run and explore the steps your graph is taking to produce its output.
![Studio UI once being run](./deployment/img/graph_run.png)
### Using LangGraph SDK
## Use with the SDK
You can also interact with your deployed LangGraph application programmatically, using [LangGraph SDK](./reference/sdk/python_sdk_ref.md).
Once you have tested that your hosted graph works as expected using LangGraph Studio, you can start using your hosted graph all over your organization by using the LangGraph SDK. Let's see how we can access our hosted graph and execute our run from a python file.
First, make sure you have the SDK installed:
=== "Python"
```shell
pip install langgraph_sdk
```
=== "Javascript"
```shell
yarn add @langchain/langgraph-sdk
```
First, make sure you have the SDK installed by calling `pip install langgraph_sdk`.
Before using, you need to get the URL of your LangGraph deployment. You can find this in the `Deployment` view. Click the URL to copy it to the clipboard.
@@ -339,8 +278,8 @@ The first thing to do when using the SDK is to setup our client, access our assi
client = get_client(url=<DEPLOYMENT_URL>)
# get default assistant
assistants = await client.assistants.search(metadata={"created_by": "system"})
assistant = assistants[0]
assistants = await client.assistants.search()
assistant = [a for a in assistants if not a["config"]][0]
# create thread
thread = await client.threads.create()
print(thread)
@@ -353,8 +292,8 @@ The first thing to do when using the SDK is to setup our client, access our assi
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
// get default assistant
const assistants = await client.assistants.search({ metadata: {"created_by": "system"} })
const assistant = assistants[0];
const assistants = await client.assistants.search();
const assistant = assistants.find(a => !a.config);
// create thread
const thread = await client.threads.create();
console.log(thread)
@@ -368,9 +307,8 @@ The first thing to do when using the SDK is to setup our client, access our assi
--header 'Content-Type: application/json' \
--data '{
"limit": 10,
"offset": 0,
"metadata": {"created_by": "system"}
}' &&
"offset": 0
}' | jq -c 'map(select(.config == null or .config == {})) | .[0]' && \
curl --request POST \
--url <DEPLOYMENT_URL>/threads \
--header 'Content-Type: application/json' \
@@ -382,35 +320,32 @@ We can then execute a run on the thread:
=== "Python"
```python
input = {
"messages": [{"role": "user", "content": "What is the weather in NYC?"}]
}
input = {"messages":[{"role": "user", "content": "Hello! My name is Bagatur and I am 26 years old."}]}
async for chunk in client.runs.stream(
thread["thread_id"],
assistant["assistant_id"],
input=input,
stream_mode="updates",
):
if chunk.data:
thread['thread_id'],
assistant["assistant_id"],
input=input,
stream_mode="updates",
):
if chunk.data and chunk.event != "metadata":
print(chunk.data)
```
=== "Javascript"
```js
const input = { "messages": [{ "role": "user", "content": "What is the weather in NYC?" }] };
const input = { "messages":[{ "role": "user", "content": "Hello! My name is Bagatur and I am 26 years old." }] };
const streamResponse = client.runs.stream(
thread["thread_id"],
assistant["assistant_id"],
{
input,
streamMode: "updates"
}
);
for await (const chunk of streamResponse) {
if (chunk.data) {
if (chunk.data && chunk.event !== "metadata" ) {
console.log(chunk.data);
}
}
@@ -422,41 +357,43 @@ We can then execute a run on the thread:
curl --request POST \
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
--header 'Content-Type: application/json' \
--data '{
"assistant_id": <ASSISTANT_ID>,
"input": {
"messages": [
{
"role": "user",
"content": "What is the weather in NYC?"
}
]
},
"stream_mode": "updates"
--data "{
\"assistant_id\": <ASSISTANT_ID>,
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"Hello! My name is Bagatur and I am 26 years old.\"}]},
}" | sed 's/\r$//' | awk '
/^event:/ { event = $2 }
/^data:/ {
json_data = substr($0, index($0, $2))
if (event != "metadata") {
print json_data
}
}'
```
Output:
```
...
{'agent': {'messages': [{'content': "Hi Bagatur! It's nice to meet you. How can I assist you today?", 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_9cb5d38cf7'}, 'type': 'ai', 'name': None, 'id': 'run-c89118b7-1b1e-42b9-a85d-c43fe99881cd', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
data: {
"agent": {
"messages": [
{
"content": "The search results from Tavily provide the current weather conditions in New York City, including temperature, wind speed, precipitation, humidity, and cloud cover. According to the results, as of 3:00pm on October 30th, 2024, it is overcast in NYC with a temperature of around 66°F (19°C), light winds from the southwest around 8 mph (13 km/h), and 66% humidity.\n\nSo in summary, the current weather in NYC is overcast with mild temperatures in the mid 60sF and light winds, based on the search results. Let me know if you need any other details!",
"type": "ai",
...
}
]
}
}
```
## Next steps
## What's Next
Congratulations! If you've worked your way through this tutorial you are well on your way to becoming a LangGraph Cloud expert. Here are some other resources to check out to help you out on the path to expertise:
* [LangGraph How-to guides](../how-tos/index.md)
* [LangGraph Tutorials](../tutorials/index.md)
### LangGraph Cloud How-tos
If you want to learn more about streaming from hosted graphs, check out the Streaming [how-to guides](how-tos/index.md#streaming).
To learn more about double-texting and all the ways you can handle it in your application, read up on these [how-to guides](how-tos/index.md#double-texting).
To learn about how to include different human-in-the-loop behavior in your graph, take a look at [these how-tos](how-tos/index.md#human-in-the-loop).
### LangGraph Tutorials
Before hosting, you have to write a graph to host. Here are some tutorials to get you more comfortable with writing LangGraph graphs and give you inspiration for the types of graphs you want to host.
[This tutorial](../tutorials/customer-support/customer-support.ipynb) walks you through how to write a customer support bot using LangGraph.
If you are interested in writing a SQL agent, check out [this tutorial](../tutorials/sql-agent.ipynb).
Check out the [LangGraph tutorials](../tutorials/index.md) page to read about more exciting use cases.
File diff suppressed because it is too large Load Diff
+62 -138
View File
@@ -1,38 +1,23 @@
# LangGraph CLI
The LangGraph command line interface includes commands to build and run a LangGraph Cloud API server locally in [Docker](https://www.docker.com/). For development and testing, you can use the CLI to deploy a local API server as an alternative to the [Studio desktop app](../../concepts/langgraph_studio.md).
The LangGraph CLI includes commands to build and run a LangGraph Cloud API server locally in [Docker](https://www.docker.com/). For development and testing, use the CLI to deploy a local API server.
## Installation
1. Ensure that Docker is installed (e.g. `docker --version`).
2. Install the `langgraph-cli` package:
=== "pip"
```bash
pip install langgraph-cli
```
=== "Homebrew (MacOS only)"
```bash
brew install langgraph-cli
```
2. Install the `langgraph-cli` Python package (e.g. `pip install langgraph-cli`).
3. Run the command `langgraph --help` to confirm that the CLI is installed.
[](){#langgraph.json}
## Configuration File
The LangGraph CLI requires a JSON configuration file with the following keys:
| Key | Description |
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</li></ul> |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `python_version` | `3.11` or `3.12`. Defaults to `3.11`. |
| `pip_config_file` | Path to `pip` config file. |
| `dockerfile_lines` | Array of additional lines to add to Dockerfile following the import from parent image. |
| Key | Description |
| --- | ----------- |
| `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</li></ul> |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `python_version` | `3.11` or `3.12`. Defaults to `3.11`. |
| `pip_config_file`| Path to `pip` config file. |
| `dockerfile_lines` | Array of additional lines to add to Dockerfile following the import from parent image. |
<div class="admonition tip">
<p class="admonition-title">Note</p>
@@ -42,162 +27,101 @@ The LangGraph CLI requires a JSON configuration file with the following keys:
</div>
Example:
```json
{
"dependencies": ["langchain_openai", "./your_package"],
"graphs": {
"my_graph_id": "./your_package/your_file.py:variable"
},
"env": "./.env"
"dependencies": [
"langchain_openai",
"./your_package"
],
"graphs": {
"my_graph_id": "./your_package/your_file.py:variable"
},
"env": "./.env"
}
```
Example with environment variables:
Example:
```json
{
"python_version": "3.11",
"dependencies": ["langchain_openai", "."],
"graphs": {
"my_graph_id": "./your_package/your_file.py:make_graph"
},
"env": {
"OPENAI_API_KEY": "secret-key"
}
"python_version": "3.11",
"dependencies": [
"langchain_openai",
"."
],
"graphs": {
"my_graph_id": "./your_package/your_file.py:make_graph"
},
"env": {
"OPENAI_API_KEY": "secret-key"
}
}
```
## Commands
The base command for the LangGraph CLI is `langgraph`.
**Usage**
```
langgraph [OPTIONS] COMMAND [ARGS]
```
### `dev`
Run LangGraph API server in development mode with hot reloading and debugging capabilities. This lightweight server requires no Docker installation and is suitable for development and testing. State is persisted to a local directory.
**Installation**
This command requires the "inmem" extra to be installed:
```bash
pip install -U "langgraph-cli[inmem]"
```
**Usage**
```
langgraph dev [OPTIONS]
```
**Options**
| Option | Default | Description |
|----------------------------|------------------|--------------------------------------------------------------------------------------------|
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables |
| `--host TEXT` | `127.0.0.1` | Host to bind the server to |
| `--port INTEGER` | `2024` | Port to bind the server to |
| `--no-reload` | | Disable auto-reload |
| `--n-jobs-per-worker INTEGER` | | Number of jobs per worker. Default is 10 |
| `--no-browser` | | Disable automatic browser opening |
| `--debug-port INTEGER` | | Port for debugger to listen on |
| `--help` | | Display command documentation |
### `build`
Build LangGraph Cloud API server Docker image.
**Usage**
```
langgraph build [OPTIONS]
```
**Options**
| Option | Default | Description |
|----------------------|------------------|------------------------------------------------------------------------------------------------------------------------------|
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
| `--pull / --no-pull` | `--pull` | Build with latest remote Docker image. Use `--no-pull` for running the LangGraph Cloud API server with locally built images. |
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
| `--help` | | Display command documentation. |
| Option | Default | Description |
| ------ | ------- | ----------- |
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
| `--pull / --no-pull` | `--pull` | Build with latest remote Docker image. Use `--no-pull` for running the LangGraph Cloud API server with locally built images. |
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
| `--help` | | Display command documentation. |
### `up`
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Cloud closed beta. Requires a license key for production use.
Start langgraph API server. For local testing, requires a LangSmith API key with access to LangGraph Cloud closed beta. Requires a license key for production use.
**Usage**
```
langgraph up [OPTIONS]
```
**Options**
| Option | Default | Description |
|------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------|
| `--wait` | | Wait for services to start before returning. Implies --detach |
| `--postgres-uri TEXT` | Local database | Postgres URI to use for the database. |
| `--watch` | | Restart on file changes |
| `--debugger-base-url TEXT` | `http://127.0.0.1:[PORT]` | URL used by the debugger to access LangGraph API. |
| `--debugger-port INTEGER` | | Pull the debugger image locally and serve the UI on specified port |
| `--verbose` | | Show more output from the server logs. |
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
| `-d, --docker-compose FILE` | | Path to docker-compose.yml file with additional services to launch. |
| `-p, --port INTEGER` | `8123` | Port to expose. Example: `langgraph up --port 8000` |
| `--pull / --no-pull` | `pull` | Pull latest images. Use `--no-pull` for running the server with locally-built images. Example: `langgraph up --no-pull` |
| `--recreate / --no-recreate` | `no-recreate` | Recreate containers even if their configuration and image haven't changed |
| `--help` | | Display command documentation. |
| Option | Default | Description |
| ------ | ------- | ----------- |
| `--wait` | | Wait for services to start before returning. Implies --detach |
| `--postgres-uri TEXT` | Local database | Postgres URI to use for the database. |
| `--watch` | | Restart on file changes |
| `--debugger-base-url TEXT` | `http://127.0.0.1:[PORT]` | URL used by the debugger to access LangGraph API. |
| `--debugger-port INTEGER` | | Pull the debugger image locally and serve the UI on specified port |
| `--verbose` | | Show more output from the server logs. |
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
| `-d, --docker-compose FILE` | | Path to docker-compose.yml file with additional services to launch. |
| `-p, --port INTEGER` | `8123` | Port to expose. Example: `langgraph test --port 8000` |
| `--pull / --no-pull` | `pull` | Pull latest images. Use --no-pull for running the server with locally-built images. Example: `langgraph up --no-pull` |
| `--recreate / --no-recreate` | `no-recreate` | Recreate containers even if their configuration and image haven't changed |
| `--help` | | Display command documentation. |
### `dockerfile`
Generate a Dockerfile for building a LangGraph Cloud API server Docker image.
### `test`
Test your LangGraph in the cloud. The only function you can call from the SDK after testing your graph is `client.runs.stream(thread_id=None, ...)`
**Usage**
```
langgraph dockerfile [OPTIONS] SAVE_PATH
langgraph test [OPTIONS]
```
**Options**
| Option | Default | Description |
|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------|
| `-c, --config FILE` | `langgraph.json` | Path to the [configuration file](#configuration-file) declaring dependencies, graphs and environment variables. |
| `--help` | | Show this message and exit. |
Example:
```bash
langgraph dockerfile -c langgraph.json Dockerfile
```
This generates a Dockerfile that looks similar to:
```dockerfile
FROM langchain/langgraph-api:3.11
ADD ./pipconf.txt /pipconfig.txt
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt langchain_community langchain_anthropic langchain_openai wikipedia scikit-learn
ADD ./graphs /deps/__outer_graphs/src
RUN set -ex && \
for line in '[project]' \
'name = "graphs"' \
'version = "0.1"' \
'[tool.setuptools.package-data]' \
'"*" = ["**/*"]'; do \
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \
done
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
| Option | Default | Description |
| ------ | ------- | ----------- |
| `--verbose` | | Show more output from the server logs. |
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
| `-p, --port INTEGER` | `8123` | Port to expose. Example: `langgraph test --port 8000` |
| `--pull / --no-pull` | `pull` | Pull latest images. Use --no-pull for running the server with locally-built images. Example: `langgraph up --no-pull` |
| `--help` | | Display command documentation. |
+3 -3
View File
@@ -103,15 +103,15 @@ Parallel processing is vital for efficient multi-agent systems and complex tasks
For practical implementation, see our [map-reduce tutorial](../how-tos/map-reduce.ipynb).
### Subgraphs
### Sub-graphs
[Subgraphs](./low_level.md#subgraphs) are essential for managing complex agent architectures, particularly in [multi-agent systems](./multi_agent.md). They allow:
Sub-graphs are essential for managing complex agent architectures, particularly in multi-agent systems. They allow:
- Isolated state management for individual agents
- Hierarchical organization of agent teams
- Controlled communication between agents and the main system
Subgraphs communicate with the parent graph through overlapping keys in the state schema. This enables flexible, modular agent design. For implementation details, refer to our [subgraph how-to guide](../how-tos/subgraph.ipynb).
Sub-graphs communicate with the parent graph through overlapping keys in the state schema. This enables flexible, modular agent design. For implementation details, refer to our [sub-graph tutorial](../how-tos/subgraph.ipynb).
### Reflection
-167
View File
@@ -1,167 +0,0 @@
# Application Structure
!!! info "Prerequisites"
- [LangGraph Server](./langgraph_server.md)
- [LangGraph Glossary](./low_level.md)
## Overview
A LangGraph application consists of one or more graphs, a LangGraph API Configuration file (`langgraph.json`), a file that specifies dependencies, and an optional .env file that specifies environment variables.
This guide shows a typical structure for a LangGraph application and shows how the required information to deploy a LangGraph application using the LangGraph Platform is specified.
## Key Concepts
To deploy using the LangGraph Platform, the following information should be provided:
1. A [LangGraph API Configuration file](#configuration-file) (`langgraph.json`) that specifies the dependencies, graphs, environment variables to use for the application.
2. The [graphs](#graphs) that implement the logic of the application.
3. A file that specifies [dependencies](#dependencies) required to run the application.
4. [Environment variable](#environment-variables) that are required for the application to run.
## File Structure
Below are examples of directory structures for Python and JavaScript applications:
=== "Python (requirements.txt)"
```plaintext
my-app/
├── my_agent # all project code lies within here
│ ├── utils # utilities for your graph
│ │ ├── __init__.py
│ │ ├── tools.py # tools for your graph
│ │ ├── nodes.py # node functions for you graph
│ │ └── state.py # state definition of your graph
│ ├── requirements.txt # package dependencies
│ ├── __init__.py
│ └── agent.py # code for constructing your graph
├── .env # environment variables
└── langgraph.json # configuration file for LangGraph
```
=== "Python (pyproject.toml)"
```plaintext
my-app/
├── my_agent # all project code lies within here
│ ├── utils # utilities for your graph
│ │ ├── __init__.py
│ │ ├── tools.py # tools for your graph
│ │ ├── nodes.py # node functions for you graph
│ │ └── state.py # state definition of your graph
│ ├── __init__.py
│ └── agent.py # code for constructing your graph
├── .env # environment variables
├── langgraph.json # configuration file for LangGraph
└── pyproject.toml # dependencies for your project
```
=== "JS (package.json)"
```plaintext
my-app/
├── src # all project code lies within here
│ ├── utils # optional utilities for your graph
│ │ ├── tools.ts # tools for your graph
│ │ ├── nodes.ts # node functions for you graph
│ │ └── state.ts # state definition of your graph
│ └── agent.ts # code for constructing your graph
├── package.json # package dependencies
├── .env # environment variables
└── langgraph.json # configuration file for LangGraph
```
!!! note
The directory structure of a LangGraph application can vary depending on the programming language and the package manager used.
## Configuration File
The `langgraph.json` file is a JSON file that specifies the dependencies, graphs, environment variables, and other settings required to deploy a LangGraph application.
The file supports specification of the following information:
| Key | Description |
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `dependencies` | **Required**. Array of dependencies for LangGraph API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</li></ul> |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `python_version` | `3.11` or `3.12`. Defaults to `3.11`. |
| `pip_config_file` | Path to `pip` config file. |
| `dockerfile_lines` | Array of additional lines to add to Dockerfile following the import from parent image. |
!!! tip
The LangGraph CLI defaults to using the configuration file **langgraph.json** in the current directory.
### Examples
=== "Python"
* The dependencies involve a custom local package and the `langchain_openai` package.
* A single graph will be loaded from the file `./your_package/your_file.py` with the variable `variable`.
* The environment variables are loaded from the `.env` file.
```json
{
"dependencies": [
"langchain_openai",
"./your_package"
],
"graphs": {
"my_agent": "./your_package/your_file.py:agent"
},
"env": "./.env"
}
```
=== "JavaScript"
* The dependencies will be loaded from a dependency file in the local directory (e.g., `package.json`).
* A single graph will be loaded from the file `./your_package/your_file.js` with the function `agent`.
* The environment variable `OPENAI_API_KEY` is set inline.
```json
{
"dependencies": [
"."
],
"graphs": {
"my_agent": "./your_package/your_file.js:agent"
},
"env": {
"OPENAI_API_KEY": "secret-key"
}
}
```
## Dependencies
A LangGraph application may depend on other Python packages or JavaScript libraries (depending on the programming language in which the application is written).
You will generally need to specify the following information for dependencies to be set up correctly:
1. A file in the directory that specifies the dependencies (e.g., `requirements.txt`, `pyproject.toml`, or `package.json`).
2. A `dependencies` key in the [LangGraph configuration file](#configuration-file) that specifies the dependencies required to run the LangGraph application.
3. Any additional binaries or system libraries can be specified using `dockerfile_lines` key in the [LangGraph configuration file](#configuration-file).
## Graphs
Use the `graphs` key in the [LangGraph configuration file](#configuration-file) to specify which graphs will be available in the deployed LangGraph application.
You can specify one or more graphs in the configuration file. Each graph is identified by a name (which should be unique) and a path for either: (1) the compiled graph or (2) a function that makes a graph is defined.
## Environment Variables
If you're working with a deployed LangGraph application locally, you can configure environment variables in the `env` key of the [LangGraph configuration file](#configuration-file).
For a production deployment, you will typically want to configure the environment variables in the deployment environment.
## Related
Please see the following resources for more information:
- How-to guides for [Application Structure](../how-tos/index.md#application-structure).

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