This file is a merged representation of the entire codebase, combined into a single document by Repomix.
The content has been processed where empty lines have been removed, content has been formatted for parsing in plain style, content has been compressed (code blocks are separated by ⋮---- delimiter), security check has been disabled.

================================================================
File Summary
================================================================

Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.

File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
  a. A separator line (================)
  b. The file path (File: path/to/file)
  c. Another separator line
  d. The full contents of the file
  e. A blank line

Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
  original repository files, not this packed version.
- When processing this file, use the file path to distinguish
  between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
  the same level of security as you would the original repository.

Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Empty lines have been removed from all files
- Content has been formatted for parsing in plain style
- Content has been compressed - code blocks are separated by ⋮---- delimiter
- Security check has been disabled - content may contain sensitive information
- Files are sorted by Git change count (files with more changes are at the bottom)


================================================================
Directory Structure
================================================================
.github/
  workflows/
    claude.yml
    lint.yml
    release-drafter.yml
    release.yml
    test.yml
  release-drafter-config.yml
examples/
  human_in_the_loop/
    breakpoints.ipynb
    dynamic_breakpoints.ipynb
    edit-graph-state.ipynb
    review-tool-calls-openai.ipynb
    review-tool-calls.ipynb
    time-travel.ipynb
    wait-user-input.ipynb
  memory/
    add-summary-conversation-history.ipynb
    delete-messages.ipynb
    manage-conversation-history.ipynb
    semantic-search.ipynb
  .gitignore
  create-react-agent-hitl.ipynb
  create-react-agent-manage-message-history.ipynb
  create-react-agent-memory.ipynb
  cross-thread-persistence-functional.ipynb
  cross-thread-persistence.ipynb
  docker-compose.yml
  Dockerfile.jupyter
  jupyter_notebook_config.py
  persistence-functional.ipynb
  README.md
  subgraph-persistence.ipynb
  subgraphs-manage-state.ipynb
langgraph/
  checkpoint/
    redis/
      __init__.py
      aio.py
      ashallow.py
      base.py
      jsonplus_redis.py
      shallow.py
      types.py
      util.py
      version.py
  store/
    redis/
      __init__.py
      aio.py
      base.py
      token_unescaper.py
      types.py
tests/
  conftest.py
  docker-compose.yml
  embed_test_utils.py
  test_async_aget_tuple_checkpoint_id.py
  test_async_cluster_mode.py
  test_async_search_limit.py
  test_async_store.py
  test_async.py
  test_checkpoint_ttl.py
  test_cluster_mode.py
  test_crossslot_integration.py
  test_decode_responses.py
  test_fix_verification.py
  test_interruption.py
  test_issue_51_adelete_thread.py
  test_key_parsing.py
  test_numeric_version_fix.py
  test_numeric_version_issue.py
  test_search_limit.py
  test_semantic_search_keys.py
  test_semantic_search_notebook.py
  test_shallow_async.py
  test_shallow_sync.py
  test_store.py
  test_streaming_modes.py
  test_streaming.py
  test_subgraph_key_parsing.py
  test_sync.py
.gitignore
CLAUDE.md
LICENSE
Makefile
pyproject.toml
README.md
scripts.py
test_key_parsing_focus.py
test_key_parsing_suite.py

================================================================
Files
================================================================

================
File: .github/workflows/claude.yml
================
name: Claude Code
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]
  pull_request_review:
    types: [submitted]
jobs:
  claude:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
      issues: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1
      - name: Run Claude Code
        id: claude
        uses: anthropics/claude-code-action@beta
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

================
File: .github/workflows/lint.yml
================
name: Lint
on:
  pull_request:
  push:
    branches:
      - main
env:
  POETRY_VERSION: "1.8.3"
jobs:
  check:
    name: Style-check ${{ matrix.python-version }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        # Only lint on the min and max supported Python versions.
        # It's extremely unlikely that there's a lint issue on any version in between
        # that doesn't show up on the min or max versions.
        #
        # GitHub rate-limits how many jobs can be running at any one time.
        # Starting new jobs is also relatively slow,
        # so linting on fewer versions makes CI faster.
        python-version:
          - "3.9"
          - "3.10"
          - "3.11"
          - "3.12"
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install Poetry
      uses: snok/install-poetry@v1
      with:
        version: ${{ env.POETRY_VERSION }}
    - name: Install dependencies
      run: |
        poetry install --all-extras
    - name: lint
      run: |
        make lint

================
File: .github/workflows/release-drafter.yml
================
name: Release Drafter
on:
  push:
    # branches to consider in the event; optional, defaults to all
    branches:
      - main
permissions: {}
jobs:
  update_release_draft:
    permissions:
      pull-requests: write  #  to add label to PR (release-drafter/release-drafter)
      contents: write  #  to create a github release (release-drafter/release-drafter)
    runs-on: ubuntu-latest
    steps:
      # Drafts your next Release notes as Pull Requests are merged into "master"
      - uses: release-drafter/release-drafter@v5
        with:
          # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
           config-name: release-drafter-config.yml
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

================
File: .github/workflows/release.yml
================
name: Publish Release
on:
  release:
    types: [published]
env:
  PYTHON_VERSION: "3.11"
  POETRY_VERSION: "1.8.3"
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ env.PYTHON_VERSION }}
      - name: Install Poetry
        uses: snok/install-poetry@v1
        with:
          version: ${{ env.POETRY_VERSION }}
      - name: Build package
        run: poetry build
      - name: Upload build
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
  publish:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ env.PYTHON_VERSION }}
      - name: Install Poetry
        uses: snok/install-poetry@v1
        with:
          version: ${{ env.POETRY_VERSION }}
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - name: Publish to PyPI
        env:
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI }}
        run: poetry publish

================
File: .github/workflows/test.yml
================
name: Test Suite
on:
  pull_request:
  push:
    branches:
      - main
  schedule:
    - cron: "0 2 * * *"  # 2 AM UTC nightly
  workflow_dispatch:
env:
  POETRY_VERSION: "1.8.3"
jobs:
  test:
    name: Python ${{ matrix.python-version }} - [redis ${{ matrix.redis-version }}]
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: [3.9, '3.10', 3.11, 3.12, 3.13]
        redis-version: ['6.2.6-v9', 'latest', '8.0.2']
    steps:
    - name: Check out repository
      uses: actions/checkout@v3
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
    - name: Install Poetry
      uses: snok/install-poetry@v1
      with:
        version: ${{ env.POETRY_VERSION }}
    - name: Install dependencies
      run: |
        pip wheel --no-cache-dir --use-pep517 ml-dtypes
        poetry install --all-extras
    - name: Set Redis image name
      run: |
        if [[ "${{ matrix.redis-version }}" == "8.0.2" ]]; then
          echo "REDIS_IMAGE=redis:${{ matrix.redis-version }}" >> $GITHUB_ENV
        else
          echo "REDIS_IMAGE=redis/redis-stack-server:${{ matrix.redis-version }}" >> $GITHUB_ENV
        fi
    - name: Run API tests
      if: matrix.redis-version == 'latest'
      env:
        OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
      run: |
        make test-all
    - name: Run tests
      if:  matrix.redis-version != 'latest'
      run: |
        make test

================
File: .github/release-drafter-config.yml
================
name-template: '$NEXT_MINOR_VERSION'
tag-template: 'v$NEXT_MINOR_VERSION'
autolabeler:
  - label: 'maintenance'
    files:
      - '*.md'
      - '.github/*'
  - label: 'bug'
    branch:
      - '/bug-.+'
  - label: 'maintenance'
    branch:
      - '/maintenance-.+'
  - label: 'feature'
    branch:
      - '/feature-.+'
categories:
  - title: 'Breaking Changes'
    labels:
      - 'breakingchange'
  - title: '🧪 Experimental Features'
    labels:
      - 'experimental'
  - title: '🚀 New Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
      - 'BUG'
  - title: '🧰 Maintenance'
    label: 'maintenance'
change-template: '- $TITLE (#$NUMBER)'
exclude-labels:
  - 'skip-changelog'
template: |
  # Changes
  $CHANGES
  ## Contributors
  We'd like to thank all the contributors who worked on this release!
  $CONTRIBUTORS

================
File: examples/human_in_the_loop/breakpoints.ipynb
================
{
 "cells": [
  {
   "attachments": {
    "e47c6871-a603-43b7-a8b0-1c75d2348747.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAACHEAAAOGCAYAAABhwp6pAAAAAXNSR0IArs4c6QAAIABJREFUeF7s3QmcTfX/x/HPrMYMxjbGWPsp+rWIikpSKlKkskSbypKdSosURbbsSSr8EJKslcoS/VOyhFCKopR9hmGYwZj9//h8ubczd+7M3DtzZ+bOzOv7eHjU3HvO93zP89y598457/P5+qSlpaUJDQEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQKBABXwIcRSoPxtHAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQSMACEOXggIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIAXCBDi8IKDwBAQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAFCHLwGEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABLxAgxOEFB4EhIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAChDh4DSCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAl4gQIjDCw4CQ0AAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBAhx8BpAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQS8QIAQhxccBIaAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggQ4uA1gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIeIEAIQ4vOAgMAQEEEEAAAQQQQAABBBDIjUByVJQk/fmn+FWoIIFXXini45Ob7lgXAQQQQAABBBBAAAEEEEAAAQQQQAABBApIgBBHAcGzWQQQQAABBBBAAAEEshJIPnxYot94Q3xLlZLAq6+WEtddJ/4REeafT4kS4CFgF0jYuVMiu3Sx/xzUoIGUf/VVCbjsMpQQQAABBBBAAAEEEEAAAQQQQAABBBBAoJAJEOIoZAeM4SKAAAIIIIAAAggUD4ELW7ZIVM+eTnc2oGZNCfzvfyWgdm0pUb++BNWvL+LrWzxg2MsMAsc6dpTEffvSPe4bEiJhEydKUMOGiCGAAAIIIIAAAggggAACCCCAAAIIIIBAIRIgxFGIDhZDRQABBBBAAAEEECg+AikxMXJqxAhJ+PlnSTl1Kssd9ytfXkK7dJFS7duLT2Bg4UJKSbk49UdRDaGkpkrs/Pmix7Nc//4ePzbJhw7JkQcfNP1qcCO4eXM5+9ln9u1UmjJFSjZu7PHt0iECCCCAAAIIIIAAAggggAACCCCAAAII5I0AIY68caVXBBBAAAEEEEAAAQQ8JpDw668S+eSTEli7tpTt00eSIyMlcfduufDTT5J85Ih9OwG1akn4tGniV6GCx7adpx2lpcmh22+XwLp1JXzq1IthjiLWUs+eNfuorfKcOVKibl2P7uGFrVslqkcP02eVxYsl4PLLTVWO4/37S0pUlHmcIIdHyekMAQQQQAABBBBAAAEEEEAAAQQQQACBPBUgxJGnvHSOAAIIIIAAAggggEDuBc6vWycnBgwwAQANAlhb4t69cnrKFInfsME8rFOthP/vf4UjyJGaKgcaNDDjrrJsmQRcdlnusXLbQ2qqqWSRev68lG7XTnxKlsxVj6lnzsihO+80fVQYOlRKPfBArvpzXPncihUSPXiwOe5VPv3U/rRWbznx/POSsGuXeazS1KlSslEjj26bzhBAAAEEEEAAAQQQQAABBBBAAAEEEEDA8wKEODxvSo8IIIAAAggggAACCHhU4OyyZXJyxIgMF+qtG9EpO2ImTDAPBd95p4Rd+n+PDiQPOjvUpImknjsnlSZPlpJNmuTBFtzr8sKWLRLVs+e/juPG5WqqF51G5fDdd5v+yg0YIGWeeELSEhMlNSbG9OsTHGymQclpi50zR2ImT5YS9etL5Vmz0nWTGhsrUc88Yypz6Daqfvml+IaG5nRTrIcAAggggAACCCCAAAIIIIAAAggggAAC+SBAiCMfkNkEAggggAACCCCAAAK5EYhbuFBOjRkjfuXLS7W1azPt6tTo0RK3eLF5vua2bbkKH+RmvO6se7RNG0k6cEDKv/yylH7kEXdWzZNlE/fskWOPP27vu2yvXhL6zDM521ZKiqmEEdmli319v/Bw+zQntgdzU6HjzMyZclqrbDRpYoIwji0lOloO33OPeZhpVXJ2GFkLAQQQQAABBBBAAAEEEEAAAQQQQACB/BQgxJGf2mwLAQQQQAABBBBAAIEcCMR+9JHETJxoqilUX78+0x5iP/xQYt55xzyvy+WmwkMOhpmjVY62by9J+/dLaNeuUrZPnxz14emVEn//XeIWLZLza9ZIQJ06UnnmTJc3Eb9+vejxSjl+3IRTXGmVJk2Sknfc4cqiGZaxVeIIathQwqdNc9qHhkgSdu6UiI8+ksCrr87RdlgJAQQQQAABBBBAAAEEEEAAAQQQQAABBPJHgBBH/jizFQQQQAABBBBAAAEEcixgnSql5vbtTvvRig/He/c2U5M4q9ihwYQLmzdLwp49Iqmp4h8RISUbN5agm24S8fHJdGzJkZGmcoR/9eqmX083W4ijzNNPS7n+/dN1n3z0qMSvWyfJ0dFSuk0bMwZvb4ebNZOUU6cyHaaGLUrccIME1qkjgVdcIf5Vq+a4YkrKyZNybsUKiZk0SQL/+1+J+Phjp9tNOXFCko8cMVOu5KZpQEVfZ4l795qAUECNGhJ8990ScMUVuemWdRFAAAEEEEAAAQQQQAABBBBAAAEEEEDAIkCIg5cDAggggAACCCCAAAJeLqBVIU699ZYZZfVvv5W0hATxLV9edKqMpL//lrOffWaqRtha+YEDpXTHjvaftQqDdUoP6+5qkKPC66+LX1hYOoWkv/6SmClTJP777+2Pa1Cg3IsvStANNzgXS001F/g1NCD+/uJXrpwE1KwpPiVLZiqcIcSRkiLxGzde3Kdvv7WvF3LffVJx5Ej7z7q/Z2bPFg2nBF5zjZS6/34p9eCD4hMU5HRbF7ZulbglSyThl18k7exZ0WlNSjZqJCGtWpkAhCst+dgx8Stb1r4/Gow4NXasCW2Edu4swXfdJUdatzaBCW26DQ1qxG/YYH4O7dZNyvbu7cqmsl3mzPTpcvqDD+zLacCm8rx54l+5cpahHF0hNTbWWJxfu1aSDx0Sn1KlJOA//5FSLVtKyTvvdFrBJXbePBMWcdbKdOpkqqj4BAZmO24WQAABBBBAAAEEEEAAAQQQQAABBBBAAIGsBQhx8ApBAAEEEEAAAQQQQMDLBc4uWyYnR4xwaZSlHnhAKrzxhv1Cvl6wP9KqlanQkVnTqgphb78tQTfeaBY5t2qVRL/6aqbLV1m61Fz0t7bEPXvk5NChkrhvX7rHNchQZcmSTKd2sYU4gps3l8DLLzfTmFgrWWg4QcMRIa1bS4m6dU3fp8aNk7gFCzKMT5cNGz8+XcWJ5KgoiRk7Nl0gxHHFEtdfL2Fjx4pfhQqZ7rNO+aJjVatqa9aYIM3RRx4xVUpsrcqiReIbGirnv/tOSlx7rQReeaU5Dofvu88sF9q9u5Tt2dOl45jdQhoeifvkE6eLacUNU+XjsstMiEZ/NlPrpKXJua++klNjxmT5etDXjwZibO3Ctm0S1b17lkMKrF1bwt55R/zDw7MbOs8jgAACCCCAAAIIIIAAAggggAACCCCAQBYChDh4eSCAAAIIIIAAAggg4OUCZ5cvNwEJV1rVL78U/ypV7IvGLVxoLtprC77zTlNJQ6dSST171lTZ0GobGjDQgETlOXNM2OHECy/Y19eKHiVvu03if/hBtC9tGkTQQIKtaZWLqB497D9rYMBMfZKSYkIdpkrErFniX6NGhl2whTic7ZtWrdBpVnz8/f/d1o8/SlSvXllSRMyfL4FXXWWqTByxhBF0JR1L6Q4dLu7/+vWSdOCA6UvDJuFTp0pArVqmkoiGFoJuvlnKv/KKeV7349il6iYaSomZPNmsb21ajaNsv34ZxnasUydJ/O03Kd2+vZTPIhzjyvG1LZP0zz8SPWSI6deVVrZXL1O5JW7x4nSLm/DMlVeafqyVT8y+9OljpnrR14PtOX1M90PDKvq6Ofvll3J66lTTZ+gzz4huh4YAAggggAACCCCAAAIIIIAAAggggAACORcgxJFzO9ZEAAEEEEAAAQQQQCBfBM6tXi3Rgwa5tC0TRnj/fQm47DKzfORTT0nCrl3m/6utXp1h2hStKJHw22/i4+dn1jly//2mSoMGMSpNmWKvaqGVNo49/rjpp/TDD0v5S+OxVmnQbVccMeLidCs+PmIdd3DTphI2cWKGfXAW4tDgSGiXLhnGqiufGDgw3dQxOhVK+HvvmX08/uyzpn+dXiVi7lwzLctxS6hCK1KETZhgpnkxLTVVzn/zjZx8802zz/5Vq4pWGdGAxLFHHjGL1Ny2zQQZdNqao+3amcd0OduUKUENG4pOPaPVQ/T/w6dNy7CPkV27SsKOHWbqlorDh7t0HF1dyFo1RY39q1UTrT6SuHu3fYzal4ZX/CIi0oU+dCw6JlvTUMapiRPtvhWGDpWQ5s3lYOPG/7rOmWM8rC319GlJ2LNHAqpWdRrUcXVfWA4BBBBAAAEEEEAAAQQQQAABBBBAAAEERAhx8CpAAAEEEEAAAQQQQMDLBeI3bLCHEbQyhFaS0CoXyceOSeKff0riH3/IuS+/tF+01wBG1ZUrxbdUKTnUpIkJKLgSIIiZMEFi58+3a2g/GnzwCQxMV6VBpx4JbtZM0pKS5MgDD5iKDLpsxOLF4l+5sn396MGD5dyKFfafK8+eLSXq1UunbatSYX0wYt48E8Rw1hxDH9apXc7MnGmvClFp0iQTtjiqVhrsqF3bVBrxCQrK0K3VV0MYfmFhcrRtW7Ncta+/Fr+KFU0IQ8MY1qZ9hs+aJRc2bjThEjWo/t13GUIOtiBNSMuWJuTiyZauQsiiRRJwxRX27rXaSEpkpJn6xb9mTTk5fLg9oFFx9GgJadEi41BSUyWyc2cTitFpZioMGWK3cJxmxZP7QV8IIIAAAggggAACCCCAAAIIIIAAAgggcFGAEAevBAQQQAABBBBAAAEEvFwg4ddfJfLJJ80o7SEOxzGnpMiZDz+0hxjKPfuslHnySTlw441myVIPPSQVXn890z1NS0yUg7fcYp63VppwXCHkvvsuBhF8fCTh55/NBX9tlWfONBf9bc0aLrA9ps/rctamlTI0RGFtJRs3NlVAMrTUVDnQoIH9YZ36RKc2sTUNKxxs1Mj8qNN6BN91lxx9+OGs3URMYEGDFtoqvf22CZocuvNO87NWNdFpVbRix4mXXrJvSytbRCxYYAIfqWfO2Jev+vnnF6eSsbTILl0kYedOM52NVgKxG+3ZIydHjTLT0+g+56Rp1RBb4MRZSMbap62KiQmbfP+9OYbO2vE+fSR+0yYTfCk/eLDdRo+7BlFoCCCAAAIIIIAAAggggAACCCCAAAIIIJB3AoQ48s6WnhFAAAEEEEAAAQQQ8IiATt1xpHVr05dOM1K2b1/n/aalmcoYuryGAjQwYAtmZFeJwxpkiFi4UHwCAiRuwQK5sGWLpMbFSeBVV4kGOELuvddeacIabNBQgFb+0KbBgqhevUyFDsdmDQKkREfL0TZtTKUQDTho5YgLW7eaVUq1bWsCKz7+/vYuUk6ckMMO1SMiPv5YdEoVbdYgSmjnzhLSurU94FDu+eelTKdOGcaTEhMjJ154wYQstFVbudIEM2xhkdDu3U3IIm7RIjn11lv29dVIQw62psdH3Z1VuLCFJxxDLKfGjpW4Tz6R4ObNJWzMmBy9VtTs0O23m3WzC1lEv/aanNMKLSEhUm3NGqdVSazTs6hXydtuk6gePUz/VOLI0SFiJQQQQAABBBBAAAEEEEAAAQQQQAABBNwSIMThFhcLI4AAAggggAACCCCQfwJ6gV6DEdZwQlYX/JP++steeUJDGxUGDZKDt91mBuysCoZ1T6whhWrffCN+5cplu6PWShxBDRuaMELi7t1y9rPP7OtWGDpUSlx9tX1aE32i/MCBUrpjRzkze7acvlRxQ8MS+tixxx6zhz+00oZPyZJS/uWXpUTduukqZtg2oIEE7c+vcmWJW7zYPl1I2T59RIMcRx580D7NTMU335QSDRoYU52KJn79eon7+GNJOXXKdBc+Y4YEXapccrhZM/N4qQcfNOGFmMmTJXbOHLOcqXJyqXKHbRy2gITug47H2mImTZLYefPSTbeSGhtrAia6jdCuXUXHm9NmG2tot25StnfvTLs5t3q1RA8aZJ4v2aSJlO3RQ/xr1JC0+HjRai/nvvhCzq9bd/H522+XsPHjTahGK3No0zCLHicaAggggAACCCCAAAIIIIAAAggggAACCOSdACGOvLOlZwQQQAABBBBAAAEEciSgF9Ujn35adEoSnbYjuFkzOffVV6ZiRYn69SX8gw/EJzBQJDXVhBGS9u83F+HPzJhh317lOXMkoGZNOdS0qf2xmtu3ZzqecytWSPTgweZ5E4Do2jX7saelSWTXrvYqFo4r6NQkGgbQpmEAW0UH/Tn47rvFr0IFU+FCm206lqQ//zRTtOi+2lrphx+W8oMGpeujzOOPS+z8+U7HqPtdee5c8S1d2oQ6tBJGdq3SpElS8o477IuZ/dqxw4Q1NLQRt2SJnBo1SvzCw0WnTDH+lmYLwejxqrZ2bbrnNMChQQ5tOq2Nf+XKcvaLL+zhkqpffGGmsMlps1X6CG7aVMImTsy0Gw0DRXbqZF5XWbWgW26RShMnmkodGnQ5/uyzZnHH6WByOl7WQwABBBBAAAEEEEAAAQQQQAABBBBAAIHMBQhx8OpAAAEEEEAAAQQQQMDLBJKjouTIfffleFRaQSH4rrtMyOPYE09I4u+/Z1uJQytDHGnVyh6e0Ok9tLKGs5Z24YIkHz0qPiVKmH8abrBVcNDlTZWHvn3TTTeij1sDAfpz2OjRcmLQoAzTiSRHRsqpkSMlfsMGs/nQHj1M1QhrEKTGjz+aqUFixo2zj1mrdZRs2lQ09GGb2kXX12lf4hYulAvbtqXbHZ2GRUMVOk2MBj6sLW7pUjOGisOHi1Y10XZ22TIzrYz+c2wakDjWsaP4li9vAinWlnzokKkI4qyVGzBAyjzxRI6Pta544ccfzfQ1IS1bmilVsmo6hU3shx+KTptiq0Ciy2tFE33NlGrf3lQ9sTXrFDZU4sjVYWJlBBBAAAEEEEAAAQQQQAABBBBAAAEEXBIgxOESEwshgAACCCCAAAIIIJB/AhqSiOrdO9MKF5mNpFTbtlK6QwcJrFPHvkhKVJTEfvyxhNx/f4ZQhWM/1moc+pxWXtBggJly49w5Sdy71wQrNIyhTatSVFu50vx/SkyMpJ45I/5VqmSoUmHdTurp05Lwyy8SeO21psqI7qupauHrm344aWkmfCF+fhKs1Tz8/NKFOGpqIEPX0WokUVEmtOEYxHDcPw1a6Di1+VesaPrMqqUcP26qhWS3nK2PtKQk8QkIcNqldToWY9u8uZR57DEpUa+eR15YyUeOmOPh4+/vcn8a4khLSDABDt8yZTJdL2HXLlPRRKdryWo5lzfMgggggAACCCCAAAIIIIAAAggggAACCCCQqQAhDl4cCCCAAAIIIIAAAgh4oUBacrKZJkVDGFolI/X8eUk7f15S4+NFkpPFNzRU/MqVM//1LVfOTJ2SXYjBld3UCg3Rr77qyqJS6oEHpMLQoS4t64mFrJU4spoaxhPbyos+En7+WTTEolPi6HGjIYAAAggggAACCCCAAAIIIIAAAggggAACjgKEOHhNIIAAAggggAACRVRg3Lhxsm3bNnn33XclLCysiO4lu5UXAjr9R+z8+RK3aFGG7jWAULJxYwlq0MBjVSRc3YeEHTsksmtXs3iNzZuzrPjhap8shwACORfgcybndqyJAAIIIIAAAggggAACCCCAAAIIZCZAiIPXBgIIIIAAAgggUMACZ8+elVmzZsnjjz8uFXTqBg+1W265RY4dOyb/+9//pHnz5h7qlW6KlcClqUpSY2LMNBp+lSu7NV2Hp62S/vlHjrZta7qtunKl+IeHe3oT9IdAkRTgc6ZIHlZ2CgEEEEAAAQQQQAABBBBAAAEEiqgAIY4iemDZLQQQQAABBBAoPAJz586VIUOGyIsvvij9+vXzyMDPnz8vV111lenrvffek1atWnmkX2edJCUlySeffCJXX3213HjjjXm2HTpGIPXsWTl0++0GovLs2fleCYQjgEBhFSjsnzPO3JOTk8Xf37+wHhLGjQACCCCAAAIIIIAAAggggAACCGQqQIiDFwcCCCCAAAIIIFDAAq+//rrMmTNHnnnmGRk8eLBHRvP7779LixYtTF8jR46UJ554wiP9OnaSmpoqPXv2lNWrV8uzzz4rAwYMyJPt0CkCNoHDzZpJyqlTUrpjRyk/cCAwCCDggkBh/pyx7Z6GNubPny8LFiyQPXv2mIe1elXHjh3N509QUJALEiyCAAIIIIAAAggggAACCCCAAAIIeL8AIQ7vP0aMEAEEEEAAAQSKgIBOa9K3b18TpmjTpk26PerevbsJQWiQo2nTph7ZW+1P+9U2fPhwefLJJz3Sr2MnU6ZMkfHjx5uHCXHkCTGdOgicGjNG4hYuNI/W+OEH8QkOxggBBETM9FlF8XNGD65OB6NBx40bNzo91nfddZdMnz5dAgICeC0ggAACCCCAAAIIIIAAAggggAAChV6AEEehP4TsAAIIIIAAAggUBoHFixeb6VJCQkJk9+7d6YbcpEkTOXjwoPzyyy8SGhrqkd2ZNm2ajBo1yvT17rvvSuvWrT3Sr7WTzZs3mzugbY0Qh8eJ6dCJwIWtWyWqRw/zTPiMGRLEFD68ThAwAkXxc8Z2aCdPniwTJ040P2oYUv/p56lW5vjggw/M47NnzxYNc9AQQAABBBBAAAEEEEAAAQQQQACBwi5AiKOwH0HGjwACCCCAAAKFQmDu3LkyZMgQM9bt27ebEvDaoqOj5cYbbzQ/6+OeauPGjTPhDW2LFi2Sm2++2VNd2/vp3Lmz/N///Z/9Z0IcHiemQ2cCKSlyfMAASfztN6k8f774h4fj5GUCKdHRcmrcOCnzxBNSom5dLxtd0R1OUfycsR0tW4jjqquukhUrVoivr6/9QLZr1062bduWp1Wniu6rhj1DAAEEEEAAAQQQQAABBBBAAAFvFCDE4Y1HhTEhgAACCCCAQJETeOWVV2TBggVmv5YvXy716tUz/7927Vrp2rWrtG/fXiZMmJBhv7U8fnx8vEREREjJkiVddhkxYoTMmDHDLJ9ZhY8dO3bIr7/+avq+6aabpEyZMi73rwumpKSYEvcffvihuUOaEIdbfCycW4HUVBHLhdzcdsf6nhOI37BBjvfrJ4G1a0vEpalvPNc7PWUm4G2fM8nJyfL999/L4cOHRcMX9evXz/F0J/pZs3DhQrnmmmvklltusROkpaVJo0aNzFQyb731ljz66KO8QBBAAAEEEEAAAQQQQAABBBBAAIFCL0CIo9AfQnYAAQQQQAABBLxdQMMODRs2lJMnT5qhvv/++9KyZUvz/0OHDjUl4N9++21p06aNeUwvVukyS5cuNRembO3hhx+Wp556Suo63Nn+888/y7p16yQuLk5atWol119/van6oXdl33HHHea/1nb06FF57bXX0lXR0OdzWoreFhgZMGCACXLQEECgeAvEr18vx599VvzCw6XaypXFGyOf9t7bPme2bNkir776quzbt88uoIFBDWLUrFnTYyoaUrRNF/bVV1/Jtdde67G+6QgBBBBAAAEEEEAAAQQQQAABBBAoKAFCHAUlz3YRQAABBBBAoNgI6MUsDWDY2uDBg+WZZ54RvUv5uuuuk3PnzsmmTZukSpUqosv27NnTHvjQdfSilFbMsLXx48fb+5syZYroz9b2+eefy0cffSSLFy8W27Zsz0dGRpp1Dx48aB7S0MeePXtk//79ZkoXLUlvLVPvykEaNGiQfPzxxyYY0r17d1dWYRkEECjCAvHffy/Hn3tOfENCpPr69UV4T71n17zpc0Y/zx555BGDExISIs2aNTNVp/Szrm3btjJp0iSPwY0aNUqmTZtmtrNr1y7x8/PzWN90hAACCCCAAAIIIIAAAggggAACCBSUACGOgpJnuwgggAACCCBQbARef/11mTNnjn1/9YLWzJkzTZn5Tp06mTLzq1atMs9rEEIDGNpq164ts2bNkho1apgpVTSUoRU2tGmljqCgIOncubP5+Z577jEl5n///XdTsn7kyJHmgtlLL70kffv2Ncto2XmdtkWDGrpNHVN4eLj89ttv9sog27dvN2EOd1r//v1FgyO6zSeeeMKdVVkWAQSKoMD5devkxIABhDjy8dh6y+eMVpxq0qSJPbAxZswYCQwMNJ9lw4YNM59rGujwRDty5IjceuutpquBAwdK7969PdEtfSCAAAIIIIAAAggggAACCCCAAAIFLkCIo8APAQNAAAEEEEAAgaIsYK22MX/+fHn88cfN7moJ+Oeee85MaaIXtp5++mnzuN6hrFOraJDi22+/ldDQ0HQ8y5cvl379+pmLZElJSbJ582ZT1UPDHz4+PmZZaxBEp1b57LPPzOMrVqyQXr162furV6+eVK9eXb788kvzWE4v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M2faggUL7Mt5C2z+/Pn29cIF1qhxY9ti2062XcdOtu0221inbbe2jttubY0aNaqU3aOfCCCAAAIIIIAAAggggAACCCCAQKoECHGk6nDQGQQQQAABBBBAoDiBFStWBANpGlQL/6v/VxnqDh06WPv27YtbIUsjgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAjQkQ4qgxejaMAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgj8T4AQB2cDAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAANXyPYkAACAASURBVAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAKBAhxpOAg0AUEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQIAQB+cAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQAgFCHCk4CHQBAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAgxME5gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpECAEEcKDgJdQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEECHFwDiCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAikQIMSRgoNAFxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAUIcnAMIIIAAAggggAAC1SKwatUqmz9/fmZbzZo1syZNmlTLtqt7I6tXr7avvvoqs9n69evbRhttVN3dSMX2Zs+ebS+88ILttttu1qVLl1T0iU4ggAACCCCAAAIIIIAAAggggAACCCCAAAJpFSDEkdYjQ78QQAABBBBAAIEKEPjhhx9s8eLF1rJlS1tnnXXy9njChAk2ZMiQzDKjRo2y4447rgL2svgufvPNN9axY8fMC7fbbjt77rnnil9Rhb9i3LhxNnTo0MxeHHvssXb55ZdX+F7RfQQQQAABBBBAAAEEEEAAAQQQQAABBBBAoHwChDjKZ8uaEUAAAQQQQACBWifwwQcf2Kuvvmrvv/++vffee/bxxx9n9rFr1662ww47mAILBx98sDVq1Chr///0pz/ZBRdckPk3Qhy17vTI2iFVI9l7773t008/zfr3d955xzbYYIMa2fklS5bYsmXLgm3Xq1fP2rZtWyP9YKMIIIAAAggggAACCCCAAAIIIIAAAggggEAuAUIcnBsIIIAAAggggAACBQU0Fcof/vAHu+qqqwouqwVat25to0ePtl122SWzPCGOulWJQ+eMQj2qSuK2SZMm1Vh44pprrrE77rgj051HHnnEevToEeucZiEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQKA6BAhxVIcy20AAAQQQQAABBCpYYO7cuXbuuefaG2+8UdReNGnSxJ555hlr37598DpCHHUrxKFjfvPNN9stt9ySOW969eplDzzwQFHnUZIL+yGO+++/33r37p3kJlgXAggggAACCCCAAAIIIIAAAggggAACCCBQJQFCHFXi48UIIIAAAggggEDtFli5cqXtt99+a02JoYDGXnvtZZ07d7aNN97Y3n33XXv55Zfts88+y4D89Kc/tccff5wQh1kwxcxzz9W9EMcPP/wQ7LfOjZ122skOPfRQa968eY390hDiqDF6NowAAggggAACCCCAAAIIIIAAAggggAACMQUIccSEYjEEEEAAAQQQQKAuCowdO9bOP//8rF3v1q2b3XrrrdaqVausf1fg46KLLrJx48aZAhyPPfaYbbnllpllqMRR90IcafudIcSRtiNCfxBAAAEEEEAAAQQQQAABBBBAAAEEEEDAFyDEwTmBAAIIIIAAAgggECmwfPly22OPPezrr7/O/PyYY46xkSNH2nrrrRf5mh9//NHuuece22effbICHFo4X4hjxYoVNm/ePFPlhs0228zq169f8lFRmGTOnDnWsGFD22STTaxevXolryt84TfffGOaVkYVSDbaaCNr0KBB3nVq+Y4dO2aWKWclDpkvXLjQVq1aZRtssIGtv/76Je+v1rF48eJgXaqwksa2ZMkSW7BggW244YZBWKiYRoijGC2WRQABBBBAAAEEEEAAAQQQQAABBBBAAIGaECDEURPqbBMBBBBAAAEEEKgAgdtvv92uvfbaTE8VYJg6dao1bdq0pN5HhTh69epl1113nT399NNZ61To4ZxzzrFf/OIXsba1aNEi+/3vf29vv/12MLVL2NRnrUvTeCiAss4668Ran0Il9913n73++uv23nvvZQVZtIJtttnG+vXrZ/37948MEhQb4lAFE20rbFdeeWUwXU3YFKQZMGCAff/998E/XXjhhbbvvvvabbfdFoRmtL2wbb/99tazZ087++yzrVGjRgX399tvv7XRo0cH2//73/+eZbfz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FC9hO+2000whkDgtqRCH3KKmcPn000/t5z//eaYrhDjiHBWWQQABBBBAAAEEEEAAAQQQQAABBBBAAIE0CxDiSPPRoW8IIIAAAggggEANCSxdutR22GGHzNY19cnbb79dpd74IQ5Ne6HpL6JaoRDHrFmzrFevXiX359JLL7UTTzwx8/rHH388K1ByxBFHZE03UuyG/BBHnNergoRCCFHND3EUCiu8/vrrdvTRR2dWpX3VPodNVUdUfSRsWn6zzTaL3LYCNO5UJgcffLCNHj06zi5ZUiGO2bNnR26PEEesw8BCCCCAAAIIIIAAAggggAACCCCAAAIIIFBBAoQ4Kuhg0VUEEEAAAQQQQKA6BTp27Jg11Ueuaghx+5SmEMcVV1xhv/nNbzJdV5WR8847L/P3vn372q233hp319ZartgQR6FQRrEhjjfffNMOP/zwTL+OOeYYu/LKKzN/90MckydPtjZt2kTu78SJE23w4MGZnx1wwAFBVZE4jRBHHCWWQQABBBBAAAEEEEAAAQQQQAABBBBAAAEE/idAiIOzAQEEEEAAAQQQQCBSQEGGd999N/Ozyy+/3I499tiStZIMcaxcudK22mqrrL506dIldt/OPPNM23fffTPL+6GH7bff3p5++unY6/MXLDbEode/+OKLts0220Rus9gQhx+8UEDljDPOyKxbAQ/tc9ieeOIJ23HHHSO3/cc//jGrisdJJ51kF198cSwbQhyxmFgIAQQQQAABBBBAAAEEEEAAAQQQQAABBBDICBDi4GRAAAEEEEAAAQQQiBQ4//zzbezYsZmfaUoVTbvRuHHjksSSDHGoAwphfPzxx5m+vP/++yX3bf78+bbbbrtl7ddbb71lG220UUn76oc4WrdubZdccomtu+66JsemTZta//79syqddOvWzWRUr169tbZZbIjjvvvus+HDh2fWc/PNN9thhx2W+btCHao+Ejb/524Hhg0bZvfff3/mn/wqJvmA0hbiuOmmm+x3v/tdpss33HBDcBxoCCCAAAIIIIAAAggggAACCCCAAAIIIIBAWgQIcaTlSNAPBBBAAAEEEEAgZQKffvqp/fznP8/q1emnnx5MOxIVNCjU/aRDHKeddpr95S9/yWzWrzZRqD/uz1evXm3t27fPesmvf/1ru+aaa4pZTWZZP8Sx3Xbb2XPPPZe1rocfftguuuiirH9T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1FJVZcL2CHbddVejNGr33HNPVVm17Qc/+EEQ3r5omzx5chBO35qctW5qMokO4hze4n3EEUdUNfOPf/xjkM7M2qBBg4I01RdffLH5+c9/XlVWDuHDDz/cHHfccYmOV10o5/7UqVONGP/2t781aq81cdtpp53MqFGjzMorr1w0usj677vvPqM5rvmoUOCuiaUiKx988MFm44037laHRAb63Nq2224bmeL73nvvNRdccEGl7Lhx44xSbrv27W9/OxBD+HbkkUeao48+OhhrpaRTuiHXdF+l7nafk5dfftkMHz48tN8aX13z0ksvGbVj3rx5VeW++c1vGqWyk4M9ypQa8JJLLjG/+c1vgrG1pjmodigateZKnIBA7VM7ZZp3Z2BMbtcAACAASURBVJ55prnuuuuM5rk/r/fee+/g8zAhge6zcOHCSht0rTvXNI5xghyl6thqq60aOgc13/Rs6H9ffPHFbvc+4IADzODBg4NnJMzCRBwSvmh+uOOha3feeWfzox/9yGyyySap+qg2KV39Cy+8UPVMiOMWW2xhjjnmmMh2hd1A46H61C6lAnHHRuW/9rWvBc/R/vvvHyqKyyLi8L9XNtxwwyC9pGsab/sMqU/6DnvmmWeMxD9PPvlk1VzecccdzbBhw0z//v1TsXvzzTeD+avn4g9/+EPlGq2dW265pfnOd76TyE589txzz8T7jRw50hxyyCGx5cReqT/l/JWddtppQZT4rOt7Ud+1iZ1MWaCI95Q812PbjTK+90gMqu8B17K8R8V977n9rmdNyWsep5xOqYs14rnIY01J3aGUBcs4j1M2nWJ1EEDEUQc8LoUABOongIijfobUAAEIQAACEIAABCAAAQhAAAKtR+Dmm28OHD/WbrnllooYQ87TX//615XP5Kju169f8O8xY8YEqY6t3X///VWO8LBUxGH05GD7yle+EgpWG8lpnF1xoyLHfZKIQ0IDOf3clM1+nY1I4SzxxKxZs2qeZCeffLI54YQTqq4PE3GowOzZsyPvo/HQvNAJ8yiTE0iO5meffTa2vXJuSgyw/fbb19yvWi7UWI4dO9bMmTMn1eUq64siFD3BFSbJGS0uYXbFFVcEDnZr6rOEDq717t079Fo59CWC0lx3HcN+4dtuu81ss802wZ/lLJfwIcwkRlJbowQCukbO7Yceeig0gsDcuXPN97///Sqh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/fY1dV3iHkW2dE2imv3337+m+nwRh7+m6/mSOOLPf/5zpf6459pnJ3GNmxLFbeTDDz9sjjzyyMqfxMRNFaMPrIDRFjrxxBODNGO+Pf3000ZOSteUplPRRNNY2UQceXzXpul32jJFvqeUXcSR91j4IoLf/va3ZtVVV007FEG5PNeUvN9TMnUkY+E8xqJRa0rGriUWL8P7e2IjKVAzAUQcNaPjQghAAAIQgAAEINC8BOREGzp0aHB6etSoUYlpRZq3p7QcAhCAAATKRMCmidEmmU7HxqVwKVO7aQsEeoqAH15bJ7V1ElUnoW1qK6VnOvfcc82XvvSlwGFvox3IeSonalqrdxNYzvGJEydWbidhw5133mmWXXbZtE0wzSLikFhmo402quqX0tjss88+qfuat3PkzTffNEpn5toBBxxgzj///LqiNqTuUEJBzQVfePCtb33LnHPOOUHEv6zmO7sUpWaXXXapqsZPL6EPm1HEoagBEsG4psgrShWy1FJLZUXXzcm3cOHC0Dqyijh0QEC/MV3L++R81s6qb/68UIQKiRuypC6x981bxOFH0NF9JIbZYYcdsnY1SL0jEYebgkTfFZdddpn5+te/nrm+JBGHKgybm1HfPT67fffdN2jbEkssUdW2f/7zn0HEKUWYsqa1Q+Il1+x3n/2b+nrrrbcaffdYU3Sqo446ykjIYS1rlKqyiTjy+K7NPBliLijyPaXsIo68x0Lrpxv5UEImrRFZLM81Je/3lCz9yFo2j7Fo1JqStW9J5cvw/p7URj6vnQAijtrZcSUEIAABCEAAAhBoSgKugEMdGDZsmBk3blxT9oVGQwACEIBAcxGQc0EnRWU6KSaHL1E5mmsMaW3jCbjpGOzdt91228BZJbv//vvN0Ucf3a1hV111lRk0aFDV35UCyXXwuR9q41+nPq3J+b/JJptUXb/66qubPn36hEJ48MEHg/dK1xQhQeksdM1qq61mPv74Y6Nw3xIc7L777mb99devKt8sIg412l3PbCckUJOzVKkGlMJAvN9+++3g44MPPriqr0U4R3SyXQI51+TYHD58uPnKV74SjMHKK69sdIJep+flpN9yyy1N//79C5/Y//73vwPHrJz6rilkvBxVcqivu+66RmkU1L533nkniNyktCFqv28SMPh1ycmryBty3CoyihsZxl4voc3AgQODuS1n72uvvWb++te/VqqXIEpRbqzJyax5bG299dYLxjfKnn/++SAijjU3AoDGQpEhXOvXr59ZYYUVEvn7kUx0gYRbevY32GCDQBSpej788EPzpz/9yShajO6t9vqWJkKErskq4hDHsBQ+OjSg1BhrrLFGIDR49913g/mnqA077bRTYt/rLaAxlCDBNzn3JejQOiQxjObde++9Z9544w2jlD2KbOOLZPIWcWi8JDLRuuja1772tSDShNKaat7Y9USiFI2v5rovftD1Ej6EpbDRd4FYa67YNUDPicZBz9khhxwSjIdraUQcKv/tb3+7EgHKXv/444+bddZZp6o+n50+VKQprQsSxSkqz8svv2wmTZpUJbpQuWeeecbo+8c1jZfWLpedWJ1wwglBP/V3PW/2vdNeG5ZqSuuO+9y791HqMonErKm9fnST5ZZbLpjj1opY323deXzX1vtM+dfn9Z6S93qsdpb9vcdlqfeun//851V4FYVGkdX0PaTvQz23eq/QGqXvRl8om+eaUuQ8znsO5vFc5Lmm5N2/ZprHefe93etDxNHuM4D+QwACEIAABCDQVgR0YlOnoLVRIxs8eHDVicm2gkFnIQABCECg4QQkJBwzZkwQQl8mp5McjmEOh4Y3jhtCoKQE5OTyhRcSBFin5O9///vAKe2bnNjuiWR9/qtf/SpwmNVqcadC5aSXI9A9RRp3nwkTJnR79ptJxBEWdSCqv2G57YtwjshBfuCBB1aJcZLGWo7s8ePHJxXL5XNfFJCmUgkVfLGGrnv00UfNYYcdlljFN7/5zSAijG9WCCVHvVLRpDU52eLuq2g5crKnNYmwNt5448Ticthljc5w5ZVXmr322qtb3UWJOHQjRbiwKZ+SOtWo36KffPJJILqyUYqS2mU/1zPuCnj097xFHKrzl7/8pVHEgSymyBISxYSZUtoomkcWu/TSS42eFdfSijgkvNhjjz2qrpVoREJC18JEHGnaqP2LM888M7TozTffbE477bQ01QRl9H2qNFO++SmaUlf4n4ISwDz00EOVy4pY323leXzXZu1fUvm83lPyXo/V7rK/97hsJXCT2MpNARbHPio1VF5rSpHzOGlOZf08r+cirzUla/uTyjfTPE7qC59nI4CIIxsvSkMAAhCAAAQgAIGmJeBH4GjUplnTAqPhEIAABCBQGAGdspw8eXKl/lmzZhGRozDaVNzsBPxw4uqPnFbHH3980DWdHNx00027dfPFF180vXr1qvp7kZvAupE2/Pfbb79UDoiwlH7NJOJQfxXN7tprr001xSS2WX755Stli3KO6J1fjvSrr746Vbt23XXXbtEhUl1YYyGdij/11FMzCU3ESqmCfEtyCg8YMCBIORIWqabZRBzqu06pa849/PDDqeiffvrp5rjjjutWtkgRh1JhSGTmRvWJaqwbUShVh+oopCgh5513Xmh0lqhqwyI2FCHi0P3nzp0bCBXSCk0UHULRdcJMkWCUFuDCCy9MTUzPpKImuZZWxKFrlBbJX3MUGUjPoDWfnaIIhAkq3DYo5dKPf/zjyJRQctzqPmkii6ouzQH/e1H3ayYRRx7ftaknRsqCeb2nNJuIo4ixuOuuu4JoMmnspptuioyklceaUtR7Spq+1VKm3ndQ3TOvNaWW9sddU/b397z7S33/JYCIg9kAAQhAAAIQgAAE2oSAQq7OmDEj6C0pVNpk0OkmBCAAgRITcE+xKyLHcFwP0wAAIABJREFU9OnTEXKUeLxoWs8RkGPPDeWulijctk6WW+vq6grC4ltTSHlFYPMtKtR+2t6lyc/+97//3Sg3uZyMfooA9z4KwS/nnGu+iENOSOWIDzM5qOXAsKZT0H46gLT9qqecTrz+7Gc/C9IYxJkiR7ipLXznyAEHHFAlbnPruuOOO4IIANbiuNgyEkvYtARx4yBHsMaqkabUBTrtqv8kNoozRTFRtAr9r2//+te/gjmg6Bj+yWXNG0V6knhG0Ul8swICpU+RwzutJUXiCEuzE1d32kgcbh2KZqU1QGMcd2J75MiR5pRTTul2e1fEEbVW6CKlFVFaD2txc9S9icblnnvuCSJyxIkSFOUibeSetOOTVO6xxx4LRE4vvPBCLDtxueKKK6pECKrbFyLErTvuuhzH2bb5s88+M9dcc00gNPFTgPj9UjmlbooziX4kxFBqk7g1QHWMHj3aSFjnmqJzKGKStTiHsUL977jjjlVMlSLlgQceqFwfJoDR8z9z5sxu66dNFaTvnDSmeiQkCZtPaofSEfkprdx6Fa0lTUScqLYkReLIe31XO+r5rk3DNEuZvN5T8l6P1Yeyv/eEcVb0NX1/S7AXt8ZrLfNT+7j11bumFP2ekmWOpS2b13NR75qStr1pyzXjPE7bN8rFE0DEwQyBAAQgAAEIQAACbUBg9uzZwcaFTJtJcpRhEIAABCAAgZ4mIHGhRIYyCTl0akz/i0EAAq1BQCn8JC5Rmg85E5Zbbjmz8sorm3XWWcesttpqrdHJ//RCEVFeeeUVo0gYSyyxRHCaU/nrv/CFLwT9XWaZZXqsvx9//LFZuHChkaNVp/QVEUT811133cgT7o1qrBz+ShXy5ptvGkVxWHLJJYN5ou+CtO1THZpnqkfc119//YroQ85Z/X3ZZZet+k/joXs1u7333nvB2GqMxWGFFVYwq6++eo/POcvV8tf4aGz0bKiNa621VpAOZKmlluqxIVDqArHTsyt2mnd6ZsuwPqk9b731VpAGVc+s5aa2iVvWuStHsNL8yMFp12KtA2uvvXZN9dUyaHFRTPTsz58/30jgtckmm5hVVlmlllsE4/j6668Hz7zWEIkrNK6tbu30XVv2scxzLPQ8SEihtUpzW+9SWqP0fGgtkDgsreW9pqS9b0+Wy2MsWFNa//29J+domnsj4khDiTIQgAAEIAABCECgyQlsscUWwYayQofq9BEOsiYfUJoPAQhAoIUIuEIO5U9XHnUMAhCAAAQgAAEIQKB1CGRJRdM6vaYnEIAABCAAgdoJIOKonR1XQgACEIAABCAAgaYhYMP64hxrmiGjoRCAAATaioBCBuu01MSJE9uq33QWAhCAAAQgAAEItAMBRBztMMr0EQIQgAAE8iSAiCNPmtQFAQhAAAIQgAAESkxAzrHOzs4St5CmQQACEIAABCAAAQhAAAIQgAAEINBqBBBxtNqI0h8IQAACECiaACKOoglTPwQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhBoUwKIONp04Ok2BCAAAQjUTAARR83ouBACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQCCOACIO5gcEIAABCEAgGwFEHNl4URoCEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQCAlAUQcKUFRDAIQgAAEIPAfAog4mAoQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAKFELj99tvN/PnzK3WfcMIJZtVVVy3kXlQKAQhAAAIQaAUCiDhaYRTpAwQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACTU8AEUfTDyEdgAAEIAABCEAAAhCAAAQgAAEItA6BxYsXm8mTJ5uOjg4zatSo1ukYPYEABCAAAQhAAAIQgAAEIAABCEAAAikIIOJIAYkiEIAABCAAAQhAoNkIyAG21157mc7OTjNlypTAEYZBAAIQgAAEmoHAvHnzzNChQ4Omzpo1y/Tt27cZmk0bIQABCEAAAhCAAAQgAAEIQAACEIBALgQQceSCkUogAAEIQAACEIBAuQi4DrDp06ebrq6ucjWQ1kAAAhCAAAQiCPAdxtSAAAQgAAEIQAACEIAABCAAAQhAoJ0JIOJo59Gn7xCAAAQgAAEItCyB2bNnmxEjRgT94xRzyw4zHYMABCDQkgQUTWqLLbYI+qZ0KqNHj27JftIpCEAAAhCAAAQgAAEIQAACEIAABCAQRgARB/MCAhCAAAQgAAEItCCBSZMmmcmTJwc9W7hwYQv2kC5BAAIQgEArE+jdu3fQPUQcrTzK9A0CEIAABCAAAQhAAAIQgAAEIACBMAKIOJgXEIAABCAAAQhAoAUJIOJowUGlSxCAAATaiMDQoUON0qooHZjSgmEQgAAEIAABCEAAAhCAAAQgAAEIQKBdCCDiaJeRpp8QgAAEIAABCLQVAUQcbTXcdBYCEIBAyxFAxNFyQ0qHIAABCEAAAhCAAAQgAAEIQAACEEhJABFHSlAUgwAEIAABCEAAAs1EABFHM41W87V18eLFZsGCBZWG9+3b13R0dDRfR2gxBCBQWgKIOEo7NDQMAhCAAAQgAAEIQAACEIAABCAAgYIJIOIoGDDVQwACEIAABCAAgZ4ggIgjmroVIMybO9csmD/fvP/BB8YsuWQQth+rj8CgQYOC1AeDBw9G1FEfSq6GQNsTQMTR9lMAABCAAAQgAAEIQAACEIAABCAAgbYlgIijbYeejkMAAhCAAAQg0MoEEHFUj66iRsycOdM8LuHGSy+18tCXom+KyjF8+HAzatSoUrSHRkAAAs1HABFH840ZLYYABCAAAQhAAAIQgAAEIAABCEAgHwKIOPLhSC0QgAAEIAABCECgVAQQcfzvcCi6xpgxY8yiRYuqxqfXiiuaHfv1M3379An+V6b/v+C118ozjkssYYz+s2b/f4q/d2255f9ea6/597+Nsf/961/R/z9j7+cpkslHHxn97+ynnjJvvPNOVQ1DhgwxY8eOJSpHRq4UhwAEjEHEwSyAAAQgAAEIQAACEIAABCAAAQhAoF0JIOJo15Gn3xCAAAQgAAEItDSBqVOnmrPPPtv06tXLvPjiiy3d16jOnXTSSWbGjBlVH0u8MXyffYL/OlZaqS25FNnpGQ8/bCbdckuVmEPpVaZPn17kbakbAhBoQQKIOFpwUOkSBCAAAQhAAAIQgAAEIAABCEAAAqkIIOJIhYlCEIAABCAAAQhAoLkILF68OBAw9OvXz8iJ3m6mvkvE4dpmffqYW846C/FGwZNh8UcfmTGXX27mPPVU5U6KxqH0KhgEIACBtAQQcaQlRTkIQAACEIAABCAAAQhAAAIQgAAEWo0AIo5WG1H6AwEIQAACEIAABNqcgFKn9O/fv4rCwO22M1NOOaXNyTS2+8dMmGDmPPFEcNOOjg4za9Ys09nZ2dhGcDcIQKBpCSDiaNqho+EQgAAEIAABCEAAAhCAAAQgAAEI1EkAEUedALkcAhCAAAQgAAEIQKBcBMaPH2+mTZtWaRQROHpmfBSR4+Dzzze/e/nloAHDhg0z48aN65nGcFcIQKDpCCDiaLoho8EQgAAEIAABCEAAAhCAAAQgAAEI5EQAEUdOIKkGAhCAAAQgAAEIQKAcBLbYYgujdDLWZl10kenbp085GtdmrZi3cKEZ+p+0NorCMXfu3DYjQHchAIFaCSDiqJUc10EAAhCAAAQgAAEIQAACEIAABCDQ7AQQcTT7CNJ+CEAAAhCAAAQgAIEKAT+VyuABA8zEkSMh1FMEllzSDL3wQjPvP2lVlFKlb9++PdUa7gsBCDQRgb322sssWLDADBw40EyZMqWJWk5TIQABCEAAAhCAAAQgAAEIQAACEIBAfQQQcdTHj6shAAEIQAACEIAABEpEYN68eUant63NveIK07nGGiVqYfs1ZdIDD5jJP/950PHp06ebrq6u9oNAjyEAgcwEevfuHVwzatQoM3r06MzXcwEEIAABCEAAAhCAAAQgAAEIQAACEGhWAog4mnXkaDcEIAABCEAAAhCAQDcCU6dONWeffXbw98369DH3XXQRlHqYwLw33zRDTzwxaAUijh4eDG4PgSYisOeee5rf/e53ZuzYsWb48OFN1HKaCgEIQAACEIAABCAAAQhAAAIQgAAE6iOAiKM+flwNAQhAAAIQgAAEIFAiApMmTTKTJ08OWjRh5EgzZMCAErWuPZuyaPFi0/8/DlhEHO05B+g1BGohoPRY8+fPNzvuuKPp6OiopQqugQAEIAABCEAAAhCAAAQgAAEIQAACTUkAEUdTDhuNhgAEIAABCEAAAskExo8fb2bOnGmmTJnSNiksXBEHqVSS50gjSix47TWz18knB7eaO3eu6ezsbMRtuQcEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAgaYkgIijKYeNRkMAAhCAAAQgAIFkAr179w4KjRo1yowePTr5ghYoYUUcvVZc0bz4i1+0QI+avwvz5s83Q886y/Tq1cu8+OKLzd8hegABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAoEACiDgKhEvVEIAABCAAAQhAoCcJtKOIY/bs2WbEiBGmq29fM338+J7Ez73/Q8CKOLq6uozSqWAQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAQTQARB7MDAhCAAAQgAAEItCiBdhRx2Egco4YMMaMPPrhFR7a5uoWIo7nGi9ZCAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACPUsAEUfP8ufuEIAABCAAAQhAoDACm2++ufnggw/MsGHDzLhx4wq7T5kqRsRRptH437Yg4ijfmNAiCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAoLwEEHGUd2xoGQQgAAEIQAACEKiLwNChQ828efNMO6WxqIg4DjnEjD7ooLr4cXE+BBBx5MORWiAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAIH2IICIoz3GmV5CAAIQgAAEINCGBNpRxDF+/Hgzbdo0M+qww8zo/fdvw1EvX5fn/e53ZujYsW0lJirfKNAiCDQngcWLF5uOjo7mbDythgAEIAABCEAAAhCAAAQgAAEIQAACNRJAxFEjOC6DAAQgAAEIQAACZSfQjiIO2+dRRxxhRn/zm2UforZo3+zf/MaMuOCCoK8LFy5siz7TSQhAoH4C/fv3NxJxTJ8+3fTt27f+CqkBAhCAAAQgAAEIQAACEIAABCAAAQg0CQFEHE0yUDQTAhCAAAQgAAEIZCXQziKOsSNHmuEDBmRFRvkCCEy6/XYz+aabgpoRcRQAmCoh0IIEFi1aZCTikE2YMMEMGTKkBXtJlyAAAQhAAAIQgAAEIAABCEAAAhCAQDgBRBzMDAhAAAIQgAAEINCiBI455hgzZ84cM3jwYDNx4sQW7WV1t6xwZfrFF5uuddZpiz6XvZOT7rjDTL7hhqCZiDjKPlq0DwLlIDBv3jyj9VymSBxdXV3laBitgAAEIAABCEAAAhCAAAQgAAEIQAACDSCAiKMBkLkFBCAAAQhAAAIQ6AkCOsn8+OOPmx133NF0dnb2RBMafs+KiOOyy0zXWms1/P7csDsBInEwKyAAgawEEHFkJUZ5CEAAAhCAAAQgAAEIQAACEIAABFqJACKOVhpN+gIBCEAAAhCAAATanIAVcVx1wQVm0IYbtjmNcnR/0q23msn/5/8EjSESRznGhFZAoOwEEHGUfYRoHwQgAAEIQAACEIAABCAAAQhAAAJFEkDEUSRd6oYABCAAAQhAAAIQaCgBK+IY9d3vmtF7793Qe3OzcAKTZs40k6dPDz5ExMEsgQAE0hBAxJGGEmUgAAEIQAACEIAABCAAAQhAAAIQaFUCiDhadWTpFwQgAAEIQAACEGhDAhURx1FHmdH77NOGBMrX5Um33GImz5gRNAwRR/nGhxZBoIwEEHGUcVRoEwQgAAEIQAACEIAABCAAAQhAAAKNIoCIo1GkuQ8EIAABCEAAAhCAQOEExowZY2bOnGlGHXmkGb3vvoXfjxskE3BFHNOnTzddXV3JF1ECAhBoawKIONp6+Ok8BCAAAQhAAAIQgAAEIAABCECg7Qkg4mj7KQAACEAAAhCAAAQg0DoEJk2aZCZPnmxGfec7ZvR++7VOx5q4J4g4mnjwaDoEeogAIo4eAs9tIQABCEAAAhCAAAQgAAEIQAACECgFAUQcpRgGGgEBCEAAAhCAAAQgkAcBK+IYPHCgmXjMMXlUSR11EkDEUSdALodAGxJYsGCB2WuvvYKez50713R2drYhBboMAQhAAAIQgAAEIAABCEAAAhCAQLsSQMTRriNPvyEAAQhAAAIQgEALErAijq4ttzTTf/SjFuxh83XJFXHgjG2+8aPFEOgpAjNmzAhuPWTIkJ5qAveFAAQgAAEIQAACEIAABCAAAQhAAAI9QgARR49g56YQgAAEIAABCECgeALjx483c+bMMRMnTjRdXV3F37AEd6iIOLbYwkwfO7YELaIJrohj4cKFAIEABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAIEYAog4mB4QgAAEIAABCECgRQn07t076NmoUaPM6NGjW7SX1d2qiDj69TPTzzqrLfpc9k4i4ij7CNE+CEAAAhCAAAQgAAEIQAACEIAABCAAAQhAoEwEEHGUaTRoCwQgAAEIQAACEMiRQDuLODpWWsm8cO21OdKkqloJIOKolRzXQQACEIAABCAAAQhAAAIQgAAEIAABCEAAAu1IABFHO446fYYABCAAAQhAoC0ItLOIQwO8cMaMthjnsncSEUfZR4j2QQACEIAABCAAAQhAAAIQgAAEIAABCEAAAmUigIijTKNBWyAAAQhAAAIQgECOBBBxIOLIcTrVXBUijprRcSEEIAABCEAAAhCAAAQgAAEIQAACEIAABCDQhgQQcbThoNNlCEAAAhCAAARan8CiRYtM//79g45OmDDBDBkypPU7bYyZNGmSmTx5ctBXInGUY8gRcZRjHGgFBCAAAQhAAAIQgAAEIAABCEAAAhCAAAQg0BwEEHE0xzjRSghAAAIQgAAEIJCJwLx588zQoUODa6ZPn266uroyXd+shWfMmGFOOumkoPmIOMoxiog4yjEOtAICEIAABCAAAQhAAAIQgAAEIAABCEAAAhBoDgKIOJpjnGglBCAAAQhAAAIQyESgXUUcbr8RcWSaMoUVnjd/vhl61lmma/vtzfQZpLgpDDQVQ6CFCCialISInZ2dgRARgwAEIAABCEAAAhCAAAQgAAEIQAAC7UQAEUc7jTZ9hQAEIAABCECgbQgg4iASR1kme0XEsd12ZvrMmWVpFu2AAARKTMD9Dps1a5bp27dviVtL0yAAAQhAAAIQgAAEIAABCEAAAhCAQL4EEHHky5PaIAABCEAAAhCAQCkItKsDzPa714ormhd/8YtSjEW7NwIRR7vPAPoPgewE2lWImJ0UV0AAAhCAAAQgAAEIQAACEIAABCDQigQQcbTiqNInCEAAAhCAAAQgYIyZNGmS6ejoMMOHD28bHtbx19W3r5k+fnzb9LvMHa2IOLq6SItQ5oGibRAoEQFEHCUaDJoCAQhAAAIQgAAEIAABCEAAAhCAQMMJIOJoOHJuCAEIQAACEIAABCBQFAFEHEWRrb1eRBy1s+NKCLQrAUQc7Try9BsCEIAABCAAAQhAAAIQgAAEIAABEUDEwTyAAAQgAAEIQAACEGgZAtbx17nGGmbuFVe0TL+auSNT77nHnH3ttaaLSBzNPIy0HQINJYCIo6G4uRkEIAABCEAAAhCAAAQgAAEIQAACJSOAiKNkA0JzIAABCEAAAhCAAATqI9C7d++gAok4JObAepbApFtuMZNnzDCdnZ1m7ty5PdsY7g4BCDQFAUQcTTFMNBICEIAABCAAAQhAAAIQgAAEIACBgggg4igILNVCAAIQgAAEIAABCPQMgaH77GPmvfiimTBypBkyYEDPNIK7VggMHTfOzFuwIPj3woULIQMBCEAgkQAijkREFIAABCAAAQhAAAIQgAAEIAABCECghQkg4mjhwaVrEIAABCAAAQhAoB0JjD/+eDPt3nvN4AEDzMSRI9sRQan63HvIkEp7EHGUamhoDARKSwARR2mHhoZBAAIQgAAEIAABCEAAAhCAAAQg0AACiDgaAJlbQAACEIAABCAAAQg0jsC8a64xQ886y3SstJJ54dprG3dj7tSNwOwnnzQjLroo+Ptmm21m7rvvPihBAAIQSCSwaNEi079//6Cc0jApHRMGAQhAAAIQgAAEIAABCEAAAhCAAATahQAijnYZafoJAQhAAAIQgAAE2oXAk0+azY880nzw8cfmqpNPNoO2375del66fkpMM2/+/KBdXV1dZvr06aVrIw2CAATKSWD27NlBwwYNGlTOBtIqCEAAAhCAAAQgAAEIQAACEIAABCBQEAFEHAWBpVoIQAACEIAABCDQkwSmTp1qzj77bDNs2DAzbty4nmxK4+/9f/+vGTN+vJn58MNm4HbbmSmnnNL4NnDHQLwhEYe1sWPHmuHDh0MGAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEYggg4mB6QAACEIAABCAAgRYkMHToUDNv3rz2jH7wt7+ZRfPmmf7f+14wsnOvuMJ0rrFGC45yebu0+KOPAgHHgtdeqzTyhRdeMB0dHeVtNC2DAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIFACAog4SjAINAECEIAABCAAAQjkTaCtRRyC+cYb5pgTTzRznnrK9O3Tx0w/6yzTsdJKeWOmvggCIy66yMx+8snKp4MHDzYTJ06EFwQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCCQQQMTBFIEABCAAAQhAAAItSKDtRRzGmEXPPWf2/Pa3zQcffxxE4pg4cqTp6tevBUe7PF1SBI5jLrooSKVibbNNNjG3zJxJFI7yDBMtgQAEIAABCEAAAhCAAAQgAAEIQAACEIAABEpMABFHiQeHpkEAAhCAAAQgAIFaCSDi+F9yM266yZx0+ukVjBJxDBkwwAzcbjsic9Q6uSKum/nww2bSLbeYRe+8UynRa6WVzH1z5pjOzs6c70Z1EIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAARakwAijtYcV3oFAQhAAAIQgECbE0DE8d8JMGPGDHPSSSd1mxGDtt8+iMzR1bdvkHKlrDZvwYKGNk080prS1SjqhlKnuOINXd+1+eZm3Pnnm75bbZW2OspBAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEGh7Aog42n4KAAACEIAABCAAgVYkgIijelRnz55txo8fb954443I4S4y1YqbXqRZ55tS0nSuuWbQ/Lj+rLvGGmb4/vub4WPGGLPCCs3aXdoNAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQKBHCCDi6BHs3BQCEIAABCAAAQgUS2DMmDFm5syZpqOjw7zwwgvF3qyJaldUjjlz5pjHH3/cfPDBB03U8nI3tWvLLU3fDTYwXV/5ihm0337GdHQYs/TS5W40rYMABEpNYNKkSWbatGlmwoQJZtCgQaVuK42DAAQgAAEIQAACEIAABCAAAQhAAAJ5EkDEkSdN6oIABCAAAQhAAAIlISDn1+TJk4PWLFy4sCStKl8z5s2bl6pREn0k2aJFi4z+S7L58+eXUkDSq1cv069fv6rmd3Z2Gv0n0/+ut956VZ93dXUldZfPIQABCNREwEaUGjhwoJkyZUpNdXARBCAAAQhAAAIQgAAEIAABCEAAAhBoRgKIOJpx1GgzBCAAAQhAAAIQSCBgRRybbbaZue++++DVAgTSikTCuuqKMVoABV2AAATagABpwdpgkOkiBCAAAQhAAAIQgAAEIAABCEAAAqEEEHEwMSAAAQhAAAIQgEALEiASRwsOKl2CAAQg0EYEEHG00WDTVQhAAAIQgAAEIAABCEAAAhCAAASqCCDiYEJAAAIQgAAEIACBFiSAiKMFB5UuQQACEGgjAog42miw6SoEIAABCEAAAhCAAAQgAAEIQAACiDiYAxCAAAQgAAEIQKDVCSDiaPURpn8QgAAEWpsAIo7WHl96BwEIQAACEIAABCAAAQhAAAIQgEA0ASJxMDsgAAEIQAACEIBACxKYPXu2GTFiRNCzWbNmmb59+7ZgL+kSBCAAAQi0KgFEHK06svQLAhCAAAQgAAEIQAACEIAABCAAgSQCiDiSCPE5BCAAAQhAAAIQaEIC8+bNM3KAyaZPn266urqasBc0GQIQgAAE2pUAIo52HXn6DQEIQAACEIAABCAAAQhAAAIQgAAiDuYABCAAAQhAAAIQaEECixYtMv379w96NmHCBDNkyJAW7CVdggAEIACBViWAiKNVR5Z+QQACEIAABCAAAQhAAAIQgAAEIJBEABFHEiE+hwAEIAABCEAAAk1KoHfv3kHLR40aZUaPHt2kvaDZEIAABCDQjgT22msvs2DBAjNw4EAzZcqUdkRAnyEAAQhAAAIQgAAEIAABCEAAAhBoUwKIONp04Ok2BCAAAQhAAAKtT2DPPfc0v/vd74JUKkqpgkEAAhCAAASahYD9DkOI2CwjRjshAAEIQAACEIAABCAAAQhAAAIQyIsAIo68SFIPBCAAAQhAAAIQKBmBMWPGmJkzZyLiKNm40BwIQAACEEgmoLRgs2fPDtKBdXR0JF9ACQhAAAIQgAAEIAABCEAAAhCAAAQg0CIEEHG0yEDSDQhAAAIQgAAEIOATWLx4ceAAGzRoEA4wpgcEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAoAkIIOJogkGiiRCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEItD4BRBytP8b0EAIQgAAEIABglPEVAAAgAElEQVQBCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIACBJiCAiKMJBokmQgACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCDQ+gQQcbT+GNNDCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAASagAAijiYYJJoIAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgEDrE0DE0fpjTA8hAAEIQAACEIAABCAAAQhAAAKlJ3D22WcHbRw7dmzp20oDIQABCEAAAhCAAAQgAAEIQAACEIBAUQQQcRRFlnohAAEIQAACEIBAyQjIOTZjxgwzffp007dv35K1juZAAAIQgEA7EzjppJOC7yiZvqe6urraGQd9hwAEIAABCEAAAhCAAAQgAAEIQKCNCSDiaOPBp+sQgAAEIAABCLQXgc0339x88MEHpqOjw8ydOzf43//P3n3ASVLX6R9/Ovfk2RzJuwQBJUiSIEGSiiiwi4AJEMN5/hXRC3oG9PT0TJyYUYISl6gILEtGogoSlxw35508nf+v77eqentm08DMBnY+dbfMTHf1r6re1Tu8fPXD82VDAAEEEEBgUwv8/ve/V9TCscsuu2jmzJmb+pQ4PgIIIIAAAggggAACCCCAAAIIILDJBAhxbDJ6DowAAggggAACCGxcAfsvnO2/dLbNmjhuueWWjXsCHA0BBBBAAIF+ArX/bpo0aZIHOAgZ8jZBAAEEEEAAAQQQQAABBBBAAIHhLECIYzjffa4dAQQQQAABBIadwDnnnKNrrrnGr3vatGn60Y9+NOwMuGAEEEAAgU0v0N7e7u0b0QiVpqYmzZgxg3Ffm/7WcAYIIIAAAggggAACCCCAAAIIILCJBQhxbOIbwOERQAABBBBAAIGNLXDMMcfomWee8cMeffTRHuTgv3re2HeB4yGAAALDV2D27NmyUKF9tY0Ax/B9L3DlCCCAAAIIIIAAAggggAACCCCwugAhDt4VCCCAAAIIIIDAMBOw//p5+vTp1SCHjVa54IILNHny5GEmweUigAACCGwKgd1331327yLb9t9/f/93EGHCTXEnOCYCCCCAAAIIIIAAAggggAACCGyOAoQ4Nse7wjkhgAACCCCAAAIbWMA+PLP/CnrWrFl+JAtw3H///Rv4qCyPAAIIIICAFDVCfeMb39CZZ54JCQIIIIAAAggggAACCCCAAAIIIIBAjQAhDt4OCCCAAAIIIIDAMBY499xzdeGFF2rSpEl64IEHhrEEl44AAgggsLEELEhof2iA2ljiHAcBBBBAAAEEEEAAAQQQQAABBN5KAoQ43kp3i3NFAAEEEEAAAQQ2gMDs2bO9xp4P0zYALksigAACW7iA/Ttk7ty5sq/25+mnn/Z/n1x11VVb+JVzeQgggAACCCCAAAIIIIAAAggggMCGESDEsWFcWRUBBBBAAAEEENiiBL797W/7B3O77rqrBz7WtD344IM6+uijdcYZZ6z12u2/vLa15syZs06fAw44QF/84hcHvY6dr9X1r22z8/nyl7+stra2Ne5iH0jaPnZdv/3tb9e5zsknn+wfYK5rW9869kHoWWedtd513va2t+mWW25Z66EGus76zseufSiuy070U5/6lG699dZB+QzV+UT3vf/57L///n3Or6WlZZ33Pbqutb1/osW22mor/ehHP1rn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AQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgYEJEOIYmBN7IYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACG1SAEMcG5WVxBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEBgYAKEOAbmxF4IIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggMAGFSDEsUF5WRwBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEBiYACGOgTmxFwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggsEEFCHFsUF4WRwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEBiZAiGNgTuyFAAIIIIAAAggggAACCGwSgUqlolgsJvs61Juty4YAAggggAACCCCAAAIIIIAAAggggAACm48AIY7N515wJggggAACCCCAAAIIIIBAH4ENGeDoT02ggzcfAggggAACCCCAAAIIIIAAAggggAACm16AEMemvwecAQIIIIAAAggggAACCCBQFejfuGE/259SqahCoahisah8Ia9ioahyuayK/ZFkpRrBvuFSYclGIh5XLBaXfU0kk0qlUkqmUkolk0omk97yYZv9M1gn+DkKkHBrEEAAAQQQQAABBBBAAAEEEEAAAQQQQGDjCRDi2HjWHAkBBBBAAAEEEEAAAQQQcIF1jUbxkEY+r97eXvX29Kg316vOjk4tX7FcK5YtV1tbm5avWKHOzk7lcjkVC4U+quWKBTsqHvCwZEcU1sjWZdXU1KyW1la1trRozOjRGjFypBobG1VXV6e6+jr/mkym1niXaOrgzYsAAggggAACCCCAAAIIIIAAAggggMCGFyDEseGNOQICCCCAAAIIIIAAAggg4AL9x6PYzxbasDBGrrdXXd3dWrp0qRYuWKC5c+Zozpw5WjBvnpYsWaKVbW3q6elRsVTycEYikfCARjyeUDwRVzwW8yYNa+YolUq+bin8vlAoqFIqVZs2Mum0WlpaNGrUKI0bO1bbbruttt9hB22z3XYaP3GCmhoala2rUzqdrjZzRLeQMAdvZgQQQAABBBBAAAEEEEAAAQQQQAABBDacACGODWfLyggggAACCCCAAAII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Ir/6Rb6LR2SHipi0aVUs8yUugY1lskM4LoU+vyrFkiypknC5gBdJRZ5fVgf8slaiEonGpGN1XLYPi0QgcoE3BiqfJOhAbgKHjihKtYRjUhGvkCUDT5E2f7tMYu+9I/LwE1IVCsmHAy4wRBylLVQ5FYg2BIIZnygRhx/uI7Go+AJByS/IN4QYJgjlQ2KUeTGcOJRNikRjUQlXV0owHpYT999O9u3ZISkMqe3xweVIgARIgARIgARIgARIgARIgARIgARIgARIoKERoIijoe0RjocESIAESIAESIAESIAESMAzAqaIY9GKDTL3l7VSXh2XYCgkfr/hxKGEBVBxQJ2QEG+oEixwf1CjiCuHiHC42hAiQFagRBwi8SgcO1DmIy7hqipZ/uW7snLRDxIMQ77hk4jEJQLRgxJ+GK4cbX1+OSKYL50lIGXo29BsqNUbxVwMVw6UVoE7xabCAlldXS1tqsLSOu6T3/MCsikak+KAyGbxSfO4SIeoSCgWk8qCkPzmi0thVVhKCwpkJTarqkpaR0XyLUSNURsR8PkkGvRLJBiQynhcNkXCUhCOS2vVwJBQwB3DkFL86U6Cki/Y7up4RNb02keKjzhcovN+EP/770s4GJKfdh4g0mE7ad68QG2H4XpicFaOHBCsxOLiDwUkEAwqCD58mCChdkdit0hCNAPBTCQSFolFpFe3VrLjNqVG6ZWEk4lnBw07IgESIAESIAESIAESIAESIAESIAESIAESIIEtSIAiji0In6smARIgARIgARIgARIgARKoXwKmeMGQIziPpDlHpkX+tPDI2LHbdTsf5RZsCSGFZfsNDYizLXXWyti22u6/LUiGqyYBEiABEiABEiABEiABEiABEiABEiABEiABVwQo4nCFi41JgARIgARIgARIgARIgAQaKwGHGos6bJ4jqYfq32L84Xh9mZYxhRDOR+B4tc4aaiKOPxdyI9FwtirFLzfdOh8AW5IACZAACZAACZAACZAACZAACZAACZAACZBAjghQxJEjsOyWBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEhgyxIoKyuTcDgsLVu23LID4drrncDatWtVmd8WLVoYpZUbSVDE0Uh2FIdJAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQmcC8efNk6tSp8vbbb8uCBQuSjb/66itp1aoV8W1FBI4++mj54Ycf1BZ37NhRDjroIDnmmGOkf//+EgwGGywJijga7K7hwEiABEiABEiABEiABEiABEiABEiABEiABEjAWwIVFRWyfv36ZKdt27Zt0C8vvd36pt3bhg0bpLy8XG2kz+eTDh06NO0N5taRAAls1QRWr14tkUhEMSgoKJDS0tKtmgc3ngRIwCCAa8Odd94pr7/+ui2ShQsXNuln35UrV0osFuPzoGXv/+Uvf5EPP/ywxvHQvXt3ue+++2TvvfdukKcPRRwNcrdwUCRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTgPYFrrrlGXnrppWTHU6ZMkV133dX7FbHHeifQt29fWb58eXK9ixcvrvcxcIUkQAIkUB8ElixZIgceeGByVUcddZQ89dRT9bFqroMESKABE/j555/lxBNPFJROsYvWrVvLl19+2YC3oO5D22WXXVK2v6k8D0ajUVm3bp0S77Vp08aVEOfmm2+W8ePHp4V7//33y6BBg+oO3+MeKOLwGCi7IwESIAESIAESIAESIAESIAESIAESIAESIIG6EhgxYoQgSWXGK6+84vhl5fz58+Xqq69OLnv66afLkCFD1M9/+9vfUmYmUsRR1z3VcJZvqi/tGw5hjoQESMALAnCEWrNmTbKrdu3aSX5+vquuMZP+0EMPTS5DEYcrfI2mMRwFqqur1XhxjOBYaaoBl7RNmzapzYObVqdOnZrqpuZsu3777Tc5+eST5Y8//qixjoMPPlj23XdfgeC1T58+Nb6fOHGiPP/88ymfP/TQQ9KlSxfb8UIU8M0336jvDjjgALn22mtztl1uO25Kz4MQ5bz55pvyxhtvyC+//JKCAu4ZvXr1kgsvvDCr+9yiRYvko48+ktmzZ8uMGTNsj5HHH39cjj32WLe4c9qeIo6c4mXnJEACJEACJEACJEACJEACJEACJEACJEACJOCewJlnnimffvppckG8dHSavIBdMGyDzYBw48orr1Q/UsThfl80liWa0kv7xsKc4yQBEnBPQL9HXX755TJy5EhXHVHE4QpXo2183HHHyXfffZcc/w8//CDNmzdvtNuTaeB///vfBUlkM1588UXp169fk9zWXGxUPB6XM844Q2bOnJnS/fHHHy8PPPBAVqHYY489JqNGjUpZdtiwYXL99dfbDtf6nA5xyKRJk3KxWbXqsyk8D27cuFHgjvHcc885YoCSKKeddpqjtmgEwc5NN91Uo72b37ccr6wODSniqAM8LkoCJEACJEACJEACJEACJEACJEACJEACJEACuSAAJ40JEyYku3777bdl5513drQqLGd14rj33ntl8ODBalmKOBwhbJSNmsJL+0YJnoMmARJwRUAXcVx88cVy3XXXueqDIg5XuBptY13EAUFHcXFxo92eTAPXRRxIXh9yyCFNcltzsVFwarj00ktTuh46dKjcdtttEggEsq7STsRRWFioSq8UFBTUWJ4ijqxIa90ATioDBw6s4byRrUMIaSCocRqTJ0+W4cOHpzQ/9dRTZfTo0U67yHk7ijhyjpgrIAESIAESIAESIAESIAESIAESIAESIAESIAF3BB588MGUl4iYMda/f39HnTzyyCNq9poZ//rXv5K28xRxOELYKBtRxNEodxsHTQJbHQGKOLa6XV7rDaaIgyIOpwfP4YcfLgsWLEg2hwjgn//8p9PFxU7EgYVRUgUlWvSgiMMxWtcNzzrrLPn4449rLId9jLI4zZo1k6+//lpeeOGFlDa77babKrviRLRjLmgn/oET4rbbbut63LlYgCKOXFBlnyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRQBwIvvfSSXHPNNckeMCsMs8OcxC233JJiPzxlyhQ3uWjxAAAgAElEQVTZdddd1aIUcTgh2DjbUMTROPcbR00CWxsBiji2tj1e++2liIMiDidHD8rsHH300SlN3333XenZs6eTxVWbdCKOdKVSKOJwjNZ1w1mzZsmgQYOSyx111FFK2A5nFGvMmTOnxu9G77//vnTv3t3xOiORiBx44IGyfPny5DIooYNSOg0hKOJoCHuBYyABEiABEiABEiABEiABEiABEiABEiABEiABC4Hp06fLOeeck/zkhhtukIsuusgRI7x4nDp1arItrKBbt26tfs4k4igrK5NVq1ZJXl6edOzYUfx+v6P12TXCS9GlS5eqGuQdOnQQn89X677MBdevXy+rV6+WVq1aJbenzp267AA118PhsGKDmX5ebJfLIaRtnknE0RDY6QMHy5UrV0ppaWmNWvW//fab+qx9+/Zpt7e+9oWX50Vd9jWOOxxvuTzusA7zGoDzzM1sVrtt87o/6zo2b96ski5t27aVli1b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nSukx0TkePfFjUKuRo0aydSpU4WUh2pKQAkoASWgBJSAElACSkAJKAEloASUgBJQAukJqIhDZ4cSSJDAunXrTLSA//73v6lab7zxRunbt2+CZ9lYFalDjj/+eEGMYI1IC0RciGOkfyENjGvz588XBCiFMEQk7BIkDYxrRAP585//nDE1DAxq164dOI4oHldeeWXqM/oxadKkQIqWNWvWyIABAwLsiAjiRq6wFeRLxPHjjz/K9ttvH2g7Oct79uyZCPZiEHEMGTJEHn/88VR/zjrrLDnvvPMC/QsT06QTcRAZZPz48YHjH3nkEenSpUsizLQSJaAESpeAijiKb+xSIo6ddpKJTqq54muptkgJVC4CpFskbcmLL75oOnbsRbdK+317VWgnbz79UPnq4/mmDYWIVFihndWTKwElUJQEli9fLrx7wNhks+WWWxZlO7VRVZMAc5M5am2zzTaTBg0aVEoYCExJe8wPttVWW5kfBJ5bb711pexzqXdq1apVsnbtWtMN0lU3adKk1Luk7VcClZrAzz//bMT81urVq1cwP0+hwW7YsEGWLFmSOu2mm24qDRsWNvpkofuc7nyff/65PPPMM9K5c2fp0KFDsTRL25EAARVxJABRq1ACLoHhw4fLQw89lPqIRXPy5MlSvXr1yKAuvvhiIzAgGkW7du2kcePGZucaxk2Y73D4u2IRvsNBjqM8qq1fv16OPvroQOSK3//+90J6lUKaH8HEnhuRAwKP7bbbTtq0aWMeOFasWCFEGyFqBS+nX3vttcAfEAg7unXrFogswnFnnnmmqYOII6TvIMS0a4gNdt999zLdzpeIgxPRN78dRBI54IADzB+v1apVE/5Ysg9eRx11VKB9fOeLX2wBBA9uFIyrrrpKdthhh8DxPNS0atUq9VnS6VRuvfVWue666wLnPOaYY1Khvd9++21B5OTbzjvvLCeccIJpG5E24IB999130r1798DY8vlee+0lcCONEGNtufHw8sUXX5jrgj801ZSAEqi8BFTEUXxjGxBxIOjzRJfF12JtkRKoHARuueUWGT16tOlMx4P6Sf/zf02ZWFE9XLn8S7ntzD6y5tvlJurdlClTZNttt62o5lSZ85JyU6OeVJnh1o5mILBw4ULZe++9UyUOPvhgufPOO5WZEigaAqT5Jd2vtco2RxGpsOGKFNT+e0zbZzY08Z5TrfgI+GmwedempgSUQPESwA/FxlhrmdKlF28vorWMewq+G2tJRzqP1oqKL8Um1/PPPz/VkBNPPFGuuOKKim+YtiARAiriSASjVqIEfiXg5p+2n5JWYsyYMYIaMIoRYePll1+OUjRVZp999pF77rlHatSoEek4/oi68MILhZQUrhHJg4gehTac/Tj94xqCE4Qnrj388MOmb1Htd7/7XZkID/bYfIo4Xn31VSOiiWLsTCA1j2szZ840Yodc7Y9//KPwIGctaREHghnmZbo/0t3rg5f5YfbBBx8E0hGlE/xkYoDoyYqgcmWlxykBJVDcBFTEUXzjExBx/P3vIltsUXyN1BaVPIE3lrwr/OzQYDvZueEO0qjOViXfp/J04IcffjBiWZxBv9migZw25jFp1KJNeapM7Nj3X31W7rv0ZFPfwIEDZcSIEYnVrRWVJUDaSJ6BmQ/9+vWTgw46SDEpgSpLgE0gbJSwVtkc5FV2YCtRxyuziIONRzgTSV+cyQYNGiRsaFNLjgAb9xYvXpyqcIstthB25Me1qO9F49ar5ZVARRBI6rqoiLZHPWec9/tR6yzWciriECEaCc+5PEu49s4771RY5LmVK1cKkfAxNtay8VYtdwIq4sidnR6pBNISuOyyy+Tuu+8OfE9Ujdtvv71M+o+wSnyVczbUlL/jjjukfv362Yqa70n7cvbZZ8s///nPQHkiPdide5EqSrgQkRmGDh1a5qaT6TSIIPxoD9y87r333kgvhxGAjBo1yuwKDLOof6zk+kc3L7AR30QxX9BQ7CIO+pRNUIM4BSGR+1LNZeH3me9mz55t/rj3H07SMawoYVKUMdUySkAJJENARRzJcEyyloCIgwhlGrY8Sbxa1y8Ebn/7Prn9nftTPHZo0EZ2brSjdG7SQfZruZfUqrFZlWJFNECiAmI9T7tE9j7ylKLq/5RbLpF/TbnPiM6nTp0a2DVVVA2tBI0hbc2TTz6Z6knbtm1N2sZevXqZKIdqSqAqEVARR1Ua7dLsa67vk4q9t2weQ0QY5d1NZd4pXlHjtHr1amnfvn3q9J06dTLppuNa1PeicevV8kqgIggkdV1URNujnlNFHE9HRVUpypE+h7Xe30SL/6SixBPXXHONjBs3LsUX/xCR1NVyI6Aijty46VFKICMB0lwcfvjhZf5QIZwtUR9I27HrrruadBFhaR5wULPQZvtDp0ePHiavdJTIGYS7Q4H35ptvygsvvGDSTLhG23jRh1O9Io0dhBMmTDA7x+bNmyeEAQ4zUq3Qf3aX7bLLLqFlUPoTOoqIF75x/Kmnnip+ihK/nPvHCqk65s/fmM/bN9Tt7s2ob9++oalCwo4lugQ3ttdffz0j+lmzZkmLFi1SZShPGpFcLVskDsQx6SKF+GmDmFPpwmLDn5Be/pxDfHT11Veb66B169ah3QgTcVCQfO8IpUiD46ek8SuiXDqRSK7s9DgloASKi4CKOIprPGhNQMTxwAMkvC6+RmqLKgWBh+c/IbMWvy6zF80J9KfJbxrLfi33NGKOrk07Voq+ZusEERfmzJkj2+3WTU6+9tf0jtmOK9T3y774SG49q4/8+P1aOemkkwThu1r+CEyfPt2kkXzllVdSJyHtYO/evY2Yg7+l1JRAVSCgIo6qMMql3cfKKuKYOHGiXHDBBYHBIVrrgAEDjKCQzVQ4nZYvX24cTaQVVkuOgO+sJnWxK/CMeiYVcUQlpeVKgUBS10Ux91VFHFVLxGHev/31rwE/1L777mv+Dqwo80UcbLZmg7tabgRUxJEbNz1KCWQlgPgAR346EQIV/Otf/5KmTZumrYuwQ0uWLJFvvvlGvv/+exPFg9B3TZo0MVE3wgQgYZWR1qJjx/QvrxFuEK2gZcuWWftV6AL0+7PPPhOEMaSjadCggRGaxAkBSKg08uDCkuMQG2y2WfHtzKSvCxYsEB4oGVsiitSpU8f0mXkSNR1Pocco6vmYh4wl/WrcuHFgviEyon81a9YM/BslPRDju3TpUlm0aJH8+OOPhh3XCsxIo8LLajUloAQqNwEVcRTf+AZEHPzx2KhR8TVSW1SpCHy2aqG8+MW/5MUvXpF3ls0L9G3HrbaT/VrsKT1a7yfb1i++590kBoIIewiUsWP+covssl/vJKpNvI6nx18jL00cZ5w3zz33XOL1a4VlCbDrlpd4CPpdY8cWYo7DDjusKP8O1LFUAkkRUBFHUiS1nnwRqIwiDjbedOnSRXgPZO3kk0+Wiy66SKpXr54vlFqvQyApZ7WKOHRaVSYCSV0XxcxERRxVT8SBP+Tpp582G7fZ7E3keVJoVZSpiCNZ8iriSJan1qYEAgSWLVtmdpilUzoXKs0DkQqIWBFmRAa58sorhUgcakpACSgBJaAESpHA+PHjhRC8XXfaSSaOHFmKXah0bQ6IOEgbpjvrKt0YF3OH3ls2X2Yuek1eXviafPDNx6mmVttkEyPk6NFqXzlgm27F3IXYbbv00ktNOsEtGjaR8+59WWpsWjN2HYU4YOWyL+W2s/vImm+Wyz/+8Q8TnVCtMATuv/9+I+b48MMPAydESE24eyJz8EP0QTUlUJkIqIijMo1m5exLZRRxsIGHncDWuLcQTbZu3bqVcxCLsFdJOatVxFGEg6tNyplAUtdFzg0owIEq4qh6Io4CTKtYp1ARRyxcWQuriCMrIi2gBMpPgNQokydPlqeeeiqQn+qOO+5IK64o/1l/reHFF18UUme4dsQRRxhVnoYySpK01qUElIASUAIVQYC0TaR/UhFHRdAPP2dAxDFhgoiKRYtncKpYS17/6h2ZufBVI+j4fPWiVO+JzoGY4+DW+0mzzZuUPJXjjz9eXn75Zdnr8JOk9+DiTlPy4OV/krmzpsuQIUPkz3/+c8mzL6UOEPkPIQc/RLHzjYiPVtDRvXv3UupalWwrqUhJWWmjEOYjAuF3331noosS4ZBooJmMnNxESaQd+dxpT5TOtWvXmqiVUXYZZhJx0GYiBVAnkRyLTcREP4nQiiWdamLFihWycuVKadasmZlDcQ1HGO1jXtSqVSvu4aHlGQt+ttxyS/MTJTJnuhMTiXXdunUmTUd56qH+n376yayZRHRlnYwaFTesbbQLdnC38628Ig6inTJP7LVBmhKi0FJ/edpankHlPeixxx6bqsJP51ueut1j46xRYedMah4zBkSIZd76kX9Zp/ks6Ws4G8OknNWZRBysIaTDsVGTs7Up0/c8o5CqumHDhlnvN+U5T67Hcn/j+qWdtJFrrKKur2x9KO/67tbPfZJ+E10n1/U0yfWYttEWrjeiD5B2PE7k6qSui2xjkOT3ce+NmUQc5WHn9ynJe6Otm/Zxv+Xa4tkz23MZKblYo6z99re/NREp8mGsAVxbXBOs9eV59qGOb7/91tRV6HtDVDblWd9VxBGVcrRyKuKIxklLKYFECHBzIzoHCz4PDeQjzPYiJIkTc6733nvP5JvkBshPnAecJNqgdSgBJaAElIASyBcBFXHki2zu9b46b54cfdllG4U1d90l0qxZ7pXpkUogIQIzF74mLy96Vfh36X+Xm1pr1dhMDmrVXQ7cZh/Zr+WeCZ2p8NXsueee8uWXX8qpNzwirdt3KXwDYpzx2fvGyHMP3GSicBCNQ63wBHgZjJCDNDxEbQyzdu3amcgchx56qOywww6Fb6SeMZQAwo2bb77ZiLbefffdQJnTTz9dzjrrLJO2daQTmWzChAmy/fbbp8qSyoiIoWE2Y8YM49B/5plnTJQznI/WSGt64oknyplnnhnqFCfay8UXXxyolpfvpE/q3LmzdOjQwYiEoggGSLtgo8YcfPDBpl5SA914442BNpEStlOnTjJ06FDjyAmzMBEHqfhuuukmYWONa0QoRRT3pz/9qdyO/6hT+JZbbpGJEyea4jbV7Ztvvim8ACdygTVYstYPHDhQunULjyZ1ww03mFS5vpE+6bbbbjOOL+YDfcfxYQ2Ow4cPN+mVMhnt4XjSM7mpg5lfrOnMv2222SZq141gY9y4cWYuz5s3L9AmKtlrr72EDUiHH3541ndYvG8jOh+bmKjP7R9Onb333tsIB6NGgiCtMdfaW2+9FbjWGAfqY1MUcyWKeGrOnDnyt7/9Td54441AehGuib/85S8m3ez++++f4sacv/POO9Ny5B3f888/b6L+MlfclCX+Qcypxx9/XFq1ahV5XJIoyJy+4IILUlURMYzrOorla42y5y7PPMbp1qdPn1Q3zj//fLMmcV3YcTjllFPkkksukSlTppjrzt5nWV+Y74S6z4cxJ0aMGBGo2l3D+SJTGm3WUtZG38JEHDxDsB67c49rgzWE9Trqu27YwOTf//63IGayxrxl3Ro0aJC5divKaB/rCv/OnTu3TDNIo96vX7+0bWSNIH20NdanMJEjZQYMGGAEYxjry3nnnRc4X9Lr+zHHHBMq6EVwxRxmbK+99trU/ck2hnlCRG/WwXSWxHo8a9Ysc1+y9uCDDxqh6HXXXVcm4jlt4TmA51bf8nVd5HNOlvfeGCbiIDJSXHZhfUzy3mjrR3TBesl15q4DfM9awHPAUUcdJW3bti3TpLgiDuYUc8vaqFGjxBWuw54NYjxvYxdeeKEQxZ5nNdYC99kCvx7PY2xMiPJsiwDs1ltvNed/++23U21g7ezYsaOZv1GeK0ihmu7vN1spz4uMdzYLe97OZX331zo4uqwYx0yCHPjyTKQWTkBFHDozlIASUAJKQAkoASWgBEqagIo4im/4AiKOe+8Vady4+BqpLaqyBNb99IO8vOg1E6EDQceqH1YbFttv2VoObLWPHLjN3ub/pWJ2DazXoKEM//ubRd/s916aJg+POsO0kxd1+dyxX/QwiqCBvNzm5Sk/7OIOMxyKiDn4Kc+usyLobkk3gQ0hvLh1X/z6HerZs6fsscceAUceEUER5Vi75557yjj67Hfk0ubF8ODBg9OyOuCAA+Tuu+8u8/31118vY8eOzciYl7gISFwnaNgBrtMQxwMOLRw06YwXw7SpS5eyIrYwEQf1IGJKZzhZH3744Ugv5cs7qc4+++yAoI1rMV06XHsuRDo4/HzD4YkQxzf4zJ8/3ziWeTmfznC+UodvOORwRuNEzGbMgWzjS9QCotVyPvclf7q6ebHPC/50Dmgc1XDMdG1QN050hBlcI5mMKBIIUjKJIzgekQnznqga6cx3poWVwxnuRqbKJOLA+XPcccdlG4bA92zqihK1JlalWQqPGTPGiIWs4ajPJhKyZfO1RiUxj5kTONusMddxPvpGfxHW+cYcZAzLGx0mDD/XNtd4rsZ8Zt3zzRdxsP74YhH3GNZ55n2Yw9WWwxlPezPVY8uy1uFIjeIkzbXv/nE4W1nLcLhGsf79+xvhIxGiXNtnn30CwsN0z72IsXBUWyOKzdVXXx2oK+n1PZ3gDmEK6xr3oY8++iht9xGH7b777mW+T2o99tdOhFIXXXRRQEDon/ykk04qI1LN13URZV7ELZPUvdFnx/0FYZ0rvozCzi+T5L2RumkPAr+w55YwdmFiwLgiDtZv996OaNldq/xnRtYePnv00UfTDidiDoSaRHRLZwiTebbOdE1xLM/YXH+so+mMZ/0wUZlbnsj7pFrNZv7zNufPZX2PI+ANa9Njjz1mBN9q4QRUxKEzQwkoASWgBJSAElACSqCkCaiIo/iGLyDiePBBkQYNiq+R2iIlICLfrFsp0z95Xp7+5AX59/L/pJgQlYPoHAg66mwaP8x8IeHaNbDt7vvISVc/UMhT53SupZ9/KDcOOsgcywsoogUWwnhxjhPcWs2aNc2ObJyb/MtPVRaUEC3SijmI8hBmRDqwYg7XiVWI8avq58D5yE59f1d1GBccwK5AIY6IAycJO8uzOa+J9uDvJmfXcKaX3G5beUGN0yuduS+VifJAuGl/d2bYscxhf3ew/0J+2223jVQX/cGRn2/zRRxcY9OnT896WnaPsvPRtXROPsrgcHOdhGEnYD0kWoTviOSFPo71qMZu/kznYhc3Do84xrgxl31HLqkcEPpEEYPY8xGtJp2zJSwdcaZ2Mj+Zd2FOeZizQzebMWfdXbXpRBxEK4kqhHDP6UYCyNaWpL73Q6kjssI5FMUyiTjKs0YlMY99EUeU/vhlpk6dKrvssksuh2Y8Jl/Oal/EgRAlkzOYRqYThNgOjB492gizolq+0vGEnZ/7LeuXH+0qW1u5p3Fvc60QIo5c1/d0TldEc0QiIEpQJgtzEie5HvtCBNYPhMfZjOcQV6iXr+siWzty+T6pe6PPLupzj8/O7UOS90bqJYIkES7i3Ls5zn9+jCPiYH4SScY1nhHd+3eY8DeT6NfWhejilVdeCRW7kyaKNTGqIVhlrqeLop8vEQfPMzZFWaa2hq3vKuKIOrq5lVMRR27c9CgloASUgBJQAkpACSiBIiFgHZjNGzWS2bfdViStqtrNCIg4HnlEpEBO2qpNPXvvN8gGWb9hvazfYP9dL+z42fjZelmf+v6X33/5boN7jASPt/VtLOPX4dS9wTlXmTrc9mSqY2M5c64MdZj+0RvbJrePXn9/LbNelvx3uXz13VLzr7XNqteUrWpvKQ1q1Ze6NX+T4nfkDj3l0G1/DX2enX7+SuAsYxdvt74DpdfpwTDa+Ttr+Woe3mNjuP3XXntNmjRpUr7KIhzNDvRzzz3X7ETPxXi5Rz57+0M0Cvd3/o8oJNsPL+PsD2Xd393/IybBaY0jgR/3/4X4fc2aNUK4ZCJz2HDCPjeYWNELO2pp15FHHhlpp34uY1DVj0E0cc455wQw9OrVy0Q8IC83Agd2DIeJPHwRx4IFC1I7+HDusmPeGqKABx7YKAZDwMDLbsaWiAmu0+6EE04w4dRde/bZZ007mDOk7Vi7dq05hhfiYWGfX3rppbQpHnynoT0PwgocTLSJ6AJ+6H8idhBpwTX/hbz9jp2ThIwnzcTSpUuNY8DfIYnTPGr6jVznqC/isPUQ6h7hDsIKuPqpC3AYsIa6L/l5Jl6yZImpgpQUboQAO7Y4YAmfTaoC2PiOOhyrvXv3TnXns88+MyIJ1zg3oc1xNLCukkrH/x6hBGujb+mECIwdbWJtZLcv0Qp8584VV1xhUvq4xs5cf7cpbcOJR6qTTz/9tEyEGNIfhEUVYV6F7T4nhRBOMAzGj/Bs61iYKClMeMVYDhs2zETGYY1FMBcWGSWdiIM1FpGNa/Sza9eu0rx5cyO+gTnnXrdunTnHJptsEjmNSa5zOOy48og48rFGJTWPw0Qct99+uxGo4Ph2o8GwPjGWXMuuIOCuu+4yqaWSNq5n0pJYI5oEu8itce27v/vnJ/UV66JvYesxc5m6SLfGem9TtLnHsvOfuekbzltC/bvGWsIatfXWWxsnIkIXrjXXiBRlr8Ok2bn1hTn96S/rBk5Ono24dv32UYcvJExSxJH0+s79gec3jDls79P0lXvPtGnTzDpKv+GOI9sX39Em5pW1JNfjdFGMeAZgnWaucP/hevLXRFdQmq/rIuk5mOS9MR07+9xICi/Od9VVVwXus4x3mBg3yXuj5UbaOl+wyjxjzeS5jHs3z4n+8yPzk3XO/g0SR8Thp/lCjOynoEv3zMg8tynjOCfXOkJN17jvsd77RmoxmzbPfsc9g2crnpVJjeZH1mBs0kXd4tmPe5prNv2R/SyXSBxufQjnEJiyvj/00ENlxsFf31mz+XvVGtHWEP5YQ2Qcdn/he57VuKZ5/lMLJ6AiDp0ZSkAJKAEloASUgBJQAiVNwIo46MTnGcIclnQnS6zxARHH5MkitWrl3IPPVy2S5d9/LffNfUzW/u/7nOvJ/4EIBzYYUYT5MYKIjf9aEcTG/zvfmf//Im5wjk3VY79L1WOP3XhM8Bwb694oXvDP8et588+hapxhaOfT5MSd+xVFZ+3O0u79T5NDB2XeNVcMDf5h7Xdy2eEbUzuwy6hNmzZ5bRZOlbAX7Xk9aRWtPCz8dxVFkWi3cbKQJ9sVGOCEJ2w+DlprvAjmc3YCuuaLONzv3nnnHZOqxBovx3k5jVOcF8DWcKozvtZ48ZwpLYcP4D//+Y9x+LkOzkw7q32nIe2iH7zYd401hPDprvnikLAX8vQNZ5T7wnj16tXG4egKB3ixnu6lc1KDHCbiwKFGRA7XeGHP7nA3SkqmiBf0zxXawBABGo47HP7WrBDQ/s44uWkgCMPOy3hrOFtJueDW4T6L23KkFmCMXWMu0wd3HuAYIfWEH9nl22+/NU5d69hgbtM2d8xw8rjzlHPhvPIdxGHsELngtHZt0qRJgbQ9OJQQqLh9pbw/7xC1zJkzJxDNifnqcoQ/Tg7/nhc2h8NEHDhHfAe2L7hJak7GqQfRny/y4Xg/mo+df2F1I85y15d8rFFJzWNfxEG/mKM4ofyUUnaHt/85QjN33Y3DO05Z1jSEUdZYy1jT4pq/HjPfcb7iRHcNgRXrrzWeTwcOHFjmdH7UJtY6RFWwtMZawWdutI4wkV7cvmQrHxZJgl3niOj8/uJI5hnT3jPCxFxJijjctie1vts6uY+6US4YC0Scd955ZyCygC8kc1OqJL0ehwkRmGPMKzdyQtgayprvPh+57JK6LrLNpTjfJ31vDGMXFl3MT+NDm8PYJXlv5BxhUT0Q1RIJzk/biGiSz7Gw54U4Ig5//oalZwl7ZuRa4LnYF/Vee+21JtWcNZ6PEJu58xNR4oEHHhiYDv5Y/Pjjjyalmrs+s87y7B01jdTChQtl7733Tp0nVxEH1z7j7Ua1Q8SDmMt9dku3vtsG+EJO/++KONeHlhVREYfOAiWgBJSAElACSkAJKIGSJqAijuIbvoCIY+pUkRo1cm7krncnv1Mt58bogUpARJpuvrU81a940pbYnUx79DxO+p5zVdGP0eoVS+TqY7uYduYrpLgLgRfs7FjiZa79wdkS9n8+c7/zf8/0XZLH0n52Prk/vEDzP7O/I9wqBsuWIqMY2liKbUAAgVPXNXYm+ikv+D5sJ2ccEQd18CIdgYhrzD/X+YwzmRfVcYyoALxgtg4vXorzojjMfKch0SJOO+200LJ+ChGihvCZtbAX8mFpVyjvhzJHXJBL+oo4XHwRR6YX76SSGDlyZKp6dnKSpiLMfCcfZXDI+XOJ9ZHIFdZw5l122WXmV3/c09XB59ddd52JBmMtbIcr85bdlq6l261PGXa849hgHfeP43vGB0eBtUwpcPzUJmPHjjWRbFzzndCZQsvjVHIjcvgpWgYPHhxwyBCBg8/C7NRTTw2kQAoTcXD9+EIXHDy+wCTO3EuiLGNU3tQgrDesO2HmC80oE3eNSnIe+yIOdx3D0W+jA+EAY50Jm6elLuIIi4hDP5955pnAOIYJ9fyxwGHIPCailG8847DG2QhTOEjTpXtLYi5Thx+Fg/YhjKxfv37oKbjnct/AobnjjjuWKVMoEUe6tTnT+u421hdx8B0O/oYNGwb6xLrppoy54447Umtz0uuxL0RgLF5//fXQ6Fik5XCFrojqiDYRZsUo4kj63uizy5Tqg3urG+0ijF2S90Yd0Pj9AAAgAElEQVTGxL8/IqD1I1W4Y8ezIsIP1h7/Wowq4kDA6qfIC0utFvbMGJZCkPYReY77siv+/cc//iG77rprqvkI0UgfZY1oPkSu8w3hqnsc3yMi7d69e6TlLSkRhy/ktScnEg/PKtaypbhSEUekYYtcSEUckVFpQSWgBJSAElACSkAJKIFiJKAijuIblZSIo107mfjUU+Vq4KUvj5av/rtMqm9STaptUk2qb1LdOFkDv5vPq/3yefVfyhW+/MYIGGIiZEgqesbG33+NwkGB1Cf8N/VdqqTz2caSzjHOOWzEjY31/VrPxnP90o5U9A9OGzyvKRHSll/b5xzzyzkCx3jnTZ0xEDkkM4+N9ZVltNEpbbl5fSvD45f+/3LeaDyC500ds2GD/LT+J/n2h1Xy7brV8vX335gUKtaYg7/bZh+5bv+LyzWvkzzYvnjtsH8f+cPwsUlWnZe6li9cIGNO3rgjKZPzLi8nr6SV4hRhFxcOD/7N9H9eOLo/lLW/U08mW7lypdmhxwtOXhZaQ8DCS1Ve8Pk7VSsp8oJ2y9+xyMt0XmSnM16Gu6lP4oo4/BfQ9jy8vCXaB0bu61GjRsXmQGQF1wmXbresL+LIFEafiArubm9XhEAD/RfyODPeeuut0Lb7Drx0zsrYHc9wgC/iQAyBaCHMfEd+JjFN2E5tdlH6KU64locPH546HbvibQjvRYsWSbdu3VLfwQ4nmrvL1H6JoxWHpTUcbn4KK98JEDeii88EZwNRQayxM75evXqh7EgvQuoHa4gwSJPimnvtZItagGPXDZv+2GOPSefOnVPVIRBxU2jALd366KdLChNx8FxEGhbXWYQziPmOA7Oi1l7fIZrLtRFXxBF3jUpyHkcVcbjzx3dul7qII10qLN8RHTaPFy9eLES2sEa0mkwpXlxhjF3Pw9afXOZd2DFEEBo/fnzqq3QOzajnK5SIg/U27vru9sEXcaQTCCLUcdNXkarQisuSXo99IQJRnPwUarYPfmqOdEJNyhejiCPpe6PPrn///gHxjTv2vqAijF2S90bO7Ytu3IguUa8tWy6KiINoLdzv3ftnuvnkPzOGPcu4bSTtCWIma66wic/8VCqZ0qT44tB06VnCGCUl4uBvjtatW5c5RZT13T1IRRxxZ3Lm8iriSJan1qYElIASUAJKQAkoASVQYAIq4igw8AinsyIOin7++ecRjtAiSqDiCcz49CV5aeG/ZNai12XVD2tSDaqzaW3Zv+Vesl/Lvcy/NarlHlkmH720u59/2+UAOfGK8B3Z+ThvrnUu+uBdufWsPibsPLvmbE7jXOvT4/JLAMcsLz9xoru5jTnrFltsYXbw89O2bdv8NqQK146T2nVyZXup6wsl4og4eFnNzuJ0YcizDcP3339vwux/8MEHguMUh913330nTZs2NalQpk2bFkizkM7p7os4iEaSLqS076Al9QwCBmv+C/lMEUD8fOlhKUGyMYj7vS/iePDBBwMhsf36fJFOuucsX8SRSyoCP01KNtEF4h7XEHG4KRJ8Z/bQoUPlnHPOiYssVd5Pl+Xnp/crPuGEE1JOHNYtd3csIjg3vQqOST8ijVsfDhO37X5qG3cOZ3MCvfHGG0KYd2thzm++GzJkiKTrI1EK2LHL/MBJjuCmUDZr1qyAc4zzImqZMWNGqgk40NJF7OD+EeY04uCwlE9x16gk57GKOMSs737KA8bqk08+kf333z/jPPYjQxBVJVO0I+YQ6aWsIQJkrufL/IgDYemZ4py7UCKOXNZ3tx++iCPbc0YYgyTXY+r3hQist9wzwsxPl5TpuacYRRxJ3xt9dvDh/pELu6TvjWGRkT788MMyAtOo15kv4kDcyprBc+N7771nIsq4aUBsvdwXSM/im//MGBZVzD2GdHPwteanGfGvi3RRPTgegRQpXqydccYZRgQSxZIScaR73o6yvrvtVBFHlFGLXkZFHNFZaUkloASUgBJQAkpACSiBIiSgIo7iGxQVcRTfmGiLwgl8+M0n8vSnL8hzn82Sz1cvChTas9nuKfFG4zrBcMLFxJOUJDhH/q91Wzn7jmeKqWmhbfn3zCfloSsHS5cuXQKh6Iu+4VWogURxsMIN/v35558DveelpxVvVHQo/6owLDfccIOwc9sau4TZxZjOfFFAHBFHtugD6c7JS3l2H5JOw93pmG18ooo4MglCEY64YezdNAac338hn85BTtliEHFkGi/a6Od0x8kdlorAF3GwU9mNuJFtbPieEOau0wxnBMLBdIYDlxf91ti17Qq8/F2mvvAhSpvcMh07dgyIguIc7+80JyoMIpVczU3jQ8QaN5+8Pyf9c0Sdo6QuwelNVI9sxjzHAdShQ4dsRfPyve/AIe0PzOOaL+LIZY1Kch6riCO9QD+Kk48URaQ9ytXSRWHJtT7/OH9NKa9opFAijlzWd7fvvogjl+s1yfWYtvlChEyiylIXcSR9b0ySXZL3RsbVj9qVKTpalOvaF3FEOSYstZw9Lur92JZHxGijl/EZ6fx4HrDmXxeUb9GiRWgzef4jOpG1Xr16BdLUZepbUiKOdM/bUdZ3t30q4ogyE6OXURFHdFZaUgkoASWgBJSAElACSqAICaiIo/gGRUUcxTcm2qIggWkfPyvTP3leZi+aE/iifaMdpVuzPWS/bfaUHRtsVxLYFixYIAceuDE9yZC7npXGLbcv6nYj4EDIEWd3UVF3qJI0DscUgg37Q+oB39q0aZMSbzRo0KCS9Lz4u0FKj7vuuivV0GwijjjODN9But9++wVCpUehs379erNTEOdcXEtCxIHIiJ2X1til7aZsifNCvhREHH6klbCc7rDwRRwXXXRRIJ95lLF65JFHBOeStWOPPVauvvrqtIf6IdL9sOw4b915Ql3UmauVx2nYs2dPue2221KnLq+j6sorrxQifWB+2ptsIo64zhGcQGPHjhX+BspmbruylU3y+3yJOHJZo5KcxyriqFgRB2knXNFeknOWuvw15dlnnxXSFuVqUUUcCLNIeRF1rU1ifXf75Is4MkULSMciyfWYcyQpRHDbXIyROJK+NybJLsl7I+Pg15ctUlW2ay+uiCOTkJdzxXlmpLwfSYtnNDfdoH9dpHtmoy4/rQ7p7dxIRJlYqIgj20wp7e9VxFHa46etVwJKQAkoASWgBJRAlSegIo7imwIq4ii+MdEWibyy+A157cu35MmPn5MV33+TQtKyXjM5bNsDpFvzPQQRRyna7rvvLitWrJBDTxku3Y/6U1F3YXiPjeH277nnnkDY7aJudCVtHGkunnvuuZRwgxfbYbbrrrua3f9E3wgLoV5J8RRNt3xnDVE5CJ2ezsoj4ujXr58Q+SOOzZw5M+W8tseR0gFxWcOGDU2IbCIT8IKZsNPswrSWhIjDd6pybsQY1uK8kC8FEYcvlKB/NWqUTfPlz5vrr78+4CSMMsazZ88OiCz86BV+HX4aHEKZk3bJGpFa3Ege5d097gta4kSdYH666VDCQrzHqY8IODZCDsImNz0I0YsyCS7iijgsT+77pKfAKfTSSy8Fri13bPyIKFHGvrxl8iXiyGWNSnIeq4ijfCIOUhscccQRqemFA9dNY5Rt3uHQJD1XvsxfU3KJSOG2LaqIw3feZhPMJbG+u+30RRysJ6RAi2NJrsecN0khgtuPYhRxJH1vTJJdkvdGxiGsPv9ZIc68iyvioO5M4qw4z4zU5V+7CHLOOuusVBf86GmZogndf//9gSgep512mvzlL3+JhENFHJEwlWwhFXGU7NBpw5WAElACSkAJKAEloAQgYEUcm9epI3PvvVehFAEBFXEUwSBoEwwBIm3MWvy6+feL1YtTVOpsWlv6tj1UDmm9f8kKN9whPvnkk80LqZY7dZTTb5xctKP/yXuvyt/OO9qE/men/Oabb160ba2sDcO5iHDD/ixbtiy0qwg32PFMegT+r1ZxBJ588kkZPHhwqgHDhg0L/O63rDwiDoQ6o0ePjtVZP31LJrHAqaeeal54W0tCxDFv3jw57LDDUnX6fYjzQr7YRRxEHWnfvn0qZU2mMOS+ky+X1CX+jlnECK+88opUq1atzBxhLencuXPq87DdtX6ocFJjkBIsrL4ok5Dw+kSmsfb4448LosZczRfIvP/++1KnTp2cqvNTy6QT21B5riIOv2FffvmlsIP+lltuCaQ1uuSSS0walkJavkQcuaxRSc7jUhJx+Kmmck1b4IuzyhNu3+eHyOGBBx4o5NTMeK5LL700EI2qvEIzX8SRbh247777hOvUWlwRRy7ruwvCF3HkkkYm6fU4SSGC29ekroskJ23S98ak2SV5b4Sbf38sj1jKF3HwnELkserVqwtrXt26dY2A1U31h9gXRptsskmZYYzzzMjBbEoYMWJEqh7/WvSjrGS6Vv31J04krWITcYwZM0ZuuummFJdchMRJXmOlXpeKOEp9BLX9SkAJKAEloASUgBKo4gSsiKPrTjvJxJEjqziN4ui+FXE0a9bMvOxXUwKFJJBOuEEbujXvLCe06yddm3YsZJPyfi4c8gMHDjTnGXzzP6TFjsXpdH/8r8NkzvS/m6gOvNxRKxyBF198UV544QXzk84Bg1iDF6uIN1S4UbixyXamt99+Ww4//PBUMUK7I9pKZ7ysJjS7NZwD7dq1Cy3up1PJxUFKWoq5c+ea+nHcI6oIezEetvsyqogjXTnOee211wbSYgwdOjQQYSHOC/liEHHce++95hoMM67jP/7xj6mv9thjj7RpbJIQcfz4449lUgk8+OCDsvfee5dpnr+TGIEGAiTXEEUccsghgc94ye/O72zXg/s9zl+cNdbghqgjLDJJlHpJ8zVt2rRUUX9HbZQ6bBnGifGydueddwph3MPMjSrI99nCvWdrB+mN3Gg02RzC2erL5ftiEnEkOY9LScTBuPmh/N99912pX79+rCFNUsTBif36SLHEWlYM5ospaFMuUSlsX/r06SMwt0YqpBYtWpTpqi+iyHbNJrG+u41IQsSR9HqctBDB7W8S10WS8zXpe2PS7JK8N8LNF/SSEm/GjBmy6aabxsbqizjC0pc99NBDMnz48EjPHv4zY9izjFuRf43Dfs8990wV4RnH/Zu3U6dOMmnSpDL9pB8IYV2xSabnQb+CYhNx+GnM4kQViT0JqsABKuKoAoOsXVQCSkAJKAEloASUQGUmoCKO4htdjcRRfGNS2Vu0eM0SefrTF2T6Jy/Igm8/DXS3Tf1t5KDW+8pBrboL/6+sZqNx7LJvbznmoluKsps2lUp5nHZF2bEibRQ7KXH240hkJ3KYqXCjSAfPaRZpb3BwuS92CSlPrmzfPvroo1RKB/tdvkUc/gvsdNELSNNCKhjXooo40u1GJPIAO0RdNoSj7t69e+o0pSbi6NWrl4mk4Ath1q1bZ9LWuAIdQnYjNAizpJx8vnMPhwZCDtcRzLwjxY87Dueee64QpcU1hDyIOChvDeEP/SVVS1xj/F1nCcf37dtXrrrqqpwiaPhh0amP3eWkCWBXbxwbO3assPPUGqlZcO6ERfbwd+qWR8RBtCXmhStGKW80gTj9tmWLScRBm5Kax6Um4jj66KMDqXwQFzGn41jSIg4/YgNtweHXpUuXOM3KS1lEru79g5Ow5vHcGifti23ckCFDhAhB1sLW7DfeeMOIm10rRRFH0utx0kIEl28S10WSEzDpe2PS7JK8N8JtypQpgZQjfMYzxBVXXBFIwRaFcRQRB3x5tuL51BpROvj7qF69eoHT+M+MfPn0008L4hDfnn/+eXNvcQ1xNBEnrfliaT73o3Fw3x45cqSJ6mGNZyNSpbkp6TLxKDYRhy9Ope25RPiJMgeqQhkVcVSFUdY+KgEloASUgBJQAkqgEhNIiTjat5eJl15aiXtaOl1TEUfpjFWpt/T1r942wo2nP3lBvv9pXaA7iDZ6/CLeKPV+Rmm/G43jqGE3ym4H9o1yWMHKTL1thLzyxD1y4IEHyoQJEwp23qp4ohtvvNHsaCMigm9EcWCn12677WZ++F2t+Akwprz0dY1dhUQeaNSokXzzzTfmZfS4ceMEJ6NrroiDHenkHrf24YcfBnYnUp+byxvHNfMkk1144YXy8MMPp4rggL7sssukadOmgvCAl+Y46NgJ6RsOrtatW5uypMGwaTV8pyHHIVTr3bu3Kfu///3P7G4m/LzbX5xtOK9dAUSpiTjoK8IUBBtcn+xMZZwYf5x9rr355pvSsGFD8xEcPv30VxEjedfZVW7tzDPPNJF2rCHCiOKU9NPVcDy7ZhkP5h4CMRycroAD5wNiE8KY+8bnRIvxjXlDhI82bdqY4wh5v2bNGvnqq69k0aJF8oc//MGc1zd2qhKC3DXOT/uYuzhTbH3UReqSXXbZRbp16xY6rf0dwhRq2bKlSUdC23D41K5dWxBXffHFF4Y5Dlh/d/2qVavMeVxDyIFgY4cddhBS49AWxshNMUT5MBEHTihEMbSFc2299dYmJRmiEL6DFfNk+vTpZeYJKZKIslNIy1XEkY81in4nNY9LTcSBoOmOO+4IDD2iQEQCpB1g/jB3SIe0ZMkSc93UrFkzUD5pEQfXDkIJ/1611157mbWhefPmJqoT9wOuI4QVXGtcO2FRnpKe14jKwtKKDRgwwAg6uGdxb2SN+vbbb2Xx4sWGnU3h4LbHj1DEd8ccc0wqIhGRtri/+8Z5uAe0atXKiDg5T5LrO+v2ihUrUqdFKElbrMGA+WHNrjnZWCe5HictRHDbnsR1kY1F3O+TvDfmg11S90a4bNiwwcxvnPquIaxAaMazCRFduU9ynS1fvtw8B7BesUa5FkXEQfkwvggweF51LUzEwXrE/ZdnKJ65WLtmzpxpRCeunX766cIzsW9h7Dg3z72wIK0cf7e5Rj3U5xvP1O7zlv2e9cEV9fK84T8bUZbP3YgnSa/vtj2sL2Hp7Uj5SFpAnh95duHvF57NeL4Li/IW9zqqrOVVxFFZR1b7pQSUgBJQAkpACSiBKkLAijh22n57mX7VVVWk18XdzVc/+USOHjbMNDJTCPTi7oW2rtgJ3P72fXL7O/cHmtmpSQfp9H8dKn3UjXRjY6NxbN2qrZw2ZpLUrhvcXVRRY7rk0//ITadtDCOPg4kXRmr5IeDvfMIBwE51drji0OR3tdIjgAMn1xQ3roiDF6XkAo9qvLieP39+xuJ+LndbmGP9F83shHQjBLgVf/DBB1KrVi3zUZiII0qb77rrLjnooIMCRUtRxBGlr4MGDZKLL744VTRsZ2umehgLHIxRDOcFopCohuPAd7S4x55//vlG2BPHiGxB1BffcAIcccQRgZQF2erFGcuu1zDDGRs3IkC6yDhh0WfCzonjynVoh4k4wna5Z+sn3+OwgbW9tqIck0SZXEUc+VijbH+SmMelJuLAQYZjLMzpFzbOYalD8uHkC9vBnm3eIWLD8Zdv++GHH4zIApFVHOP5yxU+cCzzZZ999snKn7WNNTzMuDfi4HUFltnalW19R3BC+pOohujhuOOOy1o8yfU4H0IE24EkrousMHIokNS9MR/skrw3gobryxWWRsGFiNEXfkQVcVB/2D3Aj1YXJuKI0jbKzJkzRxo3blymOAJL/9k0U508E5B6KSxylx/9LmrbbDnELIhAreVjfbd1+6lkMrW1X79+wjOTWjgBFXHozFACSkAJKAEloASUgBIoaQKo8u1uus8ffbSk+1JZGj9+1iy5/KabTHfYPaWmBPJBYMqCGXLpy6PlgG26yX4t9xIEHE3r/vpSIh/nLPY63Wgc+x59uhxyctndQIXuw88//STXD9hHVi770uRA5sWxWn4JPPnkk2bnGuINdrKpVQ4Cr732mgnbnMkZx+5dIgW88sorqU7j/GHnP5YvBympMx577LGMoBF1IAQgykSYZRJxsHPZjfYRdjyCBqIl+Du1S03Egfhh/PjxGVkSdhwnufuCP58ijrVr18qIESMiCS+IEIPAJFP6EaItkO7luuuui3xxDhs2TAYPHhxafvXq1SYaCCKeKLbvvvsGopT4x7BLnf4S3SaK0WdSlvhGJBqixWQTrLDrnUgp1sJEHERdQawSx3AEcT/wHctx6si1bDGKOJKYx6Um4mD82OXtzq9MY0rEJD9KTb6cfLNnzzZCtKhiiSeeeCJrZKhc56t/HLvIR40aFUiFkq1u0qaE7Tzn3hW2O9/Wx3X66KOPpk0pVUoiDvqU1HqcDyGCO4blvS6yzYdcvk/q3pgvdkndGy0bokpwbyeyWlTjma5GjRqp4nFEHGFiSISOkydPTj2z+M+MPLMiwiAaUDrj+fbOO+/MGEmC53LWYT8CkV8naVtuu+220MhjlC0lEQfPQEQBizK+nTp1kkmTJkWdBlWunIo4qtyQa4eVgBJQAkpACSgBJVD5COC0IBTs9NGjZSfdZVzhAzxo3DiZ8fzzph0q4qjw4dAGVDECOLFseNcTLx8vv+0a7jAtFJYJw0+Qj96cKdu0aiXTpk4tk3u4UO3Q8yiBykDAhmx/9tlnA93BCUR4epzJAwcOlLlz56a+J32KzanN7tNs6VHciqNE4qA8u29xYI0ZM8YIRXzr2bOnDB061ITI3mabbUKHIpOI46OPPjLpRJ555hnh/67xDIijPF2EEf+FfN++fUPD11MnIhN2alpDZHD00Ufnder4O0PZFcr4IYphx6RrNq0H4cZ9gw0ilqiWbad2WD04UYk64eaVt+V4AY9DNs78wiFE2gLyvmdzbJB6hzDcmYx22bQzmeqjjfQlmyGAIhVFuvDl9vgzzjhDLrjggrTV4Rgn1ZHvBIIZDg7mGLv17fdhIg4EJWHjHnZS+seu1kMPPVRYGyrCfBEHTkWEhdksX2uUe97yzGMc1ISCt+YKgrg2rr76avOV65Bi7OFhDWcfY1xIs2so8yiTEBAx1OGHHx5omiviyHRPIK0IKVGsZVprbRlSY919993m/hG2rrgNodwBBxxQSGxGEAkTIktm4gYXHK+kJAszonQQZcFfA7hvMWeIkkaaljDj3kj0gSTXd9KnIKSLalEjcbj1lXc99oUIme7HPPu4KdteeOGFtE5wt43luS6issulXHnvjflml9S9ETakTEHoxI/77BrGjfsZzzrufY3IOW3btk0VzyYGCEtxxLXQo0cPU0eY8Pfyyy83zyrMK//Zgmue6yOKaB5xGHXxDO+vJ4gtEejyrLzZZpulnTaIml2hdtz5lSkSR9LrO21bv369EZPy90EmwR79Z51UCyegIg6dGUpACSgBJaAElIASUAIlT4Adf/wxeelpp8nJaXZ4lnwnS6QDi374QQ4dPNjswuGP2fL8kVkiXdZmKoGiI8BuZV6YbL5lIznxignSvO0uFdLGyTcOl9efesicm5fbOHLVlIASKD8BXooikuSHPNI4961FdbiVvxVla6BdvKTGmceL9bp165pngS233DJVmDaTj7tmzZqBf92dlZl2fq9cudK86K9fv75sv/32GV9256OPSdcZJuJo166dOQ27GOfNm2ecHERTsWKcpNsQtz52C+Po4FmPsNwtWrTIGHkjSv04NHBwMr44dnFikAKkSZMmJoVCtWrVolSTKkPUBebaqlWrhPZSF/OQ+RgWojxb5aQ0oj7qZZ7Xrl1bGjZsKE2bNg3kl89UD4LzBQsWmGgxjCd1WGOHMNFLcKLweVgkkw0bNsjy5ctl2bJlZm5wjdnIM/SNa4Ift95s/arK3+djHhc7T9YSrl3EMsxjBHhcD6wtzGXmX0UZ7Vm6dKkQZZOxYW4zl2lXLmtA0v2AGWsAkc5oK2sU7Gife4/LdF4cwDjouZZJu+Deu6PcG5PuU6HqS3o9TrrdxXxdJH1vTJpdEvdG2yauK8TK3A+5x3Hf5zojwlyu9+64/c0WvY3nW8Q/XLuIkjNFHct0bu7lPPPQxzZt2lSJTQ48szC+iL1ZA+0az3Mka3yuLOOOcSmWVxFHKY6atlkJKAEloASUgBJQAkogQIAQpOedd540b9JEZo8dq3QqkMC5990nj02dalqguS0rcCD01FWewIknnijkNv+/NjvJgCvvkXpbFTbVzON/HSZzpv/djIMKOKr8dFQABSIwc+ZMOeGEE1JnI0w0aTZKzaKG7y+1foW1N5OIozL0T/ugBJSAElACSkAJKIFSIJBNxFEKfdA2Vj4CKuKofGOqPVICSkAJKAEloASUQJUkQPhWlPHXn3GG9E8TyrRKgilgp+cvWyaHnnFG6owVES64gN3VUymBoidAmG92/LXttK+cdNV9BWnvwv+8I5NvGi5ffTzfnE8FHAXBridRAiatXP/+/QMh6Y8//ngZNWpUydFREcfGSBxqSkAJKAEloASUgBJQAoUhoCKOwnDWs8QjoCKOeLy0tBJQAkpACSgBJaAElECRErDROOrVrSuzb7lF6lVgONgiRZTXZs3/7DM5+vLLZfWaNeY8mkolr7i1ciUQmcDvfve7jWFff7ubHDzwQtm2Q9fIx8Yp+PNP/5N/TblXnrz9CnNYrd9sLmNGX6spVOJA1LJKIAcChJ9/6623ZOTIkSZEsWuzZ8+W5s2b51BrxR6iIg4VcVTsDNSzKwEloASUgBJQAlWNgIo4qtqIl0Z/VcRRGuOkrVQCSkAJKAEloASUgBKIQOCQQw4xO1B3atVKJl52mQo5IjBLoggCjkHXXy+Lli5NVTdx4kTp2jU/zuIk2qx1KIGqQuDDDz+Uc845R+bP3xgZ48AT/iydehwl9bdulgiCVcu/lLefnSxvPfe4LP9igalz170OkFEXnSs777xzIufQSpRAVScwd+5cuffee4Xc6Px899135l/yafNvmJ111lkm1Vwpmoo4VMRRivNW26wElIASUAJKQAmULgEVcZTu2FXmlquIozKPrvZNCSgBJaAElIASUAJVjMDq1auFtCqEFCcSx8k9e8rAww5TMUce58GEp15AaaIAAAVMSURBVJ6SkXffHTjDwIEDZcSIEXk8q1atBJRAHAKsjX/9619lwoQJ5rA6m28hHXv0l9179JcmrXeMU1Wq7BfvvyXvvjBF3n7ucfl+zSrzefUaNeS4P50rV5w/OKc69SAloATCCTz//PNy0kknRcbTs2dPGT16tPymRKOSqYhDRRyRJ7sWVAJKQAkoASWgBJRAAgRUxJEARK0icQIq4kgcqVaoBJSAElACSkAJKAElUJEE2G1+yimnyOLFi00zEHOMGDBAmjduLF132qkim1Zpzr1o+XKZMWeOjJ82Tfi/a/369ZMbbrih0vRVO6IEKhOBGTNmGDGHjcpRrXp12WGPA6Rhs9bSsPm20qT1DkbUUbNWnTLd/vrLz+WjN16ST957VRBwrFoeTNuw0+57yjlDzpVD9ulcmZBpX5RAURB4/fXXpX///lnbstVWW8k111wjPXr0yFq2mAuoiENFHMU8P7VtSkAJKAEloASUQOUjoCKOyjemlaFHKuKoDKOofVACSkAJKAEloASUgBIIEGDX+aBBg+TVV18NJdO1XfaX44uWLSsjUCgmzIhTSBuTzTL1o3mjRkbcEscy1acROOKQ1LJKoGII2Kgcjz76qIlaVF7ruv8h8vs+veXYI3qVtyo9XgkogTQEPvjgAyNQ3WyzzaROnTpSu3ZtqVWrlvm3VatW0r59e2nXrp20bNlSqlWrVvIcEZvZNDH16tWTs88+u+T7lK4DkydPlnnz5qW+PvPMM6V+/fqVtr/aMSWgBJSAElACSkAJFCOBFStWyO23355q2i677CJ9+vQpxqZqm6oQARVxVKHB1q4qASWgBJSAElACSqCqEcBJiSPARuWoav0vVH/Z8TtkyBBh56yaElACpUHg66+/lueee06mTZsmL730UqxGN2veUn7fp5f07t1br/tY5LSwElACSkAJKAEloASUgBJQAkpACSgBJaAEshNQEUd2RlpCCSgBJaAElIASUAJKoMQJkDrgn//8p+nFokWLpHnz5ub/e+65p9n9yM501/jdphvIZ9dph21LtvPQ1oqyVatWpXjAxYo1aPvBBx8s7JJVUwJKoHQJLFy4UCZNmiRLly6VZcuWBf7ddttthZ/WrVub9apFixbSrVs3EwVATQkoASWgBJSAElACSkAJKAEloASUgBJQAkogeQIq4kieqdaoBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAElACSkAJKIHYBFTEERuZHqAElIASUAJKQAkoASWgBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASUAJKQAkogeQJqIgjeaZaoxJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAEYhNQEUdsZHqAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAElACSkAJKAEloASSJ6AijuSZao1KQAkoASWgBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAEohNQEUcsZHpAUpACSgBJaAElIASUAJKQAkoASWgBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASSJ6AijiSZ6o1KgEloASUgBJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAElACSkAJKAEloASUgBJQAkogNgEVccRGpgcoASWgBJSAElACSkAJKAEloASUgBJQAkpACSgBJaAElIASUAJKQAkoASWgBJSAElACSiB5Av8POSQ0hKDx8iYAAAAASUVORK5CYII="
    }
   },
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to add breakpoints\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Breakpoints](https://langchain-ai.github.io/langgraph/concepts/breakpoints)\n",
    "    * [LangGraph Glossary](https://langchain-ai.github.io/langgraph/concepts/low_level)\n",
    "    \n",
    "\n",
    "Human-in-the-loop (HIL) interactions are crucial for [agentic systems](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop). [Breakpoints](https://langchain-ai.github.io/langgraph/concepts/low_level/#breakpoints) are a common HIL interaction pattern, allowing the graph to stop at specific steps and seek human approval before proceeding (e.g., for sensitive actions). \n",
    "\n",
    "Breakpoints are built on top of LangGraph [checkpoints](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer), which save the graph's state after each node execution. Checkpoints are saved in [threads](https://langchain-ai.github.io/langgraph/concepts/low_level/#threads) that preserve graph state and can be accessed after a graph has finished execution. This allows for graph execution to pause at specific points, await human approval, and then resume execution from the last checkpoint.\n",
    "\n",
    "![approval.png](attachment:e47c6871-a603-43b7-a8b0-1c75d2348747.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "131fd44d-c0f8-473a-ae80-4b4668ad7f47",
   "metadata": {},
   "source": [
    "## Simple Usage\n",
    "\n",
    "Let's look at very basic usage of this.\n",
    "\n",
    "Below, we do two things:\n",
    "\n",
    "1) We specify the [breakpoint](https://langchain-ai.github.io/langgraph/concepts/low_level/#breakpoints) using `interrupt_before` the specified step.\n",
    "\n",
    "2) We set up a [checkpointer](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer) to save the state of the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9b53f191-1e86-4881-a667-d46a3d66958b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKMAAAHaCAIAAADjVG5qAAAQAElEQVR4nOydB3hTVd/AT3aapG269x50U5bYIkNA2XuVypK9BISyFBHQFxBEyxCoIAKfIr4IspU9LAjaQim1pZvRke6R0TSr379EYz8opX42uUnO+T19+tzce3Jvkt/9n3XPPZfZ0NCACBjARAQ8IKZxgZjGBWIaF4hpXCCmccEYTSvq1eWFCplYLatVqVVIqdAgo4fDpTPZNJ4l08KS7uxlgYwPmvG0p+VSddYdcd59aVmB3NaZw7Nk8KyY1nYshdwETLO59MoSODtVTBbtUYbMJ4zvGybwjxQgo8FYTP96uqIgR+bowfUN53sE8pApA6dmfpr0caa0ILsuerB9u86WyAig3vSDpNqL35a+Osi2c19bZF5IqlU3T5eLq5T9JroIhBQXlBSbTjxRrtE0dB9uT6PRkJlSWVJ/YlfR62MdvUP4iDqoNH39WJmlDbPD6zYIA059WdTlTVtnby6iCMpMn/mq2MWH27E3Fpq1nEwoCuggCH7FClEBHVHBrbMVjh4crDQDQ2e5pl6vKS2QIyqgwHTefYlKqYGsDOHHuDgPqJqolRS0Gykwfe1oWWRPvKK5Kf4RgsSTFcjgGNp06i/VvuECypscFBLRXQi5GjTAkGExtOm8NGn0UDuENz1GOty7Vo0Mi0FNP34gg2Yzi0VNNdB48Aripd6oQYbFoD96XpoEeoORYVm+fPmpU6fQP6dv375FRUVIDzDZdFcf7uNMGTIgBjVdKVL4Rhi6nygjIwP9c0QiUXW1HjPYwE6CwmyDmjZcz4lKodmzKn/OJj+kH44fP37o0KHCwkIul9uxY8e4uDgnJ6fOnTtrtwoEgqtXr6rV6j179vz888+lpaXW1tY9e/ZcuHChhUXjRUYIfeiR9fb2/uabb6ZOnbpz507tGyHNli1bUFsDAX33ctWwOW7IUBiuDgzXm+FCJNIPd+/e/fjjj99///0uXbpALG7dunXFihVff/312bNnBw4cuHTp0v79+0MyOBX279+/bt26oKAgyJnXrl3LZDLhnIBNLBbrwYMHcrl827Ztnp6eHh4eK1euBOuwgPQA34ohrVUjA2I401Kxim+pr8Pl5uZyOJwhQ4aAOXd3940bNxYXF8N6CFz4z+PxtAsDBgyIiory9/eHZdD55ptv3rhxQ7eTgoKCr776SpuSz28sZaysrLQLbQ7fiimtNWhDy3CmNSrE4eurWgC5NOS906dPHzZsWNeuXV1dXe3smmnLCYXCM2fOQPRD7q1SqWQyGZwEuq1eXl5azQaAzqRxuAatJBnuYDwrRk2ZEukHKF8hr4Zo3r59+9ChQ6dMmZKWlvZ8ss2bN+/du3fs2LFQWkNOPmLEiKZboSxHhkJao6IzDHqh1nCm9Z1fBQQEQLBeuHAhISGBwWAsWrRIoVA0TQDVsRMnTkyePBlKbjc3N3t7e4lEgihCVquGHwQZEMOZZnPpTl5cRb1eqiEQwampqbAAjjt16jRnzhyol1VU/Nm9rG1faDQakK3Ln6VS6fXr11tueuivYVInVTl6cpABMWhRAXXv/Pt6aUTevHlz8eLFly5dglpVZmbm4cOHXVxcnJ2dOU+5c+cOrISCvF27dqdPn4Y02dnZEPTdunWrra19+PAhlNnP7BDqYvA/MTExLy8P6YHsOxI475EBMahpuLYBnftID0ALGArd+Pj40aNHz5s3D2IRGkvaEUtQZl+8eHHu3Ll1dXWrV6+GsIZyGlpQMTExkBLOhkmTJkEF7ZkdBgcHR0dHf/7555s2bUJ6APr/fcMM2olk0DEncFn6VELRiPnuCG+eZMty7kpeH+uIDIhBY5rJojv7WCRdqER4c/NUReirhh5jZOjrxFGD7L5YktOxt82L2hi9evVqdj3kulDbQi8AKtV6agqnpKRAid7sJqjbs9nsZjf5+flBJ0yzm3LuSaxsmI6ehh46SMGIwbSb1fWyhk59mx92IhaLm10PlSYw/aLBwtAU1tM4YjguFPDNbqqvrwfTzR6XTqe/qHPtp6+Lo4bYCe2bP0X0BzVjQ88dFPmE8QM7GsXNDYbk5wMivwh+QAcKvjg1gwL6TXJOulBVlFeHcOL6sTJrexYlmhG1I/uPbS/o/IatZ5Bp34XVSn75sczOlR3S1UD96s9D5UCfke+4371alZpo6BFVhudkQhHPikmhZmQMd+Dd/qkCqqPRg+19wqi8bUlPJF+quv9LzevjHLyCKf52RnFXbaVIcfN0ObS23QMtoOeIZ2nyY4TLCusfP5AlX6wKi7Z6dZAdnU79/YVGdKc8VNAyfxdDN6HQgWXnwuZbM+FCp8CapVabwNx4DDqtplIhrVHD75mVLOHy6H7tBRHdrTkW+hpm808xItM6RA/rygrhV1PBpT06A7XtKBy5XJ6TkxMWFobaFEtbVoO6gW/NsLRluvpaWNqwkJFhjKb1Cly5WrJkydGjRxFmkLmLcIGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF7AzTaPRnJycEH5gZ7qhoaGkpAThB8m9cYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGhdwmXnurbfeEovFNBpNoVBUVFQ4OzvDslwuP3fuHMIDXB7vPmrUqPLy8sLCwrKyMo1GU1RUBMt0OkZPt8flq44cOdLT07PpGsjMunXrhrABo5N67NixTR+F4+joOHHiRIQNGJmGsHZz+/MZ7tqA9vLyQtiAkWn0tF7G4TQ+N9Td3X3y5MkIJ/AyPXz4cFdXV1iIjo728PBAOGEsrSz4GNWlyppypUbPH+fWrVvQspo/f36zD51vQ1gsmq0L28APmW4BozCdfVecmlgjq1W7+ltIq/XygGrDw7NiPsqQOHlweo52MIYp/Kk3nXVHnH5b/HqMizE8lqTNqS5TXP1v8Yi5bgIhxcFNcTmdnyZNu1nbJ9bVLDUDQgf20DmeB9Y9RFRDsel7v1RHDzPoY5gND5zErw52uP1TBaIUKk0r6zWifDnfyugeQ9PmWNqyivLkiFKoLDzEVUonL0M/hpkSLG3ZGqqf+kVtNYEmE5tJTfslNCBJtQpRCrk+jQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuIDXOLJ/Q15ezqQpo4YM64VMEzMxnZ+fGxM7GOmNsz+dmPfOFAaDgUwWMzGdlZWB9MmBg19+uPqTN/oORCaLiZXTJSWi3QnxKfeSZTKps7Pr6FGxQwaP3H8g4cDBPbD19T6d581dDCurq6t27v783r3kmppqX9+AGdPnd4jsDAmysh/Mmj3ho7WfHj32XXbOAwaD2b/fkFkzF7z0Bq3tW/c5Ojrl5WUjk8XETG/avFahVKz/T7yVlXVS0q34rRvBd8y4yWKJODHxype7v+VyLTQazfIV70ikkuXL1tjZ2p84eWTFygW7vjjo6+vPZDR+34Q921auWBfULuTWrcTVa5Z6enoPGji85eOCZmTimFjunZef06VzVHBQqJur+7Cho3ds2+fnG8DlcjlsDo1Gs7YWcjicpOTbELtxS1Z17NDFy8tn/rw4JyeXYz8e1u0EMuGQ4DCI4+joHhDr586fRhhgYjEdHdXju8P7JRJx167dIsI7BAeHPZ8mIyONxWJFtu+kfQlGIWVOTqYuQWBAkG7Zy8v36rULCANMzPS7i1b6+vhfuHj2yA/f8vn8oUNGT317DpP5f74FFOFKpbLfgGjdGrVabWv79x0bFha8JssWcN4gDDAx0yB11Kjx8FdZWXH+wpmv9u0UCm3GjpnQNA2fL2Cz2XsSDjVd2bTOVVcn0y1LZVKBwBJhgCmV03K5/MLFn1SqxqF3EKMx4yaFhIRDh8YzyYKCQhUKBcQxVLW0f2w2x97+71HlUHXXLWdmpnt6eCMMMLEa2bbtn3y65ePsnMyi4sKLl36GZnRkZGN5DHFZUVGemnpXJCru1PGVAP926zd8kJKSXCwqgmQzZ8VCDVy3k5u/Xr90+RzsAYqA9PT7A/oPbfmgNbU1d1OS4K+oqADOM+3y48cPkUlB5X1ZlSLFT/tFQ+d4tv4t6Rlpe/fugKYwRC20r6B1pM26oZ29bMV8MBE7fsrbU2ZXVVXuSoi/ffuGXF4HyQYPGjFm9FvoaY/mtBkxH67eCPXtlJQkiHVofE+cMK3lg97+7Sa0055Z2a/f4BXL1qDWIalWnT9QMHm1N6IOEzP9L9Ga3ha/Nzw8EhkQYzBNrmXhAjHdyMr3F6WlpTS7adDAEbNnLUSmD16moUP0yqWk59fHLV4FnazNvoXH4yOzgMR0I3Z29sjcIaZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBStM0OrKyM//JyABNQ4OtKwdRCpUjEWwc2QXZMpVSg8ydikI5i0XxdJkUjzlp19lSlF+HzJ2KonrfcIqvlFBsuvdYxxvHS6S1FM/KpldSrlWolOrAjhSPS6R+1mdFvebb9Y/CutsIhCxbJ47ZPL5Lo2koL5RXFNerFOo3Yqm/BcRY5uy/c6nqSXYdfJTqEgXSJ/B9FQqF9mkcesXOjQNlM2TalEezFlyegafj4cOHS5YsOXr0KMIM0p7GBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcwM40jUbz9fVF+IGd6YaGhry8PIQfJPfGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXMBl5rnZs2fLZDIajSaVSgsLCwMCAmAZ1hw5cgThAS4x3aFDhz179uhepqenw39nZ2eEDSb2pPH/N+PHj/fw8Gi6BjIz0I+wARfTVlZWAwYMaLrGxcUlJiYGYQMupgHw6u7url2GgI6IiAgNDUXYgJFpCOtBgwZplyGgIT9HOIGRaWDMmDHa0jr8KQgnjKvuXVuphMYP0hsMJOjfd8TZs2fHjJgkrtLvIyFodCSwNqKf1yja01Wlit9+rsxNlbgF8KpE+p2z32DYOLPLntS36yToPsIBGQHUmy4rqD/7dXGvsc7W9hwGk+JHDbUtdVJVySN58vnyCe95MlkUF5QUm64orj/zlWjEO17IfKkU1V87Ipq0iuLvSPGJ9tu5yt7jzbyjytaZE9zV+u6VKkQpVJpu0DTkpkoh00bmjkDIepJN8QPgqKwcVpUqfUIpfjKcYbBx4iCqK74UNwOqy5QIA6AuVFVCcZuCXJ/GBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZbxcOHeV/u3Z6efh+Wg4PDZkyb7+vrj0wKMxkxmJ+fGxM7GOmH8vKyhe/OEItrVyxbsyxudWVF+bIV8yUSCTIpzCSms7IykN44NQKougAAEABJREFUd/60XF63/j/xlgJL1DiC2G3q9HFpaSmvvvoaMh1MzHRJiWh3QnzKvWSZTOrs7Dp6VOyQwSP3H0g4cLDxnqvX+3SeN3cxrKyurtq5+/N795Jraqp9fQNmTJ/fIbIzJMjKfjBr9oSP1n569Nh32TkPGAxm/35DZs1cQKe3lLcNGTKqR/feWs2Ao2PjIJna2hpkUpiY6U2b1yqUCggvKyvrpKRb8Vs3gu+YcZPFEnFi4pUvd3/L5VpoNJrlK96RSCXLl62xs7U/cfLIipULdn1xEEpWJqPx+ybs2bZyxbqgdiG3biWuXrPU09N70MDhLRzUytIK/nQvb/92g0ajhYRGIJPCxMrpvPycLp2jgoNC3Vzdhw0dvWPbPj/fAC6Xy2Fz4Ne3thZyOJyk5NsQu3FLVnXs0MXLy2f+vDgnJ5djPx7W7eSNvgNDgsMgjqOje0CsQ+bc+g8gEhVv275p8KAR7m4eyKQwsZiOjurx3eH9Eom4a9duEeEdoBr8fJqMjDQWixXZvpP2JRiFlDk5mboEgQFBumUvL9+r1y6g1vHkyaO4ZXMD/NvB2YNMDRMz/e6ilb4+/hcunj3yw7d8Pn/okNFT357DZP6fbwFFuFKp7DcgWrdGrVbb2trpXlpY8JosW8B5g1pBZlYGFArhYZEfrFrPZrORqWFipkHqqFHj4a+ysuL8hTNf7dspFNqMHTOhaRo+XwAm9iQcarqyaZ2rrk6mW5bKpIK/qlot8Pjxw6XL5r3WrdeSxe8zGAxkgphSOS2Xyy9c/EmlaryfCmI0ZtykkJDwvLycZ5IFBYUqFAqIY6hqaf/YbI69vaMuAVTddcuZmemeHt4tHxeOuGr1kk4dX1ka94GJakYmVyPbtv2TT7d8nJ2TWVRcePHSz9CMjoxsLI8hLisqylNT70KNCZRAUbp+wwcpKcnFoiJINnNWLNTAdTu5+ev1S5fPwR6gCIBurwH9h7Z80BMnfygqKujdux+cIndTkrR/UGYjk4LKu3UqRYqf9ouGzvFs/VvSM9L27t0BTWGIWmhfQetIm3VDOxv6rcBH7Pgpb0+ZXVVVuSsh/vbtG9DjAcmgqjxm9FuQDDKAaTNiPly9EerbKSlJEOvQ+J44YVrLB4WAvnHj2jMroR2/+N33UOuQVKvOHyiYvNobUYeJmf6XaE1vi98bHh6JDIgxmCZXOHCBmG5k5fuLoB+72U2DBo6YPWshMn3wMg0dolcuJT2/Pm7xKuhkbfYtPJ6Z3DlGYroROzt7ZO4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEClabiKJnQ0vWE6/x9oyNaF4mnXqByJYOfCzr8vweGRL5XFchrVM6JSPOYksKOgkuqJugyApErp2c4CUQrFpqMG213+tgiZNQXZ0rz74ojuQkQp1M/6XFuhOLzlSa+xLtb2bJ6lWdUQa8oVJY/rspNrxi72oNMpzr6NYib3Oqn61pmK/DQpVNDKC+uRPoFvq9FoGHS9Z2b2bhxZrSqwo+Ur/W2REWBcz8Crl2mQnk/9x48fr1q16uDBg0jP0BmIxTaiobfGlVtyeHr/aVgcpNLUcSzwetQMIj0n+EBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAnam6XS6n58fwg/sTGs0mtzcXIQfJPfGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmccG45hjUHxs2bPj++++ZTCZ8XxqNptFo6HQ6/L9z5w7CA1ym2ouNjfX0bHz8Me3pRNugGZR36dIFYQMupr28vLp169Y0AxMKhZMnT0bYgNH0mRDWHh4eupf+/v7R0dEIGzAyDZqjoqK0y9bW1hMnTkQ4gdeUuLrS2s/P77XXXkM4gZdpCGsorfl8PlYltJaXtLLKCuvvXq4ueSyvk6iRWdCAGlQqNYtpJh0Jds5slarBPdCi25CXPCy9JdMP06U3T1VE9LQVOrAtBKSPxRih0VF1mUJcpUw8VjJtnQ+Xz3hhyheZfvB7bfpv4jcmuCGCKaBRN3y/OX/Kh95sbvMlcvNr5TJ1+m2i2ZSgM2h9Yp2vHy17YYJm1xbnyRlMqh/aRviHOHhYPEgSv2hr86ZrK5ROXjxEMCmgo9cvwvJFj6Fqvp5VL9eozP8ZhGZITYVCo2l+E6lR4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBMt4p79+7s278rNzdLrVZHhHeYOWOBn18AMinMZMRgfn5uTOxgpB9ycrKWrZjvYO+4bu2nq1dtqKmpXrJ0Tk1tDTIpzCSms7IykN64dv2is7Preys/otMbAwOWp04fdz/17muv9UKmg4mZLikR7U6IT7mXLJNJ4RcfPSp2yOCR+w8kHDi4B7a+3qfzvLmLYWV1ddXO3Z/fu5cM8efrGzBj+vwOkZ0hQVb2g1mzJ3y09tOjx77LznnAYDD79xsya+YCrcIXMW3qXPjTvWQwGkflMU1tdKmJfdxNm9cqlIr1/4m3srJOSroVv3Uj+I4ZN1ksEScmXvly97dcroVGo1m+4h2JVLJ82Ro7W/sTJ4+sWLlg1xcHfX39mYzG75uwZ9vKFeuC2oXcupW4es1ST0/vQQOHv/TQUELX1dUVFRfs3h0PhXSnTl2RSWFi5XRefk6XzlHBQaFuru7Dho7esW2fn28Al8vlsDk0Gs3aWsjhcJKSb0Psxi1Z1bFDFy8vn/nz4pycXI79eFi3kzf6DgwJDoM4jo7uAbF+7vzp1hw69f7dIcN6QZbA4XK3bN7FYrGQSWFipqOjenx3eP/OXZ8n3/lNqVQGB4fZ2to9kyYjIw00RLbvpH0JRqG2nJOTqUsQGBCkW/by8i0qKkCtIMA/KP6zL1cuX1tZUb44bjaUC8ikMLHc+91FK319/C9cPHvkh2/5fP7QIaOnvj3nmSITinA4CfoN+Ps+Ssh4m54QFha8JssWEom4NYcWCATt23eEhejonrEThkIm8faU2ch0MLVqBZM5atR4+KusrDh/4cxX+3YKhTZjx0xomobPF7DZ7D0Jh5qubFrnqquT6ZalMqlAYNnyQX/7/VcoHbSa0VPlLs6uT548QiaFKeXeEonkwsWfVCoVLEOMxoybFBISnpeX80yyoKBQhUIBcQxVLe0fm82xt3fUJYCqu245MzPd08O75eP+ePz7z+LXww61L6VSaWHRExcXE7vtwZRMQ51r2/ZPPt3ycXZOZlFx4cVLP0MzOjKysTyGuKyoKE9NvSsSFXfq+EqAf7v1Gz5ISUkuFhVBspmzYqEGrtvPzV+vX7p8DvYARUB6+v0B/Ye2fNzYmCkQwWvXrfg96dat2zdWfxgHZ9vAVlTXjYrm78v67VylQo7a97JFRkZ6RtrevTugKQxRC+0raB1ps25oZ0M3FtStYsdPgeKzqqpyV0L87ds35PI6SDZ40Igxo9+CZJABTJsR8+HqjVDfTklJgliHxvfECdNeety7KUl79u6A3lBoxcFpBM1rqAwi4+PMnie9xzk6enCe32Ripv8lWtPb4veGh0cic6QF0+QKBy4Q042sfH9RWlpKs5sGDRwxe9ZCZPrgZRo6RK9cSnp+fdziVdDJ2uxbeDw+MgtITDdiZ2ePzB1iGheIaVwgpnGBmMYFYhoXiGlcaBvTV6+ddXHxQAQ9wGIxfX2C0b+mbUxXVZf7+fkhgh5wdLJDbUHbmO7Tuz+fL0AEPdDQoEJtQduYtrJ0RATjhtTIcIGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheouX/6zt3fR4x6o4UE9++n5ORkIf1z4cJZiUSC/iFKpfLN/lEPH+a1JrFKpVqzdvmoMf2+O3wAUQc1pkNDIvbvO9JCgq3bP3nRjVJtSEVF+Y6dW3i8f/wMuJzcLC6H6+Xl05rESUm37qelHPrm5PgYKh+FzFizZs3zawtz69Qq5OxtgfTDosUzWSxWu8DgufOnlJQUnzj5w9EfDx/78XDnzlECgeXb08Y+epT/xx/33Nw8bIS2X+zcsnXbJydPHU1JSY4I7wBifk+6teqDxU8KHu1OiO/fb8jCd2fATnYnbJVKJWXlpR+sjhs5IkZ7oJjYwe5unkKhbb8B0Ww2+9Dh/d8d3p+aeqdbdE+IyHnvTFGrVRcv/dy7dz8Oh9P6z5+YeLVWXAMK4bMdO3bYw8Pb3a1xwOQPRw9t+OTD4yf+e+nyOW9vPwcHx2M/fr99x+aGhoYrV88P6D/0999/Xb9x9X+PfPPj8e81moaQp7fbz3vnbd3nDwtr//xOWv/Bsu/U+oTx+dbNFMoUlNMajSY3NysgIAgWHj7MdXZyef+9j0H80mXzzp079faU2ePHTQbrCbu/gcSgDRx8ve8I/P/s8/Xwq6358JP8/BwIx149+r4zLw5+xEeP8lxc3L7YsZ/JZH65ZzucQNoD1dRUl5SI4ECQHl7a2dpv+E885KVvTRx29drFvn36R73a3dLSau6cd5t+vE2b1/2SeLnpGk9Pny+2f910zYPMP0SioncXroSwPvTd/q1bNx769iQoP33mx8+3JNjbO1y4+NPqD+MOHzo9csS4X3+93qVL1NgxE+6mJG3ctObTTTv9/QPhg02fGRMYEARqm37+ZnfSJvMZUpB7P3nyCH5uXx//goLHcrl83twl2lncaDQai8WGhaycBwFPpwzLyEi7/duNBQuWc7lc2Praa6+nZ9xHTyeFjI7qoZ3XoLCoQCqVTps6V/tzZGf/+V5tMltbOzs7e1gIDg7r169xCllI5ujoXFoqakyc83diHcuWrj514mrTv2c0Aw8e/DFzxgJt7g17Li0rgS+y/+CXs2cuBEOwskf33nAulvx5lEztUQ4d+nr0qFjQDMtOTs5+foEZD9Kafv4WdvLvoSCm4Zt7e/lCXgoCfHz8dHe05uZla3NdsNWnd3/0tOIG/2fOitUmUKvV2imIIIFuLjBY9vb2dXF21e18/Pgp2uWcv35iyELaR3TUfQDIKh0cnBQKBZQRugyg9UAN7vHjhxCm2pflZaUO9o5wCLG4Nn7bRrTtz2QCgYDP40PsQtYS4B8EJzfE9NS35+j2U1tbw+cLmn7+F+0EtQVUmP4r7JrGH5y8lZUVgYHBkBvn5WXPmd2YoyoU9b16vfHeinVN315XVwe5QuBfhrKyMnTLpaUl8LP6+f458zaUo6GhEejpLwglsXYlxFBZWWl4WGT+w1zIS9zdPZ/5eC/NvTOz0uGNln/NYpZyLzksPLJeUe/o6AQ57TN7S7xx1c3VHYRBdR1KKw6Hq/u+cJ7B+Xfm7HHd53/RTtoECnLvpqYD/YN0KyHLgsy2vLwMcjOHp7HbLjDkjz9Sa8W16OlkNO+teoAq/6EAAAncSURBVLe+vh4iVcAXuP41HVij6b92olQp0dNWDfy/fOX8vdQ7cCB4CVJhWfWUr776ok/vfs7OLhCXtrb2z88C/NLcOzMzHU5HbX4DTcGr1y5Anuzj7SeRiLOfzlkJZ9u6j1bm5+c2/bJwcgQFhUJiWIZcOn7rxr59B8B51vTzv2gnbQIFMQ2qtJMow8LfmXBOpvbUtrYWQm42Y1bspk92REf3gPVz5kyEMhxiaNq0eVAvg5/G37+dbm/wU06eNFO7DNEzaODwBYumwy8IBTmDwfD1DQCjUFJA9W3ajBiVUhkcEr5wwXJIDBUFyD8nTRl14OsfoBKAWg3UFSZOmA7158/jN0Dhumzph9oiYOXydes3fKBUKBhM5pDBI6FgQk/rCpB/aN/43sqP4uM3TJw8Ek4vqAxqc/Kmn9/GxrbZnbQJ5j9L1fnzZ06dObZ961cIA/Q+S9XB/9nbypRQ54JCCxkQ6OWA8EXY0zamJ02cjowVqI51794bYY/5X+HY8ukuRCDXsvCBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVxo3jSTRdc0d92aYOTwhcwXeWt+dBHfmlFZXI8IpkZRjszGsfnHJTdv2s6Z3aAhMW1iSGuULr4WbG7zTptfa+/GEQiZ965XIoLpcP1oSYdewhdtbX4cmZbL/y2jM2jte9pCsY0IRoxcqrryvajLmzY+oS8cHN6SaeD385VpN2vAtIWludTSGxo0Gg2dwUBmAWS9hdkyezd2h142nkEt3Ur4EtOo8TaqhppypaxWjcwCkUi0a9eutWvXIrOARqMJHZm8VsThy1PQ6TQbR7aNuUzgrWTSquS5bv76uo3UaCE9J7hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wKOpl1dXRF+4Gi6qKgI4QfJvXGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcePkcg+bBokWLrl+/rnuiOHxrWG58NPydOwgPcJn6debMma6urrS/oNPp8D8gIABhAy6mQ0JCwsPDm2ZgHA5n4sSJCBswms4ZvLq4uOheuru7Dx48GGEDRqYhrCMiIrTLENDjx49HOIHXFO1vvfWWk5MTLHh6eg4fPhzhBF6mQ0NDIyMjWSxWbGwswgzjbWUV59eVPK6vLlNKa9RMFr22Uonagvr6elGJyMvTC7URFpYMJpPGt2bYOrM8AnhWdixklBid6dIn8rtXax6lS9l8Fk/IozNpTDaDZcFExtrsb9A0KOtVqno1LNYUSdhcelAXQcfXhUy2ceWXRmS6plxx7WhFZanS2sXK0oEHgpEJIhcrpFV1JdlVkT2FUYNtdX01lGMspm+eqUq/VePgZ2vtxEdmQWlulUJS13usg6svFxkBRmH67NciiZjuGGCHzAvI2B8mFb3ypnVolDWiGupN/3ywVK5gCd2skJlSmFbStZ+1fwTFeRXFpo/vKmpgW9i4mq1mLSC7fTd+WDSVkU1l/TDxZLkaccxeM+AW5pR0sabksRxRB2WmHz+QlhSo7byFCA+8Orte/r6cwhyUMtPXjlXw7c0/mnVAc4st4P56hrKHwlJjOjO5lsFhcS3ZCCfsvG1SrlUrFRpEBdSYvn9DAl8bGSubt48/dmoz0gPOATbJl6oRFVBgurpMAd1hHJ6R9g/rFZ6NRVayGFEBBabz0qR8Ox7CEq6ArahvqKlom6s1/wgKxoaWFSgsHfTVjaBWqy5e+zrl/oWq6mKhtVOP6PHRr4zSblqzsX+fnm9X15TcTT2vUMh8vCLHDHvPysoeNuU9Svnx9Kelpfm2Nq4D+s5B+sTWXVCQLbO2M3TbmoKYFuXLmWx9nWGnz22/lvhN7x6T4+YfAs0nznx2O+mEdhOdzrzyy/84Ofq8v+R43DvfFRZnXry2D9bXySX7v13Ks7BaOGd/7Ji1N38/KhaXI72h0dCqSyiIaQpM10lUTI5erlOBs5u3f+j52oQuHQbZ23lANHfuMOjyLwd1CZwcvV/pOITBYEK4twuIelKYASszsm7I6mpHDI5zdQ7wcAuJGfkhvER6A85ycTUFj203tGmFXM0VMBlMvRy3qDhLrVEF+r2iW+Pn07GisqC+XqZ96eL097BfCGKt0ZLSfBaL6+zoq10vtHa0ttLjc9VZXIainoKGlqHLabg+L6nWV96lNbp731z091Xhxj4psaSCw2msA7JYnGbfxWb9nwuL2sR6Qq1u0Kgp6CkztGk6ncbm0lUKtT4GGnC5jRW92DHrXJz8mq63tnZq4V2gWS6XNF1TV6fHhpCqXmVlTUFFmIJD8iyZynqVPky7OAcwGCyJpNIxrI92jURaBR2RLGZLnXGODl6Q54tK87QZeHFJDuQBSG+o6tWWbhQMp6HAtLMXVypTWlhyUFtjwRVEdRlx7soePl8IdauqatGJnz6HcnfahM9aeFdQYDcOm3f89KcD35ynVivPXtglENgi/aFR27lR0J1AgWnPIIukKxJrJwHSA0P6L7TgWp45v6NWXG4psAtp133AGy9pHwv4wimxm46f/eyLvTNthC4D+869/uthpLcRiuWPJd7BDsjgUDASQVmv2bsqP7i3N8IPSUWdvLJm9AI3ZHAoaE+zOHSfcAF8Z4QfdTXy0K56ycxeCjV3ynfuKzz5pUhg5/6iBLv2zSksznp+vUajRg0NdEbzH3vlu8f4vDbrZbx8/UDTXpem0CAvfEH2Dn1zwhdU9RUypbhUEtzVG1EBZePITu8VqegWQpfmT3AoZVUqxfPrlcr6hsZ2UfO1OaG1M53eZrkUtLXq5M03t2R1Yp6FZbOboNeF8YITsTCtpEsfy8COzb9R31Bmuk6qOr5L5BLqgvBAVl3XUCcZNNUZUQRlo4ss+Mwew20f38XikRhqpfrJvVIKNSNqx4a6+fMie1gV3C9B5k5+UtGE9zwRpVA/sj/nnvTWzzXuEU7IHIFaWO6twilrvCAPQ5RiFHfr5NyTXDlS5h7uZGHV9h1nFFJTIq3Ir4RoZnOov+/SWO7Aq61UnvyymMZgOfjZsi1MfpY0cZmsNLfSN5T3+lgKusOaxbjun85MFt88XclgMQX2PEtHHotjYsrrautrS2XqegWHg3qNtrNzMaIsyhjnRHiUIc1MlsJ/joAJPSVMNpPDZ6uUFIzTaA3Qi6KUK+FaJIfPVCtUfhEC//Y8Rw+juJO2KUY9x2B1mUJWq5bWqqC/hJJxGq2Bw2Vw+XSeFYNvxRQIjTcTwmU2SQKZIRYXiGlcIKZxgZjGBWIaF4hpXPhfAAAA///WZC1gAAAABklEQVQDADItWWzzqEWZAAAAAElFTkSuQmCC",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from IPython.display import Image, display\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "\n",
    "\n",
    "def step_1(state):\n",
    "    print(\"---Step 1---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_2(state):\n",
    "    print(\"---Step 2---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_3(state):\n",
    "    print(\"---Step 3---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"step_2\", step_2)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"step_2\")\n",
    "builder.add_edge(\"step_2\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory, interrupt_before=[\"step_3\"])\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7d5f80f-9d8c-4a39-b198-24fe94132b41",
   "metadata": {},
   "source": [
    "We create a [thread ID](https://langchain-ai.github.io/langgraph/concepts/low_level/#threads) for the checkpointer.\n",
    "\n",
    "We run until step 3, as defined with `interrupt_before`. \n",
    "\n",
    "After the user input / approval, [we resume execution](https://langchain-ai.github.io/langgraph/concepts/low_level/#breakpoints) by invoking the graph with `None`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dfe04a7f-988e-4a36-8ce8-2c49fab0130a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n",
      "---Step 2---\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Do you want to go to Step 3? (yes/no):  yes\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 3---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Do you want to go to Step 3? (yes/no):  yes\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Step 3---\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"input\": \"hello world\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    print(event)\n",
    "\n",
    "try:\n",
    "    user_approval = input(\"Do you want to go to Step 3? (yes/no): \")\n",
    "except:\n",
    "    user_approval = \"yes\"\n",
    "\n",
    "if user_approval.lower() == \"yes\":\n",
    "    # If approved, continue the graph execution\n",
    "    for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "        print(event)\n",
    "else:\n",
    "    print(\"Operation cancelled by user.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3333b771",
   "metadata": {},
   "source": [
    "## Agent\n",
    "\n",
    "In the context of agents, breakpoints are useful to manually approve certain agent actions.\n",
    " \n",
    "To show this, we will build a relatively simple ReAct-style agent that does tool calling. \n",
    "\n",
    "We'll add a breakpoint before the `action` node is called. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "6098e5cb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:10:34\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:10:34\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:10:34\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set up the tool\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.graph import MessagesState, START\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.graph import END, StateGraph\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return [\n",
    "        \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "    ]\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "# Set up the model\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
    "model = model.bind_tools(tools)\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Finally we pass in a mapping.\n",
    "    # The keys are strings, and the values are other nodes.\n",
    "    # END is a special node marking that the graph should finish.\n",
    "    # What will happen is we will call `should_continue`, and then the output of that\n",
    "    # will be matched against the keys in this mapping.\n",
    "    # Based on which one it matches, that node will then be called.\n",
    "    {\n",
    "        # If `tools`, then we call the tool node.\n",
    "        \"continue\": \"action\",\n",
    "        # Otherwise we finish.\n",
    "        \"end\": END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "\n",
    "# We add in `interrupt_before=[\"action\"]`\n",
    "# This will add a breakpoint before the `action` node is called\n",
    "app = workflow.compile(checkpointer=memory, interrupt_before=[\"action\"])\n",
    "\n",
    "display(Image(app.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
   "metadata": {},
   "source": [
    "## Interacting with the Agent\n",
    "\n",
    "We can now interact with the agent.\n",
    "\n",
    "We see that it stops before calling a tool, because `interrupt_before` is set before the `action` node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cfd140f0-a5a6-4697-8115-322242f197b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "search for the weather in sf now\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Certainly! I'll search for the current weather in San Francisco for you. Let me use the search function to find this information.\", 'type': 'text'}, {'id': 'toolu_01PKgmY3du7hFeLNPu2P3hMc', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_01PKgmY3du7hFeLNPu2P3hMc)\n",
      " Call ID: toolu_01PKgmY3du7hFeLNPu2P3hMc\n",
      "  Args:\n",
      "    query: current weather in San Francisco\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "thread = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "inputs = [HumanMessage(content=\"search for the weather in sf now\")]\n",
    "for event in app.stream({\"messages\": inputs}, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bca3814-db08-4b0b-8c0c-95b6c5440c81",
   "metadata": {},
   "source": [
    "**Resume**\n",
    "\n",
    "We can now call the agent again with no inputs to continue.\n",
    "\n",
    "This will run the tool as requested.\n",
    "\n",
    "Running an interrupted graph with `None` in the inputs means to `proceed as if the interruption didn't occur.`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "51923913-20f7-4ee1-b9ba-d01f5fb2869b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Certainly! I'll search for the current weather in San Francisco for you. Let me use the search function to find this information.\", 'type': 'text'}, {'id': 'toolu_01PKgmY3du7hFeLNPu2P3hMc', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_01PKgmY3du7hFeLNPu2P3hMc)\n",
      " Call ID: toolu_01PKgmY3du7hFeLNPu2P3hMc\n",
      "  Args:\n",
      "    query: current weather in San Francisco\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: search\n",
      "\n",
      "[\"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"]\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Based on the search results, I can provide you with information about the current weather in San Francisco:\n",
      "\n",
      "The weather in San Francisco is currently sunny. This means it's a clear day with plenty of sunshine.\n",
      "\n",
      "However, I should note that the search result included an unusual comment about Gemini zodiac signs. This appears to be unrelated to the weather and might be a quirk of the search results or possibly a reference to some astrological forecast. For the purposes of your weather inquiry, we can focus on the fact that it's sunny in San Francisco right now.\n",
      "\n",
      "Is there anything else you'd like to know about the weather in San Francisco or any other location?\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(None, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/human_in_the_loop/dynamic_breakpoints.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "b7d5f6a5-9e59-43e4-a4b6-8ada6dace691",
   "metadata": {},
   "source": [
    "# How to add dynamic breakpoints with `NodeInterrupt`\n",
    "\n",
    "!!! note\n",
    "\n",
    "    For **human-in-the-loop** workflows use the new [`interrupt()`](../../../reference/types/#langgraph.types.interrupt) function for **human-in-the-loop** workflows. Please review the [Human-in-the-loop conceptual guide](../../../concepts/human_in_the_loop) for more information about design patterns with `interrupt`.\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Breakpoints](../../../concepts/breakpoints)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)\n",
    "    \n",
    "\n",
    "Human-in-the-loop (HIL) interactions are crucial for [agentic systems](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop). [Breakpoints](https://langchain-ai.github.io/langgraph/concepts/low_level/#breakpoints) are a common HIL interaction pattern, allowing the graph to stop at specific steps and seek human approval before proceeding (e.g., for sensitive actions).\n",
    "\n",
    "In LangGraph you can add breakpoints before / after a node is executed. But oftentimes it may be helpful to **dynamically** interrupt the graph from inside a given node based on some condition. When doing so, it may also be helpful to include information about **why** that interrupt was raised.\n",
    "\n",
    "This guide shows how you can dynamically interrupt the graph using `NodeInterrupt` -- a special exception that can be raised from inside a node. Let's see it in action!\n",
    "\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, let's install the required packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2013d058-c245-498e-ba05-5af99b9b8a1b",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9f9574b",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9aa244f-1dd9-450e-9526-b1a28b30f84f",
   "metadata": {},
   "source": [
    "## Define the graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9a14c8b2-5c25-4201-93ea-e5358ee99bcb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from IPython.display import Image, display\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.errors import NodeInterrupt\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "\n",
    "\n",
    "def step_1(state: State) -> State:\n",
    "    print(\"---Step 1---\")\n",
    "    return state\n",
    "\n",
    "\n",
    "def step_2(state: State) -> State:\n",
    "    # Let's optionally raise a NodeInterrupt\n",
    "    # if the length of the input is longer than 5 characters\n",
    "    if len(state[\"input\"]) > 5:\n",
    "        raise NodeInterrupt(\n",
    "            f\"Received input that is longer than 5 characters: {state['input']}\"\n",
    "        )\n",
    "\n",
    "    print(\"---Step 2---\")\n",
    "    return state\n",
    "\n",
    "\n",
    "def step_3(state: State) -> State:\n",
    "    print(\"---Step 3---\")\n",
    "    return state\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"step_2\", step_2)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"step_2\")\n",
    "builder.add_edge(\"step_2\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# Compile the graph with memory\n",
    "graph = builder.compile(checkpointer=memory)\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad5521e1-0e58-42c5-9282-ff96f24ee6f6",
   "metadata": {},
   "source": [
    "## Run the graph with dynamic interrupt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83692c63-5c65-4562-9c65-5ad1935e339f",
   "metadata": {},
   "source": [
    "First, let's run the graph with an input that <= 5 characters long. This should safely ignore the interrupt condition we defined and return the original input at the end of the graph execution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b2d281f1-3349-4378-8918-7665fa7a7457",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello'}\n",
      "---Step 1---\n",
      "{'input': 'hello'}\n",
      "---Step 2---\n",
      "{'input': 'hello'}\n",
      "---Step 3---\n",
      "{'input': 'hello'}\n"
     ]
    }
   ],
   "source": [
    "initial_input = {\"input\": \"hello\"}\n",
    "thread_config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "for event in graph.stream(initial_input, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b66b926-47eb-401b-b37b-d80269d7214c",
   "metadata": {},
   "source": [
    "If we inspect the graph at this point, we can see that there are no more tasks left to run and that the graph indeed finished execution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4eac1455-e7ef-4a32-8c14-0d5789409689",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "()\n",
      "()\n"
     ]
    }
   ],
   "source": [
    "state = graph.get_state(thread_config)\n",
    "print(state.next)\n",
    "print(state.tasks)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "f8e03817-2135-4fb3-b881-fd6d2c378ccf",
   "metadata": {},
   "source": [
    "Now, let's run the graph with an input that's longer than 5 characters. This should trigger the dynamic interrupt we defined via raising a `NodeInterrupt` error inside the `step_2` node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c06192ad-13a4-4d2e-8e30-f1c08578fe77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n",
      "{'input': 'hello world'}\n",
      "{'__interrupt__': (Interrupt(value='Received input that is longer than 5 characters: hello world', resumable=False, ns=None),)}\n"
     ]
    }
   ],
   "source": [
    "initial_input = {\"input\": \"hello world\"}\n",
    "thread_config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "173fd4f1-db97-44bb-a9e5-435ed042e3a3",
   "metadata": {},
   "source": [
    "We can see that the graph now stopped while executing `step_2`. If we inspect the graph state at this point, we can see the information on what node is set to execute next (`step_2`), as well as what node raised the interrupt (also `step_2`), and additional information about the interrupt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2058593c-178e-4a23-a4c4-860d4a9c2198",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('step_2',)\n",
      "(PregelTask(id='35aff9f0-f802-eb95-9285-09849cdfd383', name='step_2', path=('__pregel_pull', 'step_2'), error=None, interrupts=(), state=None, result=None),)\n"
     ]
    }
   ],
   "source": [
    "state = graph.get_state(thread_config)\n",
    "print(state.next)\n",
    "print(state.tasks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc36d1be-ae2e-49c8-a17f-2b27be09618a",
   "metadata": {},
   "source": [
    "If we try to resume the graph from the breakpoint, we will simply interrupt again as our inputs & graph state haven't changed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "872e7a69-9784-4f81-90c6-6b6af2fa6480",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "{'__interrupt__': (Interrupt(value='Received input that is longer than 5 characters: hello world', resumable=False, ns=None),)}\n"
     ]
    }
   ],
   "source": [
    "# NOTE: to resume the graph from a dynamic interrupt we use the same syntax as with regular interrupts -- we pass None as the input\n",
    "for event in graph.stream(None, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "3275f899-7039-4029-8814-0bb5c33fabfe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('step_2',)\n",
      "(PregelTask(id='35aff9f0-f802-eb95-9285-09849cdfd383', name='step_2', path=('__pregel_pull', 'step_2'), error=None, interrupts=(), state=None, result=None),)\n"
     ]
    }
   ],
   "source": [
    "state = graph.get_state(thread_config)\n",
    "print(state.next)\n",
    "print(state.tasks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5862dea-2af2-48cb-9889-979b6c6af6aa",
   "metadata": {},
   "source": [
    "## Update the graph state"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8724ef6-877a-44b9-b96a-ae81efa2d9e4",
   "metadata": {},
   "source": [
    "To get around it, we can do several things. \n",
    "\n",
    "First, we could simply run the graph on a different thread with a shorter input, like we did in the beginning. Alternatively, if we want to resume the graph execution from the breakpoint, we can update the state to have an input that's shorter than 5 characters (the condition for our interrupt)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "2ba8dc8d-b90e-45f5-92cd-2192fc66f270",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'foo'}\n",
      "---Step 2---\n",
      "{'input': 'foo'}\n",
      "---Step 3---\n",
      "{'input': 'foo'}\n",
      "()\n",
      "{'input': 'foo'}\n"
     ]
    }
   ],
   "source": [
    "# NOTE: this update will be applied as of the last successful node before the interrupt, i.e. `step_1`, right before the node with an interrupt\n",
    "graph.update_state(config=thread_config, values={\"input\": \"foo\"})\n",
    "for event in graph.stream(None, thread_config, stream_mode=\"values\"):\n",
    "    print(event)\n",
    "\n",
    "state = graph.get_state(thread_config)\n",
    "print(state.next)\n",
    "print(state.values)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f16980e-aef4-45c9-85eb-955568a93c5b",
   "metadata": {},
   "source": [
    "You can also update the state **as node `step_2`** (interrupted node) which would skip over that node altogether"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "9a48e564-d979-4ac2-b815-c667345a9f07",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n",
      "{'input': 'hello world'}\n",
      "{'__interrupt__': (Interrupt(value='Received input that is longer than 5 characters: hello world', resumable=False, ns=None),)}\n"
     ]
    }
   ],
   "source": [
    "initial_input = {\"input\": \"hello world\"}\n",
    "thread_config = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "17f973ab-00ce-4f16-a452-641e76625fde",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 3---\n",
      "{'input': 'hello world'}\n",
      "()\n",
      "{'input': 'hello world'}\n"
     ]
    }
   ],
   "source": [
    "# NOTE: this update will skip the node `step_2` altogether\n",
    "graph.update_state(config=thread_config, values=None, as_node=\"step_2\")\n",
    "for event in graph.stream(None, thread_config, stream_mode=\"values\"):\n",
    "    print(event)\n",
    "\n",
    "state = graph.get_state(thread_config)\n",
    "print(state.next)\n",
    "print(state.values)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/human_in_the_loop/edit-graph-state.ipynb
================
{
 "cells": [
  {
   "attachments": {
    "1a5388fe-fa93-4607-a009-d71fe2223f5a.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAB8cAAAWwCAYAAADAIUCbAAAAAXNSR0IArs4c6QAAIABJREFUeF7snQmcTeUbx58x+zAztkEopVBJQqEsbeYv2SJS1uypECktlKIkW1LZtyylRFFUiEgohRKVlCJjxjbGmBnGzPw/v5dzO/fMufu5M3dmfs/nM5+Zufc97/J9zz33nPf3Ps8TlJOTkyM0EiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEijEBIIojhfi2eXQSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEFAGK4zwRSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAECj0BiuOFfoo5QBIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAYrjPAdIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAQKPQGK44V+ijlAEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABiuM8B0iABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABAo9AYrjhX6KOUASIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAGK4zwHSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAECj0BiuOFfoo5QBIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAYrjPAdIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAQKPQGK44V+ijlAEiABEiABEiABEghMAud27pRz+/ZJaOXKEnbDDRJcunRgdpS9ckkgdeVKOT1rlgSFhEhMz55SolUrkWLFXB7HAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQlwQojuclbbZFAiRAAiRAAiRAAiRgI3B85Eg5+9lndkTCataUsBo1JKxaNYmoX19Cr7qKxAKcQOaBA3KkY0e7XkbFx0vZ0aMlKCwswHvP7pEACZAACZAACZAACZAACZAACZAACZAACRQl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S3rsMZXXW2/YpFAeXu6GfOfuTDXCpmcdO+Y4B7wH40saPFjSN2+WUkOGSEy3bu40zzIFkMAjjzwia9askQ8//FDq169vyQjy8nvG2OGcnBzJysqyTOi3BAgrIQESIAESIAESIAESIAESIAESIAESyEWA4jhPChIgARIgARIggVwEXnjhBVmwYIH07dtXRlzKl+wrpoULF9rq+vLLL6VGjRq+Vml6PMLqQnT54osvZPDgwTJ06FC/tMNKfSMAj/CkRx+VzL//dllRVHy8xDz4oITXqWMrC/EUonjEpXDsZpVooc/xHo5F7m0I2ReOHBGIxqgDBs9veIAj1HnO+fMSHBdn3qesLCXMh9WoIUEREaqsUZjHuDL375eI226ToJAQm+d4+VmzJAIbNbKzlde4ErodiNDYEJCTlqY8utGOI8tOTpZiJUu65AdPcWw20BsE+MRevdTmA3icl7jvPineqpUKO++NgQU2GxSLiXF5uKvxYVzIO168bVu1UYFWOAnceeed8ueff8rs2bMlPj7ekkHm1feM1tkTJ07IrFmz5OOPP5aEhAT1crVq1aRfv37ywAMPWDImVkICJEACJEACJEACJEACJEACJEACJGAtAYrj1vJkbSRAAiRAAiRQYAhgIf/xxx+Xrl27Srt27ez6jYV9iMsQyO+44w5LxjR69GglgsA+//xzue666yyp11jJ1KlTZcKECepliuN+QWxppRBKIYbmpKdLdnq6+h0UGSnBsbFSDD/R0V55MqtO5uRIysKFcuqNNxz2GXmySz7xhMoj7g/TwqpXmDPHTtz3R1ue1olc70qEj4319FCWJwG3CMCTe/LkyTJq1CipVauW3TFVqlRR///0008Sa9E5mFffM+g3wsJ369bNJoobgTz99NPy2GOPucWJhUiABEiABEiABEiABEiABEiABEiABPKOAMXxvGPNlkiABEiABEggoAgglC3CphcvXlz27t1r17cmTZrIP//8Y6lo0aNHD9m4caNqZ/v27VKhQgXLeWzbtk06depkq5fiuOWIC2SFWadOSdq6dZL5119KDEYY89Bq1ZQntzuezr4MOvmtt+T03LlSbvJkibz9dl+q4rEkUOAIPPnkk7Js2TJp2bKlvPPOO7b+JyYmqlDq2CSFzVJWWV58z2h97dixo0oRAhszZow0atRIzp07J6+99prtu2737t1S0p3oDlYBYD0kQAIkQAIkQAIkQAIkQAIkQAIkQAIuCVAcd4mIBUiABEiABEigcBLQ8opjdD/++KOUQZ5nETl+/LjK043/8bpV1qZNG4FQADtw4IBf8rL27NlTvvrqK1uXKY5bNXusx1sCZz74QE6+9pqUfu45ie7QwdtqeBwJFEgCjz76qHz22Wcq1Pi6detsY4Ag3r9/f+nQoYNMnDjRsrHlxfeM1llNHO/du7cgFYlmaWlptsgo/oySYhk0VkQCJEACJEACJEACJEACJEACJEACRYwAxfEiNuEcLgmQAAmQAAloBJ555hl577331L8rV66U2rVrq78hYGCx30y0QD7vgwcPSmhoqFx22WUeCdz33HOP7Nu3T7WD9ox24cIF2bRpkxw+fFgJCzfddJNqxxPLysqS1NRUmT9/vkyaNIlh1T2Bx7J+IZC+ZYskDRwoEQ0bSnmd56xfGmOlJBBgBBo2bGgLO/7XX39JsWLFVA/haY1c3UiBAZFZb4H+PaP1Fd+FEP5bt24tV1xxhW0Ihw4dksaNG6v/169fL9dcc02AzQq7QwIkQAIkQAIkQAIkQAIkQAIkQAJFmwDF8aI9/xw9CZAACZBAESUAEfmWW26REydOKALTpk2Te++9V/2N3LDz5s2TN954w5aLHCHWkTcWecjPnj2rysGz/MEHH5Tu3bvbhUjPyclRgsD333+vQra3b99eKleuLFqo9meffVYeeeQRO/IITfvcc8+pHK6aQXxfunSpaHlpPZkqTXgZOnSoEshpJJBfBHIyMuSf225TzVdauVJCKlfOr66wXRLIUwJ//vmn3HnnnbY28Z1Qrlw59T9ex/tbtmxR3w+wgvY94wjm7NmzBbnP8f2HfOohISF5yp2NkQAJkAAJkAAJkAAJkAAJkAAJkAAJOCdAcZxnCAmQAAmQAAkUQQIQo/XeeiNGjJC+ffsKvLdvvPFGJYBv3bpVKlasKPrw60CFBX+8rgnZEMkXLFggtWrVUvlWEUZXHz4X5Xfs2KE86SDG673UUR/agciu1d2sWTN1PPoAYR2ivKcGAX7JkiXy/PPPS79+/Tw9nOVJwFICx0eMkLOrV0v52bMlom5dS+tmZSQQqASmTp2qPMM1++STT1REEEQQQSQRbIDatm2bersgfs+YccfmsFatWsmePXty5VkP1Hliv0iABEiABEiABEiABEiABEiABEigqBGgOF7UZpzjJQESIAESIAERlR8VgrZmEKTnzJmjwpp369ZNhTVHrlSY5vGNv7HoP378eImKipKkpCR55513lJc5DB6ACxculOnTp6v/4R1eokQJlV8cgsiLL76oXl+2bJnyWodBLEf9mhA+btw4CQsLk7lz58pLL72UK0+tu5M3aNAggRDzyiuvSNeuXd09jOVIwC8EslNSJH3rVikeHy9yKay0XxpipSQQQAQ073CtSyNHjpQ+ffqo75C33npLbcjCxqyC+j1jhnr16tUyYMAA9dby5culXr16ATQj7AoJkAAJkAAJkAAJkAAJkAAJkAAJkAAIUBzneUACJEACJEACRYyA3jt88eLF0qVLF0UA4V+feOIJ+eqrr5Qw/fDDD6vX77//fuX5/b///U9mzJhhyxmrYYN39qJFi2TIkCE2L++ZM2dK8+bNVRG0Fx8fr0LowgYOHCjDhg1Tf+tFeniY161bV+UZRx9gEFIgqHhqEF2+/PJL03y2ntbF8iRAAiRAAp4R+OWXX1SqjmrVqkmPHj2UCF67dm1577331IYobIyCkFyzZs0C+z1z+vRp+eijj+SHH36Q1NRUNQ6EjsdmrxtuuEFt0GJIdc/OG5YmARIgARIgARIgARIgARIgARIggbwgQHE8LyizDRIgARIgARIIIALbt2+XBx54QOrXry8ffvihLfcrhIvdu3ernmKxv2zZsnaiBbzEW7ZsmWskmpc2PLQhkletWlU2bNigymVnZyvvbeRg1eyKK66Qr7/+Wr139dVXq5c7deqk8ovrDWHf4W0eHR3tMT30ZfPmzXZ50z2uhAeQAAmQAAl4RUALqT506FCVNgPfNzDtewaiuT79hrYJq6B8zyQnJ6txIUS8I0PKkWeeeUalMAkKCvKKIw8iARIgARIgARIgARIgARIgARIgARKwngDFceuZskYSIAESIAESCGgCCHs+duxYefnll5VH35tvvikTJ0609Rke4rNmzbL9/9hjj8mnn36qBHWEPS92KSz08ePHleg9bdo0gQiAOpHfGx5zKJ+RkSFPPvmkfPbZZ6ouhJh99dVXlRc6hHCEXIfYfvPNNyvvu8TERJV/Fl53derUUaHdvTVNaEHo3tatW3tbDY8jARIgARLwgkD37t3VJijNOxwboLT84qhOH50E/xe07xl9jnRsAECOcUQrMTOk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bdVCWTt/gqSdSVbC+LvvvisV+TmylDErIwESIAESIAESIAESIAESIAESIIFAI+B3cfzChQuSkpIiJUqUkLCwML+P/9y5c3LmzBnVHsLk0UiABEiABEiABHwnMHv2bBk9erStooply8ojbdtKj3vu8b1ykxr2Hz6sBPI127apdy+//HKZNGmS1K9f3y/tsVIS8IVA6vLlcuKSKF6ibVsp8+KLvlTn9NjMgwclsX9/yTp2zFYuomFDKf/OO35rkxWTQF4S6N6jh3x9KYLIFdfVlcb395FaTVv6rQsJB/bK4jED5MS/B6VBgwYCj/XwcPfCtPutU6yYBEiABEiABEiABEiABEiABEiABEjAbwQsF8dTU1Nl3bp18tlnn8kPP/xg8y7r27evjBgxwm8D0Speu3at9OnTR/1bvHhxqV69urRs2VLuuecetbBOIwESIAESIAES8IwAvPfgxacZBHEI4xDI/W2vL1kib18K4R4VFSWTJ09W3+k0Egg0AnqBPLpDByn93HOWdzHnwgVJeOABgUCuWXCZMlJ57VrL22KFJJDXBM6fPy/3tGgpB/74XUqWqyi3dxogDVt3z5NuIBf5whf7SMqJRLnn3lYyY9rbedIuGyEBEiABEiABEiABEiABEiABEiABEsh7ApaK4xDER44caRduVRvSsGHDZODAgX4f4e7du6VNmzam7aD9xx57TCIjI/3ej/xu4PTp07J69WqJi4uTO+64Q0JCQvK7S4W6/fT0dEm+FP4XAwV3MndvyhFZ4uzZs7bCpUqVKrJRH7ChaNeuXUr4q1SpknsAWYoE/Ezgq6++kt69e0t2drZUq1xZXuzZU5rceKOfW7Wvftg778iHGzaoFytXrixLly5Vv2kkEGgEzn76qRx/4QXVreiHHpLSTz1laRePdu8u5/bssdUZFBkpV2zZYmkbrIwE8oPA/v375dFHH5Xff/9dYuMuk+4vz5GKV9fM0678+dM2WfhiX8k4myJde/SUV14elaftszESIIGiR+DYsWOCaIswRD7EszCNBEjAOgJZWVmSlJRkqzAmJkY5UtFcE0Bk1pMnT9oKghv4FUXjWp3zWS8Ma+JYmz548KD6geNnmTJlbD9XXXVVUTztOWYnBBITE9UaKSwoKEgqVKhAXgWUgCXieE5OjowaNUrmz5/vEANyhXbs2NHvmHByOgu5et1116lF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AAIIIIAAAggggAACCCCAAAIIIBCjAOF4jFBshgACCCCAAALJIbB37177448/rGTJkpk2uH///jZjxozwdvPnz7datWplul8ybvDhhx9ax44dw03v3r27DRgwIBlPJVtt7tOnj7366qvhOp566im7/PLLs1UnOyOAAAIIIIAAAggggAACCCCAAAIIIIBAcggQjidHP9FKBBBAAAEEEEhHQKHvypUrbe3atbZ69WrbsmWLs2WpUqWsTp06VrNmTatXr541b97c8uXLF1HLHXfcYW+88Ub4a4TjqX2Z6dpo1KhRxEnqGpkzZ06unbhmLtizZ49zfF2fFSpUyLW2cGAEEEAAAQQQQAABBBBAAAEEEEAAAQRSXYBwPNV7mPNDAAEEEEAgRQUUKA4ePDhiFHBGp9qgQQN7+umnrWzZsuHNCMfz1sjxr7/+2i688MKIy6RcuXK2ePHiXPspefjhh+3ZZ58NH3/69OnWuHHjXGsPB0YAAQQQQAABBBBAAAEEEEAAAQQQQCCVBQjHU7l3OTcEEEAAAQRSVGDZsmXWq1ev8CjxWE/ztNNOc8L0QoUKObsQjuetcFx9fs0115iun1C577777NZbb431Ekr4dt5wfMqUKXbeeecl/DhUiAACCCCAAAIIIIAAAggggAACCCCAAAJmhONcBQgggAACCCCQVALff/+9NW3aNE2bNQL4nHPOsdNPP93y589vS5cutYULF9q+ffvC25511ln2r3/9ywoXLux8jXA874XjO3bssFdeecWZhr9ly5bOSPICBQrk2s8A4Xiu0XNgBBBAAAEEEEAAAQQQQAABBBBAAIE8KEA4ngc7nVNGAAEEEEAgmQV69+5tr732WsQpdOnSxQYMGGBHHnlkxNe3bdtmN954o61Zs8Y0rfqLL75oxYoVC29DOJ73wvGgXfuE40HrEdqDAAIIIIAAAggggAACCCCAAAIIIJDKAoTjqdy7nBsCCCCAAAIpJvDFF1/YpZdeGnFWjz/+uDNVdnpl7969NnHiRLvlllsignFtn1E4rhHnv/76qxO4a1T6EUcckWXNv/76yzZv3uxM53788cdbvnz5slxXaEe176effnLO6bjjjkvzYID3AB9++KF17Ngx/OXu3bs7DxT4Uf7888+wXcmSJZ2R/FktBw8eNI32LlKkiB199NFZrca3/f7++2/TQxi7du2yE044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uHBhuP6PPvrIFMCHSt26dW3btm3pft/dsObNm5vWJA8VrT/uDdfTQwpaOO6dbv6xxx4zLRlAQQABBBBAAAEEEEAAAQQQQAABBBBAAIHECxCOJ96UGhFAAAEEEEDAJwHvVOM6zJQpUyKm5I7n0IkOx3v27Glz584NN0HTot9+++3xNCm87eHDh61y5coR+1577bX28MMPZ6k+bzjevXt3GzBgQERdauucOXMivvbBBx+kaYc28Ibjt912m917771R27Zjxw4744wzIr6nhwgUzIeKAuElS5aE/3/nnXeawnlv8Y6o1/fXrFljRx11VEwuQQvHNYL+7rvvDrc9Wr/EdGJshAACCCCAAAIIIIAAAggggAACCCCAAAKZChCOZ0rEBggggAACCCAQFAFNXX7RRRdFjM5W2xRK9+7dOyJs9bZZU3cXKVIk4suJDsc1gvmWW26JOIamE7/hhhssf/78cTPef//99tJLL0Xsp6917drVChQoEFd9sYTjP/30k5199tkR9WotcD2A4C3ecFyjwOfNm2clSpRIs60C/WeffTb89TPPPNNef/31iO00A4DWXQ+VYsWK2SuvvBIxbf6ePXusc+fOpoA8VDSbwIwZM2K2CFo4vnjxYmvfvn1E+72j6mM+OTZEAAEEEEAAAQQQQAABBBBAAAEEEEAAgQwFCMe5QBBAAAEEEEAgqQTee+8969KlS5o2V6tWzQnOq1at6qz9rXD1t99+s//973+mtckXLVpkn332mR1//PHhfRMdjqtiheMKyd1FwfHNN9/stEvhsUJ6raH+/fff23fffeesLX7iiSemOaedO3da48aNbd++fRHf0/rjV1xxhVNf6dKl7eDBg842mhr9xx9/NIXPmjbdXWIJx7X9008/bY8++mjEvhMmTHBs3cUbjut76gM9qKDjayS3zm/27NlpAv4XXnghTfv08EKTJk0iplZXH/bq1cs5T025PnXqVNNU9e7y6quvWr169SK+9vfff9vKlSujXtcK5adNmxb+XseOHe3KK6+M2LZQoUJWu3bt8Nd0Dbk9W7VqZc8991zU+hXU9+/fP/w9PRzRqVOndH/GdI1626+N9bCH2qD+1UMh27dvd5YUqFKlip1zzjlJ9TNLYxFAAAEEEEAAAQQQQAABBBBAAAEEEAiKAOF4UHqCdiCAAAIIIIBAzAKjRo2yZ555Jubt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Qw8C6O89+/dHeDSqU8f69OhhjVq0MCtQIE9bcfIIIIAAAggggAACCCCAAAIIIIAAAghkV4BwPLuC7I8AAggggAACOS+wf7/Nfv55GzN1appR4hoZrtHgLRs0cP6uULp0zrePIyKQRQGF5WNmzjTvqPJGp51mE594wkpUrZrFmtkNAQQQQAABBBBAAAEEEEAAAQQQQAABBAjHuQYQQAABBBBAIKkE1n32mXXr2TNqKH7TJZeY/mgKcgoCySygkeST5s2z2YsWhU9D1/WMxx6zmppqvXjxZD492o4AAggggAACCCCAAAIIIIAAAggggECuCBCO5wo7B0UAAQQQQACBrAjMmjDB+j34YMSu5UuXtrbnnUconhVQ9gm8wOatW23oCy/YwqVLw219rFcva9u5sxmzIgS+/2ggAggggAACCCCAAAIIIIAAAggggECwBAjHg9UftAYBBBBAAAEEogjs3r3bhvXrZ7MWLIj47uDOnZ1QnIJAqgvMWrTICclDa5LrgZDHRo40q1Ah1U+d80MAAQQQQAABBBBAAAEEEEAAAQQQQCBhAoTjCaOkIgQQQAABBBDwQ2Dz5s3WrXNnW/fNN+HqNVr8+f79rWblyn4ckjoRCKSAplq/edQo+3HrVqd9vdu2tT49e5qddFIg20ujEEAAAQQQQAABBBBAAAEEEEAAAQQQCJoA4XjQeoT2IIAAAggggEBYQCPG27dpY+u++ir8tZb169vonj1ZV5zrJE8K7N63z7qNGmWL161zzv+5u++2VhdfbFalSp704KQRQAABBBBAAAEEEEAAAQQQQAABBBCIR4BwPB4ttkUAAQQQQACBHBXo16ePzXr11fAxnZGy7drlaBs4GAJBFLjo7rtt/caNzkMiMx54wGqed55ZmTJBbCptQgABBBBAAAEEEEAAAQQQQAABBBBAIDAChOOB6QoaggACCCCAAAJugUmTJtmwYcPCX2pz3nnOiHEKAgiYbd661S7q189Zg1zLC8wYMcJK1KtnVrQoPAgggAACCCCAAAIIIIAAAggggAACCCCQjgDhOJcGAggggAACCAROYN26dda6detwu2pUrmxvPfpo4NpJgxDITYHFa9da+wcecJrQqkEDe+6RR5hePTc7hGMjgAACCCCAAAIIIIAAAggggAACCARegHA88F1EAxFAAAEEEMhbAlpnvEmTJqa/VcqX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CCCAAAIIIIAAAggggAACCCCAAAIeAcJxLgkEEEAAAQQQyFUBwvFc5efgKSgw5s03bezkyYTjKdi3nBICCCCAAAIIIIAAAggggAACCCCAQPYECMez58feCCCAAAIIIJBNgVA43rJ+fZvYv382a2P3ZBY4dPiwXTt0qFUsW9Yeve22ZD6VXG37mIULbezEiYTjudoLHBwBBBBAAAEEEEAAAQQQQAABBBBAIIgChONB7BXahAACCCCAQB4SCIXjjWrWtBlDh+ahM+dUvQKbt261Jj16OF9eNXmyHXvUUSBlQWDMu+/a2PHjCcezYMcuCCCAAAIIIIAAAggggAACCCCAAAKpLUA4ntr9y9khgAACCCAQeAHC8ex10esffWTvLl9uY++4w/IfcUT2KsvlvT9evdquHz7cacWS556zsscem2MtOnz4sP196JAVyJ8/x47p14HGvP++jR03jnDcL2DqRQABBBBAAAEEEEAAAQQQQAABBBBIWgHC8aTtOhqOAAIIIIBAaggQjmevH7uNGmULly51Rt1r9H0yl2kLF9rAiROdU1g4erSdUrGir6ezbdcumzh3rukBgy3btjnHqlahgt1y2WXW7vzzfT22n5V3Gz/eFr77LuG4n8jUjQACCCCAAAIIIIAAAggggAACCCCQlAKE40nZbTQaAQQQQACB1BEgHM9eX970yCP2zrJlNqFfP7uoYcPsVZbLew+fMsWenzvXacVbjz1mNSpV8q1F32zebB1HjAiH4t4D9b/uOut51VW+Hd/Pits/9pgt/uwzwnE/kakbAQQQQAABBBBAAAEEEEAAAQQQQCApBQjHk7LbaDQCCCCAAAKpI0A4nvW+PHT4sF07dKgtXrvWzjr1VDuxTBn74ddfbd+BA3Z08eL25J135ujU5Fk/k//bs9ODD9qilSudf382YYIdX7JkdqtMd/+2gwfbkvXrne+P6NbNmpx2mh388097+F//Crfh8xdesGOKF/etDX5V3H70aFu8eDHhuF/A1IsAAggggAACCCCAAAIIIIAAAgggkLQChONJ23U0HAEEEEAAgdQQIByPrx9Xfv21PfvGG/brzp2mf6dXSh19tE0dMMBOq1IlvInW1A7yuuSX33efff7tt057//fyy76u/x0Kx2+65BIb3Llz2Gj/wYNW44YbnP/7PXo9vp6PcesCBaz9I48QjsfIxWYI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CEAAAhCAQEUR+PbbbyNRx9mOO+5oU6ZMSdTGG2+80c4555xM2fHjx1uvXr2i33OJ44sXL7Z58+aZwlWus846tuyyyyY6X1yh//73vzZ37lxbfvnlbc0117RmzZoVXZc78KuvvrLPPvvMVlttNVt99dVLri/NCpYsWWILFy40hZH97rvvopDoLVu2tJVWWilR38spji9atMi+/PJLW3HFFa1Vq1apdNv1U3nRS63z559/jsKj/vTTT7bqqqvaCiusUFIbP/nkE/vhhx+sbdu2tswyyxRdl/r48ccf269+9avonzPE1uRI0xqL5GesWzLXeBV7T9H9TbbUUktF/6rBKmEsxKka2enZ+Omnn9pyyy0XPX+WXnrpkoa8nGPxzTffRPet1q1b2yqrrFJUO/UM0/1PYrLux6pn5ZVXLrnfRTUmOEjzR+1S+/SznrP6p+dbPiu3OC7v+A8//DB6XhTLPuxDsfeobCwkIOufnrX6V8ozspzzON9Yxn2fljiu613vrLrO03h3LaYvhR5TzrFI456COF7oiFIeAhCAAAQgAAEIQAACEIAABCAAAQg0YQK+qLPJJpvYX//61wyNQYMG2cyZMzO/6zsJsbKLL744k2Ncv9999922xRZbRN/FieO//e1v7aKLLqqTe1PnPOWUU2z33XdPNArK93jFFVfYCy+8YC+//HLmGLVLdSmk6mGHHZZTTDrqqKPsn//8Z3SsBP2zzz7bJPZfdtll0YKuM9W51157Rd+ntQidqJO/FNIi++OPPx4x+8c//lGrbWE9EvKVQ75du3bRV2+//bYdccQRtYq9//77tX5fd911czZnxowZOYWKjz76KGKmsfjXv/6VqWuttdayLl26RPnrt9tuu5zniBuLO+64I6rXb6/aqvClmivaVBFnGruamhqTUC9TmP9dd93VrrrqKps4caJpM4gz5YHt2bOnDRw4MJHgIYFEdYiJ5p1fl+ad+jlgwIBI3MlnEpXGjh1rzz33XK0+agx1zSnVQTWL44oi8cgjj0QYJIzcc889GSTqmza0yDp06BAxdXbsscfaG2+8kfn9oYceMm2MCC3Nscg3Vkm/jxuvUu8pxx13XJ37peaIzqWw0fq3/fbb5xRWdC8eN25cphu6P+ZLnfH3v//dzjzzzMwx6623nt18882xKCpxLNTQNNglHftCyoVsb7nllkg4HDNmTHT/9q1Hjx5RbuM//vGPicSzUsfiscces+HDh8d25+GHH47uk7qudX3792bNSd3rTzjhhKwiqITAZ5991hRxRvdQ3QOzmZ67559/fvQsD033B90n8pk26+m+nNQkNut+o2etzuHf38M6NtpoI3v00UczH+sZPWzYsFrFCnnW6rl26aWX5myq2nTNNdfYq6++GoWQdyb2m222mR1zzDFFPWtLvUe5dujZq/bp2fj666/X4bftttva/vvvb/vtt1/eDZGlzuOkY15subTE8fCZofbo3Unv0bq3650i1xzW+9ehhx6a2Qj061//Onr3zie069q46aabMt3Xe+KRRx5Zlvt7Oe8pcQ1GHC92VnMcBCAAAQhAAAIQgAAEIAABCEAAAhBoggT22GOPjCClRek5c+ZkKGhx3l/EljjuFutOP/30KMenMwl98t6WheK4REOVzbUgrgW6bAvz7hxPP/20KS+6L2DHDZkWYiXer7322rEj6i9KSrTfeeed6yxu+wdqAVp9kphXXyYRRQufhdgrr7yS8bDWYrrGthTTInw270WJJRrXXCKCzn388cdHgnY2z7FwLCSIqHw20xy94YYbbOutt65T5N///nc0ls4kjuuz22+/PWt9EsknTJhgWljOZhI6TjrpJHvxxRdz4tTCtjZudO/ePWs51aENAbnm8LnnnhuJQz7b9957r5ShrNdjxUqbZZxpDDT+Ej3at29fqy3vvvtuZjFfeVwdl/Be5A5KcyzShBIKHSNGjCj5niIhKd+c69q1a3TfdBuTwj7pvqx0Gc7EVfeJXJ6c4b1d6TN0LYVWqWOhdqbBLs354eoKn40SqSQ2+5uLwvNKVNT9IFe0izTGYvLkyVnn7BNPPBG9J2jTQTbTvVf35tB0jasP+Z7b/nGTJk2yXXbZpU5dEtb79euXd2juuusu07WRxK688srofSGphZsIwyg6Setx5fS+ctttt8Uepognqj8U3+MKH3744dF1ms27vRzvPYpmc+edd0YbCPO9C6jNm2++ebRZLdvGvDTmcaH8Cy2fhjiuqAlJNm/ouhk8eHDm3dpvq6LgaIOf/16tjYXabJHL/Hd+lZs2bVrs+1QaY1Gue0q2/iGOFzqbKQ8BCEAAAhCAAAQgAAEIQAACEIAABJowAQl1vheUPI4V6lzet6EY7IdO1wK1FqqdOQFMv4cCwAYbbFDL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AIEILLVl1ettFo4vjvfAVDi+555lXWaOKwQIECBAgAABAgQIECBAgAABAgQILBkB4fiS6WoNJUCAAAECCy8gHN9j4TthiddgKhzfa6+y7uMfX+Iamk+AAAECBAgQIECAAAECBAgQIEBgaQkIx5dWf2stAQIECBBYUAHhuHB8QW/AUspUOH63u5V1H/vYQlfH9QkQGCOBbJWxbNmysnz58jGqtaoSIECAAAECBAgQIECAAAECdQHhuPuBAAECBAgQGJmAcFw4PrKbrceFpsLxu9+9rDv77IWujusTIDAmAps3by6rVq2qanvppZdWIblCgAABAgQIECBAgAABAgQIjJ+AcHz8+kyNCRAgQIDA2AoIx4XjC33zdsLxZdtvXy69/PKFro7rEyAwJgIbN24sBxxwQFXbdevWlZUrV45JzVWTAAECBAgQIECAAAECBAgQqAsIx90PBAgQIECAwMgEHvCAB5Srr766nHjiiWXNmjUju+5CXqgzIGDdcceVlXsIxxeyL3Ltte9+dzn9E5+oqnHVVVctdHVcnwCBMREQjo9JR6kmAQIECBAgQIAAAQIECBAYICAcd4sQIECAAAECIxPIsrT5ZynNuBOOj+z2GupCJ515Zjl5/frqWOH4UGQOIkCglCIcdxsQIECAAAECBAgQIECAAIHJEBCOT0Y/agUBAgQIECCwSAWE44urY4Tji6s/1IbAuAgIx8elp9STAAECBAgQIECAAAECBAj0FxCOu0MIECBAgAABAvMo0AnHLzjllLJ8553n8UpOPYzAaeecU44/44zqUDPHhxFzDAECERCOuw8IECBAgAABAgQIECBAgMBkCAjHJ6MftYIAAQIECBBYhAJbtmwpe+211x+D2D8t5b0Iq7mkqrTx8svLAccd98c+sef4kup7jSUwFwHh+Fz0vJYAAQIECBAgQIAAAQIECCweAeH44ukLNSFAgAABAgQmTKATpuyy887lwlNOmbDWjWdzNl15ZVn94hdXlReOj2cfqjWBhRAQji+EumsSIECAAAECBAgQIECAAIH2BYTj7Zs6IwECBAgQIECgEqjPHL/0jDPKsu22I7MIBHZbs6aqhXB8EXSGKhAYEwHh+Jh0lGoSIECAAAECBAgQIECAAIEBAsJxtwgBAgQIECBAYB4F9nvEI8oV3/lOOfXFLy773u9+83glpx5WQDg+rJTjCBDoCGzevLnst99+1f9edtllYAgQIECAAAECBAgQIECAAIExFRCOj2nHqTYBAgQIEBhHgYQLy5Ytq/5ZKuWIgw8uGz71qXLgox5Vjv2nf1oqzV7U7eyE4+vWrSsrV65c1HVVOQIECBAgQIAAAQIECBAgQIAAAQIE2hMQjrdn6UwECBAgQIBAH4FNmzaV1atXV0cspeWs159wQjnyrW8ty3feuVxg3/EFf49suf76stcznlHVQzi+4N2hAgQIECBAgAABAgQIECBAgAABAgRGKiAcHym3ixEgQIAAgaUrcN5555VDDz10yYWSWz7zmakwNuF4QnJl4QQ2XnVVOeDII6sKXHDBBWX58uULVxlXJkCAAAECBAgQIECAAAECBAgQIEBgpALC8ZFyuxgBAgQIEFi6AieddFI5+eSTK4ClNHO8XHxxOeQNbyjnf+lLllZfBLf/Ee95T9nw8Y8vvftwEdirAgECBAgQIECAAAECBAgQIECAAIGFFhCOL3QPuD4BAgQIEFgiAks5HF//2c+WI9/2tqqnzz3hhLJi992XSK8vrmZu/s1vyqqnPrWq1C677FIuvPDCxVVBtSFAgAABAgQIECBAgAABAgQIECBAYF4FhOPzyuvkBAgQIECAQEdgyYbj3/hGKVu2lD2f/vTyqxtuqILxBOTKiAVufOOy+uijy6b0RynlxBNPLGvWrBlxJVyOAAECBAgQIECAAAECBAgQIECAAIGFFBCOL6S+axMgQIAAgSUksGTD8WuuKeWHPywnnXlmOXn9+qrHj3nGM8pBj370Eur9hW3qluuvL8d/5CNl/dlnVxXZYYcdymWXXbawlXJ1AgQIECBAgAABAgQIECBAgAABAgRGLiAcHzm5CxIgQIAAgaUpcMQRR5QNGzYsvWDy+utLufzyqtP3e/GLyxVXXlmWbbddWXfccZZXH8FbYfPPflYOOemksumb36yulmD8zDPPLCtWrBjB1V2CAIFJEti8eXPZtGlT2WeffSapWdpCgAABAgQIECBAgAABAgSWlIBwfEl1t8YSIECAAIGFEzjggAPKxo0by8qVK8u6desWriILceWrrirlJz8pm6+9tux35JHV8uopx/7TP5UDH/WohajRxF8z1iefdVZZf95509p66qmnln333Xfi26+BBAi0L7DXXnuVLVu22JahfVpnJECAAAECBAgQIECAAAECIxMQjo+M2oUIECBAgMDSFljS4Xi6/k8BeZb43v+446oZ5CnLd965rHnYw8rKPfYoK0c8m3nTlVeWLX8K6hfq7myzzQnEz//Sl8p5X/5y2fj1r09rUgZlHHvssWaML1RHuy6BCRDYbbfdqlYcdthh5fDDD5+AFmkCAQIECBAgQIAAAQIECBBYegLC8aXX51pMgAABAgQWRGDJh+NRv+66Un70o1L++7/L2ne/u5z+iU9s1Rcrdt+9WnZ9j913LwnSf3jttQP7a/NPf1rNSp/ksvJe9yrlD38o5X//t5Tf/77671133rlqcow2/mnp+rpBllDPLPE1a9ZUKxYoBAgQmIuAcHwuel5LgAABAgQIECBAgAABAgQWh4BwfHH0g1oQIECAAIGJFxCO17o4s7W3bCkbv/jFctyb31yu+O53J77/R9HAhOF7ZAb+ypVl7733FoiPAt01CCwhAeH4EupsTSVAgAABAgQIECBAgACBiRUQjk9s12oYAQIECBBYXALC8d79bM3FFQAAIABJREFUsXnz5rJ+/frqgOxnu2zZsmkHr1ixotzylrdcXB06y9pcfvnlVRvbKrFKIB6jpltb13AeAgQIREA47j4gQIAAAQIECBAgQIAAAQLjLyAcH/8+1AICBAgQIDAWAsLxsegmlSRAgACBHgLCcbcGAQIECBAgQIAAAQIECBAYfwHh+Pj3oRYQIECAAIGxEFi7dm05/fTTq9m9l1566VjUWSUJECBAgEBHQDjuXiBAgAABAgQIECBAgAABAuMvIBwf/z7UAgIECBAgMBYCJ510Ujn55JOrul511VVjUWeVJECAAAECEdi0aVNZvXp1hXHqqaeWfffdFwwBAgQIECBAgAABAgQIECAwhgLC8THsNFUmQIAAAQLjKJA9tY888siq6ueee261R7RCgAABAgTGQWDjxo0l24OkrFu3rqxcuXIcqq2OBAgQIECAAAECBAgQIECAQENAOO6WIECAAAECBEYiIFgYCbOLECBAgMA8CNQ/wwzwmgdgpyRAgAABAgQIECBAgAABAiMSEI6PCNplCBAgQIDAUhfYvHlzWbVqVcVg1t1Svxu0nwABAuMlkGXV999//7Js2bJy4YUXjlfl1ZYAAQIECBAgQIAAAQIECBCYEhCOuxkIECBAgACBkQlkafWE5AcddFAVMCgECBAgQIAAAQIECBAgQIAAAQIECBAgQGBUAsLxUUm7DgECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgsmIBwfMHoXZgAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIERiUgHB+VtOsQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwIIJCMcXjN6FCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQGBUAsLxUUm7DgECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgsmIBwfMHoXZgAAQIECBAgQIAAAQIEFrPAli1byrJlyxZzFdWNAAECBAgQIECAAAECBAgQmIGAcHwGWA4lQIAAAQIE2hNI4HD88ceX+9///mXNmjXtndiZCBAgQIBACwJHHnlkWb9+fTnmmGPKQQcd1MIZnYIAAQIECBAgQIAAAQIECBBYaAHh+EL3gOsTIECAAIElKnDaaadV4XjKiSeeKCBfoveBZhMgQGAxCtQ/ow488MBy7LHHLsZqqhMBAgQIECBAgAABAgQIECAwQwHh+AzBHE6AAAECBAi0I7B58+ay3377lV/96lfVCc8999yyYsWKdk7uLAQIECBAYJYCGzduLAcccED16l122aV88pOftLT6LC29jAABAgQIECBAgAABAgQILDYB4fhi6xH1IUCAAAECS0hg06ZNZf/9968C8uzpum7dOgH5Eup/TSVAgMBiE0gwfsghh5Rs/bHDDjuUM8880+fSYusk9SFAgAABAgQIECBAgAABAnMQEI7PAc9LCRAgQIAAgbkLZD/X7OvaKVm6NkvYKgQIECBAYJQCp59+elm7du3UJU899dSy7777jrIKrkWAAAECBAgQIECAAAECBAjMs4BwfJ6BnZ4AAQIECBAYLNAMyBNGZB/yzCZXCBAgQIDAfAtkkFY+i1IyYzwDtdasWTPfl3V+AgQIECBAgAABAgQIECBAYMQCwvERg7scAQIECBAg0F0gS6wffPDB5eqrr64O2Geffco73/lOXAQIECBAYF4FTjvttHL88cdX18ge4+9617sspT6v4k5OgAABAgQIECBAgAABAgQWTkA4vnD2rkyAAAECBAg0BLLH6xFHHFHOP/984bi7gwABAgRGIrB58+ay3377lT322KMalGXVkpGwuwgBAgQIECBAgAABAgQIEFgQAeH4grC7KAECBAgQINBPILPIly9fLqBwmxAgQIAAAQIECBAgQIAAAQIECBAgQIBAawLC8dYonYgAAQIECBAYlUBmmB9wwAFVeH7LW96yWv42YXr+Scn/m/k3qt5wHQIECCy8QD4XMrAqpfPfmRH+wx/+sGzcuLEcc8wx5aCDDlr4iqoBAQIECBAgQIAAAQIECBAgsKACwvEF5XdxAgQIECBAYDYCCToSjg9T9t1333Lqqaf2PHT9+vXVXrMJU3qVhO4JVnKuXuW8884rp59++sAqZS/1fgFNwpzU55e//GXfcx1++OFl5cqVPY+JUeoz6DwHHnhg33alPieffHIVMPUrT3jCE8qaNWt6HpLQKvXpd54MdEh9+rVrmPrkPI9//OP7tiv9nXZdfvnlPes8zHmGbdeg+gzTrlR0kPOw5xlVvw9bn7beF4vtPLmX817tVeKTrSQGlWHOc8ghh0yFw93OlwFD6fd+9cn9nPOkXv1KznPsscf2fb8Pc548M0488cQ5n2eQX34ew3Xr1g1zqGMIECBAgAABAgQIECBAgACBCRYQjk9w52oaAQIECBCYVIEEmwm1szd5wtarr766Z1N32GGHctlll/X8eYKpDRs2DKRK6Ja9aHuVYc+zyy67lAsvvLDneU466aQqtB1UBgU9w9Zn0HnifOSRRw6qThnUrrVr1w41eGBQfYb1SRB46aWX9qz3aaedVg1CGFTuete7lk9+8pNz7q9B5xm2PqnIVVddNe/1GdZ5UL8Pe/+01a5B9WnLedh2DXr+DFufcTtPWz7DnqfXGyL3w6677lqtJpJBSZ3VRQa97/2cAAECBAgQIECAAAECBAgQmFwB4fjk9q2WESBAgACBJSWQGY+d2d+ZDdz577333nvgTOQEMINKZo0nYOlVcv0EXf1mXCawzczffjPQ8/oEk4Nmbg4zczz16TcjPm0ZZgZxgu1B5xk0Yzcz2dOuQWXQzOg4pz6DyjAzbYeZsTuoXcPWZ9B50t/DOA/TrmHO01Z9hjnPMPdzW+cZdP8M+/4adJ68H9KuzjLeve7HNs4zzHMj9cn7fVDJM6zf86cz8GjQ+33Qc7XN82RVjkHPw7Q7TnvssccUQb8VKAY5+TkBAgQIECBAgAABAgQIECAwuQLC8cntWy0jQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgT8JCMfdCgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAw8QLC8YnvYg0kQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAeG4e4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEJl5AOD7xXayBBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQICAcdw8QIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwMQLCMcnvos1kAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgSE4+4BAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEJh4AeH4xHexBhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQICAcNw9QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQITLyAcn/gu1kACBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQEI67BwgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIEBg4gWE4xPfxRpIgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAsJx9wABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQITLyAcHziu1gDCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQEA47h4gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYkXEI5PfBdrIAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgIx90DBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIDDxAsLxie9iDSRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAAB4bh7gAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQmXkA4PvFdrIEECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgIBx3DxAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAxAsIxye+izWQAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBITj7gECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQmHgB4fjEd7EGEiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgIBw3D1AgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAhMvIByf+C7WQAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAQjrsHCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQGDiBYTjE9/FGkiAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECwnH3AAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhMvIBwfOK7WAMJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAQDjuHiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBiRcQjk98F2sgAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAjH3QMECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMPECwvGJ72INJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAHhuHuAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBCZeQDg+8V2sgQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAgHHcPECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgMDECwjHJ76LNZAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEhOPuAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCYeAHh+MR3sQYSIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAgHDcPUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECEy8gHJ/4LtZAAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBCOuwcIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAYOIFhOMT38UaSIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLCcfcAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECEy8gHB84rtYAwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIEBAOO4eIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGJFxCOT3wXayABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQICMfdAwQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAw8QLC8YnvYg0kQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAeG4e4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEJl5AOD7xXayBBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQICAcdw8QIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwMQLCMcnvos1kAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgSE4+4BAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEJh4AeH4xHexBhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQICAcNw9QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQITLyAcn/gu1kACBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQEI67BwgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIEBg4gWE4xPfxRpIgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAsJx9wABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQITLyAcHziu1gDCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQEA47h4gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYkXEI5PfBdrIAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgIx90DBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIDDxAsLxie9iDSRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAAB4bh7gAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQmXkA4PvFdrIEECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgIBx3DxAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAxAsIxye+izWQAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBITj7gECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQmHgB4fjEd7EGEiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgIBw3D1AgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAhMvIByf+C7WQAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAQjrsHCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQGDiBYTjE9/FGkiAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECwnH3AAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhMvIBwfOK7WAMJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAQDjuHiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBiRcQjk98F2sgAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAjH3QMECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMPECwvGJ72INJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAHhuHuAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBCZeQDg+8V2sgQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECAgHHcPECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgMDECwjHJ76LNZAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEhOPuAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCYeAHh+MR3sQYSIECAAIGlKfDLX/6y3HDDDVXjt9lmm/KXf/mXEwtx3XXXld/97ndT7bvNbW5TbnSjG01se3s1LAaf/vSny5YtW8qjHvWosv322y85Aw0mQIAAAQIECBAgQIAAAQIECBAgQKC3gHDc3UGAAAECBAiMjcDPfvazsu2225bttttuYJ1XrlxZrrnmmqnjrrrqqoGvGdcD/v7v/7587Wtfm6r+17/+9XLLW95yXJszq3onGH/Qgx40rc8/97nPld13331W5/MiAgQIECBAgAABAgQIECBAgAABAgQmT0A4Pnl9qkUECBAgQGAiBBJ2fvzjHy9XXHFF2bRpU7nkkkvK9ddfX7Xtdre7Xbnb3e5WVqxYUR7ykIeUPffcc6s252ed4/ND4fhE3BY9G3HOOeeU5zznOdN+nv8/6qijFqThv//978vVV189de0MVli2bNmC1MVFCRAgQIAAAQIECBAgQIAAAQIECBD4o4Bw3J1AgAABAgQILDqB733ve+X5z39+ueyyy4aq21Of+tSydu3acpOb3GTqeOH40po5/oEPfKAcffTR0+6XJz3pSeV1r3vdUPdQ2wdlafe99tpr6rT3uc99yoc//OG2L+N8BAgQIECAAAECBAgQIECAAAECBAjMQEA4PgMshxIgQIAAAQLzK/CHP/yhrFu3blazfZ/+9KeX448/fqqCwvGlFY5nj/msJlAvH/3oR8s973nP+b1pe5y9GY5ndYPMblcIECBAgAABAgQIECBAgAABAgQIEFg4AeH4wtm7MgECBAgQINAQOPvss6sZ482SYDEzb/fYY4/yi1/8olx00UXl//7f/zvtsGc961nlZS972dSfCceXVjiejs+KAxlc8V//9V/lH/7hH8q9733vBXuPCccXjN6FCRAgQIAAAQIECBAgQIAAAQIECPQUEI67OQgQIECAAIFFIfCb3/ym2j/8mmuumVafE088saxZs2arOn71q18tT37yk6t9xQ855JDy8pe/vGyzzTZTxwnHl144vihu5D9VQji+mHpDXQgQIECAAAECBAgQIECAAAECBAj8UUA47k4gQIAAAQIEFoXA6aefXu0b3inbbbddtUfzXe961571+853vlM++9nPloMPPnirY/qF45l9fu2115Zb3epW5S/+4i/m1P4s551z7bLLLuUWt7jFnM6VF//+978vP/7xj6vZzzvttFNZtmzZtNC/2wX+/u//vnzta1+b+tHXv/71cstbzk84fsMNN5Rf/epX1bVuc5vbzKm9CZBzvh133LFsu+22czrXfLz4d7/7XfnJT35Sfvvb35Zdd9213PSmNx36MsLxoakcSIAAAQIECBAgQIAAAQIECBAgQGBkAsLxkVG7EAECBAgQINBLIGHr/e9//2oWeKe89KUvLc9+9rNnjdYtHH/ve99bTj755PKzn/1s6rwJ4R/1qEeVV7ziFVVIO0z55Cc/WdavX1++8pWvTDvX7W53u2qP6xe96EVl9913H+ZU1TFXXHFFOe2006p/X3bZZVu97rGPfWx5whOeUB74wAd2PedMwvEzzzyzvOUtb5k6z/7777/VUvZvfetbq+XJUzJ4IHt3f/nLXy6ve93rysUX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AoTjicrNwxBAAAEEEEh6ApZwfOXYseLl+d9ZsM4uoVuVdwsMlF1HjsTa1WweHjKgZUsTfkctGqBfvXFDMmfIEK8h/n3lity7d0+ye3jE6z4qJx0B/cFG6PHjZsCsHE86885I7RM4cTlM+q//RPZdOGhzQ7qUaeXXd5ZK8mTJ7WuIWggggAACCCCAAAIIIIAAAggg4NYChONuPb0MDgEEEEAAgScvoGeO6/bqg1q3lnavv/7kOxSPHkRGRsqmfftk8/79EnbunGRIl0506/NKJUpI8YIFrVulx6NJqiLwSAKXr16VMq1bm3tLliwpq1ateqR2uAkBdxaIlEiZsfsbmy3WA18eIjUKVHXnYTM2BBBAAAEEEEAAAQQQQAABBBCIhwDheDywqIoAAggggAAC8RcICAiQwMBAm7Ov498KdyCQtAVWb94sHceONQht27aVwYMHJ20QRo/AAwTOXwuXBaFL5NXCNaVY1sJYIYAAAggggAACCCCAAAIIIIAAAlYBwnFeBgQQQAABBBBwqICuGtfV45nSp5c9s2Y59Fk0joC7CvSaOFEWBweb4X366afSuHFjdx0q40IAAQQQQAABBBBAAAEEEEAAAQQQQMBhAoTjDqOlYQQQQAABBBCwCLjquePMIALOIuDTpYuEnf8/e28CJ8ddnvk/XVV9z63RLfmU75PLlmQT2yFgjiRcEgnkgkDyd0KyCwGS7Gc3F9mTkCW7sJBNPsnGJgnBZgMkCwYCtjEYjLE5YluyjQ3WLY1Go5npu7uq6/953l9VqzWW5bEsWZrRU9B0T3cdv9+3alpC33red78N5/bbb8fFF198qgxN4xABERABERABERABERABERABERABERABERCBBUNAcnzBnCoNVAREQAREQAQWLoGF3Hd84VLXyBcLgS1PPolXve99Np3BwUE89NBDi2VqmocIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIPK8EJMefV9w6mAiIgAiIgAicngR6fcevvhp/+d73np4QNGsROEYCH7r1VvzZbbfZ1uvXr8cnP/nJY9yTNhMBERABERABERABERABERABERABERABERCB05uA5Pjpff41exEQAREQARF4Xghs2bIFr3rVq+xY93z0o1izdOnzclwdRAQWOoHZWg3XvPOd4DOX3//938fb3/72hT4tjV8EREAEREAEREAEREAEREAEREAEREAEREAETgoByfGTgl0HFQEREAEREIHTj8ArX/lKbN26FZte9jL86U03nX4ANGMROAYC/alxllT/xje+gaGhoWPYkzYRAREQAREQAREQAREQAREQAREQAREQAREQARGQHNc1IAIiIAIiIAIi8LwQuO222/DepKS60uPPC3IdZIETmJsa/+Vf/mX8wR/8wQKflYYvAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiePgOT4yWOvI4uACIiACIjAaUdg48aN2LVrF971lrfg3a9//Wk3f01YBJ4Ngdvuugvv/V//q7fJPffcgzVr1jybXWhdERABERABERABERABERABERABERABERABERCBPgKS47ocREAEREAEREAEnjcCaXp8aHAQ93z4wxgql5+3Y+tAIrCQCDA1/qr3vQ879++3YW/atAl/+qd/upCmoLGKgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIwClHQHL8lDslGpAIiIAIiIAILF4Cs7OzYHq8UqngxmuvxV/823+7eCermYnAcyDwq3/yJ/jifff19vDJT34S69evfw571KYiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAKS47oGREAEREAEREAEnlcC/b3H3/XmN+Pdb3jD83p8HUwETnUCH7r1VvzZbbf1hqnU+Kl+xo59fHEcH/vGz7BlJpM5YfvWjkVABERABERABERABERABERABERABERgoRKQHF+oZ07jFgEREAEREIEFTOA973kPPvWpT9kM/uJ978ONV121gGejoYvA8SPAtDhT4+ly0UUX4Qtf+MLxO4D2dNIIHE2EnyiR/XTHPFHHO2lwdWAREAEREAEREAEREAEREAEREAEREAERmCcByfF5gtJqIiACIiACIiACx5fAK1/5SmzduhXsP/7J3/99XHzWWcf3ANqbCCwwAluefBI/84d/CPYb5zI4OGhifM2aNQtsJhruXAJHktQuNB4jiiI0mg3Ua3U0m01Uq1U0Gg10Oh1EYdjbVYwYth/3XzAYHvgBfN9HLpdDoVhEqVRCMX0uFOAHgW1M9fpmAAAgAElEQVTjQuSHJ8klyHWdioAIiIAIiIAIiIAIiIAIiIAIiIAInI4EJMdPx7OuOYuACIiACIjAKUBg586doCBn/3EK8g/edJMS5KfAedEQTg4BJsbf+9GP9sQ4R6E+4yfnXDyXozoRnUns9eF74metVgszszOYOjCFyf37MXXgAA4kj5npg6hUa6jV6+i0WojiGHG3axac/+FOu8l7YRQBybF4vCAIUCgUUCyV7Pt06fg4xpcuxZIlS7Bs2TIsW74c40vHUSqV5w7KWXYtIiACIiACIiACIiACIiACIiACIiACInCaEJAcP01OtKYpAiIgAiIgAqcigXvvvRfveMc7TJBz2Xz99fj9t74VQ+U5AudUHLzGJALHiQD7i7PPeP/yrne9C+9+97uP0xG0m+eDwJHS4bV6DQf2T2Lfvr3Yu28Ce3bvxv6JCUxOTuLgwYOYmZ5GtVZDq9lEJwwtEe5lPPjZAIHnIeP58LyME+4Aut2uJc35SF8zYd7lz5Yqjy1JXiwULEVOUT4+Po7lK1Zgzdq1OOPMM3HG2rVYtXo1hoaH4XneYWiUJn8+rhQdQwREQAREQAREQAREQAREQAREQARE4GQSkBw/mfR1bBEQAREQAREQAWzZsgW/9Vu/ZSXWuVx87rn405tuUpl1XRuLngDLpzMtztR4//LBD34QmzdvXvTzX2wTTOU40+Hbtm3DI1u24Ec/+hFYJWNiYgJTBw/ajUBhp2Np7Ww2a2XPA9+3R8b3TWxbTjwR4hmz5SSVsfLoTJJTglOMc+GzvUdpztf8LBXnYYgwDE2k8/1CPo/R0VGsXr0a55xzDi655BKcf9GFWLNmLfL5vO0vleO9BPxiO0majwiIgAiIgAiIgAiIgAiIgAiIgAiIwGlPQHL8tL8EBEAEREAEREAETj6B2dlZ/NEf/RE+9alP9Qaz/pJL8O7Nm8FnLSKwmAhQin/qq1/FX33uc9g5MdGbGnuM33rrrbj44osX03QX5VyOlBLnRGu1Ku6//wF8+UtfwkMPPmgJ8Xang2wu5x6U4cnDRDgT4UyIJx3BPd+3kuzduGuf8WPKchPjmYwrs84C665huXt/zmuuQ1FOSZ6mzNm7nMl0PvNBMX/mGWfgsssvxwte+AJcdPEllizn2PoXJckX5eWrSYmACIiACIiACIiACIiACIiACIjAaU1Acvy0Pv2avAiIgAiIgAicWgT+6q/+Cu9///sPG9TFZ52Ft7/mNdh0/fWn1mA1GhF4lgQoxf/68583Kc7X/ctFF11kYnxoaOhZ7lWrnywCabqaqe1O2EHcjXH//ffjf3/sY3jg/vsxODIC3vDAVDalM0Vzmvz2KL49z8qa978+moxOk+M05nPl/GGCvC9J3o0oyiPwuduNTJqngrzTbiOOIutHvn79evzYDTfg/PPPt5/TcuuS4yfr6tJxRUAEREAEREAEREAEREAEREAEREAEThQByfETRVb7FQEREAEREAEROCYCLEH8oQ99CF/84hd7vci5ozVLl2LDJZdYkpwP/qxFBBYCgXu3bMGX7rsPt91111OkOOXp29/+dvUXXwgnMhljKsX5PHXgAB597DFs374d1WoV93z96/jOAw+gWCphdGzMlU73fRPgXJj3tu1ZLZ3vMzVuCfFDjyOiYLl028GhxLjJ9r7keLqdHcPKrbvy6ybHe4I8KcFuqfIQnU6IbhjafpcvX46NGzfi+h+/ARdcdHHvRo30OBLlC+gi1VBFQAREQAREQAREQAREQAREQAREQASeloDkuC4OERABERABERCBU5IAS60zSc4H+/TOXVJZ/oqrrjJRzoS5FhE4FQhsefJJfOnb38Y3t2zBvQ89dMQhse8zpTh7iystfiqctWceQ39au9lsYPu27SbDb7/9djy8ZQtmZmYslT00PIyVq1bZeWVi3OS45x0qf951JdOPJMeP1Ov7kATnGGN0Y+tAbgPuH9Pc16kktx7lSd9xk+VpuXX2JI9YZj2yPugcOyX+xRddhNe89rVYv2EDli5deij1TqmfSP5npqU1REAEREAEREAEREAEREAEREAEREAERODUJCA5fmqeF41KBERABERABESgj8Btt92GL33pS/jmN795RFGerkpJvmbZMlxy1lkYKpdNmPP5VF0oUeeW1z4ZY/3mww+fjMOekGMOJ+f9ue48vZaebj88bzx/XNg3fMfkpPWOvvdf//Woh6YUf/e7321SXMvCINCfFOeIJyb24b57v4Uv3n477vv2t7F3YgLNRtNEM0ulj42OYlUqx7PZnlw2Qc5Edxw7MU5BzveYIve8Hoy5JdP5GaV2+syy6hkmyOeUVz+stHqSUud6aSl3fs79OEEeOklOMR5F6CRynO8xRT42MoIN11yDl7/iFbj0sstQKpVsfJLjC+Oa1ShFQAREQAREQAREQAREQAREQAREQASenoDkuK4OERABERABERCBBUVgy5YtJsnT5127di2o8WuwpxcByvANGzZYT2c+r1mz5vQCsMBn2y+qKZVnpqfxpS9+EX9/y8exY+cO1FstVKpV1Op1k86B72N4eBir16zByMiIlVVnetx6i1OAJ2XRKbZ9yvEgcOlvJrKZzE7WoYTm/o4kpFmSnWnwI33WL8hT9Gnp9X45nkpyPjM1HoYssd5xkpzynM+dDq655hr87M//HC677HKUy+Ve+fcFflo1fBEQAREQAREQAREQAREQAREQAREQgdOYgOT4aXzyNXUREAEREAERWAwE2KOcDy47duzove5//7B5Uiody2MxwDrKHNgXW8tTCbAn+CWXXPKMaCi9+8V3KsIlw58R3Sm7wmFiPO5ix7bt+OdPfxqf//ztODB1ANl8HvVGA7v37EGVrR8ovH0fA6UyVq5eZSXJc7mcCfJUjvf372Zy3GR4XxI8hZEmtOemyPthWQI9SZyn6z1dsjsV5JThaZI8LbHOZ5PiUYR2u22vWYadZdYp8M86+2z87JvfjGuuvRYjo6O9Pumn7InTwERABERABERABERABERABERABERABETgKAQkx3V5iIAIiIAIiIAIiMB8CDDFSak+n+enW2eulOdx+9872s/PZt00nTqffTOxerRHIu+Ous6J2AeP+3SP+ZwvrSMCz4FAfwKb8njXrp34h7/9O3zh9tvRbLVQGhyAH2Sxd+9esHpFvV63VLXveSbDly9fjhUrV6JYLDpBnqTHTY4n40pFNp/TR5oipzh3v/KH9/me+/NcGX6oP7nbrn/9VJBzv4fKq0e910yPM0XeTpLkfG3rhSGWL1uKN7/l5/Cyl78cS8bH7SYAlVh/DheYNhUBERABERABERABERABERABERABEThpBCTHTxp6HVgEREAEREAEREAEREAEROBUJJDKcaaptz35JG75q7/CPd/4BmrNJoaGh5HN5Uwk79q5Ezt27kSj1QJ1NqUx09yjSd9xVh7I5/MmzNPP+qV1v7xOZbP1Ie+T4nMldz+v9DO+d5hg71upP4Xe2xdg5dMtSR7HlhpP5bj1IE8S5NaXPIoQR5GVid+0eTNefuONYLsAlouXID8Vr16NSQREQAREQAREQAREQAREQAREQARE4GgEJMd1fYiACIiACIiACIiACIiACIhAQqC/RzeT4X/zl3+JL37pSwijEMOjY8jl8yaVKZBNjjM53mw6Oe55JoxLpRJWrFhhkpxynI+Agjz5PJXUvT7kSfWFNDHe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3VK2bFl57LHHJH369E43//zzz+WNN96wf0Y47tu5CbTWYmJipG7duqIvdjiWrVu3iu7gkBIlKipKLl26ZG4dFhYmBQsWTIlucE8EEEAAAQQQQAABBBBAAAEEEEAAAYsLEI5bfIIZHgIIIIBAcAk88cQTsm3btkR3Wq/Nli1boq/354WE477R1i3RZ8yYISNGjPCowfz588t7770nlStXttcnHA+tleP6zOjLEo7HGujDsG7duhQLpUeNGiVTp061P5OfffaZ1KxZ06NnmkoIIIAAAggggAACCCCAAAIIIIAAAgh4KkA47qkU9RBAAAEEEPCDAOE426p785gdO3ZMevbsKevXr/fmMnOe+LJly6Ro0aLmOsLx0ArHdc7Hjx8vEyZMsD83Dz30kMyePdur58iXlV3D8Y8++khq167ty1vQFgIIIIAAAggggAACCCCAAAIIIIAAAkI4zkOAAAIIIIBAAAm4huMNGjTwqncadmnwGQyFleNJm6Xo6GipX79+rK2xdf4ffPBBqVChguTNm9fsRPDjjz/KoUOH7DfMmTOnLFy4kHBcREL1zHE9k/6bb74xz0alSpWkSZMmkjVr1qQ9lEm4mnA8CXhcigACCCCAAAIIIIAAAggggAACCCDgsQDhuMdUVEQAAQQQQCD5BRzD8cKFC8uaNWuS/6YpdAfC8aTBz507V15//XWnRu677z559913JU+ePE6fa5Det29fmTdvnmgwvmDBAilevLi9DivHQ2/leNKePt9fTTjue1NaRAABBBBAAAEEEEAAAQQQQAABBBCILUA4zlOBAAIIIIBAAAn4Kxy/ePGiXL161ZxPHhER4VMBPc/4xIkTcvPmTXN+cerUqd22769wXIPhI0eOSLp06SRfvnwSFhaW6PHquNSuQIEC9hX6+/btkzp16tjbbNiwoUyfPj3R9/DkQp27WrVqydmzZ+3V27RpI4MHD47T+/bt2zJz5kypV6+eUzCuDcQXjus8njx5UnSlcaFChSRNmjSedNFtHV/Ohe0Gem62bi+vK+Zz5coladOmjbd/P/30kzz77LP2Osm5clztTp06ZfqUI0cOSZUqVaLt9Hd1/vx50X/qjgCBWKKiouT06dNmrPoShjeFcNwbLeoigAACCCCAAAIIIIAAAggggAACCCRWgHA8sXJchwACCCCAQDIIJGc4vnHjRvnwww9l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AQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQsL0A4bvkpZoAIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAoTjPAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApYXIBy3/BQzQAQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABwnGeAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABywsQjlt+ihkgAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggADhOM8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIDlBQjHLT/FDBABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAgHCcZwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAwPIChOOWn2IGiAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBAOM4zgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBgeQHCcctPMQNEAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEECAc5xlAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEELC8AOG45aeYASKAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIEI7zDCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIWF6AcNzyU8wAEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQIx3kGEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQsL0A4bvkpZoAIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAoTjPAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApYXIBy3/BQzQAQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABwnGeAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABywsQjlt+ihkgAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggADhOM8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIDlBQjHLT/FDBABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAgHCcZwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAwPIChOOWn2IGiAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBAOM4zgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBgeQHCcctPMQNEAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEECAc5xlAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEELC8AOG45aeYASKAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIEI7zDCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIWF6AcNzyU8wAEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQIx3kGEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQsL0A4bvkpZoAIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAoTjPAMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApYXIBy3/BQzQAQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABwnGeAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABywsQjlt+ihkgAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggADhOM8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIDlBQjHLT/FDBABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAgHCcZwABBBBAAAEEEEAAgQAQOH36tERHR5ueRERESPbs2QOgV9bqgvqqs62kS5dOcuTIYa1BMhoEEEAAAQQQQAABBBBAAAEEEEAAgTgFCMd5OBBAAAEEEEAAAQQQSGGBw4cPS61atey9aNiwoUyfPj2Fe2W92+/bt0/q1KmDs/WmlhEhgAACCCCAAAIIIIAAAggggAAC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ujds2JD+9Kc/pU022SR77njxM9X/8pe/pNe+9rUpZqbHv8d/KwQIECBAgAABAgQIECBAgAABAs0hIBxvjnHWSwIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECDS1gHC8qYdf5wkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQINAcAsLx5hhnvSRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEBTCwjHm3r4dZ4AAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLNISAcb45x1ksCBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAg0tYBwvKmHX+cJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECDQHALC8eYYZ70kQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAUwsIx5t6+HWeAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECzSEgHG+OcdZLAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQINLWAcLyph1/nCRAgQIAAAQLlBVavXp3iT0dlwIABaciQIWUrWr58edlthg8f3uk20Y6lS5em9evXd7jdwIED06hRo1K0q6PS0tKS1dNZif3Hjh3brXpuvvnm1qp32223NHHixE6NlixZkubPn99pe7beeut07rnnpuhfR6WSemLfKVOmpM6sFy5cmBYtWpSNe0djH+2I9owePbrD9syePTvrV7nxKlfPJZdckubMmdNpPTFeUc+4ceM6bE/UM2PGjLLjPnPmzLL9ivaUO3/KtSecoz2d+VTSr1Ln+H1s7/yfPn16p+dh/I5GXZ2VOA/jfO7s/Infrxj3zs7VOEb8fnW2Tenve0evNeVeN+JYhXO5I5uyL042IECAAAECBAgQIECAAAECBBpCQDjeEMOoEwQIECBAgEC9BAoBSyHojf//85//XPHhy4VTlYSJERBVI7yLRkc9nYWJ559/ftnQNuqZPHlyFrh2VKZOnZqFreVKhG5h1FEZM2ZMiuCtXIlQctKkSR1uNnLkyE4D/8KO5fo1YcKEVEnoX66eY445Jq1cubJct8o6V9qecs7VqmfEiBFpzZo1Pe7X6aefXvZmhjhIhK2zZs3q8HiV1lNuvCr1qVZ74maPefPmddivSttTrl+V+kQYvWDBgpo7V9qecs6V3BQRnSn3uhH1dHZTTdwIE2F//Ik2KQQIECBAgAABAgQIECBAgED+BITj+RsTLSJAgAABAgRyKDBt2rQUszx7WsqFU3kLSSsNp8qFrZWG4+XCqUrbE8FdZ7NJKwn9t9pqqyxo7WxmdIRlcUPDhg0b2pwapccud1NEudAtKo+bIqKecjPH4yaEcjO148aBzmb7xw0I0aYonR0vbqzo7Odx40DxLPr2fn+iX1FPZzP94yaUSn7/yrWnknry1p4wK9evGK9YNaBQ4v9Lz4EYp7iBpdx4xbh3dv7EMcqtPBBtid+Lzm74qOT3q5Lf02hPuXC80nrK3YRQ6c0e0aZyNx719L3E/gQIECBAgAABAgQIECBAgED3BITj3XOzFwECBAgQINBEAhE0xYzl9squu+6aYrZgJSVCqQg3OwsB6xkmRnsi+O2sPRGSVTJTu5JljcvNsC7MuKzE0jYECDSfQHuvR119rMOKFSvaDf+jnng97MnNJ1F34UaZcjfoNN/o6TEBAgQIECBAgAABAgQIEMiHgHA8H+OgFQQIECBAgEDOBWIWZAQzEZwMHTo0m3XbWaic8+5oHgECBAgQIECAAAECBAgQIECAAAECBJpOQDjedEOuwwQIECBAgECpQCy1HEG3sNu5QYAAAQK1FohHBMTjD8o9bqHW7VA/AQIECBAgQIAAAQIECBBoRgHheDOOuj4TIECAAAECrQIRUsTzxGMm+OLFi8kQIECAAIGaCkyYMCG6wcaSAAAgAElEQVQVHjPh2eQ1pVY5AQIECBAgQIAAAQIECBDYSEA47qQgQIAAAQIEmlYgQvEIx6NstdVW6e67725aCx0nQIAAgfoILFmyJJ1xxhmtBxOQ18fdUQgQIECAAAECBAgQIECAQAgIx50HBAgQIECAQNMJxLPDY+ZeS0tL1vddd901XXzxxdnscYUAAQIECNRaIN5/xo8fnzZs2JAd6txzz02TJk2q9WHVT4AAAQIECBAgQIAAAQIEml5AON70pwAAAgQIECDQXAKlwfjw4cPTvHnzPG+8uU4DvSVAgECvC5QG5PFoDzdp9fqwaAABAgQIECBAgAABAgQINLiAcLzBB1j3CBAgQIAAgbYCxUupjx07Ns2aNQsRAQIECBDoFYEIyMeMGZMde+DAgSkC8gEDBvRKWxyUAAECBAgQIECAAAECBAg0g4BwvBlGWR8JECBAgACBTOD8889P8+fPz/571KhR2YxxhQABAgQI9KbAJZdckmbMmJE1wU1bvTkSjk2AAAECBAgQIECAAAECzSAgHG+GUdZHAgQIECBAIMVy6sOGDcsk9tlnn3T55Zebnee8IECAAIFcCJx++ulp6dKlWVtWrVqVizZpBAECBAgQIECAAAECBAgQaEQB4Xgjjqo+ESBAgAABAu0KRPiwYsWKLBiP5WsVAgQIECCQB4G4gStWN4lVTUaPHp2HJmkDAQIECBAgQIAAAQIECBBoSAHheEMOq04RIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQLGAcNz5QIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQINLyAcb/gh1kECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQEI47BwgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECg4QWE4w0/xDpIgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAsJx5wABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQINLyAcLzhh1gHCRAgQIBA8wqsXr06LV++PI0aNSoNGDCgeSH0nAABAgT6lEC8dy1dujRNnDgxDRw4sE+1XWMJECBAgAABAgQIECBAgECeBYTjeR4dbSNAgAABAgR6JHD66ae3hgvTp0/vUV12JkCAAAEC9RIYM2ZMamlpycJx71/1UnccAgQIECBAgAABAgQIEGgGAeF4M4yyPhIgQIAAgSYVGDZsWFq/fn0aO3ZsmjVrVpMq6DYBAgQI9DWBCRMmZCufDB8+PC1YsKCvNV97CRAgQIAAAQIECBAgQIBAbgWE47kdGg0jQIAAAQIEeiowaNCgrIrJkyenKVOm9LQ6+xMgQIAAgboInH/++Wn+/PnZsVatWlWXYzoIAQIECBAgQIAAAQIECBBoBgHheDOMsj4SIECAAIEmFIjlaGNZ2ihz585No0ePbkIFXSZAgACBviiwcOHCNG3atKzpixcvTkOGDOmL3dBmAgQIECBAgAABAgQIECCQOwHheO6GRIMIECBAgACBagjEcrSxLG2UWJI2lqZVCBAgQIBAXxAofg9zg1dfGDFtJECAAAECBAgQIECAAIG+IiAc7ysjpZ0ECBAgQIBAlwSE413isjEBAgQI5EzAo0FyNiCaQ4AAAQIECBAgQIAAAQINISAcb4hh1AkCBAgQIECgVEA47pwg0BwC9zx1f3r2pefS79fekXX4rW/YI2212ZY16fyGl55Nf3zq/prU3V6lu265Y9ply51qerzoT/QrSvwddvHnwJ32TXu/YY+aHlvlnQsUwvGJEyem6dOn4yJAgAABAgQIECBAgAABAgSqICAcrwKiKggQIECAAIH8CSxZsiSdccYZWcM8rzV/46NFBLorEAHuT/50XbrlkTvSLWvvaA12u1uf/ToWiJD8g0NPTB8c8v6a3XDAv2OBeDRI3OgVjwWJx4MoBAgQIECAAAECBAgQIECAQM8FhOM9N1QDAQIECBAgkEOB2bNnpzlz5mQtW7VqVQ5bqEkECHRV4KLbv58uW3HFRoH4tltsk7buv1VXq+vW9s88ty498/z6bu3b2ztts8WAtE3/rVub8eJLf01r1z1Wtlkxg3zGYWebSV5WqrobCMer66k2AgQIECBAgAABAgQIECAQAsJx5wEBAgQIECDQkALC8eoOa8xeLC0tLS1p/fqNQ8L493Xr1nW7AatXr07xZ8iQIWnAgAHdrid2HDp0aLt1RL1Rf2kZOHBgij9KvgRitvhpi6elWEK9uIzZ76g0ZJe92gS++Wr5xq2JcP3p57v/+1Gosd+m/dLO2+xQle4++PjD6cUX/prWPPFIunX1ndky9aUlZpEvOOGitMuWO1blmCopLyAcL29kCwIECBAgQIAAAQIECBAg0FUB4XhXxWxPgAABAgQI9AmBCGjHjx+fhaOWo207ZBE8h0/8iXLzzTe3bhBhd+Hf+8RA16mRxUF9sVEsdxwlzrP471GjRtWpRc11mAlXndkmGO+36ebp5BEnpjdtv3tzQdShtzu+bvu09Qtbpp/ctyRd9aelbYLymEEeAblSHwHheH2cHYUAAQIECBAgQIAAAQIEmktAON5c4623BAgQIECAQJMKROA9f/78tOTaa9P6DRtqprDr9tun3bbfvmb1d1bxnx9/PK15/PFeOXbhoDEjfdKkSWnixIk9nvXeqx3J0cEvuu17KZZTL5RYGvzkESdVbdZ0jrqam6b022TzNHKLg9LfX345hf9lLVe2tu3Mt38onfmOD+emrY3cEOF4I4+uvhEgQIAAAQIECBAgQIBAbwkIx3tL3nEJECBAgAABAnUQiFnOp59+empvWfQ4/D6DB6dDhgxJw4cOTVv379/aogH9+6chgwfXoYX5PMTqxx9Pqx9r/1nMEcK3PPhgannoobT8f2ffl/YiQvJYsaC9pdvz2eN8tuovzz6ajl14SpvGTTz8A2aM12m4Dtvi4LTNawdkAXnhBoVYVv2acZfWqQXNfZiRI0dmj5gYO3ZsmjVrVnNj6D0BAgQIECBAgAABAgQIEKiSgHC8SpCqIUCAAAECBAjkTSBmi8fMw9Lngo866KAsDB998MFpYC/N8s6bVU/as3zFinTzihVpyR/+kFbe3/aZ2DNnzkzjxo3rSfVNve9lK65IF/zum60Gg7ffLU06/OSmNql359+71VFpk7RJGrPwlPTIs49mh4+l1WOJdaW2AoMGDcoOMHny5DRlypTaHkztBAgQIECAAAECBAgQIECgSQSE400y0LpJgAABAgQINJdABOITxo1LLX/8Y9bxrbbYIgvDp4wfLxCv4amw+qmn0sI//CFd8oMfpA3/u3x9zCAvPJu8hoduyKpLnzU+dcyZaZv+WzdkX/PaqV033Snt3+9tqfhGBUur12e0hOP1cXYUAgQIECBAgAABAgQIEGguAeF4c4233hIgQIAAAQJNIjD7i19Mc7756ozbmCk+/dRTheJ1HPvVW2yRTps2La1cuTINHDgwLV682DPIu+H/9m+/p3WvQ/Y6MB2731HdqMUuPRUYsvle6fUv9Wtd4v6I3UekOUed39Nq7V9GQDjuFCFAgAABAgQIECBAgAABAtUXEI5X31SNBAgQIECAAIFeFVj+61+nCae8+pzmsUcckWaddVavtqcpD96/f1q99dbpmGOOyWaQT5w4MU2fPr0pKbrb6Xueuj/FzPFCMWu8u5LV2e/g1++XPnrllGxp9QN22jddMsYzsKsj23EtwvFaC6ufAAECBAgQIECAAAECBJpRQDjejKOuzwQIECBAgEBDC0x43/vS8ttuS8OHDEkLzje7s9cGe/DgNPuyy9KcOXOyWePLli0ze7wLg3HL2jvSaYunZXtss8WANPXYj3Vhb5tWW2CH170xzb3x0nTr2jvTVpttmX79wSurfQj1lQhMnTo13Xzzzeniiy9OQ4YM4UOAAAECBAgQIECAAAECBAhUQUA4XgVEVRAgQIAAAQL5FFi9enW2pHUzlYWXXpqmffazWZfnnn129pxxpZcE/nf2+MiRI7MGzJw5M40bN66XGtP3DlscjltSPR/j98d770uX3XmFmeP5GA6tIECAAAECBAgQIECAAAECBLohIBzvBppdCBAgQIAAgfwLjBkzJrW0tDRdIHn6Rz6Slt54YzZAd33nO2lA//75H6xGbuHgwWnE+96X1qxZY2n1Lo5zcTj+8aNPTTtvs0MXa7B5tQVuXnlLumbF9cLxasOqjwABAgQIECBAgAABAgQIEKibgHC8btQORIAAAQIECNRToFmf1TrmPe9JLffem/YZPDhde8EF9SR3rPYEtt46TTj33LR8+fI0fPjwtGDBAk4VChTC8X6bbp4+e8LkCveyWS0FVv7lvvSDm8wcr6WxugkQIECAAAECBAgQIECAAIHaCgjHa+urdgIECBAgQKCXBJo1HC/0e+wRR6RZZ53VS/oOWyxw/k9/muZ/73vC8S6eFjc8vCxNuf68NHj73dKkw0/u4t42r4XAg48/nOb/8odmjtcCV50ECBAgQIAAAQIECBAgQIBAXQSE43VhdhACBAgQIECg3gLNHo5PHjcuTRk/vt7sjteOwIQvfCEtv+22NGrUqDRv3jxGFQpcdNv30kW3fz+9Y9Db0okHHVfhXjarpYBwvJa66iZAgAABAgQIECBAgAABAgTqISAcr4eyYxAgQIAAAQJ1F2j2cHzu2Wen0QcfXHd3B9xYYMKMGWn5XXelyZMnpylTpiCqUKAQjh85ZGR695BDK9zLZrUUKITjW27WP/3mgz+u5aHUTYAAAQIECBAgQIAAAQIECBCoiYBwvCasKiVAgAABAgR6W6BZw/EJEyZkz7decN55afjQob09DI6fUpowfXpa3tKS5s6dm0aPHs2kQgHheIVQddysEI7HIW8/9bo6HtmhCBAgQIAAAQIECBAgQIAAAQLVERCOV8dRLQQIECBAgEDOBJo1HB82bFhav369cDxH52MhHF+wYEH23HGlMoFv3va99K3bv5/MHK/Mqx5bCcfrofx/x1iyZElas2ZNGjt2bBowYEB9D+5oBAgQIECAAAECBAgQIECgQQWE4w06sLpFgAABAgSaXaBZw/FCv80cz89vgHC8e2Mx5w8Xp+/csUA43j2+muwlHK8Ja4eVFl7PZ86cmcaNG1ffgzsaAQIECBAgQIAAAQIECBBoUAHheIMOrG4RIECAAIFmF2jGcDxmjMfM8SjC8fz8BgjHuzcWU395frr+gd8Ix7vHV5O9hOM1YS0bjk+ePDlNmTKlvgd3NAIECBAgQIAAAQIECBAg0KACwvEGHVjdIkCAAAECzS5wzDHHpJUrV6bFixenIUOGNAVHPGs8njkeRTienyFvDcd/9KM0/JBD8tOwnLfkAz/7eFr52H3C8RyNk3C8voPRjDd51VfY0QgQIECAAAECBAgQIECgGQWE48046vpMgAABAgQINKSAcDyfw9oajl96aRp+2GH5bGQOWzX+Z/+S7n3sAeF4jsZGOF7fwRCO19fb0QgQIECAAAECBAgQIECgOQSE480xznpJgAABAgQINIHA7Nmz05w5c7KemjmenwFvDce/8500/Mgj89OwnLfkxKtPSw88vko4nqNx+kXLb9INLcuyFt1+6nU5alljNkU43pjjqlcECBAgQIAAAQIECBAg0LsCwvHe9Xd0AgQIECBAgEDVBITjVaOsakWt4fgll6ThRx9d1bobtbJ/pH+k9189KT30+J+F4zkaZOF4fQdDOF5fb0cjQIAAAQIECBAgQIAAgeYQEI43xzjrJQECBAgQINAEAlOnTk2LFi3KemrmeH4GvDUcnzs3DR89Oj8Ny3FLXv7Hy2nsz87IwvEx+x2VRux1YI5b2zxNE47Xd6yF4/X1djQCBAgQIECAAAECBAgQaA4B4XhzjLNeEiBAgAABAk0gMGHChBTPHY8yedy4NGX8+Cbodf672BqOX3hhGn7ccflvcA5a+Nd/vJRGLzw5PfPcujTx8A+kN22/ew5apQnC8fqeA8Lx+no7GgECBAgQIECAAAECBAg0h4BwvDnGWS8JECBAgACBJhAQjudzkAvh+MzPfjaNO+OMfDYyZ616/pUX0ojvHp+1Sjien8ERjtd3LITj9fV2NAIECBAgQIAAAQIECBBoDgHheHOMs14SIECAAAECTSAgHM/nIBfC8cmTJqUp556bz0bmrFXPvvJcOvS77xOO52xchOP1HZB4VMaSJUvSxRdfnIYPH17fgzsaAQIECBAgQIAAAQIECBBoUAHheIMOrG4RIECAAIFmF1i9enVqaWnJAoUBAwY0BYdwPJ/DLBzv+ris+/uGNOGqM9PadY+ZOd51vprtUQjHD9hp33TJmFk1O46KCRAgQIAAAQIECBAgQIAAAQK1EhCO10pWvQQIECBAgECvCowZMyYLxydOnJimT5/eq22p18GF4/WS7tpxhONd84qtn/r7M2ni4k+nhx7/s3C863w120M4XjNaFRMgQIAAAQIECBAgQIAAAQJ1EhCO1wnaYQgQIECAAIH6ChSC4pg5vmDBgvoevJeOVng+bRx+8rhxacr48b3UEoctFhCOd/18WP/Ks+nD13xKON51upruIRyvKa/KCRAgQIAAAQIECBAgQIAAgToICMfrgOwQBAgQIECAQP0FhOPC8fqfde0fUTje9ZH4e3olnXT1acLxrtPVdA/heE15VU6AAAECBAgQIECAAAECBAjUQUA4XgdkhyBAgAABAgTqL9Ds4fjEY49N0089tf7wjriRgHC8eyfFiVeflh54fJVl1bvHV5O9hOM1YVUpAQIECBAgQIAAAQIECBAgUEcB4XgdsR2KAAECBAgQqJ9As4fjw4cMSQvOP79+4I7UoYBwvHsnx7ir/yXd9/gDwvHu8dVkL+F4TVhVSoAAAQIECBAgQIAAAQIECNRRQDheR2yHIkCAAAECBOon0Izh+IgRI9KaNWsyZOF4/c61ckdqDcdPPz1N+Y//KLe5n/+vwD//7GPpj4/9KZ144LHpHYOHccmBgHA8B4OgCQQIECBAgAABAgQIECBAgECPBITjPeKzMwECBAgQIJBXgWYMxwt9jjERjufnzGwNxz/2sTTlM5/JT8Ny3pIP/uwTacVj96Qjh4xM7x5yaM5b2xzNE47Xd5xXr16dFi1alMaOHZsGDhxY34M7GgECBAgQIECAAAECBAgQaFAB4XiDDqxuESBAgACBZheYOnVqFipEoLBs2bKm4BCO53OYW8PxT3wiTTn77Hw2Moet+ug1k9Ptj64QjudobITj9R2M008/PS1dujQLx2fNmlXfgzsaAQIECBAgQIAAAQIECBBoUAHheIMOrG4RIECAAIFmF5g9e3aaM2dOxrBq1aqm4CgOx0cddFCad845TdHvvHeyNRz/139NUz796bw3Nzftm7R4arp17Z3C8dyMSEo33XdLWnzH9emAnfZNl4wR1tZ6aJpxBZRam6qfAAECBAgQIECAAAECBAgIx50DBAgQIECAQEMKLFy4ME2bNi2NGjUqzZs3ryH7WNqp4nB88rhxacr48U3R77x3sjUcnzw5TZkyJe/NzU37LvjthemyliuF47kZkZQefPzhNP+XPxSO12lMhON1gnYYAgQIECBAgAABAgQIEGgqAeF4Uw23zhIgQIAAgeYSWL9+fRowYEDTdLp4trxwPD/DLhzv3lhcdNv30kW3f1843j2+muwlHK8Ja4eVCsfr6+1oBAgQIECAAAECBAgQINAcAsLx5hhnvSRAgAABAgSaQKAwWz66KhzPz4AXwvEFCxak4cOH56dhOW/JZSuuSBf87ptpn132SiePODHnrW2O5gnH6zvOwvH6ejsaAQIECBAgQIAAAQIECDSHgHC8OcZZLwkQIECAAIEmEFi+fHmKMEU4nq/BFo53bzwKM8cHb79bmnT4yd2rxF5VFfDM8apylq1MOF6WyAYECBAgQIAAAQIECBAgQKDLAsLxLpPZgQABAgQIECCQX4FBgwZljVtw3nlp+NCh+W1oE7VMON69wS6E4/023Tx99oTJ3avEXlUV+EXLb9INLcvS3m/YIy044aKq1q2yjQWE484KAgQIECBAgAABAgQIECBQfQHhePVN1UiAAAECBAgQ6DWBESNGpDVr1gjHe20E2gm4pk9Py1takmXVuzYohXA89opwPEJypXcFrvj9z9Jtq+7OGnH7qdf1bmOa4OjC8SYYZF0kQIAAAQIECBAgQIAAgboLCMfrTu6ABAgQIECAAIHaCRTCFDPHa2fclZrXP/dcGvbRj2a7zJ07N40ePboruzf1tsXh+MTDP5DetP3uTe2Rh85f+PPvpEeeeTTtvOWOafG4S/PQpIZuw+mnn56WLl2ahg8fnt1coxAgQIAAAQIECBAgQIAAAQI9FxCO99xQDQQIECBAgACB3AiMHDkyrV692szxnIzIwhtvTNO+8Y2sNZMnT05TpkzJScvy34yf3Lc0nfubC7KGHjlkZHr3kEPz3+gGbuGLf/tr+q+r5mQ9PGCnfdMlY2Y1cG/z0bUlS5akqVOnpkmTJnntyMeQaAUBAgQIECBAgAABAgQINICAcLwBBlEXCBAgQIAAgfYFYsZdlFGjRjUNkWeO52uox5x9dmp56KGsUWPHjk2zZgkUKx2hW9bekU5bPC3bfJv+W6epY86sdFfb1UDgtofuSlfcck1W8z/tOSp9/rCza3AUVRIgQIAAAQIECBAgQIAAAQIEaisgHK+tr9oJECBAgACBXhJoaWlJY8aMyY6+bNmyNHDgwF5qSX0P+7a3vS1t2LAhzT377DT64IPre3BHayOw+vHH08iPf7z13yyN3LUTZMNLz6bDLnt/606WVu+aX7W3/sFNV6SVf7kvq3b2UeelI3cfWe1DqI8AAQIECBAgQIAAAQIECBAgUHMB4XjNiR2AAAECBAgQ6A2B5cuXp3j+dpR4VmsEk81QJowbl5b/7ndp4rHHpumnntoMXc5tH2dffnmas3Bha/vMHO/6UH3u1xekn/7p1RUg4pnjEZAr9Rd45JnH0oU//3Z2YM8br7+/IxIgQIAAAQIECBAgQIAAAQLVExCOV89STQQIECBAgECOBJo1HD//vPPS/G9/Ow3cfvu07MILczQizdWUWEp9wnnnpfXPPZeGH3RQWv7733vmeDdOgeKl1WP3MfsdlUbsdWA3arJLdwXiWePzf/nD9Mgzj2ZVzDj07HT8Xs3zqIruutmPAAECBAgQIECAAAECBAgQyKeAcDyf46JVBAgQIECAQA8FmjUcX7hwYZo27dXnNM8866w07ogjeihp9+4IFJ41PnzYsDRw773TokWL0rnnnpsmTZrUneqaep/i2eMBceKBx6Z3DB7W1Cb16nzMGL/ylp+l+DuKZ43XS95xCBAgQIAAAQIECBAgQIAAgVoJCMdrJateAgQIECBAoFcFmjUcD/TCc8cH9O+fFl9wQTaLXKmfQPFy6suuvjqNOfnktH79+rRs2bI0cODA+jWkgY5UGpBHOP7ufUambfpv3UC97P2uxCzxBx9/OD30+MOp5S/3pWeeW5c1asvN+qdThpyYznzHh3u/kVpAgAABAgQIECBAgAABAgQIEOiBgHC8B3h2JUCAAAECBPIr0Mzh+OzZs9OcOXOywRkyeHBacN55KYJypfYCS373u3TGBRdkB5p5zjlpfb9+acaMGWmfffZJ1157be0b0MBH+Ml9S9OXf3dheval51p7uW3/bdI2WwzInkfeb9PN087b7FgVgc2zunaoSl15ruShx/+cLZf+zPPrstnhEYyXlgN22jd9/rBz0i5bVsc2zx7aRoAAAQIECBAgQIAAAQIECDS+gHC88cdYDwkQIECAQFMKNHM4HrOUx48fn1auXJmNfQTj884+Ow0fOrQpz4V6dXrOwoUpZo1HGXvUUWnStGlpzJgx2f/PnTs3jR49ul5Naejj3PDwsvSLVTeln/5paUP2M0L5fpv267RvL/7txdalzmuFsPOWO6aPvf3Dni9eK+AK6o3X8ngvGzJkiFUnKvCyCQECBAgQIECAAAECBAgQqERAOF6Jkm0IECBAgACBPifQzOF4DNbq1avTMccckzZs2NA6dqMPPjhNPPZYIXmVz+aWhx5KU7/xjRR/R4lgfNSECdmz3yPcmjhxYpo+fXqVj6q6DS89m25Ze0cWlP/l2bUZyK1r7wTTRYEIwQuzwnfZcqd00E77pQN33s9M8S461mLz888/P82fPz8NHz48LViwoBaHUCcBAgQIECBAgAABAgQIEGg6AeF40w25DhMgQIAAgeYQKF5a/K677koDBgxojo4X9TKC2alTp6alS9vOsI2l1g8ZOjSNOvjgNHzIkKZzqUaHVz/+eFq+YkVa+vvfp5tXrEjrn3t1qe+J48enlocfzmZ7RolQa968eU15/lXDudZ1RLje18veb9gjbbXZln29G9rfjsCECROy1xLhuNODAAECBAgQIECAAAECBAhUT0A4Xj1LNREgQIAAAQI5EliyZEk644wzshYtW7asqZekDYuYfVgIbEuHaeD226eBO3T9+cpb9++fPdO8miXqq9bz0WMmdyG07m4bY/8V/zsjvFDH6sceSxGOlytmjJcT8nMCBDoTKNzkNXDgwOx9TCFAgAABAgQIECBAgAABAgR6LiAc77mhGggQIECAAIEcCjT7surtDUkstR5BeUtLS7r55pvTmjVrcjhyfb9JY8eOTVOmTGnqGzL6/ijqAYHeFyheAWXVqlW93yAtIECAAAECBAgQIECAAAECDSAgHG+AQdQFAgQIECBAYGMB4XhlZ0UE5vGnUNatW5eF55WWjmajV7p/HraLJYsrKUOGDElbb711dmNBe+WQQw7Jlj9WCBAgUA0B4Xg1FNVBgAABAgQIECBAgAABAgTaCgjHnREECBAgQIBAQxmUaLoAACAASURBVAoIxxtyWHWKAAECTSMgHG+aodZRAgQIECBAgAABAgQIEKijgHC8jtgORYAAAQIECNRPIGY/jxkzJjvg3Llz0+jRo+t3cEciQIAAAQI9FJg6dWpatGhR2nXXXdNNN93Uw9rsToAAAQIECBAgQIAAAQIECISAcNx5QIAAAQIECDSswKBBg7K+TZ48OXsGtEKAAAECBPqKwIQJE1KsghKPa1iwYEFfabZ2EiBAgAABAgQIECBAgACBXAsIx3M9PBpHgAABAgQI9ERgxIgRac2aNWns2LFp1qxZPanK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r3pi3CMIj1LpkvjN+h6p3wQIECBAgAABAgQIECBAIG8CwvG8jYj2ECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEDVBYTjVSdVIQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjkTUA4nrcR0R4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqLqAcLzqpCokQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbwJCMfzNiLaQ4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJVFxCOV51UhQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECCQNwHheN5GRHsIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoOoCwvGqk6qQAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBPImIBzP24hoDwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUXUA4XnVSFRIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA3gSE43kbEe0hQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgaoLCMerTqpCAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEMibgHA8byOiPQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBQdQHheNVJVUiAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECeRMQjudtRLSHAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBKouIByvOqkKCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQCBvAsLxvI2I9hAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA1QWE41UnVSEBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQI5E1AOJ63EdEeAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEKi6gHC86qQqJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIG8CQjH8zYi2kOAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECVRcQjledVIUECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgkDcB4XjeRkR7CBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKDqAsLxqpOqkAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgTyJiAcz9uIaA8BAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIVF1AOF51UhUSIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQN4EhON5GxHtIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGqCwjHq06qQgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBDIm4BwPG8joj0ECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgUHUB4XjVSVVIgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAnkTEI7nbUS0hwABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgSqLiAcrzqpCgkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIEAgbwLC8byNiPYQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQNUFhONVJ1UhAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECORNQDietxHRHgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCouoBwvOqkKiRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBvAkIx/M2ItpDgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAlUXEI5XnVSFBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIJA3AeF43kZEewgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECg6gLC8aqTqpAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIE8iYgHM/biGgPAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFRdQDhedVIVEiBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEDeBITjeRsR7SFAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBqgsIx6tOqkICBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQyJuAcDxvI6I9BAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFB1AeF41UlVSIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJ5ExCO521EtIcAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEqi4gHK86qQoJECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAIG8CwvG8jYj2ECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEDVBYTjVSdVIQECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjkTUA4nrcR0R4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqLqAcLzqpCokQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbwJCMfzNiLaQ4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJVFxCOV51UhQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECCQNwHheN5GRHsIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoOoCwvGqk6qQAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBPImIBzP24hoDwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAhUXUA4XnVSFRIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA3gSE43kbEe0hQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgaoLCMerTqpCAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEMibgHA8byOiPQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBQdQHheNVJVUiAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECeRMQjudtRLSHAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBKouIByvOqkKCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQCBvAsLxvI2I9hAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA1QWE41UnVSEBAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQI5E1AOJ63EdEeAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEKi6gHC86qQqJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIG8CQjH8zYi2kOAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECVRcQjledVIUECBAgQIBAswjc89T96Za1d6YNLz27UZfvefL+dv+9Epu/PLs2/eXZRyvZtCrbbLXZlmnvN+xRlbo6qmTvN+6R4jiFkvVxw6Np3Usb0n1PPZD9c7Qhtjlw5/3SkbuPqHmbatphlRMgQIAAAQIECBAgQIAAAQIECBAgkDsB4XjuhkSDCBAgQIAAgTwL3LL2jnTpiitS/P3sS8/lual9vm0H7rRfOvudHxOS9/mR1AECBAgQIECAAAECBAgQIECAAAEC+RAQjudjHLSCAAECBAgQyLlAzBL/4m+/nm5be3drS7fZYkDaeZsd007b7JDz1ue3eWufeSy98LcXU/z94t/+2m5DZxx6djp+r1H57YSWESBAgAABAgQIECBAgAABAgQIECDQJwSE431imDSSAAECBAgQ6E2BCMYnLv50eu6l51O/TTdPh+x1YNp/0LC0Tf+te7NZDXfsR555LN226q70xzX3paefX9fav1hq/eIxM80gb7gR1yECBAgQIECAAAECBAgQIECAAAEC9RUQjtfX29EIECBAgACBPiYQy6eftnha1uqdtt4hTTri5CwgV2or8MeH70tX3b6kden6eB75ghMuqu1B1U6AAAECBAgQIECAAAECBAgQIECAQEMLCMcbenh1jgABAgQIEOiJwIaXnk0Trjoz/eXZR9M7Br0tnXjQcT2pzr5dFNju5W3T7F/NTfc+9UC2Z8wej+eQKwQINL7A448/nn7729+m3/3ud9mflStXZp0+4YQT0v7775/e+ta3puHDhzc+hB4SIECAAAECBAgQIECAAAECVRUQjleVU2UECBAgQIBAIwlc8NsL02UtV6Z4tvhZ75loxnidB/f1r+mX9nvdkPThn3wqPfLso+mf9hyVPn/Y2XVuhcMRIFAPgWeeeSb95je/Sbfeemu65ZZb0p133ln2sLvuums66aST0lFHHZXe/va3l93eBgQIECBAgAABAgQIECBAgAAB4bhzgAABAgQIECDQjkDMGj/ssvdnPzl5xIlpn1324tQLAntuNjg99/RzrUvb//qDV6Z4BrlCgEDfF4hA/MYbb0w33HBD9nf8f3ul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Q8sXJ7v84Ni5brfh44vsrm1FQAQqQmDevHlJDfKXX3456dHEKR90Ez/4ETdu74Pq7uGaP/7WLVv4H0kKdS+KEy1+/vnnu2nTptXdrnYUAREQAcTWn4MAAAYVSURBVBEQAREQAREQAREQAREQARHoTgISx7vzvGpUIiACIiACIiACDRCYu+RaN/ex65IWBvTbyl04/RPJ37LWE1j/2kvum3fMTQ48cdTe7l+O/lbrO6EjioAIlEZg2bJliUB+55139rT57qNPcvsddYobPWEv16//gJrHenHNCvfHxx92Sx/4D/fkL+7u2X7ChAnuhBNOcKeffnqS8lgmAiIgAiIgAiIgAiIgAiIgAiIgAiIgApaAxHHNCREQAREQAREQAREwBOYvv9N9+YFLe/7r/mP3didMmi5ObSBw44O3uN+uXp4c+eqj/5+bNEr1LttwGnRIESidgI0i5wADBw1yO07Yw+2wyzvd0FE7uUHbjHID3jYkiQh/6fm1bvXvl7pnfvdrt/65Z3r1Z++993annnqqO/HEE12/fv1K76saFAEREAEREAEREAEREAEREAEREAER6B4CEse751xqJCIgAiIgAiIgAiUReOVvr7pDbzi+V2sSyEuCW6CZJX/6tbtl8e3JHtPGH+6+cdgXC+ytTUVABKpOgCjy++67zy1atCj589prr+Xqcv/+/d3BBx/sdt99dzd16lQ3adKkXPtpIxEQAREQAREQAREQAREQAREQAREQARGQOK45IAIiIAIiIAIiIAIRAp9ecLG79+kH3cB+A92b7u/u9Tf+6saN2DGJIB/2tq3FrMkE7lm20N297IHkKGOG7eBu/MBlbkj/wU0+qpoXARFoJ4GHH37YPfTQQ0kXli9f7l544YWe7lBHnFTpEydOdJMnT1aEeDtPlI4tAiIgAiIgAiIgAiIgAiIgAiIgAh1MQOJ4B588dV0EREAEREAERKB5BIgeP/rf/8G9+rdNIxmJIn/fHodIJC8ZPw4IpFBHFKfWODZ62Ch3zdFz3MgBby/5aGpOBERABERABERABERABERABERABERABERABESgrxGQON7XzrjGKwIiIAIiIAIikJvAky/+wRFBvubVZ6P7bD9spNt/p73d2BE7Ov4tK07AC+JPrXvakUY9tIlj93GXHDrTjdpSbIuT1R4iIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAKWgMRxzQkREAEREAEREAERyCBABPkNv7nF3fr7O1NFcnYf0G+rJO369sO269XaqGEj3cB+A1rKGKE5zV5/43W3Zv1zdf9edCBwCZmQkn6bQVs7+kE/Y30ZO2KM+9A7j3LHjXm/G76FUtgXZa7tRUAEREAEREAEREAEREAEREAEREAEREAEREAE4gQkjmtmiIAIiIAIiIAIiEBOAkSS37NioXvixT84/p0WUZ6zOW0WEEBA33XEzu6wnd/tJo+c6EZv2dvJQLBEQAREQAREQAREQAREQAREQAREQAREQAREQAREoFECEscbJaj9RUAEREAEREAE+jSBxWsfT8a/+pVn3epX1/Zi8chbv+UFtPrVZ3sE912H7+yG9B+cd9eGtztw1L4Nt1G0gdGDR7nRQ7Zzuw0f39KxFu2nthcBERABERABERABERABERABERABERABERABEegOAhLHu+M8ahQiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIZBCSOa3qIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAh0PQGJ411/ijVAERABERABERABERABERABERABERABERABERABERABERABERABERABERABieOaAyIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAl1PQOJ4159iDVAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREDiuOaACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIhA1xOQON71p1gDFAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERkDiuOSACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACItD1BP4/OrOMWDz8TRsAAAAASUVORK5CYII="
    }
   },
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to edit graph state\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    * [Human-in-the-loop](../../../concepts/human_in_the_loop)\n",
    "    * [Breakpoints](../../../concepts/breakpoints)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)\n",
    "\n",
    "Human-in-the-loop (HIL) interactions are crucial for [agentic systems](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop). Manually updating the graph state a common HIL interaction pattern, allowing the human to edit actions (e.g., what tool is being called or how it is being called).\n",
    "\n",
    "We can implement this in LangGraph using a [breakpoint](https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/breakpoints/): breakpoints allow us to interrupt graph execution before a specific step. At this breakpoint, we can manually update the graph state and then resume from that spot to continue.  \n",
    "\n",
    "![edit_graph_state.png](attachment:1a5388fe-fa93-4607-a009-d71fe2223f5a.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "035e567c-db5c-4085-ba4e-5b3814561c21",
   "metadata": {},
   "source": [
    "## Simple Usage\n",
    "\n",
    "Let's look at very basic usage of this.\n",
    "\n",
    "Below, we do three things:\n",
    "\n",
    "1) We specify the [breakpoint](https://langchain-ai.github.io/langgraph/concepts/low_level/#breakpoints) using `interrupt_before` a specified step (node).\n",
    "\n",
    "2) We set up a [checkpointer](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer) to save the state of the graph up until this node.\n",
    "\n",
    "3) We use `.update_state` to update the state of the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "85e452f8-f33a-4ead-bb4d-7386cdba8edc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKMAAAHaCAIAAADjVG5qAAAQAElEQVR4nOydB1hTV9/AT3ZIAoS9NyggIK5asHXV1oV7IXXVvapWcbXWqm9ftXbhqEq1Vv1aa1+rdVsV66habUERqcgQHIywR/bk+2PalFeB0rckN8k5vydPnpubk3uT/O7/rHvuucz6+npEwAAmIuABMY0LxDQuENO4QEzjAjGNC+ZoWqXUVhSpZGKtrE6j1SC1SofMHg6XzmTTeLZMG1u6u58NMj9o5tOeVki1ObfE+Xel5YUKR3cOz5bBs2PaO7FUCgswzebSq0rh6NQwWbRHWbKACH5ghCA4WoDMBnMx/fPJysI8masPNzCS79OOhywZODQLMqWPs6WFufLYOOf2XW2RGUC96fupdSlfl7042LFrP0dkXUhqNNdPVoir1f0negiEFBeUFJu+eqxCp6t/ebgzjUZDVkpVqfLYjuI+Y139w/mIOqg0feVIua0Ds1MfB4QBJz4v7vaao7s/F1EEZaZPfVHiEcDt3BcLzXqOJxeHdBKEvWCHqICOqODG6UpXHw5WmoGhszwzrtSWFSoQFVBgOv+uRKPWQVaG8GNcog9UTbRqCtqNFJi+fLg8uhde0dyY4CjB1eOVyOSY2nTGTzWBkQLKmxwUEvWyEHI1aIAh02Jq0/mZ0tihTghveo50uXO5BpkWk5p+fF8GzWYWi5pqoPngF8rLuFaLTItJ//T8TAn0BiPTsnz58hMnTqC/T79+/YqLi5ERYLLpngHcx9kyZEJMarpKpAqMMnU/UVZWFvr7iESimhojZrDtugiKck1q2nQ9JxqVbteqgjmbgpBxOHr06IEDB4qKirhcbufOnRMTE93c3Lp27ap/VyAQXLp0SavV7tq164cffigrK7O3t+/Vq9fChQttbBpOMkLoQ4+sv7//V199NXXq1O3bt+s/CGk+/vhj1NZAQN/+sXrYHC9kKkxXB4bzzXAiEhmH27dvv//++++88063bt0gFjdv3rxixYovv/zy9OnTgwYNWrp06YABAyAZHAp79+5dt25daGgo5Mxr165lMplwTMBbLBbr/v37CoViy5Ytvr6+Pj4+K1euBOuwgIwA344hrdMiE2I601Kxhm9rrN09ePCAw+EMGTIEzHl7e2/cuLGkpATWQ+DCM4/H0y8MHDgwJiYmODgYlkHna6+9du3aNcNGCgsLv/jiC31KPr+hlLGzs9MvtDl8O6a0zqQNLdOZ1mkQh2+sagHk0pD3Tp8+fdiwYd27d/f09HRyaqItJxQKT506BdEPubdGo5HJZHAQGN718/PTazYBdCaNwzVpJcl0O+PZMWrL1cg4QPkKeTVE89atW4cOHTplypTMzMznk3344Ye7d+8eO3YslNaQk48YMaLxu1CWI1MhrdXQGSY9UWs608bOr0JCQiBYz58/n5yczGAwFi1apFKpGieA6tixY8cmT54MJbeXl5ezs7NEIkEUIavTwh+CTIjpTLO5dDc/rkpplGoIRHBGRgYsgOMuXbrMmTMH6mWVlb93L+vbFzqdDmQb8mepVHrlypWWmx7Ga5jIpRpXXw4yISYtKqDuXXDXKI3I69evL168+MKFC1Crys7OPnjwoIeHh7u7O+cpt27dgpVQkLdv3/7kyZOQJjc3F4K+R48edXV1Dx8+hDL7mQ1CXQyer169mp+fj4xA7i0JHPfIhJjUNJzbgM59ZASgBQyFblJS0ujRo+fNmwexCI0l/YglKLNTUlLmzp0rl8tXr14NYQ3lNLSg4uPjISUcDZMmTYIK2jMbDAsLi42N/fTTTzdt2oSMAPT/B0aYtBPJpGNO4LT0ieTiEfO9Ed48yZXl3Zb0GeuKTIhJY5rJorsH2KSer0J4c/1EZYcXTT3GyNTniWMGO322JK9zX4fm2hi9e/ducj3kulDbQs0AlWojNYXT09OhRG/yLajbs9nsJt8KCgqCTpgm38q7I7FzYLr6mnroIAUjBjOv1yhl9V36NT3sRCwWN7keKk1gurnBwtAUNtI4YtgvFPBNvqVUKsF0k/ul0+nNda6d+bIkZoiT0LnpQ8R4UDM29Ox+UUAEv11ns7i4wZT8sE8UFMUP6UTBD6dmUED/Se6p56uL8+UIJ64cKbd3ZlGiGVE7sv/I1sKurzr6hlr2VVit5Kfvy5082eHdTdSv/jxUDvQZ+ab37UvVGVdNPaLK9BxPLubZMSnUjMzhCrybZyqhOhob5xwQQeVlS0Yi7UL13Z9q+4xz8Quj+NeZxVW1VSLV9ZMV0Nr2bmcDPUc8W4sfI1xepHx8X5aWUh0Ra/fiYCc6nfrrC83oSnmooGX/KoZuQqELy8mDzbdnwolOgT1Lq7WAufEYdFptlUpaq4X/MydNwuXRgzoKol6259gYa5jN38WMTBsQPZSXF8G/poFTe3QGattROAqFIi8vLyIiArUpto6sem09355h68j0DLSxdWAhM8McTRsVOHO1ZMmSw4cPI8wgcxfhAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuYGeaRqO5ubkh/MDOdH19fWlpKcIPknvjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LuAy89zrr78uFotpNJpKpaqsrHR3d4dlhUJx9uxZhAe43N591KhRFRUVRUVF5eXlOp2uuLgYlul0jO5uj8tPHTlypK+vb+M1kJn16NEDYQNGB/XYsWMb3wrH1dV14sSJCBswMg1h7eX1+z3c9QHt5+eHsAEj0+hpvYzDabhvqLe39+TJkxFO4GV6+PDhnp6esBAbG+vj44NwwlxaWfA1asrUtRVqnZG/zo0bN6BlNX/+/CZvOt+GsFg0Rw+2iW8y3QJmYTr3tjjjaq2sTusZbCOtMcoNqk0Pz475KEvi5sPpNdrFHKbwp950zi3xvZviPvEe5nBbkjanplx16T8lI+Z6CYQUBzfF5XRBpjTzet0rCZ5WqRkQurCHzvHdt+4hohqKTd/5qSZ2mElvw2x64CB+Mc7l5plKRClUmlYrdaICBd/O7G5D0+bYOrKK8xWIUqgsPMTVajc/U9+GmRJsHdk6qu/6RW01gSYTW0lN+y+oR5IaDaIUcn4aF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxAa9xZP+E/Py8SVNGDRnWG1kmVmK6oOBBfEIcMhqnzxyb9+YUBoOBLBYrMZ2Tk4WMyb79n7+3+oNX+w1CFouFldOlpaKdyUnpd9JkMqm7u+foUQlD4kbu3Ze8b/8ueLfPK13nzV0MK2tqqrfv/PTOnbTa2prAwJAZ0+d3iu4KCXJy78+aPeFfaz86fOSb3Lz7DAZzQP8hs2Yu+MsLtLZu3uPq6pafn4ssFgszvenDtSq1av2/k+zs7FNTbyRt3gi+48dNFkvEV69e/Hzn11yujU6nW77iTYlUsnzZGidH52PHD61YuWDHZ/sDA4OZjIbfm7xry8oV60Lbh9+4cXX1mqW+vv6DBw1veb+gGVk4FpZ75xfkdesaExbawcvTe9jQ0du27AkKDOFyuRw2h0aj2dsLORxOatpNiN3EJas6d+rm5xcwf16im5vHke8PGjYCmXB4WATEcWxsT4j1s+dOIgywsJiOjen5zcG9Eom4e/ceUZGdwsIink+TlZXJYrGiO3bRvwSjkDIvL9uQoF1IqGHZzy/w0uXzCAMszPRbi1YGBgSfTzl96Luv+Xz+0CGjp74xh8n8r18BRbhare4/MNawRqvVOjr+ecWGjQ2v0bINHDcIAyzMNEgdNWo8PKqqKs+dP/XFnu1CocPYMRMap+HzBWw2e1fygcYrG9e55HKZYVkqkwoEtggDLKmcVigU51POaDQNQ+8gRuPHTQoPj4QOjWeShYZ2UKlUEMdQ1dI/2GyOs/Ofo8qh6m5Yzs6+5+vjjzDAwmpkW7Z+8NHH7+fmZReXFKVc+AGa0dHRDeUxxGVlZUVGxm2RqKRL5xdCgtuv3/BuenpaiagYks2clQA1cMNGrv985cKPZ2ELUATcu3d34IChLe+0tq72dnoqPIqLC+E40y8/fvwQWRRUXpdVJVKd2SsaOse39R+5l5W5e/c2aApD1EL7ClpH+qwb2tnLVswHEwnjp7wxZXZ1ddWO5KSbN68pFHJIFjd4xJjRr6OnPZrTZsS/t3oj1LfT01Mh1qHxPXHCtJZ3evOX69BOe2Zl//5xK5atQa1DUqM5t69w8mp/RB0WZvofoje9JWl3ZGQ0MiHmYJqcy8IFYrqBle8sysxMb/KtwYNGzJ61EFk+eJmGDtGLF1KfX5+4eBV0sjb5ER6Pj6wCEtMNODk5I2uHmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgUqTdPoyM7J+icjA3T19Y6eHEQpVI5EcHBlF+bKNGodsnYqixQsFsXTZVI85qR9V1tRgRxZO5XFysBIis+UUGy671jXa0dLpXUUz8pmVNIvV2rU2nadKR6XSP2szyql7uv1jyJedhAIWY5uHKu5fZdOV19RpKgsUWpU2lcTqL8ExFzm7L91ofpJrhy+Sk2pChkT+L0qlUp/Nw6j4uTFgbIZMm3Ko1kPLvfAM/Dw4cMlS5YcPnwYYQZpT+MCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC5gZ5pGowUGBiL8wM50fX19fn4+wg+Se+MCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQu4DLz3OzZs2UyGY1Gk0qlRUVFISEhsAxrDh06hPAAl5ju1KnTrl27DC/v3bsHz+7u7ggbLOxO4/8z48eP9/HxabwGMjPQj7ABF9N2dnYDBw5svMbDwyM+Ph5hAy6mAfDq7e2tX4aAjoqK6tChA8IGjExDWA8ePFi/DAEN+TnCCYxMA2PGjNGX1pFPQTjRqrq3Rq2TS6zhvigMJBjQb8Tp06fHjJgkrraGW0LU6+pbeX+iv2hPZ/1Sl/FTbZVIZSNgIIL5IXRjF+fJAqMEL7zm6OjObiFlS6Z/OVdVUayO7uVo64jFXa0sFK22vrZCdfk/Jf0nu7v5cJtL1qzpmz9U1VVqXoxzRQQL4ehnjwZMcnfxbvoWI03XyKrLVBVFSqLZsugb7/Hruarm3m3aNGiur6f4nm2Ev4udE/tRVrP3FGzatKRW69J8jk8wW/w78KtE6ibfarqVpVbq1ApEsDhqK9TNvUXOT+MCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q063i4cP8z3dvvXfvLiyHhUXMmDY/MDAYWRRWMmKwoOBBfEIcMg4VFeUL35ohFtetWLZmWeLqqsqKZSvmSyQSZFFYSUzn5GQho3H2JufmlQAAEABJREFU3EmFQr7+30m2AlvUMILYa+r0cZmZ6S+++BKyHCzMdGmpaGdyUvqdNJlM6u7uOXpUwpC4kXv3Je/b33DNVZ9Xus6buxhW1tRUb9/56Z07abW1NYGBITOmz+8U3RUS5OTenzV7wr/WfnT4yDe5efcZDOaA/kNmzVxAp7eUtw0ZMqrny331mgFX14aruerqapFFYWGmN324VqVWQXjZ2dmnpt5I2rwRfMePmyyWiK9evfj5zq+5XBudTrd8xZsSqWT5sjVOjs7Hjh9asXLBjs/2Q8nKZDT83uRdW1auWBfaPvzGjaur1yz19fUfPGh4Czu1s7WDh+HlzV+u0Wi08A5RyKKwsHI6vyCvW9eYsNAOXp7ew4aO3rZlT1BgCJfL5bA58O/b2ws5HE5q2k2I3cQlqzp36ubnFzB/XqKbm8eR7w8aNvJqv0HhYREQx7GxPSHWIXNu/RcQiUq2bN0UN3iEt5cPsigsLKZjY3p+c3CvRCLu3r1HVGQnqAY/nyYrK5PFYkV37KJ/CUYhZV5etiFBu5BQw7KfX+Cly+dR63jy5FHisrkhwe3h6EGWhoWZfmvRysCA4PMppw999zWfzx86ZPTUN+Ywmf/1K6AIV6vV/QfGGtZotVpHRyfDSxsbXqNlGzhuUCvIzsmCQiEyIvrdVevZbDayNCzMNEgdNWo8PKqqKs+dP/XFnu1CocPYMRMap+HzBWBiV/KBxisb17nkcplhWSqTCv6oarXA48cPly6b91KP3ksWv8NgWOTlLJZUTkMT9nzKGY2m4XoqiNH4cZPCwyPz8/OeSRYa2kGlUkEcQ1VL/2CzOc7Of45dh6q7YTk7+56vj3/L+4U9rlq9pEvnF5YmvmuhmpFlmYY615atH3z08fu5ednFJUUpF36AZnR0dEN5DHFZWVmRkXEbakygBIrS9RveTU9PKxEVQ7KZsxKgBm7YzvWfr1z48SxsAYoA6PYaOGBoy/s9dvy74uLCvn37wyFyOz1V/4AyG1kUTV+t88vZKpUCdeztiMyMe1mZu3dvg6YwRC20r6B1pM+6oZ0N/VbgI2H8lDemzK6urtqRnHTz5jXo8YBkUFUeM/p1SAYZwLQZ8e+t3gj17fT0VIh1aHxPnDCt5Z1CQF+7dvmZldCOX/zW28jMOLXrSd9xrq4+TVywY2Gm/yF601uSdkdGRiNrpAXT5AwHLhDTDax8ZxH0Yzf51uBBI2bPWogsH7xMQ4foxQupz69PXLwKOlmb/AiPx0dWAYnpBpycnJG1Q0zjAjGNC8Q0LhDTuEBM4wIxjQvENC60jekzZw87CJ0QwQhwOOxO0bHoH9M2ppVKeVhYe0QwAjY8DmoL2sZ0v1cG8fkCRDACOp0KtQVtY1rAt7bzm+YDg942Y9ZIjQwXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcoOb66Vu3fx0x6tUWEty9m56Xl4OMz/nzp/+HOeTUavVrA2IePsxvTWKNRrNm7fJRY/p/c3Afog5qTHcIj9q751ALCTZv/aC5C6XakMrKim3bP+bxeH/3g3kPcrgcrp9fQGsSp6beuJuZfuCr4+PjJyPqYKxZs+b5tUUP5FoNcve3QcZh0eKZLBarfbuwufOnlJaWHDv+3eHvDx75/mDXrjECge0b08Y+elTw2293vLx8HISOn23/ePOWD46fOJyenhYV2QnE/Jp6Y9W7i58UPtqZnDSg/5CFb82AjexM3iyVSsoryt5dnThyRLx+R/EJcd5evkKhY/+BsWw2+8DBvd8c3JuRcatHbC+IyHlvTtFqNSkXfujbtz+H8zcG8Vy9eqlOXAsK4bsdOXLQx8dfP2nVd4cPbPjgvaPH/nPhx7P+/kEuLq5Hvv9267YP6+vrL146N3DA0F9//Xn9xtX/OfTV90e/1enqw5/OvTTvzTcM3z8iouPzG2n9F8u9VRcQwefbN1EoU1BO63S6Bw9yQkJCYeHhwwfubh7vvP0+iF+6bN7ZsyfemDJ7/LjJYD1551eQGLSBgy/3HILnTz5dD//amvc+KCjIg3Ds3bPfm/MS4U989Cjfw8Prs217mUzm57u2wgGk31FtbU1pqQh2BOnhpZOj84Z/J0Fe+vrEYZcup/R7ZUDMiy/b2trNnfNW46+36cN1P139sfEaX9+Az7Z+2XjN/ezfRKLitxauhLA+8M3ezZs3Hvj6OCg/eer7Tz9OdnZ2OZ9yZvV7iQcPnBw5YtzPP1/p1i1m7JgJt9NTN25a89Gm7cHB7eCLTZ8Z3y4kFNQ2/v5NbuSZyZn+NyjIvZ88eQR/d2BAcGHhY4VCMW/uEtCMnk5jwmI1jKTJybsf8nTKsKyszJu/XFuwYDmXy4V3X3qpz72shtl4c3Lvx8b01M9rUFRcKJVKp02dq/87cnN//6w+maOjk5OTMyyEhUX0798whSwkc3V1LysTNSTO+zOxgWVLV584dqnx4xnNwP37v82csUCfe8OWy8pL4Yfs3f/57JkLwRCs7PlyXzgWS3/fS7Z+LwcOfDl6VAJohmU3N/egoHZZ9zMbf/8WNvLPoSCm4Zf7+wVCXgoCAgKCDFe0PsjP1ee6YOuVvgPQ04obPM+claBPoNVq9VMQQQII/d+3lnvf3z/Qw93TsPHx46fol/P++IshC+kY1dnwBSCrdHFxU6lUUEYYMoDWAzW4x48fQpjqX1aUl7k4u8IuxOK6pC0b0ZbfkwkEAj6PD7ELWUtIcCgc3BDTU9+YY9hOXV0tny9o/P2b2whqC6gw/UfYNY4/OHirqirbtQuD3Dg/P3fO7IYcVaVS9u796tsr1jX+uFwuh1yh3R+GcnKyDMtlZaXwtwYFhuhfQjna4ensnvAPQkmsXwkxVF5eFhkRXfDwAeQl3t6+z3y9v8y9s3PuwQcNE8am30mLiIxWqpSurm6Q0z6ztavXLnl5eoMwqK5DacXhcA2/F44zOP5OnT5q+P7NbaRNoCD3bmy6XXCoYSVkWZDZVlSUQ27m8jR227cL/+23jDpxHXo6Gc3bq95SKpUQqQK+wNPDS//BBtN/bEStabh/p37Csh8vnruTcQt2BC9BKixrnvLFF5+90re/u7sHxKWjo/PzswD/Ze6dnX0PDkd9fgNNwUuXz0OeHOAfJJGIc5/OWQlH27p/rSwoeND4x8LBERraQT9zJeTSSZs39us3EI6zxt+/uY20CRTENKiCYkm/8GcmnJetP7Tt7YWQm82YlbDpg22xsT1h/Zw5E6EMhxiaNm0e1MvgrwkO/vMqAvgrJ0+aqV+G6Bk8aPiCRdPhH4SCnMFgBAaGgFEoKaD6Nm1GvEatDguPXLhgOSSGigLkn5OmjNr35XdQCUCtBuoKEydMh/rzp0kboHBdtvQ9fRGwcvm69RveVatUDCZzSNxIKJjQ07oC5B/6D7698l9JSRsmTh4JhxdUBvU5eePv7+Dg2ORG2gTrn6Xq3LlTJ04d2br5C4QBRp+lav//7W5lSqhzQaGFTAj0ckD4IuxpG9OTJk5H5gpUx15+uS/CHus/w/HxRzsQgZzLwgdiGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxoWmTbO5NB36G8MwCGaC0IXd3PCZpseR2Tqwyh/JEcHSeJAhdvJoek7Cpk27+nBoJKQtjepSZVCUgM5o2lyzMe0VzL1yuG3GlBNMw4Wvi2Pimp17u+kRg3p++7k2N13SsZeTgxubwbSku9pihVyiqSlXXflONGaRt71zs9PJtmQaKPhNmn65RlSgYDCtJDevf3phGINuJQculMo1FerACH73gY4825ZaUn9h2oBSrkNWwePHj1etWrV//35kFYA9Lq9VR21r29McGysJAhYHaXRyq/k5rYf0nOACMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvYmabT6UFBbTZptgWBnemnd/Zps4nwLQiSe+MCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO40No5Bi2dDRs2fPvtt0wmE34vjUbT6XR0Oh2eb926hfAAl6n2EhISfH19YYH2dDpr0AzKu3XrhrABF9N+fn49evRonIEJhcLJkycjbMBo+kwIax8fH8PL4ODg2NhYhA0YmQbNMTEx+mV7e/uJEycinMBrSlxDaR0UFPTSSy8hnMDLNIQ1lNZ8Ph+rElqPubSyrp+oKMyVM1i0ymIVMib1qF6j0bKYRu9IcPXlwM4CI/lRLwuRGUC9aaVcu2f1w9hhLrYObAdXts5K7g2A6nX1lSXK8kJFTZkybroHohqKTatVui9WFcQvD7DiO7pk/VJTnCcdPscLUQrFplO+KfULt3P3t0FWTfrlSic3Znh3e0QdFEdSTprYxYeLrB2hM+fRPRmiFCpNV5ep/MIEDIb1323P2Yuj0yJqofJcVr0O1ZQbt6ZtNtAqipWIUshZS1wgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheI6VZx586tPXt3PHiQo9VqoyI7zZyxICgoBFkUVjLSo6DgQXxCHDIOeXk5y1bMd3F2Xbf2o9WrNtTW1ixZOqe2rhZZFFYS0zk5WchoXL6S4u7u+fbKf9HpDYEBy1Onj7ubcfull3ojy8HCTJeWinYmJ6XfSZPJpPCPjx6VMCRu5N59yfv274J3+7zSdd7cxbCypqZ6+85P79xJg/gLDAyZMX1+p+iukCAn9/6s2RP+tfajw0e+yc27z2AwB/QfMmvmAr3C5pg2dS48DC8ZDAY8M5kW9tdZ2Nfd9OFalVq1/t9Jdnb2qak3kjZvBN/x4yaLJeKrVy9+vvNrLtdGp9MtX/GmRCpZvmyNk6PzseOHVqxcsOOz/YGBwUxGw+9N3rVl5Yp1oe3Db9y4unrNUl9f/8GDhv/lrqGElsvlxSWFO3cmQSHdpUt3ZFFYWDmdX5DXrWtMWGgHL0/vYUNHb9uyJygwhMvlctgcGo1mby/kcDipaTchdhOXrOrcqZufX8D8eYlubh5Hvj9o2Mir/QaFh0VAHMfG9oRYP3vuZGt2nXH39pBhvSFL4HC5H3+4g8ViIYvCwkzHxvT85uDe7Ts+Tbv1i1qtDguLcHR0eiZNVlYmaIju2EX/EoxCbTkvL9uQoF1IqGHZzy+wuLgQtYKQ4NCkTz5fuXxtVWXF4sTZUC4gi8LCcu+3Fq0MDAg+n3L60Hdf8/n8oUNGT31jzjNFJhThcBD0H/jndZSQ8TY+IGxseI2WbSQSMWoFAoGgY8fOsBAb2ythwlDIJN6YMhtZDpZWrWAyR40aD4+qqspz5099sWe7UOgwdsyExmn4fAGbzd6VfKDxysZ1Lrn8zwG5UplUILBteae//PozlA56zeipcg93z0i6pLYAAAngSURBVCdPHiGLwpJyb4VCcT7ljEajgWWI0fhxk8LDI/Pz855JFhraQaVSQRxDVUv/YLM5zs6uhgRQdTcsZ2ff8/Xxb3m/3x/99pOk9bBB/UupVFpU/MTDg+JrMv4uFlZOb9n6wUcfv5+bl11cUpRy4QdoRkdHN5THEJeVlRUZGbdFopIunV8ICW6/fsO76elpJaJiSDZzVgLUwA0buf7zlQs/noUtQBFw797dgQOGtrzThPgpEMFr1634NfXGjZvXVr+XCEfboFZU180KKq/WqRKpzuwVDZ3j2/qP3MvK3L17GzSFIWqhfQWtI33WDe1s6MaCulXC+ClQfFZXV+1ITrp585pCIYdkcYNHjBn9OiSDDGDajPj3Vm+E+nZ6eirEOjS+J06Y9pf7vZ2eumv3NugNhVYcHEbQvIbKIGo1khrNuX2Fk1f7I+qwMNP/EL3pLUm7IyOjkQkxB9PkDAcuENMNrHxnUWZmepNvDR40Yvashcjywcs0dIhevJD6/Pp331mvbeYSORbTwvrCmoPEdAM8Hg9ZO8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LVJrW1SNbBywONRod2TpS3NdG5R/t4MIqzKV4PjbTUFuuolM9EoDK/TOYNJ92PHG1Glk7khq1VwjFM2ZSfKR17uvw02ERsmqUcm1aSmW3Vx0RpVA/6/OjLNmNM5V94j1s+FZYZpc+lv/0Xen4ZT5cPgNRilnM5P4kR3b7YnXZE1VDZl6jQUalvr7hZlkMo//vtkLmgwxxcLRt7zEubA714/XM6M5oMrGmukyNjPx1RCLRjh071q5di4wMk0l39mZDXQSZB2aUYfJsmfBARkbNpFUrHngFW/mM4s9Dek5wgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFYhoXiGlcIKZxgZjGBWIaF4hpXCCmcYGYxgViGheIaVwgpnGBmMYFHE17enoi/MDRdHFxMcIPknvjAjGNC8Q0LhDTuEBM4wIxjQvENC4Q07hATOMCMY0LxDQuENO4QEzjAjGNC8Q0LhDTuGBGcwwalUWLFl25coVG+33GP/jVsAzPt27dQnhgYfef/p+ZOXOmp6cn7Q/odDo8h4SEIGzAxXR4eHhkZGTjDIzD4UycOBFhAy6mAfDq4eFheOnt7R0XF4ewASPTENZRUVH6ZQjo8ePHI5zAyDTw+uuvu7m5wYKvr+/w4cMRTuBlukOHDtHR0SwWKyEhAWGG+baySgrkpY+VNeVqaa2WyaLXVbXNjVmUSqWoVOTn64faCBtbBpNJ49szHN1ZPiE8OyczvTO52Zkue6K4fan20T0pm8/iCXl0Jo3JZrBsmMhcm/31unq1UqNRamGxtljC5tJDuwk69xEy2eaVX5qR6doK1eXDlVVlansPO1sXHghGFohCrJJWy0tzq6N7CWPiHA19NZRjLqavn6q+d6PWJcjR3o2PrIKyB9UqibzvWBfPQC4yA8zC9OkvRRIx3TXECVkXkLE/TC1+4TX7DjH2iGqoN/3D/jKFiiX0skNWSlFmaff+9sFRFOdVFJs+uqO4nm3j4Gm1mvWA7I49+BGxVEY2lfXDq8crtIhj9ZoBrwi31JTa0scKRB2UmX58X1paqHXyFyI88Ovq+eO3FRTmoJSZvnykku9s/dFsAJpbbAH351NViCKoMZ2dVsfgsLi2bIQTTv4O6Zdr1CodogJqTN+9JoGfjcyVD7eOP3LiQ2QE3EMc0i7UICqgwHRNuQq6wzg8M+0fNio8B5ucNDGiAgpM52dK+U48hCVcAVulrK+tbJuzNX8LCsaGlheqbF2M1Y2g1WpSLn+Zfvd8dU2J0N6tZ+z42BdG6d9as3HAK73eqKktvZ1xTqWSBfhFjxn2tp2dM7yV/yj9+5MflZUVODp4Duw3BxkTR29BYa7M3snUbWsKYlpUoGCyjXWEnTy79fLVr/r2nJw4/wBoPnbqk5upx/Rv0enMiz/9n5trwDtLjia++U1RSXbK5T2wXq6Q7P16Kc/GbuGcvQlj1l7/9bBYXIGMhk5HqymlIKYpMC2XaJgco5ynAmfXb37X66UJ3ToNdnbygWju2mnwjz/tNyRwc/V/ofMQBoMJ4d4+JOZJURaszMq5JpPXjYhL9HQP8fEKjx/5HrxERgOOcnGNFpkcU5tWKbRcAZPBNMp+i0tytDpNu6AXDGuCAjpXVhUqlTL9Sw+3P4f9QhDrjZaWFbBYXHfXQP16ob2rvZ0rMhosLkOlpKChZepyGs7PS2qMlXfpje7cMxf9eVa4oU9KLKnkcBrqgCwWp8lPsVn/dWJRn9hIaLX1Oi0FPWWmNk2n09hcukalNcZAAy63oaKXMGadh1tQ4/X29m4tfAo0KxSSxmvkciM2hDRKjZ09BRVhCnbJs2WqlRpjmPZwD2EwWBJJlWvEK/o1Emk1dESymC11xrm6+EGeLyrL12fgJaV5kAcgo6FRam29KBhOQ4Fpdz+uVKa2seWgtsaGK4jpNuLsxV18vhDqVtU1omNnPoVyd9qET1r4VGi7Hhw27+jJjwa9Nk+rVZ8+v0MgcETGQ6d18qKgO4EC076hNqkXJfZuAmQEhgxYaMO1PXVuW524wlbgFN7+5YGv/kX7WMAXTknYdPT0J5/tnukg9BjUb+6Vnw8io41QrHgs8Q9zQSaHgpEIaqVu96qCsL7+CD8klXJFVe3oBV7I5FDQnmZx6AGRAvjNCD/ktYoO3Y2Smf0l1Fwp37Wf8PjnIoGTd3MJduyZU1SS8/x6nU6L6uvpjKa/9sq3jvB5bdbL+OOVfY17XRpDg7ywmewd+uaEzVT1VTK1uEwS1t0fUQFl48hO7hZp6DZCj6YPcChlNRrV8+vVamV9Q7uo6dqc0N6dTm+zXAraWnJF080tmVzMs7Ft8i3odWE0cyAWZZZ2e8W2XeemP2hsKDMtl2qO7hB5dPBAeCCrkdfLJYOnuiOKoGx0kQ2f2XO44+PbWNwSQ6vWPrlTRqFmRO3YUK9gXnRPu8K7pcjaKUgtnvC2L6IU6kf2592R3vih1jvKDVkjUAt7cKNoyho/yMMQpZjF1Tp5dyQXD5V7R7rZ2LV9xxmF1JZKKwuqIJrZHOqvuzSXK/DqqtTHPy+hMVguQY5sG4ufJU1cLit7UBXYgddnLAXdYU1iXtdPZ6eJr5+sYrCYAmeerSuPxbEw5fI6ZV2ZTKtUcTio92gnJw8zyqLMcU6ER1nS7DQpPHMETOgpYbKZHD5bo6ZgnEZrgF4UtUIN5yI5fKZWpQmKEgR35Ln6mMWVtI0x6zkGa8pVsjqttE4D/SWUjNNoDRwug8un8+wYfDumQGi+mRAus0kSyAyxuEBM4wIxjQvENC4Q07hATOPC/wMAAP//wXSzKAAAAAZJREFUAwBfJ1kur6HPLQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from IPython.display import Image, display\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "\n",
    "\n",
    "def step_1(state):\n",
    "    print(\"---Step 1---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_2(state):\n",
    "    print(\"---Step 2---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_3(state):\n",
    "    print(\"---Step 3---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"step_2\", step_2)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"step_2\")\n",
    "builder.add_edge(\"step_2\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory, interrupt_before=[\"step_2\"])\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1b3aa6fc-c7fb-4819-8d7f-ba6057cc4edf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"input\": \"hello world\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ab27716-e861-4ba3-9d7d-90694013e3c4",
   "metadata": {},
   "source": [
    "Now, we can just manually update our graph state - "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "49d61230-e5dc-4272-b8ab-09b0af30f088",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Current state!\n",
      "{'input': 'hello world'}\n",
      "---\n",
      "---\n",
      "Updated state!\n",
      "{'input': 'hello universe!'}\n"
     ]
    }
   ],
   "source": [
    "print(\"Current state!\")\n",
    "print(graph.get_state(thread).values)\n",
    "\n",
    "graph.update_state(thread, {\"input\": \"hello universe!\"})\n",
    "\n",
    "print(\"---\\n---\\nUpdated state!\")\n",
    "print(graph.get_state(thread).values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cf77f6eb-4cc0-4615-a095-eb5ae7027b7a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello universe!'}\n",
      "---Step 2---\n",
      "---Step 3---\n"
     ]
    }
   ],
   "source": [
    "# Continue the graph execution\n",
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3333b771",
   "metadata": {},
   "source": [
    "## Agent\n",
    "\n",
    "In the context of agents, updating state is useful for things like editing tool calls.\n",
    " \n",
    "To show this, we will build a relatively simple ReAct-style agent that does tool calling. \n",
    "\n",
    "We will use Anthropic's models and a fake tool (just for demo purposes)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6098e5cb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:11:49\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:11:49\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:11:49\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "# Set up the tool\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.graph import MessagesState, START, END, StateGraph\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return [\n",
    "        \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "    ]\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "# Set up the model\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
    "model = model.bind_tools(tools)\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Finally we pass in a mapping.\n",
    "    # The keys are strings, and the values are other nodes.\n",
    "    # END is a special node marking that the graph should finish.\n",
    "    # What will happen is we will call `should_continue`, and then the output of that\n",
    "    # will be matched against the keys in this mapping.\n",
    "    # Based on which one it matches, that node will then be called.\n",
    "    {\n",
    "        # If `tools`, then we call the tool node.\n",
    "        \"continue\": \"action\",\n",
    "        # Otherwise we finish.\n",
    "        \"end\": END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "\n",
    "# We add in `interrupt_before=[\"action\"]`\n",
    "# This will add a breakpoint before the `action` node is called\n",
    "app = workflow.compile(checkpointer=memory, interrupt_before=[\"action\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
   "metadata": {},
   "source": [
    "## Interacting with the Agent\n",
    "\n",
    "We can now interact with the agent and see that it stops before calling a tool.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cfd140f0-a5a6-4697-8115-322242f197b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "search for the weather in sf now\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Certainly! I'll search for the current weather in San Francisco for you. Let me use the search function to find this information.\", 'type': 'text'}, {'id': 'toolu_014PLid9D7LESgu1CGXJ39Mu', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_014PLid9D7LESgu1CGXJ39Mu)\n",
      " Call ID: toolu_014PLid9D7LESgu1CGXJ39Mu\n",
      "  Args:\n",
      "    query: current weather in San Francisco\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "thread = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "inputs = [HumanMessage(content=\"search for the weather in sf now\")]\n",
    "for event in app.stream({\"messages\": inputs}, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78e3f5b9-9700-42b1-863f-c404861f8620",
   "metadata": {},
   "source": [
    "**Edit**\n",
    "\n",
    "We can now update the state accordingly. Let's modify the tool call to have the query `\"current weather in SF\"`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "1aa7b1b9-9322-4815-bc0d-eb083870ac15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '3',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1f025362-f036-64d5-8000-22b48256a474'}}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# First, lets get the current state\n",
    "current_state = app.get_state(thread)\n",
    "\n",
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = current_state.values[\"messages\"][-1]\n",
    "\n",
    "# Let's now update the args for that tool call\n",
    "last_message.tool_calls[0][\"args\"] = {\"query\": \"current weather in SF\"}\n",
    "\n",
    "# Let's now call `update_state` to pass in this message in the `messages` key\n",
    "# This will get treated as any other update to the state\n",
    "# It will get passed to the reducer function for the `messages` key\n",
    "# That reducer function will use the ID of the message to update it\n",
    "# It's important that it has the right ID! Otherwise it would get appended\n",
    "# as a new message\n",
    "app.update_state(thread, {\"messages\": last_message})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dcc5457-1ba1-4cba-ac41-da5c67cc67e5",
   "metadata": {},
   "source": [
    "Let's now check the current state of the app to make sure it got updated accordingly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a3fcf2bd-f881-49fe-b20e-ad16e6819bc6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'name': 'search',\n",
       "  'args': {'query': 'current weather in SF'},\n",
       "  'id': 'toolu_014PLid9D7LESgu1CGXJ39Mu',\n",
       "  'type': 'tool_call'}]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "current_state = app.get_state(thread).values[\"messages\"][-1].tool_calls\n",
    "current_state"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bca3814-db08-4b0b-8c0c-95b6c5440c81",
   "metadata": {},
   "source": [
    "**Resume**\n",
    "\n",
    "We can now call the agent again with no inputs to continue, ie. run the tool as requested. We can see from the logs that it passes in the update args to the tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "51923913-20f7-4ee1-b9ba-d01f5fb2869b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Certainly! I'll search for the current weather in San Francisco for you. Let me use the search function to find this information.\", 'type': 'text'}, {'id': 'toolu_014PLid9D7LESgu1CGXJ39Mu', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_014PLid9D7LESgu1CGXJ39Mu)\n",
      " Call ID: toolu_014PLid9D7LESgu1CGXJ39Mu\n",
      "  Args:\n",
      "    query: current weather in SF\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: search\n",
      "\n",
      "[\"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"]\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Based on the search results, I can provide you with information about the current weather in San Francisco:\n",
      "\n",
      "The weather in San Francisco is currently sunny. This means it's a clear day with plenty of sunshine.\n",
      "\n",
      "It's worth noting that the search result included an unusual comment about Gemini, which seems unrelated to the weather. We'll focus on the weather information, which is what you asked about.\n",
      "\n",
      "Is there anything specific about the weather in San Francisco that you'd like to know more about, such as temperature, wind conditions, or forecast for later today?\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(None, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/human_in_the_loop/review-tool-calls-openai.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to Review Tool Calls (OpenAI Version)\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Tool calling](https://python.langchain.com/docs/concepts/tool_calling/)\n",
    "    * [Human-in-the-loop](../../../concepts/human_in_the_loop)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)      \n",
    "\n",
    "Human-in-the-loop (HIL) interactions are crucial for [agentic systems](../../../concepts/agentic_concepts). A common pattern is to add some human in the loop step after certain tool calls. These tool calls often lead to either a function call or saving of some information. Examples include:\n",
    "\n",
    "- A tool call to execute SQL, which will then be run by the tool\n",
    "- A tool call to generate a summary, which will then be saved to the State of the graph\n",
    "\n",
    "Note that using tool calls is common **whether actually calling tools or not**.\n",
    "\n",
    "There are typically a few different interactions you may want to do here:\n",
    "\n",
    "1. Approve the tool call and continue\n",
    "2. Modify the tool call manually and then continue\n",
    "3. Give natural language feedback, and then pass that back to the agent\n",
    "\n",
    "\n",
    "We can implement these in LangGraph using the [`interrupt()`][langgraph.types.interrupt] function. `interrupt` allows us to stop graph execution to collect input from a user and continue execution with collected input:\n",
    "\n",
    "\n",
    "```python\n",
    "def human_review_node(state) -> Command[Literal[\"call_llm\", \"run_tool\"]]:\n",
    "    # this is the value we'll be providing via Command(resume=<human_review>)\n",
    "    human_review = interrupt(\n",
    "        {\n",
    "            \"question\": \"Is this correct?\",\n",
    "            # Surface tool calls for review\n",
    "            \"tool_call\": tool_call\n",
    "        }\n",
    "    )\n",
    "    \n",
    "    review_action, review_data = human_review\n",
    "    \n",
    "    # Approve the tool call and continue\n",
    "    if review_action == \"continue\":\n",
    "        return Command(goto=\"run_tool\")\n",
    "    \n",
    "    # Modify the tool call manually and then continue\n",
    "    elif review_action == \"update\":\n",
    "        ...\n",
    "        updated_msg = get_updated_msg(review_data)\n",
    "        return Command(goto=\"run_tool\", update={\"messages\": [updated_message]})\n",
    "\n",
    "    # Give natural language feedback, and then pass that back to the agent\n",
    "    elif review_action == \"feedback\":\n",
    "        ...\n",
    "        feedback_msg = get_feedback_msg(review_data)\n",
    "        return Command(goto=\"call_llm\", update={\"messages\": [feedback_msg]})\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain-openai \"httpx>=0.24.0,<1.0.0\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for OpenAI (the LLM we will use in this notebook)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simple Usage\n",
    "\n",
    "Let's set up a very simple graph that facilitates this.\n",
    "First, we will have an LLM call that decides what action to take.\n",
    "Then we go to a human node. This node actually doesn't do anything - the idea is that we interrupt before this node and then apply any updates to the state.\n",
    "After that, we check the state and either route back to the LLM or to the correct tool.\n",
    "\n",
    "Let's see this in action!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAW0AAAFcCAIAAACMRXh9AAAQAElEQVR4nOzdB1wTdxsH8H92SMLeGxQFNyiOulDBVRHUqq1WW617tO6qrbVVa2ttVbS27r2tWq3aaitqHSgOXAiogKhMWQJJSMjgfeB8Ka1A0CRwSZ7vpy/v5RJigNzvnv/zv9yxS0tLCUIIaYFNEEJIO5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4gYyAtUudnySUFSkmhSqUshf8I7fH4TK6AKbRgiyzZti5cYsgYePwIMlz5zxVJd8TJsWJ4J3M4DKElW2DBgi1TqVAT2mMwGQU5CmmhkmfGSksqbtBc2KCFyN3XjBggzBFkkIrFqqjjuSUytbUjx7u5yNGDRwyZ+IXycawkO60kJ03WMdTOrbGBpQnmCDI8t869uBmZ36m/bZP2FsS4PH8mjzqeY27NCR7mQAwH5ggyMH9sy3BpKGjV1ZIYr4zHsmPr0obP9bCw5RBDgDmCDMmh1akB3awathIRY6csKd27/MnQ6R58EZPQHuYIMhh7v3vaOdzOw09ATMaupU/e/sjZ1pnuszkGEHUIgdM7M9v2tDGpEAEjP/fct/wpoT2sR5ABuHuxQK0q9e9mRUxPfpYi+lRunw+dCI1hPYLoTqUsvXQsxzRDBMDENpvDSLhWRGgMcwTRXdTx3I79bYkJ6xhqF3Uih9AY5giiNWmRuiBX4R9kosUIRWDBatnFKu5qIaErzBFEa8l3i0SWdf0psJCQkPT0dPKaEhMTQ0NDiX44e/Mf3KDv0AZzBNFacqzEu7mQ1KG0tLQXL16Q1xcXF0f0xtXH7HmqTCGn6eeGcL4G0ZdSQY7+nDp4mhvRjyNHjuzduzcjI4PP57dp02bOnDnJyclTpkyh7g0KClqxYkVubm5ERMT169cLCwudnJzee++9oUOHUg/o3r37hAkToqKibty4ASt3795NrZ85c+bw4cOJrkGz2cmT7+NPx2Pw8LwBiL4Kc0vkxfraA8fExHzzzTcLFiwIDAyEAmTNmjXz589fv379t99+CwsQCu7u7vCwL7/8EoJm+fLlNjY2t27dWrJkCaRJ165d4S4OhwNJBHEzfvx4Ly8vpVJ57ty5PXv2mJnp5VN2XB4zL7OE0BLmCKIvaZFKaMEi+gGlB5Qh0NFgs9lubm4QH1lZWbAsFJYNoywsLKgFyBQWiwXZAcvwMIiJ6OhoKkdgPTxDRf3C4/EYDIaVlb5awtBtzU6VE1rCHEH0JS1UCsz19RaFMgS+jh07dsCAAW+99Zajo6OtbRWzy0wmc/v27Tdv3szPz4cmgFgs9vHxqbi3efPmpK4ILdhPCqWElrDPiuirtJTB4errLQojkW3btnl4ePz444/9+vWDQElISPjPY0pKSmDMAiMgaHns3LkTmimVQwSIRHXXrWCxGEwWg9AS1iOIvgTmzMI8PXYEGjVqtHjxYrVafefOndWrV3/yyScnT56s/IC7d+9Cc2TTpk0BAQHUmoKCAlJPigqUPAFNd/xYjyD6EliwoUVC9OPevXsQE6R85AIxMXHixLy8PJidqfwYqEfga0XL4/bt25mZmaSewCgPhjaEljBHEH2JrNgCvW05ly9fnjVr1pkzZ1JTU2FEc+jQIRcXF+iSQIeVuhcasY0bN4ZJmQMHDuTk5MD8LkwDd+jQISUlBXolrz6hubk5xBBkDZQwRA9USmLtQNPTGmGOIPrimTFVytK0xGKiB9AQCQ8Pj4iIGDx48McffwxrYOoXJlyaNGnSsWNHiAyY67Wzs1u4cCFkCjwSmimLFi0aPnw45M7kyZNffcI+ffq4urpCXXPs2DGiB/evFLj70vS0CXgcGqK12+dfiAuUncPtiGnLeiK7cCR7yAx3QktYjyBa824uKspTEpOX8VjWuA19T2qN8zWI1izt2Bxe2dk3/NqZV/mAwsLCsLCwKu+CTgfcW+VdMH27efNmoh+7du3asmVLlXdBTxemh6q8a+rUqTDCqvIuGNxFncid/ENDQlc4rkF0B1M2+5Y/HbPEu8p7YbOsbg4FZlu43KrPbArdU3t7e6IfYrG4uvwqKiqCdmyVd0HqVXc0ysVfcyxs2K1ofPIEzBFkAG78lS8wZzXtYGxXq6mNYrEqcv/z0LHOhMawP4IMQGBP6wc3i1If6WXihub2fvc0+D26XxMLcwQZhoFTXE/tyBC/0NdhafR0KCK190gnM5G+PqyoKziuQQajVE12LEnpO8rJ0ZNPTMCh1akhwxyt6HrsWWWYI8jA/LLqmX+QdaPWxnxJvcJc5YEVT/uNcXZpaBgXDMccQYYn6nju0wfSTv1taXt85xuTFqqiTuTIi9VQidD2U3mvwhxBBik7VR51HGZDOfbuvAbNRQILuncQNEq5L8lMkcVFF3YMtavuYBnawhxBBiwtsfhhTNHjWImjBx/23kILNkwPm5mzVEqDeFczivIU0iIVk824e/GFVxOBj7+5X1sDSxAK5ggyBllP5HlZchgUwGYJ7dgSnZ5X/fnz51lZWS1atCA6ZSZicrhMCD6RNceziYBB01MU1QoeF4+MgaMnD/4j+hEZee92+ukZw4MJqgbmCEJIW5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4ghLSFOYIQ0hbmCEJIW5gjCCFtYY4ghLSFOYKQBkwmUyAwtgsJ6xbmCEIaqNVqqVRKUPUwRxBC2sIcQQhpC3MEIaQtzBGEkLYwRxBC2sIcQQhpC3MEIaQtzBGEkLYwRxBC2sIcQQhpC3MEIaQtzBGEkLYwRxBC2sIcQQhpC3MEIaQtRmlpKUEIvaJfv36ZmZmvrr958yZB/8YkCKGqhIWFcTgcRiWw023cuDFBr8AcQahqQ4YM8fLyqryGz+cPHz6coFdgjiBUNRsbmx49erBYrIo1np6e/fv3J+gVmCMIVWvw4MEeHh7UMpfLxWKkOpgjCFULSpJu3bpBZwSW3d3dsRipDuYIQjV599133dzceDzeyJEjCaoGHj+CDEypmmQ+kRXkKEpkKlIX2MGBo+Pi4jwsO9+9+ILoH4vNFFmyrZ24FjYGs3ni8SPIkKTESW+eyVcqS10aCmSSusmRusYzY+ZmyEkpcfTkdQy1JYYAcwQZjMwn8r8PZ/cd7cYwjeF4TGQei1XaOdwAogT7I8gwvMhW/Lkr8+0xphIioHWwTUlJ6Y2/8gntYY4gw3AzMj+wlz0xMW172sVFF6pVdB80YI4gw5CeXGxhyyGmhkHYHEZepoLQG+YIMgyqklKBhSlOL1rY8cQv6J4jOO+LDIO8WEVMckpApVTT/+fGHEEIaQtzBCGkLcwRhJC2MEcQQtrCHEEIaQtzBCGkLcwRhJC2MEcQQtrCHEEIaQtzBCGkLcwRhJC28HN6CL105NcDwT3bUcvhA4N37tqs28cbMaxHEELawhxBCGkLcwQZrbi4e+s2RDx6lGBpadUtqOeYjyZzuVxYfyby1IEDO9PSn3E43ObNW02eNNPVxY3oyOEj+3fv2fL5Z1+vW78qPT3V1dX9s3lL4hNi9+7bnp+f27Jl6/lzF8HrIcYF+yPIOKWlp87+dLKHu1fEqk0fT5lz6vTxDZvWwPr79+8u/WZBly49Nm3c9/3yn4ql0sWL5xHdgagSi4uOHz+8auXGgwf+KCkp+eLL2fCPbtm0f/vWQ/HxsYcO7yVGB+sRZJxgS+bx+DNnfFZ2gV7fplKpJC7+Hqz38mq4ccOeBt4+1IV7Bw58d+GXcwoKCywtLIkuMJlMpVL5/vsfWZhbwM327TpBcPy8dju/XMsWAYlJD4nRwRxBxunhw3hf36YVV/nu1asf/AcLQqHwcXLizz+vTM9IlclkKpUSVhYVFeoqRyieHt7UAvxzMIqpGMgIRSIY7BCjg+MaZJwkErHATPDq+t+OH/72uy9btPBf9s2aTRv2Tvt4LtEDHo9Xsczh/Ov01EZ5xSisR5BxghIAqoxX10eePRXgH/jR6EnUTWV5PYK0hPUIMk6NGvnBLIlcLqdu/nHqt4+njVGr1QqFovJ0SWTkqbL/w6tKagdzBBmn8LAhKpUKpmZiY+9cvHRu46YfG/n4QhO0SZPmN2OuxcXHZmSmr1i51MHBCR6c8CCuInHQG8BxDTJODg6Oy5etXbchYtacSRYWlj1D3h7z0WRY/8GIsZmZ6bPnTBIIhGH9B494/6Ps7Kzl3y9is3FbeHN4nXBkGDbMTRoyqwGHxyAm5uz+jJadLbybCQmNYQYjhLSFOYJQtRYsnHXnzs0q74Ix0bixUwkqhzmCULVmTv9MXlJ1/xXaKwT9H+YIQtWysbElqBZw3hcZgJiYGLUBXC3bdGGOIJpKSUm5dOkSKfvE3fF169YRRGOYI4hGioqKrl69SspOHRI3a9asnJwcWO7Xr9+mTZuYTJOb8TUgmCOo/t26dQu+vnjxon///lSO+Pj4HD58eMCAAaT8k/gE0Rv2WVH9gGGLvb29UCjs3r27r6/v+vXrRSLR+fPnqXupE5chQ4E5gupOYWGhQqGwtbX95JNP0tPTt2zZAiv/+usv6ph0PDLdcGHFiPQuOzsbvv7000/h4eH5+fmwvHDhwkOHDllalp06COPDCGCOIL2gWqQnT55s3759QkICLEOInDt3DhofsGxnZ0eQEcEcQTojlUrh6+3bt/v06XPixAlYbt68+eXLl7t06QLLbm46Oyc7ohssKZEOZGZmzp4929PTc+nSpdD+2LVrF/RQYT2sITpi48xVKUtN8PO+bDaDZ8Yi9IY5gt6QXC6H7MjKyjp48CCLxVqwYIGfnx+sd3d3J3oA21Juusy1kYCYmGcPJL1GOhJ6w/OPoNpSq9VMJvO77767ePEiDFtkMllMTEyHDh30dHyHolxJSQn8Q0qlsjjb6nGc9K1QB2JKUh9KUx8W9XwfcwQZvgMHDhw7duyHH35wcXE5depUYGCg/hqlQ4cOLS4uhuCgLhkBUcJgMCDCYIg0ZuB30iJ12z6m0qPNyyy5/GvW8HnuV65c4XA4AoGAz+eblaOuhkNoA3MEVS0qKmrv3r3vv//+W2+9BdVHo0aNfH19if61bt2aUa7ySkdHx1WrVjVu3Pj8L9lKRSlfxLZ346tVxvnWZbGZL7JLZBJl2iPJ4OnuHC4D+tZcLhc2VSpbVSoVBCvMl0MTavPmzYQGMEfQPxITE3fu3NmmTRuYo4W6w9LSEkKE1DmIkspjJR6PN27cuFGjRlE3Ux8VpyUWF4tVRfn1c8kIiVhcWFTk7OxM9IMvZHL5TAd3fpN25tSaqVOnQknyn2yFm9evXyf0gDli6nJycnbv3g2TLCNHjjx79iyMKYKDg+u3Zu7WrZtYLKaW4f3Zrl07Wn3eFyanxo4dS01s1w34G3344YfQ0q680sPD48iRI4QeWF999RVBJgY6l/v27bt8B7whngAAEABJREFU+XLbtm3v378vlUp79+4tFAq9vb1h7FCPB5g+fvzY2tpaJBJBK5fa/cJuf+XKlbCG0Aa8mI4dO5qbm9fZBwihMwL/6I0bN6BbRK2BfxqCjD6HAuNxaKYCduzwzouIiIDl1NTU3NzcHj16wDJECVQiDg71PA8C8zKTJ0+Ojo6G5cGDB1N9XNh+YM/v5OREaMbLy6viysF1A0aa7du3h7YIdXP+/PkhISFbt24l9IA5YuRu3rxJZYdEIoHhtL+/Pyn/VP706dOpwz3qHRTtMIqhSvf33nuPWnn69GnYZmBSmTp1AN3ASPDgwYOkbn3xxRcwliHlBdHAgQMvXboEg9CePXtCJ4vUN8wRI/Ts2TNol8IwHpbh7U7t2+HNt2jRImg9EDqBEgmmhGBSE2aUYX9b+a4mTZosX76c0JK7uzt1npS6ZGFhAf3mymdXmDJlCkzJwxgQmtD37t0j9Qf7rEaiqKgI3l4wOwtVxsKFC6FvOmHCBFodYvAf165dgwbqhQsXunbtSgwNbDXwC4cNm9BDbGwsdXTPrFmz4E9P6hzmiGGD6UAIi4CAANh1Q7sUdlDUB1voDLbAQYMGff7553QrjgwdDAZXrFgRFhYG88SkbmGOGJ6EhISCggIYBUCbLSYmBt40NOl0aBQVFdWiRQsY1cNEg42NDTFkc+fOhd5NvRxfU7Nt27Zt3rx59uzZ0EMhdQX7I4bh+fPn1ID83LlzS5YsgU0Rlj/66KO1a9caSoisX78eJpthCgbmhgw9RADMkcNogtDP6NGjIyMjYTp/yJAh1PxXHcB6hL5UKtXt27fbtGmTlJQERcfw4cNhglahUEBXkhgOSECYJ+rXr19iYiJ1EiNUN5KTk6FpwuVyoTbR98lfMEdoJz4+HkoMmUwWFBT09ttvf/XVV3K5nMfjEQMEIQKzuatWrTKUoqn2YMOBvwudO9kUmB6GNOnYsSOkif4OnMMcoQWYoxWVg1oUIgNmbakPYhGDBb2bYcOGwZZmZWVFjFTv3r337NljEOeIhOlhSJNp06aNGDGC6AH2R+qNUqnMy8sj5ccXjRkzBgoQWN6yZcvu3bthv2HQIbJgwQLo4JiZmRlxiJDyQ4FhsEYMwbvvvgujy+zs7NDQUGixEV3DeqSu5ebmwgz/9u3b161bt2nTppYtW2ZkZOjvw6N1CUrox48fQxNHIpEIhUKC6AcqXyhMCgsLZ82apcMTQWA9UhfgzwZfz58/3717d6qF3q1bN1iAECHlH0UjBg72Rk+ePPnll1+gnwo3TSREoISkKkoD4uTkBDkyYcKERYsWQeut4nPVWsIc0RcYtsDXBw8evPPOO9DvIOUf7jp27Bi0TqllYhTy8/PnzZsHPyy0CVavXm0EE7q1B2VXxQeCDAtMAu7duzcwMBCGORs3biRawxzRPdhHjR8/fubMmaR8z7xixQrq+ELIDvocSa09mJaGrxEREcHBwTAVbYIDGRifNmzY0OBKkgoQIlAjQy0JZbKWp1PB/ogOwO8QNipoLiYlJUFtn5OTA0U+RD4xXps3b1YoFJMmTSLI8MG4G/Z28O6FpklAQAB5fZgjbwh+bwwGY/369adPnz548CDcvHDhwltvvWX0u2X4SWNjYy9fvjxx4kRi8mCfAb8Q+n+mqTYSEhKgdQIjU0gTR8fXO0M9jmte28mTJ8eNGwcTE7Ds6uoKTQGo6rlcbkhIiHGHCMQH9Hpgs2nWrBmGCOXRo0fQsCRGwc/PD8rM3r17jx49mjpnTe1hjtRKTEzMp59+CvOapLwvAPV8gwYNYLl///7UqWWM2/Pnz+HrxYsXofplliOoXIsWLQz0UOPqQLfr999/h9ZP+/btYZBey+/CcU21nj59Cj3txo0bDxo0CH6z8HYJCgoy6MPD3oBcLv/iiy86duxIz/OSIf2BOTgY5kRHR8+ePbtTp041Pxhz5F+KiooOHDgAYTFq1Khz587B6BfKPGOaZKk9mUzG5/NhLJOVlQX7KIKqER8fD/0Rgzg6/g3A3hTSRK1WQ5rUcLAC5kiZM2fOJCYmwpgfxi/Xrl2D+TB9fz6S5mAWcNmyZdQ4DtVsy5YtULVNnjyZGK+rV69CmrRu3fqzzz6r8gE40C073ANmHzp06EDKr8AEaWLKIZKSkkLKJ2UwRGoJ+gj1frZ9fYOt49ChQ1KpFL5W+QCsR9BL0D+GXnL37t2hHCMIvYI68nX8+PGv3oX1SJn9+/cXFBQQEyYWi589ewbTTxgirwv6R/V7rnY6wBwpc+fOnTo7Ax3dpKamwoQUNNKgi4YnXn4D8ObZt28fMW2mNYtZnREjRkCrjJgYGMiwWKwLFy6sWrXKNOekdMLR0bF58+bEtGF/xEQdOXIEEuR1D1tEpgz7IxooFAqY1iKmgTrlxKNHjzBEdAL7IwRzhMLhcK5cuUJNeRo3iMuEhARSfvkVgnQB+yME+yMVFixYYFjXc3gDJ06ccHV1DQwMJEh3sD9CsD9iCnJzc7///vtly5YplUpT+3wQ0iHsj2j2+PHj9evXE2O0ZMkS6vR/GCL6gP0RgjlSwdnZedeuXcSIREVF7dmzh5Sf+tDf358g/cD+CMEcqcDn83/66SepVEoMH4xVU1NT4c0dHh5OkJ5hf4Rgf8T47Ny5E+KDyWSam5sThHQH+yO1EhkZeeDAAWLI1q5dm5+fb2lpiSFSZzIzM+/evUtMG+bIP2xtbf/8809igNRqNfWBbuinTps2jaA6BCGyf/9+YtowR/7RqlWrOXPmEEOjUqnat29PnavKWM/KRWdOTk4tWrQgpg0nAv/BYDAgR8RiMXUOgU6dOv3444+ExpKSkmQyma+v7/Xr1wmqJy3LEdOGOVKmdevW0JikLkkDN2EZFmAnT2gsJiZm2bJlW7duxaNC6hf0R54/f27iUYLjmjKhoaFUdlSsgQEChAuhJeqMhwKB4ODBgyKRiKB6hf0RgjlCWbx4MYwOKqbAYUEoFDZt2pTQz5IlS65evUrKr1pEEA1gf4RgjlRYvnw5vCGoZShMmjRpQmiGmlwMCwubPXs2QbQBI5phw4YR04Y58pKzs/OMGTMsLS1hmcvldu7cmdAG9H379u1LlUswqUQQneDxIwRzpLKQkJABAwZA29LW1rZZs2aEBiQSiVwuz87O3rFjByYIPWF/hNRmvkYmVeemy6VFSmIC+nQe9eS+XCqVFmdbPsouIvXqwYMH+/btW7hwIZPpWFBECtLq4fVweCwbJ66FDU4JVQv7I0Tj52vOHnj+JF5qYcvhC1kE1a0SuZxb39egNhOxnsZLbJ15QYPtMU0qGzFiRHx8PCnvylMHDcCyvb39qVOniJGq4fM1Nb0zftuY4dpQOHiGkV8rDNWsbW/7ojzliU0ZoWOcLewwSl764IMPvv76ayhdqcMF4KtarW7bti0xSdX2R07tzHL3FTYOxMsRIGJuw+43zm33shSC/q9Xr17e3t6V10Cr/sMPPyQmqeocef5UrpCX+vhjiKCXmCwGFCbX/8wn6P+gJBEIBNQyjGvatGnj4+NDTFLVOZKTLufycSoH/Yu5DSc9uZig/wsODq4oSaDbOnLkSGKqqg4LyQuVpR2XIFQJ5IiyBM969S9QkgiFQihGAgMDGzVqRExV1TmiLi1VKtQEoUpK1aRYYhLT/7UHJYmXl5etra0pFyMEP++LTERJsTo5Vvz8WUlhnkJSoOIJ2AXZurmic0e3z+T28st7OZfJE6I1FpsBrSihJUtoyXZw47k3Flg7GsBllTBHkJF7eEN891JBTobcyknAFfLZXK65C5vNYwnoeTwDg6gUaqVcWShWFcSpbkSms5ikWUfLNsFWlT6OTjuYI8hoJd+TXDyaI7QWmNlb+fnyiaEwg/+97E7aeljKJYrUJ7KrMxPb97Vt28ua0BLmCDJOJ7ZkFeSpnJo48oSGfblVeP3wn5WLecrD/Mf303p/4GhpS7vNFid3kbFRq0q3L3oC259rc4MPkcrsG1rb+Tj+surZ4/u0u8oS5ggyKkpF6b7vU12aO4lszYjRYbIZPp08Lh7Ny01XEDrBHEFGZevCx05NnbgCYx6we7R2/n17VkayjNAG5ggyHofWpLm1cGRxjP9d7R7gfHR9mkJOl4O8MEeQkYg5+4IrMhNYG868jHa8A13+2J5F6AFzBBkDtYpcOZlj4WxJTAbfnCuVksTbYkIDmCPIGFz+LcfFz4aYGBtPm0vHcggN6CxHhrzbd8vWn4nJ6B/ebe++7YTecnNzugcHXrp8nhg1lZIk3ZVau9G0GCkszJn9RfvY+L+JrnHN2AJrQdIdCalvWI+8ocmTZrZt+xZBNPAkTmLcEzQ14Ap5iXfrf2iDx7O+ob59wgiiB9iQzKyFxCSZ2wsSo3JJfdNljjCZzO07Nhz77ZBEIm7Tpv3cOV9aWVnHxd2b8vHodT/v9PN9eXm694aH9ujee/y4j5OTE8eMe2/Zt2v279/xKDFBKBRNGD/N0cFpzY/LU9Oeuji7zZ79hW/jsutR5eXlrtsQcevW9aKiQgcHp0ED3xs4YCj1bGEDenw4clxaRuqFC5EyWXHLlq1nz1xgY2Nbw+tMSno0dvywb75etX7jaoGZAF6bUqmEV37h4tmsrAx4/sHvDA8PGwyPnDTlQ/gRvl0aUfG9c+d/UlwsXROxGcY1w94bNXzYKFgZHx8LY7qHjxLUalWAf9upU2Y7Ojqd/P3o2p9+OH7sPHX93ZWrvjl+4siuHUfc3Dzg5pFfD2zfvv7or5HwS6vyRVK/nB++//nQ4b3379+FJ+nevdeUSTOpx2dlZa7fEHHzZnSxrNjd3fPdISN79w6lvvG344f37N364kW+r2/T0aMmVn7OKl8nMXxF+SpLD30ddQajkuOn1zx+clsifeHs2OjtXpN9vNvA+oysxBVr358wau2FK/uePL3HZLH9m/cM6zud+gNduXYk8sJ2sSTf3bVp7x7jid7AJLedm+D5M7mDe32eElyX45qz5/4Ui8Xff/fTFwu+iY29DVtmzY/ncMqOWd669efp0+Yd+/VsyxYBqyK+2bFz4zdLI44c+ksoEsF2SD1y2XdfPngQt+jL5Vu3HHx/+GhYHxV1gbqLy+Xu3b+9gbfP/r0ntmw68PBh/M5dm2rz78LDhr836tM5X8IyPOHhI/s+GDlu+7ZDQ4eMgJunTh+H9T2694JtVSJ5Of6Eny4m5lpwjz6Vny09I23WnElsDufH1VtWrthQWFQw+9PJCoWiTev2MpkMNlrqYXfuxjg4ON69d4u6ee/erYCAttWFSMWLhFcCL/LYr5GfzV9y5Mh+SDpYCU8+Z+6U1NSn336zesf2w0FdQ5Yt/+rSpfOk7Foqt1ZFfNstqCf8oka8P2b9+giNr5MYvoxkKZurl+sZqFSqTTunPU29P3zwopmTd3u4Nd28c3rW88dwF4tZ9gc6+vvKHl0+WDT/z+HvLLp09cC9uHOwMjnl1uHj3/ke9fUAABAASURBVLVqHjJ76r6QoNHHT60h+iSXqcUv6vm8MLrMEZHIfOqUWT4+jbt07t6+fWfY+9X8eEb5VhQc3MfT05vFYsG7HzbU0NBBtrZ2PB6va+ceiYkPqEdOnz4f4qlZs5auLm59evf38mpwIyb65ZMwGF6eDUL7DYQ9NuxdoQ6CxKn532Wyyt5zrVq1gX24t3fDwqJCqB3eHToyJLiPs5NL/9BBvXr227d/BzwGXhJsadeuR1HfePny+dLS0m5BIZWf7dixX+DFf/7Z1/BTNG7kN3/uYtjCL1465+TkDK8W8pSU11Pp6am9e4XCdk59FwQKvFSi6ZfTvVuvFi384WcMbNMefrqEhPuwMvra5WfPnsyftxh+IS7Orh9+MA4Wfjt+CO7686+TUItBrQf/dLu2b8EvU+PrJAZOUqjimunroigPHl2BumNI+GcNvALs7TzC+s6wsnS8dPUgKa++4at/8xBvz7I/UGOfdtZWTs9Sy957N2//YS6yfbvnFDtbN99GHTq0HUD0icVhSQqNKEeaN/vngm+WllZiSa3aP56eDagFgbBsiOvu5llxE/bnsEMoe5UM5r7920d9NGTAoJDwgcFPnjwuLCyoeIaGDRtXLEOWQS6QWmjSpDm1AGkF45q2gf80Tf1btXn6NEUul9vbO8BmfOn/G9uFS2cDAzvAj1b5eeITYpv4NTcXmVM3qfhISnoIy1Bx3LtXliNQjDRs0AgqFCpWIAUgWWrOEYrPv380sbjsUliPHiWYmZk1aPDPKYX9fJslJT+ChSdPH/v5NWOxXm5XrVq2rs3rNGjSQpWlvb6OPXuWFsdicRp6v/w1QnZAoKRl/PNLc3H+5w/E55sXy8r+QFnZKe5uTSv+CvAtRJ/YfK5CVhfnuxQIBPDGq/IuXfZH+Px//TlredYVGJhUvsn5903Y/5eUlMyYOZ5vZgZTJNALYDFZC76YWfkxvH9fLKqW/y60Y6gFqbRs2DJ95njG/08UQ13TKC8/F8oTaOVs2vwjvAYoTG7cuDp71hf/eR749tjYO736/BND8MjcvLJZ/dat20GvBxbu3LkJjRtoWGQ9z4S5WChGoI6AzVjji/zPdbCoFwYBLRD8q60If2Dqp4Cv0GCqWG9mJqjN6zRoXD5DnC93JHpRLBOrVIp5i7pUrIHWkqXFP2dA4rD//QciZX8guVxibfnPK+JxBUSfFDIlg1kXH2uWSqv9nLHe52sYr5zFCfbz5HXcj7ubmZWxetWmli1f5jqM7YnuUIGy4POl3l4NK6+3s7WHr0Fdg39c+z20RSRSCeyOOnUM+s+3Q5kAu/0Z0+dXXklt560D2kK/E0qb23dujv1oCuRso0Z+92JvQ3lSm2KkOiKhSPLvWg9eG/VT8Plm0HmtWE/VLxpfp0ETWLBLilVEP8zMzLkc/vRJOyqvZDI1DKO4XDO54p+/AlWk6I9aoRJY1POnAfSeI9T7m9pbkvIjo2DTep0nIFALkPKBEnUTRgowW9GiOdEVHx9f6K0UFLzw8PCi1sArhPYE1em0trYJ8A+8Gn0pPz/vrQ5dKq5XUgHGFGfPnXZxcaPmZUj5sIWaMILX3LBho6grFyBKYHxEyod+d+/GwH+TJs4gb8q3cVMY8SUmPoRWFLUm7v5dGM6Q8lHhjZtXoWyh4hvirzav06BxuGU/q1qpZrJ1fzAUzLaUlI8ZHO29qDV5+enQ+6j5u+xtPR4mRVf8FRKTrxN9UimUQot6PoBD78ehOTm5wOb0558noQcBnQuYgDA3f73La0GPADbpX48egAyKvhb1088r2gZ2gC3zdfOoOtAygN7q1m3rzp3/CyY1bt2+AfMa3/+wuOIBMOF67VoUbJ//mamhhIcPgd0+zJg8SnwAncsdOzeNHjO0YpqmdUA7eOXQ2qRyENIEYuX58yxonZA31a5dR3hCeIXxCffT0lM3bV774GH84EHDSXnTGn5L69ZHwLTx3xcioe1ay9dp0OzdzWRFJUQPfH3auzg13nfoy8THNyFBYu6eXvnzyCvXj9T8XQGtehcW5cA0DfRo78aevXHrD6JPMrHCwa2erwOt9xyB9se8uYtgbNI/vNvHn3zUo0dvNzcPyJTaPwNM38yZvTA6+vL7I8P37tsGz/bOO8PTM1Jh2pLoCHRewsMGb9i4+sNR78AcM9T/n81bUnFvly49snOeQ18GNuBXvxd6KKtWbszPy/1k2piJk0dev3EF5q0rDpaBoQ2kRkW/E+oRKKZ8GzexMH/zaxVCQbF82VpnZ9dP504ZNXowdG2WLlnp7192UAMk7ORJM6DugFdy8Jfds2eXdXOo33bNr9OgNWgmKMrRyynCWCz2uA9XO9p779w/f/mad8+c29qr+9iuHYfV/F2QPv37TLt978+IdaP+jto7dMBnpGwKWS9TKtJ8mYUNh2tWzwemM6jW3X9E/5GnUJBWQSb3wSdUg4IcxfmD6SPmexKaeZGtOLw2vWEHzX1r45OVmOfThN0mpC7O/7xx40b4On58FYfV4edrkMGzsufYOHH1NLShOYW0xC+w/q/DbZyfrzlwcNfuPVuqvMvb22dNxGZCA3Fx9+bO/7i6e/ftOSESiQiqnXY9rSJ/yfXwd67uAYuXh5Yoqrg4MczjMhms6g4W+HzWMTO+zv4K2/bMSUqJqfIukdBaLKmi38dishfNP02qkfes0K0hV2ilr8Pwas84c6Tf2wO7dg2u8i4Omy4nEId5oo0b9lZ376sTQ6gGrj5mIhFTnFMssqv6QKmPx28uLa3iLIRKZQkTZnqq+YCCbg/9eCdsnlJZ9UEPJQo5l1Nlr7Smw6EyH+aFf9uA0IBx5oioHKE36EBD75MgHek90vHohozqcsTaqv4/kWhhrstZ9rwn+UGD7FkcWlxlD/sjyEhAed8l3CbtXiYxAQUZRZaW6mYd678zQsEcQcbDw08QEGSREZdNjFp+ahGHWRI8jEYXKMYcQUalaXvzgCBRWixdTqSuc/mpBepiyduj6HWVczwfGjI2foEinhnz4tF0W28boRFdhkKlUBekF9jak6B3XAnNYI4gI+TdTGDnzP1jR2b+M4Z9Qzue0ODf588T8/LTi3oMcWjUmo4TCJgjyDiZ27CHznB7Eie99leOpFAlsBaa2wvMLLjEcKhK1AXPJdI8KYtV2thf0OZjWkzxVglzBBkzz6YC+O/5U3lyrCTpTk5hXgmbx+Ty2SJrnkxaz+cQqxKTRZRydUmxUl6ssnHi27lwWne09GpG99M7YI4g4+fgwYP/OrxtU1pKJAVKKE9kUpVaVRfnEHtdDAaDy2MILdkCCzaHS4tjQ2oDcwSZEAaDiKzY8B9BOlX1vC9fwGSxDSYLUd2AHbiNQz2f5wLRU9U5Yu3IzXxcTBCqJCdNxhfiAUeoClW/LdwaC+QytbKEjgNIVF+ynhT7+ONHkFEVqs4RJpN0e8fu7L50glC56N+zbZ05Hn74KWRUhWobTk5e/K6D7PYsTWoZZGNlz+UL6/8cB6heZKfKCrJLLGzY7Xrj+fFQ1WpqXNu78cZ+3eDWufyUe4XiQn2d2p/mZOWsrKyISbK258AuxK+tyMMXKxFULQ0TYGwuo61p74UiIyNPnz69fNFyghCqBk6ka+Dv7+/mZoonEEao9jBHNLAtRxBC1cPDATS4ffv2li1bCEKoeliPaJCbm/vgwQOCEKoe5ogG2B9BSCPMEQ2wP4KQRtgf0QD7IwhphPWIBtgfQUgjzBENsD+CkEaYIxpgfwQhjbA/ogH2RxDSCOsRDbA/gpBGmCMaBAQEeHh4EIRQ9TBHNLApRxBC1cP+iAa3bt3auHEjQQhVD+sRDfLy8hITEwlCqHqYIxpgfwQhjTBHNMD+CEIaYX9EA+yPIKQR1iMaYH8EIY0wRzTA/ghCGmGOaID9EYQ0wv6IBtAfiYiIIAiZPIVCUd1dWI9o4Onpefv27cLCQpFIxGRi7CJTdOXKlWPHjl24cOHgwYNVPoBRWooXA9cMkpjBYHTp0mXMmDFjx44lCJmA1NTU38r5+PiEh4f37NmzukdijryGkpKSv//+G36bMTEx8fHxQ4YM4XK5BCGjc+LECShAsrOz+/fvDwliZ2dX8+MxR96EVCpdv349hMjUqVOTkpIaNmxIEDJ8MIQ/fvw4JEhoaGhYWFjr1q1r+Y2YI9o6evTo2rVrN2/e7OXlRRAyQHl5edT4xdramipAYBT/Ws+AOaID+fn5EonEzc1twYIFgYGBAwYMIAgZgsjISChA7t+/H1YOZhXIG8Ec0aVHjx4dOHDg008/lclkDx8+hEwhCNEPvFGpAqR9+/YQH507dybawRzRC8iRadOmQZW4bNkyWObz+QSh+gZvRSo+lEolVYCIRCKiC5gjepSTkwON7jNnzvz6668zZ87EdiyqL1evXoX4gNlGKj6aNGlCdApzpC7AX1EsFoeEhJw8eRLSxM/PjyCkf2lpadT8C7zroIHau3dvoh+YI3UKAgUmd6AdC1GC4x2kPydOnIAEycjIoAoQBwcHok+YI/WAShCY1mnWrNnSpUsJQjpy9+5dqgPSt29fKEDqrNOPOVKfzp8/361bt5SUFNh7DB06VN87DWSsXrx4AYMXKEDMzc2pAoTFYpE6hDlS/6B5vnv3bihB58+fn5yc7O3t/bpHASGTde7cOag+7t27R8VHfR0MiTlCL9BAmTp1KvRQOnToQBCqRmJiIjV+gZELxEfXrl1JvcIcoaPHjx9DVfL111+7urp+8MEHdVyjItoqKSmh4kMul1MFCAxkCA1gjtBXTk7O/v37Q0NDoVg9e/Zsjx49CDJV0dHREB/wNggPD4cGKnToCZ1gjhgGmCpOSEg4dOhQcXGxmZkZQaYBumZUA9XT0xOqjz59+hBawhwxGFSCPHjwYNGiRdOnT2/Xrh1Bxuv333+HAiQ1NZUqQJycnAiNYY4YHogSmNbp27fvmTNnIFk6depEkLGIjY2lCpCePXtCghjKRz0xRwwYpElERMSAAQOgdZKVleXo6EiQYSooKIDqAxJEJBJRDVQ225DOnYw5YvCgdc/j8T7//HOIkp9++gmWCTIc58+fhwS5ffs2ZAcUIDBPRwwQ5ojxuHXrVqNGjbhc7nfffTd06FBfX1+C6ApqSaoAad26NSRIUFAQMWSYI0bo6NGjMTExixcvhi6dQCDA63jRh0KhoA4AkUqlUH1AglhYWBDDhzlizFJSUsaNGzdp0qRBgwYRXYA3S1GeojBPaQpvGwZhCC1ZlnZcpi4OA7x+/TpUH9Aap9ofzZs3J0YEc8T4UUfHrl27ViaTTZw48Y1PgZVwvSg2qlBSoLD3MCsuUhJjx2Izi/JLStWMpu3NA3takzeSmZlJFSBubm5QgMAsGzFGmCOmoqSk5MiRI9A0CQgIOHXqFAzIX+t4NkiQlDhp5wGOLI5pfYZQrSIxkblsdmmXgXav9Y3wS4YC5NmzZ9QZ2Gl+AIiWMEdM0bayITj4AAAGVElEQVRt27Zs2QIzBWq1+tVLeXXs2HHIkCEzZsyoWJNwrSjxriRoiDFvCTWLOZvL4ZCOobYaHxkbG0udgiwkJATGLyZyuCDmiOmCECksLBw2bNiECRMqrpUBe860tDQY+4wYMYK6Ammpmhz+Ma3nSBcmy6TPZnB2X3r3IQ4WtlUf1gG/SWr8AlUeVYBwIHhMBl4n3HQxmUwrK6sdO3Zcu3YNbkZFReXl5eXm5sKyWCzet28f3Dt48OCCXIW0SGniIVKOkZspfzVH/v77byhAbt68CdXHt99+a5pn88YcMXUODg6hoaGwAK2TNWvWQC+WWl9QULBhwwZzc/MmXl0cPfCTgcTGmSd5oai4Cd1rqgBp1aoVJMgPP/xATBjmCHrJ1tY2Ojq68pr8/PyVK1fOmmQvFZtuZ6SCQqZWKphKpZKKDyjZID4OHz4MVRsxeUyC0P9lZ2dXLJeWgzVbt24lqNwff/zRqVOn+Pj4WbNmHTp06IMPPsAQoWA9gl6C0Y2FhQVM37BYLGgW8ng8oVAIRYqrdSuCynl6ev6nZEMUzBH00okTJ+Ar7GxhH2tjY1Pxeb+nCdKbZ18QREjTpk0JqgrmCPoXnV+xEZkCzBGEkLYwRxBC2sIcQQhpC3MEIaQtzBGEkLYwRxBC2sIcQQhpC4+LR6boyK8HgnvihcR0BnMEGZ7k5MT3hocSRBs4rkGG58HDOILoBOsRpHtfLJy9eMn8bdvX9+3X+cqVi3v3bYeFinszMtO7BwdGX4uC5cNH9g98p+e9e7cnTBwRGhb0/ojwU6eP1/zkW7b+vPz7xVlZmfAkhw7vhTWwvGjxvLDw7j17d/ho7LunT5+oeDA88yfTx/Z5uxO8gJmzJsbHxxKkB1iPIN3jcDjJjxNLFCXLl6318PR+nJJU3SO5XK5YXLRr9+bFi36wt3fYsXPjipVL27RuD8vVfcv7wz+SFksvXTq3cf0ePt9MoVDMmTuFx+V9+81qaxvbv/76fdnyr4RCUefO3Z49ezL708ldu/SYNePz0tJSCKBZcybt2Ha4hidHbwbrEaR7TBYrLe3Z3E+/atHC39LCsqZHMsvODDRyxFhHRydY7t27P9xMSnpYw7fw+XxIDQaDYWlpxePxoq9dhryYP29xs2YtXZxdP/xgHCz8dvwQPPLob79AoMDL8PT09vJqMG/uIpVK9deZ3wnSNcwRpBfu7p7mIvNaPrhBg0bUgrl52cXlisRFpNYePUowMzNr0MCnYo2fb7Ok5EfUXb6+TSsuuC0UCj3cvWoOKfRmMEeQXkAhUPsH//fa5q9zDQOxRCwQCCuvEQgEUqkEFuCr8N93mcFdxVKCdA1zBNW1Ermc6I5IKJJIxJXXSCA+ylMMvor/c5dELBK+4eUEUQ0wR5DeiUTmMpkMehPUzUSdjix8GzeFJ09M/Oc54+7f9fNrRt314EEcNFyo9TBcevo0BUY6BOka5gjSO2rTpSZ0YUummqDagGDKy8uFOd3MzIx27TpCG/X7HxbHJ9xPS0/dtHntg4fxgwcNh4eFhQ0uLpZ+v2IJNGKTkxO/Xvo5fGPPkLcJ0jXMEaR3vo2bjB0zZdv29aFhQbBVT5k8C1aqlG9+pfHgHn2cnV1nzp74x6lj0EaF2WW4+encKaNGD75x4+rSJSv9/dvAw9xc3b//7qf09NSx44dN/WQ0TPFErNwIszwE6RpelxNpQJ3nOeR9F2Larp/KsXFk+3fDGKoCHoeGENIW5giio/CBwWq1qsq7Pp//dYcOnQmiE8wRREcb1u0uJVWPuK2tbAiiGcwRREdOTs4EGQ7MEYSQtjBHEELawhxBCGkLcwQhpC3MEYSQtjBHEELawhxBCGkLcwQhpC3MEYSQtjBHkAYsNkNgziImj8Nn8szwPBtVw98L0sDOhfckTkJMXkaS1NqRS1BVMEeQBjwB062xID+rhJgwZUkpk0UcPfkEVQVzBGnWbbD9+YMZKoXpnvLqzO70TmF2DAZBVcLzoaFakRaqdixJafe2g8iSZWHHLVUZ/9sGUkNSqCzMUdz4K2fgFFd7Nx5B1cAcQa/h2um89OTiUhUpyn/zs6saCjaXAWM6Z0+zNiHWsEBQ9TBHEELawnlfhJC2MEcQQtrCHEEIaQtzBCGkLcwRhJC2MEcQQtrCHEEIaet/AAAA///O3ko5AAAABklEQVQDAKQ1hodIzusJAAAAAElFTkSuQmCC",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict, Literal\n",
    "from langgraph.graph import StateGraph, START, END, MessagesState\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.types import Command, interrupt\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.tools import tool\n",
    "from langchain_core.messages import AIMessage\n",
    "from IPython.display import Image, display\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "@tool\n",
    "def weather_search(city: str):\n",
    "    \"\"\"Search for the weather\"\"\"\n",
    "    print(\"----\")\n",
    "    print(f\"Searching for: {city}\")\n",
    "    print(\"----\")\n",
    "    return \"Sunny!\"\n",
    "\n",
    "# Use OpenAI with tool binding\n",
    "model = ChatOpenAI(model=\"gpt-4o\").bind_tools([weather_search])\n",
    "\n",
    "class State(MessagesState):\n",
    "    \"\"\"Simple state.\"\"\"\n",
    "\n",
    "\n",
    "def call_llm(state):\n",
    "    return {\"messages\": [model.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "def human_review_node(state) -> Command[Literal[\"call_llm\", \"run_tool\"]]:\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    \n",
    "    # Get the tool call from OpenAI format\n",
    "    tool_call = last_message.tool_calls[-1] if hasattr(last_message, \"tool_calls\") and last_message.tool_calls else None\n",
    "    \n",
    "    # this is the value we'll be providing via Command(resume=<human_review>)\n",
    "    human_review = interrupt(\n",
    "        {\n",
    "            \"question\": \"Is this correct?\",\n",
    "            # Surface tool calls for review\n",
    "            \"tool_call\": tool_call,\n",
    "        }\n",
    "    )\n",
    "\n",
    "    review_action = human_review[\"action\"]\n",
    "    review_data = human_review.get(\"data\")\n",
    "\n",
    "    # if approved, call the tool\n",
    "    if review_action == \"continue\":\n",
    "        return Command(goto=\"run_tool\")\n",
    "\n",
    "    # update the AI message AND call tools\n",
    "    elif review_action == \"update\":\n",
    "        # Handle OpenAI format\n",
    "        updated_message = {\n",
    "            \"role\": \"ai\",\n",
    "            \"content\": last_message.content,\n",
    "            \"tool_calls\": [\n",
    "                {\n",
    "                    \"id\": tool_call[\"id\"],\n",
    "                    \"name\": tool_call[\"name\"],\n",
    "                    # This the update provided by the human\n",
    "                    \"args\": review_data,\n",
    "                }\n",
    "            ],\n",
    "            # This is important - this needs to be the same as the message you replacing!\n",
    "            # Otherwise, it will show up as a separate message\n",
    "            \"id\": last_message.id,\n",
    "        }\n",
    "        \n",
    "        return Command(goto=\"run_tool\", update={\"messages\": [updated_message]})\n",
    "\n",
    "    # provide feedback to LLM\n",
    "    elif review_action == \"feedback\":\n",
    "        # NOTE: we're adding feedback message as a ToolMessage\n",
    "        # to preserve the correct order in the message history\n",
    "        # (AI messages with tool calls need to be followed by tool call messages)\n",
    "        tool_message = {\n",
    "            \"role\": \"tool\",\n",
    "            # This is our natural language feedback\n",
    "            \"content\": review_data,\n",
    "            \"name\": tool_call[\"name\"],\n",
    "            \"tool_call_id\": tool_call[\"id\"],\n",
    "        }\n",
    "        return Command(goto=\"call_llm\", update={\"messages\": [tool_message]})\n",
    "\n",
    "\n",
    "def run_tool(state):\n",
    "    new_messages = []\n",
    "    tools = {\"weather_search\": weather_search}\n",
    "    \n",
    "    # Get tool calls from OpenAI format\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    tool_calls = last_message.tool_calls if hasattr(last_message, \"tool_calls\") else []\n",
    "    \n",
    "    for tool_call in tool_calls:\n",
    "        tool_name = tool_call[\"name\"]\n",
    "        if tool_name in tools:\n",
    "            tool = tools[tool_name]\n",
    "            result = tool.invoke(tool_call[\"args\"])\n",
    "            new_messages.append(\n",
    "                {\n",
    "                    \"role\": \"tool\",\n",
    "                    \"name\": tool_call[\"name\"],\n",
    "                    \"content\": result,\n",
    "                    \"tool_call_id\": tool_call[\"id\"],\n",
    "                }\n",
    "            )\n",
    "    return {\"messages\": new_messages}\n",
    "\n",
    "\n",
    "def route_after_llm(state) -> Literal[END, \"human_review_node\"]:\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    \n",
    "    # Check for OpenAI tool calls\n",
    "    has_tool_calls = hasattr(last_message, \"tool_calls\") and len(last_message.tool_calls) > 0\n",
    "    \n",
    "    if has_tool_calls:\n",
    "        return \"human_review_node\"\n",
    "    else:\n",
    "        return END\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(call_llm)\n",
    "builder.add_node(run_tool)\n",
    "builder.add_node(human_review_node)\n",
    "builder.add_edge(START, \"call_llm\")\n",
    "builder.add_conditional_edges(\"call_llm\", route_after_llm)\n",
    "builder.add_edge(\"run_tool\", \"call_llm\")\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory)\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example with no review\n",
    "\n",
    "Let's look at an example when no review is required (because no tools are called)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content='Hello! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 44, 'total_tokens': 55, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_90122d973c', 'id': 'chatcmpl-BRluSv7cMhtqsKGNfpuvpygg05aLl', 'finish_reason': 'stop', 'logprobs': None}, id='run-0b664c20-9e59-4c95-a77f-fdc585029ea4-0', usage_metadata={'input_tokens': 44, 'output_tokens': 11, 'total_tokens': 55, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"hi!\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"openai-1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we check the state, we can see that it is finished"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example of approving tool\n",
    "\n",
    "Let's now look at what it looks like to approve a tool call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_TwU0AILv55GWgEe9cKwKmQyK', 'function': {'arguments': '{\"city\":\"San Francisco\"}', 'name': 'weather_search'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 49, 'total_tokens': 65, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_90122d973c', 'id': 'chatcmpl-BRluSoo0gyDHx1uB5SX2hBXH8d6vU', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-2929f867-5dac-463f-9cba-bce36f666f10-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'call_TwU0AILv55GWgEe9cKwKmQyK', 'type': 'tool_call'}], usage_metadata={'input_tokens': 49, 'output_tokens': 16, 'total_tokens': 65, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'call_TwU0AILv55GWgEe9cKwKmQyK', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:c9df3a9e-497b-d78d-0759-90a1cad5c416']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"openai-2\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we now check, we can see that it is waiting on human review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To approve the tool call, we can just continue the thread with no edits. To do so, we need to let `human_review_node` know what value to use for the `human_review` variable we defined inside the node. We can provide this value by invoking the graph with a `Command(resume=<human_review>)` input.  Since we're approving the tool call, we'll provide `resume` value of `{\"action\": \"continue\"}` to navigate to `run_tool` node:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': None}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'call_TwU0AILv55GWgEe9cKwKmQyK'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content='The weather in San Francisco is currently sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 74, 'total_tokens': 85, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f5bdcc3276', 'id': 'chatcmpl-BRluTeL16wIx1Q8gc6vQvm1fmfrPv', 'finish_reason': 'stop', 'logprobs': None}, id='run-26f957fa-6b0c-4652-9fe9-6392afe31aa8-0', usage_metadata={'input_tokens': 74, 'output_tokens': 11, 'total_tokens': 85, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(\n",
    "    # provide value\n",
    "    Command(resume={\"action\": \"continue\"}),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Edit Tool Call\n",
    "\n",
    "Let's now say we want to edit the tool call. E.g. change some of the parameters (or even the tool called!) but then execute that tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_M6bzAiY3457k7fTVmmzoDq0N', 'function': {'arguments': '{\"city\":\"San Francisco\"}', 'name': 'weather_search'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 49, 'total_tokens': 65, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_90122d973c', 'id': 'chatcmpl-BRluUKXq2iuemse8Jg7fHWYW2fhdG', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-bd72dd5e-247e-4d02-bc52-2e38168593b8-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'call_M6bzAiY3457k7fTVmmzoDq0N', 'type': 'tool_call'}], usage_metadata={'input_tokens': 49, 'output_tokens': 16, 'total_tokens': 65, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'call_M6bzAiY3457k7fTVmmzoDq0N', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:8c914865-df8d-e5a6-46b6-2c18e91ef978']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"openai-3\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To do this, we will use `Command` with a different resume value of `{\"action\": \"update\", \"data\": <tool call args>}`. This will do the following:\n",
    "\n",
    "* combine existing tool call with user-provided tool call arguments and update the existing AI message with the new tool call\n",
    "* navigate to `run_tool` node with the updated AI message and continue execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': {'messages': [{'role': 'ai', 'content': '', 'tool_calls': [{'id': 'call_M6bzAiY3457k7fTVmmzoDq0N', 'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}}], 'id': 'run-bd72dd5e-247e-4d02-bc52-2e38168593b8-0'}]}}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco, USA\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'call_M6bzAiY3457k7fTVmmzoDq0N'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content='The weather in San Francisco is currently sunny. Enjoy the clear skies!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 76, 'total_tokens': 92, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f5bdcc3276', 'id': 'chatcmpl-BRluUJKUZobRoOCZYow4cRKNgmBgH', 'finish_reason': 'stop', 'logprobs': None}, id='run-bad4d994-3ac4-4d69-a032-8c22bbdf5385-0', usage_metadata={'input_tokens': 76, 'output_tokens': 16, 'total_tokens': 92, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Let's now continue executing from here\n",
    "for event in graph.stream(\n",
    "    Command(resume={\"action\": \"update\", \"data\": {\"city\": \"San Francisco, USA\"}}),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Give feedback to a tool call\n",
    "\n",
    "Sometimes, you may not want to execute a tool call, but you also may not want to ask the user to manually modify the tool call. In that case it may be better to get natural language feedback from the user. You can then insert this feedback as a mock **RESULT** of the tool call.\n",
    "\n",
    "There are multiple ways to do this:\n",
    "\n",
    "1. You could add a new message to the state (representing the \"result\" of a tool call)\n",
    "2. You could add TWO new messages to the state - one representing an \"error\" from the tool call, other HumanMessage representing the feedback\n",
    "\n",
    "Both are similar in that they involve adding messages to the state. The main difference lies in the logic AFTER the `human_review_node` and how it handles different types of messages.\n",
    "\n",
    "For this example we will just add a single tool call representing the feedback (see `human_review_node` implementation). Let's see this in action!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_X2Ln7Su0gUyhyCcXy0QI7pnE', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'weather_search'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 49, 'total_tokens': 64, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_90122d973c', 'id': 'chatcmpl-BRluVOHc3x8uoIVSjXtHkcygfV9mO', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-f992c3ea-a4ec-408b-a2b5-d4804a3e802e-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'sf'}, 'id': 'call_X2Ln7Su0gUyhyCcXy0QI7pnE', 'type': 'tool_call'}], usage_metadata={'input_tokens': 49, 'output_tokens': 15, 'total_tokens': 64, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'sf'}, 'id': 'call_X2Ln7Su0gUyhyCcXy0QI7pnE', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:04bb6708-e8d5-5353-491a-37ead2ba6eb8']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"openai-4\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To do this, we will use `Command` with a different resume value of `{\"action\": \"feedback\", \"data\": <feedback string>}`. This will do the following:\n",
    "\n",
    "* create a new tool message that combines existing tool call from LLM with the with user-provided feedback as content\n",
    "* navigate to `call_llm` node with the updated tool message and continue execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': {'messages': [{'role': 'tool', 'content': 'User requested changes: use <city, country> format for location', 'name': 'weather_search', 'tool_call_id': 'call_X2Ln7Su0gUyhyCcXy0QI7pnE'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_RpRf2VCPyatj9En9PMDlhvrV', 'function': {'arguments': '{\"city\":\"San Francisco, US\"}', 'name': 'weather_search'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 84, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f5bdcc3276', 'id': 'chatcmpl-BRluWDATYYVxJ5wP9Hd2zhi4QzBjn', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-65564d88-a9c0-4831-9f8a-cf8ba95f5b81-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco, US'}, 'id': 'call_RpRf2VCPyatj9En9PMDlhvrV', 'type': 'tool_call'}], usage_metadata={'input_tokens': 84, 'output_tokens': 18, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco, US'}, 'id': 'call_RpRf2VCPyatj9En9PMDlhvrV', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:6a75aa82-8038-8160-0569-903cec06c197']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Let's now continue executing from here\n",
    "for event in graph.stream(\n",
    "    # provide our natural language feedback!\n",
    "    Command(\n",
    "        resume={\n",
    "            \"action\": \"feedback\",\n",
    "            \"data\": \"User requested changes: use <city, country> format for location\",\n",
    "        }\n",
    "    ),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that we now get to another interrupt - because it went back to the model and got an entirely new prediction of what to call. Let's now approve this one and continue."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': None}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco, US\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'call_RpRf2VCPyatj9En9PMDlhvrV'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content='The weather in San Francisco, US is sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 12, 'prompt_tokens': 111, 'total_tokens': 123, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f5bdcc3276', 'id': 'chatcmpl-BRluXTlCd6rlHNrKWk1cVQ067FXVT', 'finish_reason': 'stop', 'logprobs': None}, id='run-dbc464a9-945c-45e8-8633-d4ed9e5f1f95-0', usage_metadata={'input_tokens': 111, 'output_tokens': 12, 'total_tokens': 123, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(\n",
    "    Command(resume={\"action\": \"continue\"}), thread, stream_mode=\"updates\"\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}

================
File: examples/human_in_the_loop/review-tool-calls.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to Review Tool Calls\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Tool calling](https://python.langchain.com/docs/concepts/tool_calling/)\n",
    "    * [Human-in-the-loop](../../../concepts/human_in_the_loop)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)      \n",
    "\n",
    "Human-in-the-loop (HIL) interactions are crucial for [agentic systems](../../../concepts/agentic_concepts). A common pattern is to add some human in the loop step after certain tool calls. These tool calls often lead to either a function call or saving of some information. Examples include:\n",
    "\n",
    "- A tool call to execute SQL, which will then be run by the tool\n",
    "- A tool call to generate a summary, which will then be saved to the State of the graph\n",
    "\n",
    "Note that using tool calls is common **whether actually calling tools or not**.\n",
    "\n",
    "There are typically a few different interactions you may want to do here:\n",
    "\n",
    "1. Approve the tool call and continue\n",
    "2. Modify the tool call manually and then continue\n",
    "3. Give natural language feedback, and then pass that back to the agent\n",
    "\n",
    "\n",
    "We can implement these in LangGraph using the [`interrupt()`][langgraph.types.interrupt] function. `interrupt` allows us to stop graph execution to collect input from a user and continue execution with collected input:\n",
    "\n",
    "\n",
    "```python\n",
    "def human_review_node(state) -> Command[Literal[\"call_llm\", \"run_tool\"]]:\n",
    "    # this is the value we'll be providing via Command(resume=<human_review>)\n",
    "    human_review = interrupt(\n",
    "        {\n",
    "            \"question\": \"Is this correct?\",\n",
    "            # Surface tool calls for review\n",
    "            \"tool_call\": tool_call\n",
    "        }\n",
    "    )\n",
    "    \n",
    "    review_action, review_data = human_review\n",
    "    \n",
    "    # Approve the tool call and continue\n",
    "    if review_action == \"continue\":\n",
    "        return Command(goto=\"run_tool\")\n",
    "    \n",
    "    # Modify the tool call manually and then continue\n",
    "    elif review_action == \"update\":\n",
    "        ...\n",
    "        updated_msg = get_updated_msg(review_data)\n",
    "        return Command(goto=\"run_tool\", update={\"messages\": [updated_message]})\n",
    "\n",
    "    # Give natural language feedback, and then pass that back to the agent\n",
    "    elif review_action == \"feedback\":\n",
    "        ...\n",
    "        feedback_msg = get_feedback_msg(review_data)\n",
    "        return Command(goto=\"call_llm\", update={\"messages\": [feedback_msg]})\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic \"httpx>=0.24.0,<1.0.0\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "035e567c-db5c-4085-ba4e-5b3814561c21",
   "metadata": {},
   "source": [
    "## Simple Usage\n",
    "\n",
    "Let's set up a very simple graph that facilitates this.\n",
    "First, we will have an LLM call that decides what action to take.\n",
    "Then we go to a human node. This node actually doesn't do anything - the idea is that we interrupt before this node and then apply any updates to the state.\n",
    "After that, we check the state and either route back to the LLM or to the correct tool.\n",
    "\n",
    "Let's see this in action!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "85e452f8-f33a-4ead-bb4d-7386cdba8edc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict, Literal\n",
    "from langgraph.graph import StateGraph, START, END, MessagesState\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.types import Command, interrupt\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "from langchain_core.messages import AIMessage\n",
    "from IPython.display import Image, display\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "@tool\n",
    "def weather_search(city: str):\n",
    "    \"\"\"Search for the weather\"\"\"\n",
    "    print(\"----\")\n",
    "    print(f\"Searching for: {city}\")\n",
    "    print(\"----\")\n",
    "    return \"Sunny!\"\n",
    "\n",
    "\n",
    "model = ChatAnthropic(model_name=\"claude-3-5-sonnet-latest\").bind_tools([weather_search])\n",
    "\n",
    "\n",
    "class State(MessagesState):\n",
    "    \"\"\"Simple state.\"\"\"\n",
    "\n",
    "\n",
    "def call_llm(state):\n",
    "    return {\"messages\": [model.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "def human_review_node(state) -> Command[Literal[\"call_llm\", \"run_tool\"]]:\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    \n",
    "    # Handle Anthropic message format which uses content list with tool_use type\n",
    "    tool_call = None\n",
    "    if hasattr(last_message, \"content\") and isinstance(last_message.content, list):\n",
    "        for part in last_message.content:\n",
    "            if isinstance(part, dict) and part.get(\"type\") == \"tool_use\":\n",
    "                tool_call = {\n",
    "                    \"name\": part.get(\"name\"),\n",
    "                    \"args\": part.get(\"input\", {}),\n",
    "                    \"id\": part.get(\"id\"),\n",
    "                    \"type\": \"tool_call\"\n",
    "                }\n",
    "                break\n",
    "        \n",
    "    # this is the value we'll be providing via Command(resume=<human_review>)\n",
    "    human_review = interrupt(\n",
    "        {\n",
    "            \"question\": \"Is this correct?\",\n",
    "            # Surface tool calls for review\n",
    "            \"tool_call\": tool_call,\n",
    "        }\n",
    "    )\n",
    "\n",
    "    review_action = human_review[\"action\"]\n",
    "    review_data = human_review.get(\"data\")\n",
    "\n",
    "    # if approved, call the tool\n",
    "    if review_action == \"continue\":\n",
    "        return Command(goto=\"run_tool\")\n",
    "\n",
    "    # update the AI message AND call tools\n",
    "    elif review_action == \"update\":\n",
    "        # For Anthropic format\n",
    "        updated_content = []\n",
    "        for part in last_message.content:\n",
    "            if isinstance(part, dict) and part.get(\"type\") == \"tool_use\":\n",
    "                updated_part = part.copy()\n",
    "                updated_part[\"input\"] = review_data\n",
    "                updated_content.append(updated_part)\n",
    "            else:\n",
    "                updated_content.append(part)\n",
    "        \n",
    "        updated_message = {\n",
    "            \"role\": \"ai\",\n",
    "            \"content\": updated_content,\n",
    "            \"id\": last_message.id,\n",
    "        }\n",
    "            \n",
    "        return Command(goto=\"run_tool\", update={\"messages\": [updated_message]})\n",
    "\n",
    "    # provide feedback to LLM\n",
    "    elif review_action == \"feedback\":\n",
    "        # NOTE: we're adding feedback message as a ToolMessage\n",
    "        # to preserve the correct order in the message history\n",
    "        # (AI messages with tool calls need to be followed by tool call messages)\n",
    "        tool_message = {\n",
    "            \"role\": \"tool\",\n",
    "            # This is our natural language feedback\n",
    "            \"content\": review_data,\n",
    "            \"name\": tool_call[\"name\"],\n",
    "            \"tool_call_id\": tool_call[\"id\"],\n",
    "        }\n",
    "        return Command(goto=\"call_llm\", update={\"messages\": [tool_message]})\n",
    "\n",
    "\n",
    "def run_tool(state):\n",
    "    new_messages = []\n",
    "    tools = {\"weather_search\": weather_search}\n",
    "    \n",
    "    # Handle different message formats\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    tool_calls = []\n",
    "    \n",
    "    # Handle Anthropic format\n",
    "    if hasattr(last_message, \"content\") and isinstance(last_message.content, list):\n",
    "        for part in last_message.content:\n",
    "            if isinstance(part, dict) and part.get(\"type\") == \"tool_use\":\n",
    "                tool_calls.append({\n",
    "                    \"name\": part.get(\"name\"),\n",
    "                    \"args\": part.get(\"input\", {}),\n",
    "                    \"id\": part.get(\"id\"),\n",
    "                })\n",
    "    \n",
    "    for tool_call in tool_calls:\n",
    "        tool_name = tool_call[\"name\"]\n",
    "        if tool_name in tools:\n",
    "            tool = tools[tool_name]\n",
    "            result = tool.invoke(tool_call[\"args\"])\n",
    "            new_messages.append(\n",
    "                {\n",
    "                    \"role\": \"tool\",\n",
    "                    \"name\": tool_call[\"name\"],\n",
    "                    \"content\": result,\n",
    "                    \"tool_call_id\": tool_call[\"id\"],\n",
    "                }\n",
    "            )\n",
    "    return {\"messages\": new_messages}\n",
    "\n",
    "\n",
    "def route_after_llm(state) -> Literal[END, \"human_review_node\"]:\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    \n",
    "    # Check for Anthropic tool calls\n",
    "    has_tool_calls = False\n",
    "    if hasattr(last_message, \"content\") and isinstance(last_message.content, list):\n",
    "        for part in last_message.content:\n",
    "            if isinstance(part, dict) and part.get(\"type\") == \"tool_use\":\n",
    "                has_tool_calls = True\n",
    "                break\n",
    "    \n",
    "    if has_tool_calls:\n",
    "        return \"human_review_node\"\n",
    "    else:\n",
    "        return END\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(call_llm)\n",
    "builder.add_node(run_tool)\n",
    "builder.add_node(human_review_node)\n",
    "builder.add_edge(START, \"call_llm\")\n",
    "builder.add_conditional_edges(\"call_llm\", route_after_llm)\n",
    "builder.add_edge(\"run_tool\", \"call_llm\")\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory)\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d246d39f-4b36-459b-bd54-bf363753e590",
   "metadata": {},
   "source": [
    "## Example with no review\n",
    "\n",
    "Let's look at an example when no review is required (because no tools are called)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1b3aa6fc-c7fb-4819-8d7f-ba6057cc4edf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content=\"Hello! I can help you find weather information using the weather search tool. Would you like to know the weather for a specific city? Just let me know which city you're interested in and I'll look that up for you.\", additional_kwargs={}, response_metadata={'id': 'msg_011Uk3am3VPYPuUHAswbF5sb', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 374, 'output_tokens': 49}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-7be06d70-058d-450f-b911-b9badd6ff906-0', usage_metadata={'input_tokens': 374, 'output_tokens': 49, 'total_tokens': 423, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"hi!\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d59dc607-e70d-497b-aac9-78c847c27042",
   "metadata": {},
   "source": [
    "If we check the state, we can see that it is finished"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c1985f7-54f1-420f-a2b6-5e6154909966",
   "metadata": {},
   "source": [
    "## Example of approving tool\n",
    "\n",
    "Let's now look at what it looks like to approve a tool call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "2561a38f-edb5-4b44-b2d7-6a7b70d2e6b7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content=[{'text': \"I'll help you check the weather in San Francisco.\", 'type': 'text'}, {'id': 'toolu_016TFh3JmzT9FEYs2MH9z7HH', 'input': {'city': 'San Francisco'}, 'name': 'weather_search', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01WcE3joeEa6tiwy2bP7ae5y', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 379, 'output_tokens': 66}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-b2b74408-e17e-44b5-9544-03fe0b99b4e6-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_016TFh3JmzT9FEYs2MH9z7HH', 'type': 'tool_call'}], usage_metadata={'input_tokens': 379, 'output_tokens': 66, 'total_tokens': 445, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_016TFh3JmzT9FEYs2MH9z7HH', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:37c4d941-77c1-361b-78b5-c0472fd06e6a']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ef6d51c-e2b6-4266-8de7-acf1a0b62a57",
   "metadata": {},
   "source": [
    "If we now check, we can see that it is waiting on human review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "33d68f0f-d435-4dd1-8013-6a59186dc9f5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14c99fdd-4204-4c2d-b1af-02f38ab6ad57",
   "metadata": {},
   "source": [
    "To approve the tool call, we can just continue the thread with no edits. To do so, we need to let `human_review_node` know what value to use for the `human_review` variable we defined inside the node. We can provide this value by invoking the graph with a `Command(resume=<human_review>)` input.  Since we're approving the tool call, we'll provide `resume` value of `{\"action\": \"continue\"}` to navigate to `run_tool` node:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f9a0d5d4-52ff-49e0-a6f4-41f9a0e844d8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': None}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_016TFh3JmzT9FEYs2MH9z7HH'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content=\"It's sunny in San Francisco right now!\", additional_kwargs={}, response_metadata={'id': 'msg_01Q329vWXkkkYEDD3UPKmEMG', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 458, 'output_tokens': 13}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-9880cea1-d4b3-4dc3-9e00-dff54f322d21-0', usage_metadata={'input_tokens': 458, 'output_tokens': 13, 'total_tokens': 471, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(\n",
    "    # provide value\n",
    "    Command(resume={\"action\": \"continue\"}),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d30c4a7-b480-4ede-b2b4-8ec11de95e30",
   "metadata": {},
   "source": [
    "## Edit Tool Call\n",
    "\n",
    "Let's now say we want to edit the tool call. E.g. change some of the parameters (or even the tool called!) but then execute that tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ec77831c-e6b8-4903-9146-e098a4b2fda1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content=[{'text': \"I'll help you check the weather in San Francisco.\", 'type': 'text'}, {'id': 'toolu_01ApBN1kuKJk1tdNLXp14B1q', 'input': {'city': 'sf'}, 'name': 'weather_search', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01LcaF12XWCrwuTqKxFKyiRV', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 379, 'output_tokens': 65}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-05798f48-2626-47c5-a88c-71f7926354d0-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'sf'}, 'id': 'toolu_01ApBN1kuKJk1tdNLXp14B1q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 379, 'output_tokens': 65, 'total_tokens': 444, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'sf'}, 'id': 'toolu_01ApBN1kuKJk1tdNLXp14B1q', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:ddd61db1-16d0-3595-e42b-4e2822740950']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "edcffbd7-829b-4d0c-88bf-cd531bc0e6b2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87358aca-9b8f-48c7-98d4-3d755f6b0104",
   "metadata": {},
   "source": [
    "To do this, we will use `Command` with a different resume value of `{\"action\": \"update\", \"data\": <tool call args>}`. This will do the following:\n",
    "\n",
    "* combine existing tool call with user-provided tool call arguments and update the existing AI message with the new tool call\n",
    "* navigate to `run_tool` node with the updated AI message and continue execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b2f73998-baae-4c00-8a90-f4153e924941",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': {'messages': [{'role': 'ai', 'content': [{'text': \"I'll help you check the weather in San Francisco.\", 'type': 'text'}, {'id': 'toolu_01ApBN1kuKJk1tdNLXp14B1q', 'input': {'city': 'San Francisco, USA'}, 'name': 'weather_search', 'type': 'tool_use'}], 'id': 'run-05798f48-2626-47c5-a88c-71f7926354d0-0'}]}}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco, USA\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_01ApBN1kuKJk1tdNLXp14B1q'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content=\"According to the search, it's sunny in San Francisco right now!\", additional_kwargs={}, response_metadata={'id': 'msg_01XjurtXyNPFbxbZ7NAuDvdm', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 460, 'output_tokens': 18}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-442de8d9-e410-418a-aeba-94fb5cc13d95-0', usage_metadata={'input_tokens': 460, 'output_tokens': 18, 'total_tokens': 478, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Let's now continue executing from here\n",
    "for event in graph.stream(\n",
    "    Command(resume={\"action\": \"update\", \"data\": {\"city\": \"San Francisco, USA\"}}),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e14acc96-3d50-44b1-8616-b8d9131e46c4",
   "metadata": {},
   "source": [
    "## Give feedback to a tool call\n",
    "\n",
    "Sometimes, you may not want to execute a tool call, but you also may not want to ask the user to manually modify the tool call. In that case it may be better to get natural language feedback from the user. You can then insert this feedback as a mock **RESULT** of the tool call.\n",
    "\n",
    "There are multiple ways to do this:\n",
    "\n",
    "1. You could add a new message to the state (representing the \"result\" of a tool call)\n",
    "2. You could add TWO new messages to the state - one representing an \"error\" from the tool call, other HumanMessage representing the feedback\n",
    "\n",
    "Both are similar in that they involve adding messages to the state. The main difference lies in the logic AFTER the `human_review_node` and how it handles different types of messages.\n",
    "\n",
    "For this example we will just add a single tool call representing the feedback (see `human_review_node` implementation). Let's see this in action!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d57d5131-7912-4216-aa87-b7272507fa51",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'call_llm': {'messages': [AIMessage(content=[{'text': \"I'll help you check the weather in San Francisco.\", 'type': 'text'}, {'id': 'toolu_01B2fEU5quHZwJGhMguzwG3h', 'input': {'city': 'San Francisco'}, 'name': 'weather_search', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01B8oWNxNafoiR2YwEb8df4a', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 379, 'output_tokens': 66}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-90a9c3d3-d4fe-43a1-a2ec-f0966b5ddec8-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_01B2fEU5quHZwJGhMguzwG3h', 'type': 'tool_call'}], usage_metadata={'input_tokens': 379, 'output_tokens': 66, 'total_tokens': 445, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_01B2fEU5quHZwJGhMguzwG3h', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:52e176da-0f12-9ad1-70b7-94bb2268acd3']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf?\"}]}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"4\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "e33ad664-0307-43c5-b85a-1e02eebceb5c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "483d9455-8625-4c6a-9b98-f731403b2ed3",
   "metadata": {},
   "source": [
    "To do this, we will use `Command` with a different resume value of `{\"action\": \"feedback\", \"data\": <feedback string>}`. This will do the following:\n",
    "\n",
    "* create a new tool message that combines existing tool call from LLM with the with user-provided feedback as content\n",
    "* navigate to `call_llm` node with the updated tool message and continue execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "3f05f8b6-6128-4de5-8884-862fc93f1227",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': {'messages': [{'role': 'tool', 'content': 'User requested changes: use <city, country> format for location', 'name': 'weather_search', 'tool_call_id': 'toolu_01B2fEU5quHZwJGhMguzwG3h'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content=[{'text': 'Let me try that again with the correct format.', 'type': 'text'}, {'id': 'toolu_01WkQvzDBjWxo43RM1TUpG8W', 'input': {'city': 'San Francisco, USA'}, 'name': 'weather_search', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01JpNxKtF7idCm3hxBGcQGSV', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 469, 'output_tokens': 68}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-7e1a1bd8-96db-488a-95e4-e7c753983b47-0', tool_calls=[{'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}, 'id': 'toolu_01WkQvzDBjWxo43RM1TUpG8W', 'type': 'tool_call'}], usage_metadata={'input_tokens': 469, 'output_tokens': 68, 'total_tokens': 537, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n",
      "{'__interrupt__': (Interrupt(value={'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}, 'id': 'toolu_01WkQvzDBjWxo43RM1TUpG8W', 'type': 'tool_call'}}, resumable=True, ns=['human_review_node:07cc1657-faba-45ed-335b-59bc64a9c873']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Let's now continue executing from here\n",
    "for event in graph.stream(\n",
    "    # provide our natural language feedback!\n",
    "    Command(\n",
    "        resume={\n",
    "            \"action\": \"feedback\",\n",
    "            \"data\": \"User requested changes: use <city, country> format for location\",\n",
    "        }\n",
    "    ),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d2e79ab-7cdb-42ce-b2ca-2932f8782c90",
   "metadata": {},
   "source": [
    "We can see that we now get to another interrupt - because it went back to the model and got an entirely new prediction of what to call. Let's now approve this one and continue."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ca558915-f4d9-4ff2-95b7-cdaf0c6db485",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pending Executions!\n",
      "('human_review_node',)\n"
     ]
    }
   ],
   "source": [
    "print(\"Pending Executions!\")\n",
    "print(graph.get_state(thread).next)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a30d40ad-611d-4ec3-84be-869ea05acb89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'human_review_node': None}\n",
      "\n",
      "\n",
      "----\n",
      "Searching for: San Francisco, USA\n",
      "----\n",
      "{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_01WkQvzDBjWxo43RM1TUpG8W'}]}}\n",
      "\n",
      "\n",
      "{'call_llm': {'messages': [AIMessage(content=\"It's sunny in San Francisco right now!\", additional_kwargs={}, response_metadata={'id': 'msg_015ZtPA91x32qanJt3ybkndX', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 550, 'output_tokens': 13}, 'model_name': 'claude-3-5-sonnet-20241022'}, id='run-03b7df2b-4022-4dfb-afa1-b1a450569ba7-0', usage_metadata={'input_tokens': 550, 'output_tokens': 13, 'total_tokens': 563, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})]}}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(\n",
    "    Command(resume={\"action\": \"continue\"}), thread, stream_mode=\"updates\"\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/human_in_the_loop/time-travel.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to view and update past graph state\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Time Travel](../../../concepts/time-travel)\n",
    "    * [Breakpoints](../../../concepts/breakpoints)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)\n",
    "\n",
    "\n",
    "Once you start [checkpointing](../../persistence) your graphs, you can easily **get** or **update** the state of the agent at any point in time. This permits a few things:\n",
    "\n",
    "1. You can surface a state during an interrupt to a user to let them accept an action.\n",
    "2. You can **rewind** the graph to reproduce or avoid issues.\n",
    "3. You can **modify** the state to embed your agent into a larger system, or to let the user better control its actions.\n",
    "\n",
    "The key methods used for this functionality are:\n",
    "\n",
    "- [get_state](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.graph.CompiledGraph.get_state): fetch the values from the target config\n",
    "- [update_state](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.graph.CompiledGraph.update_state): apply the given values to the target state\n",
    "\n",
    "**Note:** this requires passing in a checkpointer.\n",
    "\n",
    "Below is a quick example."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_openai"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for OpenAI (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e36f89e5",
   "metadata": {},
   "source": [
    "## Build the agent\n",
    "\n",
    "We can now build the agent. We will build a relatively simple ReAct-style agent that does tool calling. We will use Anthropic's models and fake tools (just for demo purposes)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f5319e01",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set up the tool\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.graph import MessagesState, START\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.graph import END, StateGraph\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "@tool\n",
    "def play_song_on_spotify(song: str):\n",
    "    \"\"\"Play a song on Spotify\"\"\"\n",
    "    # Call the spotify API ...\n",
    "    return f\"Successfully played {song} on Spotify!\"\n",
    "\n",
    "\n",
    "@tool\n",
    "def play_song_on_apple(song: str):\n",
    "    \"\"\"Play a song on Apple Music\"\"\"\n",
    "    # Call the apple music API ...\n",
    "    return f\"Successfully played {song} on Apple Music!\"\n",
    "\n",
    "\n",
    "tools = [play_song_on_apple, play_song_on_spotify]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "# Set up the model\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "model = model.bind_tools(tools, parallel_tool_calls=False)\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Finally we pass in a mapping.\n",
    "    # The keys are strings, and the values are other nodes.\n",
    "    # END is a special node marking that the graph should finish.\n",
    "    # What will happen is we will call `should_continue`, and then the output of that\n",
    "    # will be matched against the keys in this mapping.\n",
    "    # Based on which one it matches, that node will then be called.\n",
    "    {\n",
    "        # If `tools`, then we call the tool node.\n",
    "        \"continue\": \"action\",\n",
    "        # Otherwise we finish.\n",
    "        \"end\": END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "\n",
    "# We add in `interrupt_before=[\"action\"]`\n",
    "# This will add a breakpoint before the `action` node is called\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
   "metadata": {},
   "source": [
    "## Interacting with the Agent\n",
    "\n",
    "We can now interact with the agent. Let's ask it to play Taylor Swift's most popular song:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cfd140f0-a5a6-4697-8115-322242f197b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Can you play Taylor Swift's most popular song?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  play_song_on_apple (call_SwbvKPaZxLnxuStPuXQkQg0Y)\n",
      " Call ID: call_SwbvKPaZxLnxuStPuXQkQg0Y\n",
      "  Args:\n",
      "    song: Anti-Hero by Taylor Swift\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: play_song_on_apple\n",
      "\n",
      "Successfully played Anti-Hero by Taylor Swift on Apple Music!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I've started playing \"Anti-Hero\" by Taylor Swift on Apple Music! Enjoy the music!\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "input_message = HumanMessage(content=\"Can you play Taylor Swift's most popular song?\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c38c505-6cee-427f-9dcd-493a2ade7ebb",
   "metadata": {},
   "source": [
    "## Checking history\n",
    "\n",
    "Let's browse the history of this thread, from start to finish."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "777538a5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'),\n",
       " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n",
       " ToolMessage(content='Successfully played Anti-Hero by Taylor Swift on Apple Music!', name='play_song_on_apple', id='aad71a5f-492b-48bc-a487-c620ec193d02', tool_call_id='call_SwbvKPaZxLnxuStPuXQkQg0Y'),\n",
       " AIMessage(content='I\\'ve started playing \"Anti-Hero\" by Taylor Swift on Apple Music! Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 125, 'total_tokens': 146, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5HAeEb5fYyAV4IMdIABAwnqo0Z', 'finish_reason': 'stop', 'logprobs': None}, id='run-d45f3b55-528a-403b-9f0c-f10c814ff583-0', usage_metadata={'input_tokens': 125, 'output_tokens': 21, 'total_tokens': 146, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app.get_state(config).values[\"messages\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "8578a66d-6489-4e03-8c23-fd0530278455",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': ''}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"Can you play Taylor Swift's most popular song?\", 'type': 'human'}}]}}, 'step': -1, 'parents': {}, 'thread_id': '1'}, created_at='2025-04-29T20:43:09.896874+00:00', parent_config=None, tasks=(PregelTask(id='01db093c-5b4c-404e-adc7-4c2f1b79d9ce', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0253a8-fc68-66d4-bfff-3d93672c32b8'}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}, 'thread_id': '1'}, created_at='2025-04-29T20:43:09.898069+00:00', parent_config=None, tasks=(PregelTask(id='8da50206-f1b7-c43d-ff08-02fc892c084d', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=('action',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0253a8-fc6b-65a1-8000-88c6f3a42fab'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', 'tool_calls': [{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'step': 1, 'parents': {}, 'thread_id': '1'}, created_at='2025-04-29T20:43:10.848784+00:00', parent_config=None, tasks=(PregelTask(id='47f235be-81a2-1a1c-1162-69e0e3d33e95', name='action', path=('__pregel_pull', 'action'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='Successfully played Anti-Hero by Taylor Swift on Apple Music!', name='play_song_on_apple', id='aad71a5f-492b-48bc-a487-c620ec193d02', tool_call_id='call_SwbvKPaZxLnxuStPuXQkQg0Y')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0253a9-057c-6718-8001-11e7f8ccf6da'}}, metadata={'source': 'loop', 'writes': {'action': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': 'Successfully played Anti-Hero by Taylor Swift on Apple Music!', 'type': 'tool', 'name': 'play_song_on_apple', 'id': 'aad71a5f-492b-48bc-a487-c620ec193d02', 'tool_call_id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'status': 'success'}}]}}, 'step': 2, 'parents': {}, 'thread_id': '1'}, created_at='2025-04-29T20:43:10.852299+00:00', parent_config=None, tasks=(PregelTask(id='a4b9ee27-8d9b-a5dc-67ec-023449044f52', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='Successfully played Anti-Hero by Taylor Swift on Apple Music!', name='play_song_on_apple', id='aad71a5f-492b-48bc-a487-c620ec193d02', tool_call_id='call_SwbvKPaZxLnxuStPuXQkQg0Y'), AIMessage(content='I\\'ve started playing \"Anti-Hero\" by Taylor Swift on Apple Music! Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 125, 'total_tokens': 146, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5HAeEb5fYyAV4IMdIABAwnqo0Z', 'finish_reason': 'stop', 'logprobs': None}, id='run-d45f3b55-528a-403b-9f0c-f10c814ff583-0', usage_metadata={'input_tokens': 125, 'output_tokens': 21, 'total_tokens': 146, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0253a9-0585-606e-8002-2788747e0e46'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'I\\'ve started playing \"Anti-Hero\" by Taylor Swift on Apple Music! Enjoy the music!', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 21, 'prompt_tokens': 125, 'total_tokens': 146, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5HAeEb5fYyAV4IMdIABAwnqo0Z', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-d45f3b55-528a-403b-9f0c-f10c814ff583-0', 'usage_metadata': {'input_tokens': 125, 'output_tokens': 21, 'total_tokens': 146, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'step': 3, 'parents': {}, 'thread_id': '1'}, created_at='2025-04-29T20:43:11.643083+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "--\n"
     ]
    }
   ],
   "source": [
    "all_states = []\n",
    "for state in app.get_state_history(config):\n",
    "    print(state)\n",
    "    all_states.append(state)\n",
    "    print(\"--\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ec41c37-7c09-4cc7-8475-bf373fe66584",
   "metadata": {},
   "source": [
    "## Replay a state\n",
    "\n",
    "We can go back to any of these states and restart the agent from there! Let's go back to right before the tool call gets executed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "02250602-8c4a-4fb5-bd6c-d0b9046e8699",
   "metadata": {},
   "outputs": [],
   "source": [
    "to_replay = all_states[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "21e7fc18-6fd9-4e11-a84b-e0325c9640c8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'),\n",
       "  AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'function': {'arguments': '{\"song\":\"Anti-Hero by Taylor Swift\"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 80, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5GxWKro32HznmzffDPbKEDt32h', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Anti-Hero by Taylor Swift'}, 'id': 'call_SwbvKPaZxLnxuStPuXQkQg0Y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 23, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_replay.values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d4b01634-0041-4632-8d1f-5464580e54f5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('action',)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_replay.next"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29da43ea-9295-43e2-b164-0eb28d96749c",
   "metadata": {},
   "source": [
    "To replay from this place we just need to pass its config back to the agent. Notice that it just resumes from right where it left all - making a tool call."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "e986f94f-706f-4b6f-b3c4-f95483b9e9b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content='Successfully played Anti-Hero by Taylor Swift on Apple Music!', name='play_song_on_apple', id='699ce951-d08c-4d0a-acd1-fd651d319960', tool_call_id='call_SwbvKPaZxLnxuStPuXQkQg0Y')]}\n",
      "{'messages': [AIMessage(content='I\\'ve successfully played \"Anti-Hero\" by Taylor Swift on Apple Music! Enjoy the song!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 125, 'total_tokens': 146, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5HmyKkgd5Ay8EtancJIVSfN7Jo', 'finish_reason': 'stop', 'logprobs': None}, id='run-b570874a-c7be-42e0-9a02-7ab0d8320bfa-0', usage_metadata={'input_tokens': 125, 'output_tokens': 21, 'total_tokens': 146, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(None, to_replay.config):\n",
    "    for v in event.values():\n",
    "        print(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59910951-fae1-4475-8511-f622439b590d",
   "metadata": {},
   "source": [
    "## Branch off a past state\n",
    "\n",
    "Using LangGraph's checkpointing, you can do more than just replay past states. You can branch off previous locations to let the agent explore alternate trajectories or to let a user \"version control\" changes in a workflow.\n",
    "\n",
    "Let's show how to do this to edit the state at a particular point in time. Let's update the state to instead of playing the song on Apple to play it on Spotify:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fbd5ad3b-5363-4ab7-ac63-b04668bc998f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = to_replay.values[\"messages\"][-1]\n",
    "\n",
    "\n",
    "# Let's now update the tool we are calling\n",
    "last_message.tool_calls[0][\"name\"] = \"play_song_on_spotify\"\n",
    "\n",
    "branch_config = app.update_state(\n",
    "    to_replay.config,\n",
    "    {\"messages\": [last_message]},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bced65eb-2158-43e6-a9e3-3b047c8d418e",
   "metadata": {},
   "source": [
    "We can then invoke with this new `branch_config` to resume running from here with changed state. We can see from the log that the tool was called with different input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "9a92d3da-62e2-45a2-8545-e4f6a64e0ffe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content='Successfully played Anti-Hero by Taylor Swift on Spotify!', name='play_song_on_spotify', id='0545c90a-b7df-4712-97f3-776e94021c0a', tool_call_id='call_SwbvKPaZxLnxuStPuXQkQg0Y')]}\n",
      "{'messages': [AIMessage(content='I\\'ve played \"Anti-Hero\" by Taylor Swift on Spotify. Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 124, 'total_tokens': 143, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BRm5IeJQKhrV7HJMY0qXTVfoxsf96', 'finish_reason': 'stop', 'logprobs': None}, id='run-5898fa8d-d271-4176-be35-45fc815503cd-0', usage_metadata={'input_tokens': 124, 'output_tokens': 19, 'total_tokens': 143, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(None, branch_config):\n",
    "    for v in event.values():\n",
    "        print(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "511e319e-d10d-4b04-a4e0-fc4f3d87cb23",
   "metadata": {},
   "source": [
    "Alternatively, we could update the state to not even call a tool!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "01abb480-df55-4eba-a2be-cf9372b60b54",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage\n",
    "\n",
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = to_replay.values[\"messages\"][-1]\n",
    "\n",
    "# Let's now get the ID for the last message, and create a new message with that ID.\n",
    "new_message = AIMessage(\n",
    "    content=\"It's quiet hours so I can't play any music right now!\", id=last_message.id\n",
    ")\n",
    "\n",
    "branch_config = app.update_state(\n",
    "    to_replay.config,\n",
    "    {\"messages\": [new_message]},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "1a7cfcd4-289e-419e-8b49-dfaef4f88641",
   "metadata": {},
   "outputs": [],
   "source": [
    "branch_state = app.get_state(branch_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "5198f9c1-d2d4-458a-993d-3caa55810b1e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='ce9e880c-05a3-41cb-855c-e666c8f9cbd1'),\n",
       "  AIMessage(content=\"It's quiet hours so I can't play any music right now!\", additional_kwargs={}, response_metadata={}, id='run-a43f1c2b-1e11-47c7-b60a-2469a55c82e9-0')]}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "branch_state.values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5d89d55d-db84-4c2d-828b-64a29a69947b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "branch_state.next"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc168c90-a374-4280-a9a6-8bc232dbb006",
   "metadata": {},
   "source": [
    "You can see the snapshot was updated and now correctly reflects that there is no next step."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/human_in_the_loop/wait-user-input.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to wait for user input using `interrupt`\n",
    "\n",
    "!!! tip \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following concepts:\n",
    "\n",
    "    * [Human-in-the-loop](../../../concepts/human_in_the_loop)\n",
    "    * [LangGraph Glossary](../../../concepts/low_level)\n",
    "    \n",
    "\n",
    "**Human-in-the-loop (HIL)** interactions are crucial for [agentic systems](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop). Waiting for human input is a common HIL interaction pattern, allowing the agent to ask the user clarifying questions and await input before proceeding. \n",
    "\n",
    "We can implement this in LangGraph using the [`interrupt()`][langgraph.types.interrupt] function. `interrupt` allows us to stop graph execution to collect input from a user and continue execution with collected input."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic and / or OpenAI (the LLM(s) we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6cf1fad-5ab6-49c5-b0c8-15a1b6e8cf21",
   "metadata": {},
   "source": [
    "## Simple Usage\n",
    "\n",
    "Let's explore a basic example of using human feedback. A straightforward approach is to create a node, **`human_feedback`**, designed specifically to collect user input. This allows us to gather feedback at a specific, chosen point in our graph.\n",
    "\n",
    "Steps:\n",
    "\n",
    "1. **Call `interrupt()`** inside the **`human_feedback`** node.  \n",
    "2. **Set up a [checkpointer](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer)** to save the graph's state up to this node.  \n",
    "3. **Use `Command(resume=...)`** to provide the requested value to the **`human_feedback`** node and resume execution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "58eae42d-be32-48da-8d0a-ab64471657d9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# highlight-next-line\n",
    "from langgraph.types import Command, interrupt\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from IPython.display import Image, display\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "    user_feedback: str\n",
    "\n",
    "\n",
    "def step_1(state):\n",
    "    print(\"---Step 1---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def human_feedback(state):\n",
    "    print(\"---human_feedback---\")\n",
    "    # highlight-next-line\n",
    "    feedback = interrupt(\"Please provide feedback:\")\n",
    "    return {\"user_feedback\": feedback}\n",
    "\n",
    "\n",
    "def step_3(state):\n",
    "    print(\"---Step 3---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"human_feedback\", human_feedback)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"human_feedback\")\n",
    "builder.add_edge(\"human_feedback\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory)\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce0fe2bc-86fc-465f-956c-729805d50404",
   "metadata": {},
   "source": [
    "Run until our `interrupt()` at `human_feedback`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "eb8e7d47-e7c9-4217-b72c-08394a2c4d3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Step 1---\n",
      "{'step_1': None}\n",
      "\n",
      "\n",
      "---human_feedback---\n",
      "{'__interrupt__': (Interrupt(value='Please provide feedback:', resumable=True, ns=['human_feedback:baae6117-80c0-2698-6bb3-46e87ca2fd6e']),)}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"input\": \"hello world\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"updates\"):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28a7d545-ab19-4800-985b-62837d060809",
   "metadata": {},
   "source": [
    "Now, we can manually update our graph state with the user input:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3cca588f-e8d8-416b-aba7-0f3ae5e51598",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---human_feedback---\n",
      "{'human_feedback': {'user_feedback': 'go to step 3!'}}\n",
      "\n",
      "\n",
      "---Step 3---\n",
      "{'step_3': None}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Continue the graph execution\n",
    "for event in graph.stream(\n",
    "    # highlight-next-line\n",
    "    Command(resume=\"go to step 3!\"),\n",
    "    thread,\n",
    "    stream_mode=\"updates\",\n",
    "):\n",
    "    print(event)\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a75a1060-47aa-4cc6-8c41-e6ba2e9d7923",
   "metadata": {},
   "source": [
    "We can see our feedback was added to state - "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2b83e5ca-8497-43ca-bff7-7203e654c4d3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'hello world', 'user_feedback': 'go to step 3!'}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(thread).values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b22b9598-7ce4-4d16-b932-bba2bc2803ec",
   "metadata": {},
   "source": [
    "## Agent\n",
    "\n",
    "In the context of [agents](../../../concepts/agentic_concepts), waiting for user feedback is especially useful for asking clarifying questions. To illustrate this, we’ll create a simple [ReAct-style agent](../../../concepts/agentic_concepts#react-implementation) capable of [tool calling](https://python.langchain.com/docs/concepts/tool_calling/). \n",
    "\n",
    "For this example, we’ll use Anthropic's chat model along with a **mock tool** (purely for demonstration purposes)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01789855-b769-426d-a329-3cdb29684df8",
   "metadata": {},
   "source": [
    "<div class=\"admonition note\">\n",
    "    <p class=\"admonition-title\">Using Pydantic with LangChain</p>\n",
    "    <p>\n",
    "        This notebook uses Pydantic v2 <code>BaseModel</code>, which requires <code>langchain-core >= 0.3</code>. Using <code>langchain-core < 0.3</code> will result in errors due to mixing of Pydantic v1 and v2 <code>BaseModels</code>.\n",
    "    </p>\n",
    "</div>  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f5319e01",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:16:51\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:16:51\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:16:51\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set up the state\n",
    "from langgraph.graph import MessagesState, START\n",
    "\n",
    "# Set up the tool\n",
    "# We will have one real tool - a search tool\n",
    "# We'll also have one \"fake\" tool - a \"ask_human\" tool\n",
    "# Here we define any ACTUAL tools\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return f\"I looked up: {query}. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "# Set up the model\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n",
    "\n",
    "from pydantic import BaseModel\n",
    "\n",
    "\n",
    "# We are going \"bind\" all tools to the model\n",
    "# We have the ACTUAL tools from above, but we also need a mock tool to ask a human\n",
    "# Since `bind_tools` takes in tools but also just tool definitions,\n",
    "# We can define a tool definition for `ask_human`\n",
    "class AskHuman(BaseModel):\n",
    "    \"\"\"Ask the human a question\"\"\"\n",
    "\n",
    "    question: str\n",
    "\n",
    "\n",
    "model = model.bind_tools(tools + [AskHuman])\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return END\n",
    "    # If tool call is asking Human, we return that node\n",
    "    # You could also add logic here to let some system know that there's something that requires Human input\n",
    "    # For example, send a slack message, etc\n",
    "    elif last_message.tool_calls[0][\"name\"] == \"AskHuman\":\n",
    "        return \"ask_human\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"action\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# We define a fake node to ask the human\n",
    "def ask_human(state):\n",
    "    tool_call_id = state[\"messages\"][-1].tool_calls[0][\"id\"]\n",
    "    ask = AskHuman.model_validate(state[\"messages\"][-1].tool_calls[0][\"args\"])\n",
    "    # highlight-next-line\n",
    "    location = interrupt(ask.question)\n",
    "    tool_message = [{\"tool_call_id\": tool_call_id, \"type\": \"tool\", \"content\": location}]\n",
    "    return {\"messages\": tool_message}\n",
    "\n",
    "\n",
    "# Build the graph\n",
    "\n",
    "from langgraph.graph import END, StateGraph\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the three nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "workflow.add_node(\"ask_human\", ask_human)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    path_map=[\"ask_human\", \"action\", END],\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# After we get back the human response, we go back to the agent\n",
    "workflow.add_edge(\"ask_human\", \"agent\")\n",
    "\n",
    "# Set up Redis connection\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "app = workflow.compile(checkpointer=memory)\n",
    "\n",
    "display(Image(app.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
   "metadata": {},
   "source": [
    "## Interacting with the Agent\n",
    "\n",
    "We can now interact with the agent. Let's ask it to ask the user where they are, then tell them the weather. \n",
    "\n",
    "This should make it use the `ask_human` tool first, then use the normal tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cfd140f0-a5a6-4697-8115-322242f197b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Ask the user where they are, then look up the weather there\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"I'll help you with that. Let me first ask the user about their location.\", 'type': 'text'}, {'id': 'toolu_01PewfDABq8kiEkVQHQ9Ggme', 'input': {'question': 'Where are you located?'}, 'name': 'AskHuman', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  AskHuman (toolu_01PewfDABq8kiEkVQHQ9Ggme)\n",
      " Call ID: toolu_01PewfDABq8kiEkVQHQ9Ggme\n",
      "  Args:\n",
      "    question: Where are you located?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "for event in app.stream(\n",
    "    {\n",
    "        \"messages\": [\n",
    "            (\n",
    "                \"user\",\n",
    "                \"Ask the user where they are, then look up the weather there\",\n",
    "            )\n",
    "        ]\n",
    "    },\n",
    "    config,\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    if \"messages\" in event:\n",
    "        event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "924a30ea-94c0-468e-90fe-47eb9c08584d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('ask_human',)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app.get_state(config).next"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a30c9fb-2a40-45cc-87ba-406c11c9f0cf",
   "metadata": {},
   "source": [
    "You can see that our graph got interrupted inside the `ask_human` node, which is now waiting for a `location` to be provided. We can provide this value by invoking the graph with a `Command(resume=\"<location>\")` input:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a9f599b5-1a55-406b-a76b-f52b3ca06975",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"I'll help you with that. Let me first ask the user about their location.\", 'type': 'text'}, {'id': 'toolu_01PewfDABq8kiEkVQHQ9Ggme', 'input': {'question': 'Where are you located?'}, 'name': 'AskHuman', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  AskHuman (toolu_01PewfDABq8kiEkVQHQ9Ggme)\n",
      " Call ID: toolu_01PewfDABq8kiEkVQHQ9Ggme\n",
      "  Args:\n",
      "    question: Where are you located?\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "\n",
      "san francisco\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Now I'll search for the weather in San Francisco.\", 'type': 'text'}, {'id': 'toolu_01M7oa7bbUWba21rDyqCA3xB', 'input': {'query': 'current weather san francisco'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_01M7oa7bbUWba21rDyqCA3xB)\n",
      " Call ID: toolu_01M7oa7bbUWba21rDyqCA3xB\n",
      "  Args:\n",
      "    query: current weather san francisco\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: search\n",
      "\n",
      "I looked up: current weather san francisco. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "[{'text': \"Based on the search results, it's currently sunny in San Francisco. Let me be more specific and search again for more detailed weather information.\", 'type': 'text'}, {'id': 'toolu_012Hcpe3Lovcf4rZJsySpARP', 'input': {'query': 'san francisco temperature today forecast'}, 'name': 'search', 'type': 'tool_use'}]\n",
      "Tool Calls:\n",
      "  search (toolu_012Hcpe3Lovcf4rZJsySpARP)\n",
      " Call ID: toolu_012Hcpe3Lovcf4rZJsySpARP\n",
      "  Args:\n",
      "    query: san francisco temperature today forecast\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: search\n",
      "\n",
      "I looked up: san francisco temperature today forecast. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I apologize, but it seems I'm only able to confirm that it's sunny in San Francisco today. The search results aren't providing detailed temperature information. However, you can be confident that it's a sunny day in San Francisco!\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(\n",
    "    # highlight-next-line\n",
    "    Command(resume=\"san francisco\"),\n",
    "    config,\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    if \"messages\" in event:\n",
    "        event[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/memory/add-summary-conversation-history.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to add summary of the conversation history\n",
    "\n",
    "One of the most common use cases for persistence is to use it to keep track of conversation history. This is great - it makes it easy to continue conversations. As conversations get longer and longer, however, this conversation history can build up and take up more and more of the context window. This can often be undesirable as it leads to more expensive and longer calls to the LLM, and potentially ones that error. One way to work around that is to create a summary of the conversation to date, and use that with the past N messages. This guide will go through an example of how to do that.\n",
    "\n",
    "This will involve a few steps:\n",
    "\n",
    "- Check if the conversation is too long (can be done by checking number of messages or length of messages)\n",
    "- If yes, the create summary (will need a prompt for this)\n",
    "- Then remove all except the last N messages\n",
    "\n",
    "A big part of this is deleting old messages. For an in depth guide on how to do that, see [this guide](../delete-messages)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's set up the packages we're going to want to use"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84835fdb-a5f3-4c90-85f3-0e6257650aba",
   "metadata": {},
   "source": [
    "## Build the chatbot\n",
    "\n",
    "Let's now build the chatbot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "378899a9-3b9a-4748-95b6-eb00e0828677",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.messages import SystemMessage, RemoveMessage, HumanMessage\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.graph import MessagesState, StateGraph, START, END\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "# We will add a `summary` attribute (in addition to `messages` key,\n",
    "# which MessagesState already has)\n",
    "class State(MessagesState):\n",
    "    summary: str\n",
    "\n",
    "\n",
    "# We will use this model for both the conversation and the summarization\n",
    "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "\n",
    "\n",
    "# Define the logic to call the model\n",
    "def call_model(state: State):\n",
    "    # If a summary exists, we add this in as a system message\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "    if summary:\n",
    "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
    "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
    "    else:\n",
    "        messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# We now define the logic for determining whether to end or summarize the conversation\n",
    "def should_continue(state: State) -> Literal[\"summarize_conversation\", END]:\n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    messages = state[\"messages\"]\n",
    "    # If there are more than six messages, then we summarize the conversation\n",
    "    if len(messages) > 6:\n",
    "        return \"summarize_conversation\"\n",
    "    # Otherwise we can just end\n",
    "    return END\n",
    "\n",
    "\n",
    "def summarize_conversation(state: State):\n",
    "    # First, we summarize the conversation\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "    if summary:\n",
    "        # If a summary already exists, we use a different system prompt\n",
    "        # to summarize it than if one didn't\n",
    "        summary_message = (\n",
    "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
    "            \"Extend the summary by taking into account the new messages above:\"\n",
    "        )\n",
    "    else:\n",
    "        summary_message = \"Create a summary of the conversation above:\"\n",
    "\n",
    "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
    "    response = model.invoke(messages)\n",
    "    # We now need to delete messages that we no longer want to show up\n",
    "    # I will delete all but the last two messages, but you can change this\n",
    "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
    "    return {\"summary\": response.content, \"messages\": delete_messages}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(State)\n",
    "\n",
    "# Define the conversation node and the summarize node\n",
    "workflow.add_node(\"conversation\", call_model)\n",
    "workflow.add_node(summarize_conversation)\n",
    "\n",
    "# Set the entrypoint as conversation\n",
    "workflow.add_edge(START, \"conversation\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `conversation`.\n",
    "    # This means these are the edges taken after the `conversation` node is called.\n",
    "    \"conversation\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `summarize_conversation` to END.\n",
    "# This means that after `summarize_conversation` is called, we end.\n",
    "workflow.add_edge(\"summarize_conversation\", END)\n",
    "\n",
    "# Finally, we compile it!\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41c2872e-04b3-4c44-9e03-9e84a5230adf",
   "metadata": {},
   "source": [
    "## Using the graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dc697132-8fa1-4bf5-9722-56a9859331ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_update(update):\n",
    "    for k, v in update.items():\n",
    "        for m in v[\"messages\"]:\n",
    "            m.pretty_print()\n",
    "        if \"summary\" in v:\n",
    "            print(v[\"summary\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "57b27553-21be-43e5-ac48-d1d0a3aa0dca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "hi! I'm bob\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Bob! It's nice to meet you. I'm Claude, an AI assistant created by Anthropic. I'm here to help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You said your name is Bob, so that is the name I have for you.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "i like the celtics!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "That's great that you're a Celtics fan! The Celtics are a storied NBA franchise with a rich history of success. Some key things about the Celtics:\n",
      "\n",
      "- They have won 17 NBA championships, the most of any team. Their most recent title was in 2008.\n",
      "\n",
      "- They have had many all-time great players wear the Celtics jersey, including Bill Russell, Larry Bird, Paul Pierce, and more.\n",
      "\n",
      "- The Celtics-Lakers rivalry is one of the most intense in professional sports, with the two teams meeting in the Finals 12 times.\n",
      "\n",
      "- The Celtics play their home games at the TD Garden in Boston, which has a fantastic game-day atmosphere.\n",
      "\n",
      "As a fellow Celtics fan, I always enjoy discussing the team and their journey. Let me know if you have any other thoughts or opinions on the Celtics that you'd like to share!\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"4\"}}\n",
    "input_message = HumanMessage(content=\"hi! I'm bob\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)\n",
    "\n",
    "input_message = HumanMessage(content=\"i like the celtics!\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9760e219-a7fc-4d81-b4e8-1334c5afc510",
   "metadata": {},
   "source": [
    "We can see that so far no summarization has happened - this is because there are only six messages in the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "935265a0-d511-475a-8a0d-b3c3cc5e42a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content=\"hi! I'm bob\", additional_kwargs={}, response_metadata={}, id='6bb57452-d968-4ca2-b641-a72a09b7dfbf'),\n",
       "  AIMessage(content=\"Hi Bob! It's nice to meet you. I'm Claude, an AI assistant created by Anthropic. I'm here to help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\", additional_kwargs={}, response_metadata={'id': 'msg_011jBGcbsvqnA6gCXExmN1a6', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 12, 'output_tokens': 56}, 'model_name': 'claude-3-haiku-20240307'}, id='run-39f0f967-454c-4047-a3db-9196c041668b-0', usage_metadata={'input_tokens': 12, 'output_tokens': 56, 'total_tokens': 68, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}}),\n",
       "  HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='5fd5c63c-f680-45c9-ba74-ae36f0004ecd'),\n",
       "  AIMessage(content='You said your name is Bob, so that is the name I have for you.', additional_kwargs={}, response_metadata={'id': 'msg_019gbVCckc8LDkDAK7n4w8SG', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 76, 'output_tokens': 20}, 'model_name': 'claude-3-haiku-20240307'}, id='run-11232468-dc34-4f32-84a5-34de7a82f147-0', usage_metadata={'input_tokens': 76, 'output_tokens': 20, 'total_tokens': 96, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}}),\n",
       "  HumanMessage(content='i like the celtics!', additional_kwargs={}, response_metadata={}, id='0d3a5506-f36e-4008-afa9-877abe188311'),\n",
       "  AIMessage(content=\"That's great that you're a Celtics fan! The Celtics are a storied NBA franchise with a rich history of success. Some key things about the Celtics:\\n\\n- They have won 17 NBA championships, the most of any team. Their most recent title was in 2008.\\n\\n- They have had many all-time great players wear the Celtics jersey, including Bill Russell, Larry Bird, Paul Pierce, and more.\\n\\n- The Celtics-Lakers rivalry is one of the most intense in professional sports, with the two teams meeting in the Finals 12 times.\\n\\n- The Celtics play their home games at the TD Garden in Boston, which has a fantastic game-day atmosphere.\\n\\nAs a fellow Celtics fan, I always enjoy discussing the team and their journey. Let me know if you have any other thoughts or opinions on the Celtics that you'd like to share!\", additional_kwargs={}, response_metadata={'id': 'msg_01EUNtTQZHcgyST7xhhGvWX8', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 105, 'output_tokens': 199}, 'model_name': 'claude-3-haiku-20240307'}, id='run-aacbc85e-471c-4834-9726-433328240953-0', usage_metadata={'input_tokens': 105, 'output_tokens': 199, 'total_tokens': 304, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}})]}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "values = app.get_state(config).values\n",
    "values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb40eddb-9a31-4410-a4c0-9762e2d89e56",
   "metadata": {},
   "source": [
    "Now let's send another message in"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "048805a4-3d97-4e76-ac45-8d80d4364c46",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "i like how much they win\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I agree, the Celtics' consistent winning over the decades is really impressive. A few reasons why the Celtics have been so successful:\n",
      "\n",
      "- Great coaching - They've had legendary coaches like Red Auerbach, Doc Rivers, and now Ime Udoka who have gotten the most out of their talented rosters.\n",
      "\n",
      "- Sustained excellence - Unlike some teams that have short windows of success, the Celtics have been a perennial contender for the majority of their history.\n",
      "\n",
      "- Ability to reload - Even when they lose star players, the Celtics have done a great job of rebuilding and restocking their roster to remain competitive.\n",
      "\n",
      "- Knack for developing talent - Players like Larry Bird, Kevin McHale, and others have blossomed into all-time greats under the Celtics' system.\n",
      "\n",
      "The Celtics' winning culture and pedigree as an organization is really admirable. It's no wonder they have such a passionate fan base like yourself who takes pride in their sustained success over the decades. It's fun to be a fan of a team that expects to win championships year in and year out.\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "Sure, here's a summary of our conversation so far:\n",
      "\n",
      "The conversation began with me introducing myself as Claude, an AI assistant, and greeting the user who identified themselves as Bob. \n",
      "\n",
      "Bob then expressed that he likes the Boston Celtics basketball team. I responded positively, noting the Celtics' impressive history of 17 NBA championships, their storied rivalry with the Lakers, and the great atmosphere at their home games.\n",
      "\n",
      "Bob said he likes how much the Celtics win, and I agreed, explaining some of the key reasons for the Celtics' sustained success over the decades - great coaching, the ability to reload and develop talent, and the team's winning culture and high expectations.\n",
      "\n",
      "Throughout the conversation, I tried to engage with Bob's interest in the Celtics, demonstrating my knowledge of the team's history and achievements while also inviting him to share more of his thoughts and opinions as a fan.\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"i like how much they win\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b196367-6151-4982-9430-3db7373de06e",
   "metadata": {},
   "source": [
    "If we check the state now, we can see that we have a summary of the conversation, as well as the last two messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "09ebb693-4738-4474-a095-6491def5c5f9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='i like how much they win', additional_kwargs={}, response_metadata={}, id='26916ba3-a474-48ec-a3d2-0da1d3b9f433'),\n",
       "  AIMessage(content=\"I agree, the Celtics' consistent winning over the decades is really impressive. A few reasons why the Celtics have been so successful:\\n\\n- Great coaching - They've had legendary coaches like Red Auerbach, Doc Rivers, and now Ime Udoka who have gotten the most out of their talented rosters.\\n\\n- Sustained excellence - Unlike some teams that have short windows of success, the Celtics have been a perennial contender for the majority of their history.\\n\\n- Ability to reload - Even when they lose star players, the Celtics have done a great job of rebuilding and restocking their roster to remain competitive.\\n\\n- Knack for developing talent - Players like Larry Bird, Kevin McHale, and others have blossomed into all-time greats under the Celtics' system.\\n\\nThe Celtics' winning culture and pedigree as an organization is really admirable. It's no wonder they have such a passionate fan base like yourself who takes pride in their sustained success over the decades. It's fun to be a fan of a team that expects to win championships year in and year out.\", additional_kwargs={}, response_metadata={'id': 'msg_01Pnf5fNM12szy1j2BSmfgsm', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 313, 'output_tokens': 245}, 'model_name': 'claude-3-haiku-20240307'}, id='run-2bfb8b79-1097-4fc4-bf49-256c08442556-0', usage_metadata={'input_tokens': 313, 'output_tokens': 245, 'total_tokens': 558, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}})],\n",
       " 'summary': \"Sure, here's a summary of our conversation so far:\\n\\nThe conversation began with me introducing myself as Claude, an AI assistant, and greeting the user who identified themselves as Bob. \\n\\nBob then expressed that he likes the Boston Celtics basketball team. I responded positively, noting the Celtics' impressive history of 17 NBA championships, their storied rivalry with the Lakers, and the great atmosphere at their home games.\\n\\nBob said he likes how much the Celtics win, and I agreed, explaining some of the key reasons for the Celtics' sustained success over the decades - great coaching, the ability to reload and develop talent, and the team's winning culture and high expectations.\\n\\nThroughout the conversation, I tried to engage with Bob's interest in the Celtics, demonstrating my knowledge of the team's history and achievements while also inviting him to share more of his thoughts and opinions as a fan.\"}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "values = app.get_state(config).values\n",
    "values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "966e4177-c0fc-4fd0-a494-dd03f7f2fddb",
   "metadata": {},
   "source": [
    "We can now resume having a conversation! Note that even though we only have the last two messages, we can still ask it questions about things mentioned earlier in the conversation (because we summarized those)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7094c5ab-66f8-42ff-b1c3-90c8a9468e62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You haven't explicitly told me your name in our conversation, so I don't know what your name is. I addressed you as \"Bob\" earlier based on the context, but I don't have definitive information about your actual name. If you let me know your name, I'll be happy to refer to you by it going forward.\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "40e5db8e-9db9-4ac7-9d76-a99fd4034bf3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what NFL team do you think I like?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hmm, without any additional information about your preferences, it's hard for me to confidently guess which NFL team you might like. There are so many great NFL franchises, each with their own passionate fanbases. \n",
      "\n",
      "Since we've been discussing your interest in the Boston Celtics, one possibility could be that you're a fan of another New England team, like the Patriots. Their success over the past couple of decades has certainly earned them a large and devoted following.\n",
      "\n",
      "Alternatively, you could be a fan of a team with a strong connection to basketball, like the Dallas Cowboys which play in the same stadium as the NBA's Mavericks.\n",
      "\n",
      "Or you might support an underdog team that's been on the rise, like the Cincinnati Bengals or Jacksonville Jaguars, who have developed exciting young cores.\n",
      "\n",
      "Really, without more context about your background or other sports/team interests, I don't want to make an assumption. I'm happy to continue our conversation and see if any clues emerge about which NFL franchise you might root for. What do you think - any hints you can provide?\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"what NFL team do you think I like?\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "0a1a0fda-5309-45f0-9465-9f3dff604d74",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "i like the patriots!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Ah I see, that makes a lot of sense! As a fellow Boston sports fan, it's great to hear that you're also a supporter of the New England Patriots.\n",
      "\n",
      "The Patriots have been one of the most dominant and consistent franchises in the NFL over the past two decades, with 6 Super Bowl championships during the Tom Brady and Bill Belichick era. Their sustained excellence and championship pedigree is really impressive.\n",
      "\n",
      "Some of the things that make the Patriots such an appealing team to root for:\n",
      "\n",
      "- Winning culture and high expectations year after year\n",
      "- Innovative, adaptable game-planning and coaching from Belichick\n",
      "- Clutch performances from legendary players like Brady, Gronkowski, etc.\n",
      "- Passionate, loyal fanbase in the New England region\n",
      "\n",
      "It's always fun to be a fan of a team that is consistently in contention for the title. As a fellow Boston sports enthusiast, I can understand the pride and excitement of cheering on the Patriots. Their success has been truly remarkable.\n",
      "\n",
      "Does the Patriots' sustained dominance over the past 20+ years resonate with you as a fan? I'd be curious to hear more about what you enjoy most about following them.\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "================================\u001b[1m Remove Message \u001b[0m================================\n",
      "\n",
      "\n",
      "Extending the summary based on the new messages:\n",
      "\n",
      "After discussing the Celtics, I then asked Bob what his name was, and he did not provide it. I noted that I had previously addressed him as \"Bob\" based on the context, but did not have definitive information about his actual name.\n",
      "\n",
      "I then asked Bob what NFL team he thought he might like, since he was a fan of the Boston Celtics. Without any additional clues, I speculated that he could be a fan of other New England teams like the Patriots, or a team with ties to basketball. \n",
      "\n",
      "Bob then revealed that he is indeed a fan of the New England Patriots, which made sense given his interest in other Boston sports teams. I expressed my understanding of why the Patriots' sustained success and winning culture would appeal to a Boston sports fan like himself.\n",
      "\n",
      "I asked Bob to share more about what he enjoys most about being a Patriots fan, given their two decades of dominance under Tom Brady and Bill Belichick. I emphasized my appreciation for the Patriots' impressive accomplishments and the passion of their fanbase.\n",
      "\n",
      "Throughout this extended exchange, I aimed to have a friendly, engaging dialogue where I demonstrated my knowledge of sports teams and their histories, while also inviting Bob to contribute his own perspectives and experiences as a fan. The conversation flowed naturally between discussing the Celtics and then transitioning to the Patriots.\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"i like the patriots!\")\n",
    "input_message.pretty_print()\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"updates\"):\n",
    "    print_update(event)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/memory/delete-messages.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to delete messages\n",
    "\n",
    "One of the common states for a graph is a list of messages. Usually you only add messages to that state. However, sometimes you may want to remove messages (either by directly modifying the state or as part of the graph). To do that, you can use the `RemoveMessage` modifier. In this guide, we will cover how to do that.\n",
    "\n",
    "The key idea is that each state key has a `reducer` key. This key specifies how to combine updates to the state. The default `MessagesState` has a messages key, and the reducer for that key accepts these `RemoveMessage` modifiers. That reducer then uses these `RemoveMessage` to delete messages from the key.\n",
    "\n",
    "So note that just because your graph state has a key that is a list of messages, it doesn't mean that that this `RemoveMessage` modifier will work. You also have to have a `reducer` defined that knows how to work with this.\n",
    "\n",
    "**NOTE**: Many models expect certain rules around lists of messages. For example, some expect them to start with a `user` message, others expect all messages with tool calls to be followed by a tool message. **When deleting messages, you will want to make sure you don't violate these rules.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's build a simple graph that uses messages. Note that it's using the `MessagesState` which has the required `reducer`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4767ef1c-a7cf-41f8-a301-558988cb7ac5",
   "metadata": {},
   "source": [
    "## Build the agent\n",
    "Let's now build a simple ReAct style agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "378899a9-3b9a-4748-95b6-eb00e0828677",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.graph import MessagesState, StateGraph, START, END\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "bound_model = model.bind_tools(tools)\n",
    "\n",
    "\n",
    "def should_continue(state: MessagesState):\n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return END\n",
    "    # Otherwise if there is, we continue\n",
    "    return \"action\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state: MessagesState):\n",
    "    response = model.invoke(state[\"messages\"])\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Next, we pass in the path map - all the possible nodes this edge could go to\n",
    "    [\"action\", END],\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "57b27553-21be-43e5-ac48-d1d0a3aa0dca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "hi! I'm bob\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "It's nice to meet you, Bob! As an AI assistant, I'm here to help you with any questions or tasks you may have. Please feel free to ask me anything, and I'll do my best to assist you.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You told me your name is Bob.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "input_message = HumanMessage(content=\"hi! I'm bob\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fb0de5b-30ec-42d4-813a-7ad63fe1c367",
   "metadata": {},
   "source": [
    "## Manually deleting messages\n",
    "\n",
    "First, we will cover how to manually delete messages. Let's take a look at the current state of the thread:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8a850529-d038-48f7-b5a2-8d4d2923f83a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content=\"hi! I'm bob\", additional_kwargs={}, response_metadata={}, id='a17d82c0-7fe1-4896-9640-060f2c35cbb7'),\n",
       " AIMessage(content=\"It's nice to meet you, Bob! As an AI assistant, I'm here to help you with any questions or tasks you may have. Please feel free to ask me anything, and I'll do my best to assist you.\", additional_kwargs={}, response_metadata={'id': 'msg_01B37ymr999e6yd2RX4wnC7y', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 12, 'output_tokens': 50}, 'model_name': 'claude-3-haiku-20240307'}, id='run-09073daa-b991-488a-ac81-5d94627d9e07-0', usage_metadata={'input_tokens': 12, 'output_tokens': 50, 'total_tokens': 62, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}}),\n",
       " HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='9a05305e-2e78-473b-9fc5-47a5e0533864'),\n",
       " AIMessage(content='You told me your name is Bob.', additional_kwargs={}, response_metadata={'id': 'msg_01GUJqbBMVdRxfRgNCdELf1x', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 70, 'output_tokens': 11}, 'model_name': 'claude-3-haiku-20240307'}, id='run-905da30e-9014-4ec0-8e1f-2eaf606adddc-0', usage_metadata={'input_tokens': 70, 'output_tokens': 11, 'total_tokens': 81, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}})]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = app.get_state(config).values[\"messages\"]\n",
    "messages"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81be8a0a-1e94-4302-bd84-d1b72e3c501c",
   "metadata": {},
   "source": [
    "We can call `update_state` and pass in the id of the first message. This will delete that message."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "df1a0970-7e64-4170-beef-2855d10eef42",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '2',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1f025359-21de-60f0-8003-97dbb738829b'}}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage\n",
    "\n",
    "app.update_state(config, {\"messages\": RemoveMessage(id=messages[0].id)})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c9127ae-0d42-42b8-957f-ea69a5da555f",
   "metadata": {},
   "source": [
    "If we now look at the messages, we can verify that the first one was deleted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8bfe4ffa-e170-43bc-aec4-6e36ac620931",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content=\"It's nice to meet you, Bob! As an AI assistant, I'm here to help you with any questions or tasks you may have. Please feel free to ask me anything, and I'll do my best to assist you.\", additional_kwargs={}, response_metadata={'id': 'msg_01B37ymr999e6yd2RX4wnC7y', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 12, 'output_tokens': 50}, 'model_name': 'claude-3-haiku-20240307'}, id='run-09073daa-b991-488a-ac81-5d94627d9e07-0', usage_metadata={'input_tokens': 12, 'output_tokens': 50, 'total_tokens': 62, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}}),\n",
       " HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='9a05305e-2e78-473b-9fc5-47a5e0533864'),\n",
       " AIMessage(content='You told me your name is Bob.', additional_kwargs={}, response_metadata={'id': 'msg_01GUJqbBMVdRxfRgNCdELf1x', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 70, 'output_tokens': 11}, 'model_name': 'claude-3-haiku-20240307'}, id='run-905da30e-9014-4ec0-8e1f-2eaf606adddc-0', usage_metadata={'input_tokens': 70, 'output_tokens': 11, 'total_tokens': 81, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}})]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = app.get_state(config).values[\"messages\"]\n",
    "messages"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef129a75-4cad-44d7-b532-eb37b0553c0c",
   "metadata": {},
   "source": [
    "## Programmatically deleting messages\n",
    "\n",
    "We can also delete messages programmatically from inside the graph. Here we'll modify the graph to delete any old messages (longer than 3 messages ago) at the end of a graph run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "bb22ede0-e153-4fd0-a4c0-f9af2f7663b1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:07:26\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:07:26\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:07:26\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage\n",
    "from langgraph.graph import END\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "def delete_messages(state):\n",
    "    messages = state[\"messages\"]\n",
    "    if len(messages) > 3:\n",
    "        return {\"messages\": [RemoveMessage(id=m.id) for m in messages[:-3]]}\n",
    "\n",
    "\n",
    "# We need to modify the logic to call delete_messages rather than end right away\n",
    "def should_continue(state: MessagesState) -> Literal[\"action\", \"delete_messages\"]:\n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    # If there is no function call, then we call our delete_messages function\n",
    "    if not last_message.tool_calls:\n",
    "        return \"delete_messages\"\n",
    "    # Otherwise if there is, we continue\n",
    "    return \"action\"\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# This is our new node we're defining\n",
    "workflow.add_node(delete_messages)\n",
    "\n",
    "\n",
    "workflow.add_edge(START, \"agent\")\n",
    "workflow.add_conditional_edges(\n",
    "    \"agent\",\n",
    "    should_continue,\n",
    ")\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# This is the new edge we're adding: after we delete messages, we finish\n",
    "workflow.add_edge(\"delete_messages\", END)\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52cbdef6-7db7-45a2-8194-de4f8929bd1f",
   "metadata": {},
   "source": [
    "We can now try this out. We can call the graph twice and then check the state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3975f34c-c243-40ea-b9d2-424d50a48dc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('human', \"hi! I'm bob\")]\n",
      "[('human', \"hi! I'm bob\"), ('ai', \"It's nice to meet you, Bob! As an AI assistant, I don't have a physical form, but I'm always happy to chat and help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\")]\n",
      "[('human', \"hi! I'm bob\"), ('ai', \"It's nice to meet you, Bob! As an AI assistant, I don't have a physical form, but I'm always happy to chat and help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\"), ('human', \"what's my name?\")]\n",
      "[('human', \"hi! I'm bob\"), ('ai', \"It's nice to meet you, Bob! As an AI assistant, I don't have a physical form, but I'm always happy to chat and help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\"), ('human', \"what's my name?\"), ('ai', 'You told me your name is Bob, so your name is Bob.')]\n",
      "[('ai', \"It's nice to meet you, Bob! As an AI assistant, I don't have a physical form, but I'm always happy to chat and help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\"), ('human', \"what's my name?\"), ('ai', 'You told me your name is Bob, so your name is Bob.')]\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "input_message = HumanMessage(content=\"hi! I'm bob\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    print([(message.type, message.content) for message in event[\"messages\"]])\n",
    "\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    print([(message.type, message.content) for message in event[\"messages\"]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67b2fd2a-14a1-4c47-8632-f8cbb0ba1d35",
   "metadata": {},
   "source": [
    "If we now check the state, we should see that it is only three messages long. This is because we just deleted the earlier messages - otherwise it would be four!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a3e15abb-81d8-4072-9f10-61ae0fd61dac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content=\"It's nice to meet you, Bob! As an AI assistant, I don't have a physical form, but I'm always happy to chat and help out however I can. Please let me know if you have any questions or if there's anything I can assist you with.\", additional_kwargs={}, response_metadata={'id': 'msg_01NX4B5nswy32CoYTyCFugsF', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 12, 'output_tokens': 59}, 'model_name': 'claude-3-haiku-20240307'}, id='run-9e65ccb3-743e-4498-90cd-38081ca077d4-0', usage_metadata={'input_tokens': 12, 'output_tokens': 59, 'total_tokens': 71, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}}),\n",
       " HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='fb3aac2e-ec62-416c-aefe-891f0830cbd3'),\n",
       " AIMessage(content='You told me your name is Bob, so your name is Bob.', additional_kwargs={}, response_metadata={'id': 'msg_01WHWB3SkMQSXKAr1KJgf1Wh', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 79, 'output_tokens': 17}, 'model_name': 'claude-3-haiku-20240307'}, id='run-b46fb921-e936-452e-9598-b61a36c4bf18-0', usage_metadata={'input_tokens': 79, 'output_tokens': 17, 'total_tokens': 96, 'input_token_details': {'cache_creation': 0, 'cache_read': 0}})]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = app.get_state(config).values[\"messages\"]\n",
    "messages"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "359cfeae-d43a-46ee-9069-a1cab9a5720a",
   "metadata": {},
   "source": [
    "Remember, when deleting messages you will want to make sure that the remaining message list is still valid. This message list **may actually not be** - this is because it currently starts with an AI message, which some models do not allow."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/memory/manage-conversation-history.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to manage conversation history\n",
    "\n",
    "One of the most common use cases for persistence is to use it to keep track of conversation history. This is great - it makes it easy to continue conversations. As conversations get longer and longer, however, this conversation history can build up and take up more and more of the context window. This can often be undesirable as it leads to more expensive and longer calls to the LLM, and potentially ones that error. In order to prevent this from happening, you need to properly manage the conversation history.\n",
    "\n",
    "Note: this guide focuses on how to do this in LangGraph, where you can fully customize how this is done. If you want a more off-the-shelf solution, you can look into functionality provided in LangChain:\n",
    "\n",
    "- [How to filter messages](https://python.langchain.com/docs/how_to/filter_messages/)\n",
    "- [How to trim messages](https://python.langchain.com/docs/how_to/trim_messages/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's set up the packages we're going to want to use"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for Anthropic (the LLM we will use)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4767ef1c-a7cf-41f8-a301-558988cb7ac5",
   "metadata": {},
   "source": [
    "## Build the agent\n",
    "Let's now build a simple ReAct style agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "378899a9-3b9a-4748-95b6-eb00e0828677",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.graph import MessagesState, StateGraph, START, END\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "bound_model = model.bind_tools(tools)\n",
    "\n",
    "\n",
    "def should_continue(state: MessagesState):\n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return END\n",
    "    # Otherwise if there is, we continue\n",
    "    return \"action\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state: MessagesState):\n",
    "    response = bound_model.invoke(state[\"messages\"])\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Next, we pass in the path map - all the possible nodes this edge could go to\n",
    "    [\"action\", END],\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "57b27553-21be-43e5-ac48-d1d0a3aa0dca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "hi! I'm bob\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Bob! It's nice to meet you. How can I assist you today?\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You said your name is Bob, so your name is Bob.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "input_message = HumanMessage(content=\"hi! I'm bob\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d5da4c9-ba8b-46cb-a860-63fe585d15c5",
   "metadata": {},
   "source": [
    "## Filtering messages\n",
    "\n",
    "The most straight-forward thing to do to prevent conversation history from blowing up is to filter the list of messages before they get passed to the LLM. This involves two parts: defining a function to filter messages, and then adding it to the graph. See the example below which defines a really simple `filter_messages` function and then uses it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "eb20430f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:08:12\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:08:12\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:08:12\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "from typing import Literal\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.graph import MessagesState, StateGraph, START\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "\n",
    "\n",
    "tools = [search]\n",
    "tool_node = ToolNode(tools)\n",
    "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "bound_model = model.bind_tools(tools)\n",
    "\n",
    "\n",
    "def should_continue(state: MessagesState):\n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return END\n",
    "    # Otherwise if there is, we continue\n",
    "    return \"action\"\n",
    "\n",
    "\n",
    "def filter_messages(messages: list):\n",
    "    # This is very simple helper function which only ever uses the last message\n",
    "    return messages[-1:]\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state: MessagesState):\n",
    "    messages = filter_messages(state[\"messages\"])\n",
    "    response = bound_model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Next, we pass in the pathmap - all the possible nodes this edge could go to\n",
    "    [\"action\", END],\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "52468ebb-4b23-45ac-a98e-b4439f37740a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "hi! I'm bob\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Nice to meet you, Bob! It's a pleasure to chat with you. As an AI assistant, I'm here to help you with any tasks or queries you may have. Please feel free to ask me anything, and I'll do my best to assist you.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I'm afraid I don't actually know your name. As an AI assistant, I don't have personal information about you unless you provide it to me.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "input_message = HumanMessage(content=\"hi! I'm bob\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "# This will now not remember the previous messages\n",
    "# (because we set `messages[-1:]` in the filter messages argument)\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "454102b6-7112-4710-aa08-ba675e8be14c",
   "metadata": {},
   "source": [
    "In the above example we defined the `filter_messages` function ourselves. We also provide off-the-shelf ways to trim and filter messages in LangChain. \n",
    "\n",
    "- [How to filter messages](https://python.langchain.com/docs/how_to/filter_messages/)\n",
    "- [How to trim messages](https://python.langchain.com/docs/how_to/trim_messages/)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/memory/semantic-search.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to add semantic search to your agent's memory\n",
    "\n",
    "This guide shows how to enable semantic search in your agent's memory store. This lets search for items in the store by semantic similarity.\n",
    "\n",
    "!!! tip Prerequisites\n",
    "    This guide assumes familiarity with the [memory in LangGraph](https://langchain-ai.github.io/langgraph/concepts/memory/).\n",
    "\n",
    "> **Note**: This notebook uses different namespaces (`user_123`, `user_456`, etc.) for different examples to avoid conflicts between stored memories. Each example demonstrates a specific feature in isolation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph langchain-openai langchain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, create the store with an [index configuration](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.IndexConfig). By default, stores are configured without semantic/vector search. You can opt in to indexing items when creating the store by providing an [IndexConfig](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.IndexConfig) to the store's constructor. If your store class does not implement this interface, or if you do not pass in an index configuration, semantic search is disabled, and all `index` arguments passed to `put` or `aput` will have no effect. Below is an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1484/3301134131.py:6: LangChainBetaWarning: The function `init_embeddings` is in beta. It is actively being worked on, so the API may change.\n",
      "  embeddings = init_embeddings(\"openai:text-embedding-3-small\")\n"
     ]
    }
   ],
   "source": [
    "from langchain.embeddings import init_embeddings\n",
    "from langgraph.store.redis import RedisStore\n",
    "from langgraph.store.base import IndexConfig\n",
    "\n",
    "# Create Redis store with semantic search enabled\n",
    "embeddings = init_embeddings(\"openai:text-embedding-3-small\")\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "\n",
    "# Create index configuration for vector search\n",
    "index_config: IndexConfig = {\n",
    "    \"dims\": 1536,\n",
    "    \"embed\": embeddings,\n",
    "    \"ann_index_config\": {\n",
    "        \"vector_type\": \"vector\",\n",
    "    },\n",
    "    \"distance_type\": \"cosine\",\n",
    "}\n",
    "\n",
    "# Initialize the Redis store\n",
    "redis_store = None\n",
    "with RedisStore.from_conn_string(REDIS_URI, index=index_config) as s:\n",
    "    s.setup()\n",
    "    redis_store = s\n",
    "    \n",
    "store = redis_store"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's store some memories:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Store some memories\n",
    "store.put((\"user_123\", \"memories\"), \"1\", {\"text\": \"I love pizza\"})\n",
    "store.put((\"user_123\", \"memories\"), \"2\", {\"text\": \"I prefer Italian food\"})\n",
    "store.put((\"user_123\", \"memories\"), \"3\", {\"text\": \"I don't like spicy food\"})\n",
    "store.put((\"user_123\", \"memories\"), \"3\", {\"text\": \"I am studying econometrics\"})\n",
    "store.put((\"user_123\", \"memories\"), \"3\", {\"text\": \"I am a plumber\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Search memories using natural language:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Memory: I prefer Italian food (similarity: 0.46481049060799995)\n",
      "Memory: I love pizza (similarity: 0.35512423515299996)\n",
      "Memory: I am a plumber (similarity: 0.155683338642)\n"
     ]
    }
   ],
   "source": [
    "# Find memories about food preferences\n",
    "memories = store.search((\"user_123\", \"memories\"), query=\"I like food?\", limit=5)\n",
    "\n",
    "for memory in memories:\n",
    "    print(f'Memory: {memory.value[\"text\"]} (similarity: {memory.score})')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using in your agent\n",
    "\n",
    "Add semantic search to any node by injecting the store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "What are you in the mood for? Since you love pizza, would you like to have that, or are you thinking about something else?"
     ]
    }
   ],
   "source": [
    "from typing import Optional\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langgraph.store.base import BaseStore\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "from langgraph.graph import START, MessagesState, StateGraph\n",
    "\n",
    "llm = init_chat_model(\"openai:gpt-4o-mini\")\n",
    "\n",
    "\n",
    "def chat(state, *, store: BaseStore):\n",
    "    # Search based on user's last message\n",
    "    items = store.search(\n",
    "        (\"user_123\", \"memories\"), query=state[\"messages\"][-1].content, limit=2\n",
    "    )\n",
    "    memories = \"\\n\".join(item.value[\"text\"] for item in items)\n",
    "    memories = f\"## Memories of user\\n{memories}\" if memories else \"\"\n",
    "    response = llm.invoke(\n",
    "        [\n",
    "            {\"role\": \"system\", \"content\": f\"You are a helpful assistant.\\n{memories}\"},\n",
    "            *state[\"messages\"],\n",
    "        ]\n",
    "    )\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "builder = StateGraph(MessagesState)\n",
    "builder.add_node(chat)\n",
    "builder.add_edge(START, \"chat\")\n",
    "graph = builder.compile(checkpointer=checkpointer, store=store)\n",
    "\n",
    "# Add required configuration parameters\n",
    "config = {\"configurable\": {\"thread_id\": \"semantic_search_thread\"}}\n",
    "for message, metadata in graph.stream(\n",
    "    input={\"messages\": [{\"role\": \"user\", \"content\": \"I'm hungry\"}]},\n",
    "    config=config,  # Add this line with required config\n",
    "    stream_mode=\"messages\",\n",
    "):\n",
    "    print(message.content, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using in `create_react_agent` {#using-in-create-react-agent}\n",
    "\n",
    "Add semantic search to your tool calling agent by injecting the store in the `prompt` function. You can also use the store in a tool to let your agent manually store or search for memories."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:09:05\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:09:05\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:09:05\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "import uuid\n",
    "from typing import Optional\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langgraph.prebuilt import InjectedStore\n",
    "from langgraph.store.base import BaseStore\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from typing_extensions import Annotated\n",
    "\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "\n",
    "def prepare_messages(state, *, store: BaseStore):\n",
    "    # Search based on user's last message\n",
    "    items = store.search(\n",
    "        (\"user_123\", \"memories\"), query=state[\"messages\"][-1].content, limit=2\n",
    "    )\n",
    "    memories = \"\\n\".join(item.value[\"text\"] for item in items)\n",
    "    memories = f\"## Memories of user\\n{memories}\" if memories else \"\"\n",
    "    return [\n",
    "        {\"role\": \"system\", \"content\": f\"You are a helpful assistant.\\n{memories}\"}\n",
    "    ] + state[\"messages\"]\n",
    "\n",
    "\n",
    "# You can also use the store directly within a tool!\n",
    "def upsert_memory(\n",
    "    content: str,\n",
    "    *,\n",
    "    memory_id: Optional[uuid.UUID] = None,\n",
    "    store: Annotated[BaseStore, InjectedStore],\n",
    "):\n",
    "    \"\"\"Upsert a memory in the database.\"\"\"\n",
    "    # The LLM can use this tool to store a new memory\n",
    "    mem_id = memory_id or uuid.uuid4()\n",
    "    store.put(\n",
    "        (\"user_123\", \"memories\"),\n",
    "        key=str(mem_id),\n",
    "        value={\"text\": content},\n",
    "    )\n",
    "    return f\"Stored memory {mem_id}\"\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "agent = create_react_agent(\n",
    "    init_chat_model(\"openai:gpt-4o-mini\"),\n",
    "    tools=[upsert_memory],\n",
    "    # The 'prompt' function is run to prepare the messages for the LLM. It is called\n",
    "    # right before each LLM call\n",
    "    prompt=prepare_messages,\n",
    "    checkpointer=checkpointer,\n",
    "    store=store,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Based on your memories, you have a preference for Italian food, and you specifically love pizza."
     ]
    }
   ],
   "source": [
    "# Alternative approach using agent\n",
    "config = {\"configurable\": {\"thread_id\": \"semantic_search_thread_agent\"}}\n",
    "try:\n",
    "    # Run the agent with proper configuration\n",
    "    for message, metadata in agent.stream(\n",
    "        input={\"messages\": [{\"role\": \"user\", \"content\": \"Tell me about my food preferences based on my memories\"}]},\n",
    "        config=config,  # This is required for the checkpointer\n",
    "        stream_mode=\"messages\",\n",
    "    ):\n",
    "        print(message.content, end=\"\")\n",
    "except Exception as e:\n",
    "    print(f\"Error running agent: {e}\")\n",
    "    # Try with different configuration if needed\n",
    "    config = {\"configurable\": {\"thread_id\": \"semantic_search_thread_agent\", \"checkpoint_ns\": \"\", \"checkpoint_id\": \"\"}}\n",
    "    for message, metadata in agent.stream(\n",
    "        input={\"messages\": [{\"role\": \"user\", \"content\": \"Tell me about my food preferences based on my memories\"}]},\n",
    "        config=config,\n",
    "        stream_mode=\"messages\",\n",
    "    ):\n",
    "        print(message.content, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Advanced Usage\n",
    "\n",
    "#### Multi-vector indexing\n",
    "\n",
    "Store and search different aspects of memories separately to improve recall or omit certain fields from being indexed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:09:08\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:09:08\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "Expect mem 2\n",
      "Item: mem2; Score (0.589500546455)\n",
      "Memory: Ate alone at home\n",
      "Emotion: felt a bit lonely\n",
      "\n",
      "Expect mem1\n",
      "Item: mem2; Score (0.23533040285100004)\n",
      "Memory: Ate alone at home\n",
      "Emotion: felt a bit lonely\n",
      "\n",
      "Expect random lower score (ravioli not indexed)\n",
      "Item: mem2; Score (0.15017718076700004)\n",
      "Memory: Ate alone at home\n",
      "Emotion: felt a bit lonely\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Configure Redis store to embed both memory content and emotional context\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "with RedisStore.from_conn_string(\n",
    "    REDIS_URI, \n",
    "    index={\"embed\": embeddings, \"dims\": 1536, \"fields\": [\"memory\", \"emotional_context\"]}\n",
    ") as store:\n",
    "    store.setup()\n",
    "    \n",
    "    # Store memories with different content/emotion pairs\n",
    "    # Use a different namespace to avoid conflicts with previous examples\n",
    "    store.put(\n",
    "        (\"user_456\", \"multi_vector_memories\"),\n",
    "        \"mem1\",\n",
    "        {\n",
    "            \"memory\": \"Had pizza with friends at Mario's\",\n",
    "            \"emotional_context\": \"felt happy and connected\",\n",
    "            \"this_isnt_indexed\": \"I prefer ravioli though\",\n",
    "        },\n",
    "    )\n",
    "    store.put(\n",
    "        (\"user_456\", \"multi_vector_memories\"),\n",
    "        \"mem2\",\n",
    "        {\n",
    "            \"memory\": \"Ate alone at home\",\n",
    "            \"emotional_context\": \"felt a bit lonely\",\n",
    "            \"this_isnt_indexed\": \"I like pie\",\n",
    "        },\n",
    "    )\n",
    "\n",
    "    # Search focusing on emotional state - matches mem2\n",
    "    results = store.search(\n",
    "        (\"user_456\", \"multi_vector_memories\"), query=\"times they felt isolated\", limit=1\n",
    "    )\n",
    "    print(\"Expect mem 2\")\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Emotion: {r.value['emotional_context']}\\n\")\n",
    "\n",
    "    # Search focusing on social eating - matches mem1\n",
    "    print(\"Expect mem1\")\n",
    "    results = store.search(\n",
    "        (\"user_456\", \"multi_vector_memories\"), query=\"fun pizza\", limit=1\n",
    "    )\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Emotion: {r.value['emotional_context']}\\n\")\n",
    "\n",
    "    print(\"Expect random lower score (ravioli not indexed)\")\n",
    "    results = store.search(\n",
    "        (\"user_456\", \"multi_vector_memories\"), query=\"ravioli\", limit=1\n",
    "    )\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Emotion: {r.value['emotional_context']}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Override fields at storage time\n",
    "You can override which fields to embed when storing a specific memory using `put(..., index=[...fields])`, regardless of the store's default configuration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:09:10\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:09:10\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "Expect mem1\n",
      "Item: mem1; Score (0.337496995926)\n",
      "Memory: I love spicy food\n",
      "Context: At a Thai restaurant\n",
      "\n",
      "Expect mem2\n",
      "Item: mem2; Score (0.36791670322400005)\n",
      "Memory: The restaurant was too loud\n",
      "Context: Dinner at an Italian place\n",
      "\n"
     ]
    }
   ],
   "source": [
    "REDIS_URI = \"redis://redis:6379\"\n",
    "with RedisStore.from_conn_string(\n",
    "    REDIS_URI,\n",
    "    index={\n",
    "        \"embed\": embeddings,\n",
    "        \"dims\": 1536,\n",
    "        \"fields\": [\"memory\"],\n",
    "    }  # Default to embed memory field\n",
    ") as store:\n",
    "    store.setup()\n",
    "    \n",
    "    # Store one memory with default indexing\n",
    "    # Use a different namespace to avoid conflicts with previous examples\n",
    "    store.put(\n",
    "        (\"user_789\", \"override_field_memories\"),\n",
    "        \"mem1\",\n",
    "        {\"memory\": \"I love spicy food\", \"context\": \"At a Thai restaurant\"},\n",
    "    )\n",
    "\n",
    "    # Store another overriding which fields to embed\n",
    "    store.put(\n",
    "        (\"user_789\", \"override_field_memories\"),\n",
    "        \"mem2\",\n",
    "        {\"memory\": \"The restaurant was too loud\", \"context\": \"Dinner at an Italian place\"},\n",
    "        index=[\"context\"],  # Override: only embed the context\n",
    "    )\n",
    "\n",
    "    # Search about food - matches mem1 (using default field)\n",
    "    print(\"Expect mem1\")\n",
    "    results = store.search(\n",
    "        (\"user_789\", \"override_field_memories\"), query=\"what food do they like\", limit=1\n",
    "    )\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Context: {r.value['context']}\\n\")\n",
    "\n",
    "    # Search about restaurant atmosphere - matches mem2 (using overridden field)\n",
    "    print(\"Expect mem2\")\n",
    "    results = store.search(\n",
    "        (\"user_789\", \"override_field_memories\"), query=\"restaurant environment\", limit=1\n",
    "    )\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Context: {r.value['context']}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Disable Indexing for Specific Memories\n",
    "\n",
    "Some memories shouldn't be searchable by content. You can disable indexing for these while still storing them using \n",
    "`put(..., index=False)`. Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:09:11\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:09:11\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "Expect mem1\n",
      "Item: mem1; Score (0.32269132137300005)\n",
      "Memory: I love chocolate ice cream\n",
      "Type: preference\n",
      "\n",
      "Expect low score (mem2 not indexed)\n",
      "Item: mem1; Score (0.010228455066999986)\n",
      "Memory: I love chocolate ice cream\n",
      "Type: preference\n",
      "\n"
     ]
    }
   ],
   "source": [
    "REDIS_URI = \"redis://redis:6379\"\n",
    "with RedisStore.from_conn_string(\n",
    "    REDIS_URI,\n",
    "    index={\"embed\": embeddings, \"dims\": 1536, \"fields\": [\"memory\"]}\n",
    ") as store:\n",
    "    store.setup()\n",
    "    \n",
    "    # Store a normal indexed memory\n",
    "    # Use a different namespace to avoid conflicts with previous examples\n",
    "    store.put(\n",
    "        (\"user_999\", \"disable_index_memories\"),\n",
    "        \"mem1\",\n",
    "        {\"memory\": \"I love chocolate ice cream\", \"type\": \"preference\"},\n",
    "    )\n",
    "\n",
    "    # Store a system memory without indexing\n",
    "    store.put(\n",
    "        (\"user_999\", \"disable_index_memories\"),\n",
    "        \"mem2\",\n",
    "        {\"memory\": \"User completed onboarding\", \"type\": \"system\"},\n",
    "        index=False,  # Disable indexing entirely\n",
    "    )\n",
    "\n",
    "    # Search about food preferences - finds mem1\n",
    "    print(\"Expect mem1\")\n",
    "    results = store.search((\"user_999\", \"disable_index_memories\"), query=\"what food preferences\", limit=1)\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Type: {r.value['type']}\\n\")\n",
    "\n",
    "    # Search about onboarding - won't find mem2 (not indexed)\n",
    "    print(\"Expect low score (mem2 not indexed)\")\n",
    "    results = store.search((\"user_999\", \"disable_index_memories\"), query=\"onboarding status\", limit=1)\n",
    "    for r in results:\n",
    "        print(f\"Item: {r.key}; Score ({r.score})\")\n",
    "        print(f\"Memory: {r.value['memory']}\")\n",
    "        print(f\"Type: {r.value['type']}\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}

================
File: examples/.gitignore
================
# .gitignore for /docs directory
.ipynb_checkpoints
*.pyc
__pycache__

================
File: examples/create-react-agent-hitl.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
   "metadata": {},
   "source": [
    "# How to add human-in-the-loop processes to the prebuilt ReAct agent\n",
    "\n",
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/\">\n",
    "                    Human-in-the-loop\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/\">\n",
    "                    Agent Architectures\n",
    "                </a>                   \n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://python.langchain.com/docs/concepts/chat_models/\">\n",
    "                    Chat Models\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://python.langchain.com/docs/concepts/tools/\">\n",
    "                    Tools\n",
    "                </a>\n",
    "            </li>            \n",
    "        </ul>\n",
    "    </p>\n",
    "</div> \n",
    "\n",
    "This guide will show how to add human-in-the-loop processes to the prebuilt ReAct agent. Please see [this tutorial](../create-react-agent) for how to get started with the prebuilt ReAct agent\n",
    "\n",
    "You can add a a breakpoint before tools are called by passing `interrupt_before=[\"tools\"]` to `create_react_agent`. Note that you need to be using a checkpointer for this to work."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's install the required packages and set our API keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a213e11a-5c62-4ddb-a707-490d91add383",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph langchain-openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "23a1885c-04ab-4750-aefa-105891fddf3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4c5c054",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
   "metadata": {},
   "source": [
    "## Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "7a154152-973e-4b5d-aa13-48c617744a4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# First we initialize the model we want to use.\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
    "\n",
    "\n",
    "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
    "from typing import Literal\n",
    "\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_weather(location: str):\n",
    "    \"\"\"Use this to get weather information from a given location.\"\"\"\n",
    "    if location.lower() in [\"nyc\", \"new york\"]:\n",
    "        return \"It might be cloudy in nyc\"\n",
    "    elif location.lower() in [\"sf\", \"san francisco\"]:\n",
    "        return \"It's always sunny in sf\"\n",
    "    else:\n",
    "        raise AssertionError(\"Unknown Location\")\n",
    "\n",
    "\n",
    "tools = [get_weather]\n",
    "\n",
    "# We need a checkpointer to enable human-in-the-loop patterns\n",
    "# Using Redis checkpointer for persistence\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Define the graph\n",
    "\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "graph = create_react_agent(\n",
    "    model, tools=tools, interrupt_before=[\"tools\"], checkpointer=memory\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00407425-506d-4ffd-9c86-987921d8c844",
   "metadata": {},
   "source": [
    "## Usage\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_stream(stream):\n",
    "    \"\"\"A utility to pretty print the stream.\"\"\"\n",
    "    for s in stream:\n",
    "        message = s[\"messages\"][-1]\n",
    "        if isinstance(message, tuple):\n",
    "            print(message)\n",
    "        else:\n",
    "            message.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what is the weather in SF, CA?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (call_UXoJGnV30VwVoT0W1KNcdvi1)\n",
      " Call ID: call_UXoJGnV30VwVoT0W1KNcdvi1\n",
      "  Args:\n",
      "    location: SF, CA\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"42\"}}\n",
    "inputs = {\"messages\": [(\"user\", \"what is the weather in SF, CA?\")]}\n",
    "\n",
    "print_stream(graph.stream(inputs, config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca40a719",
   "metadata": {},
   "source": [
    "We can verify that our graph stopped at the right place:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Next step:  ('tools',)\n"
     ]
    }
   ],
   "source": [
    "snapshot = graph.get_state(config)\n",
    "print(\"Next step: \", snapshot.next)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7de6ca78",
   "metadata": {},
   "source": [
    "Now we can either approve or edit the tool call before proceeding to the next node. If we wanted to approve the tool call, we would simply continue streaming the graph with `None` input. If we wanted to edit the tool call we need to update the state to have the correct tool call, and then after the update has been applied we can continue.\n",
    "\n",
    "We can try resuming and we will see an error arise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "740bbaeb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (call_UXoJGnV30VwVoT0W1KNcdvi1)\n",
      " Call ID: call_UXoJGnV30VwVoT0W1KNcdvi1\n",
      "  Args:\n",
      "    location: SF, CA\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_weather\n",
      "\n",
      "Error: AssertionError('Unknown Location')\n",
      " Please fix your mistakes.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (call_VqOHbYg8acRh0qeZqP7QOAY0)\n",
      " Call ID: call_VqOHbYg8acRh0qeZqP7QOAY0\n",
      "  Args:\n",
      "    location: San Francisco, CA\n"
     ]
    }
   ],
   "source": [
    "print_stream(graph.stream(None, config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1cf5950",
   "metadata": {},
   "source": [
    "This error arose because our tool argument of \"San Francisco, CA\" is not a location our tool recognizes.\n",
    "\n",
    "Let's show how we would edit the tool call to search for \"San Francisco\" instead of \"San Francisco, CA\" - since our tool as written treats \"San Francisco, CA\" as an unknown location. We will update the state and then resume streaming the graph and should see no errors arise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1c81ed9f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '42',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1f025333-b553-6b92-8002-21537132a652'}}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = graph.get_state(config)\n",
    "\n",
    "last_message = state.values[\"messages\"][-1]\n",
    "last_message.tool_calls[0][\"args\"] = {\"location\": \"San Francisco\"}\n",
    "\n",
    "graph.update_state(config, {\"messages\": [last_message]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "83148e08-63e8-49e5-a08b-02dc907bed1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (call_VqOHbYg8acRh0qeZqP7QOAY0)\n",
      " Call ID: call_VqOHbYg8acRh0qeZqP7QOAY0\n",
      "  Args:\n",
      "    location: San Francisco\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_weather\n",
      "\n",
      "It's always sunny in sf\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The weather in San Francisco is currently sunny.\n"
     ]
    }
   ],
   "source": [
    "print_stream(graph.stream(None, config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8202a5f9",
   "metadata": {},
   "source": [
    "Fantastic! Our graph updated properly to query the weather in San Francisco and got the correct \"It's always sunny in sf\" response from the tool, and then responded to the user accordingly."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/create-react-agent-manage-message-history.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
   "metadata": {},
   "source": [
    "# How to manage conversation history in a ReAct Agent with Redis\n",
    "\n",
    "!!! info \"Prerequisites\"\n",
    "    This guide assumes familiarity with the following:\n",
    "\n",
    "    - [Prebuilt create_react_agent](../create-react-agent)\n",
    "    - [Persistence](../../concepts/persistence)\n",
    "    - [Short-term Memory](../../concepts/memory/#short-term-memory)\n",
    "    - [Trimming Messages](https://python.langchain.com/docs/how_to/trim_messages/)\n",
    "\n",
    "Message history can grow quickly and exceed LLM context window size, whether you're building chatbots with many conversation turns or agentic systems with numerous tool calls. There are several strategies for managing the message history:\n",
    "\n",
    "* [message trimming](#keep-the-original-message-history-unmodified) - remove first or last N messages in the history\n",
    "* [summarization](#summarizing-message-history) - summarize earlier messages in the history and replace them with a summary\n",
    "* custom strategies (e.g., message filtering, etc.)\n",
    "\n",
    "To manage message history in `create_react_agent`, you need to define a `pre_model_hook` function or [runnable](https://python.langchain.com/docs/concepts/runnables/) that takes graph state an returns a state update:\n",
    "\n",
    "\n",
    "* Trimming example:\n",
    "    ```python\n",
    "    # highlight-next-line\n",
    "    from langchain_core.messages.utils import (\n",
    "        # highlight-next-line\n",
    "        trim_messages, \n",
    "        # highlight-next-line\n",
    "        count_tokens_approximately\n",
    "    # highlight-next-line\n",
    "    )\n",
    "    from langgraph.prebuilt import create_react_agent\n",
    "    from langgraph.checkpoint.redis import RedisSaver\n",
    "    \n",
    "    # This function will be called every time before the node that calls LLM\n",
    "    def pre_model_hook(state):\n",
    "        trimmed_messages = trim_messages(\n",
    "            state[\"messages\"],\n",
    "            strategy=\"last\",\n",
    "            token_counter=count_tokens_approximately,\n",
    "            max_tokens=384,\n",
    "            start_on=\"human\",\n",
    "            end_on=(\"human\", \"tool\"),\n",
    "        )\n",
    "        # You can return updated messages either under `llm_input_messages` or \n",
    "        # `messages` key (see the note below)\n",
    "        # highlight-next-line\n",
    "        return {\"llm_input_messages\": trimmed_messages}\n",
    "\n",
    "    # Set up Redis connection for checkpointer\n",
    "    REDIS_URI = \"redis://redis:6379\"\n",
    "    checkpointer = None\n",
    "    with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "        cp.setup()\n",
    "        checkpointer = cp\n",
    "        \n",
    "    agent = create_react_agent(\n",
    "        model,\n",
    "        tools,\n",
    "        # highlight-next-line\n",
    "        pre_model_hook=pre_model_hook,\n",
    "        checkpointer=checkpointer,\n",
    "    )\n",
    "    ```\n",
    "\n",
    "* Summarization example:\n",
    "    ```python\n",
    "    # highlight-next-line\n",
    "    from langmem.short_term import SummarizationNode\n",
    "    from langchain_core.messages.utils import count_tokens_approximately\n",
    "    from langgraph.prebuilt.chat_agent_executor import AgentState\n",
    "    from langgraph.checkpoint.redis import RedisSaver\n",
    "    from typing import Any\n",
    "    \n",
    "    model = ChatOpenAI(model=\"gpt-4o\")\n",
    "    \n",
    "    summarization_node = SummarizationNode(\n",
    "        token_counter=count_tokens_approximately,\n",
    "        model=model,\n",
    "        max_tokens=384,\n",
    "        max_summary_tokens=128,\n",
    "        output_messages_key=\"llm_input_messages\",\n",
    "    )\n",
    "\n",
    "    class State(AgentState):\n",
    "        # NOTE: we're adding this key to keep track of previous summary information\n",
    "        # to make sure we're not summarizing on every LLM call\n",
    "        # highlight-next-line\n",
    "        context: dict[str, Any]\n",
    "    \n",
    "    # Set up Redis connection for checkpointer\n",
    "    REDIS_URI = \"redis://redis:6379\"\n",
    "    checkpointer = None\n",
    "    with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "        cp.setup()\n",
    "        checkpointer = cp\n",
    "    \n",
    "    graph = create_react_agent(\n",
    "        model,\n",
    "        tools,\n",
    "        # highlight-next-line\n",
    "        pre_model_hook=summarization_node,\n",
    "        # highlight-next-line\n",
    "        state_schema=State,\n",
    "        checkpointer=checkpointer,\n",
    "    )\n",
    "    ```\n",
    "\n",
    "!!! Important\n",
    "    \n",
    "    * To **keep the original message history unmodified** in the graph state and pass the updated history **only as the input to the LLM**, return updated messages under `llm_input_messages` key\n",
    "    * To **overwrite the original message history** in the graph state with the updated history, return updated messages under `messages` key\n",
    "    \n",
    "    To overwrite the `messages` key, you need to do the following:\n",
    "\n",
    "    ```python\n",
    "    from langchain_core.messages import RemoveMessage\n",
    "    from langgraph.graph.message import REMOVE_ALL_MESSAGES\n",
    "\n",
    "    def pre_model_hook(state):\n",
    "        updated_messages = ...\n",
    "        return {\n",
    "            \"messages\": [RemoveMessage(id=REMOVE_ALL_MESSAGES), *updated_messages]\n",
    "            ...\n",
    "        }\n",
    "    ```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's install the required packages and set our API keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a213e11a-5c62-4ddb-a707-490d91add383",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph langchain-openai \"httpx>=0.24.0,<1.0.0\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "23a1885c-04ab-4750-aefa-105891fddf3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        value = getpass.getpass(f\"{var}: \")\n",
    "        if value.strip():\n",
    "            os.environ[var] = value\n",
    "\n",
    "\n",
    "# Try to set OpenAI API key\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87a00ce9",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
   "metadata": {},
   "source": [
    "## Keep the original message history unmodified"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd6cbd3a-8632-47ae-9ec5-eec8d7b05cae",
   "metadata": {},
   "source": [
    "Let's build a ReAct agent with a step that manages the conversation history: when the length of the history exceeds a specified number of tokens, we will call [`trim_messages`](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) utility that that will reduce the history while satisfying LLM provider constraints.\n",
    "\n",
    "There are two ways that the updated message history can be applied inside ReAct agent:\n",
    "\n",
    "  * [**Keep the original message history unmodified**](#keep-the-original-message-history-unmodified) in the graph state and pass the updated history **only as the input to the LLM**\n",
    "  * [**Overwrite the original message history**](#overwrite-the-original-message-history) in the graph state with the updated history\n",
    "\n",
    "Let's start by implementing the first one. We'll need to first define model and tools for our agent:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "eaad19ee-e174-4c6c-b2b8-3530d7acea40",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
    "\n",
    "\n",
    "def get_weather(location: str) -> str:\n",
    "    \"\"\"Use this to get weather information.\"\"\"\n",
    "    if any([city in location.lower() for city in [\"nyc\", \"new york city\"]]):\n",
    "        return \"It might be cloudy in nyc, with a chance of rain and temperatures up to 80 degrees.\"\n",
    "    elif any([city in location.lower() for city in [\"sf\", \"san francisco\"]]):\n",
    "        return \"It's always sunny in sf\"\n",
    "    else:\n",
    "        return f\"I am not sure what the weather is in {location}\"\n",
    "\n",
    "\n",
    "tools = [get_weather]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52402333-61ab-47d3-8549-6a70f6f1cf36",
   "metadata": {},
   "source": [
    "Now let's implement `pre_model_hook` — a function that will be added as a new node and called every time **before** the node that calls the LLM (the `agent` node).\n",
    "\n",
    "Our implementation will wrap the `trim_messages` call and return the trimmed messages under `llm_input_messages`. This will **keep the original message history unmodified** in the graph state and pass the updated history **only as the input to the LLM**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b507eb58-6e02-4ac6-b48b-ea4defdc11f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# highlight-next-line\n",
    "from langchain_core.messages.utils import (\n",
    "    # highlight-next-line\n",
    "    trim_messages,\n",
    "    # highlight-next-line\n",
    "    count_tokens_approximately,\n",
    "    # highlight-next-line\n",
    ")\n",
    "\n",
    "\n",
    "# This function will be added as a new node in ReAct agent graph\n",
    "# that will run every time before the node that calls the LLM.\n",
    "# The messages returned by this function will be the input to the LLM.\n",
    "def pre_model_hook(state):\n",
    "    trimmed_messages = trim_messages(\n",
    "        state[\"messages\"],\n",
    "        strategy=\"last\",\n",
    "        token_counter=count_tokens_approximately,\n",
    "        max_tokens=384,\n",
    "        start_on=\"human\",\n",
    "        end_on=(\"human\", \"tool\"),\n",
    "    )\n",
    "    # highlight-next-line\n",
    "    return {\"llm_input_messages\": trimmed_messages}\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "graph = create_react_agent(\n",
    "    model,\n",
    "    tools,\n",
    "    # highlight-next-line\n",
    "    pre_model_hook=pre_model_hook,\n",
    "    checkpointer=checkpointer,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8182ab45-86b3-4d6f-b75e-58862a14fa4e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import display, Image\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d41e8e76-5d43-44cd-bf01-a39212cedd8d",
   "metadata": {},
   "source": [
    "We'll also define a utility to render the agent outputs nicely:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_stream(stream, output_messages_key=\"llm_input_messages\"):\n",
    "    for chunk in stream:\n",
    "        for node, update in chunk.items():\n",
    "            print(f\"Update from node: {node}\")\n",
    "            messages_key = (\n",
    "                output_messages_key if node == \"pre_model_hook\" else \"messages\"\n",
    "            )\n",
    "            for message in update[messages_key]:\n",
    "                if isinstance(message, tuple):\n",
    "                    print(message)\n",
    "                else:\n",
    "                    message.pretty_print()\n",
    "\n",
    "        print(\"\\n\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84448d29-b323-4833-80fc-4fff2f5a0950",
   "metadata": {},
   "source": [
    "Now let's run the agent with a few different queries to reach the specified max tokens limit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
    "result = graph.invoke(inputs, config=config)\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
    "result = graph.invoke(inputs, config=config)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fdb186da-b55d-4cb8-a237-e9e157ab0458",
   "metadata": {},
   "source": [
    "Let's see how many tokens we have in the message history so far:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "41ba0253-5199-4d29-82ae-258cbbebddb4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "417"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = result[\"messages\"]\n",
    "count_tokens_approximately(messages)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "812987ac-66ba-4122-8281-469cbdced7c7",
   "metadata": {},
   "source": [
    "You can see that we are close to the `max_tokens` threshold, so on the next invocation we should see `pre_model_hook` kick-in and trim the message history. Let's run it again:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "26c53429-90ba-4d0b-abb9-423d9120ad26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Update from node: pre_model_hook\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "What's it known for?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "New York City is known for a variety of iconic landmarks, cultural institutions, and vibrant neighborhoods. Some of the most notable features include:\n",
      "\n",
      "1. **Statue of Liberty**: A symbol of freedom and democracy, located on Liberty Island.\n",
      "2. **Times Square**: Known for its bright lights, Broadway theaters, and bustling atmosphere.\n",
      "3. **Central Park**: A large public park offering a natural retreat in the midst of the city.\n",
      "4. **Empire State Building**: An iconic skyscraper offering panoramic views of the city.\n",
      "5. **Broadway**: Famous for its world-class theater productions.\n",
      "6. **Wall Street**: The financial hub of the United States.\n",
      "7. **Museums**: Including the Metropolitan Museum of Art, Museum of Modern Art (MoMA), and the American Museum of Natural History.\n",
      "8. **Diverse Cuisine**: A melting pot of cultures reflected in its diverse food scene.\n",
      "9. **Cultural Diversity**: A rich tapestry of cultures and ethnicities, contributing to its dynamic atmosphere.\n",
      "10. **Fashion**: A global fashion capital, hosting events like New York Fashion Week.\n",
      "\n",
      "These are just a few highlights of what makes New York City a unique and exciting place.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "where can i find the best bagel?\n",
      "\n",
      "\n",
      "\n",
      "Update from node: agent\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Finding the \"best\" bagel in New York City can be subjective, as it often depends on personal taste. However, several bagel shops are frequently mentioned as top contenders:\n",
      "\n",
      "1. **Ess-a-Bagel**: Known for its large, chewy bagels and a wide variety of spreads.\n",
      "2. **Russ & Daughters**: Famous for its bagels with lox and other traditional Jewish delicacies.\n",
      "3. **H&H Bagels**: A classic choice, known for its fresh, hand-rolled bagels.\n",
      "4. **Murray’s Bagels**: Offers a wide selection of bagels and toppings, with a focus on traditional methods.\n",
      "5. **Tompkins Square Bagels**: Known for its creative cream cheese flavors and fresh ingredients.\n",
      "6. **Absolute Bagels**: A favorite on the Upper West Side, known for its authentic taste and texture.\n",
      "7. **Bagel Hole**: A small shop in Brooklyn known for its dense, flavorful bagels.\n",
      "\n",
      "These spots are scattered throughout the city, so you can find a great bagel in various neighborhoods. Each of these places has its own unique style and flavor, so it might be worth trying a few to find your personal favorite!\n",
      "\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "inputs = {\"messages\": [(\"user\", \"where can i find the best bagel?\")]}\n",
    "print_stream(graph.stream(inputs, config=config, stream_mode=\"updates\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58fe0399-4e7d-4482-a4cb-5301311932d0",
   "metadata": {},
   "source": [
    "You can see that the `pre_model_hook` node now only returned the last 3 messages, as expected. However, the existing message history is untouched:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "7ecfc310-8f9e-4aa0-9e58-17e71551639a",
   "metadata": {},
   "outputs": [],
   "source": [
    "updated_messages = graph.get_state(config).values[\"messages\"]\n",
    "assert [(m.type, m.content) for m in updated_messages[: len(messages)]] == [\n",
    "    (m.type, m.content) for m in messages\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "035864e3-0083-4dea-bf85-3a702fa5303f",
   "metadata": {},
   "source": [
    "## Overwrite the original message history"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b0a4fd5-a2ba-4eca-91a9-d294f4f2d884",
   "metadata": {},
   "source": [
    "Let's now change the `pre_model_hook` to **overwrite** the message history in the graph state. To do this, we’ll return the updated messages under `messages` key. We’ll also include a special `RemoveMessage(REMOVE_ALL_MESSAGES)` object, which tells `create_react_agent` to remove previous messages from the graph state:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "48c2a65b-685a-4750-baa6-2d61efe76b5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:30:48\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:30:48\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:30:48\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage\n",
    "from langgraph.graph.message import REMOVE_ALL_MESSAGES\n",
    "\n",
    "\n",
    "def pre_model_hook(state):\n",
    "    trimmed_messages = trim_messages(\n",
    "        state[\"messages\"],\n",
    "        strategy=\"last\",\n",
    "        token_counter=count_tokens_approximately,\n",
    "        max_tokens=384,\n",
    "        start_on=\"human\",\n",
    "        end_on=(\"human\", \"tool\"),\n",
    "    )\n",
    "    # NOTE that we're now returning the messages under the `messages` key\n",
    "    # We also remove the existing messages in the history to ensure we're overwriting the history\n",
    "    # highlight-next-line\n",
    "    return {\"messages\": [RemoveMessage(REMOVE_ALL_MESSAGES)] + trimmed_messages}\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "graph = create_react_agent(\n",
    "    model,\n",
    "    tools,\n",
    "    # highlight-next-line\n",
    "    pre_model_hook=pre_model_hook,\n",
    "    checkpointer=checkpointer,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd061682-231c-4487-9c2f-a6820dfbcab7",
   "metadata": {},
   "source": [
    "Now let's run the agent with the same queries as before:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "831be36a-78a1-4885-9a03-8d085dfd7e37",
   "metadata": {},
   "outputs": [
    {
     "ename": "RedisSearchError",
     "evalue": "Error while searching: checkpoints_blobs: no such index",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mResponseError\u001b[39m                             Traceback (most recent call last)",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:795\u001b[39m, in \u001b[36mSearchIndex.search\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m    794\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m795\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_redis_client\u001b[49m\u001b[43m.\u001b[49m\u001b[43mft\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m.\u001b[49m\u001b[43mindex\u001b[49m\u001b[43m.\u001b[49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43m  \u001b[49m\u001b[38;5;66;43;03m# type: ignore\u001b[39;49;00m\n\u001b[32m    796\u001b[39m \u001b[43m        \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m    797\u001b[39m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    798\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/commands/search/commands.py:508\u001b[39m, in \u001b[36mSearchCommands.search\u001b[39m\u001b[34m(self, query, query_params)\u001b[39m\n\u001b[32m    506\u001b[39m     options[NEVER_DECODE] = \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m508\u001b[39m res = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mexecute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[43mSEARCH_CMD\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    510\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(res, Pipeline):\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/client.py:559\u001b[39m, in \u001b[36mRedis.execute_command\u001b[39m\u001b[34m(self, *args, **options)\u001b[39m\n\u001b[32m    558\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mexecute_command\u001b[39m(\u001b[38;5;28mself\u001b[39m, *args, **options):\n\u001b[32m--> \u001b[39m\u001b[32m559\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_execute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/client.py:567\u001b[39m, in \u001b[36mRedis._execute_command\u001b[39m\u001b[34m(self, *args, **options)\u001b[39m\n\u001b[32m    566\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m567\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43mretry\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcall_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    568\u001b[39m \u001b[43m        \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    569\u001b[39m \u001b[43m            \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptions\u001b[49m\n\u001b[32m    570\u001b[39m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    571\u001b[39m \u001b[43m        \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_disconnect_raise\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    572\u001b[39m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    573\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/retry.py:62\u001b[39m, in \u001b[36mRetry.call_with_retry\u001b[39m\u001b[34m(self, do, fail)\u001b[39m\n\u001b[32m     61\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m62\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdo\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     63\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;28mself\u001b[39m._supported_errors \u001b[38;5;28;01mas\u001b[39;00m error:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/client.py:568\u001b[39m, in \u001b[36mRedis._execute_command.<locals>.<lambda>\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m    566\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m    567\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m conn.retry.call_with_retry(\n\u001b[32m--> \u001b[39m\u001b[32m568\u001b[39m         \u001b[38;5;28;01mlambda\u001b[39;00m: \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    569\u001b[39m \u001b[43m            \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptions\u001b[49m\n\u001b[32m    570\u001b[39m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[32m    571\u001b[39m         \u001b[38;5;28;01mlambda\u001b[39;00m error: \u001b[38;5;28mself\u001b[39m._disconnect_raise(conn, error),\n\u001b[32m    572\u001b[39m     )\n\u001b[32m    573\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/client.py:542\u001b[39m, in \u001b[36mRedis._send_command_parse_response\u001b[39m\u001b[34m(self, conn, command_name, *args, **options)\u001b[39m\n\u001b[32m    541\u001b[39m conn.send_command(*args, **options)\n\u001b[32m--> \u001b[39m\u001b[32m542\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mparse_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/client.py:581\u001b[39m, in \u001b[36mRedis.parse_response\u001b[39m\u001b[34m(self, connection, command_name, **options)\u001b[39m\n\u001b[32m    580\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m NEVER_DECODE \u001b[38;5;129;01min\u001b[39;00m options:\n\u001b[32m--> \u001b[39m\u001b[32m581\u001b[39m     response = \u001b[43mconnection\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdisable_decoding\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[32m    582\u001b[39m     options.pop(NEVER_DECODE)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redis/connection.py:616\u001b[39m, in \u001b[36mAbstractConnection.read_response\u001b[39m\u001b[34m(self, disable_decoding, disconnect_on_error, push_request)\u001b[39m\n\u001b[32m    615\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m616\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m response\n\u001b[32m    617\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n",
      "\u001b[31mResponseError\u001b[39m: checkpoints_blobs: no such index",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[31mRedisSearchError\u001b[39m                          Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      8\u001b[39m messages = result[\u001b[33m\"\u001b[39m\u001b[33mmessages\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m     10\u001b[39m inputs = {\u001b[33m\"\u001b[39m\u001b[33mmessages\u001b[39m\u001b[33m\"\u001b[39m: [(\u001b[33m\"\u001b[39m\u001b[33muser\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mwhere can i find the best bagel?\u001b[39m\u001b[33m\"\u001b[39m)]}\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m \u001b[43mprint_stream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m     12\u001b[39m \u001b[43m    \u001b[49m\u001b[43mgraph\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mupdates\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     13\u001b[39m \u001b[43m    \u001b[49m\u001b[43moutput_messages_key\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m     14\u001b[39m \u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mprint_stream\u001b[39m\u001b[34m(stream, output_messages_key)\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mprint_stream\u001b[39m(stream, output_messages_key=\u001b[33m\"\u001b[39m\u001b[33mllm_input_messages\u001b[39m\u001b[33m\"\u001b[39m):\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m      3\u001b[39m \u001b[43m        \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mnode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mupdate\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m.\u001b[49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m      4\u001b[39m \u001b[43m            \u001b[49m\u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[33;43mf\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mUpdate from node: \u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mnode\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:2377\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, checkpoint_during, debug, subgraphs)\u001b[39m\n\u001b[32m   2375\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m checkpoint_during \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m   2376\u001b[39m     config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during\n\u001b[32m-> \u001b[39m\u001b[32m2377\u001b[39m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mwith\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mSyncPregelLoop\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m   2378\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m   2379\u001b[39m \u001b[43m    \u001b[49m\u001b[43minput_model\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43minput_model\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2380\u001b[39m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m=\u001b[49m\u001b[43mStreamProtocol\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m.\u001b[49m\u001b[43mput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_modes\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2381\u001b[39m \u001b[43m    \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2382\u001b[39m \u001b[43m    \u001b[49m\u001b[43mstore\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2383\u001b[39m \u001b[43m    \u001b[49m\u001b[43mcheckpointer\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcheckpointer\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2384\u001b[39m \u001b[43m    \u001b[49m\u001b[43mnodes\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mnodes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2385\u001b[39m \u001b[43m    \u001b[49m\u001b[43mspecs\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mchannels\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2386\u001b[39m \u001b[43m    \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2387\u001b[39m \u001b[43m    \u001b[49m\u001b[43mstream_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstream_channels_asis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2388\u001b[39m \u001b[43m    \u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_before_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2389\u001b[39m \u001b[43m    \u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_after_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2390\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmanager\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2391\u001b[39m \u001b[43m    \u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2392\u001b[39m \u001b[43m    \u001b[49m\u001b[43mcheckpoint_during\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcheckpoint_during\u001b[49m\n\u001b[32m   2393\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mcheckpoint_during\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\n\u001b[32m   2394\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m[\u001b[49m\u001b[43mCONF\u001b[49m\u001b[43m]\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43mCONFIG_KEY_CHECKPOINT_DURING\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2395\u001b[39m \u001b[43m    \u001b[49m\u001b[43mtrigger_to_nodes\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mtrigger_to_nodes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2396\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmigrate_checkpoint\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_migrate_checkpoint\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2397\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mas\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m   2398\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;66;43;03m# create runner\u001b[39;49;00m\n\u001b[32m   2399\u001b[39m \u001b[43m    \u001b[49m\u001b[43mrunner\u001b[49m\u001b[43m \u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43mPregelRunner\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m   2400\u001b[39m \u001b[43m        \u001b[49m\u001b[43msubmit\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m[\u001b[49m\u001b[43mCONF\u001b[49m\u001b[43m]\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m   2401\u001b[39m \u001b[43m            \u001b[49m\u001b[43mCONFIG_KEY_RUNNER_SUBMIT\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mweakref\u001b[49m\u001b[43m.\u001b[49m\u001b[43mWeakMethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43msubmit\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m   (...)\u001b[39m\u001b[32m   2405\u001b[39m \u001b[43m        \u001b[49m\u001b[43mnode_finished\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m[\u001b[49m\u001b[43mCONF\u001b[49m\u001b[43m]\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43mCONFIG_KEY_NODE_FINISHED\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m   2406\u001b[39m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m   2407\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;66;43;03m# enable subgraph streaming\u001b[39;49;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/langgraph/pregel/loop.py:1058\u001b[39m, in \u001b[36mSyncPregelLoop.__enter__\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m   1056\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m CheckpointNotLatest\n\u001b[32m   1057\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.checkpointer:\n\u001b[32m-> \u001b[39m\u001b[32m1058\u001b[39m     saved = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcheckpointer\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_tuple\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcheckpoint_config\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m   1059\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m   1060\u001b[39m     saved = \u001b[38;5;28;01mNone\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/workspace/libs/checkpoint-redis/langgraph/checkpoint/redis/__init__.py:335\u001b[39m, in \u001b[36mRedisSaver.get_tuple\u001b[39m\u001b[34m(self, config)\u001b[39m\n\u001b[32m    332\u001b[39m doc_parent_checkpoint_id = from_storage_safe_id(doc[\u001b[33m\"\u001b[39m\u001b[33mparent_checkpoint_id\u001b[39m\u001b[33m\"\u001b[39m])\n\u001b[32m    334\u001b[39m \u001b[38;5;66;03m# Fetch channel_values\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m channel_values = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mget_channel_values\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    336\u001b[39m \u001b[43m    \u001b[49m\u001b[43mthread_id\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdoc_thread_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    337\u001b[39m \u001b[43m    \u001b[49m\u001b[43mcheckpoint_ns\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdoc_checkpoint_ns\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    338\u001b[39m \u001b[43m    \u001b[49m\u001b[43mcheckpoint_id\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdoc_checkpoint_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    339\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    341\u001b[39m \u001b[38;5;66;03m# Fetch pending_sends from parent checkpoint\u001b[39;00m\n\u001b[32m    342\u001b[39m pending_sends = []\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/workspace/libs/checkpoint-redis/langgraph/checkpoint/redis/__init__.py:452\u001b[39m, in \u001b[36mRedisSaver.get_channel_values\u001b[39m\u001b[34m(self, thread_id, checkpoint_ns, checkpoint_id)\u001b[39m\n\u001b[32m    442\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m channel, version \u001b[38;5;129;01min\u001b[39;00m channel_versions.items():\n\u001b[32m    443\u001b[39m     blob_query = FilterQuery(\n\u001b[32m    444\u001b[39m         filter_expression=(Tag(\u001b[33m\"\u001b[39m\u001b[33mthread_id\u001b[39m\u001b[33m\"\u001b[39m) == storage_safe_thread_id)\n\u001b[32m    445\u001b[39m         & (Tag(\u001b[33m\"\u001b[39m\u001b[33mcheckpoint_ns\u001b[39m\u001b[33m\"\u001b[39m) == storage_safe_checkpoint_ns)\n\u001b[32m   (...)\u001b[39m\u001b[32m    449\u001b[39m         num_results=\u001b[32m1\u001b[39m,\n\u001b[32m    450\u001b[39m     )\n\u001b[32m--> \u001b[39m\u001b[32m452\u001b[39m     blob_results = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcheckpoint_blobs_index\u001b[49m\u001b[43m.\u001b[49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mblob_query\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    453\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m blob_results.docs:\n\u001b[32m    454\u001b[39m         blob_doc = blob_results.docs[\u001b[32m0\u001b[39m]\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:799\u001b[39m, in \u001b[36mSearchIndex.search\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m    795\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._redis_client.ft(\u001b[38;5;28mself\u001b[39m.schema.index.name).search(  \u001b[38;5;66;03m# type: ignore\u001b[39;00m\n\u001b[32m    796\u001b[39m         *args, **kwargs\n\u001b[32m    797\u001b[39m     )\n\u001b[32m    798\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m--> \u001b[39m\u001b[32m799\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m RedisSearchError(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mError while searching: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01me\u001b[39;00m\n",
      "\u001b[31mRedisSearchError\u001b[39m: Error while searching: checkpoints_blobs: no such index"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
    "result = graph.invoke(inputs, config=config)\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
    "result = graph.invoke(inputs, config=config)\n",
    "messages = result[\"messages\"]\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"where can i find the best bagel?\")]}\n",
    "print_stream(\n",
    "    graph.stream(inputs, config=config, stream_mode=\"updates\"),\n",
    "    output_messages_key=\"messages\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc9a0604-3d2b-48ff-9eaf-d16ea351fb30",
   "metadata": {},
   "source": [
    "You can see that the `pre_model_hook` node returned the last 3 messages again. However, this time, the message history is modified in the graph state as well:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "394f72f8-f817-472d-a193-e01509a86132",
   "metadata": {},
   "outputs": [],
   "source": [
    "updated_messages = graph.get_state(config).values[\"messages\"]\n",
    "assert (\n",
    "    # First 2 messages in the new history are the same as last 2 messages in the old\n",
    "    [(m.type, m.content) for m in updated_messages[:2]]\n",
    "    == [(m.type, m.content) for m in messages[-2:]]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee186d6d-4d07-404f-b236-f662db62339d",
   "metadata": {},
   "source": [
    "## Summarizing message history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa6e4bdf",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langmem"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6e53e0f-9a1e-4188-8435-c23ad8148b4f",
   "metadata": {},
   "source": [
    "Finally, let's apply a different strategy for managing message history — summarization. Just as with trimming, you can choose to keep original message history unmodified or overwrite it. The example below will only show the former.\n",
    "\n",
    "We will use the [`SummarizationNode`](https://langchain-ai.github.io/langmem/guides/summarization/#using-summarizationnode) from the prebuilt `langmem` library. Once the message history reaches the token limit, the summarization node will summarize earlier messages to make sure they fit into `max_tokens`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9540c1c-2eba-42da-ba4e-478521161a1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# highlight-next-line\n",
    "from langmem.short_term import SummarizationNode\n",
    "from langgraph.prebuilt.chat_agent_executor import AgentState\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from typing import Any\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o\")\n",
    "summarization_model = model.bind(max_tokens=128)\n",
    "\n",
    "summarization_node = SummarizationNode(\n",
    "    token_counter=count_tokens_approximately,\n",
    "    model=summarization_model,\n",
    "    max_tokens=384,\n",
    "    max_summary_tokens=128,\n",
    "    output_messages_key=\"llm_input_messages\",\n",
    ")\n",
    "\n",
    "\n",
    "class State(AgentState):\n",
    "    # NOTE: we're adding this key to keep track of previous summary information\n",
    "    # to make sure we're not summarizing on every LLM call\n",
    "    # highlight-next-line\n",
    "    context: dict[str, Any]\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "graph = create_react_agent(\n",
    "    # limit the output size to ensure consistent behavior\n",
    "    model.bind(max_tokens=256),\n",
    "    tools,\n",
    "    # highlight-next-line\n",
    "    pre_model_hook=summarization_node,\n",
    "    # highlight-next-line\n",
    "    state_schema=State,\n",
    "    checkpointer=checkpointer,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8eccaaca-5d9c-4faf-b997-d4b8e84b59ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
    "\n",
    "result = graph.invoke(inputs, config=config)\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
    "result = graph.invoke(inputs, config=config)\n",
    "\n",
    "inputs = {\"messages\": [(\"user\", \"where can i find the best bagel?\")]}\n",
    "print_stream(graph.stream(inputs, config=config, stream_mode=\"updates\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7caaf2f7-281a-4421-bf98-c745d950c56f",
   "metadata": {},
   "source": [
    "You can see that the earlier messages have now been replaced with the summary of the earlier conversation!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/create-react-agent-memory.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
   "metadata": {},
   "source": [
    "# How to add thread-level memory to a ReAct Agent\n",
    "\n",
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/\">\n",
    "                    LangGraph Persistence\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/#checkpointer-interface\">\n",
    "                    Checkpointer interface\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/\">\n",
    "                    Agent Architectures\n",
    "                </a>                   \n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://python.langchain.com/docs/concepts/chat_models/\">\n",
    "                    Chat Models\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://python.langchain.com/docs/concepts/tools/\">\n",
    "                    Tools\n",
    "                </a>\n",
    "            </li>\n",
    "        </ul>\n",
    "    </p>\n",
    "</div> \n",
    "\n",
    "This guide will show how to add memory to the prebuilt ReAct agent. Please see [this tutorial](../create-react-agent) for how to get started with the prebuilt ReAct agent\n",
    "\n",
    "We can add memory to the agent, by passing a [checkpointer](https://langchain-ai.github.io/langgraph/reference/checkpoints/) to the [create_react_agent](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent) function."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's install the required packages and set our API keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a213e11a-5c62-4ddb-a707-490d91add383",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph langchain-openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "23a1885c-04ab-4750-aefa-105891fddf3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87a00ce9",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
   "metadata": {},
   "source": [
    "## Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "7a154152-973e-4b5d-aa13-48c617744a4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# First we initialize the model we want to use.\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
    "\n",
    "\n",
    "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
    "\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_weather(location: str) -> str:\n",
    "    \"\"\"Use this to get weather information.\"\"\"\n",
    "    if any([city in location.lower() for city in [\"nyc\", \"new york city\"]]):\n",
    "        return \"It might be cloudy in nyc\"\n",
    "    elif any([city in location.lower() for city in [\"sf\", \"san francisco\"]]):\n",
    "        return \"It's always sunny in sf\"\n",
    "    else:\n",
    "        return f\"I am not sure what the weather is in {location}\"\n",
    "\n",
    "\n",
    "tools = [get_weather]\n",
    "\n",
    "# We can add \"chat memory\" to the graph with LangGraph's Redis checkpointer\n",
    "# to retain the chat context between interactions\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "# Define the graph\n",
    "\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "graph = create_react_agent(model, tools=tools, checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00407425-506d-4ffd-9c86-987921d8c844",
   "metadata": {},
   "source": [
    "## Usage\n",
    "\n",
    "Let's interact with it multiple times to show that it can remember"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_stream(stream):\n",
    "    for s in stream:\n",
    "        message = s[\"messages\"][-1]\n",
    "        if isinstance(message, tuple):\n",
    "            print(message)\n",
    "        else:\n",
    "            message.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "What's the weather in NYC?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (call_1aAbFecdc3xn5yLVkOBScflI)\n",
      " Call ID: call_1aAbFecdc3xn5yLVkOBScflI\n",
      "  Args:\n",
      "    location: New York City\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_weather\n",
      "\n",
      "It might be cloudy in nyc\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The weather in New York City might be cloudy.\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
    "\n",
    "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "838a043f-90ad-4e69-9d1d-6e22db2c346c",
   "metadata": {},
   "source": [
    "Notice that when we pass the same thread ID, the chat history is preserved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "187479f9-32fa-4611-9487-cf816ba2e147",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "What's it known for?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "New York City is known for many things, including:\n",
      "\n",
      "1. **Landmarks and Attractions**: The Statue of Liberty, Times Square, Central Park, Empire State Building, and Broadway theaters.\n",
      "   \n",
      "2. **Cultural Diversity**: NYC is a melting pot of cultures, with a rich tapestry of ethnic neighborhoods like Chinatown, Little Italy, and Harlem.\n",
      "\n",
      "3. **Financial Hub**: Home to Wall Street and the New York Stock Exchange, it's a global financial center.\n",
      "\n",
      "4. **Arts and Entertainment**: Renowned for its museums (e.g., The Metropolitan Museum of Art, MoMA), music venues, and vibrant arts scene.\n",
      "\n",
      "5. **Cuisine**: Famous for its diverse food offerings, including New York-style pizza, bagels, and international cuisines.\n",
      "\n",
      "6. **Fashion**: A major fashion capital, hosting New York Fashion Week and home to numerous designers and fashion houses.\n",
      "\n",
      "7. **Media and Publishing**: Headquarters for major media companies and publishers, including The New York Times and NBC.\n",
      "\n",
      "8. **Skyscrapers**: Known for its iconic skyline, featuring numerous skyscrapers.\n",
      "\n",
      "9. **Public Transportation**: An extensive subway system and iconic yellow taxis.\n",
      "\n",
      "10. **Sports**: Home to major sports teams like the New York Yankees, Mets, Knicks, and Giants.\n",
      "\n",
      "These are just a few highlights of what makes New York City a unique and vibrant place.\n"
     ]
    }
   ],
   "source": [
    "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
    "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c461eb47-b4f9-406f-8923-c68db7c5687f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/cross-thread-persistence-functional.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d2eecb96-cf0e-47ed-8116-88a7eaa4236d",
   "metadata": {},
   "source": [
    "# How to add cross-thread persistence (functional API)\n",
    "\n",
    "!!! info \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following:\n",
    "    \n",
    "    - [Functional API](https://langchain-ai.github.io/langgraph/concepts/functional_api/)\n",
    "    - [Persistence](https://langchain-ai.github.io/langgraph/concepts/persistence/)\n",
    "    - [Memory](https://langchain-ai.github.io/langgraph/concepts/memory/)\n",
    "    - [Chat Models](https://python.langchain.com/docs/concepts/chat_models/)\n",
    "\n",
    "LangGraph allows you to persist data across **different [threads](https://langchain-ai.github.io/langgraph/concepts/persistence/#threads)**. For instance, you can store information about users (their names or preferences) in a shared (cross-thread) memory and reuse them in the new threads (e.g., new conversations).\n",
    "\n",
    "When using the [functional API](https://langchain-ai.github.io/langgraph/concepts/functional_api/), you can set it up to store and retrieve memories by using the [Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) interface:\n",
    "\n",
    "1. Create an instance of a `Store`\n",
    "\n",
    "    ```python\n",
    "    from langgraph.store.redis import RedisStore, BaseStore\n",
    "    \n",
    "    store = RedisStore.from_conn_string(\"redis://redis:6379\")\n",
    "    ```\n",
    "\n",
    "2. Pass the `store` instance to the `entrypoint()` decorator and expose `store` parameter in the function signature:\n",
    "\n",
    "    ```python\n",
    "    from langgraph.func import entrypoint\n",
    "\n",
    "    @entrypoint(store=store)\n",
    "    def workflow(inputs: dict, store: BaseStore):\n",
    "        my_task(inputs).result()\n",
    "        ...\n",
    "    ```\n",
    "    \n",
    "In this guide, we will show how to construct and use a workflow that has a shared memory implemented using the [Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) interface.\n",
    "\n",
    "!!! note Note\n",
    "\n",
    "    Support for the [`Store`](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) API that is used in this guide was added in LangGraph `v0.2.32`.\n",
    "\n",
    "    Support for __index__ and __query__ arguments of the [`Store`](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) API that is used in this guide was added in LangGraph `v0.2.54`.\n",
    "\n",
    "!!! tip \"Note\"\n",
    "\n",
    "    If you need to add cross-thread persistence to a `StateGraph`, check out this [how-to guide](../cross-thread-persistence).\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, let's install the required packages and set our API keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3457aadf",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langchain_anthropic langchain_openai langgraph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aa2c64a7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n",
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51b6817d",
   "metadata": {},
   "source": [
    "!!! tip \"Set up [LangSmith](https://smith.langchain.com) for LangGraph development\"\n",
    "\n",
    "    Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started [here](https://docs.smith.langchain.com)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b5b3d42-3d2c-455e-ac10-e2ae74dc1cf1",
   "metadata": {},
   "source": [
    "## Example: simple chatbot with long-term memory"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4c550b5-1954-496b-8b9d-800361af17dc",
   "metadata": {},
   "source": [
    "### Define store\n",
    "\n",
    "In this example we will create a workflow that will be able to retrieve information about a user's preferences. We will do so by defining an `InMemoryStore` - an object that can store data in memory and query that data.\n",
    "\n",
    "When storing objects using the `Store` interface you define two things:\n",
    "\n",
    "* the namespace for the object, a tuple (similar to directories)\n",
    "* the object key (similar to filenames)\n",
    "\n",
    "In our example, we'll be using `(\"memories\", <user_id>)` as namespace and random UUID as key for each new memory.\n",
    "\n",
    "Importantly, to determine the user, we will be passing `user_id` via the config keyword argument of the node function.\n",
    "\n",
    "Let's first define our store!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a7f303d6-612e-4e34-bf36-29d4ed25d802",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.store.redis import RedisStore\n",
    "from langgraph.store.base import IndexConfig\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "\n",
    "# Create index configuration for vector search\n",
    "index_config: IndexConfig = {\n",
    "    \"dims\": 1536,\n",
    "    \"embed\": OpenAIEmbeddings(model=\"text-embedding-3-small\"),\n",
    "    \"ann_index_config\": {\n",
    "        \"vector_type\": \"vector\",\n",
    "    },\n",
    "    \"distance_type\": \"cosine\",\n",
    "}\n",
    "\n",
    "# Initialize the Redis store\n",
    "redis_store = None\n",
    "with RedisStore.from_conn_string(REDIS_URI, index=index_config) as s:\n",
    "    s.setup()\n",
    "    redis_store = s"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3389c9f4-226d-40c7-8bfc-ee8aac24f79d",
   "metadata": {},
   "source": [
    "### Create workflow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2a30a362-528c-45ee-9df6-630d2d843588",
   "metadata": {},
   "outputs": [],
   "source": [
    "import uuid\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.runnables import RunnableConfig\n",
    "from langchain_core.messages import BaseMessage\n",
    "from langgraph.func import entrypoint, task\n",
    "from langgraph.graph import add_messages\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.store.base import BaseStore\n",
    "\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n",
    "\n",
    "\n",
    "@task\n",
    "def call_model(messages: list[BaseMessage], memory_store: BaseStore, user_id: str):\n",
    "    namespace = (\"memories\", user_id)\n",
    "    last_message = messages[-1]\n",
    "    memories = memory_store.search(namespace, query=str(last_message.content))\n",
    "    info = \"\\n\".join([d.value[\"data\"] for d in memories])\n",
    "    system_msg = f\"You are a helpful assistant talking to the user. User info: {info}\"\n",
    "\n",
    "    # Store new memories if the user asks the model to remember\n",
    "    if \"remember\" in last_message.content.lower():\n",
    "        memory = \"User name is Bob\"\n",
    "        memory_store.put(namespace, str(uuid.uuid4()), {\"data\": memory})\n",
    "\n",
    "    response = model.invoke([{\"role\": \"system\", \"content\": system_msg}] + messages)\n",
    "    return response\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "# NOTE: we're passing the store object here when creating a workflow via entrypoint()\n",
    "@entrypoint(checkpointer=checkpointer, store=redis_store)\n",
    "def workflow(\n",
    "    inputs: list[BaseMessage],\n",
    "    *,\n",
    "    previous: list[BaseMessage],\n",
    "    config: RunnableConfig,\n",
    "    store: BaseStore,\n",
    "):\n",
    "    user_id = config[\"configurable\"][\"user_id\"]\n",
    "    previous = previous or []\n",
    "    inputs = add_messages(previous, inputs)\n",
    "    response = call_model(inputs, store, user_id).result()\n",
    "    return entrypoint.final(value=response, save=add_messages(inputs, response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f22a4a18-67e4-4f0b-b655-a29bbe202e1c",
   "metadata": {},
   "source": [
    "!!! note Note\n",
    "\n",
    "    If you're using LangGraph Cloud or LangGraph Studio, you __don't need__ to pass store to the entrypoint decorator, since it's done automatically."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "552d4e33-556d-4fa5-8094-2a076bc21529",
   "metadata": {},
   "source": [
    "### Run the workflow!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1842c626-6cd9-4f58-b549-58978e478098",
   "metadata": {},
   "source": [
    "Now let's specify a user ID in the config and tell the model our name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c871a073-a466-46ad-aafe-2b870831057e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Bob! Nice to meet you. I'll remember that you're Bob. How can I help you today?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"Hi! Remember: my name is Bob\"}\n",
    "for chunk in workflow.stream([input_message], config, stream_mode=\"values\"):\n",
    "    chunk.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d862be40-1f8a-4057-81c4-b7bf073dc4c1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Your name is Bob!\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n",
    "for chunk in workflow.stream([input_message], config, stream_mode=\"values\"):\n",
    "    chunk.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80fd01ec-f135-4811-8743-daff8daea422",
   "metadata": {},
   "source": [
    "We can now inspect our Redis store and verify that we have in fact saved the memories for the user:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "76cde493-89cf-4709-a339-207d2b7e9ea7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'data': 'User name is Bob'}\n"
     ]
    }
   ],
   "source": [
    "for memory in redis_store.search((\"memories\", \"1\")):\n",
    "    print(memory.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23f5d7eb-af23-4131-b8fd-2a69e74e6e55",
   "metadata": {},
   "source": [
    "Let's now run the workflow for another user to verify that the memories about the first user are self contained:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d362350b-d730-48bd-9652-983812fd7811",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I don't know your name as it wasn't provided in your information. Would you like to tell me your name?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n",
    "for chunk in workflow.stream([input_message], config, stream_mode=\"values\"):\n",
    "    chunk.pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/cross-thread-persistence.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d2eecb96-cf0e-47ed-8116-88a7eaa4236d",
   "metadata": {},
   "source": [
    "# How to add cross-thread persistence to your graph\n",
    "\n",
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>\n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/\">\n",
    "                    Persistence\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/memory/\">\n",
    "                    Memory\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://python.langchain.com/docs/concepts/#chat-models/\">\n",
    "                    Chat Models\n",
    "                </a>\n",
    "            </li>             \n",
    "        </ul>\n",
    "    </p>\n",
    "</div>\n",
    "\n",
    "In the [previous guide](https://langchain-ai.github.io/langgraph/how-tos/persistence/) you learned how to persist graph state across multiple interactions on a single [thread](). LangGraph also allows you to persist data across **multiple threads**. For instance, you can store information about users (their names or preferences) in a shared memory and reuse them in the new conversational threads.\n",
    "\n",
    "In this guide, we will show how to construct and use a graph that has a shared memory implemented using the [Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) interface.\n",
    "\n",
    "<div class=\"admonition note\">\n",
    "    <p class=\"admonition-title\">Note</p>\n",
    "    <p>\n",
    "    Support for the <code><a href=\"https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore\">Store</a></code> API that is used in this guide was added in LangGraph <code>v0.2.32</code>.\n",
    "    </p>\n",
    "    <p>\n",
    "    Support for <b>index</b> and <b>query</b> arguments of the <code><a href=\"https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore\">Store</a></code> API that is used in this guide was added in LangGraph <code>v0.2.54</code>.\n",
    "    </p>\n",
    "</div>\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, let's install the required packages and set our API keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3457aadf",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langchain_openai langgraph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aa2c64a7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n",
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51b6817d",
   "metadata": {},
   "source": [
    "!!! tip \"Set up [LangSmith](https://smith.langchain.com) for LangGraph development\"\n",
    "\n",
    "    Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started [here](https://docs.smith.langchain.com)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4c550b5-1954-496b-8b9d-800361af17dc",
   "metadata": {},
   "source": [
    "## Define store\n",
    "\n",
    "In this example we will create a graph that will be able to retrieve information about a user's preferences. We will do so by defining an `InMemoryStore` - an object that can store data in memory and query that data. We will then pass the store object when compiling the graph. This allows each node in the graph to access the store: when you define node functions, you can define `store` keyword argument, and LangGraph will automatically pass the store object you compiled the graph with.\n",
    "\n",
    "When storing objects using the `Store` interface you define two things:\n",
    "\n",
    "* the namespace for the object, a tuple (similar to directories)\n",
    "* the object key (similar to filenames)\n",
    "\n",
    "In our example, we'll be using `(\"memories\", <user_id>)` as namespace and random UUID as key for each new memory.\n",
    "\n",
    "Importantly, to determine the user, we will be passing `user_id` via the config keyword argument of the node function.\n",
    "\n",
    "Let's first define an `InMemoryStore` already populated with some memories about the users."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a7f303d6-612e-4e34-bf36-29d4ed25d802",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langgraph.store.redis import RedisStore\n",
    "from langgraph.store.base import IndexConfig\n",
    "\n",
    "# Set up Redis connection\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "\n",
    "# Create index configuration for vector search\n",
    "index_config: IndexConfig = {\n",
    "    \"dims\": 1536,\n",
    "    \"embed\": OpenAIEmbeddings(model=\"text-embedding-3-small\"),\n",
    "    \"ann_index_config\": {\n",
    "        \"vector_type\": \"vector\",\n",
    "    },\n",
    "    \"distance_type\": \"cosine\",\n",
    "}\n",
    "\n",
    "# Initialize the Redis store\n",
    "redis_store = None\n",
    "with RedisStore.from_conn_string(REDIS_URI, index=index_config) as s:\n",
    "    s.setup()\n",
    "    redis_store = s"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3389c9f4-226d-40c7-8bfc-ee8aac24f79d",
   "metadata": {},
   "source": [
    "## Create graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2a30a362-528c-45ee-9df6-630d2d843588",
   "metadata": {},
   "outputs": [],
   "source": [
    "import uuid\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.runnables import RunnableConfig\n",
    "from langgraph.graph import StateGraph, MessagesState, START\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "from langgraph.store.base import BaseStore\n",
    "\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
    "\n",
    "\n",
    "# NOTE: we're passing the Store param to the node --\n",
    "# this is the Store we compile the graph with\n",
    "def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):\n",
    "    user_id = config[\"configurable\"][\"user_id\"]\n",
    "    namespace = (\"memories\", user_id)\n",
    "    memories = store.search(namespace, query=str(state[\"messages\"][-1].content))\n",
    "    info = \"\\n\".join([d.value[\"data\"] for d in memories])\n",
    "    system_msg = f\"You are a helpful assistant talking to the user. User info: {info}\"\n",
    "\n",
    "    # Store new memories if the user asks the model to remember\n",
    "    last_message = state[\"messages\"][-1]\n",
    "    if \"remember\" in last_message.content.lower():\n",
    "        memory = \"User name is Bob\"\n",
    "        store.put(namespace, str(uuid.uuid4()), {\"data\": memory})\n",
    "\n",
    "    response = model.invoke(\n",
    "        [{\"role\": \"system\", \"content\": system_msg}] + state[\"messages\"]\n",
    "    )\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "builder = StateGraph(MessagesState)\n",
    "builder.add_node(\"call_model\", call_model)\n",
    "builder.add_edge(START, \"call_model\")\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "# NOTE: we're passing the store object here when compiling the graph\n",
    "graph = builder.compile(checkpointer=checkpointer, store=redis_store)\n",
    "# If you're using LangGraph Cloud or LangGraph Studio, you don't need to pass the store or checkpointer when compiling the graph, since it's done automatically."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f22a4a18-67e4-4f0b-b655-a29bbe202e1c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Note</p>\n",
    "    <p>\n",
    "        If you're using LangGraph Cloud or LangGraph Studio, you <strong>don't need</strong> to pass store when compiling the graph, since it's done automatically.\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "552d4e33-556d-4fa5-8094-2a076bc21529",
   "metadata": {},
   "source": [
    "## Run the graph!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1842c626-6cd9-4f58-b549-58978e478098",
   "metadata": {},
   "source": [
    "Now let's specify a user ID in the config and tell the model our name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c871a073-a466-46ad-aafe-2b870831057e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hi! Remember: my name is Bob\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello Bob! It's nice to meet you. I'll remember that your name is Bob. How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"Hi! Remember: my name is Bob\"}\n",
    "for chunk in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    chunk[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d862be40-1f8a-4057-81c4-b7bf073dc4c1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what is my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Your name is Bob.\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n",
    "for chunk in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    chunk[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80fd01ec-f135-4811-8743-daff8daea422",
   "metadata": {},
   "source": [
    "We can now inspect our Redis store and verify that we have in fact saved the memories for the user:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "76cde493-89cf-4709-a339-207d2b7e9ea7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'data': 'User name is Bob'}\n"
     ]
    }
   ],
   "source": [
    "for memory in redis_store.search((\"memories\", \"1\")):\n",
    "    print(memory.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23f5d7eb-af23-4131-b8fd-2a69e74e6e55",
   "metadata": {},
   "source": [
    "Let's now run the graph for another user to verify that the memories about the first user are self contained:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d362350b-d730-48bd-9652-983812fd7811",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what is my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I apologize, but I don't have any specific information about your name or personal details. As an AI language model, I don't have access to personal information about individual users unless it's provided in the conversation. Is there something else I can help you with?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n",
    "for chunk in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    chunk[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/docker-compose.yml
================
name: langgraph-redis-notebooks
services:
  jupyter:
    build:
      context: .  # Build from current directory
      dockerfile: Dockerfile.jupyter
    ports:
      - "8888:8888"
    volumes:
      - ./:/home/jupyter/workspace/examples
    environment:
      - REDIS_URL=redis://redis:6379
      - USER_AGENT=LangGraphRedisJupyterNotebooks/0.0.4
    user: jupyter
    working_dir: /home/jupyter/workspace/examples
    depends_on:
      - redis
  redis:
    image: redis/redis-stack:latest
    ports:
      - "6379:6379"
      - "8001:8001"

================
File: examples/Dockerfile.jupyter
================
FROM python:3.11

# Set up a non-root user
RUN useradd -m jupyter

WORKDIR /home/jupyter/workspace

# Copy notebook files to the workspace
COPY ./ /home/jupyter/workspace/examples/

# Set permissions
RUN chown -R jupyter:jupyter /home/jupyter/workspace

# Switch to non-root user
USER jupyter

# Set up virtual environment
RUN python -m venv /home/jupyter/venv
ENV PATH="/home/jupyter/venv/bin:$PATH"

# Install dependencies
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir "httpx>=0.24.0,<1.0.0" && \
    pip install --no-cache-dir "langgraph>=0.3.0" && \
    pip install --no-cache-dir "langgraph-checkpoint-redis>=0.0.4" && \
    pip install --no-cache-dir jupyter "redis>=5.2.1" "redisvl>=0.5.1" langchain-openai langchain-anthropic python-ulid
# Note: Notebook-specific dependencies will be installed in the notebook cells as needed

# Set the working directory to the examples folder
WORKDIR /home/jupyter/workspace/examples

# Expose Jupyter port
EXPOSE 8888

# Start Jupyter Notebook with checkpoints disabled
CMD ["jupyter", "notebook", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--ServerApp.token=''", "--ServerApp.password=''", "--ServerApp.allow_root=True", "--NotebookApp.disable_check_xsrf=True", "--FileContentsManager.checkpoints_kwargs={'root_dir':'/tmp/checkpoints'}"]

================
File: examples/jupyter_notebook_config.py
================
# mypy: disable-error-code="name-defined"
c = get_config()  # type: ignore # noqa: F821

================
File: examples/persistence-functional.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
   "metadata": {},
   "source": [
    "# How to add thread-level persistence with Redis (functional API)\n",
    "\n",
    "!!! info \"Prerequisites\"\n",
    "\n",
    "    This guide assumes familiarity with the following:\n",
    "    \n",
    "    - [Functional API](../../concepts/functional_api/)\n",
    "    - [Persistence](../../concepts/persistence/)\n",
    "    - [Memory](../../concepts/memory/)\n",
    "    - [Chat Models](https://python.langchain.com/docs/concepts/chat_models/)\n",
    "\n",
    "Many AI applications need memory to share context across multiple interactions on the same [thread](../../concepts/persistence#threads) (e.g., multiple turns of a conversation). In LangGraph functional API, this kind of memory can be added to any [entrypoint()][langgraph.func.entrypoint] workflow using [thread-level persistence](https://langchain-ai.github.io/langgraph/concepts/persistence).\n",
    "\n",
    "When creating a LangGraph workflow, you can set it up to persist its results by using a [checkpointer](https://langchain-ai.github.io/langgraph/reference/checkpoints/#basecheckpointsaver):\n",
    "\n",
    "\n",
    "1. Create an instance of a Redis checkpointer:\n",
    "\n",
    "    ```python\n",
    "    from langgraph.checkpoint.redis import RedisSaver\n",
    "    \n",
    "    # Set up Redis connection for checkpointer\n",
    "    REDIS_URI = \"redis://redis:6379\"\n",
    "    checkpointer = None\n",
    "    with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "        cp.setup()\n",
    "        checkpointer = cp      \n",
    "    ```\n",
    "\n",
    "2. Pass `checkpointer` instance to the `entrypoint()` decorator:\n",
    "\n",
    "    ```python\n",
    "    from langgraph.func import entrypoint\n",
    "    \n",
    "    @entrypoint(checkpointer=checkpointer)\n",
    "    def workflow(inputs)\n",
    "        ...\n",
    "    ```\n",
    "\n",
    "3. Optionally expose `previous` parameter in the workflow function signature:\n",
    "\n",
    "    ```python\n",
    "    @entrypoint(checkpointer=checkpointer)\n",
    "    def workflow(\n",
    "        inputs,\n",
    "        *,\n",
    "        # you can optionally specify `previous` in the workflow function signature\n",
    "        # to access the return value from the workflow as of the last execution\n",
    "        previous\n",
    "    ):\n",
    "        previous = previous or []\n",
    "        combined_inputs = previous + inputs\n",
    "        result = do_something(combined_inputs)\n",
    "        ...\n",
    "    ```\n",
    "\n",
    "4. Optionally choose which values will be returned from the workflow and which will be saved by the checkpointer as `previous`:\n",
    "\n",
    "    ```python\n",
    "    @entrypoint(checkpointer=checkpointer)\n",
    "    def workflow(inputs, *, previous):\n",
    "        ...\n",
    "        result = do_something(...)\n",
    "        return entrypoint.final(value=result, save=combine(inputs, result))\n",
    "    ```\n",
    "\n",
    "This guide shows how you can add thread-level persistence to your workflow using Redis as the backing store.\n",
    "\n",
    "!!! tip \"Note\"\n",
    "\n",
    "    If you need memory that is __shared__ across multiple conversations or users (cross-thread persistence), check out this [how-to guide](../cross-thread-persistence-functional).\n",
    "\n",
    "!!! tip \"Note\"\n",
    "\n",
    "    If you need to add thread-level persistence to a `StateGraph`, check out this [how-to guide](../persistence)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbd446a-808f-4394-be92-d45ab818953c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First we need to install the packages required"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_anthropic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
   "metadata": {},
   "source": [
    "Next, we need to set API key for Anthropic (the LLM we will use)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "ANTHROPIC_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"ANTHROPIC_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cf509bc",
   "metadata": {},
   "source": [
    "## Example: simple chatbot with short-term memory\n",
    "\n",
    "We will be using a workflow with a single task that calls a [chat model](https://python.langchain.com/docs/concepts/chat_models/).\n",
    "\n",
    "Let's first define the model we'll be using:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "892b54b9-75f0-4804-9ed0-88b5e5532989",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_anthropic import ChatAnthropic\n",
    "\n",
    "model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b7a2792-982b-4e47-83eb-0c594725d1c1",
   "metadata": {},
   "source": [
    "Now we can define our task and workflow. To add in persistence, we need to pass in a [Checkpointer](https://langchain-ai.github.io/langgraph/reference/checkpoints/#langgraph.checkpoint.base.BaseCheckpointSaver) to the [entrypoint()][langgraph.func.entrypoint] decorator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "87326ea6-34c5-46da-a41f-dda26ef9bd74",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import BaseMessage\n",
    "from langgraph.graph import add_messages\n",
    "from langgraph.func import entrypoint, task\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "@task\n",
    "def call_model(messages: list[BaseMessage]):\n",
    "    response = model.invoke(messages)\n",
    "    return response\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "\n",
    "@entrypoint(checkpointer=checkpointer)\n",
    "def workflow(inputs: list[BaseMessage], *, previous: list[BaseMessage]):\n",
    "    if previous:\n",
    "        inputs = add_messages(previous, inputs)\n",
    "\n",
    "    response = call_model(inputs).result()\n",
    "    return entrypoint.final(value=response, save=add_messages(inputs, response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "250d8fd9-2e7a-4892-9adc-19762a1e3cce",
   "metadata": {},
   "source": [
    "If we try to use this workflow, the context of the conversation will be persisted across interactions:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7654ebcc-2179-41b4-92d1-6666f6f8634f",
   "metadata": {},
   "source": [
    "!!! note Note\n",
    "\n",
    "    If you're using LangGraph Cloud or LangGraph Studio, you __don't need__ to pass checkpointer to the entrypoint decorator, since it's done automatically."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
   "metadata": {},
   "source": [
    "We can now interact with the agent and see that it remembers previous messages!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cfd140f0-a5a6-4697-8115-322242f197b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Bob! I'm Claude. Nice to meet you! How are you today?\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "input_message = {\"role\": \"user\", \"content\": \"hi! I'm bob\"}\n",
    "for chunk in workflow.stream([input_message], config, stream_mode=\"values\"):\n",
    "    chunk.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bb07bf8-68b7-4049-a0f1-eb67a4879a3a",
   "metadata": {},
   "source": [
    "You can always resume previous threads:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "08ae8246-11d5-40e1-8567-361e5bef8917",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Your name is Bob. You told me that in your first message when you said \"hi! I'm bob\"\n"
     ]
    }
   ],
   "source": [
    "input_message = {\"role\": \"user\", \"content\": \"what's my name?\"}\n",
    "for chunk in workflow.stream([input_message], config, stream_mode=\"values\"):\n",
    "    chunk.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f47bbfc-d9ef-4288-ba4a-ebbc0136fa9d",
   "metadata": {},
   "source": [
    "If we want to start a new conversation, we can pass in a different `thread_id`. Poof! All the memories are gone!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "273d56a8-f40f-4a51-a27f-7c6bb2bda0ba",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I don't know your name. I can only see our current conversation and don't have access to personal information unless you choose to share it with me.\n"
     ]
    }
   ],
   "source": [
    "input_message = {\"role\": \"user\", \"content\": \"what's my name?\"}\n",
    "for chunk in workflow.stream(\n",
    "    [input_message],\n",
    "    {\"configurable\": {\"thread_id\": \"2\"}},\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    chunk.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac7926a8-4c88-4b16-973c-53d6da3f4a08",
   "metadata": {},
   "source": [
    "!!! tip \"Streaming tokens\"\n",
    "\n",
    "    If you would like to stream LLM tokens from your chatbot, you can use `stream_mode=\"messages\"`. Check out this [how-to guide](../streaming-tokens) to learn more."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/README.md
================
# Redis Notebooks for LangGraph

This directory contains Jupyter notebooks demonstrating the usage of Redis with LangGraph.

## Running Notebooks with Docker

To run these notebooks using Docker (recommended for consistent environment):

1. Ensure you have Docker and Docker Compose installed on your system.
2. Navigate to this directory (`examples`) in your terminal.
3. Run the following command:

   ```bash
   docker compose up
   ```

4. Look for a URL in the console output that starts with `http://127.0.0.1:8888/tree`. Open this URL in your web browser to access Jupyter Notebook.
5. You can now run the notebooks with all dependencies pre-installed.

Note: 
- The first time you run this, it may take a few minutes to build the Docker image.
- The Docker setup uses a simplified structure where the examples are self-contained, making it portable and independent of the repository structure.

To stop the Docker containers, use Ctrl+C in the terminal where you ran `docker compose up`, then run:

```bash
docker compose down
```

## Running Notebooks Locally

If you prefer to run these notebooks locally without Docker:

1. Make sure you have Redis running locally or accessible from your machine.
2. Install the required dependencies:

   ```bash
   pip install langgraph-checkpoint-redis
   pip install langgraph>=0.3.0 
   pip install jupyter redis>=5.2.1 redisvl>=0.5.1 
   pip install langchain-openai langchain-anthropic 
   pip install python-ulid "httpx>=0.24.0,<1.0.0"
   
   # Some notebooks may require additional packages, which will be installed 
   # within the notebooks themselves when needed
   ```

3. Set the appropriate Redis connection string in the notebooks.
4. Launch Jupyter Notebook:

   ```bash
   jupyter notebook
   ```

5. Navigate to the notebook you want to run and open it.

## Notebook Contents

- `persistence-functional.ipynb`: Demonstrates the usage of `RedisSaver` and functional persistence patterns with LangGraph.
- `create-react-agent-memory.ipynb`: Shows how to create an agent with persistent memory using Redis.
- `cross-thread-persistence.ipynb`: Demonstrates cross-thread persistence capabilities with Redis.
- `cross-thread-persistence-functional.ipynb`: Shows functional cross-thread persistence patterns with Redis.
- `create-react-agent-manage-message-history.ipynb`: Shows how to manage conversation history in a ReAct agent with Redis.
- `subgraph-persistence.ipynb`: Demonstrates persistence with subgraphs using Redis.
- `subgraphs-manage-state.ipynb`: Shows how to manage state in subgraphs with Redis.
- `create-react-agent-hitl.ipynb`: Demonstrates human-in-the-loop (HITL) capabilities with Redis.
- `human_in_the_loop/*.ipynb`: Demonstrates various human-in-the-loop interaction patterns with LangGraph and Redis.

All notebooks have been updated to use the Redis implementation instead of memory implementation, showcasing the proper usage of Redis integration with LangGraph.

These notebooks are designed to work both within this Docker environment (using local package builds) and standalone (using installed packages via pip).

================
File: examples/subgraph-persistence.ipynb
================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "176e8dbb-1a0a-49ce-a10e-2417e8ea17a0",
   "metadata": {},
   "source": [
    "# How to add thread-level persistence to a subgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c67581a-49fb-4597-a7fc-6774581c2160",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/low_level/#subgraphs\">\n",
    "                    Subgraphs\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/persistence/\">\n",
    "                    Persistence\n",
    "                </a>\n",
    "            </li>\n",
    "        </ul>\n",
    "    </p>\n",
    "</div>\n",
    "\n",
    "This guide shows how you can add [thread-level](https://langchain-ai.github.io/langgraph/how-tos/persistence/) persistence to graphs that use [subgraphs](https://langchain-ai.github.io/langgraph/how-tos/subgraph/)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f83b855-ab23-4de7-9559-702cad9a29c6",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's install the required packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "77d1eafa-3252-45f6-9af0-d94e1f9c5c9e",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e60c6cd-bf4e-46af-9761-b872d0fbe3b6",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "871b9056-fec7-4683-8c22-f56c91f5b13b",
   "metadata": {},
   "source": [
    "## Define the graph with persistence"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "9f1303ef-df37-48e0-8a59-8ff169c52c5b",
   "metadata": {},
   "source": [
    "To add persistence to a graph with subgraphs, all you need to do is pass a [checkpointer](https://langchain-ai.github.io/langgraph/reference/checkpoints/#langgraph.checkpoint.base.BaseCheckpointSaver) when **compiling the parent graph**. LangGraph will automatically propagate the checkpointer to the child subgraphs."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c74cde2e-c127-4326-8d36-b6acef987f0a",
   "metadata": {},
   "source": [
    "!!! note\n",
    "    You **shouldn't provide** a checkpointer when compiling a subgraph. Instead, you must define a **single** checkpointer that you pass to `parent_graph.compile()`, and LangGraph will automatically propagate the checkpointer to the child subgraphs. If you pass the checkpointer to the `subgraph.compile()`, it will simply be ignored. This also applies when you [add a node function that invokes the subgraph](../subgraph#add-a-node-function-that-invokes-the-subgraph)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3a1fe22-1ca9-45eb-a35b-71b9c905e8c5",
   "metadata": {},
   "source": [
    "Let's define a simple graph with a single subgraph node to show how to do this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0d76f0c0-bd77-4eca-9527-27bcdf85dd42",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0xffff943a5090>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langgraph.graph import START, StateGraph\n",
    "from typing import TypedDict\n",
    "\n",
    "\n",
    "# subgraph\n",
    "\n",
    "\n",
    "class SubgraphState(TypedDict):\n",
    "    foo: str  # note that this key is shared with the parent graph state\n",
    "    bar: str\n",
    "\n",
    "\n",
    "def subgraph_node_1(state: SubgraphState):\n",
    "    return {\"bar\": \"bar\"}\n",
    "\n",
    "\n",
    "def subgraph_node_2(state: SubgraphState):\n",
    "    # note that this node is using a state key ('bar') that is only available in the subgraph\n",
    "    # and is sending update on the shared state key ('foo')\n",
    "    return {\"foo\": state[\"foo\"] + state[\"bar\"]}\n",
    "\n",
    "\n",
    "subgraph_builder = StateGraph(SubgraphState)\n",
    "subgraph_builder.add_node(subgraph_node_1)\n",
    "subgraph_builder.add_node(subgraph_node_2)\n",
    "subgraph_builder.add_edge(START, \"subgraph_node_1\")\n",
    "subgraph_builder.add_edge(\"subgraph_node_1\", \"subgraph_node_2\")\n",
    "subgraph = subgraph_builder.compile()\n",
    "\n",
    "\n",
    "# parent graph\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    foo: str\n",
    "\n",
    "\n",
    "def node_1(state: State):\n",
    "    return {\"foo\": \"hi! \" + state[\"foo\"]}\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "# note that we're adding the compiled subgraph as a node to the parent graph\n",
    "builder.add_node(\"node_2\", subgraph)\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_edge(\"node_1\", \"node_2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47084b1f-9fd5-40a9-9d75-89eb5f853d02",
   "metadata": {},
   "source": [
    "We can now compile the graph with an in-memory checkpointer (`MemorySaver`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "7657d285-c896-40c9-a569-b4a3b9c230c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "checkpointer = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    checkpointer = cp\n",
    "\n",
    "# You must only pass checkpointer when compiling the parent graph.\n",
    "# LangGraph will automatically propagate the checkpointer to the child subgraphs.\n",
    "graph = builder.compile(checkpointer=checkpointer)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d193e3c-4ec3-4034-beed-8e5550c6542c",
   "metadata": {},
   "source": [
    "## Verify persistence works"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb69a5f0-b92e-4d4e-9aa9-c4c4ec7de91a",
   "metadata": {},
   "source": [
    "Let's now run the graph and inspect the persisted state for both the parent graph and the subgraph to verify that persistence works. We should expect to see the final execution results for both the parent and subgraph in `state.values`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "13da686e-6ed6-4b83-93e8-1631fcc8c2a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8721f045-2e82-4bf0-9d85-5ba6ecf899d6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'node_1': {'foo': 'hi! foo'}}\n",
      "{'subgraph_node_1': {'bar': 'bar'}}\n",
      "{'subgraph_node_2': {'foo': 'hi! foobar'}}\n",
      "{'node_2': {'foo': 'hi! foobar'}}\n"
     ]
    }
   ],
   "source": [
    "for _, chunk in graph.stream({\"foo\": \"foo\"}, config, subgraphs=True):\n",
    "    print(chunk)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec6b5ce4-becc-4910-8a6d-d6b60d9d6f60",
   "metadata": {},
   "source": [
    "We can now view the parent graph state by calling `graph.get_state()` with the same config that we used to invoke the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "3e817283-142d-4fda-8cb1-8de34717f833",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'foo': 'hi! foobar'}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbc4f30b-941e-4140-8bfa-3b8cc670489c",
   "metadata": {},
   "source": [
    "To view the subgraph state, we need to do two things:\n",
    "\n",
    "1. Find the most recent config value for the subgraph\n",
    "2. Use `graph.get_state()` to retrieve that value for the most recent subgraph config.\n",
    "\n",
    "To find the correct config, we can examine the state history from the parent graph and find the state snapshot before we return results from `node_2` (the node with subgraph):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e896628f-36b2-45eb-b7c5-c64c1098f328",
   "metadata": {},
   "outputs": [],
   "source": [
    "state_with_subgraph = [\n",
    "    s for s in graph.get_state_history(config) if s.next == (\"node_2\",)\n",
    "][0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7af49977-42b1-40a1-88f1-f07437f8b7f9",
   "metadata": {},
   "source": [
    "The state snapshot will include the list of `tasks` to be executed next. When using subgraphs, the `tasks` will contain the config that we can use to retrieve the subgraph state:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "21e96df3-946d-40f8-8d6d-055ae4177452",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '1',\n",
       "  'checkpoint_ns': 'node_2:36b675af-bd6e-89a7-d67b-31c68339886d'}}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subgraph_config = state_with_subgraph.tasks[0].state\n",
    "subgraph_config"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "1d2401b3-d52b-4895-a5d1-dccf015ba216",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'foo': 'hi! foobar', 'bar': 'bar'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(subgraph_config).values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40aded92-99dd-427b-932d-aa78f474c271",
   "metadata": {},
   "source": [
    "If you want to learn more about how to modify the subgraph state for human-in-the-loop workflows, check out this [how-to guide](https://langchain-ai.github.io/langgraph/how-tos/subgraphs-manage-state/)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

================
File: examples/subgraphs-manage-state.ipynb
================
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to view and update state in subgraphs\n",
    "\n",
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/low_level/#subgraphs\">\n",
    "                    Subgraphs\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/\">\n",
    "                    Human-in-the-loop\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>            \n",
    "                <a href=\"https://langchain-ai.github.io/langgraph/concepts/low_level/#state\">\n",
    "                    State\n",
    "                </a>\n",
    "            </li>\n",
    "        </ul>\n",
    "    </p>\n",
    "</div> \n",
    "\n",
    "Once you add [persistence](../subgraph-persistence), you can easily view and update the state of the subgraph at any point in time. This enables a lot of the human-in-the-loop interaction patterns:\n",
    "\n",
    "* You can surface a state during an interrupt to a user to let them accept an action.\n",
    "* You can rewind the subgraph to reproduce or avoid issues.\n",
    "* You can modify the state to let the user better control its actions.\n",
    "\n",
    "This guide shows how you can do this."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "First, let's install the required packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we need to set API keys for OpenAI (the LLM we will use):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define subgraph\n",
    "\n",
    "First, let's set up our subgraph. For this, we will create a simple graph that can get the weather for a specific city. We will compile this graph with a [breakpoint](https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/breakpoints/) before the `weather_node`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import StateGraph, END, START, MessagesState\n",
    "from langchain_core.tools import tool\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_weather(city: str):\n",
    "    \"\"\"Get the weather for a specific city\"\"\"\n",
    "    return f\"It's sunny in {city}!\"\n",
    "\n",
    "\n",
    "raw_model = ChatOpenAI(model=\"gpt-4o\")\n",
    "model = raw_model.with_structured_output(get_weather)\n",
    "\n",
    "\n",
    "class SubGraphState(MessagesState):\n",
    "    city: str\n",
    "\n",
    "\n",
    "def model_node(state: SubGraphState):\n",
    "    result = model.invoke(state[\"messages\"])\n",
    "    return {\"city\": result[\"city\"]}\n",
    "\n",
    "\n",
    "def weather_node(state: SubGraphState):\n",
    "    result = get_weather.invoke({\"city\": state[\"city\"]})\n",
    "    return {\"messages\": [{\"role\": \"assistant\", \"content\": result}]}\n",
    "\n",
    "\n",
    "subgraph = StateGraph(SubGraphState)\n",
    "subgraph.add_node(model_node)\n",
    "subgraph.add_node(weather_node)\n",
    "subgraph.add_edge(START, \"model_node\")\n",
    "subgraph.add_edge(\"model_node\", \"weather_node\")\n",
    "subgraph.add_edge(\"weather_node\", END)\n",
    "subgraph = subgraph.compile(interrupt_before=[\"weather_node\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define parent graph\n",
    "\n",
    "We can now setup the overall graph. This graph will first route to the subgraph if it needs to get the weather, otherwise it will route to a normal LLM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "class RouterState(MessagesState):\n",
    "    route: Literal[\"weather\", \"other\"]\n",
    "\n",
    "\n",
    "class Router(TypedDict):\n",
    "    route: Literal[\"weather\", \"other\"]\n",
    "\n",
    "\n",
    "router_model = raw_model.with_structured_output(Router)\n",
    "\n",
    "\n",
    "def router_node(state: RouterState):\n",
    "    system_message = \"Classify the incoming query as either about weather or not.\"\n",
    "    messages = [{\"role\": \"system\", \"content\": system_message}] + state[\"messages\"]\n",
    "    route = router_model.invoke(messages)\n",
    "    return {\"route\": route[\"route\"]}\n",
    "\n",
    "\n",
    "def normal_llm_node(state: RouterState):\n",
    "    response = raw_model.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "def route_after_prediction(\n",
    "    state: RouterState,\n",
    ") -> Literal[\"weather_graph\", \"normal_llm_node\"]:\n",
    "    if state[\"route\"] == \"weather\":\n",
    "        return \"weather_graph\"\n",
    "    else:\n",
    "        return \"normal_llm_node\"\n",
    "\n",
    "\n",
    "graph = StateGraph(RouterState)\n",
    "graph.add_node(router_node)\n",
    "graph.add_node(normal_llm_node)\n",
    "graph.add_node(\"weather_graph\", subgraph)\n",
    "graph.add_edge(START, \"router_node\")\n",
    "graph.add_conditional_edges(\"router_node\", route_after_prediction)\n",
    "graph.add_edge(\"normal_llm_node\", END)\n",
    "graph.add_edge(\"weather_graph\", END)\n",
    "graph = graph.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "# Setting xray to 1 will show the internal structure of the nested graph\n",
    "display(Image(graph.get_graph(xray=1).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's test this out with a normal query to make sure it works as intended!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'router_node': {'route': 'other'}}\n",
      "{'normal_llm_node': {'messages': [AIMessage(content='Hello! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 9, 'total_tokens': 19, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f5bdcc3276', 'id': 'chatcmpl-BRlSXXvMNEzsNaXLZjedowbm8hL33', 'finish_reason': 'stop', 'logprobs': None}, id='run-4e5e0dc8-b928-4d9f-9479-8ab8b5cf6160-0', usage_metadata={'input_tokens': 9, 'output_tokens': 10, 'total_tokens': 19, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"hi!\"}]}\n",
    "for update in graph.stream(inputs, config=config, stream_mode=\"updates\"):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Great! We didn't ask about the weather, so we got a normal response from the LLM.\n",
    "\n",
    "## Resuming from breakpoints\n",
    "\n",
    "Let's now look at what happens with breakpoints. Let's invoke it with a query that should get routed to the weather subgraph where we have the interrupt node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'router_node': {'route': 'weather'}}\n",
      "{'__interrupt__': ()}\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in graph.stream(inputs, config=config, stream_mode=\"updates\"):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the graph stream doesn't include subgraph events. If we want to stream subgraph events, we can pass `subgraphs=True` and get back subgraph events like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')]})\n",
      "((), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')], 'route': 'weather'})\n",
      "(('weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0',), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')]})\n",
      "(('weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0',), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')], 'city': 'San Francisco'})\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in graph.stream(inputs, config=config, stream_mode=\"values\", subgraphs=True):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we get the state now, we can see that it's paused on `weather_graph`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('weather_graph',)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = graph.get_state(config)\n",
    "state.next"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we look at the pending tasks for our current state, we can see that we have one task named `weather_graph`, which corresponds to the subgraph task."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(PregelTask(id='7bd6b183-2a8a-824e-5496-40a40a0966c0', name='weather_graph', path=('__pregel_pull', 'weather_graph'), error=None, interrupts=(), state={'configurable': {'thread_id': '3', 'checkpoint_ns': 'weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0'}}, result=None),)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state.tasks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However since we got the state using the config of the parent graph, we don't have access to the subgraph state. If you look at the `state` value of the `PregelTask` above you will note that it is simply the configuration of the parent graph. If we want to actually populate the subgraph state, we can pass in `subgraphs=True` to `get_state` like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PregelTask(id='7bd6b183-2a8a-824e-5496-40a40a0966c0', name='weather_graph', path=('__pregel_pull', 'weather_graph'), error=None, interrupts=(), state=StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')], 'city': 'San Francisco'}, next=('weather_node',), config={'configurable': {'thread_id': '3', 'checkpoint_ns': 'weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0', 'checkpoint_id': '1f02534f-aa2b-6f07-8000-fbe2669bffca', 'checkpoint_map': {'': '1f02534f-aa20-6448-8001-dacd90901fb8', 'weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0': '1f02534f-aa2b-6f07-8000-fbe2669bffca'}}}, metadata={'source': 'loop', 'writes': {'model_node': {'city': 'San Francisco'}}, 'step': 1, 'parents': {'': '1f02534f-aa20-6448-8001-dacd90901fb8'}, 'thread_id': '3', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0'}, created_at='2025-04-29T20:03:12.808506+00:00', parent_config=None, tasks=(PregelTask(id='1221f28f-d77c-4051-1eb9-52d177bc65b6', name='weather_node', path=('__pregel_pull', 'weather_node'), error=None, interrupts=(), state=None, result=None),), interrupts=()), result=None)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = graph.get_state(config, subgraphs=True)\n",
    "state.tasks[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we have access to the subgraph state! If you look at the `state` value of the `PregelTask` you can see that it has all the information we need, like the next node (`weather_node`) and the current state values (e.g. `city`).\n",
    "\n",
    "To resume execution, we can just invoke the outer graph as normal:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')], 'route': 'weather'})\n",
      "(('weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0',), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa')], 'city': 'San Francisco'})\n",
      "(('weather_graph:7bd6b183-2a8a-824e-5496-40a40a0966c0',), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa'), AIMessage(content=\"It's sunny in San Francisco!\", additional_kwargs={}, response_metadata={}, id='fe75cd26-96ec-4660-b16a-3ab87dce7296')], 'city': 'San Francisco'})\n",
      "((), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa'), AIMessage(content=\"It's sunny in San Francisco!\", additional_kwargs={}, response_metadata={}, id='fe75cd26-96ec-4660-b16a-3ab87dce7296')], 'route': 'weather'})\n"
     ]
    }
   ],
   "source": [
    "for update in graph.stream(None, config=config, stream_mode=\"values\", subgraphs=True):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Resuming from specific subgraph node\n",
    "\n",
    "In the example above, we were replaying from the outer graph - which automatically replayed the subgraph from whatever state it was in previously (paused before the `weather_node` in our case), but it is also possible to replay from inside a subgraph. In order to do so, we need to get the configuration from the exact subgraph state that we want to replay from.\n",
    "\n",
    "We can do this by exploring the state history of the subgraph, and selecting the state before `model_node` - which we can do by filtering on the `.next` parameter.\n",
    "\n",
    "To get the state history of the subgraph, we need to first pass in  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "parent_graph_state_before_subgraph = next(\n",
    "    h for h in graph.get_state_history(config) if h.next == (\"weather_graph\",)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "subgraph_state_before_model_node = next(\n",
    "    h\n",
    "    for h in graph.get_state_history(parent_graph_state_before_subgraph.tasks[0].state)\n",
    "    if h.next == (\"model_node\",)\n",
    ")\n",
    "\n",
    "# This pattern can be extended no matter how many levels deep\n",
    "# subsubgraph_stat_history = next(h for h in graph.get_state_history(subgraph_state_before_model_node.tasks[0].state) if h.next == ('my_subsubgraph_node',))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can confirm that we have gotten the correct state by comparing the `.next` parameter of the `subgraph_state_before_model_node`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('model_node',)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subgraph_state_before_model_node.next"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Perfect! We have gotten the correct state snaphshot, and we can now resume from the `model_node` inside of our subgraph:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='4480c377-9426-4fb7-b869-e2a3552cc3fa'), AIMessage(content=\"It's sunny in San Francisco!\", additional_kwargs={}, response_metadata={}, id='fe75cd26-96ec-4660-b16a-3ab87dce7296')], 'route': 'weather'})\n"
     ]
    }
   ],
   "source": [
    "for value in graph.stream(\n",
    "    None,\n",
    "    config=subgraph_state_before_model_node.config,\n",
    "    stream_mode=\"values\",\n",
    "    subgraphs=True,\n",
    "):\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Great, this subsection has shown how you can replay from any node, no matter how deeply nested it is inside your graph - a powerful tool for testing how deterministic your agent is."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Modifying state\n",
    "\n",
    "### Update the state of a subgraph\n",
    "\n",
    "What if we want to modify the state of a subgraph? We can do this similarly to how we [update the state of normal graphs](https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/time-travel/), just being careful to pass in the config of the subgraph to `update_state`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'router_node': {'route': 'weather'}}\n",
      "{'__interrupt__': ()}\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"4\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in graph.stream(inputs, config=config, stream_mode=\"updates\"):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='bb44b193-07e8-43e8-8d0f-c0ccb1009cc2')]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = graph.get_state(config, subgraphs=True)\n",
    "state.values[\"messages\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to update the state of the **inner** graph, we need to pass the config for the **inner** graph, which we can get by accessing calling `state.tasks[0].state.config` - since we interrupted inside the subgraph, the state of the task is just the state of the subgraph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '4',\n",
       "  'checkpoint_ns': 'weather_graph:44a45213-d789-63e2-f893-efb606d654da',\n",
       "  'checkpoint_id': '1f02534f-b85b-65a9-8000-3ac82b3323fa',\n",
       "  'checkpoint_map': {'': '1f02534f-b84e-614c-8001-62bc48f85f0e',\n",
       "   'weather_graph:44a45213-d789-63e2-f893-efb606d654da': '1f02534f-b85b-65a9-8000-3ac82b3323fa'}}}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.update_state(state.tasks[0].state.config, {\"city\": \"la\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now resume streaming the outer graph (which will resume the subgraph!) and check that we updated our search to use LA instead of SF."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(('weather_graph:44a45213-d789-63e2-f893-efb606d654da',), {'weather_node': {'messages': [{'role': 'assistant', 'content': \"It's sunny in la!\"}]}})\n",
      "((), {'weather_graph': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='bb44b193-07e8-43e8-8d0f-c0ccb1009cc2'), AIMessage(content=\"It's sunny in la!\", additional_kwargs={}, response_metadata={}, id='0d94045b-1b72-4f06-be72-076cbb5e3c93')]}})\n"
     ]
    }
   ],
   "source": [
    "for update in graph.stream(None, config=config, stream_mode=\"updates\", subgraphs=True):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fantastic! The AI responded with \"It's sunny in LA!\" as we expected.\n",
    "\n",
    "### Acting as a subgraph node\n",
    "\n",
    "Another way we could update the state is by acting as the `weather_node` ourselves instead of editing the state before `weather_node` is ran as we did above. We can do this by passing the subgraph config and also the `as_node` argument, which allows us to update the state as if we are the node we specify. Thus by setting an interrupt before the `weather_node` and then using the update state function as the `weather_node`, the graph itself never calls `weather_node` directly but instead we decide what the output of `weather_node` should be."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'router_node': {'route': 'weather'}})\n",
      "(('weather_graph:01697bb1-b7c9-de92-fe7e-015347bfe710',), {'model_node': {'city': 'San Francisco'}})\n",
      "((), {'__interrupt__': ()})\n",
      "interrupted!\n",
      "((), {'weather_graph': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='96cbe893-f204-4c06-9253-bf0700bfbc34'), AIMessage(content='rainy', additional_kwargs={}, response_metadata={}, id='12b47fde-603c-4d72-8710-993a896cc890')]}})\n",
      "[HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='96cbe893-f204-4c06-9253-bf0700bfbc34'), AIMessage(content='rainy', additional_kwargs={}, response_metadata={}, id='12b47fde-603c-4d72-8710-993a896cc890')]\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"14\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in graph.stream(\n",
    "    inputs, config=config, stream_mode=\"updates\", subgraphs=True\n",
    "):\n",
    "    print(update)\n",
    "# Graph execution should stop before the weather node\n",
    "print(\"interrupted!\")\n",
    "\n",
    "state = graph.get_state(config, subgraphs=True)\n",
    "\n",
    "# We update the state by passing in the message we want returned from the weather node, and make sure to use as_node\n",
    "graph.update_state(\n",
    "    state.tasks[0].state.config,\n",
    "    {\"messages\": [{\"role\": \"assistant\", \"content\": \"rainy\"}]},\n",
    "    as_node=\"weather_node\",\n",
    ")\n",
    "for update in graph.stream(None, config=config, stream_mode=\"updates\", subgraphs=True):\n",
    "    print(update)\n",
    "\n",
    "print(graph.get_state(config).values[\"messages\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Perfect! The AI responded with the message we passed in ourselves.\n",
    "\n",
    "### Acting as the entire subgraph\n",
    "\n",
    "Lastly, we could also update the graph just acting as the **entire** subgraph. This is similar to the case above but instead of acting as just the `weather_node` we are acting as the entire subgraph. This is done by passing in the normal graph config as well as the `as_node` argument, where we specify the we are acting as the entire subgraph node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'router_node': {'route': 'weather'}})\n",
      "(('weather_graph:45a2c9ac-19b8-f35e-d2bb-80c2c8fe8f86',), {'model_node': {'city': 'San Francisco'}})\n",
      "((), {'__interrupt__': ()})\n",
      "interrupted!\n",
      "[HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='f6c83085-adbe-4eef-a95d-4208cc4432f9'), AIMessage(content='rainy', additional_kwargs={}, response_metadata={}, id='853fa6f1-11a3-41e7-87f3-38eeaf40c69c')]\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"8\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in graph.stream(\n",
    "    inputs, config=config, stream_mode=\"updates\", subgraphs=True\n",
    "):\n",
    "    print(update)\n",
    "# Graph execution should stop before the weather node\n",
    "print(\"interrupted!\")\n",
    "\n",
    "# We update the state by passing in the message we want returned from the weather graph, making sure to use as_node\n",
    "# Note that we don't need to pass in the subgraph config, since we aren't updating the state inside the subgraph\n",
    "graph.update_state(\n",
    "    config,\n",
    "    {\"messages\": [{\"role\": \"assistant\", \"content\": \"rainy\"}]},\n",
    "    as_node=\"weather_graph\",\n",
    ")\n",
    "for update in graph.stream(None, config=config, stream_mode=\"updates\"):\n",
    "    print(update)\n",
    "\n",
    "print(graph.get_state(config).values[\"messages\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, the AI responded with \"rainy\" as we expected.\n",
    "\n",
    "## Double nested subgraphs\n",
    "\n",
    "This same functionality continues to work no matter the level of nesting. Here is an example of doing the same things with a double nested subgraph (although any level of nesting will work). We add another router on top of our already defined graphs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "from typing import Literal\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "class RouterState(MessagesState):\n",
    "    route: Literal[\"weather\", \"other\"]\n",
    "\n",
    "\n",
    "class Router(TypedDict):\n",
    "    route: Literal[\"weather\", \"other\"]\n",
    "\n",
    "\n",
    "router_model = raw_model.with_structured_output(Router)\n",
    "\n",
    "\n",
    "def router_node(state: RouterState):\n",
    "    system_message = \"Classify the incoming query as either about weather or not.\"\n",
    "    messages = [{\"role\": \"system\", \"content\": system_message}] + state[\"messages\"]\n",
    "    route = router_model.invoke(messages)\n",
    "    return {\"route\": route[\"route\"]}\n",
    "\n",
    "\n",
    "def normal_llm_node(state: RouterState):\n",
    "    response = raw_model.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "def route_after_prediction(\n",
    "    state: RouterState,\n",
    ") -> Literal[\"weather_graph\", \"normal_llm_node\"]:\n",
    "    if state[\"route\"] == \"weather\":\n",
    "        return \"weather_graph\"\n",
    "    else:\n",
    "        return \"normal_llm_node\"\n",
    "\n",
    "\n",
    "graph = StateGraph(RouterState)\n",
    "graph.add_node(router_node)\n",
    "graph.add_node(normal_llm_node)\n",
    "graph.add_node(\"weather_graph\", subgraph)\n",
    "graph.add_edge(START, \"router_node\")\n",
    "graph.add_conditional_edges(\"router_node\", route_after_prediction)\n",
    "graph.add_edge(\"normal_llm_node\", END)\n",
    "graph.add_edge(\"weather_graph\", END)\n",
    "graph = graph.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n",
      "\u001b[32m20:03:18\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m   Index already exists, not overwriting.\n"
     ]
    }
   ],
   "source": [
    "from langgraph.checkpoint.redis import RedisSaver\n",
    "\n",
    "# Set up Redis connection for checkpointer\n",
    "REDIS_URI = \"redis://redis:6379\"\n",
    "memory = None\n",
    "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n",
    "    cp.setup()\n",
    "    memory = cp\n",
    "\n",
    "\n",
    "class GrandfatherState(MessagesState):\n",
    "    to_continue: bool\n",
    "\n",
    "\n",
    "def router_node(state: GrandfatherState):\n",
    "    # Dummy logic that will always continue\n",
    "    return {\"to_continue\": True}\n",
    "\n",
    "\n",
    "def route_after_prediction(state: GrandfatherState):\n",
    "    if state[\"to_continue\"]:\n",
    "        return \"graph\"\n",
    "    else:\n",
    "        return END\n",
    "\n",
    "\n",
    "grandparent_graph = StateGraph(GrandfatherState)\n",
    "grandparent_graph.add_node(router_node)\n",
    "grandparent_graph.add_node(\"graph\", graph)\n",
    "grandparent_graph.add_edge(START, \"router_node\")\n",
    "grandparent_graph.add_conditional_edges(\n",
    "    \"router_node\", route_after_prediction, [\"graph\", END]\n",
    ")\n",
    "grandparent_graph.add_edge(\"graph\", END)\n",
    "grandparent_graph = grandparent_graph.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "# Setting xray to 1 will show the internal structure of the nested graph\n",
    "display(Image(grandparent_graph.get_graph(xray=2).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we run until the interrupt, we can now see that there are snapshots of the state of all three graphs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "((), {'router_node': {'to_continue': True}})\n",
      "(('graph:ecd08a47-d858-7231-c7a0-aa74b7934e49',), {'router_node': {'route': 'weather'}})\n",
      "(('graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6'), {'model_node': {'city': 'San Francisco'}})\n",
      "((), {'__interrupt__': ()})\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "inputs = {\"messages\": [{\"role\": \"user\", \"content\": \"what's the weather in sf\"}]}\n",
    "for update in grandparent_graph.stream(\n",
    "    inputs, config=config, stream_mode=\"updates\", subgraphs=True\n",
    "):\n",
    "    print(update)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Grandparent State:\n",
      "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')], 'to_continue': True}\n",
      "---------------\n",
      "Parent Graph State:\n",
      "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')], 'route': 'weather'}\n",
      "---------------\n",
      "Subgraph State:\n",
      "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}\n"
     ]
    }
   ],
   "source": [
    "state = grandparent_graph.get_state(config, subgraphs=True)\n",
    "print(\"Grandparent State:\")\n",
    "print(state.values)\n",
    "print(\"---------------\")\n",
    "print(\"Parent Graph State:\")\n",
    "print(state.tasks[0].state.values)\n",
    "print(\"---------------\")\n",
    "print(\"Subgraph State:\")\n",
    "print(state.tasks[0].state.tasks[0].state.values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now continue, acting as the node three levels down"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(('graph:ecd08a47-d858-7231-c7a0-aa74b7934e49',), {'weather_graph': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}})\n",
      "((), {'graph': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}})\n",
      "[HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]\n"
     ]
    }
   ],
   "source": [
    "grandparent_graph_state = state\n",
    "parent_graph_state = grandparent_graph_state.tasks[0].state\n",
    "subgraph_state = parent_graph_state.tasks[0].state\n",
    "grandparent_graph.update_state(\n",
    "    subgraph_state.config,\n",
    "    {\"messages\": [{\"role\": \"assistant\", \"content\": \"rainy\"}]},\n",
    "    as_node=\"weather_node\",\n",
    ")\n",
    "for update in grandparent_graph.stream(\n",
    "    None, config=config, stream_mode=\"updates\", subgraphs=True\n",
    "):\n",
    "    print(update)\n",
    "\n",
    "print(grandparent_graph.get_state(config).values[\"messages\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As in the cases above, we can see that the AI responds with \"rainy\" as we expect.\n",
    "\n",
    "We can explore the state history to see how the state of the grandparent graph was updated at each step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': ''}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': \"what's the weather in sf\"}]}}, 'step': -1, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:10.223564+00:00', parent_config=None, tasks=(PregelTask(id='21d8f2f8-46c3-3701-812b-6bcf24bda147', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1')]}, next=('router_node',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-9757-6061-bfff-ad28e8ed1a58'}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:10.225471+00:00', parent_config=None, tasks=(PregelTask(id='4c5096be-5c6b-8732-8623-0c6106f96dfa', name='router_node', path=('__pregel_pull', 'router_node'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597', 'checkpoint_id': '', 'checkpoint_map': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7', 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597': ''}}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': '0a0cde55-27e8-4c98-bf2a-0707a1d887a1'}}], 'route': 'weather'}}, 'step': -1, 'parents': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7'}, 'thread_id': '2', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597'}, created_at='2025-04-29T20:03:10.812412+00:00', parent_config=None, tasks=(PregelTask(id='30fd433b-43cf-7dce-f6cf-09c0b098387a', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597', 'checkpoint_id': '1f02534f-9cf4-6a2b-bfff-036966fe6cce', 'checkpoint_map': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7', 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597': '1f02534f-9cf4-6a2b-bfff-036966fe6cce'}}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7'}, 'thread_id': '2', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597'}, created_at='2025-04-29T20:03:10.815786+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-975b-6af2-8000-d525c5020c98'}}, metadata={'source': 'loop', 'writes': {'router_node': {'route': 'weather'}}, 'step': 1, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:10.808486+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597', 'checkpoint_id': '1f02534f-9cfc-6e34-8000-e4c77de3aaf7', 'checkpoint_map': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7', 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597': '1f02534f-9cfc-6e34-8000-e4c77de3aaf7'}}}, metadata={'source': 'loop', 'writes': {'model_node': {'city': 'sf'}}, 'step': 1, 'parents': {'': '1f02534f-9ceb-61f3-8001-e0d2101e26b7'}, 'thread_id': '2', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'weather_graph:ed8b1cf5-2ebb-102e-1d28-4d7bd2d7c597'}, created_at='2025-04-29T20:03:11.359276+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1')]}, next=('__start__',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-9ceb-61f3-8001-e0d2101e26b7'}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': \"what's the weather in sf\"}]}}, 'step': 2, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:18.341546+00:00', parent_config=None, tasks=(PregelTask(id='59ceba0c-a86b-9f80-d2d4-bdc1ef42d60f', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}, next=('router_node',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-e4c2-6500-8002-607354b38b9c'}}, metadata={'source': 'loop', 'writes': None, 'step': 3, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:18.342922+00:00', parent_config=None, tasks=(PregelTask(id='40f6faec-869b-587b-074a-3988857a8011', name='router_node', path=('__pregel_pull', 'router_node'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')], 'to_continue': True}, next=('graph',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-e4c5-6aa6-8003-00a8d2565f11'}}, metadata={'source': 'loop', 'writes': {'router_node': {'to_continue': True}}, 'step': 4, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:18.345047+00:00', parent_config=None, tasks=(PregelTask(id='ecd08a47-d858-7231-c7a0-aa74b7934e49', name='graph', path=('__pregel_pull', 'graph'), error=None, interrupts=(), state={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49'}}, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'checkpoint_id': '', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': ''}}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': '0a0cde55-27e8-4c98-bf2a-0707a1d887a1'}}, {'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': 'cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b'}}], 'to_continue': True}}, 'step': -1, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644'}, 'thread_id': '2', 'langgraph_step': 5, 'langgraph_node': 'graph', 'langgraph_triggers': ['branch:to:graph'], 'langgraph_path': ['__pregel_pull', 'graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49'}, created_at='2025-04-29T20:03:18.348322+00:00', parent_config=None, tasks=(PregelTask(id='2cd6780d-5584-5481-476f-f46eb3ab707c', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}, next=('router_node',), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'checkpoint_id': '1f02534f-e4d2-6d80-bfff-0da28d2d90d1', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-e4d2-6d80-bfff-0da28d2d90d1'}}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644'}, 'thread_id': '2', 'langgraph_step': 5, 'langgraph_node': 'graph', 'langgraph_triggers': ['branch:to:graph'], 'langgraph_path': ['__pregel_pull', 'graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49'}, created_at='2025-04-29T20:03:18.350141+00:00', parent_config=None, tasks=(PregelTask(id='ffce42bc-3dcb-79d6-9684-007d1556c852', name='router_node', path=('__pregel_pull', 'router_node'), error=None, interrupts=(), state=None, result=None),), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'checkpoint_id': '1f02534f-e4d7-64b8-8000-42a577aa117a', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-e4d7-64b8-8000-42a577aa117a'}}}, metadata={'source': 'loop', 'writes': {'router_node': {'route': 'weather'}}, 'step': 1, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644'}, 'thread_id': '2', 'langgraph_step': 5, 'langgraph_node': 'graph', 'langgraph_triggers': ['branch:to:graph'], 'langgraph_path': ['__pregel_pull', 'graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49'}, created_at='2025-04-29T20:03:18.945545+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': []}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6', 'checkpoint_id': '1f02534f-ea97-6e0c-8001-4cb845379071', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-ea85-604e-8001-fd2c19df9a62', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6': '1f02534f-ea97-6e0c-8001-4cb845379071'}}}, metadata={'source': 'loop', 'writes': None, 'step': 2, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-ea85-604e-8001-fd2c19df9a62'}, 'thread_id': '2', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6'}, created_at='2025-04-29T20:03:18.955348+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': []}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6', 'checkpoint_id': '1f02534f-ea9c-6dc8-8002-9eaf091dda7c', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-ea85-604e-8001-fd2c19df9a62', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6': '1f02534f-ea9c-6dc8-8002-9eaf091dda7c'}}}, metadata={'source': 'loop', 'writes': {'model_node': {'city': 'San Francisco'}}, 'step': 3, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-ea85-604e-8001-fd2c19df9a62'}, 'thread_id': '2', 'langgraph_step': 2, 'langgraph_node': 'weather_graph', 'langgraph_triggers': ['branch:to:weather_graph'], 'langgraph_path': ['__pregel_pull', 'weather_graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6'}, created_at='2025-04-29T20:03:20.277729+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6', 'checkpoint_id': '1f02534f-e4d7-64b8-8000-42a577aa117a', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6': '1f02534f-e4d7-64b8-8000-42a577aa117a'}}}, metadata={'source': 'update', 'writes': {'weather_node': {'messages': [{'role': 'assistant', 'content': 'rainy'}]}}, 'step': 2, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644'}, 'thread_id': '2', 'langgraph_step': 5, 'langgraph_node': 'graph', 'langgraph_triggers': ['branch:to:graph'], 'langgraph_path': ['__pregel_pull', 'graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49|weather_graph:64329b7f-d9e7-1f2c-9a6e-7a3d819eaed6', 'checkpoint_id': '1f02534f-e4d7-64b8-8000-42a577aa117a'}, created_at='2025-04-29T20:03:20.313043+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')]}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49', 'checkpoint_id': '1f02534f-ea85-604e-8001-fd2c19df9a62', 'checkpoint_map': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644', 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49': '1f02534f-ea85-604e-8001-fd2c19df9a62'}}}, metadata={'source': 'loop', 'writes': {'weather_graph': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': '0a0cde55-27e8-4c98-bf2a-0707a1d887a1'}}, {'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': 'cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b'}}]}}, 'step': 2, 'parents': {'': '1f02534f-e4ca-6df0-8004-ff0a69394644'}, 'thread_id': '2', 'langgraph_step': 5, 'langgraph_node': 'graph', 'langgraph_triggers': ['branch:to:graph'], 'langgraph_path': ['__pregel_pull', 'graph'], 'langgraph_checkpoint_ns': 'graph:ecd08a47-d858-7231-c7a0-aa74b7934e49'}, created_at='2025-04-29T20:03:20.321696+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a0cde55-27e8-4c98-bf2a-0707a1d887a1'), HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b')], 'to_continue': True}, next=(), config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1f02534f-e4ca-6df0-8004-ff0a69394644'}}, metadata={'source': 'loop', 'writes': {'graph': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': '0a0cde55-27e8-4c98-bf2a-0707a1d887a1'}}, {'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'HumanMessage'], 'kwargs': {'content': \"what's the weather in sf\", 'type': 'human', 'id': 'cc01dc7a-f5bc-4ed3-8ea7-430941d46c7b'}}]}}, 'step': 5, 'parents': {}, 'thread_id': '2'}, created_at='2025-04-29T20:03:20.324070+00:00', parent_config=None, tasks=(), interrupts=())\n",
      "-----\n"
     ]
    }
   ],
   "source": [
    "for state in grandparent_graph.get_state_history(config):\n",
    "    print(state)\n",
    "    print(\"-----\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}

================
File: langgraph/checkpoint/redis/__init__.py
================
logger = logging.getLogger(__name__)
class RedisSaver(BaseRedisSaver[Union[Redis, RedisCluster], SearchIndex])
⋮----
"""Standard Redis implementation for checkpoint saving."""
_redis: Union[Redis, RedisCluster]  # Support both standalone and cluster clients
# Whether to assume the Redis server is a cluster; None triggers auto-detection
cluster_mode: Optional[bool] = None
⋮----
"""Configure the Redis client."""
⋮----
def create_indexes(self) -> None
def setup(self) -> None
⋮----
"""Initialize the indices in Redis and detect cluster mode."""
⋮----
def _detect_cluster_mode(self) -> None
⋮----
"""Detect if the Redis client is a cluster client by inspecting its class."""
⋮----
# Determine cluster mode based on client class
⋮----
"""List checkpoints from Redis."""
# Construct the filter expression
filter_expression = []
⋮----
# Reproducing the logic from the Postgres implementation, we'll
# search for checkpoints with any namespace, including an empty
# string, while `checkpoint_id` has to have a value.
⋮----
# if before:
#     filter_expression.append(Tag("checkpoint_id") < get_checkpoint_id(before))
# Combine all filter expressions
combined_filter = filter_expression[0] if filter_expression else "*"
⋮----
# Construct the Redis query
query = FilterQuery(
# Execute the query
results = self.checkpoints_index.search(query)
# Process the results
⋮----
thread_id = from_storage_safe_id(doc["thread_id"])
checkpoint_ns = from_storage_safe_str(doc["checkpoint_ns"])
checkpoint_id = from_storage_safe_id(doc["checkpoint_id"])
parent_checkpoint_id = from_storage_safe_id(doc["parent_checkpoint_id"])
# Fetch channel_values
channel_values = self.get_channel_values(
# Fetch pending_sends from parent checkpoint
pending_sends = []
⋮----
pending_sends = self._load_pending_sends(
# Fetch and parse metadata
raw_metadata = getattr(doc, "$.metadata", "{}")
metadata_dict = (
# Ensure metadata matches CheckpointMetadata type
sanitized_metadata = {
metadata = cast(CheckpointMetadata, sanitized_metadata)
config_param: RunnableConfig = {
checkpoint_param = self._load_checkpoint(
pending_writes = self._load_pending_writes(
⋮----
"""Store a checkpoint to Redis."""
configurable = config["configurable"].copy()
thread_id = configurable.pop("thread_id")
checkpoint_ns = configurable.pop("checkpoint_ns")
thread_ts = configurable.pop("thread_ts", "")
checkpoint_id = (
# For values we store in Redis, we need to convert empty strings to the
# sentinel value.
storage_safe_thread_id = to_storage_safe_id(thread_id)
storage_safe_checkpoint_ns = to_storage_safe_str(checkpoint_ns)
storage_safe_checkpoint_id = to_storage_safe_id(checkpoint_id)
copy = checkpoint.copy()
# When we return the config, we need to preserve empty strings that
# were passed in, instead of the sentinel value.
next_config = {
# Store checkpoint data.
checkpoint_data = {
# store at top-level for filters in list()
⋮----
# Create the checkpoint key
checkpoint_key = BaseRedisSaver._make_redis_checkpoint_key(
⋮----
# Store blob values.
blobs = self._dump_blobs(
blob_keys = []
⋮----
# Unzip the list of tuples into separate lists for keys and data
⋮----
blob_keys = list(keys)
⋮----
# Apply TTL to checkpoint and blob keys if configured
⋮----
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Get a checkpoint tuple from Redis.
        Args:
            config (RunnableConfig): The config to use for retrieving the checkpoint.
        Returns:
            Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
        """
thread_id = config["configurable"]["thread_id"]
checkpoint_id = get_checkpoint_id(config)
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
ascending = True
⋮----
checkpoint_filter_expression = (
⋮----
ascending = False
# Construct the query
checkpoints_query = FilterQuery(
⋮----
results = self.checkpoints_index.search(checkpoints_query)
⋮----
doc = results.docs[0]
doc_thread_id = from_storage_safe_id(doc["thread_id"])
doc_checkpoint_ns = from_storage_safe_str(doc["checkpoint_ns"])
doc_checkpoint_id = from_storage_safe_id(doc["checkpoint_id"])
doc_parent_checkpoint_id = from_storage_safe_id(doc["parent_checkpoint_id"])
# If refresh_on_read is enabled, refresh TTL for checkpoint key and related keys
⋮----
# Get the checkpoint key
⋮----
# Get all blob keys related to this checkpoint
⋮----
# Get the blob keys using search index instead of keys()
blob_query = FilterQuery(
⋮----
return_fields=["key"],  # Assuming the key field exists in the index
⋮----
blob_results = self.checkpoint_blobs_index.search(blob_query)
blob_keys = [
# Get checkpoint write keys using search index
write_query = FilterQuery(
write_results = self.checkpoint_writes_index.search(write_query)
write_keys = [
# Apply TTL to checkpoint, blob keys, and write keys
all_related_keys = blob_keys + write_keys
⋮----
# Fetch channel_values
⋮----
# Fetch pending_sends from parent checkpoint
⋮----
# Fetch and parse metadata
⋮----
# Ensure metadata matches CheckpointMetadata type
⋮----
"""Create a new RedisSaver instance."""
saver: Optional[RedisSaver] = None
⋮----
saver = cls(
⋮----
if saver and saver._owns_its_client:  # Ensure saver is not None
⋮----
"""Retrieve channel_values dictionary with properly constructed message objects."""
⋮----
checkpoint_query = FilterQuery(
checkpoint_result = self.checkpoints_index.search(checkpoint_query)
⋮----
channel_versions = json.loads(
⋮----
channel_values = {}
⋮----
blob_doc = blob_results.docs[0]
blob_type = getattr(blob_doc, "type", None)
blob_data = getattr(blob_doc, "$.blob", None)
⋮----
# Ensure blob_data is bytes for deserialization
⋮----
blob_data = blob_data.encode("utf-8")
⋮----
"""Load pending sends for a parent checkpoint.
        Args:
            thread_id: The thread ID
            checkpoint_ns: The checkpoint namespace
            parent_checkpoint_id: The ID of the parent checkpoint
        Returns:
            List of (type, blob) tuples representing pending sends
        """
storage_safe_thread_id = to_storage_safe_str(thread_id)
⋮----
storage_safe_parent_checkpoint_id = to_storage_safe_str(parent_checkpoint_id)
# Query checkpoint_writes for parent checkpoint's TASKS channel
parent_writes_query = FilterQuery(
⋮----
num_results=100,  # Adjust as needed
⋮----
parent_writes_results = self.checkpoint_writes_index.search(parent_writes_query)
# Sort results by task_path, task_id, idx (matching Postgres implementation)
sorted_writes = sorted(
# Extract type and blob pairs
⋮----
def delete_thread(self, thread_id: str) -> None
⋮----
"""Delete all checkpoints and writes associated with a specific thread ID.
        Args:
            thread_id: The thread ID whose checkpoints should be deleted.
        """
⋮----
# Delete all checkpoints for this thread
⋮----
num_results=10000,  # Get all checkpoints for this thread
⋮----
checkpoint_results = self.checkpoints_index.search(checkpoint_query)
# Collect all keys to delete
keys_to_delete = []
⋮----
checkpoint_ns = getattr(doc, "checkpoint_ns", "")
checkpoint_id = getattr(doc, "checkpoint_id", "")
# Delete checkpoint key
⋮----
# Delete all blobs for this thread
⋮----
channel = getattr(doc, "channel", "")
version = getattr(doc, "version", "")
blob_key = BaseRedisSaver._make_redis_checkpoint_blob_key(
⋮----
# Delete all writes for this thread
writes_query = FilterQuery(
writes_results = self.checkpoint_writes_index.search(writes_query)
⋮----
task_id = getattr(doc, "task_id", "")
idx = getattr(doc, "idx", 0)
write_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
⋮----
# Execute all deletions based on cluster mode
⋮----
# For cluster mode, delete keys individually
⋮----
# For non-cluster mode, use pipeline for efficiency
pipeline = self._redis.pipeline()
⋮----
__all__ = [

================
File: langgraph/checkpoint/redis/aio.py
================
"""Async implementation of Redis checkpoint saver."""
⋮----
logger = logging.getLogger(__name__)
class AsyncRedisSaver(
⋮----
"""Async Redis implementation for checkpoint saver."""
_redis_url: str
checkpoints_index: AsyncSearchIndex
checkpoint_blobs_index: AsyncSearchIndex
checkpoint_writes_index: AsyncSearchIndex
_redis: Union[
⋮----
]  # Support both standalone and cluster clients
# Whether to assume the Redis server is a cluster; None triggers auto-detection
cluster_mode: Optional[bool] = None
⋮----
"""Configure the Redis client."""
⋮----
# Use direct AsyncRedis.from_url to avoid the deprecated get_async_redis_connection
⋮----
redis_url = os.environ.get("REDIS_URL")
⋮----
def create_indexes(self) -> None
⋮----
"""Create indexes without connecting to Redis."""
⋮----
async def __aenter__(self) -> AsyncRedisSaver
⋮----
"""Async context manager enter."""
⋮----
# Set client info once Redis is set up
⋮----
"""Async context manager exit."""
⋮----
coro = self._redis.connection_pool.disconnect()
⋮----
# Prevent RedisVL from attempting to close the client
# on an event loop in a separate thread.
⋮----
async def asetup(self) -> None
⋮----
"""Set up the checkpoint saver."""
⋮----
# Detect cluster mode if not explicitly set
⋮----
async def _detect_cluster_mode(self) -> None
⋮----
"""Detect if the Redis client is a cluster client by inspecting its class."""
⋮----
# Determine cluster mode based on client class
⋮----
"""Apply Redis native TTL to keys asynchronously.
        Args:
            main_key: The primary Redis key
            related_keys: Additional Redis keys that should expire at the same time
            ttl_minutes: Time-to-live in minutes, overrides default_ttl if provided
        Returns:
            Result of the Redis operation
        """
⋮----
# Check if there's a default TTL in config
⋮----
ttl_minutes = self.ttl_config.get("default_ttl")
⋮----
ttl_seconds = int(ttl_minutes * 60)
⋮----
# For cluster mode, execute TTL operations individually
⋮----
# For non-cluster mode, use pipeline for efficiency
pipeline = self._redis.pipeline()
# Set TTL for main key
⋮----
# Set TTL for related keys
⋮----
async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Get a checkpoint tuple from Redis asynchronously."""
thread_id = config["configurable"]["thread_id"]
checkpoint_id = get_checkpoint_id(config)
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
ascending = True
⋮----
checkpoint_filter_expression = (
⋮----
ascending = False
# Construct the query
checkpoints_query = FilterQuery(
⋮----
# Execute the query
results = await self.checkpoints_index.search(checkpoints_query)
⋮----
doc = results.docs[0]
doc_thread_id = from_storage_safe_id(doc["thread_id"])
doc_checkpoint_ns = from_storage_safe_str(doc["checkpoint_ns"])
doc_checkpoint_id = from_storage_safe_id(doc["checkpoint_id"])
doc_parent_checkpoint_id = from_storage_safe_id(doc["parent_checkpoint_id"])
# If refresh_on_read is enabled, refresh TTL for checkpoint key and related keys
⋮----
# Get the checkpoint key
checkpoint_key = BaseRedisSaver._make_redis_checkpoint_key(
# Get all blob keys related to this checkpoint
⋮----
# Get the blob keys
blob_key_pattern = f"{CHECKPOINT_BLOB_PREFIX}:{to_storage_safe_id(doc_thread_id)}:{to_storage_safe_str(doc_checkpoint_ns)}:*"
blob_keys = await self._redis.keys(blob_key_pattern)
# Use safely_decode to handle both string and bytes responses
blob_keys = [safely_decode(key) for key in blob_keys]
# Also get checkpoint write keys that should have the same TTL
write_key_pattern = f"{CHECKPOINT_WRITE_PREFIX}:{to_storage_safe_id(doc_thread_id)}:{to_storage_safe_str(doc_checkpoint_ns)}:{to_storage_safe_id(doc_checkpoint_id)}:*"
write_keys = await self._redis.keys(write_key_pattern)
⋮----
write_keys = [safely_decode(key) for key in write_keys]
# Apply TTL to checkpoint, blob keys, and write keys
all_related_keys = blob_keys + write_keys
⋮----
# Fetch channel_values
channel_values = await self.aget_channel_values(
# Fetch pending_sends from parent checkpoint
pending_sends = []
⋮----
pending_sends = await self._aload_pending_sends(
# Fetch and parse metadata
raw_metadata = getattr(doc, "$.metadata", "{}")
metadata_dict = (
# Ensure metadata matches CheckpointMetadata type
sanitized_metadata = {
metadata = cast(CheckpointMetadata, sanitized_metadata)
config_param: RunnableConfig = {
checkpoint_param = self._load_checkpoint(
pending_writes = await self._aload_pending_writes(
⋮----
"""List checkpoints from Redis asynchronously."""
# Construct the filter expression
filter_expression = []
⋮----
# Reproducing the logic from the Postgres implementation, we'll
# search for checkpoints with any namespace, including an empty
# string, while `checkpoint_id` has to have a value.
⋮----
# if before:
#     filter_expression.append(Tag("checkpoint_id") < get_checkpoint_id(before))
# Combine all filter expressions
combined_filter = filter_expression[0] if filter_expression else "*"
⋮----
# Construct the Redis query
query = FilterQuery(
# Execute the query asynchronously
results = await self.checkpoints_index.search(query)
# Process the results
⋮----
thread_id = from_storage_safe_id(doc["thread_id"])
checkpoint_ns = from_storage_safe_str(doc["checkpoint_ns"])
checkpoint_id = from_storage_safe_id(doc["checkpoint_id"])
parent_checkpoint_id = from_storage_safe_id(doc["parent_checkpoint_id"])
# Fetch channel_values
⋮----
# Fetch pending_sends from parent checkpoint
⋮----
# Fetch and parse metadata
⋮----
# Ensure metadata matches CheckpointMetadata type
⋮----
"""Store a checkpoint to Redis with proper transaction handling.
        This method ensures that all Redis operations are performed atomically
        using Redis transactions. In case of interruption (asyncio.CancelledError),
        the transaction will be aborted, ensuring consistency.
        Args:
            config: The config to associate with the checkpoint
            checkpoint: The checkpoint data to store
            metadata: Additional metadata to save with the checkpoint
            new_versions: New channel versions as of this write
            stream_mode: The streaming mode being used (values, updates, etc.)
        Returns:
            Updated configuration after storing the checkpoint
        Raises:
            asyncio.CancelledError: If the operation is cancelled/interrupted
        """
configurable = config["configurable"].copy()
thread_id = configurable.pop("thread_id")
checkpoint_ns = configurable.pop("checkpoint_ns")
thread_ts = configurable.pop("thread_ts", "")
checkpoint_id = (
# For values we store in Redis, we need to convert empty strings to the
# sentinel value.
storage_safe_thread_id = to_storage_safe_id(thread_id)
storage_safe_checkpoint_ns = to_storage_safe_str(checkpoint_ns)
storage_safe_checkpoint_id = to_storage_safe_id(checkpoint_id)
copy = checkpoint.copy()
next_config = {
# Store checkpoint data with cluster-aware handling
⋮----
# Store checkpoint data
checkpoint_data = {
# store at top-level for filters in list()
⋮----
# Prepare checkpoint key
⋮----
# Store blob values
blobs = self._dump_blobs(
⋮----
# For cluster mode, execute operations individually
await self._redis.json().set(checkpoint_key, "$", checkpoint_data)  # type: ignore[misc]
⋮----
await self._redis.json().set(key, "$", data)  # type: ignore[misc]
# Apply TTL if configured
⋮----
# For non-cluster mode, use pipeline with transaction for atomicity
pipeline = self._redis.pipeline(transaction=True)
# Add checkpoint data to pipeline
⋮----
# Add all blob operations to the pipeline
⋮----
# Execute all operations atomically
⋮----
# Apply TTL to checkpoint and blob keys if configured
⋮----
# Handle cancellation/interruption based on stream mode
⋮----
# For these modes, we want to ensure any partial state is committed
# to allow resuming the stream later
⋮----
# Store minimal checkpoint data
⋮----
# Prepare checkpoint key
⋮----
# For cluster mode, execute operation directly
await self._redis.json().set(  # type: ignore[misc]
⋮----
# For non-cluster mode, use pipeline
⋮----
# If this also fails, we just propagate the original cancellation
⋮----
# Re-raise the cancellation
⋮----
# Re-raise other exceptions
⋮----
"""Store intermediate writes linked to a checkpoint using Redis JSON with transaction handling.
        This method uses Redis pipeline with transaction=True to ensure atomicity of all
        write operations. In case of interruption, all operations will be aborted.
        Args:
            config (RunnableConfig): Configuration of the related checkpoint.
            writes (List[Tuple[str, Any]]): List of writes to store.
            task_id (str): Identifier for the task creating the writes.
            task_path (str): Path of the task creating the writes.
        Raises:
            asyncio.CancelledError: If the operation is cancelled/interrupted
        """
⋮----
checkpoint_id = config["configurable"]["checkpoint_id"]
# Transform writes into appropriate format
writes_objects = []
⋮----
write_obj = {
⋮----
# Determine if this is an upsert case
upsert_case = all(w[0] in WRITES_IDX_MAP for w in writes)
created_keys = []
⋮----
key = self._make_redis_checkpoint_writes_key(
⋮----
write_obj["idx"],  # type: ignore[arg-type]
⋮----
# For upsert case, check if key exists and update differently
exists = await self._redis.exists(key)
⋮----
# Update existing key
await self._redis.json().set(key, "$.channel", write_obj["channel"])  # type: ignore[misc, arg-type]
await self._redis.json().set(key, "$.type", write_obj["type"])  # type: ignore[misc, arg-type]
await self._redis.json().set(key, "$.blob", write_obj["blob"])  # type: ignore[misc, arg-type]
⋮----
# Create new key
await self._redis.json().set(key, "$", write_obj)  # type: ignore[misc]
⋮----
# For non-upsert case, only set if key doesn't exist
⋮----
# Apply TTL to newly created keys
⋮----
# For non-cluster mode, use transaction pipeline for atomicity
⋮----
# Add all write operations to the pipeline
⋮----
# For upsert case, we need to check if the key exists and update differently
⋮----
write_obj["channel"],  # type: ignore[arg-type]
⋮----
write_obj["type"],  # type: ignore[arg-type]
⋮----
write_obj["blob"],  # type: ignore[arg-type]
⋮----
# Handle cancellation/interruption
# Pipeline will be automatically discarded
# Either all operations succeed or none do
⋮----
"""Synchronous wrapper for aput_writes.
        Args:
            config (RunnableConfig): Configuration of the related checkpoint.
            writes (List[Tuple[str, Any]]): List of writes to store.
            task_id (str): Identifier for the task creating the writes.
            task_path (str): Path of the task creating the writes.
        """
⋮----
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Get a checkpoint tuple from Redis.
        Args:
            config (RunnableConfig): The config to use for retrieving the checkpoint.
        Returns:
            Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
        Raises:
            asyncio.InvalidStateError: If called from the wrong thread/event loop
        """
⋮----
# check if we are in the main thread, only bg threads can block
⋮----
"""Store a checkpoint to Redis.
        Args:
            config (RunnableConfig): The config to associate with the checkpoint.
            checkpoint (Checkpoint): The checkpoint to save.
            metadata (CheckpointMetadata): Additional metadata to save with the checkpoint.
            new_versions (ChannelVersions): New channel versions as of this write.
        Returns:
            RunnableConfig: Updated configuration after storing the checkpoint.
        Raises:
            asyncio.InvalidStateError: If called from the wrong thread/event loop
        """
⋮----
"""Retrieve channel_values dictionary with properly constructed message objects."""
⋮----
checkpoint_query = FilterQuery(
checkpoint_result = await self.checkpoints_index.search(checkpoint_query)
⋮----
channel_versions = json.loads(
⋮----
channel_values = {}
⋮----
blob_query = FilterQuery(
blob_results = await self.checkpoint_blobs_index.search(blob_query)
⋮----
blob_doc = blob_results.docs[0]
blob_type = blob_doc.type
blob_data = getattr(blob_doc, "$.blob", None)
⋮----
"""Load pending sends for a parent checkpoint.
        Args:
            thread_id: The thread ID
            checkpoint_ns: The checkpoint namespace
            parent_checkpoint_id: The ID of the parent checkpoint
        Returns:
            List of (type, blob) tuples representing pending sends
        """
# Query checkpoint_writes for parent checkpoint's TASKS channel
parent_writes_query = FilterQuery(
res = await self.checkpoint_writes_index.search(parent_writes_query)
# Sort results for deterministic order
docs = sorted(
# Convert to expected format
⋮----
return []  # Early return if no checkpoint_id
# Use search index instead of keys() to avoid CrossSlot errors
# Note: For checkpoint_ns, we use the raw value for tag searches
# because RediSearch may not handle sentinel values correctly in tag fields
writes_query = FilterQuery(
⋮----
num_results=1000,  # Adjust as needed
⋮----
writes_results = await self.checkpoint_writes_index.search(writes_query)
# Sort results by idx to maintain order
sorted_writes = sorted(writes_results.docs, key=lambda x: getattr(x, "idx", 0))
# Build the writes dictionary
writes_dict: Dict[Tuple[str, str], Dict[str, Any]] = {}
⋮----
task_id = str(getattr(doc, "task_id", ""))
idx = str(getattr(doc, "idx", 0))
blob_data = getattr(doc, "$.blob", "")
# Ensure blob is bytes for deserialization
⋮----
blob_data = blob_data.encode("utf-8")
⋮----
pending_writes = BaseRedisSaver._load_writes(self.serde, writes_dict)
⋮----
async def adelete_thread(self, thread_id: str) -> None
⋮----
"""Delete all checkpoints and writes associated with a specific thread ID.
        Args:
            thread_id: The thread ID whose checkpoints should be deleted.
        """
⋮----
# Delete all checkpoints for this thread
⋮----
num_results=10000,  # Get all checkpoints for this thread
⋮----
checkpoint_results = await self.checkpoints_index.search(checkpoint_query)
# Collect all keys to delete
keys_to_delete = []
⋮----
checkpoint_ns = getattr(doc, "checkpoint_ns", "")
checkpoint_id = getattr(doc, "checkpoint_id", "")
# Delete checkpoint key
⋮----
# Delete all blobs for this thread
⋮----
channel = getattr(doc, "channel", "")
version = getattr(doc, "version", "")
blob_key = BaseRedisSaver._make_redis_checkpoint_blob_key(
⋮----
# Delete all writes for this thread
⋮----
task_id = getattr(doc, "task_id", "")
idx = getattr(doc, "idx", 0)
write_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
⋮----
# Execute all deletions based on cluster mode
⋮----
# For cluster mode, delete keys individually
⋮----
# For non-cluster mode, use pipeline for efficiency

================
File: langgraph/checkpoint/redis/ashallow.py
================
"""Async shallow Redis implementation for LangGraph checkpoint saving."""
⋮----
SCHEMAS = [
class AsyncShallowRedisSaver(BaseRedisSaver[AsyncRedis, AsyncSearchIndex])
⋮----
"""Async Redis implementation that only stores the most recent checkpoint."""
_redis_url: str
checkpoints_index: AsyncSearchIndex
checkpoint_blobs_index: AsyncSearchIndex
checkpoint_writes_index: AsyncSearchIndex
_redis: AsyncRedis  # Override the type from the base class
⋮----
async def __aenter__(self) -> AsyncShallowRedisSaver
⋮----
"""Async context manager enter."""
⋮----
# Set client info once Redis is set up
⋮----
coro = self._redis.connection_pool.disconnect()
⋮----
# Prevent RedisVL from attempting to close the client
# on an event loop in a separate thread.
⋮----
"""Create a new AsyncShallowRedisSaver instance."""
⋮----
async def asetup(self) -> None
⋮----
"""Initialize Redis indexes asynchronously."""
# Create indexes in Redis asynchronously
⋮----
"""Store only the latest checkpoint asynchronously and clean up old blobs with transaction handling.
        This method uses Redis pipeline with transaction=True to ensure atomicity of checkpoint operations.
        In case of interruption, all operations will be aborted, maintaining consistency.
        Args:
            config: The config to associate with the checkpoint
            checkpoint: The checkpoint data to store
            metadata: Additional metadata to save with the checkpoint
            new_versions: New channel versions as of this write
        Returns:
            Updated configuration after storing the checkpoint
        Raises:
            asyncio.CancelledError: If the operation is cancelled/interrupted
        """
configurable = config["configurable"].copy()
thread_id = configurable.pop("thread_id")
checkpoint_ns = configurable.pop("checkpoint_ns")
copy = checkpoint.copy()
next_config = {
⋮----
# Create a pipeline with transaction=True for atomicity
pipeline = self._redis.pipeline(transaction=True)
# Store checkpoint data
checkpoint_data = {
# store at top-level for filters in list()
⋮----
# Note: Need to keep track of the current versions to keep
current_channel_versions = new_versions.copy()
# Prepare the checkpoint key
checkpoint_key = AsyncShallowRedisSaver._make_shallow_redis_checkpoint_key(
# Add checkpoint data to pipeline
⋮----
# Before storing the new blobs, clean up old ones that won't be needed
# - Get a list of all blob keys for this thread_id and checkpoint_ns
# - Then delete the ones that aren't in new_versions
# Get all blob keys for this thread/namespace (this is done outside the pipeline)
blob_key_pattern = (
existing_blob_keys = await self._redis.keys(blob_key_pattern)
# Process each existing blob key to determine if it should be kept or deleted
⋮----
# Use safely_decode to handle both string and bytes responses
decoded_key = safely_decode(blob_key)
key_parts = decoded_key.split(REDIS_KEY_SEPARATOR)
# The key format is checkpoint_blob:thread_id:checkpoint_ns:channel:version
⋮----
channel = key_parts[3]
version = key_parts[4]
# Only keep the blob if it's referenced by the current versions
⋮----
# This is a current version, keep it
⋮----
# This is an old version, delete it
⋮----
# Store the new blob values
blobs = self._dump_blobs(
⋮----
# Add all blob data to pipeline
⋮----
# Execute all operations atomically
⋮----
# Apply TTL to checkpoint and blob keys if configured
⋮----
# Prepare the list of keys to apply TTL
ttl_keys = [checkpoint_key]
⋮----
# Apply TTL to all keys
ttl_minutes = self.ttl_config.get("default_ttl")
ttl_seconds = int(ttl_minutes * 60)
ttl_pipeline = self._redis.pipeline()
⋮----
# Handle cancellation/interruption
# Pipeline will be automatically discarded
# Either all operations succeed or none do
⋮----
# Re-raise other exceptions
⋮----
"""List checkpoints from Redis asynchronously."""
query_filter = []
⋮----
combined_filter = query_filter[0] if query_filter else "*"
⋮----
query = FilterQuery(
⋮----
num_results=limit or 100,  # Set higher limit to retrieve more results
⋮----
results = await self.checkpoints_index.search(query)
⋮----
async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Retrieve a checkpoint tuple from Redis asynchronously."""
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_filter_expression = (Tag("thread_id") == thread_id) & (
# Construct the query
checkpoints_query = FilterQuery(
# Execute the query
results = await self.checkpoints_index.search(checkpoints_query)
⋮----
doc = results.docs[0]
# If refresh_on_read is enabled, refresh TTL for checkpoint key and related keys
⋮----
thread_id = getattr(doc, "thread_id", "")
checkpoint_ns = getattr(doc, "checkpoint_ns", "")
# Get the checkpoint key
⋮----
# Get all blob keys related to this checkpoint
⋮----
blob_keys = await self._redis.keys(blob_key_pattern)
# Use safely_decode to handle both string and bytes responses
blob_keys = [safely_decode(key) for key in blob_keys]
# Apply TTL
⋮----
pipeline = self._redis.pipeline()
⋮----
checkpoint = json.loads(doc["$.checkpoint"])
# Fetch channel_values
channel_values = await self.aget_channel_values(
# Fetch pending_sends from parent checkpoint
pending_sends = await self._aload_pending_sends(
# Fetch and parse metadata
raw_metadata = getattr(doc, "$.metadata", "{}")
metadata_dict = (
# Ensure metadata matches CheckpointMetadata type
sanitized_metadata = {
metadata = cast(CheckpointMetadata, sanitized_metadata)
config_param: RunnableConfig = {
checkpoint_param = self._load_checkpoint(
pending_writes = await self._aload_pending_writes(
⋮----
"""Store intermediate writes for the latest checkpoint and clean up old writes with transaction handling.
        This method uses Redis pipeline with transaction=True to ensure atomicity of all
        write operations. In case of interruption, all operations will be aborted.
        Args:
            config (RunnableConfig): Configuration of the related checkpoint.
            writes (List[Tuple[str, Any]]): List of writes to store.
            task_id (str): Identifier for the task creating the writes.
            task_path (str): Path of the task creating the writes.
        Raises:
            asyncio.CancelledError: If the operation is cancelled/interrupted
        """
⋮----
checkpoint_id = config["configurable"]["checkpoint_id"]
⋮----
# Create a transaction pipeline for atomicity
⋮----
# Transform writes into appropriate format
writes_objects = []
⋮----
write_obj = {
⋮----
# First get all writes keys for this thread/namespace (outside the pipeline)
writes_key_pattern = AsyncShallowRedisSaver._make_shallow_redis_checkpoint_writes_key_pattern(
existing_writes_keys = await self._redis.keys(writes_key_pattern)
# Process each existing writes key to determine if it should be kept or deleted
⋮----
decoded_key = safely_decode(write_key)
⋮----
# The key format is checkpoint_write:thread_id:checkpoint_ns:checkpoint_id:task_id:idx
⋮----
key_checkpoint_id = key_parts[3]
# If the write is for a different checkpoint_id, delete it
⋮----
# Add new writes to the pipeline
upsert_case = all(w[0] in WRITES_IDX_MAP for w in writes)
⋮----
key = self._make_redis_checkpoint_writes_key(
⋮----
# For upsert case, we need to check if the key exists (outside the pipeline)
exists = await self._redis.exists(key)
⋮----
# Update existing key
⋮----
# Create new key
⋮----
# For shallow implementation, always set the full object
⋮----
"""Retrieve channel_values dictionary with properly constructed message objects."""
checkpoint_query = FilterQuery(
checkpoint_result = await self.checkpoints_index.search(checkpoint_query)
⋮----
channel_versions = json.loads(
⋮----
channel_values = {}
⋮----
blob_query = FilterQuery(
blob_results = await self.checkpoint_blobs_index.search(blob_query)
⋮----
blob_doc = blob_results.docs[0]
blob_type = blob_doc.type
blob_data = getattr(blob_doc, "$.blob", None)
⋮----
"""Load pending sends for a parent checkpoint.
        Args:
            thread_id: The thread ID
            checkpoint_ns: The checkpoint namespace
            parent_checkpoint_id: The ID of the parent checkpoint
        Returns:
            List of (type, blob) tuples representing pending sends
        """
# Query checkpoint_writes for parent checkpoint's TASKS channel
parent_writes_query = FilterQuery(
parent_writes_results = await self.checkpoint_writes_index.search(
# Sort results by task_path, task_id, idx (matching Postgres implementation)
sorted_writes = sorted(
# Extract type and blob pairs
⋮----
return []  # Early return if no checkpoint_id
writes_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
matching_keys = await self._redis.keys(pattern=writes_key)
# Use safely_decode to handle both string and bytes responses
decoded_keys = [safely_decode(key) for key in matching_keys]
parsed_keys = [
pending_writes = BaseRedisSaver._load_writes(
⋮----
)  # type: ignore[misc]
⋮----
"""Configure the Redis client."""
⋮----
# Use direct AsyncRedis.from_url to avoid the deprecated get_async_redis_connection
⋮----
redis_url = os.environ.get("REDIS_URL")
⋮----
def create_indexes(self) -> None
⋮----
"""Create indexes without connecting to Redis."""
⋮----
def setup(self) -> None
⋮----
"""Initialize the checkpoint_index in Redis."""
⋮----
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Retrieve a checkpoint tuple from Redis synchronously."""
⋮----
"""Store only the latest checkpoint synchronously."""
⋮----
"""Store intermediate writes synchronously."""
⋮----
@staticmethod
    def _make_shallow_redis_checkpoint_key(thread_id: str, checkpoint_ns: str) -> str
⋮----
"""Create a key for shallow checkpoints using only thread_id and checkpoint_ns."""
⋮----
"""Create a pattern to match all blob keys for a thread and namespace."""
⋮----
"""Create a pattern to match all writes keys for a thread and namespace."""

================
File: langgraph/checkpoint/redis/base.py
================
REDIS_KEY_SEPARATOR = ":"
CHECKPOINT_PREFIX = "checkpoint"
CHECKPOINT_BLOB_PREFIX = "checkpoint_blob"
CHECKPOINT_WRITE_PREFIX = "checkpoint_write"
SCHEMAS = [
class BaseRedisSaver(BaseCheckpointSaver[str], Generic[RedisClientType, IndexType])
⋮----
"""Base Redis implementation for checkpoint saving.
    Uses Redis JSON for storing checkpoints and related data, with RediSearch for querying.
    """
_redis: RedisClientType
_owns_its_client: bool = False
SCHEMAS = SCHEMAS
checkpoints_index: IndexType
checkpoint_blobs_index: IndexType
checkpoint_writes_index: IndexType
⋮----
"""Initialize Redis-backed checkpoint saver.
        Args:
            redis_url: Redis connection URL
            redis_client: Redis client instance to use (alternative to redis_url)
            connection_args: Additional arguments for Redis connection
            ttl: Optional TTL configuration dict with optional keys:
                - default_ttl: TTL in minutes for all checkpoint keys
                - refresh_on_read: Whether to refresh TTL on reads
        """
⋮----
# Store TTL configuration
⋮----
# Initialize indexes
⋮----
@abstractmethod
    def create_indexes(self) -> None
⋮----
"""Create appropriate SearchIndex instances."""
⋮----
"""Configure the Redis client."""
⋮----
def set_client_info(self) -> None
⋮----
"""Set client info for Redis monitoring."""
⋮----
# Try to use client_setinfo command if available
⋮----
# Fall back to a simple echo if client_setinfo is not available
⋮----
# Silently fail if even echo doesn't work
⋮----
async def aset_client_info(self) -> None
⋮----
"""Set client info for Redis monitoring asynchronously."""
⋮----
# Create the client info string with only the redisvl version
client_info = f"redis-py(redisvl_v{__redisvl_version__})"
⋮----
# Call with await to ensure it's an async call
echo_result = self._redis.echo(client_info)
⋮----
def setup(self) -> None
⋮----
"""Initialize the indices in Redis."""
# Create indexes in Redis
⋮----
loaded = json.loads(checkpoint)  # type: ignore[arg-type]
# Note: TTL refresh is now handled in get_tuple() to ensure it works
# with all Redis operations, not just internal deserialization
⋮----
"""Apply Redis native TTL to keys.
        Args:
            main_key: The primary Redis key
            related_keys: Additional Redis keys that should expire at the same time
            ttl_minutes: Time-to-live in minutes, overrides default_ttl if provided
        Returns:
            Result of the Redis operation
        """
⋮----
# Check if there's a default TTL in config
⋮----
ttl_minutes = self.ttl_config.get("default_ttl")
⋮----
ttl_seconds = int(ttl_minutes * 60)
# Check if cluster mode is detected (for sync checkpoint savers)
cluster_mode = getattr(self, "cluster_mode", False)
⋮----
# For cluster mode, execute TTL operations individually
⋮----
# For non-cluster mode, use pipeline for efficiency
pipeline = self._redis.pipeline()
# Set TTL for main key
⋮----
# Set TTL for related keys
⋮----
def _dump_checkpoint(self, checkpoint: Checkpoint) -> dict[str, Any]
⋮----
"""Convert checkpoint to Redis format."""
⋮----
# Decode bytes to avoid double serialization
checkpoint_data = json.loads(data)
⋮----
def _load_blobs(self, blob_values: dict[str, Any]) -> dict[str, Any]
⋮----
"""Load binary data from Redis."""
⋮----
def _get_type_and_blob(self, value: Any) -> Tuple[str, Optional[bytes]]
⋮----
"""Helper to get type and blob from a value."""
⋮----
"""Convert blob data for Redis storage."""
⋮----
storage_safe_thread_id = to_storage_safe_id(thread_id)
storage_safe_checkpoint_ns = to_storage_safe_str(checkpoint_ns)
# Ensure all versions are converted to strings to avoid TypeError with Tag filters
str_versions = {k: str(v) for k, v in versions.items()}
⋮----
str_versions[k],  # Use the string version
⋮----
"version": str_versions[k],  # Use the string version
⋮----
"""Convert write operations for Redis storage."""
⋮----
def _load_metadata(self, metadata: dict[str, Any]) -> CheckpointMetadata
⋮----
"""Load metadata from Redis-compatible dictionary.
        Args:
            metadata: Dictionary representation from Redis.
        Returns:
            Original metadata dictionary.
        """
⋮----
def _dump_metadata(self, metadata: CheckpointMetadata) -> str
⋮----
"""Convert metadata to a Redis-compatible dictionary.
        Args:
            metadata: Metadata to convert.
        Returns:
            Dictionary representation of metadata for Redis storage.
        """
serialized_metadata = self.serde.dumps(metadata)
# NOTE: we're using JSON serializer (not msgpack), so we need to remove null characters before writing
⋮----
def get_next_version(  # type: ignore[override]
⋮----
"""Generate next version number."""
⋮----
current_v = 0
⋮----
current_v = current
⋮----
current_v = int(current.split(".")[0])
next_v = current_v + 1
next_h = random.random()
⋮----
def _encode_blob(self, blob: Any) -> str
⋮----
"""Encode blob data for Redis storage."""
⋮----
def _decode_blob(self, blob: str) -> bytes
⋮----
"""Decode blob data from Redis storage."""
⋮----
# Handle both malformed base64 data and incorrect input types
⋮----
def _load_writes_from_redis(self, write_key: str) -> List[Tuple[str, str, Any]]
⋮----
"""Load writes from Redis JSON storage by key."""
⋮----
# Get the full JSON document
result = self._redis.json().get(write_key)
⋮----
writes = []
for write in result["writes"]:  # type: ignore[call-overload]
⋮----
"""Store intermediate writes linked to a checkpoint.
        Args:
            config: Configuration of the related checkpoint.
            writes: List of writes to store, each as (channel, value) pair.
            task_id: Identifier for the task creating the writes.
            task_path: Optional path info for the task.
        """
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = config["configurable"]["checkpoint_id"]
# Transform writes into appropriate format
writes_objects = []
⋮----
write_obj = {
⋮----
"checkpoint_ns": checkpoint_ns,  # Don't use sentinel for tag fields in RediSearch
⋮----
# For each write, check existence and then perform appropriate operation
⋮----
# Keep track of keys we're creating
created_keys = []
⋮----
key = self._make_redis_checkpoint_writes_key(
# First check if key exists
key_exists = self._redis.exists(key) == 1
⋮----
# UPSERT case - only update specific fields
⋮----
# Update only channel, type, and blob fields
⋮----
# For new records, set the complete object
⋮----
# INSERT case - only insert if doesn't exist
⋮----
# Apply TTL to newly created keys
⋮----
return []  # Early return if no checkpoint_id
# Use search index instead of keys() to avoid CrossSlot errors
# Note: For checkpoint_ns, we use the raw value for tag searches
# because RediSearch may not handle sentinel values correctly in tag fields
writes_query = FilterQuery(
⋮----
num_results=1000,  # Adjust as needed
⋮----
writes_results = self.checkpoint_writes_index.search(writes_query)
# Sort results by idx to maintain order
sorted_writes = sorted(writes_results.docs, key=lambda x: getattr(x, "idx", 0))
# Build the writes dictionary
writes_dict: Dict[Tuple[str, str], Dict[str, Any]] = {}
⋮----
task_id = str(getattr(doc, "task_id", ""))
idx = str(getattr(doc, "idx", 0))
blob_data = getattr(doc, "$.blob", "")
# Ensure blob is bytes for deserialization
⋮----
blob_data = blob_data.encode("utf-8")
⋮----
pending_writes = BaseRedisSaver._load_writes(self.serde, writes_dict)
⋮----
"""Deserialize pending writes."""
writes = [
⋮----
@staticmethod
    def _parse_redis_checkpoint_writes_key(redis_key: str) -> dict
⋮----
# Ensure redis_key is a string
redis_key = safely_decode(redis_key)
parts = redis_key.split(REDIS_KEY_SEPARATOR)
# Ensure we have at least 6 parts
⋮----
# Extract the first 6 parts regardless of total length
⋮----
storage_safe_thread_id = str(to_storage_safe_id(thread_id))
⋮----
storage_safe_checkpoint_id = str(to_storage_safe_id(checkpoint_id))

================
File: langgraph/checkpoint/redis/jsonplus_redis.py
================
logger = logging.getLogger(__name__)
class JsonPlusRedisSerializer(JsonPlusSerializer)
⋮----
"""Redis-optimized serializer that stores strings directly."""
SENTINEL_FIELDS = [
def dumps_typed(self, obj: Any) -> tuple[str, str]:  # type: ignore[override]
def loads_typed(self, data: tuple[str, Union[str, bytes]]) -> Any
⋮----
decoded = base64.b64decode(
⋮----
data_bytes = data_ if isinstance(data_, bytes) else data_.encode()

================
File: langgraph/checkpoint/redis/shallow.py
================
SCHEMAS = [
class ShallowRedisSaver(BaseRedisSaver[Redis, SearchIndex])
⋮----
"""Redis implementation that only stores the most recent checkpoint."""
⋮----
"""Create a new ShallowRedisSaver instance."""
saver: Optional[ShallowRedisSaver] = None
⋮----
saver = cls(
⋮----
"""Store only the latest checkpoint and clean up old blobs."""
configurable = config["configurable"].copy()
thread_id = configurable.pop("thread_id")
checkpoint_ns = configurable.pop("checkpoint_ns")
copy = checkpoint.copy()
next_config = {
# Store checkpoint data
checkpoint_data = {
# store at top-level for filters in list()
⋮----
# Note: Need to keep track of the current versions to keep
current_channel_versions = new_versions.copy()
⋮----
# Before storing the new blobs, clean up old ones that won't be needed
# - Get a list of all blob keys for this thread_id and checkpoint_ns
# - Then delete the ones that aren't in new_versions
cleanup_pipeline = self._redis.json().pipeline(transaction=False)
# Get all blob keys for this thread/namespace
blob_key_pattern = (
existing_blob_keys = self._redis.keys(blob_key_pattern)
# Process each existing blob key to determine if it should be kept or deleted
⋮----
# Use safely_decode to handle both string and bytes responses
decoded_key = safely_decode(blob_key)
key_parts = decoded_key.split(REDIS_KEY_SEPARATOR)
# The key format is checkpoint_blob:thread_id:checkpoint_ns:channel:version
⋮----
channel = key_parts[3]
version = key_parts[4]
# Only keep the blob if it's referenced by the current versions
⋮----
# This is a current version, keep it
⋮----
# This is an old version, delete it
⋮----
# Execute the cleanup
⋮----
# Store blob values
blobs = self._dump_blobs(
blob_keys = []
⋮----
# Unzip the list of tuples into separate lists for keys and data
⋮----
blob_keys = list(keys)
⋮----
# Apply TTL to checkpoint and blob keys if configured
checkpoint_key = ShallowRedisSaver._make_shallow_redis_checkpoint_key(
⋮----
"""List checkpoints from Redis."""
# Construct the filter expression
filter_expression = []
⋮----
# if before:
#     filter_expression.append(Tag("checkpoint_id") < get_checkpoint_id(before))
# Combine all filter expressions
combined_filter = filter_expression[0] if filter_expression else "*"
⋮----
# Construct the Redis query
query = FilterQuery(
# Execute the query
results = self.checkpoints_index.search(query)
# Process the results
⋮----
thread_id = cast(str, getattr(doc, "thread_id", ""))
checkpoint_ns = cast(str, getattr(doc, "checkpoint_ns", ""))
checkpoint = json.loads(doc["$.checkpoint"])
# Fetch channel_values
channel_values = self.get_channel_values(
# Fetch pending_sends from parent checkpoint
pending_sends = self._load_pending_sends(
# Fetch and parse metadata
raw_metadata = getattr(doc, "$.metadata", "{}")
metadata_dict = (
# Ensure metadata matches CheckpointMetadata type
sanitized_metadata = {
metadata = cast(CheckpointMetadata, sanitized_metadata)
checkpoint_param = self._load_checkpoint(
config_param: RunnableConfig = {
pending_writes = self._load_pending_writes(
⋮----
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]
⋮----
"""Get a checkpoint tuple from Redis.
        Args:
            config (RunnableConfig): The config to use for retrieving the checkpoint.
        Returns:
            Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
        """
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_filter_expression = (Tag("thread_id") == thread_id) & (
# Construct the query
checkpoints_query = FilterQuery(
⋮----
results = self.checkpoints_index.search(checkpoints_query)
⋮----
doc = results.docs[0]
# If refresh_on_read is enabled, refresh TTL for checkpoint key and related keys
⋮----
thread_id = getattr(doc, "thread_id", "")
checkpoint_ns = getattr(doc, "checkpoint_ns", "")
# Get the checkpoint key
⋮----
# Get all blob keys related to this checkpoint
⋮----
# Use safely_decode to handle both string and bytes responses
blob_keys = [
# Apply TTL
⋮----
# Fetch channel_values
⋮----
# Fetch pending_sends from parent checkpoint
⋮----
# Fetch and parse metadata
⋮----
# Ensure metadata matches CheckpointMetadata type
⋮----
"""Configure the Redis client."""
⋮----
# Set client info for Redis monitoring
⋮----
def create_indexes(self) -> None
⋮----
"""Store intermediate writes linked to a checkpoint and clean up old writes.
        Args:
            config: Configuration of the related checkpoint.
            writes: List of writes to store, each as (channel, value) pair.
            task_id: Identifier for the task creating the writes.
            task_path: Optional path info for the task.
        """
⋮----
checkpoint_id = config["configurable"]["checkpoint_id"]
# Transform writes into appropriate format
writes_objects = []
⋮----
write_obj = {
⋮----
# First clean up old writes for this thread and namespace if they're for a different checkpoint_id
⋮----
# Get all writes keys for this thread/namespace
writes_key_pattern = (
existing_writes_keys = self._redis.keys(writes_key_pattern)
# Process each existing writes key to determine if it should be kept or deleted
⋮----
decoded_key = safely_decode(write_key)
⋮----
# The key format is checkpoint_write:thread_id:checkpoint_ns:checkpoint_id:task_id:idx
⋮----
key_checkpoint_id = key_parts[3]
# If the write is for a different checkpoint_id, delete it
⋮----
# For each write, check existence and then perform appropriate operation
⋮----
key = self._make_redis_checkpoint_writes_key(
# First check if key exists
key_exists = self._redis.exists(key) == 1
⋮----
# UPSERT case - only update specific fields
⋮----
# Update only channel, type, and blob fields
⋮----
# For new records, set the complete object
⋮----
# INSERT case
⋮----
"""Convert blob data for Redis storage.
        In the shallow implementation, we use the version in the key to allow
        storing multiple versions without conflicts and to facilitate cleanup.
        """
⋮----
# Use the base Redis checkpoint blob key to include version, enabling version tracking
⋮----
"version": ver,  # Include version in the data as well
⋮----
"""Retrieve channel_values dictionary with properly constructed message objects."""
checkpoint_query = FilterQuery(
checkpoint_result = self.checkpoints_index.search(checkpoint_query)
⋮----
channel_versions = json.loads(
⋮----
channel_values = {}
⋮----
blob_query = FilterQuery(
blob_results = self.checkpoint_blobs_index.search(blob_query)
⋮----
blob_doc = blob_results.docs[0]
blob_type = blob_doc.type
blob_data = getattr(blob_doc, "$.blob", None)
⋮----
"""Load pending sends for a parent checkpoint.
        Args:
            thread_id: The thread ID
            checkpoint_ns: The checkpoint namespace
            parent_checkpoint_id: The ID of the parent checkpoint
        Returns:
            List of (type, blob) tuples representing pending sends
        """
# Query checkpoint_writes for parent checkpoint's TASKS channel
parent_writes_query = FilterQuery(
parent_writes_results = self.checkpoint_writes_index.search(parent_writes_query)
# Sort results by task_path, task_id, idx (matching Postgres implementation)
sorted_writes = sorted(
# Extract type and blob pairs
⋮----
@staticmethod
    def _make_shallow_redis_checkpoint_key(thread_id: str, checkpoint_ns: str) -> str
⋮----
"""Create a key for shallow checkpoints using only thread_id and checkpoint_ns."""
⋮----
"""Create a key for a blob in a shallow checkpoint."""
⋮----
"""Create a pattern to match all blob keys for a thread and namespace."""
⋮----
"""Create a pattern to match all writes keys for a thread and namespace."""

================
File: langgraph/checkpoint/redis/types.py
================
RedisClientType = TypeVar(
IndexType = TypeVar("IndexType", bound=Union[SearchIndex, AsyncSearchIndex])
MetadataInput = Optional[dict[str, Any]]

================
File: langgraph/checkpoint/redis/util.py
================
"""
RediSearch versions below 2.10 don't support indexing and querying
empty strings, so we use a sentinel value to represent empty strings.
Because checkpoint queries are sorted by checkpoint_id, we use a UUID
that is lexicographically sortable. Typically, checkpoints that need
sentinel values are from the first run of the graph, so this should
generally be correct.
This module also includes utility functions for safely handling Redis responses,
including handling bytes vs string responses depending on how the Redis client is
configured with decode_responses.
"""
⋮----
EMPTY_STRING_SENTINEL = "__empty__"
EMPTY_ID_SENTINEL = "00000000-0000-0000-0000-000000000000"
def to_storage_safe_str(value: str) -> str
⋮----
"""
    Prepare a value for storage in Redis as a string.
    Convert an empty string to a sentinel value, otherwise return the
    value as a string.
    Args:
        value (str): The value to convert.
    Returns:
        str: The converted value.
    """
⋮----
def from_storage_safe_str(value: str) -> str
⋮----
"""
    Convert a value from a sentinel value to an empty string if present,
    otherwise return the value unchanged.
    Args:
        value (str): The value to convert.
    Returns:
        str: The converted value.
    """
⋮----
def to_storage_safe_id(value: str) -> str
⋮----
"""
    Prepare a value for storage in Redis as an ID.
    Convert an empty string to a sentinel value for empty ID strings, otherwise
    return the value as a string.
    Args:
        value (str): The value to convert.
    Returns:
        str: The converted value.
    """
⋮----
def from_storage_safe_id(value: str) -> str
⋮----
"""
    Convert a value from a sentinel value for empty ID strings to an empty
    ID string if present, otherwise return the value unchanged.
    Args:
        value (str): The value to convert.
    Returns:
        str: The converted value.
    """
⋮----
def safely_decode(obj: Any) -> Any
⋮----
"""
    Safely decode Redis responses, handling both string and bytes types.
    This is especially useful when working with Redis clients configured with
    different decode_responses settings. It recursively processes nested
    data structures (dicts, lists, tuples, sets).
    Based on RedisVL's convert_bytes function (redisvl.redis.utils.convert_bytes)
    but implemented directly to avoid runtime import issues and ensure consistent
    behavior with sets and other data structures. See PR #34 and referenced
    implementation: https://github.com/redis/redis-vl-python/blob/9f22a9ad4c2166af6462b007833b456448714dd9/redisvl/redis/utils.py#L20
    Args:
        obj: The object to decode. Can be a string, bytes, or a nested structure
            containing strings/bytes (dict, list, tuple, set).
    Returns:
        The decoded object with all bytes converted to strings.
    """
⋮----
# If decoding fails, return the original bytes
⋮----
# For other types (int, float, bool, etc.), return as is

================
File: langgraph/checkpoint/redis/version.py
================
__version__ = "0.0.4"
__lib_name__ = f"langgraph-checkpoint-redis_v{__version__}"
__full_lib_name__ = f"redis-py(redisvl_v{__redisvl_version__};{__lib_name__})"

================
File: langgraph/store/redis/__init__.py
================
"""Synchronous Redis store implementation."""
⋮----
_token_escaper = TokenEscaper()
_token_unescaper = TokenUnescaper()
logger = logging.getLogger(__name__)
def _convert_redis_score_to_similarity(score: float, distance_type: str) -> float
⋮----
"""Convert Redis vector distance to similarity score."""
⋮----
# Redis returns cosine distance (1 - cosine_similarity)
# Convert back to similarity
⋮----
# For L2, smaller distance means more similar
# Use a simple exponential decay
⋮----
# For inner product, Redis already returns what we want
⋮----
class RedisStore(BaseStore, BaseRedisStore[Redis, SearchIndex])
⋮----
"""Redis-backed store with optional vector search.
    Provides synchronous operations for storing and retrieving data with optional
    vector similarity search support.
    """
# Enable TTL support
supports_ttl = True
ttl_config: Optional[TTLConfig] = None
⋮----
# Detection will happen in setup()
⋮----
"""Create store from Redis connection string."""
client = None
⋮----
client = RedisConnectionFactory.get_redis_connection(conn_string)
store = cls(client, index=index, ttl=ttl)
# Client info will already be set in __init__, but we set it up here
# to make the method behavior consistent with AsyncRedisStore
⋮----
def setup(self) -> None
⋮----
"""Initialize store indices."""
# Detect if we're connected to a Redis cluster
⋮----
def batch(self, ops: Iterable[Op]) -> list[Result]
⋮----
"""Execute batch of operations."""
⋮----
results: list[Result] = [None] * num_ops
⋮----
def _detect_cluster_mode(self) -> None
⋮----
"""Detect if the Redis client is a cluster client by inspecting its class."""
# If we passed in_cluster_mode explicitly, respect it
⋮----
"""Execute list namespaces operations in batch."""
⋮----
# Construct base query for namespace search
base_query = "*"  # Start with all documents
⋮----
conditions = []
⋮----
prefix = _namespace_to_text(
⋮----
suffix = _namespace_to_text(
⋮----
base_query = " ".join(conditions)
# Execute search with return_fields=["prefix"] to get just namespaces
query = FilterQuery(filter_expression=base_query, return_fields=["prefix"])
res = self.store_index.search(query)
# Extract unique namespaces
namespaces = set()
⋮----
ns = tuple(_token_unescaper.unescape(doc.prefix).split("."))
# Apply max_depth if specified
⋮----
ns = ns[: op.max_depth]
⋮----
# Sort and apply pagination
sorted_namespaces = sorted(namespaces)
⋮----
offset = op.offset or 0
limit = op.limit or 10
sorted_namespaces = sorted_namespaces[offset : offset + limit]
⋮----
"""Execute GET operations in batch."""
refresh_keys_by_idx: dict[int, list[str]] = (
⋮----
)  # Track keys that need TTL refreshed by op index
⋮----
res = self.store_index.search(Query(query))
# Parse JSON from each document
key_to_row = {
⋮----
# Find the corresponding operation by looking it up in the operation list
# This is needed because idx is the index in the overall operation list
op_idx = None
⋮----
op_idx = i
⋮----
op = get_ops[op_idx][1]
⋮----
# Also add vector keys for the same document
doc_uuid = doc_id.split(":")[-1]
vector_key = (
⋮----
# Now refresh TTLs for any keys that need it
⋮----
# Get default TTL from config
ttl_minutes = None
⋮----
ttl_minutes = self.ttl_config.get("default_ttl")
⋮----
ttl_seconds = int(ttl_minutes * 60)
⋮----
ttl = self._redis.ttl(key)
⋮----
pipeline = self._redis.pipeline(transaction=True)
⋮----
# Only refresh TTL if the key exists and has a TTL
⋮----
if ttl > 0:  # Only refresh if key exists and has TTL
⋮----
"""Execute PUT operations in batch."""
⋮----
# First delete any existing documents that are being updated/deleted
⋮----
namespace = _namespace_to_text(op.namespace)
query = f"@prefix:{namespace} @key:{{{_token_escaper.escape(op.key)}}}"
results = self.store_index.search(query)
⋮----
vector_results = self.vector_index.search(query)
⋮----
# Now handle new document creation
doc_ids: dict[tuple[str, str], str] = {}
store_docs: list[RedisDocument] = []
store_keys: list[str] = []
ttl_tracking: dict[str, tuple[list[str], Optional[float]]] = (
⋮----
)  # Tracks keys that need TTL + their TTL values
# Generate IDs for PUT operations
⋮----
generated_doc_id = str(ULID())
⋮----
# Track TTL for this document if specified
⋮----
main_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{generated_doc_id}"
⋮----
# Load store docs with explicit keys
⋮----
store_key = (doc["prefix"], doc["key"])
doc_id = doc_ids[store_key]
# Remove TTL fields - they're not needed with Redis native TTL
⋮----
redis_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# Load individually if cluster
⋮----
# Handle vector embeddings with same IDs
⋮----
vectors = self.embeddings.embed_documents(
vector_docs: list[dict[str, Any]] = []
vector_keys: list[str] = []
# Check if we're using hash storage for vectors
vector_storage_type = "json"  # default
⋮----
index_dict = dict(self.index_config)
vector_storage_type = index_dict.get("vector_storage_type", "json")
⋮----
vector_key: tuple[str, str] = (ns, key)
doc_id = doc_ids[vector_key]
# Prepare vector based on storage type
⋮----
# For hash storage, convert vector to byte string
⋮----
vector_list = (
embedding_value = array_to_buffer(vector_list, "float32")
⋮----
# For JSON storage, keep as list
embedding_value = (
⋮----
redis_vector_key = f"{STORE_VECTOR_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# Add this vector key to the related keys list for TTL
main_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# Load individually if cluster
⋮----
# Now apply TTLs after all documents are loaded
⋮----
"""Execute search operations in batch."""
⋮----
# Handle vector search
query_vectors = {}
⋮----
query_vectors = dict(zip([idx for idx, _ in embedding_requests], vectors))
# Process each search operation
⋮----
# Vector similarity search
vector = query_vectors[idx]
vector_query = VectorQuery(
⋮----
num_results=limit,  # Use the user-specified limit
⋮----
vector_results = self.vector_index.query(vector_query)
# Get matching store docs
result_map = {}  # Map store key to vector result with distances
⋮----
store_docs = []
# Direct JSON GET for cluster mode
⋮----
doc_id = (
⋮----
doc_uuid = doc_id.split(":")[1]
store_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_uuid}"
⋮----
# Fetch individually in cluster mode
store_doc_item = self._redis.json().get(store_key)
⋮----
store_docs_raw = store_docs
⋮----
pipe = self._redis.pipeline(transaction=True)
⋮----
# Execute all lookups in one batch
store_docs_raw = pipe.execute()
# Process results maintaining order and applying filters
items = []
refresh_keys = []  # Track keys that need TTL refreshed
store_docs_iter = iter(store_docs_raw)
⋮----
store_doc = next(store_docs_iter, None)
⋮----
vector_result = result_map[store_key]
# Get vector_distance from original search result
dist = (
# Convert to similarity score
score = (1.0 - float(dist)) if dist is not None else 0.0
⋮----
store_doc = json.loads(
⋮----
store_doc  # type: ignore[arg-type]
)  # Attempt to parse if it's a JSON string
⋮----
continue  # Skip this problematic document
⋮----
):  # Check again after potential parsing
⋮----
# if still not a dict, this means it's a problematic entry
⋮----
# Apply value filters if needed
⋮----
matches = True
value = store_doc.get("value", {})
⋮----
actual = value.get(key)
⋮----
matches = False
⋮----
# If refresh_ttl is true, add to list for refreshing
⋮----
# Also find associated vector keys with same ID
doc_id = store_key.split(":")[-1]
⋮----
# Refresh TTL if requested
⋮----
# Get default TTL from config
⋮----
# Only refresh TTL if the key exists and has a TTL
⋮----
if ttl > 0:  # Only refresh if key exists and has TTL
⋮----
# Regular search
# Create a query with LIMIT and OFFSET parameters
query = Query(query_str).paging(offset, limit)
# Execute search with limit and offset applied by Redis
⋮----
data = json.loads(doc.json)
# Apply value filters
⋮----
value = data.get("value", {})
⋮----
# If refresh_ttl is true, add the key to refresh list
⋮----
# Also find associated vector keys with same ID
doc_id = doc.id.split(":")[-1]
⋮----
async def abatch(self, ops: Iterable[Op]) -> list[Result]
⋮----
"""Execute batch of operations asynchronously."""
⋮----
__all__ = ["AsyncRedisStore", "RedisStore"]

================
File: langgraph/store/redis/aio.py
================
_token_escaper = TokenEscaper()
_token_unescaper = TokenUnescaper()
⋮----
class AsyncRedisStore(
⋮----
"""Async Redis store with optional vector search."""
store_index: AsyncSearchIndex
vector_index: AsyncSearchIndex
_owns_its_client: bool
supports_ttl: bool = True
# Use a different name to avoid conflicting with the base class attribute
_async_ttl_stop_event: asyncio.Event | None = None
_ttl_sweeper_task: asyncio.Task | None = None
ttl_config: Optional[TTLConfig] = None
# Whether to assume the Redis server is a cluster; None triggers auto-detection
cluster_mode: Optional[bool] = None
⋮----
"""Initialize store with Redis connection and optional index config."""
⋮----
# Initialize base classes
⋮----
# Set up store configuration
⋮----
fields = (
⋮----
fields = [fields]
⋮----
# Configure client
⋮----
# Validate and store cluster_mode; None means auto-detect later
⋮----
# Create store index
⋮----
# Configure vector index if needed
⋮----
vector_schema = self.SCHEMAS[1].copy()
vector_fields = vector_schema.get("fields", [])
vector_field = None
⋮----
vector_field = f
⋮----
# Configure vector field with index config values
⋮----
"algorithm": "flat",  # Default to flat
⋮----
# Apply any additional vector type config
⋮----
"""Configure the Redis client."""
⋮----
# Use direct AsyncRedis.from_url to avoid the deprecated get_async_redis_connection
⋮----
redis_url = os.environ.get("REDIS_URL")
⋮----
async def setup(self) -> None
⋮----
"""Initialize store indices."""
# Handle embeddings in same way as sync store
⋮----
# Auto-detect cluster mode if not explicitly set
⋮----
# Create indices in Redis
⋮----
async def _detect_cluster_mode(self) -> None
⋮----
"""Detect if the Redis client is a cluster client by inspecting its class."""
# Determine cluster mode based on client class
⋮----
# This can't be properly typed due to covariance issues with async methods
⋮----
"""Apply Redis native TTL to keys asynchronously.
        Args:
            main_key: The primary Redis key
            related_keys: Additional Redis keys that should expire at the same time
            ttl_minutes: Time-to-live in minutes
        """
⋮----
# Check if there's a default TTL in config
⋮----
ttl_minutes = self.ttl_config.get("default_ttl")
⋮----
ttl_seconds = int(ttl_minutes * 60)
⋮----
pipeline = self._redis.pipeline(transaction=True)
# Set TTL for main key
⋮----
# Set TTL for related keys
if related_keys:  # Check if related_keys is not None
⋮----
async def sweep_ttl(self) -> int:  # type: ignore[override]
⋮----
"""Clean up any remaining expired items.
        This is not needed with Redis native TTL, but kept for API compatibility.
        Redis automatically removes expired keys.
        Returns:
            int: Always returns 0 as Redis handles expiration automatically
        """
⋮----
async def start_ttl_sweeper(  # type: ignore[override]
⋮----
"""Start TTL sweeper.
        This is a no-op with Redis native TTL, but kept for API compatibility.
        Redis automatically removes expired keys.
        Args:
            sweep_interval_minutes: Ignored parameter, kept for API compatibility
        """
# No-op: Redis handles TTL expiration automatically
⋮----
async def stop_ttl_sweeper(self, timeout: Optional[float] = None) -> bool:  # type: ignore[override]
⋮----
"""Stop TTL sweeper.
        This is a no-op with Redis native TTL, but kept for API compatibility.
        Args:
            timeout: Ignored parameter, kept for API compatibility
        Returns:
            bool: Always True as there's no sweeper to stop
        """
⋮----
"""Create store from Redis connection string."""
⋮----
# Set client information after setup
⋮----
def create_indexes(self) -> None
⋮----
"""Create async indices."""
⋮----
async def __aenter__(self) -> AsyncRedisStore
⋮----
"""Async context manager enter."""
# Client info was already set in __init__,
# but we'll set it again here to be consistent with checkpoint code
⋮----
"""Async context manager exit."""
# Cancel the background task created by AsyncBatchedBaseStore
⋮----
# Close Redis connections if we own them
⋮----
async def abatch(self, ops: Iterable[Op]) -> list[Result]
⋮----
"""Execute batch of operations asynchronously."""
⋮----
results: list[Result] = [None] * num_ops
tasks = []
⋮----
def batch(self: AsyncRedisStore, ops: Iterable[Op]) -> list[Result]
⋮----
"""Execute batch of operations synchronously.
        Args:
            ops: Operations to execute in batch
        Returns:
            Results from batch execution
        Raises:
            asyncio.InvalidStateError: If called from the main event loop
        """
⋮----
"""Execute GET operations in batch asynchronously."""
refresh_keys_by_idx: dict[int, list[str]] = (
⋮----
)  # Track keys that need TTL refreshed by op index
⋮----
res = await self.store_index.search(Query(query))
# Parse JSON from each document
key_to_row = {
⋮----
# Find the corresponding operation by looking it up in the operation list
# This is needed because idx is the index in the overall operation list
op_idx = None
⋮----
op_idx = i
⋮----
op = get_ops[op_idx][1]
⋮----
# Also add vector keys for the same document
doc_uuid = doc_id.split(":")[-1]
vector_key = (
⋮----
# Now refresh TTLs for any keys that need it
⋮----
# Get default TTL from config
ttl_minutes = None
⋮----
ttl = await self._redis.ttl(key)
⋮----
# In cluster mode, we must use transaction=False # Comment no longer relevant
pipeline = self._redis.pipeline(
⋮----
)  # Assuming non-cluster or single node for now
⋮----
# Only refresh TTL if the key exists and has a TTL
⋮----
if ttl > 0:  # Only refresh if key exists and has TTL
⋮----
"""Prepare queries - no Redis operations in async version."""
# Last-write wins
dedupped_ops: dict[tuple[tuple[str, ...], str], PutOp] = {}
⋮----
inserts: list[PutOp] = []
deletes: list[PutOp] = []
⋮----
operations: list[RedisDocument] = []
embedding_request = None
to_embed: list[tuple[str, str, str, str]] = []
⋮----
# Delete matching documents
⋮----
prefix = _namespace_to_text(op.namespace)
query = f"(@prefix:{prefix} @key:{{{op.key}}})"
results = await self.store_index.search(query)
⋮----
# Handle inserts
⋮----
now = int(datetime.now(timezone.utc).timestamp() * 1_000_000)
# Handle TTL
⋮----
expires_at = None
⋮----
ttl_minutes = op.ttl
expires_at = int(
doc = RedisDocument(
⋮----
paths = (
⋮----
texts = get_text_at_path(op.value, tokenized_path)
⋮----
embedding_request = ("", to_embed)
⋮----
"""Execute PUT operations in batch asynchronously."""
⋮----
# First delete any existing documents that are being updated/deleted
⋮----
namespace = _namespace_to_text(op.namespace)
query = f"@prefix:{namespace} @key:{{{_token_escaper.escape(op.key)}}}"
⋮----
vector_results = await self.vector_index.search(query)
⋮----
):  # Check if pipeline has commands before executing
⋮----
# Now handle new document creation
doc_ids: dict[tuple[str, str], str] = {}
store_docs: list[RedisDocument] = []
store_keys: list[str] = []
ttl_tracking: dict[str, tuple[list[str], Optional[float]]] = (
⋮----
)  # Tracks keys that need TTL + their TTL values
# Generate IDs for PUT operations
⋮----
generated_doc_id = str(ULID())
⋮----
# Track TTL for this document if specified
⋮----
main_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{generated_doc_id}"
⋮----
# Load store docs with explicit keys
⋮----
store_key = (doc["prefix"], doc["key"])
doc_id = doc_ids[store_key]
# Remove TTL fields - they're not needed with Redis native TTL
⋮----
redis_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# For cluster mode, load documents individually if SearchIndex.load isn't cluster-safe for batching.
# This is a conservative approach. If redisvl's load is cluster-safe, this can be optimized.
⋮----
# Handle vector embeddings with same IDs
⋮----
vectors = await self.embeddings.aembed_documents(
vector_docs: list[dict[str, Any]] = []
vector_keys: list[str] = []
⋮----
vector_key: tuple[str, str] = (ns, key)
doc_id = doc_ids[vector_key]
⋮----
redis_vector_key = f"{STORE_VECTOR_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# Add this vector key to the related keys list for TTL
main_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
⋮----
# Similar to store_docs, load vector docs individually in cluster mode as a precaution.
⋮----
# Now apply TTLs after all documents are loaded
⋮----
"""Execute search operations in batch asynchronously."""
⋮----
# Handle vector search
query_vectors = {}
⋮----
query_vectors = dict(zip([idx for idx, _ in embedding_requests], vectors))
# Process each search operation
⋮----
# Vector similarity search
vector = query_vectors[idx]
vector_query = VectorQuery(
⋮----
num_results=limit,  # Use the user-specified limit
⋮----
vector_results_docs = await self.vector_index.query(vector_query)
# Get matching store docs
result_map = {}
⋮----
store_docs = []
⋮----
doc_id = (
⋮----
doc_uuid = doc_id.split(":")[1]
store_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_uuid}"
⋮----
# Fetch individually in cluster mode
store_doc_item = await self._redis.json().get(store_key)  # type: ignore
⋮----
store_docs_raw = store_docs
⋮----
pipeline = self._redis.pipeline(transaction=False)
⋮----
):  # doc_vr is now an individual doc from the list
⋮----
store_docs_raw = await pipeline.execute()
# Process results maintaining order and applying filters
items = []
refresh_keys = []  # Track keys that need TTL refreshed
store_docs_iter = iter(store_docs_raw)
⋮----
store_doc = next(store_docs_iter, None)
⋮----
vector_result = result_map[store_key]
# Get vector_distance from original search result
dist = (
# Convert to similarity score
score = (1.0 - float(dist)) if dist is not None else 0.0
# Ensure store_doc is a dictionary before trying to assign to it
⋮----
store_doc = json.loads(
⋮----
)  # Attempt to parse if it's a JSON string
⋮----
continue  # Skip this problematic document
⋮----
):  # Check again after potential parsing
⋮----
# if still not a dict, this means it's a problematic entry
⋮----
# Apply value filters if needed
⋮----
matches = True
value = store_doc.get("value", {})
⋮----
actual = value.get(key)
⋮----
matches = False
⋮----
# If refresh_ttl is true, add to list for refreshing
⋮----
# Also find associated vector keys with same ID
doc_id = store_key.split(":")[-1]
⋮----
# Refresh TTL if requested
⋮----
# Get default TTL from config
⋮----
# Only refresh TTL if the key exists and has a TTL
⋮----
if ttl > 0:  # Only refresh if key exists and has TTL
⋮----
# Regular search
# Create a query with LIMIT and OFFSET parameters
query = Query(query_str).paging(offset, limit)
# Execute search with limit and offset applied by Redis
res = await self.store_index.search(query)
⋮----
data = json.loads(doc.json)
# Apply value filters
⋮----
value = data.get("value", {})
⋮----
# If refresh_ttl is true, add the key to refresh list
⋮----
# Also find associated vector keys with same ID
doc_id = doc.id.split(":")[-1]
⋮----
"""Execute list namespaces operations in batch."""
⋮----
# Construct base query for namespace search
base_query = "*"  # Start with all documents
⋮----
conditions = []
⋮----
prefix = _namespace_to_text(
⋮----
suffix = _namespace_to_text(
⋮----
base_query = " ".join(conditions)
# Execute search with return_fields=["prefix"] to get just namespaces
query = FilterQuery(filter_expression=base_query, return_fields=["prefix"])
⋮----
# Extract unique namespaces
namespaces = set()
⋮----
ns = tuple(_token_unescaper.unescape(doc.prefix).split("."))
# Apply max_depth if specified
⋮----
ns = ns[: op.max_depth]
⋮----
# Sort and apply pagination
sorted_namespaces = sorted(namespaces)
⋮----
offset = op.offset or 0
limit = op.limit or 10
sorted_namespaces = sorted_namespaces[offset : offset + limit]
⋮----
# We don't need _run_background_tasks anymore as AsyncBatchedBaseStore provides this

================
File: langgraph/store/redis/base.py
================
"""Base implementation for Redis-backed store with optional vector search capabilities."""
⋮----
_token_escaper = TokenEscaper()
_token_unescaper = TokenUnescaper()
logger = logging.getLogger(__name__)
REDIS_KEY_SEPARATOR = ":"
STORE_PREFIX = "store"
STORE_VECTOR_PREFIX = "store_vectors"
# Schemas for Redis Search indices
SCHEMAS = [
def _ensure_string_or_literal(value: Any) -> str
⋮----
"""Convert value to string safely."""
⋮----
C = TypeVar("C", bound=Union[Redis, AsyncRedis])
class RedisDocument(TypedDict, total=False)
⋮----
prefix: str
key: str
value: Optional[str]
created_at: int
updated_at: int
ttl_minutes: Optional[float]
expires_at: Optional[int]
class BaseRedisStore(Generic[RedisClientType, IndexType])
⋮----
"""Base Redis implementation for persistent key-value store with optional vector search."""
_redis: RedisClientType
store_index: IndexType
vector_index: IndexType
_ttl_sweeper_thread: Optional[threading.Thread] = None
_ttl_stop_event: threading.Event | None = None
# Whether to operate in Redis cluster mode; None triggers auto-detection
cluster_mode: Optional[bool] = None
SCHEMAS = SCHEMAS
supports_ttl: bool = True
ttl_config: Optional[TTLConfig] = None
⋮----
"""Apply Redis native TTL to keys.
        Args:
            main_key: The primary Redis key
            related_keys: Additional Redis keys that should expire at the same time
            ttl_minutes: Time-to-live in minutes
        """
⋮----
# Check if there's a default TTL in config
⋮----
ttl_minutes = self.ttl_config.get("default_ttl")
⋮----
ttl_seconds = int(ttl_minutes * 60)
# Use the cluster_mode attribute to determine the approach
⋮----
# Cluster path: direct expire calls
⋮----
# Non-cluster path: transactional pipeline
pipeline = self._redis.pipeline(transaction=True)
⋮----
def sweep_ttl(self) -> int
⋮----
"""Clean up any remaining expired items.
        This is not needed with Redis native TTL, but kept for API compatibility.
        Redis automatically removes expired keys.
        Returns:
            int: Always returns 0 as Redis handles expiration automatically
        """
⋮----
def start_ttl_sweeper(self, sweep_interval_minutes: Optional[int] = None) -> None
⋮----
"""Start TTL sweeper.
        This is a no-op with Redis native TTL, but kept for API compatibility.
        Redis automatically removes expired keys.
        Args:
            sweep_interval_minutes: Ignored parameter, kept for API compatibility
        """
# No-op: Redis handles TTL expiration automatically
⋮----
def stop_ttl_sweeper(self, timeout: Optional[float] = None) -> bool
⋮----
"""Stop TTL sweeper.
        This is a no-op with Redis native TTL, but kept for API compatibility.
        Args:
            timeout: Ignored parameter, kept for API compatibility
        Returns:
            bool: Always True as there's no sweeper to stop
        """
⋮----
ttl: Optional[TTLConfig] = None,  # Corrected type hint for ttl
⋮----
"""Initialize store with Redis connection and optional index config."""
⋮----
# Store cluster_mode; None means auto-detect in RedisStore or AsyncRedisStore
⋮----
fields = self.index_config.get("fields", ["$"]) or []
⋮----
fields = [fields]
⋮----
# Initialize search indices
⋮----
# Configure vector index if needed
⋮----
# Get storage type from index config, default to "json" for backward compatibility
# Cast to dict to safely access potential extra fields
index_dict = dict(self.index_config)
vector_storage_type = index_dict.get("vector_storage_type", "json")
vector_schema: Dict[str, Any] = copy.deepcopy(self.SCHEMAS[1])
# Update storage type in schema
⋮----
vector_fields = vector_schema.get("fields", [])
vector_field = None
⋮----
vector_field = f
⋮----
# Configure vector field with index config values
⋮----
"algorithm": "flat",  # Default to flat
⋮----
# Map distance metrics to Redis-accepted literals
⋮----
# Apply any additional vector type config
⋮----
# Set client information in Redis
⋮----
def set_client_info(self) -> None
⋮----
"""Set client info for Redis monitoring."""
⋮----
# Create the client info string with only the redisvl version
client_info = f"redis-py(redisvl_v{__redisvl_version__})"
⋮----
# Try to use client_setinfo command if available
⋮----
# Fall back to a simple echo if client_setinfo is not available
⋮----
# Silently fail if even echo doesn't work
⋮----
async def aset_client_info(self) -> None
⋮----
"""Set client info for Redis monitoring asynchronously."""
⋮----
# Call with await to ensure it's an async call
echo_result = self._redis.echo(client_info)
⋮----
"""Convert GET operations into Redis queries."""
namespace_groups = defaultdict(list)
⋮----
results: list[tuple[str, Sequence, tuple[str, ...], list]] = []
⋮----
# Use Tag helper to properly escape all special characters
prefix_filter = Text("prefix") == _namespace_to_text(namespace)
filter_str = f"({prefix_filter} "
⋮----
key_filter = Tag("key") == list(keys)
⋮----
# Last-write wins
dedupped_ops: dict[tuple[tuple[str, ...], str], PutOp] = {}
⋮----
inserts: list[PutOp] = []
deletes: list[PutOp] = []
⋮----
operations: list[RedisDocument] = []
embedding_request = None
to_embed: list[tuple[str, str, str, str]] = []
⋮----
# Delete matching documents
⋮----
prefix = _namespace_to_text(op.namespace)
query = f"(@prefix:{prefix} @key:{{{op.key}}})"
results = self.store_index.search(query)
⋮----
# Handle inserts
⋮----
now = int(datetime.now(timezone.utc).timestamp() * 1_000_000)
# With native Redis TTL, we don't need to store TTL in document
# but store it for backward compatibility and metadata purposes
ttl_minutes = None
expires_at = None
⋮----
ttl_minutes = op.ttl
# Calculate expiration but don't rely on it for actual expiration
# as we'll use Redis native TTL
expires_at = int(
doc = RedisDocument(
⋮----
paths = (
⋮----
texts = get_text_at_path(op.value, tokenized_path)
⋮----
embedding_request = ("", to_embed)
⋮----
"""Convert search operations into Redis queries."""
queries = []
embedding_requests = []
⋮----
filter_conditions = []
⋮----
prefix = _namespace_to_text(op.namespace_prefix)
⋮----
query = " ".join(filter_conditions) if filter_conditions else "*"
limit = op.limit if op.limit is not None else 10
offset = op.offset if op.offset is not None else 0
params = [limit, offset]
⋮----
"""Convert list namespaces operations into Redis queries."""
⋮----
conditions = []
⋮----
path = _namespace_to_text(condition.path, handle_wildcards=True)
⋮----
query = " ".join(conditions) if conditions else "*"
params = [op.limit, op.offset] if op.limit or op.offset else []
⋮----
def _get_filter_condition(self, key: str, op: str, value: Any) -> str
⋮----
"""Get Redis search filter condition for an operator."""
⋮----
"""Compute cosine similarity between vectors."""
# Note: For production use, consider importing numpy for better performance
similarities = []
⋮----
dot_product = sum(a * b for a, b in zip(vec1, vec2))
norm1 = (sum(x * x for x in vec1)) ** 0.5
norm2 = (sum(x * x for x in vec2)) ** 0.5
⋮----
"""Convert namespace tuple to text string with proper escaping.
    Args:
        namespace: Tuple of strings representing namespace components
        handle_wildcards: Whether to handle wildcard characters specially
    Returns:
        Properly escaped string representation of namespace
    """
⋮----
namespace = tuple("%" if val == "*" else val for val in namespace)
# First join with dots
ns_text = _token_escaper.escape(".".join(namespace))
⋮----
def _decode_ns(ns: str) -> tuple[str, ...]
⋮----
"""Convert a dotted namespace string back into a tuple."""
⋮----
def _row_to_item(namespace: tuple[str, ...], row: dict[str, Any]) -> Item
⋮----
"""Convert a row from Redis to an Item."""
⋮----
"""Convert a row from Redis to a SearchItem."""
⋮----
def _group_ops(ops: Iterable[Op]) -> tuple[dict[type, list[tuple[int, Op]]], int]
⋮----
"""Group operations by type for batch processing."""
grouped_ops: dict[type, list[tuple[int, Op]]] = defaultdict(list)
tot = 0

================
File: langgraph/store/redis/token_unescaper.py
================
class TokenUnescaper
⋮----
"""Unescape previously escaped punctuation within an input string.
    Handles unescaping of RediSearch escaped characters. Should be used to unescape
    strings that were previously escaped using TokenEscaper.
    """
# Pattern to match escaped characters (backslash followed by any character)
DEFAULT_UNESCAPED_PATTERN = r"\\(.)"
def __init__(self, unescape_pattern: Optional[Pattern] = None)
def unescape(self, value: str) -> str
⋮----
"""Unescape a RedisSearch escaped string.
        Args:
            value: The string to unescape
        Returns:
            The unescaped string with backslash escapes removed
        Raises:
            TypeError: If input value is not a string
        """
⋮----
def unescape_symbol(match: Match[str]) -> str
⋮----
# Return just the character after the backslash

================
File: langgraph/store/redis/types.py
================
RedisClientType = TypeVar("RedisClientType", bound=Union[Redis, AsyncRedis])
IndexType = TypeVar("IndexType", bound=Union[SearchIndex, AsyncSearchIndex])

================
File: tests/conftest.py
================
VECTOR_TYPES = ["vector", "halfvec"]
⋮----
@pytest.fixture(autouse=True)
def set_tokenizers_parallelism()
⋮----
"""Disable tokenizers parallelism in tests to avoid deadlocks"""
⋮----
@pytest.fixture(scope="session", autouse=True)
def redis_container(request)
⋮----
"""
    If using xdist, create a unique Compose project for each xdist worker by
    setting COMPOSE_PROJECT_NAME. That prevents collisions on container/volume
    names.
    """
# In xdist, the config has "workerid" in workerinput
workerinput = getattr(request.config, "workerinput", {})
worker_id = workerinput.get("workerid", "master")
# Set the Compose project name so containers do not clash across workers
⋮----
compose = DockerCompose(
⋮----
# Ignore compose startup errors (e.g., existing containers)
⋮----
# Ignore compose stop errors
⋮----
@pytest.fixture(scope="session")
def redis_url(redis_container)
⋮----
"""
    Use the `DockerCompose` fixture to get host/port of the 'redis' service
    on container port 6379 (mapped to an ephemeral port on the host).
    """
⋮----
# Wait up to 15 seconds for the container to accept TCP connections.
deadline = time.time() + 15
⋮----
break  # Redis is accepting connections
⋮----
@pytest.fixture
async def async_client(redis_url)
⋮----
"""
    An async Redis client that uses the dynamic `redis_url`.
    """
⋮----
@pytest.fixture
def client(redis_url)
⋮----
"""
    A sync Redis client that uses the dynamic `redis_url`.
    """
conn = RedisConnectionFactory.get_redis_connection(redis_url)
⋮----
@pytest.fixture(autouse=True)
async def clear_redis(redis_url: str) -> None
⋮----
"""Clear Redis before each test."""
# Add a small delay to allow container to stabilize between tests
⋮----
client = Redis.from_url(redis_url)
⋮----
# Ignore clear_redis errors when Redis container is unavailable
⋮----
def pytest_addoption(parser: pytest.Parser) -> None
def pytest_configure(config: pytest.Config) -> None
⋮----
skip_api = pytest.mark.skip(

================
File: tests/docker-compose.yml
================
version: "3.9"
services:
  redis:
    image: "${REDIS_IMAGE}"
    ports:
      - target: 6379
        published: 0
        protocol: tcp
        mode: host
    environment:
      - "REDIS_ARGS=--save '' --appendonly no"
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

================
File: tests/embed_test_utils.py
================
"""Embedding utilities for testing."""
⋮----
class CharacterEmbeddings(Embeddings)
⋮----
"""Simple character-frequency based embeddings using random projections."""
def __init__(self, dims: int = 50, seed: int = 42)
⋮----
"""Initialize with embedding dimensions and random seed."""
⋮----
# Create projection vector for each character lazily
⋮----
def _embed_one(self, text: str) -> list[float]
⋮----
"""Embed a single text."""
counts = Counter(text)
total = sum(counts.values())
⋮----
embedding = [0.0] * self.dims
⋮----
weight = count / total
char_proj = self._char_projections[char]
⋮----
norm = math.sqrt(sum(x * x for x in embedding))
⋮----
embedding = [x / norm for x in embedding]
⋮----
def embed_documents(self, texts: list[str]) -> list[list[float]]
⋮----
"""Embed a list of documents."""
⋮----
def embed_query(self, text: str) -> list[float]
⋮----
"""Embed a query string."""
⋮----
def __eq__(self, other: Any) -> bool
class AsyncCharacterEmbeddings(CharacterEmbeddings)
⋮----
"""Character-based embeddings with both sync and async methods for testing."""
async def aembed_documents(self, texts: list[str]) -> list[list[float]]
⋮----
"""Async version of embed_documents."""
⋮----
async def aembed_query(self, text: str) -> list[float]
⋮----
"""Async version of embed_query."""

================
File: tests/test_async_aget_tuple_checkpoint_id.py
================
"""Test for AsyncRedisSaver aget_tuple checkpoint_id issue (GitHub issue #64)."""
⋮----
@pytest.fixture
async def saver(redis_url: str) -> AsyncGenerator[AsyncRedisSaver, None]
⋮----
"""Async saver fixture for this test."""
saver = AsyncRedisSaver(redis_url)
⋮----
@pytest.mark.asyncio
async def test_aget_tuple_returns_correct_checkpoint_id(saver: AsyncRedisSaver)
⋮----
"""Test that aget_tuple returns the correct checkpoint_id when not specified in config.
    This test reproduces the issue described in GitHub issue #64 where AsyncRedisSaver
    aget_tuple was returning None for checkpoint_id while the sync version worked correctly.
    """
# Create a unique thread ID
thread_id = str(uuid.uuid4())
# Config with only thread_id and checkpoint_ns (no checkpoint_id)
runnable_config: RunnableConfig = {
# Put several checkpoints
checkpoint_ids = []
⋮----
checkpoint_id = str(run)
⋮----
# Get the tuple using the config without checkpoint_id
# This should return the latest checkpoint
get_tuple = await saver.aget_tuple(runnable_config)
# Verify the checkpoint_id is not None and matches the expected value
⋮----
returned_checkpoint_id = get_tuple.config["configurable"]["checkpoint_id"]
⋮----
# Since we're getting the latest checkpoint each time, it should be the current checkpoint_id
⋮----
@pytest.mark.asyncio
async def test_aget_tuple_with_explicit_checkpoint_id(saver: AsyncRedisSaver)
⋮----
"""Test that aget_tuple works correctly when checkpoint_id is explicitly provided."""
⋮----
# Test retrieving each checkpoint by explicit checkpoint_id
⋮----
config_with_id: RunnableConfig = {
get_tuple = await saver.aget_tuple(config_with_id)
⋮----
@pytest.mark.asyncio
async def test_aget_tuple_no_checkpoint_returns_none(saver: AsyncRedisSaver)
⋮----
"""Test that aget_tuple returns None when no checkpoint exists for the thread."""
# Use a thread ID that doesn't exist

================
File: tests/test_async_cluster_mode.py
================
"""Tests for Redis Cluster mode functionality with AsyncRedisStore."""
⋮----
RedisCluster as AsyncRedisCluster,  # Import actual for isinstance checks if needed by store
⋮----
# Override session-scoped redis_container fixture to prevent Docker operations and provide dummy host/port
class DummyCompose
⋮----
def get_service_host_and_port(self, service, port)
⋮----
# Return localhost and specified port for dummy usage
⋮----
@pytest.fixture(scope="session", autouse=True)
def redis_container()
⋮----
"""Override redis_container to use DummyCompose instead of real DockerCompose."""
⋮----
# Basic Mock for non-cluster async client
class AsyncMockRedis(AsyncRedis)
⋮----
def __init__(self, *args, **kwargs)
⋮----
# Add other attributes/methods to track if needed
def pipeline(self, transaction=True)
⋮----
# print(f"AsyncMockRedis.pipeline called with transaction={transaction}")
⋮----
mock_pipeline = AsyncMock()  # Use AsyncMock for awaitable methods
⋮----
# Mock json().get() behavior within pipeline
mock_json_pipeline = AsyncMock()
⋮----
async def expire(self, key, ttl)
⋮----
# print(f"AsyncMockRedis.expire called with key={key}, ttl={ttl}")
⋮----
async def delete(self, key)
async def ttl(self, key)
⋮----
return 3600  # Default TTL
def json(self)
⋮----
mock_json = AsyncMock()
⋮----
# Mock cluster method to simulate a non-cluster client
async def cluster(self, command, *args, **kwargs)
# Mock for cluster async client
class AsyncMockRedisCluster(
⋮----
):  # Inherit from real to pass isinstance checks in store
⋮----
# super().__init__ might be tricky to call if it requires actual cluster setup
# For mocking purposes, we often bypass the real __init__ or simplify it.
# If AsyncRedisCluster.__init__ is simple enough or can be called with None/mock args:
# try:
#     super().__init__(startup_nodes=None) # Example, adjust as needed
# except: # pylint: disable=bare-except
#     pass # Fallback if super().__init__ is problematic
⋮----
# Add required cluster attributes to prevent AttributeError
⋮----
# Mock the client_setinfo method that's called during setup
async def client_setinfo(self, *args, **kwargs)
# Mock execute_command to avoid cluster-specific execution
async def execute_command(self, *args, **kwargs)
⋮----
command = args[0] if args else ""
⋮----
# Add other command responses as needed
⋮----
# Mock module_list method for Redis modules check
async def module_list(self)
⋮----
# Return mock modules that satisfy the validation requirements
⋮----
# Mock pipeline to record calls and simulate async behavior
⋮----
# print(f"AsyncMockRedisCluster.pipeline called with transaction={transaction}")
⋮----
mock_pipeline = MagicMock()
⋮----
mock_json_pipeline = MagicMock()
⋮----
# print(f"AsyncMockRedisCluster.expire called with key={key}, ttl={ttl}")
⋮----
# Mock cluster method to simulate a cluster client
⋮----
@pytest.fixture
async def mock_async_redis_cluster_client(redis_url)
⋮----
# This fixture provides a mock that IS an instance of AsyncRedisCluster
# but with mocked methods for testing.
# For simplicity, we're not trying to fully initialize a real AsyncRedisCluster connection.
mock_client = AsyncMockRedisCluster(
⋮----
)  # host arg may be needed by parent
# If AsyncRedisStore relies on specific attributes from the client, mock them here:
# mock_client.connection_pool = AsyncMock()
⋮----
@pytest.fixture
async def mock_async_redis_client(redis_url)
⋮----
# This provides a mock non-cluster client
return AsyncMockRedis.from_url(redis_url)  # Standard way to get an async client
⋮----
"""Test that AsyncRedisStore behavior differs for cluster vs. non-cluster clients."""
async_cluster_store = AsyncRedisStore(redis_client=mock_async_redis_cluster_client)
mock_index_cluster = AsyncMock()
⋮----
mock_index_cluster.query = AsyncMock(return_value=[])  # For vector search mocks
mock_index_cluster.create = AsyncMock(return_value=None)  # For setup
⋮----
await async_cluster_store.setup()  # Call setup to initialize indices
⋮----
# --- Test with AsyncMockRedis (simulates non-cluster) ---
async_non_cluster_store = AsyncRedisStore(redis_client=mock_async_redis_client)
# Mock indices for async_non_cluster_store
mock_index_non_cluster = AsyncMock()
⋮----
@pytest.fixture(params=[False, True])
async def async_checkpoint_saver(request)
⋮----
"""Parameterized fixture for AsyncRedisSaver with regular or cluster client."""
is_cluster = request.param
client = AsyncMockRedisCluster() if is_cluster else AsyncMockRedis()
saver = AsyncRedisSaver(redis_client=client)
# Mock the search indices
⋮----
# Skip asetup() to avoid complex RedisVL index creation, just test cluster detection
⋮----
@pytest.mark.asyncio
async def test_async_checkpoint_saver_cluster_detection(async_checkpoint_saver)
⋮----
"""Test that async checkpoint saver cluster_mode is set correctly."""
is_client_cluster = isinstance(async_checkpoint_saver._redis, AsyncRedisCluster)
⋮----
@pytest.mark.asyncio
async def test_async_checkpoint_saver_aput_ttl_behavior(async_checkpoint_saver)
⋮----
"""Test TTL behavior in aput for async checkpoint saver in cluster vs. non-cluster mode."""
⋮----
client = async_checkpoint_saver._redis
⋮----
# Set up TTL config
⋮----
# Mock the JSON operations to avoid actual data operations
⋮----
# Create mock checkpoint and metadata
config = {
checkpoint: Checkpoint = {"channel_values": {}, "version": "1.0"}
metadata: CheckpointMetadata = {"source": "test", "step": 1}
new_versions = {}
# Call aput which should trigger TTL operations
⋮----
# In cluster mode, TTL operations should be called directly
assert len(client.expire_calls) >= 1  # At least one TTL call for the checkpoint
# Check that expire was called with correct TTL (5 minutes = 300 seconds)
ttl_calls = [call for call in client.expire_calls if call.get("ttl") == 300]
⋮----
# In non-cluster mode, pipeline should be used for TTL operations
⋮----
# Should have pipeline calls for the main operations and potentially TTL operations
⋮----
@pytest.mark.asyncio
async def test_async_checkpoint_saver_delete_thread_behavior(async_checkpoint_saver)
⋮----
"""Test delete_thread behavior for async checkpoint saver in cluster vs. non-cluster mode."""
⋮----
# Mock search results to simulate existing data
mock_checkpoint_doc = MagicMock()
⋮----
mock_blob_doc = MagicMock()
⋮----
mock_write_doc = MagicMock()
⋮----
# In cluster mode, delete operations should be called directly
assert len(client.delete_calls) > 0  # At least one checkpoint key deletion
# Pipeline should not be used for deletions in cluster mode
# (it might be called for other reasons but not for delete operations)
⋮----
# In non-cluster mode, pipeline should be used for deletions
assert len(client.pipeline_calls) > 0  # At least one pipeline used
# Direct delete calls should not be made in non-cluster mode

================
File: tests/test_async_search_limit.py
================
"""Tests for AsyncRedisStore search limits."""
⋮----
@pytest_asyncio.fixture(scope="function")
async def async_store(redis_url) -> AsyncRedisStore
⋮----
"""Fixture to create an AsyncRedisStore."""
⋮----
await store.setup()  # Initialize indices
⋮----
@pytest.mark.asyncio
async def test_async_search_with_larger_limit(async_store: AsyncRedisStore) -> None
⋮----
"""Test async search with limit > 10."""
# Create 15 test documents
⋮----
# Search with a limit of 15
results = await async_store.asearch(("test_namespace",), limit=15)
# Should return all 15 results
⋮----
# Verify we have all the items
result_keys = {item.key for item in results}
expected_keys = {f"key{i}" for i in range(15)}
⋮----
@pytest.mark.asyncio
async def test_async_vector_search_with_larger_limit(redis_url) -> None
⋮----
"""Test async vector search with limit > 10."""
⋮----
# Create vector store with embeddings
embeddings = CharacterEmbeddings(dims=4)
index_config = {
⋮----
# Create 15 test documents
⋮----
# Create documents with slightly different texts
⋮----
# Search with a limit of 15
results = await store.asearch(("test_namespace",), query="sample", limit=15)
# Should return all 15 results
⋮----
# Verify we have all the items

================
File: tests/test_async_store.py
================
"""Tests for AsyncRedisStore."""
⋮----
TTL_SECONDS = 2
TTL_MINUTES = TTL_SECONDS / 60
⋮----
@pytest.fixture
def fake_embeddings() -> CharacterEmbeddings
⋮----
"""Create test embeddings for vector search."""
⋮----
@pytest.fixture(scope="function")
async def store(redis_url) -> AsyncIterator[AsyncRedisStore]
⋮----
"""Create an async Redis store with TTL enabled."""
ttl_config = {
⋮----
await store.setup()  # Initialize indices
⋮----
"""Create an async Redis store with vector search capabilities."""
vector_type = request.param
distance_type = "cosine"
# Include fields parameter in index_config
index_config = {
⋮----
"fields": ["text"],  # Field to embed
⋮----
ttl_config = {"default_ttl": 2, "refresh_on_read": True}
# Create a unique index name for each test run
unique_id = str(uuid4())[:8]
# Use different Redis prefix for vector store tests to avoid conflicts
⋮----
@pytest.mark.asyncio
async def test_basic_ops(store: AsyncRedisStore) -> None
⋮----
"""Test basic CRUD operations with async store."""
namespace = ("test", "documents")
item_id = "doc1"
item_value = {"title": "Test Document", "content": "Hello, World!"}
⋮----
item = await store.aget(namespace, item_id)
⋮----
# Test update
updated_value = {"title": "Updated Document", "content": "Hello, Updated!"}
⋮----
updated_item = await store.aget(namespace, item_id)
⋮----
# Test non-existent namespace
different_namespace = ("test", "other_documents")
item_in_different_namespace = await store.aget(different_namespace, item_id)
⋮----
# Test delete
⋮----
deleted_item = await store.aget(namespace, item_id)
⋮----
@pytest.mark.asyncio
async def test_search(store: AsyncRedisStore) -> None
⋮----
"""Test search functionality with async store."""
# Create test data
test_data = [
⋮----
# Test basic search
all_items = await store.asearch(["test"])
⋮----
# Test namespace filtering
docs_items = await store.asearch(["test", "docs"])
⋮----
# Test value filtering
alice_items = await store.asearch(["test"], filter={"author": "Alice"})
⋮----
# Test pagination
paginated_items = await store.asearch(["test"], limit=2)
⋮----
offset_items = await store.asearch(["test"], offset=2)
⋮----
# Cleanup
⋮----
@pytest.mark.asyncio
async def test_batch_put_ops(store: AsyncRedisStore) -> None
⋮----
"""Test batch put operations with async store."""
ops = [
⋮----
PutOp(namespace=("test",), key="key3", value=None),  # Delete operation
⋮----
results = await store.abatch(ops)
⋮----
# Verify the puts worked
item1 = await store.aget(("test",), "key1")
item2 = await store.aget(("test",), "key2")
item3 = await store.aget(("test",), "key3")
⋮----
@pytest.mark.asyncio
async def test_batch_search_ops(store: AsyncRedisStore) -> None
⋮----
"""Test batch search operations with async store."""
# Setup test data
⋮----
# First search should find items with tag "a"
⋮----
# Second search should return first 2 items
⋮----
# Third search should only find items in test/foo namespace
⋮----
@pytest.mark.asyncio
async def test_batch_list_namespaces_ops(store: AsyncRedisStore) -> None
⋮----
"""Test batch list namespaces operations with async store."""
# Setup test data with various namespaces
⋮----
# First operation should list all namespaces
⋮----
# Second operation should only return namespaces up to depth 2
⋮----
@pytest.mark.asyncio
async def test_list_namespaces(store: AsyncRedisStore) -> None
⋮----
"""Test listing namespaces with async store."""
# Create test data with various namespaces
test_namespaces = [
# Insert test data
⋮----
# Test listing with various filters
all_namespaces = await store.alist_namespaces()
⋮----
# Test prefix filtering
test_prefix_namespaces = await store.alist_namespaces(prefix=["test"])
⋮----
# Test suffix filtering
public_namespaces = await store.alist_namespaces(suffix=["public"])
⋮----
# Test max depth
depth_2_namespaces = await store.alist_namespaces(max_depth=2)
⋮----
paginated_namespaces = await store.alist_namespaces(limit=3)
⋮----
@pytest.mark.asyncio
async def test_batch_order(store: AsyncRedisStore) -> None
⋮----
"""Test batch operations with async store.
    This test focuses on verifying that multiple operations can be executed
    successfully in a batch, rather than testing strict sequential ordering.
    """
namespace = ("test", "batch")
# First, put multiple items in a batch
put_ops = [
# Execute the batch of puts
put_results = await store.abatch(put_ops)
⋮----
# Then get multiple items in a batch
get_ops = [GetOp(namespace=namespace, key=f"key{i}") for i in range(5)]
# Execute the batch of gets
get_results = await store.abatch(get_ops)
⋮----
# Verify all items were retrieved correctly
⋮----
# Create additional items individually
namespace2 = ("test", "batch_mixed")
⋮----
# Now search for items in a separate operation
fruit_items = await store.asearch(namespace2, filter={"category": "fruit"})
⋮----
# Cleanup - delete all the items we created
⋮----
@pytest.mark.asyncio
async def test_vector_search(vector_store: AsyncRedisStore) -> None
⋮----
"""Test vector search functionality with async store."""
# Insert documents with text that can be embedded
docs = [
⋮----
# Search with query
results = await vector_store.asearch(("test",), query="longer text")
⋮----
# Doc2 and doc3 should be closer matches to "longer text"
doc_keys = [r.key for r in results]
⋮----
"""Test that updating items properly updates their embeddings with async store."""
⋮----
# Search for a term similar to doc1's content
results_initial = await vector_store.asearch(("test",), query="zany xylophone")
⋮----
initial_score = results_initial[0].score
# Update doc1 to be about dogs instead
⋮----
# The original query should now match doc1 less strongly
results_after = await vector_store.asearch(("test",), query="zany xylophone")
⋮----
after_score = next((r.score for r in results_after if r.key == "doc1"), None)
⋮----
# A dog-related query should now match doc1 more strongly
results_new = await vector_store.asearch(("test",), query="dogs text")
doc1_score = next((r.score for r in results_new if r.key == "doc1"), None)
⋮----
@pytest.mark.asyncio
async def test_large_batches(store: AsyncRedisStore) -> None
⋮----
"""Test large batch operations with async store."""
# Reduce number of operations for stability
N = 20  # Smaller number for async test to avoid timeouts
ops = []
# Add many put operations
⋮----
# Execute puts first to make sure data exists before querying
put_results = await store.abatch(ops)
⋮----
# Create operations for gets, search, and list
get_ops = []
# Add get operations
⋮----
# Add search operations
⋮----
# Add list namespaces operations
⋮----
# Execute get, search, and list operations
⋮----
expected_results_len = N // 5 + N // 10 + 1
⋮----
# Verify gets (they should return Items)
⋮----
result = get_results[i]
⋮----
# Verify searches (they should return lists)
⋮----
# Verify list namespaces (it should return a list)
⋮----
@pytest.mark.asyncio
async def test_store_ttl(store: AsyncRedisStore) -> None
⋮----
"""Test TTL functionality in async Redis store."""
# Assumes a TTL of TTL_MINUTES
ns = ("foo",)
# Store an item with TTL
⋮----
# Check item exists and refresh TTL
res = await store.aget(ns, key="item1", refresh_ttl=True)
⋮----
# Search for the item with refresh
results = await store.asearch(ns, query="foo", refresh_ttl=True)
⋮----
# Do one more get without refreshing TTL
res = await store.aget(ns, key="item1", refresh_ttl=False)
⋮----
# Wait for the TTL to expire
⋮----
# Force a sweep to remove expired items
⋮----
# Verify item is gone due to TTL expiration
res = await store.asearch(ns, query="bar", refresh_ttl=False)
⋮----
@pytest.mark.asyncio
async def test_async_store_with_memory_persistence(redis_url: str) -> None
⋮----
"""Test basic persistence operations with Redis.
    This test verifies that data persists in Redis after
    creating a new store connection.
    """
# Create a unique namespace for this test
namespace = ("test", "persistence", str(uuid4()))
key = "persisted_item"
value = {"data": "persist_me", "timestamp": time.time()}
# First store instance - write data
⋮----
# Verify the data was written
item = await store1.aget(namespace, key)
⋮----
# Use approximate comparison for floating point values
⋮----
# Second store instance - verify data persisted
⋮----
# Read the item with the new store instance
persisted_item = await store2.aget(namespace, key)
⋮----
# Cleanup
⋮----
@pytest.mark.asyncio
async def test_async_redis_store_client_info(redis_url: str, monkeypatch) -> None
⋮----
"""Test that AsyncRedisStore sets client info correctly."""
⋮----
# Expected client info format
expected_client_info = f"redis-py(redisvl_v{__redisvl_version__})"
# Track if client_setinfo was called with the right parameters
client_info_called = False
# Store the original method
original_client_setinfo = Redis.client_setinfo
# Create a mock function for client_setinfo
async def mock_client_setinfo(self, key, value)
⋮----
# Note: RedisVL might call this with its own lib name first
# We only track calls with our full lib name
⋮----
client_info_called = True
# Call original method to ensure normal function
⋮----
# Apply the mock
⋮----
# Test client info setting when creating a new async store
⋮----
# Verify client_setinfo was called with our library info
⋮----
"""Test that AsyncRedisStore falls back to echo when client_setinfo is not available."""
⋮----
# Remove client_setinfo to simulate older Redis version
⋮----
# Track if echo was called as fallback
echo_called = False
original_echo = Redis.echo
# Create mock for echo
async def mock_echo(self, message)
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Test client info setting with fallback
⋮----
# Verify echo was called as fallback
⋮----
@pytest.mark.asyncio
async def test_async_redis_store_graceful_failure(redis_url: str, monkeypatch) -> None
⋮----
"""Test that async store client info setting fails gracefully when all methods fail."""
⋮----
# Create a patch for the RedisVL validation to avoid it using echo
⋮----
original_validate = RedisConnectionFactory.validate_async_redis
# Create a replacement validation function that doesn't use echo
async def mock_validate(redis_client, lib_name=None)
# Apply the validation mock first to prevent echo from being called by RedisVL
⋮----
# Simulate failures for both methods
⋮----
# Apply the Redis mocks
⋮----
# Should not raise any exceptions when both methods fail

================
File: tests/test_async.py
================
"""Tests for AsyncRedisSaver."""
⋮----
@pytest.fixture
async def test_data() -> Dict[str, Any]
⋮----
"""Test data fixture."""
config_1: RunnableConfig = {
config_2: RunnableConfig = {
config_3: RunnableConfig = {
chkpnt_1: Checkpoint = empty_checkpoint()
chkpnt_2: Checkpoint = create_checkpoint(chkpnt_1, {}, 1)
chkpnt_3: Checkpoint = empty_checkpoint()
metadata_1: CheckpointMetadata = {
metadata_2: CheckpointMetadata = {
metadata_3: CheckpointMetadata = {
⋮----
@pytest.fixture
async def saver(redis_url: str) -> AsyncGenerator[AsyncRedisSaver, None]
⋮----
"""Async saver fixture."""
saver = AsyncRedisSaver(redis_url)
⋮----
({"source": "input"}, 1),  # Matches metadata.source
({"step": 1}, 1),  # Matches metadata.step
({}, 3),  # Retrieve all checkpoints
({"source": "update"}, 0),  # No matches
⋮----
"""Test search functionality with different queries."""
configs = test_data["configs"]
checkpoints = test_data["checkpoints"]
metadata = test_data["metadata"]
# Save test data
⋮----
# Execute search
search_results = [c async for c in saver.alist(None, filter=query)]
⋮----
("source", "\x00abc"),  # Null character in value
⋮----
config = await saver.aput(
⋮----
{key: value},  # type: ignore[misc]
⋮----
result = await saver.aget_tuple(config)
⋮----
sanitized_value = value.replace("\x00", "")
assert result.metadata[key] == sanitized_value  # type: ignore[literal-required]
⋮----
@pytest.mark.asyncio
async def test_put_writes_async(redis_url: str, test_data: Dict[str, Any]) -> None
⋮----
"""Test storing writes in Redis asynchronously."""
⋮----
config = test_data["configs"][0]
checkpoint = test_data["checkpoints"][0]
metadata = test_data["metadata"][0]
# First store a checkpoint
saved_config = await saver.aput(config, checkpoint, metadata, {})
# Test regular writes
writes = [("channel1", "value1"), ("channel2", "value2")]
task_id = "task1"
⋮----
# Test special writes (using WRITES_IDX_MAP)
special_writes = [("__error__", "error_value"), ("channel3", "value3")]
task_id2 = "task2"
⋮----
# Verify writes through get_tuple
result = await saver.aget_tuple(saved_config)
⋮----
pending_writes = result.pending_writes
⋮----
# Verify regular writes
found_writes = {(w[1], w[2]) for w in pending_writes}
⋮----
# Verify special writes
⋮----
"""Test concurrent writes to Redis."""
⋮----
# Create multiple write operations
tasks = []
⋮----
writes = [(f"channel{i}", f"value{i}")]
⋮----
# Execute writes concurrently
⋮----
# Verify all writes were stored
⋮----
# Verify each write was stored
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_with_url(redis_url: str) -> None
⋮----
"""Test creating an AsyncRedisSaver with a connection URL."""
⋮----
# Verify connection works by creating and checking a key
⋮----
value = await saver._redis.get("test_key")
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_with_client(redis_url: str) -> None
⋮----
"""Test creating an AsyncRedisSaver with an existing Redis client."""
client = Redis.from_url(redis_url)
⋮----
# Verify connection works
⋮----
value = await saver._redis.get("test_key2")
⋮----
await client.aclose()  # type: ignore[attr-defined]
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_with_connection_args(redis_url: str) -> None
⋮----
"""Test creating an AsyncRedisSaver with connection arguments."""
⋮----
# Test with decode_responses=True, so we get str instead of bytes
⋮----
value = await saver._redis.get("test_key3")
assert isinstance(value, str)  # not bytes
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_cleanup(redis_url: str) -> None
⋮----
"""Test proper cleanup of Redis connections."""
# When creating from URL
client = None
saver = None
⋮----
saver = s
client = s._redis
assert await client.ping()  # Connection works
# When passing external client, should not close it
ext_client = Redis.from_url(redis_url)
⋮----
client = saver._redis
⋮----
assert await ext_client.ping()  # Should still work
⋮----
await ext_client.aclose()  # type: ignore[attr-defined]
⋮----
@pytest.mark.asyncio
async def test_async_client_info_setting(redis_url: str, monkeypatch) -> None
⋮----
"""Test that async client_setinfo is called with correct library information."""
⋮----
# Expected client info format
expected_client_info = f"redis-py(redisvl_v{__redisvl_version__})"
# Track if client_setinfo was called with the right parameters
client_info_called = False
# Store the original method
original_client_setinfo = Redis.client_setinfo
# Create a mock function for client_setinfo
async def mock_client_setinfo(self, key, value)
⋮----
# Note: RedisVL might call this with its own lib name first
# We only track calls with our full lib name
⋮----
client_info_called = True
# Call original method to ensure normal function
⋮----
# Apply the mock
⋮----
# Test client info setting when creating a new saver with async context manager
⋮----
# __aenter__ should have called aset_client_info
# Verify client_setinfo was called with our library info
⋮----
@pytest.mark.asyncio
async def test_async_client_info_fallback_to_echo(redis_url: str, monkeypatch) -> None
⋮----
"""Test that async client_setinfo falls back to echo when not available."""
⋮----
# Remove client_setinfo to simulate older Redis version
⋮----
# Track if echo was called as fallback
echo_called = False
original_echo = Redis.echo
# Create mock for echo
async def mock_echo(self, message)
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Test client info setting with fallback
⋮----
# __aenter__ should have called aset_client_info with fallback to echo
# Verify echo was called as fallback
⋮----
@pytest.mark.asyncio
async def test_async_client_info_graceful_failure(redis_url: str, monkeypatch) -> None
⋮----
"""Test that async client info setting fails gracefully when all methods fail."""
⋮----
# Create a patch for the RedisVL validation to avoid it using echo
⋮----
original_validate = RedisConnectionFactory.validate_async_redis
# Create a replacement validation function that doesn't use echo
async def mock_validate(redis_client, lib_name=None)
# Apply the validation mock first to prevent echo from being called by RedisVL
⋮----
# Simulate failures for both methods
⋮----
# Apply the Redis mocks
⋮----
# Should not raise any exceptions when both methods fail
⋮----
# __aenter__ should handle failures gracefully
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_errors() -> None
⋮----
"""Test error conditions for from_conn_string."""
# Test with neither URL nor client provided
⋮----
# Test with empty URL - should fail
⋮----
# Test with invalid connection URL
⋮----
# Test with non-functional client
client = Redis.from_url("redis://nonexistent:6379")
⋮----
"""Test that writes are properly stored in Redis JSON format."""
⋮----
# First store a checkpoint to get proper config
⋮----
writes = [("channel1", "value1")]
⋮----
# Store write
⋮----
# Verify JSON structure directly
thread_id = saved_config["configurable"]["thread_id"]
checkpoint_ns = saved_config["configurable"].get("checkpoint_ns", "")
checkpoint_id = saved_config["configurable"]["checkpoint_id"]
⋮----
write_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
# Get raw JSON
json_data = await saver._redis.json().get(write_key)
# Verify structure
⋮----
@pytest.mark.asyncio
async def test_search_writes_async(redis_url: str) -> None
⋮----
"""Test searching writes using Redis Search."""
⋮----
# Set up test data with proper typing
config: RunnableConfig = {
checkpoint = empty_checkpoint()
metadata: CheckpointMetadata = {"source": "test", "step": 1, "writes": {}}
# Store checkpoint to get proper config
⋮----
# Add writes for multiple channels
writes1 = [("channel1", "value1"), ("channel2", "value2")]
⋮----
writes2 = [("channel1", "value3")]
⋮----
# Search by channel
query = "(@channel:{channel1})"
results = await saver.checkpoint_writes_index.search(query)
assert len(results.docs) == 2  # One document per channel1 writes
doc1 = json.loads(results.docs[0].json)
doc2 = json.loads(results.docs[1].json)
⋮----
# Search by task
query = "(@task_id:{task1})"
⋮----
# Search by thread/namespace
query = "(@thread_id:{thread1} @checkpoint_ns:{ns1})"
⋮----
assert len(results.docs) == 3  # Contains all three writes
⋮----
doc3 = json.loads(results.docs[2].json)
⋮----
@pytest.mark.asyncio
async def test_no_running_loop(redis_url: str, test_data: dict[str, Any]) -> None
⋮----
"""Test that sync operations raise error when called from async loop."""
⋮----
saver.loop = asyncio.get_running_loop()  # Set the loop explicitly
# Test get_tuple which has the explicit check
⋮----
# Other sync operations from background threads should work
⋮----
future = pool.submit(
result = await asyncio.wrap_future(future)
⋮----
@pytest.mark.asyncio
async def test_large_batches(redis_url: str, test_data: dict[str, Any]) -> None
⋮----
"""Test handling large numbers of operations with thread pool."""
⋮----
N = 1000
M = 5
⋮----
futures = []
⋮----
# Create config and cast to RunnableConfig type
test_config: RunnableConfig = {
⋮----
results = await asyncio.gather(
⋮----
@pytest.mark.asyncio
async def test_large_batches_async(redis_url: str, test_data: dict[str, Any]) -> None
⋮----
"""Test handling large numbers of async operations."""
⋮----
M = 2
# Store configs and their responses
stored_configs = []
coros = []
⋮----
put_results = await asyncio.gather(*coros)
⋮----
# Verify we can retrieve using the configs returned from put
verify_coros = []
⋮----
verify_results = await asyncio.gather(*verify_coros)
⋮----
@tool
def get_weather(city: Literal["nyc", "sf"]) -> str
⋮----
"""Use this to get weather information."""
⋮----
@pytest.fixture
def tools() -> List[BaseTool]
⋮----
@pytest.fixture
def mock_llm() -> Any
⋮----
"""Create a mock LLM for testing without requiring API keys."""
⋮----
# Create a mock that can be used in place of a real LLM
mock = MagicMock()
⋮----
@pytest.fixture
def mock_agent() -> Any
⋮----
"""Create a mock agent that creates checkpoints without requiring a real LLM."""
⋮----
# Create a mock agent that returns a dummy response
⋮----
# Set the ainvoke method to also create a fake chat session
async def mock_ainvoke(messages, config)
⋮----
# Return a dummy response that mimics a chat conversation
⋮----
"""Test AsyncRedisSaver checkpoint functionality using a mock agent."""
⋮----
# Use the mock agent instead of creating a real one
graph = mock_agent
# Use a unique thread_id
thread_id = f"test-{uuid4()}"
# Test initial query
⋮----
# Create a checkpoint manually to simulate what would happen during agent execution
checkpoint = {
# Store the checkpoint
next_config = await checkpointer.aput(
# Verify next_config has the right structure
⋮----
# Test checkpoint retrieval
latest = await checkpointer.aget(config)
⋮----
# Test checkpoint tuple
tuple_result = await checkpointer.aget_tuple(config)
⋮----
# Test listing checkpoints
checkpoints = [c async for c in checkpointer.alist(config)]
⋮----
"""
    A regression test for a bug where queries for checkpoints from the
    root graph were failing to find valid checkpoints. When called from
    a root graph, the `checkpoint_id` and `checkpoint_ns` keys are not
    in the config object.
    """
⋮----
thread_id = f"root-graph-{uuid4()}"
# Create a config with checkpoint_id and checkpoint_ns
# For a root graph test, we need to add an empty checkpoint_ns
# since that's how real root graphs work
⋮----
"checkpoint_ns": "",  # Empty string is valid
⋮----
# Verify the checkpoint was stored
⋮----
# Test retrieving the checkpoint with a root graph config
# that doesn't have checkpoint_id or checkpoint_ns
⋮----
# This is the key test - verify we can retrieve checkpoints
# when called from a root graph configuration
⋮----
)  # Initial + LLM + Tool + Final

================
File: tests/test_checkpoint_ttl.py
================
"""Tests for TTL functionality with RedisSaver."""
⋮----
class State(TypedDict)
⋮----
"""Simple state with count."""
count: int
⋮----
@pytest.fixture(scope="function")
def redis_url(redis_container) -> str
⋮----
"""Get the Redis URL from the container."""
⋮----
@pytest.fixture(scope="function")
def redis_client(redis_url: str) -> Generator[Redis, None, None]
⋮----
"""Create a Redis client for testing."""
client = Redis.from_url(redis_url)
⋮----
# Clean up any test keys
keys = client.keys("checkpoint:test_ttl*")
⋮----
@pytest.fixture(scope="function")
def ttl_checkpoint_saver(redis_client: Redis) -> Generator[RedisSaver, None, None]
⋮----
"""Create a RedisSaver instance with TTL support."""
saver = RedisSaver(
⋮----
},  # 0.1 minutes = 6 seconds TTL
⋮----
def test_ttl_config_in_constructor(redis_client: Redis) -> None
⋮----
"""Test that TTL config can be passed through constructor."""
⋮----
def test_checkpoint_expires(redis_client: Redis) -> None
⋮----
"""Test that a checkpoint expires after the TTL period."""
⋮----
# Create unique identifiers to avoid test collisions
unique_prefix = f"expires_test_{int(time.time())}"
# Create a saver with TTL
ttl_checkpoint_saver = RedisSaver(
⋮----
"default_ttl": 0.1,  # 0.1 minutes = 6 seconds TTL
⋮----
# Create a checkpoint with unique thread ID
thread_id = f"{unique_prefix}_thread"
checkpoint_ns = f"{unique_prefix}_ns"
checkpoint_id = f"{unique_prefix}_checkpoint"
config: RunnableConfig = {
checkpoint: Checkpoint = {
metadata: CheckpointMetadata = {
# Save the checkpoint (with default TTL of 0.1 minutes = 6 seconds)
⋮----
# Verify checkpoint exists immediately after creation
initial_result = ttl_checkpoint_saver.get_tuple(config)
⋮----
# Wait for TTL to expire (plus a small buffer)
time.sleep(7)  # 7 seconds > 6 seconds TTL
# Verify checkpoint no longer exists
result = ttl_checkpoint_saver.get_tuple(config)
⋮----
# Clean up
keys = redis_client.keys(f"checkpoint:*{thread_id}*")
⋮----
# Do not close the client as it's provided by the fixture
def test_ttl_refresh_on_read(redis_client: Redis) -> None
⋮----
"""Test that TTL is refreshed when reading a checkpoint if refresh_on_read is enabled."""
⋮----
unique_prefix = f"refresh_test_{int(time.time())}"
# Create a saver with TTL and refresh_on_read enabled
⋮----
# Wait for 3 seconds (less than TTL)
⋮----
# Read the checkpoint (should refresh TTL)
⋮----
# Wait another 2 seconds (would be 5 seconds total, less than original TTL)
⋮----
# Wait extra time to account for any test delays
⋮----
# Checkpoint should still exist because TTL was refreshed
⋮----
# Wait for TTL to expire again
⋮----
def test_put_writes_with_ttl(redis_client: Redis) -> None
⋮----
"""Test that writes also expire with TTL."""
⋮----
unique_prefix = f"writes_test_{int(time.time())}"
⋮----
# Create some writes
⋮----
# Verify writes exist immediately after creation
initial_writes = ttl_checkpoint_saver._load_pending_writes(
⋮----
# Wait for TTL to expire
⋮----
# Verify writes no longer exist
writes = ttl_checkpoint_saver._load_pending_writes(
⋮----
def test_no_ttl_when_not_configured(redis_client: Redis) -> None
⋮----
"""Test that keys don't expire when TTL is not configured."""
⋮----
unique_prefix = f"no_ttl_test_{int(time.time())}"
# Create a saver without TTL
saver = RedisSaver(redis_client=redis_client)
⋮----
# Save the checkpoint (no TTL configured)
⋮----
initial_result = saver.get_tuple(config)
⋮----
# Wait for the same amount of time that would cause TTL expiration
⋮----
# Verify checkpoint still exists
result = saver.get_tuple(config)
⋮----
def test_simple_graph_with_ttl(redis_client: Redis) -> None
⋮----
"""Test a simple graph with TTL configuration."""
# Use an isolated Redis client to prevent interference from parallel tests
unique_prefix = f"graph_test_{int(time.time())}"
⋮----
def add_one(state)
⋮----
"""Add one to the state."""
⋮----
# Define a simple graph
builder = StateGraph(State)
⋮----
# Create a checkpointer with TTL
⋮----
ttl={"default_ttl": 0.1, "refresh_on_read": True},  # 6 seconds TTL
⋮----
# Compile the graph with the checkpointer
graph = builder.compile(checkpointer=checkpointer)
# Use the graph with a specific thread_id
config = {"configurable": {"thread_id": thread_id}}
# Initial run
result = graph.invoke({"count": 0}, config=config)
⋮----
# Run again immediately - should continue from checkpoint
result = graph.invoke({}, config=config)
⋮----
# Wait for TTL to expire
time.sleep(7)  # Wait longer than the 6 second TTL
# Run again - should start from beginning since checkpoint expired

================
File: tests/test_cluster_mode.py
================
"""Tests for RedisStore Redis Cluster mode functionality."""
⋮----
# Override session-scoped redis_container fixture to prevent Docker operations and provide dummy host/port
class DummyCompose
⋮----
def get_service_host_and_port(self, service, port)
⋮----
# Return localhost and default port for dummy usage
⋮----
@pytest.fixture(scope="session", autouse=True)
def redis_container()
⋮----
"""Override redis_container to use DummyCompose instead of real DockerCompose."""
⋮----
# Synchronous Mock Redis Clients
class BaseMockRedis
⋮----
def __init__(self, *args, **kwargs)
⋮----
# Do not call super().__init__ to avoid real connection
⋮----
# Pipeline mock
⋮----
def pipeline(self, transaction=True)
def expire(self, key, ttl)
def connection_pool(self)
def delete(self, *keys)
def ttl(self, key)
def json(self)
⋮----
json_mock = MagicMock()
⋮----
def cluster(self, subcmd: str, *args, **kwargs)
class MockRedis(BaseMockRedis, Redis)
class MockRedisCluster(BaseMockRedis, SyncRedisCluster)
⋮----
# Do not call super().__init__ from SyncRedisCluster
⋮----
@pytest.fixture(params=[False, True])
def store(request)
⋮----
"""Parameterized fixture for RedisStore with regular or cluster client."""
is_cluster = request.param
client = MockRedisCluster() if is_cluster else MockRedis()
# Basic IndexConfig, embeddings won't be used in these tests
index_config = {
store = RedisStore(conn=client, index=index_config)  # type: ignore
# Mock the search indices
⋮----
def test_cluster_detection(store)
⋮----
"""Test that store.cluster_mode is set correctly."""
is_client_cluster = isinstance(store._redis, SyncRedisCluster)
⋮----
def test_apply_ttl_to_keys_behavior(store)
⋮----
"""Test _apply_ttl_to_keys behavior for cluster vs. non-cluster."""
client = store._redis
⋮----
main_key = "main:key"
related_keys = ["related:key1", "related:key2"]
ttl_minutes = 10.0
⋮----
def test_batch_get_ops_ttl_refresh(store)
⋮----
"""Test TTL refresh in _batch_get_ops."""
⋮----
op_idx = 0
doc_id = str(ULID())
store_doc_id = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{doc_id}"
# Mock store_index.search to return a document
mock_doc_data = {
mock_redis_doc = MagicMock()
⋮----
# Mock client.ttl to control TTL refresh logic
⋮----
get_ops = [
results = [None]
⋮----
def test_batch_put_ops_pre_delete_behavior(store)
⋮----
"""Test pre-delete behavior in _batch_put_ops."""
⋮----
doc_id_to_delete = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{str(ULID())}"
vector_doc_id_to_delete = f"{STORE_VECTOR_PREFIX}{REDIS_KEY_SEPARATOR}{str(ULID())}"
# Mock store_index.search to return a document that needs to be deleted
mock_store_doc = MagicMock(id=doc_id_to_delete)
⋮----
# Mock vector_index.search if index_config is present
⋮----
mock_vector_doc = MagicMock(id=vector_doc_id_to_delete)
⋮----
put_ops = [
⋮----
def test_batch_search_ops_vector_fetch_behavior(store)
⋮----
"""Test fetching store docs after vector search in _batch_search_ops."""
⋮----
mock_vector_doc_id = str(ULID())
mock_vector_result_doc = MagicMock()
⋮----
expected_store_key = f"{STORE_PREFIX}{REDIS_KEY_SEPARATOR}{mock_vector_doc_id}"
mock_store_data_search = {
mock_json = MagicMock(get=MagicMock(return_value=mock_store_data_search))
⋮----
search_ops = [
⋮----
def test_batch_list_namespaces_ops_behavior(store)
⋮----
"""Test listing namespaces in _batch_list_namespaces_ops."""
mock_doc1 = MagicMock(prefix="test.documents.public")
mock_doc2 = MagicMock(prefix="test.documents.private")
mock_doc3 = MagicMock(prefix="test.images.public")
mock_doc4 = MagicMock(prefix="prod.documents.public")
mock_search_result = MagicMock(docs=[mock_doc1, mock_doc2, mock_doc3, mock_doc4])
⋮----
list_ops = [
results: list[Any] = [None, None]
⋮----
# Verify results for full depth
⋮----
# Verify results for depth 2
⋮----
@pytest.fixture(params=[False, True])
def checkpoint_saver(request)
⋮----
"""Parameterized fixture for RedisSaver with regular or cluster client."""
⋮----
saver = RedisSaver(redis_client=client)
⋮----
def test_checkpoint_saver_cluster_detection(checkpoint_saver)
⋮----
"""Test that checkpoint saver cluster_mode is set correctly."""
is_client_cluster = isinstance(checkpoint_saver._redis, SyncRedisCluster)
⋮----
def test_checkpoint_saver_ttl_behavior(checkpoint_saver)
⋮----
"""Test TTL behavior for checkpoint saver in cluster vs. non-cluster mode."""
client = checkpoint_saver._redis
⋮----
# Set up TTL config
⋮----
main_key = "checkpoint:test:key"
blob_keys = ["blob:key1", "blob:key2"]
⋮----
# In cluster mode, TTL operations should be called directly
⋮----
} in client.expire_calls  # 5 minutes = 300 seconds
⋮----
# Pipeline should not be used
⋮----
# In non-cluster mode, pipeline should be used
⋮----
# Direct expire calls should not be made
⋮----
def test_checkpoint_saver_delete_thread_behavior(checkpoint_saver)
⋮----
"""Test delete_thread behavior for checkpoint saver in cluster vs. non-cluster mode."""
⋮----
# Mock search results to simulate existing data
mock_checkpoint_doc = MagicMock()
⋮----
mock_blob_doc = MagicMock()
⋮----
mock_write_doc = MagicMock()
⋮----
# In cluster mode, delete operations should be called directly
⋮----
# Pipeline should not be used for deletions
⋮----
# In non-cluster mode, pipeline should be used for deletions
⋮----
# Direct delete calls should not be made

================
File: tests/test_crossslot_integration.py
================
"""Integration tests for CrossSlot error fix in checkpoint operations."""
⋮----
def test_checkpoint_operations_no_crossslot_errors(redis_url: str) -> None
⋮----
"""Test that checkpoint operations work without CrossSlot errors.
    This test verifies that the fix for using search indexes instead of keys()
    works correctly in a real Redis environment.
    """
# Create a saver
saver = RedisSaver(redis_url)
⋮----
# Create test data
thread_id = "test-thread-crossslot"
checkpoint_ns = "test-ns"
# Create checkpoints with unique IDs
checkpoint1 = create_checkpoint(empty_checkpoint(), {}, 1)
checkpoint2 = create_checkpoint(checkpoint1, {"messages": ["hello"]}, 2)
checkpoint3 = create_checkpoint(checkpoint2, {"messages": ["hello", "world"]}, 3)
# Create metadata
metadata1 = {"source": "input", "step": 1, "writes": {"task1": "value1"}}
metadata2 = {"source": "loop", "step": 2, "writes": {"task2": "value2"}}
metadata3 = {"source": "loop", "step": 3, "writes": {"task3": "value3"}}
# Put checkpoints with writes
config1 = {"configurable": {"thread_id": thread_id, "checkpoint_ns": checkpoint_ns}}
config2 = {"configurable": {"thread_id": thread_id, "checkpoint_ns": checkpoint_ns}}
config3 = {"configurable": {"thread_id": thread_id, "checkpoint_ns": checkpoint_ns}}
# Put checkpoints first to get configs with checkpoint_ids
saved_config1 = saver.put(config1, checkpoint1, metadata1, {})
saved_config2 = saver.put(config2, checkpoint2, metadata2, {})
saved_config3 = saver.put(config3, checkpoint3, metadata3, {})
# Add some pending writes using saved configs
⋮----
# Now test operations that previously used keys() and would fail in cluster mode
# Test 1: Load pending writes (uses _load_pending_writes)
# This should work without CrossSlot errors
tuple1 = saver.get_tuple(saved_config1)
⋮----
# Verify pending writes were loaded
⋮----
pending_channels = [w[1] for w in tuple1.pending_writes]
⋮----
# Test 2: Get tuple with TTL (uses get_tuple which searches for blob and write keys)
saver_with_ttl = RedisSaver(redis_url, ttl={"checkpoint": 3600})
⋮----
# Put a checkpoint with TTL
config_ttl = {
⋮----
# Get the checkpoint - this triggers TTL application which uses key searches
tuple_ttl = saver_with_ttl.get_tuple(config_ttl)
⋮----
# Test 3: List checkpoints - this should work without CrossSlot errors
# List returns only the latest checkpoint by default
checkpoints = list(saver.list(config1))
⋮----
# The latest checkpoint should have the pending writes from checkpoint1
latest_checkpoint = checkpoints[0]
⋮----
# The important part is that all these operations work without CrossSlot errors
# In a Redis cluster, the old keys() based approach would have failed by now
def test_subgraph_checkpoint_operations(redis_url: str) -> None
⋮----
"""Test checkpoint operations with subgraphs work without CrossSlot errors."""
⋮----
# Create nested namespace checkpoints
thread_id = "test-thread-subgraph"
# Parent checkpoint
parent_config = {
parent_checkpoint = empty_checkpoint()
parent_metadata = {"source": "input", "step": 1}
# Child checkpoint in subgraph
child_config = {
child_checkpoint = create_checkpoint(parent_checkpoint, {"subgraph": "data"}, 1)
child_metadata = {"source": "loop", "step": 1}
# Grandchild checkpoint in nested subgraph
grandchild_config = {
grandchild_checkpoint = create_checkpoint(child_checkpoint, {"nested": "data"}, 2)
grandchild_metadata = {"source": "loop", "step": 2}
# Put all checkpoints first to get saved configs
saved_parent_config = saver.put(
saved_child_config = saver.put(child_config, child_checkpoint, child_metadata, {})
saved_grandchild_config = saver.put(
# Put checkpoints with writes using saved configs
⋮----
# Test loading checkpoints with pending writes from different namespaces
parent_tuple = saver.get_tuple(parent_config)
⋮----
child_tuple = saver.get_tuple(child_config)
⋮----
grandchild_tuple = saver.get_tuple(grandchild_config)
⋮----
# List all checkpoints - should work without CrossSlot errors
all_checkpoints = list(saver.list({"configurable": {"thread_id": thread_id}}))

================
File: tests/test_decode_responses.py
================
"""Tests for Redis key decoding functionality."""
⋮----
def test_safely_decode_basic_types()
⋮----
"""Test safely_decode function with basic type inputs."""
# Test with bytes
⋮----
# Test with string
⋮----
# Test with None
⋮----
# Test with other types
⋮----
def test_safely_decode_nested_structures()
⋮----
"""Test safely_decode function with nested data structures."""
# Test with dictionary
⋮----
# Test with nested dictionary
nested_dict = {b"outer": {b"inner": b"value"}}
⋮----
# Test with list
⋮----
# Test with tuple
⋮----
# Test with set
decoded_set = safely_decode({b"item1", b"item2"})
⋮----
# Test with complex nested structure
complex_struct = {
decoded = safely_decode(complex_struct)
⋮----
@pytest.mark.parametrize("decode_responses", [True, False])
def test_safely_decode_with_redis(decode_responses: bool, redis_url)
⋮----
"""Test safely_decode function with actual Redis responses using TestContainers."""
r = Redis.from_url(redis_url, decode_responses=decode_responses)
⋮----
# Clean up before test to ensure a clean state
⋮----
# Set up test data
⋮----
# Test string value
string_val = r.get("test:string")
decoded_string = safely_decode(string_val)
⋮----
# Test hash value
hash_val = r.hgetall("test:hash")
decoded_hash = safely_decode(hash_val)
⋮----
# Test list value
list_val = r.lrange("test:list", 0, -1)
decoded_list = safely_decode(list_val)
⋮----
# Test set value
set_val = r.smembers("test:set")
decoded_set = safely_decode(set_val)
⋮----
# Test key fetching
keys = r.keys("test:*")
decoded_keys = safely_decode(keys)
⋮----
# Clean up after test
⋮----
def test_safely_decode_unicode_error_handling()
⋮----
"""Test safely_decode function with invalid UTF-8 bytes."""
# Create bytes that will cause UnicodeDecodeError
invalid_utf8 = b"\xff\xfe\xfd"
# Should return the original bytes if it can't be decoded
result = safely_decode(invalid_utf8)
⋮----
# Test with mixed valid and invalid in a complex structure
mixed = {
result = safely_decode(mixed)

================
File: tests/test_fix_verification.py
================
"""Final verification tests for Redis key parsing fixes.
This test specifically tests the key parsing fix that was causing issues in:
1. semantic-search.ipynb
2. subgraphs-manage-state.ipynb
3. subgraph-persistence.ipynb
"""
⋮----
# Test for the specific key parsing issue with extra components
def test_key_parsing_fix_handles_extra_components()
⋮----
"""Test that our fix for key parsing correctly handles keys with extra components."""
# Create various keys with different numbers of components
keys = [
⋮----
# Standard key with exactly 6 components (the original expected format)
⋮----
# Key with 7 components (would have failed before the fix)
⋮----
# Key with 8 components (would have failed before the fix)
⋮----
# Key with 9 components (would have failed before the fix)
⋮----
# Key with a common subgraph pattern (from subgraphs-manage-state.ipynb)
⋮----
# Key with a pattern seen in semantic-search.ipynb
⋮----
# Test each key with the _parse_redis_checkpoint_writes_key method
⋮----
# This would have raised a ValueError before the fix
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# Verify that only the expected fields are present and have the right values
⋮----
# Check the extracted values match what we expect (only the first 6 components)
parts = key.split(REDIS_KEY_SEPARATOR)

================
File: tests/test_interruption.py
================
"""Tests for interruption handling in Redis checkpointers."""
⋮----
class InterruptionError(Exception)
⋮----
"""Error used to simulate an interruption during checkpoint operations."""
⋮----
class MockRedis(Redis)
⋮----
"""Mock Redis class that can simulate interruptions during operations."""
def __init__(self, real_redis: Redis, interrupt_on: str = None) -> None
⋮----
"""Initialize with a real Redis client and optional interruption point.
        Args:
            real_redis: The real Redis client to delegate to
            interrupt_on: Operation name to interrupt on (e.g., 'json().set', 'Pipeline.execute')
        """
# Copy connection info from real_redis to satisfy Redis base class
⋮----
def __getattribute__(self, name)
⋮----
"""Proxy attribute access to the real Redis client, but track operations."""
# For special attributes we've set in __init__, use the parent implementation
⋮----
# For Redis base class attributes
⋮----
attr = getattr(self.real_redis, name)
⋮----
# Fall back to parent class
⋮----
# For methods we want to potentially interrupt
⋮----
# Initialize counter for this operation if not exist
⋮----
async def wrapper(*args, **kwargs)
⋮----
# Increment operation count
⋮----
# Check if we should interrupt
⋮----
# Otherwise, call the real method
⋮----
# Special handling for pipeline to ensure we can intercept pipeline.execute()
⋮----
original_method = attr
def pipeline_wrapper(*args, **kwargs)
⋮----
pipeline = original_method(*args, **kwargs)
⋮----
# For Redis subsystems (like json())
⋮----
def subsystem_wrapper(*args, **kwargs)
⋮----
subsystem = original_method(*args, **kwargs)
⋮----
# For other attributes, return as is
⋮----
class MockRedisSubsystem
⋮----
"""Mock Redis subsystem (like json()) that can simulate interruptions."""
def __init__(self, real_subsystem, parent_mock)
def __getattr__(self, name)
⋮----
attr = getattr(self.real_subsystem, name)
⋮----
operation_name = f"{self.real_subsystem.__class__.__name__}.{name}"
⋮----
# For non-async methods
def sync_wrapper(*args, **kwargs)
⋮----
# Increment operation count
⋮----
# Check if we should interrupt
⋮----
# Otherwise, call the real method
⋮----
# Special handling for pipeline method to track operations within the pipeline
⋮----
# This is likely a pipeline execute method
async def execute_wrapper(*args, **kwargs)
⋮----
# Check if we should interrupt pipeline execution
⋮----
# Otherwise call the real execute
⋮----
"""Create a saver with a mock Redis client that can simulate interruptions.
    Args:
        redis_url: Redis connection URL
        saver_class: The saver class to instantiate (AsyncRedisSaver or AsyncShallowRedisSaver)
        interrupt_on: Operation to interrupt on
        interrupt_after_count: Number of operations to allow before interrupting
    Yields:
        A configured saver instance with interruptible Redis client
    """
# Create real Redis client
real_redis = Redis.from_url(redis_url)
# Create mock Redis client that will interrupt on specified operation
mock_redis = MockRedis(real_redis, interrupt_on)
⋮----
# Create saver with mock Redis
saver = saver_class(redis_client=mock_redis)
⋮----
# Close Redis client
⋮----
# Cleanup manually if __aexit__ doesn't exist
⋮----
"""Create test checkpoint data for the tests."""
thread_id = f"test-{uuid.uuid4()}"
checkpoint_id = str(uuid.uuid4())
config = {
checkpoint = {
metadata = {
new_versions = {"messages": "1"}
⋮----
"""Verify whether checkpoint data exists in Redis as expected."""
# Check if checkpoint data exists in Redis
keys = redis_client.keys(f"*{thread_id}*")
⋮----
# Check if specific checkpoint ID exists
⋮----
@pytest.mark.asyncio
async def test_aput_interruption_regular_saver(redis_url: str) -> None
⋮----
"""Test interruption during AsyncRedisSaver.aput operation."""
# Create test data
⋮----
thread_id = config["configurable"]["thread_id"]
checkpoint_id = checkpoint["id"]
# Create saver with interruption during pipeline execute
⋮----
# Try to save checkpoint, expect interruption
⋮----
# Verify that the checkpoint data is incomplete or inconsistent
⋮----
# Attempt to retrieve the checkpoint
result = await saver.aget(config)
# Either the result should be None or contain incomplete data
⋮----
@pytest.mark.asyncio
async def test_aput_interruption_shallow_saver(redis_url: str) -> None
⋮----
"""Test interruption during AsyncShallowRedisSaver.aput operation."""
⋮----
@pytest.mark.asyncio
async def test_aput_writes_interruption(redis_url: str) -> None
⋮----
"""Test interruption during aput_writes operation."""
⋮----
# Successfully save a checkpoint first
⋮----
next_config = await saver.aput(config, checkpoint, metadata, new_versions)
# Now create a saver that will interrupt during pipeline execution
mock_redis = MockRedis(saver._redis, "Pipeline.execute")
⋮----
# Replace the Redis client with our mock
original_redis = saver._redis
⋮----
# Try to save writes, expect interruption
⋮----
# Restore original Redis client to verify state
⋮----
# Verify that no writes were saved due to transaction abort
checkpoint_tuple = await saver.aget_tuple(next_config)
# Either there are no pending writes or they are not the ones we tried to save
⋮----
# Cleanup
⋮----
@pytest.mark.asyncio
async def test_recovery_after_interruption(redis_url: str) -> None
⋮----
"""Test whether checkpoint operations can recover after an interruption."""
⋮----
# Step 1: Try to save with interruption
⋮----
# Step 2: Try to save again with a new saver (simulate process restart after interruption)
⋮----
# Try to save the same checkpoint again
next_config = await new_saver.aput(config, checkpoint, metadata, new_versions)
# Verify the checkpoint was saved successfully
result = await new_saver.aget(config)
⋮----
# Clean up
⋮----
@pytest.mark.asyncio
async def test_graph_simulation_with_interruption(redis_url: str) -> None
⋮----
"""Test a more complete scenario simulating a graph execution with interruption."""
# Create a mock graph execution
⋮----
# Config without checkpoint_id to simulate first run
initial_config = {
# Create initial checkpoint
initial_checkpoint = {
# First save the initial checkpoint
⋮----
next_config = await saver.aput(
# Verify initial checkpoint was saved
initial_result = await saver.aget(initial_config)
⋮----
# Now prepare update with interruption
second_checkpoint = {
# Replace Redis client with mock that will interrupt
⋮----
mock_redis = MockRedis(original_redis, "Pipeline.execute")
⋮----
# Try to update, expect interruption
⋮----
# Restore original Redis for verification
⋮----
# Check checkpoint state - with transaction handling, we expect to see the initial checkpoint
# since the transaction should have been rolled back
current = await saver.aget(next_config)
# With transaction handling, we should still see the initial checkpoint

================
File: tests/test_issue_51_adelete_thread.py
================
"""Test for issue #51 - adelete_thread implementation."""
⋮----
@pytest.mark.asyncio
async def test_adelete_thread_implemented(redis_url)
⋮----
"""Test that adelete_thread method is now implemented in AsyncRedisSaver."""
⋮----
# Create a checkpoint
thread_id = "test-thread-to-delete"
config: RunnableConfig = {
checkpoint = Checkpoint(
# Store checkpoint
⋮----
# Verify checkpoint exists
result = await checkpointer.aget_tuple(config)
⋮----
# Delete the thread
⋮----
# Verify checkpoint is deleted
⋮----
def test_delete_thread_implemented(redis_url)
⋮----
"""Test that delete_thread method is now implemented in RedisSaver."""
⋮----
checkpointer.setup()  # Initialize Redis indices
⋮----
thread_id = "test-thread-to-delete-sync"
⋮----
result = checkpointer.get_tuple(config)
⋮----
@pytest.mark.asyncio
async def test_adelete_thread_comprehensive(redis_url)
⋮----
"""Comprehensive test for adelete_thread with multiple checkpoints and namespaces."""
⋮----
thread_id = "test-thread-comprehensive"
other_thread_id = "other-thread"
# Create multiple checkpoints for the thread
checkpoints_data = [
# Also create checkpoints for another thread that should not be deleted
other_checkpoints_data = [
# Store all checkpoints
⋮----
# Also add some writes
⋮----
# Store checkpoints for other thread
⋮----
# Verify all checkpoints exist
⋮----
# Verify other thread checkpoints exist
⋮----
# Verify all checkpoints for the thread are deleted
⋮----
# Verify other thread checkpoints still exist

================
File: tests/test_key_parsing.py
================
"""Tests for Redis key parsing in the BaseRedisSaver class."""
⋮----
def test_parse_redis_checkpoint_writes_key_with_exact_parts()
⋮----
"""Test parsing a Redis key with exactly 6 parts."""
# Create a key with exactly 6 parts
key = REDIS_KEY_SEPARATOR.join(
# Parse the key
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# Verify the result
⋮----
def test_parse_redis_checkpoint_writes_key_with_extra_parts()
⋮----
"""Test parsing a Redis key with more than 6 parts."""
# Create a key with more than 6 parts
⋮----
# Verify the result - should only include the first 6 parts
⋮----
# Extra parts should be ignored
⋮----
def test_parse_redis_checkpoint_writes_key_with_insufficient_parts()
⋮----
"""Test parsing a Redis key with fewer than 6 parts."""
# Create a key with fewer than 6 parts
⋮----
# Parse the key - should raise ValueError
⋮----
def test_parse_redis_checkpoint_writes_key_with_incorrect_prefix()
⋮----
"""Test parsing a Redis key with an incorrect prefix."""
# Create a key with an incorrect prefix
⋮----
def test_parse_redis_checkpoint_writes_key_with_escaped_special_characters()
⋮----
"""Test parsing a Redis key with escaped special characters in the parts."""
# In practice, special characters would be escaped before creating the key
# This test makes sure the to_storage_safe_str function is being called
# Create a key with parts that don't contain the separator character

================
File: tests/test_numeric_version_fix.py
================
"""
Test for the fix to issue #40 - Fixing numeric version handling with Tag type.
"""
⋮----
@pytest.fixture(autouse=True)
async def clear_test_redis(redis_url: str) -> None
⋮----
"""Clear Redis before each test."""
client = Redis.from_url(redis_url)
⋮----
@contextmanager
def patched_redis_saver(redis_url)
⋮----
"""
    Create a RedisSaver with a patched _dump_blobs method to fix the issue.
    This demonstrates the fix approach.
    """
original_dump_blobs = RedisSaver._dump_blobs
def patched_dump_blobs(self, thread_id, checkpoint_ns, values, versions)
⋮----
"""
        Patched version of _dump_blobs that ensures version is a string.
        """
# Convert version to string in versions dictionary
string_versions = {k: str(v) for k, v in versions.items()}
# Call the original method with string versions
⋮----
# Apply the patch
⋮----
# Create the saver with patched method
saver = RedisSaver(redis_url)
⋮----
# Restore the original method
⋮----
# Clean up
⋮----
def test_numeric_version_fix(redis_url: str) -> None
⋮----
"""
    Test that demonstrates the fix for issue #40.
    This shows how to handle numeric versions correctly by ensuring
    they are converted to strings before being used with Tag.
    """
# Use our patched version that converts numeric versions to strings
⋮----
# Set up a basic config
config = {
# Create a basic checkpoint
checkpoint = empty_checkpoint()
# Store the checkpoint with our patched method
saved_config = saver.put(
⋮----
)  # Numeric version
# Get the checkpoint ID from the saved config
thread_id = saved_config["configurable"]["thread_id"]
checkpoint_ns = saved_config["configurable"].get("checkpoint_ns", "")
# Now query the data - this should work with the fix
query = f"@channel:{{test_channel}}"
# This should not raise an error now with our patch
results = saver.checkpoint_blobs_index.search(query)
# Verify we can find the data
⋮----
# Load one document and verify the version is a string
doc = results.docs[0]
data = saver._redis.json().get(doc.id)
# The key test: version should be a string even though we passed a numeric value

================
File: tests/test_numeric_version_issue.py
================
"""
Test for issue #40 - Error when comparing numeric version with Tag type.
"""
⋮----
@pytest.fixture(autouse=True)
async def clear_test_redis(redis_url: str) -> None
⋮----
"""Clear Redis before each test."""
client = Redis.from_url(redis_url)
⋮----
def test_numeric_version_issue(redis_url: str) -> None
⋮----
"""
    Test reproduction for issue #40.
    This test explicitly creates a scenario where a numeric version field
    is compared with a Tag type, which should cause a TypeError.
    """
# Create a Redis saver with default configuration
saver = RedisSaver(redis_url)
⋮----
# Here we'll directly test the specific problem from issue #40
# In the real app, the version field is stored as a number in Redis
# Then when the code in _dump_blobs tries to pass that numeric version
# to the Tag filter, it causes a TypeError
# First create a fixed test with direct Tag usage to demonstrate the issue
tag_filter = Tag("version")
⋮----
# This will trigger the error because we're comparing Tag with integer
result = tag_filter == 1  # Integer instead of string
# Verify the specific error message related to Tag comparison
⋮----
# Another approach would be a direct test of our _dump_blobs method
# by creating a fake numeric version and then trying to create a Tag query
# based on it
channel_name = "test_channel"
numeric_version = 1  # This is the root issue - numeric version not string
# This mimics the code in _dump_blobs that would fail
versions = {channel_name: numeric_version}
# We can't directly patch the method, but we can verify the same type issue
# Here we simulate what happens when a numeric version is passed to Tag filter
⋮----
# This fails because we're comparing a Tag with a numeric value directly
result = tag_filter == versions[channel_name]  # Numeric version
# Check the error message
⋮----
# Clean up

================
File: tests/test_search_limit.py
================
"""Tests for RedisStore search limits."""
⋮----
@pytest.fixture(scope="function")
def store(redis_url) -> RedisStore
⋮----
"""Fixture to create a Redis store."""
⋮----
store.setup()  # Initialize indices
⋮----
def test_search_with_larger_limit(store: RedisStore) -> None
⋮----
"""Test search with limit > 10."""
# Create 15 test documents
⋮----
# Search with a limit of 15
results = store.search(("test_namespace",), limit=15)
# Should return all 15 results
⋮----
# Verify we have all the items
result_keys = {item.key for item in results}
expected_keys = {f"key{i}" for i in range(15)}
⋮----
def test_vector_search_with_larger_limit(redis_url) -> None
⋮----
"""Test vector search with limit > 10."""
⋮----
# Create vector store with embeddings
embeddings = CharacterEmbeddings(dims=4)
index_config = {
⋮----
# Create 15 test documents
⋮----
# Create documents with slightly different texts
⋮----
# Search with a limit of 15
results = store.search(("test_namespace",), query="sample", limit=15)
# Should return all 15 results
⋮----
# Verify we have all the items

================
File: tests/test_semantic_search_keys.py
================
"""Tests for Redis key parsing with semantic search.
This test verifies that the fix to the Redis key handling works correctly
with the semantic search functionality.
"""
⋮----
# Import the Redis store - we'll use mock when needed
⋮----
class Memory(TypedDict)
⋮----
content: str
metadata: Dict[str, Any]
embedding: List[float]
def create_dummy_embedding(size: int = 4) -> List[float]
⋮----
"""Create a dummy embedding for testing."""
⋮----
class TestSemanticSearchKeyHandling
⋮----
"""Test semantic search key handling without requiring RediSearch."""
def test_parse_complex_keys(self)
⋮----
"""Test that the _parse_redis_checkpoint_writes_key method handles complex keys."""
# This directly tests the fix we made
# Create a key that simulates what would be generated in semantic search
complex_key = f"{CHECKPOINT_WRITE_PREFIX}{REDIS_KEY_SEPARATOR}thread_123{REDIS_KEY_SEPARATOR}memory_ns{REDIS_KEY_SEPARATOR}user_memories{REDIS_KEY_SEPARATOR}task_id{REDIS_KEY_SEPARATOR}0{REDIS_KEY_SEPARATOR}extra_component{REDIS_KEY_SEPARATOR}another_component"
# Parse the key using the patched method
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(complex_key)
# Verify the result contains the expected components
⋮----
# The extra components should be ignored by our fix
def test_semantic_search_key_simulation(self)
⋮----
"""Simulate semantic search operations and key handling."""
# Create a key pattern like those generated in semantic search
namespace = "user_123"
memory_id = "memory_456"
# Mock Redis client
mock_redis = mock.MagicMock()
⋮----
# Create a mock for RedisStore with a mocked Redis client
⋮----
# Mock the RedisStore for testing
store = RedisStore("redis://localhost")
# Create a test memory
memory = {
# Test with tuple key - simulate storing
⋮----
# Test retrieval
retrieved = store.get(namespace, memory_id)
# Verify the retrieved data
⋮----
def test_complex_semantic_search_keys(self)
⋮----
"""Test with more complex keys that would be used in semantic search."""
# Create complex keys with special characters and multiple components
namespace = "user/123:456"
memory_id = "memory/with:special.chars/456"
# Construct a checkpoint key like the ones that would be generated
# This simulates what would happen internally in the checkpointer
checkpoint_key = f"{CHECKPOINT_WRITE_PREFIX}{REDIS_KEY_SEPARATOR}{namespace}:{memory_id}{REDIS_KEY_SEPARATOR}memories{REDIS_KEY_SEPARATOR}search_results{REDIS_KEY_SEPARATOR}task_123{REDIS_KEY_SEPARATOR}0{REDIS_KEY_SEPARATOR}extra{REDIS_KEY_SEPARATOR}components"
# Parse with our fixed method
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(checkpoint_key)
# Verify the components are extracted correctly
⋮----
# The key would have been successfully parsed with our fix
# which is what prevented the original notebooks from working

================
File: tests/test_semantic_search_notebook.py
================
"""Test for the semantic search notebook functionality.
This test makes sure that the key parsing fix works with the semantic search
notebook by simulating its exact workflow.
"""
⋮----
class TestSemanticSearchNotebook
⋮----
"""Tests simulating the semantic search notebook."""
def test_semantic_search_complex_key_parsing(self)
⋮----
"""Test that the key parsing fix works with complex keys from semantic search."""
# Create complex keys that would be generated in semantic search
test_keys = [
⋮----
# Simple key with exact number of parts
⋮----
# Complex key with extra components - this would have failed before our fix
⋮----
# Very complex key with multiple extra components
⋮----
# Key with special characters that would be used in tuple keys
⋮----
# Test parsing each key
⋮----
# This would have failed before our fix for keys with more than 6 components
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# Verify we get back a proper result dict with all required keys
⋮----
# Verify the first key is parsed correctly (exact number of parts)
⋮----
# Verify the semantic search key parsing (extra components)
⋮----
def test_semantic_search_insufficient_key_parts(self)
⋮----
"""Test that we properly raise errors for keys with insufficient parts."""
# Key with insufficient parts
insufficient_key = f"{CHECKPOINT_WRITE_PREFIX}{REDIS_KEY_SEPARATOR}thread_123{REDIS_KEY_SEPARATOR}memory_ns{REDIS_KEY_SEPARATOR}checkpoint_id{REDIS_KEY_SEPARATOR}task_id"
# This should raise a ValueError
⋮----
# Verify the error message mentions the right number of parts
⋮----
def test_semantic_search_incorrect_prefix(self)
⋮----
"""Test that we properly raise errors for keys with incorrect prefix."""
# Key with incorrect prefix
incorrect_prefix_key = f"wrong_prefix{REDIS_KEY_SEPARATOR}thread_123{REDIS_KEY_SEPARATOR}memory_ns{REDIS_KEY_SEPARATOR}checkpoint_id{REDIS_KEY_SEPARATOR}task_id{REDIS_KEY_SEPARATOR}0"
⋮----
# Verify the error message mentions the prefix issue

================
File: tests/test_shallow_async.py
================
@pytest.fixture
async def test_data() -> Dict[str, Any]
⋮----
"""Test data fixture."""
config_1: RunnableConfig = RunnableConfig(
config_2: RunnableConfig = RunnableConfig(
config_3: RunnableConfig = RunnableConfig(
chkpnt_1: Checkpoint = empty_checkpoint()
chkpnt_2: Checkpoint = create_checkpoint(chkpnt_1, {}, 1)
chkpnt_3: Checkpoint = empty_checkpoint()
metadata_1: CheckpointMetadata = {
metadata_2: CheckpointMetadata = {
metadata_3: CheckpointMetadata = {}
⋮----
@pytest.fixture
async def saver(redis_url: str) -> AsyncGenerator[AsyncShallowRedisSaver, None]
⋮----
"""AsyncShallowRedisSaver fixture."""
saver = AsyncShallowRedisSaver(redis_url)
⋮----
"""Test that only the latest checkpoint is stored."""
thread_id = "test-thread"
checkpoint_ns = ""
# Create initial checkpoint
config_1 = RunnableConfig(
checkpoint_1 = test_data["checkpoints"][0]
⋮----
# Create second checkpoint
config_2 = RunnableConfig(
checkpoint_2 = test_data["checkpoints"][1]
⋮----
# Verify only latest checkpoint exists
results = [c async for c in saver.alist(None)]
⋮----
({"source": "input"}, 1),  # Matches metadata.source
({"step": 1}, 1),  # Matches metadata.step
({}, 3),  # Retrieve all checkpoints
({"source": "update"}, 0),  # No matches
⋮----
"""Test search functionality."""
configs = test_data["configs"]
checkpoints = test_data["checkpoints"]
metadata = test_data["metadata"]
⋮----
results = [c async for c in saver.alist(None, filter=query)]
⋮----
"""Test handling of null characters."""
config = await saver.aput(
result = await saver.aget_tuple(config)
⋮----
sanitized_value = "\x00value".replace("\x00", "")
⋮----
"""Test storing writes asynchronously."""
config = test_data["configs"][0]
checkpoint = test_data["checkpoints"][0]
metadata = test_data["metadata"][0]
saved_config = await saver.aput(config, checkpoint, metadata, {})
writes = [("channel1", "value1"), ("channel2", "value2")]
⋮----
result = await saver.aget_tuple(saved_config)
⋮----
found_writes = {(w[1], w[2]) for w in result.pending_writes or []}
⋮----
"""Test sequential writes for consistent overwrite behavior."""
⋮----
# Add initial writes
initial_writes = [("channel1", "value1")]
⋮----
# Add more writes
new_writes = [("channel2", "value2")]
⋮----
# Verify only latest writes exist
⋮----
@pytest.mark.asyncio
async def test_from_conn_string_errors(redis_url: str) -> None
⋮----
"""Test proper cleanup of Redis connections."""
⋮----
saver_redis = s._redis
⋮----
client = Redis.from_url(redis_url)
⋮----
"""Test error conditions for from_conn_string."""
# Test with neither URL nor client provided
⋮----
# Test with invalid connection URL
⋮----
# Test with non-responding client
client = Redis(host="nonexistent", port=6379)
⋮----
# Test with empty URL
⋮----
@pytest.mark.asyncio
async def test_async_shallow_client_info_setting(redis_url: str, monkeypatch) -> None
⋮----
"""Test that client_setinfo is called with correct library information in AsyncShallowRedisSaver."""
⋮----
# Expected client info format
expected_client_info = f"redis-py(redisvl_v{__redisvl_version__})"
# Track if client_setinfo was called with the right parameters
client_info_called = False
# Store the original method
original_client_setinfo = Redis.client_setinfo
# Create a mock function for client_setinfo
async def mock_client_setinfo(self, key, value)
⋮----
# Note: RedisVL might call this with its own lib name first
# We only track calls with our full lib name
⋮----
client_info_called = True
# Call original method to ensure normal function
⋮----
# Apply the mock
⋮----
# Test client info setting when creating a new async shallow saver
⋮----
# Verify client_setinfo was called with our library info
⋮----
@pytest.mark.asyncio
async def test_async_shallow_client_info_fallback(redis_url: str, monkeypatch) -> None
⋮----
"""Test that AsyncShallowRedisSaver falls back to echo when client_setinfo is not available."""
⋮----
# Create a Redis client directly first - this bypasses RedisVL validation
⋮----
# Remove client_setinfo to simulate older Redis version
⋮----
# Track if echo was called with our lib name
echo_called = False
echo_messages = []
original_echo = Redis.echo
# Create mock for echo
async def mock_echo(self, message)
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Test direct fallback without RedisVL interference
⋮----
# Force another call to set_client_info
⋮----
# Print debug info
⋮----
# Verify echo was called as fallback with our library info
⋮----
@pytest.mark.asyncio
async def test_shallow_redis_saver_blob_cleanup(redis_url: str) -> None
⋮----
"""Test that the AsyncShallowRedisSaver properly cleans up old blobs and writes.
    This test verifies that the fix for the GitHub issue is working correctly.
    The issue was that AsyncShallowRedisSaver was not cleaning up old checkpoint_blob
    and checkpoint_writes entries, causing them to accumulate in Redis even though
    they were no longer referenced by the current checkpoint.
    After the fix, old blobs and writes should be automatically deleted when new
    versions are created, keeping only the necessary current objects in Redis.
    """
⋮----
# Set up test parameters
thread_id = "test-thread-blob-accumulation"
checkpoint_ns = "test-ns"
# Create a test config
test_config = {
# Test AsyncShallowRedisSaver to see if it accumulates blobs and writes
⋮----
# Create a client to check Redis directly
redis_client = Redis.from_url(redis_url)
⋮----
# We need to do a few updates to create multiple versions of blobs
⋮----
checkpoint_id = f"id-{i}"
# Create checkpoint
checkpoint = {
metadata = {
# Define new_versions to force blob creation
new_versions = {"messages": f"version-{i}"}
# Save the checkpoint
config = await shallow_saver.aput(
# Add write for this checkpoint
⋮----
# Let's dump the Redis database to see what's stored
# First count the number of entries for each data type
all_keys = await redis_client.keys("*")
# Explicitly print to stdout to ensure visibility
⋮----
# Count the number of blobs and writes in Redis
# For blobs
blob_keys_pattern = f"{CHECKPOINT_BLOB_PREFIX}:*"
blob_keys = await redis_client.keys(blob_keys_pattern)
blob_count = len(blob_keys)
# Get content of each blob key
blob_contents = []
⋮----
blob_data = await redis_client.json().get(key.decode())
⋮----
# For writes
writes_keys_pattern = f"{CHECKPOINT_WRITE_PREFIX}:*"
writes_keys = await redis_client.keys(writes_keys_pattern)
writes_count = len(writes_keys)
# Get content of each write key
write_contents = []
⋮----
write_data = await redis_client.json().get(key.decode())
⋮----
# Print debug info about the keys found
⋮----
# Look at stored checkpoint, which should have the latest values
latest_checkpoint = await shallow_saver.aget(test_config)
⋮----
# Verify the fix works:
# 1. We should only have one blob entry - the latest version
⋮----
# 2. We should only have one write entry - for the latest checkpoint
⋮----
# 3. The checkpoint should reference the latest version
⋮----
# 4. Check that the blob we have is for the latest version
⋮----
# Clean up test data
⋮----
# For comparison, test with regular AsyncRedisSaver
# The regular saver should also accumulate entries, but this is by design since it keeps history
⋮----
# Do the same operations as above
⋮----
# Update test_config with the proper checkpoint_id
config = {
⋮----
saved_config = await regular_saver.aput(
⋮----
# With regular saver, we expect it to retain all history (this is by design)

================
File: tests/test_shallow_sync.py
================
@pytest.fixture
def test_data() -> dict[str, list[Any]]
⋮----
"""Test data fixture."""
config_1: RunnableConfig = {
config_2: RunnableConfig = {
config_3: RunnableConfig = {
chkpnt_1: Checkpoint = empty_checkpoint()
chkpnt_2: Checkpoint = create_checkpoint(chkpnt_1, {}, 1)
chkpnt_3: Checkpoint = empty_checkpoint()
metadata_1: CheckpointMetadata = {
metadata_2: CheckpointMetadata = {
metadata_3: CheckpointMetadata = {}
⋮----
@pytest.fixture(autouse=True)
async def clear_test_redis(redis_url: str) -> None
⋮----
"""Clear Redis before each test."""
client = Redis.from_url(redis_url)
⋮----
@contextmanager
def _saver(redis_url: str) -> Any
⋮----
"""Fixture for shallow saver testing."""
saver = ShallowRedisSaver(redis_url)
⋮----
"""Test that only latest checkpoint is stored."""
⋮----
thread_id = "test-thread"
checkpoint_ns = ""
# Create initial checkpoint
config_1 = {
checkpoint_1 = test_data["checkpoints"][0]
⋮----
# Create second checkpoint
config_2 = {
checkpoint_2 = test_data["checkpoints"][1]
⋮----
# Verify only latest checkpoint exists
results = list(saver.list(None))
⋮----
({"source": "input"}, 1),  # Matches metadata.source
({"step": 1}, 1),  # Matches metadata.step
({}, 2),  # Retrieve latest checkpoints (one per thread)
({"source": "update", "step": 1}, 0),  # No matches
⋮----
"""Test search functionality."""
⋮----
configs = test_data["configs"]
checkpoints = test_data["checkpoints"]
metadata = test_data["metadata"]
# Store checkpoints with different thread IDs
⋮----
search_results = list(saver.list(None, filter=query))
⋮----
def test_overwrite_writes(test_data: dict[str, list[Any]], redis_url: str) -> None
⋮----
"""Test that writes are overwritten, not appended."""
⋮----
config = test_data["configs"][0]
checkpoint = test_data["checkpoints"][0]
metadata = test_data["metadata"][0]
# Store initial checkpoint
saved_config = saver.put(config, checkpoint, metadata, {})
# Add initial writes
initial_writes = [("channel1", "value1")]
⋮----
# Add more writes
new_writes = [("channel2", "value2")]
⋮----
# Verify only latest writes exist
result = saver.get_tuple(saved_config)
⋮----
("my_key", "\x00abc"),  # Null character in value
⋮----
"""Test handling of null characters."""
⋮----
config = saver.put(
result = saver.get_tuple(config)
⋮----
sanitized_key = key.replace("\x00", "")
sanitized_value = value.replace("\x00", "")
⋮----
def test_from_conn_string_with_url(redis_url: str) -> None
⋮----
"""Test creating ShallowRedisSaver with connection URL."""
⋮----
def test_from_conn_string_with_client(redis_url: str) -> None
⋮----
"""Test creating ShallowRedisSaver with existing client."""
⋮----
def test_from_conn_string_with_connection_args(redis_url: str) -> None
⋮----
"""Test creating ShallowRedisSaver with connection arguments."""
⋮----
value = saver._redis.get("test_key")
⋮----
def test_from_conn_string_errors(redis_url: str) -> None
⋮----
"""Test proper cleanup of Redis connections."""
⋮----
saver_redis = s._redis
⋮----
"""Test error conditions for from_conn_string."""
# Test with neither URL nor client provided
⋮----
# Test with invalid connection URL
⋮----
# Test with non-responding client
client = Redis(host="nonexistent", port=6379)
⋮----
# Test with empty URL
# Handle both old and new RedisVL error message formats
⋮----
def test_shallow_client_info_setting(redis_url: str, monkeypatch) -> None
⋮----
"""Test that ShallowRedisSaver sets client info correctly."""
⋮----
# Create a mock to track if client_setinfo was called with our library name
client_info_called = False
original_client_setinfo = Redis.client_setinfo
def mock_client_setinfo(self, key, value)
⋮----
# Note: RedisVL might call this with its own lib name first
# We only track calls with our full lib name
⋮----
client_info_called = True
⋮----
# Apply the mock
⋮----
# Test client info setting when creating a new shallow saver
⋮----
# Verify client_setinfo was called with our library info
⋮----
def test_shallow_client_info_fallback(redis_url: str, monkeypatch) -> None
⋮----
"""Test that ShallowRedisSaver falls back to echo when client_setinfo is not available."""
⋮----
# Create a Redis client directly first - this bypasses RedisVL validation
⋮----
# Remove client_setinfo to simulate older Redis version
⋮----
# Track if echo was called with our lib name
echo_called = False
echo_messages = []
original_echo = Redis.echo
def mock_echo(self, message)
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Test direct fallback without RedisVL interference
⋮----
# Force another call to set_client_info
⋮----
# Print debug info
⋮----
# Verify echo was called as fallback with our library info
⋮----
def test_key_generation_inconsistency(redis_url: str) -> None
⋮----
"""Test for Key generation consistency between base and shallow savers.
    This test verifies that both the base class and shallow saver cleanup patterns
    use the same storage-safe transformations, ensuring cleanup works correctly.
    """
⋮----
thread_id = "test_thread"
checkpoint_ns = ""  # Empty namespace - the problematic case
channel = "test_channel"
version = "1"
# Test blob key generation
base_blob_key = BaseRedisSaver._make_redis_checkpoint_blob_key(
shallow_blob_pattern = (
# The base key uses storage-safe transformations
expected_base_key = f"{CHECKPOINT_BLOB_PREFIX}:test_thread:__empty__:test_channel:1"
⋮----
# The shallow pattern now uses storage-safe transformations (fixed!)
expected_pattern = f"{CHECKPOINT_BLOB_PREFIX}:test_thread:__empty__:*"
⋮----
# Both base key and pattern now consistently use "__empty__" (fix confirmed!)
⋮----
# Test writes key generation
checkpoint_id = "test_checkpoint"
task_id = "test_task"
base_writes_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
shallow_writes_pattern = (
⋮----
expected_base_key = (
⋮----
expected_pattern = f"{CHECKPOINT_WRITE_PREFIX}:test_thread:__empty__:*"
⋮----
def test_incomplete_blob_cleanup(redis_url: str) -> None
⋮----
"""Test for Complete cleanup of blobs in ShallowRedisSaver.
    This test verifies that old blob versions are properly cleaned up
    when putting new checkpoints, now that key generation is consistent.
    """
⋮----
# Create test data
thread_id = f"test_thread_{uuid.uuid4()}"
checkpoint_ns = ""  # Empty namespace - problematic case
checkpoint_id1 = str(uuid.uuid4())
checkpoint_id2 = str(uuid.uuid4())
config1: RunnableConfig = {
config2: RunnableConfig = {
# Create first checkpoint
checkpoint1 = {
metadata1: CheckpointMetadata = {"source": "input", "step": 1}
versions1 = {"test_channel": "1", "another_channel": "2"}
⋮----
# Check keys after first checkpoint
redis_client = Redis.from_url(redis_url, decode_responses=True)
⋮----
all_keys = redis_client.keys("*")
test_keys = [k for k in all_keys if thread_id in k]
blob_keys_after_first = [
# Should have 2 blob keys
⋮----
# Create second checkpoint with different channel versions
checkpoint2 = {
metadata2: CheckpointMetadata = {"source": "loop", "step": 2}
versions2 = {
⋮----
}  # Different versions
⋮----
# Check keys after second checkpoint
⋮----
blob_keys_after_second = [
# This demonstrates the bug: we should only have 2 blob keys (latest versions)
# but we have 4 because cleanup didn't work due to key generation inconsistency
⋮----
# This should pass when the bug is fixed - we should only have latest blob versions
⋮----
def test_pr37_incomplete_writes_cleanup(redis_url: str) -> None
⋮----
"""Test for PR #37: Complete cleanup of writes in ShallowRedisSaver.
    This test verifies that old writes are properly cleaned up
    when putting new writes, now that key generation is consistent.
    """
⋮----
# Add writes for first checkpoint
writes1 = [("channel1", "value1"), ("channel2", "value2")]
⋮----
# Check writes after first checkpoint
⋮----
write_keys_after_first = [
# Should have 2 write keys
⋮----
# Create second checkpoint
⋮----
# Add writes for second checkpoint
writes2 = [("channel3", "value3"), ("channel4", "value4")]
⋮----
# Check writes after second checkpoint
⋮----
write_keys_after_second = [
⋮----
# In a proper shallow implementation, old writes for different checkpoints
# should be cleaned up. This test verifies the cleanup works correctly.
# We expect only the writes from the second checkpoint (2 writes)

================
File: tests/test_store.py
================
"""Tests for RedisStore."""
⋮----
TTL_SECONDS = 2
TTL_MINUTES = TTL_SECONDS / 60
⋮----
@pytest.fixture
def fake_embeddings() -> CharacterEmbeddings
⋮----
"""Create test embeddings for vector search."""
⋮----
@pytest.fixture(scope="function")
def store(redis_url) -> Iterator[RedisStore]
⋮----
"""Fixture to create a Redis store with TTL support."""
ttl_config = {
⋮----
store.setup()  # Initialize indices
⋮----
"""Fixture to create a Redis store with vector search capabilities."""
vector_type = request.param
distance_type = "cosine"  # Other options: "l2", "inner_product"
# Include fields parameter in index_config
index_config = {
⋮----
"fields": ["text"],  # Field to embed
⋮----
ttl_config = {"default_ttl": 2, "refresh_on_read": True}
# Create a unique index name for each test run
unique_id = str(uuid4())[:8]
# Use different Redis prefix for vector store tests to avoid conflicts
⋮----
def test_batch_order(store: RedisStore) -> None
⋮----
"""Test that operations are executed in the correct order."""
# Setup test data
⋮----
ops = [
results = store.batch(ops)
⋮----
assert results[1] is None  # Put operation returns None
⋮----
assert len(results[3]) > 0  # Should contain at least our test namespaces
assert results[4] is None  # Non-existent key returns None
# Test reordered operations
ops_reordered = [
results_reordered = store.batch(ops_reordered)
⋮----
assert len(results_reordered[0]) >= 2  # Should find at least our two test items
⋮----
assert results_reordered[3] is None  # Put operation returns None
⋮----
def test_batch_put_ops(store: RedisStore) -> None
⋮----
"""Test batch operations with multiple puts."""
⋮----
PutOp(namespace=("test",), key="key3", value=None),  # Delete operation
⋮----
# Verify the puts worked
item1 = store.get(("test",), "key1")
item2 = store.get(("test",), "key2")
item3 = store.get(("test",), "key3")
⋮----
def test_batch_search_ops(store: RedisStore) -> None
⋮----
"""Test batch operations with search operations."""
⋮----
test_data = [
⋮----
# First search should find items with tag "a"
⋮----
# Second search should return first 2 items (depends on sorting which could be arbitrary)
⋮----
# Third search should only find items in test/foo namespace
⋮----
def test_batch_list_namespaces_ops(store: RedisStore) -> None
⋮----
"""Test batch operations with list namespaces operations."""
# Setup test data with various namespaces
⋮----
# First operation should list all namespaces
⋮----
# Second operation should only return namespaces up to depth 2
⋮----
# Third operation should only return namespaces ending with "public"
⋮----
def test_list_namespaces(store: RedisStore) -> None
⋮----
"""Test listing namespaces with various filters."""
# Create test data with various namespaces
test_namespaces = [
# Insert test data
⋮----
# Test listing with various filters
all_namespaces = store.list_namespaces()
⋮----
# Test prefix filtering
test_prefix_namespaces = store.list_namespaces(prefix=["test"])
⋮----
# Test suffix filtering
public_namespaces = store.list_namespaces(suffix=["public"])
⋮----
# Test max depth
depth_2_namespaces = store.list_namespaces(max_depth=2)
⋮----
# Test pagination
paginated_namespaces = store.list_namespaces(limit=3)
⋮----
# Cleanup
⋮----
def test_vector_search(vector_store: RedisStore) -> None
⋮----
"""Test vector search functionality."""
# Insert documents with text that can be embedded
docs = [
⋮----
# Search with query
results = vector_store.search(("test",), query="longer text")
⋮----
# Doc2 and doc3 should be closer matches to "longer text"
doc_keys = [r.key for r in results]
⋮----
def test_vector_search_with_filters(vector_store: RedisStore) -> None
⋮----
"""Test vector search with additional filters."""
# Insert test documents
⋮----
# Search for "apple" within red items
results = vector_store.search(("test",), query="apple", filter={"color": "red"})
⋮----
# Doc1 should be the closest match for "apple" with color=red
⋮----
# Search for "car" within red items
results = vector_store.search(("test",), query="car", filter={"color": "red"})
⋮----
# Doc2 should be the closest match for "car" with color=red
⋮----
def test_vector_update_with_score_verification(vector_store: RedisStore) -> None
⋮----
"""Test that updating items properly updates their embeddings."""
⋮----
# Search for a term similar to doc1's content
results_initial = vector_store.search(("test",), query="zany xylophone")
⋮----
initial_score = results_initial[0].score
# Update doc1 to be about dogs instead
⋮----
# The original query should now match doc1 less strongly
results_after = vector_store.search(("test",), query="zany xylophone")
⋮----
after_score = next((r.score for r in results_after if r.key == "doc1"), None)
⋮----
# A dog-related query should now match doc1 more strongly
results_new = vector_store.search(("test",), query="dogs text")
doc1_score = next((r.score for r in results_new if r.key == "doc1"), None)
⋮----
def test_basic_ops(store: RedisStore) -> None
⋮----
"""Test basic CRUD operations."""
namespace = ("test", "documents")
item_id = "doc1"
item_value = {"title": "Test Document", "content": "Hello, World!"}
⋮----
item = store.get(namespace, item_id)
⋮----
# Test update
updated_value = {"title": "Updated Document", "content": "Hello, Updated!"}
⋮----
updated_item = store.get(namespace, item_id)
⋮----
# Test get from non-existent namespace
different_namespace = ("test", "other_documents")
item_in_different_namespace = store.get(different_namespace, item_id)
⋮----
# Test delete
⋮----
deleted_item = store.get(namespace, item_id)
⋮----
def test_search(store: RedisStore) -> None
⋮----
"""Test search functionality."""
# Create test data
⋮----
# Test basic search
all_items = store.search(["test"])
⋮----
# Test namespace filtering
docs_items = store.search(["test", "docs"])
⋮----
# Test value filtering
alice_items = store.search(["test"], filter={"author": "Alice"})
⋮----
paginated_items = store.search(["test"], limit=2)
⋮----
offset_items = store.search(["test"], offset=2)
⋮----
def test_large_batches(store: RedisStore) -> None
⋮----
"""Test handling large numbers of operations."""
# Reduce number of operations for stability
N = 20
ops = []
# Add many put operations
⋮----
# Execute puts first to make sure data exists before querying
put_results = store.batch(ops)
⋮----
# Create operations for gets, search, and list
get_ops = []
# Add get operations
⋮----
# Add search operations
⋮----
# Add list namespaces operations
⋮----
# Execute get, search, and list operations
get_results = store.batch(get_ops)
expected_results_len = N // 5 + N // 10 + 1
⋮----
# Verify gets (they should return Items)
⋮----
result = get_results[i]
⋮----
# Verify searches (they should return lists)
⋮----
# Verify list namespaces (it should return a list)
⋮----
def test_store_ttl(store: RedisStore) -> None
⋮----
"""Test TTL functionality in Redis store."""
# Assumes a TTL of TTL_MINUTES
ns = ("foo",)
# Store an item with TTL
⋮----
# Check item exists and refresh TTL
res = store.get(ns, key="item1", refresh_ttl=True)
⋮----
# Search for the item with refresh
results = store.search(ns, query="foo", refresh_ttl=True)
⋮----
# Do one more get without refreshing TTL
res = store.get(ns, key="item1", refresh_ttl=False)
⋮----
# Wait for the TTL to expire
⋮----
# Force a sweep to remove expired items
⋮----
# Verify item is gone due to TTL expiration
res = store.search(ns, query="bar", refresh_ttl=False)
⋮----
def test_redis_store_client_info(redis_url: str, monkeypatch) -> None
⋮----
"""Test that RedisStore sets client info correctly."""
⋮----
# Expected client info format
expected_client_info = f"redis-py(redisvl_v{__redisvl_version__})"
# Create a direct Redis client to bypass RedisVL validation
client = NativeRedis.from_url(redis_url)
⋮----
# Create a mock to track if client_setinfo was called with our library name
client_info_called = False
original_client_setinfo = NativeRedis.client_setinfo
def mock_client_setinfo(self, key, value)
⋮----
# We only track calls with our full lib name
⋮----
client_info_called = True
⋮----
# Apply the mock
⋮----
# Test client info setting by creating store directly
store = RedisStore(client)
⋮----
# Verify client_setinfo was called with our library info
⋮----
def test_redis_store_client_info_fallback(redis_url: str, monkeypatch) -> None
⋮----
"""Test that RedisStore falls back to echo when client_setinfo is not available."""
⋮----
# Track if echo was called
echo_called = False
original_echo = NativeRedis.echo
# Remove client_setinfo to simulate older Redis version
⋮----
def mock_echo(self, message)
⋮----
# We only want to track our library's echo calls
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Verify echo was called as fallback
⋮----
def test_redis_store_graceful_failure(redis_url: str, monkeypatch) -> None
⋮----
"""Test graceful failure when both client_setinfo and echo fail."""
⋮----
# Simulate failures for both methods
⋮----
# Should not raise any exceptions when both methods fail
⋮----
# Test client info setting by creating store directly
⋮----
def test_vector_storage_json(redis_url, fake_embeddings: CharacterEmbeddings) -> None
⋮----
"""Test JSON vector storage (default behavior)."""
# Test data
⋮----
# vector_storage_type defaults to "json"
⋮----
# Insert documents
⋮----
# Test vector search functionality
results = store.search(("test_json",), query="hello")
⋮----
# Verify both hello documents are found
⋮----
# Test that scores are reasonable (should be > 0 for cosine similarity)
⋮----
# Test retrieval by key still works
item = store.get(("test_json",), "doc1")
⋮----
def test_vector_storage_hash(redis_url, fake_embeddings: CharacterEmbeddings) -> None
⋮----
"""Test hash vector storage for improved memory efficiency."""
⋮----
"vector_storage_type": "hash",  # Enable hash storage
⋮----
results = store.search(("test_hash",), query="hello")
⋮----
item = store.get(("test_hash",), "doc1")
⋮----
def test_vector_search_hash(redis_url, fake_embeddings: CharacterEmbeddings) -> None
⋮----
"""Test vector search functionality with hash storage."""
⋮----
# Insert documents with text that can be embedded
⋮----
# Search with query
results = store.search(("test",), query="longer text")
⋮----
# Doc2 and doc3 should be closer matches to "longer text"
⋮----
"""Test vector search with additional filters using hash storage."""
⋮----
# Insert test documents
⋮----
# Search for "apple" within red items
results = store.search(("test",), query="apple", filter={"color": "red"})
⋮----
# Doc1 should be the closest match for "apple" with color=red
⋮----
# Search for "car" within red items
results = store.search(("test",), query="car", filter={"color": "red"})
⋮----
# Doc2 should be the closest match for "car" with color=red
⋮----
"""Test that updating items properly updates their embeddings with hash storage."""
⋮----
# Search for a term similar to doc1's content
results_initial = store.search(("test",), query="zany xylophone")
⋮----
# Update doc1 to be about dogs instead
⋮----
# The original query should now match doc1 less strongly
results_after = store.search(("test",), query="zany xylophone")
⋮----
# A dog-related query should now match doc1 more strongly
results_new = store.search(("test",), query="dogs text")

================
File: tests/test_streaming_modes.py
================
"""Tests for streaming with different modes using Redis checkpointing.
This test verifies that the streaming functionality works correctly with
different streaming modes when using Redis checkpointing. This uses
mocking to ensure tests work with different API versions.
"""
⋮----
class ChatState(TypedDict)
⋮----
messages: List[Dict[str, str]]
current_response: Optional[str]
def add_user_message(state: ChatState, message: str) -> Dict[str, Any]
⋮----
"""Add a user message to the state."""
⋮----
def add_ai_message(state: ChatState) -> Dict[str, Any]
⋮----
"""Generate and add an AI message to the state."""
# Simple AI response generation for testing
response = f"Response to: {state['messages'][-1]['content']}"
⋮----
def stream_ai_response(state: ChatState) -> Dict[str, Any]
⋮----
"""Stream an AI response one word at a time."""
last_user_message = next(
response = f"Response to: {last_user_message}"
words = response.split()
current = state.get("current_response", "")
⋮----
# Start streaming with first word
⋮----
# Find current position
current_word_count = len(current.split())
⋮----
# Streaming complete, add message to history and clear current
⋮----
# Add next word
⋮----
def router(state: ChatState) -> Literal["stream_ai_response", "END"]
⋮----
"""Route based on current response status."""
⋮----
# Continue streaming
⋮----
class TestStreamingKeyHandling
⋮----
"""Test streaming functionality with Redis checkpointing.
    This class mocks the actual StateGraph to test our key handling.
    """
def test_key_parsing_with_streaming(self)
⋮----
"""Verify that our key parsing fix works with streaming operations."""
# Create a mock for the Redis client
mock_redis = mock.MagicMock()
# Simulate a checkpoint write key for a streaming operation
streaming_key = f"{CHECKPOINT_WRITE_PREFIX}{REDIS_KEY_SEPARATOR}thread_streaming{REDIS_KEY_SEPARATOR}messages{REDIS_KEY_SEPARATOR}stream_123{REDIS_KEY_SEPARATOR}task_456{REDIS_KEY_SEPARATOR}0{REDIS_KEY_SEPARATOR}update{REDIS_KEY_SEPARATOR}2"
# Parse using our fixed method
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(streaming_key)
# Verify the result
⋮----
# The extra components (update, 2) should be ignored by our fix
def test_complex_streaming_keys(self)
⋮----
"""Test with more complex keys that contain additional components."""
# Create a key with many additional components
complex_key = f"{CHECKPOINT_WRITE_PREFIX}{REDIS_KEY_SEPARATOR}thread_complex{REDIS_KEY_SEPARATOR}messages{REDIS_KEY_SEPARATOR}stream_complex{REDIS_KEY_SEPARATOR}task_complex{REDIS_KEY_SEPARATOR}0{REDIS_KEY_SEPARATOR}update{REDIS_KEY_SEPARATOR}3{REDIS_KEY_SEPARATOR}values{REDIS_KEY_SEPARATOR}partial"
# Parse with our fixed method
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(complex_key)
# Verify the components are extracted correctly
⋮----
# Our fix should handle this complex key correctly
def test_streaming_with_mocked_graph(self)
⋮----
"""Test streaming using a mocked StateGraph to avoid API incompatibilities."""
# Create a mock for the StateGraph
mock_graph = mock.MagicMock()
# Set up the mock to return stream chunks
⋮----
# Mock the RedisSaver
mock_saver = mock.MagicMock(spec=RedisSaver)
⋮----
# Run a streaming operation
thread_config = {"configurable": {"thread_id": "test_mock_stream"}}
input_data = {"message": "Hello"}
initial_state = {"messages": [], "current_response": None}
# Call the mocked stream method
results = list(
# Verify we got the expected number of chunks
⋮----
# Verify the final state has complete response
final_state = results[-1]
⋮----
@pytest.mark.asyncio
    async def test_async_streaming_with_mock(self)
⋮----
"""Test async streaming with a mocked async graph."""
# Create a mock for AsyncRedisSaver
mock_async_saver = mock.MagicMock(spec=AsyncRedisSaver)
⋮----
# Create a mock graph with async capability
class MockAsyncGraph
⋮----
async def astream(self, *args, **kwargs)
⋮----
"""Mock async streaming method."""
chunks = [
⋮----
await asyncio.sleep(0.01)  # Small delay to simulate async behavior
mock_async_graph = MockAsyncGraph()
# Run the async streaming operation
results = []
thread_config = {"configurable": {"thread_id": "test_async_mock_stream"}}
⋮----
# Simulate cancellation after 3 chunks
⋮----
# Verify the streaming was working correctly

================
File: tests/test_streaming.py
================
"""Tests for streaming with Redis checkpointing."""
⋮----
class State(TypedDict)
⋮----
counter: int
values: List[str]
def count_node(state: State) -> Dict[str, Any]
⋮----
"""Simple counting node."""
⋮----
def values_node(state: State) -> Dict[str, Any]
⋮----
"""Add a value to the list."""
⋮----
def conditional_router(state: State) -> Literal["count_node", "END"]
⋮----
"""Route based on counter value."""
⋮----
@pytest.fixture
def graph_with_redis_checkpointer(redis_url: str)
⋮----
"""Create a graph with Redis checkpointer."""
builder = StateGraph(State)
⋮----
graph = builder.compile(checkpointer=checkpointer)
⋮----
def test_streaming_values_with_redis_checkpointer(graph_with_redis_checkpointer)
⋮----
"""Test streaming with 'values' mode."""
# Create a thread config with a unique ID
thread_config = {"configurable": {"thread_id": "test_stream_values"}}
# Stream with values mode
results = []
⋮----
# Verify results
assert len(results) == 11  # 5 iterations x 2 nodes + initial state
# Check state history from the checkpointer
states = list(graph_with_redis_checkpointer.get_state_history(thread_config))
⋮----
final_state = states[-1]
⋮----
def test_streaming_updates_with_redis_checkpointer(graph_with_redis_checkpointer)
⋮----
"""Test streaming with 'updates' mode."""
⋮----
thread_config = {"configurable": {"thread_id": "test_stream_updates"}}
# Stream with updates mode
⋮----
# Verify results - we should get an update from each node
assert len(results) == 10  # 5 iterations x 2 nodes
# Check that each update contains the expected keys
⋮----
if i % 2 == 0:  # count_node
⋮----
else:  # values_node
⋮----
@pytest.mark.asyncio
async def test_streaming_with_cancellation(graph_with_redis_checkpointer)
⋮----
"""Test streaming with cancellation."""
⋮----
thread_config = {"configurable": {"thread_id": "test_stream_cancel"}}
# Create a task that streams with interruption
async def stream_with_cancel()
⋮----
# Simulate cancellation after 3 chunks
⋮----
# Expected - just pass
⋮----
# Run the task
task = asyncio.create_task(stream_with_cancel())
await asyncio.sleep(0.1)  # Let it run a bit
results = await task
# Verify results - we should have 3 chunks
⋮----
# We expect some state to be saved even after cancellation
⋮----
# Should be able to continue from the last saved state
last_state = graph_with_redis_checkpointer.get_state(thread_config)
continuation_results = []
⋮----
None, thread_config, stream_mode="values"  # No input, continue from last state
⋮----
# Verify we can continue after cancellation

================
File: tests/test_subgraph_key_parsing.py
================
"""Tests for Redis key parsing with subgraphs.
This test verifies that the fix to the _parse_redis_checkpoint_writes_key method
can handle keys formatted by subgraphs correctly.
"""
⋮----
class State(TypedDict)
⋮----
counter: int
message: str
class NestedState(TypedDict)
⋮----
user: str
history: List[str]
def increment_counter(state: State) -> Dict[str, Any]
⋮----
"""Simple increment function."""
⋮----
def add_message(state: State) -> Dict[str, Any]
⋮----
"""Add a message based on counter."""
⋮----
def build_subgraph()
⋮----
"""Build a simple subgraph to test."""
builder = StateGraph(State)
⋮----
def test_parse_subgraph_write_key()
⋮----
"""Test the key parsing with subgraph keys."""
# Create a complex key with subgraph components - similar to what would
# happen in a real scenario with nested subgraphs
key = REDIS_KEY_SEPARATOR.join(
# Parse the key
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# Verify the result has the expected components
⋮----
# The extra components should be ignored
⋮----
@pytest.fixture
def redis_saver(redis_url: str)
def test_complex_thread_ids(redis_saver)
⋮----
"""Test key parsing with complex thread IDs."""
# Some thread IDs might contain special formatting
complex_thread_id = "parent/subgraph:nested.component-123"
# Create a key with this complex thread ID
⋮----
# Parse the key directly
parsed_key = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# The thread_id would be processed by to_storage_safe_str
# which handles special characters
⋮----
def test_subgraph_state_history(redis_url: str)
⋮----
"""Test for state history with subgraphs."""
# Create main graph with a subgraph
main_builder = StateGraph(NestedState)
# Add the subgraph
subgraph = build_subgraph()
⋮----
# Add edges for the main graph
⋮----
# Create checkpointer
⋮----
# Compile the graph with the checkpointer
main_graph = main_builder.compile(checkpointer=checkpointer)
# Create thread config
thread_config = {
# Run the graph
result = main_graph.invoke(
# Get state history - this would have failed before the fix
⋮----
# Get state history
states = list(main_graph.get_state_history(thread_config))
⋮----
# The test passes if we don't get a "too many values to unpack" error
# which would have happened before our key parsing fix

================
File: tests/test_sync.py
================
@pytest.fixture
def test_data() -> dict[str, list[Any]]
⋮----
"""Test data fixture."""
config_1: RunnableConfig = {
config_2: RunnableConfig = {
config_3: RunnableConfig = {
chkpnt_1: Checkpoint = empty_checkpoint()
chkpnt_2: Checkpoint = create_checkpoint(chkpnt_1, {}, 1)
chkpnt_3: Checkpoint = empty_checkpoint()
metadata_1: CheckpointMetadata = {
metadata_2: CheckpointMetadata = {
metadata_3: CheckpointMetadata = {}
⋮----
@pytest.fixture(autouse=True)
async def clear_test_redis(redis_url: str) -> None
⋮----
"""Clear Redis before each test."""
client = Redis.from_url(redis_url)
⋮----
@contextmanager
def _saver(redis_url: str) -> Any
⋮----
"""Fixture for regular saver testing."""
saver = RedisSaver(redis_url)
⋮----
({"source": "input"}, 1),  # search by 1 key
({"step": 1}, 1),  # search by multiple keys - , "writes": {"foo": "bar"}
({}, 3),  # search by no keys, return all checkpoints
({"source": "update", "step": 1}, 0),  # no matches
⋮----
configs = test_data["configs"]
checkpoints = test_data["checkpoints"]
metadata = test_data["metadata"]
⋮----
search_results = list(saver.list(None, filter=query))
⋮----
("my_key", "\x00abc"),  # Null character in value
⋮----
config = saver.put(
result = saver.get_tuple(config)
⋮----
sanitized_key = key.replace("\x00", "")
sanitized_value = value.replace("\x00", "")
⋮----
def test_put_writes(test_data: dict[str, list[Any]], redis_url: str) -> None
⋮----
"""Test storing writes in Redis."""
⋮----
config = test_data["configs"][0]
checkpoint = test_data["checkpoints"][0]
metadata = test_data["metadata"][0]
# First store a checkpoint
saved_config = saver.put(config, checkpoint, metadata, {})
# Test regular writes
writes = [("channel1", "value1"), ("channel2", "value2")]
task_id = "task1"
⋮----
# Test special writes (using WRITES_IDX_MAP)
special_writes = [("__error__", "error_value"), ("channel3", "value3")]
task_id2 = "task2"
⋮----
# Verify writes through get_tuple
result = saver.get_tuple(saved_config)
⋮----
# Verify regular writes
found_writes = {(w[1], w[2]) for w in result.pending_writes}
⋮----
# Verify special writes
⋮----
"""Test that writes are properly stored in Redis JSON format."""
⋮----
# First store a checkpoint to get proper config
⋮----
writes = [("channel1", "value1")]
⋮----
# Store write
⋮----
# Verify JSON structure directly
thread_id = saved_config["configurable"]["thread_id"]
checkpoint_ns = saved_config["configurable"].get("checkpoint_ns", "")
checkpoint_id = saved_config["configurable"]["checkpoint_id"]
write_key = BaseRedisSaver._make_redis_checkpoint_writes_key(
# Get raw JSON
json_data = saver._redis.json().get(write_key)
# Verify structure
⋮----
def test_search_writes(redis_url: str) -> None
⋮----
"""Test searching writes using Redis Search."""
⋮----
# Set up some test data
# Create initial config with checkpoint and metadata
config = {"configurable": {"thread_id": "thread1", "checkpoint_ns": "ns1"}}
checkpoint = empty_checkpoint()  # Need to import this
metadata = {"source": "test", "step": 1, "writes": {}}
# Store checkpoint to get proper config
⋮----
# Add writes for multiple channels
writes1 = [("channel1", "value1"), ("channel2", "value2")]
⋮----
writes2 = [("channel1", "value3")]
⋮----
# Search by channel
query = "(@channel:{channel1})"
results = saver.checkpoint_writes_index.search(query)
assert len(results.docs) == 2  # One document per channel1 writes
doc1 = json.loads(results.docs[0].json)
doc2 = json.loads(results.docs[1].json)
⋮----
# Search by task
query = "(@task_id:{task1})"
⋮----
# Search by thread/namespace
query = "(@thread_id:{thread1} @checkpoint_ns:{ns1})"
⋮----
assert len(results.docs) == 3  # Contains all three writes
⋮----
doc3 = json.loads(results.docs[2].json)
⋮----
def test_from_conn_string_with_url(redis_url: str) -> None
⋮----
"""Test creating a RedisSaver with a connection URL."""
⋮----
# Verify connection works by creating and checking a key
⋮----
def test_from_conn_string_with_client(redis_url: str) -> None
⋮----
"""Test creating a RedisSaver with an existing Redis client."""
⋮----
# Verify connection works
⋮----
def test_from_conn_string_with_connection_args(redis_url: str) -> None
⋮----
"""Test creating a RedisSaver with connection arguments."""
# Test that decode_responses is propagated to Redis
⋮----
# Check the connection parameter was passed through
⋮----
# Functional test - we should get str not bytes back
⋮----
value = saver._redis.get("test_key")
assert isinstance(value, str)  # not bytes
def test_from_conn_string_cleanup(redis_url: str) -> None
⋮----
"""Test proper cleanup of Redis connections."""
# Test with auto-created client
⋮----
saver_redis = s._redis
# Verify it works during context
⋮----
# Test with provided client
⋮----
# Verify both use the same client
⋮----
# Verify client still works after context
⋮----
def test_client_info_setting(redis_url: str, monkeypatch) -> None
⋮----
"""Test that client_setinfo is called with correct library information."""
⋮----
# Create a mock to track if client_setinfo was called with our library name
client_info_called = False
lib_calls = []
original_client_setinfo = Redis.client_setinfo
def mock_client_setinfo(self, key, value)
⋮----
# Note: RedisVL might call this with its own lib name first
# We only track calls with our lib name
⋮----
client_info_called = True
⋮----
# Apply the mock
⋮----
# Test client info setting when creating a new saver
⋮----
# Call set_client_info directly to ensure it's called
⋮----
# Print debug info
⋮----
# Verify client_setinfo was called with our library info
⋮----
def test_client_info_fallback_to_echo(redis_url: str, monkeypatch) -> None
⋮----
"""Test that when client_setinfo is not available, fall back to echo."""
⋮----
# Create a Redis client directly first - this bypasses RedisVL validation
⋮----
# Remove client_setinfo to simulate older Redis version
⋮----
# Track if echo was called with our lib name
echo_called = False
echo_messages = []
original_echo = Redis.echo
def mock_echo(self, message)
⋮----
echo_called = True
⋮----
# Apply the mocks
⋮----
# Test direct fallback without RedisVL interference
⋮----
# Force another call to set_client_info
⋮----
# Print debug info
⋮----
# Verify echo was called as fallback with our library info
⋮----
def test_client_info_graceful_failure(redis_url: str, monkeypatch) -> None
⋮----
"""Test graceful failure when both client_setinfo and echo fail."""
⋮----
# Simulate failures for both methods
⋮----
# Should not raise any exceptions when both methods fail
⋮----
# Explicitly call set_client_info which should handle the errors
⋮----
def test_from_conn_string_errors() -> None
⋮----
"""Test error conditions for from_conn_string."""
# Test with neither URL nor client provided
⋮----
# Test with empty URL
# Handle both old and new RedisVL error message formats
⋮----
# Test with invalid connection URL
⋮----
# Test with non-responding client
client = Redis(host="nonexistent", port=6379)
⋮----
def test_large_batches(test_data: dict[str, Any], redis_url: str) -> None
⋮----
"""Test handling large numbers of operations with thread pool."""
⋮----
N = 10  # Number of operations per batch
M = 5  # Number of batches
⋮----
# Store configs and futures for debugging
stored_configs = []
futures = []
⋮----
test_config: RunnableConfig = {
⋮----
# Get results from puts
put_results = [future.result() for future in futures]
⋮----
# Verify using configs returned from put
verify_futures = []
⋮----
# Get verification results and add debug output
verify_results = [future.result() for future in verify_futures]
# Debug output for failures
⋮----
@tool
def get_weather(city: Literal["nyc", "sf"]) -> str
⋮----
"""Use this to get weather information."""
⋮----
@pytest.fixture
def tools() -> List[BaseTool]
⋮----
@pytest.fixture
def mock_llm() -> Any
⋮----
"""Create a mock LLM for testing without requiring API keys."""
⋮----
# Create a mock that can be used in place of a real LLM
mock = MagicMock()
⋮----
@pytest.fixture
def mock_agent() -> Any
⋮----
"""Create a mock agent that creates checkpoints without requiring a real LLM."""
⋮----
# Create a mock agent that returns a dummy response
⋮----
# Set the invoke method to also create a fake chat session
def mock_invoke(messages, config)
⋮----
# Return a dummy response that mimics a chat conversation
⋮----
"""Test RedisSaver checkpoint functionality using a mock agent."""
⋮----
# Use the mock agent instead of creating a real one
graph = mock_agent
# Use a unique thread_id
thread_id = f"test-{uuid4()}"
# Test initial query
config: RunnableConfig = {
# Create a checkpoint manually to simulate what would happen during agent execution
checkpoint = {
# Store the checkpoint
next_config = checkpointer.put(
# Verify next_config has the right structure
⋮----
# Test checkpoint retrieval
latest = checkpointer.get(config)
⋮----
# Test checkpoint tuple
tuple_result = checkpointer.get_tuple(config)
⋮----
# Test listing checkpoints
checkpoints = list(checkpointer.list(config))
⋮----
"""
    A regression test for a bug where queries for checkpoints from the
    root graph were failing to find valid checkpoints. When called from
    a root graph, the `checkpoint_id` and `checkpoint_ns` keys are not
    in the config object.
    """
⋮----
thread_id = f"root-graph-{uuid4()}"
# Create a config with checkpoint_id and checkpoint_ns
# For a root graph test, we need to add an empty checkpoint_ns
# since that's how real root graphs work
⋮----
"checkpoint_ns": "",  # Empty string is valid
⋮----
# Verify the checkpoint was stored
⋮----
# Test retrieving the checkpoint with a root graph config
# that doesn't have checkpoint_id or checkpoint_ns
⋮----
# This is the key test - verify we can retrieve checkpoints
# when called from a root graph configuration
⋮----
)  # Initial + LLM + Tool + Final

================
File: .gitignore
================
# Created by https://www.toptal.com/developers/gitignore/api/python,venv,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=python,venv,macos

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml

# ruff
.ruff_cache/

# LSP config files
pyrightconfig.json

### venv ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
pyvenv.cfg
pip-selfcheck.json

libs/redis/docs/.Trash*
.python-version
.idea/*
examples/.Trash*
.claude

================
File: CLAUDE.md
================
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

### Setup and Dependencies

```bash
poetry install --all-extras  # Install all dependencies with poetry (from README)
make redis-start             # Start Redis Stack container (includes RedisJSON and RediSearch)
make redis-stop              # Stop Redis container
```

### Testing

```bash
make test                    # Run tests with verbose output
make test-all               # Run all tests including API tests
pytest tests/test_specific.py  # Run specific test file
pytest tests/test_specific.py::test_function  # Run specific test
pytest --run-api-tests      # Include API integration tests
```

### Code Quality

```bash
make format          # Format code with black and isort
make lint            # Run formatting, type checking, and other linters
make check-types     # Run mypy type checking
make check           # Run both linting and tests
```

### Development

```bash
make clean           # Remove cache and build artifacts
```

## Architecture Overview

### Core Components

**Checkpoint Savers** (`langgraph/checkpoint/redis/`):

- `base.py`: `BaseRedisSaver` - Abstract base class with shared Redis operations, schemas, and TTL management
- `__init__.py`: `RedisSaver` - Standard sync implementation
- `aio.py`: `AsyncRedisSaver` - Async implementation
- `shallow.py` / `ashallow.py`: Shallow variants that store only latest checkpoint

**Stores** (`langgraph/store/redis/`):

- `base.py`: `BaseRedisStore` - Abstract base with Redis operations, vector search, and TTL support
- `__init__.py`: `RedisStore` - Sync store with key-value and vector search
- `aio.py`: `AsyncRedisStore` - Async store implementation

### Key Architecture Patterns

**Dual Implementation Strategy**: Each major component has both sync and async variants that share common base classes. The base classes (`BaseRedisSaver`, `BaseRedisStore`) contain the bulk of the business logic, while concrete implementations handle Redis client management and specific I/O patterns.

**Redis Module Dependencies**: The library requires RedisJSON and RediSearch modules. Redis 8.0+ includes these by default; earlier versions need Redis Stack. All operations use structured JSON storage with search indices for efficient querying.

**Schema-Driven Indexing**: Both checkpoints and stores use predefined schemas (`SCHEMAS` constants) that define Redis Search indices. Checkpoint indices track thread/namespace/version hierarchies; store indices support both key-value lookup and optional vector similarity search.

**TTL Integration**: Native Redis TTL support is integrated throughout, with configurable defaults and refresh-on-read capabilities. TTL applies to all related keys (main document, vectors, writes) atomically.

**Cluster Support**: Full Redis Cluster support with automatic detection and cluster-aware operations (individual key operations vs. pipelined operations).

**Type System**: Heavy use of generics (`BaseRedisSaver[RedisClientType, IndexType]`) to maintain type safety across sync/async variants while sharing implementation code.

### Redis Key Patterns

- Checkpoints: `checkpoint:{thread_id}:{namespace}:{checkpoint_id}`
- Checkpoint blobs: `checkpoint_blob:{thread_id}:{namespace}:{channel}:{version}`
- Checkpoint writes: `checkpoint_write:{thread_id}:{namespace}:{checkpoint_id}:{task_id}`
- Store items: `store:{uuid}`
- Store vectors: `store_vectors:{uuid}`

### Testing Strategy

Tests are organized by functionality:

- `test_sync.py` / `test_async.py`: Core checkpoint functionality
- `test_store.py` / `test_async_store.py`: Store operations
- `test_cluster_mode.py`: Redis Cluster specific tests
- `test_*_ttl.py`: TTL functionality
- `test_key_parsing.py`: Key generation and parsing logic
- `test_semantic_search_*.py`: Vector search capabilities

### Important Dependencies

- Requires Redis with RedisJSON and RediSearch modules
- Uses `redisvl` for vector operations and search index management
- Uses `python-ulid` for unique document IDs
- Integrates with LangGraph's checkpoint and store base classes

================
File: LICENSE
================
MIT License

Copyright (c) 2023 Redis, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================
File: Makefile
================
.PHONY: install format lint test test-all clean redis-start redis-stop check-types check

install:
	poetry install --all-extras

redis-start:
	docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

redis-stop:
	docker stop redis-stack

format:
	poetry run format
	poetry run sort-imports

check-types:
	poetry run check-mypy

lint: format check-types
	
test:
	poetry run test-verbose

test-all:
	poetry run test-verbose --run-api-tests

check: lint test

clean:
	find . -type d -name "__pycache__" -exec rm -rf {} +
	find . -type d -name ".pytest_cache" -exec rm -rf {} +
	find . -type d -name ".mypy_cache" -exec rm -rf {} +
	find . -type d -name ".coverage" -delete
	find . -type d -name "htmlcov" -exec rm -rf {} +
	find . -type d -name "dist" -exec rm -rf {} +
	find . -type d -name "build" -exec rm -rf {} +
	find . -type d -name "*.egg-info" -exec rm -rf {} +
	find . -type d -name "_build" -exec rm -rf {} +

================
File: pyproject.toml
================
[tool.poetry]
name = "langgraph-checkpoint-redis"
version = "0.0.8"
description = "Redis implementation of the LangGraph agent checkpoint saver and store."
authors = ["Redis Inc. <applied.ai@redis.com>"]
license = "MIT"
readme = "README.md"
repository = "https://www.github.com/redis-developer/langgraph-redis"
keywords = ["ai", "redis", "redis-client", "vector-database", "agents", "langgraph", "langchain"]
classifiers = [
  "Programming Language :: Python :: 3.9",
  "Programming Language :: Python :: 3.10",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3.12",
  "Programming Language :: Python :: 3.13",
  "License :: OSI Approved :: MIT License",
]
packages = [{ include = "langgraph" }]

[tool.poetry.dependencies]
python = ">=3.9,<3.14"
langgraph-checkpoint = ">=2.0.26"
redisvl = ">=0.5.1,<1.0.0"

[tool.poetry.group.dev.dependencies]
langgraph = ">=0.3.0,<0.5.0"
black = "^25.1.0"
codespell = "^2.2.0"
pytest = "^7.2.1"
anyio = "^4.4.0"
pytest-asyncio = "^0.21.1"
pytest-xdist = {extras = ["psutil"], version = "^3.6.1"}
pytest-mock = "^3.11.1"
mypy = ">=1.11.0,<2"
aioconsole = "^0.8.1"
langchain-openai = "^0.3.2"
testcontainers = "^4.9.1"
isort = "^6.0.0"
cryptography = { version = ">=44.0.1", markers = "python_version > '3.9.1'" }

[tool.pytest.ini_options]
# --strict-markers will raise errors on unknown marks.
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
#
# https://docs.pytest.org/en/7.1.x/reference/reference.html
# --strict-config       any warnings encountered while parsing the `pytest`
#                       section of the configuration file raise errors.
addopts = "--strict-markers --strict-config --durations=5 -vv"
asyncio_mode = "auto"

[tool.poetry.scripts]
format = "scripts:format"
check-format = "scripts:check_format"
sort-imports = "scripts:sort_imports"
check-sort-imports = "scripts:check_sort_imports"
check-lint = "scripts:check_lint"
check-mypy = "scripts:check_mypy"
test = "scripts:test"
test-verbose = "scripts:test_verbose"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.black]
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
exclude = '''
(
  | \.egg
  | \.git
  | \.hg
  | \.mypy_cache
  | \.nox
  | \.tox
  | \.venv
  | _build
  | build
  | dist
  | setup.py
)
'''

[tool.mypy]
# https://mypy.readthedocs.io/en/stable/config_file.html
disallow_untyped_defs = true
explicit_package_bases = true
warn_no_return = false
warn_unused_ignores = true
warn_redundant_casts = true
allow_redefinition = true
ignore_missing_imports = true
disable_error_code = "typeddict-item, return-value, union-attr, operator, assignment"

================
File: README.md
================
# LangGraph Redis

This repository contains Redis implementations for LangGraph, providing both Checkpoint Savers and Stores functionality.

## Overview

The project consists of two main components:

1. **Redis Checkpoint Savers**: Implementations for storing and managing checkpoints using Redis
2. **Redis Stores**: Redis-backed key-value stores with optional vector search capabilities

## Dependencies

### Python Dependencies

The project requires the following main Python dependencies:

- `redis>=5.2.1`
- `redisvl>=0.5.1`
- `langgraph-checkpoint>=2.0.24`

### Redis Modules Requirements

**IMPORTANT:** This library requires Redis with the following modules:

- **RedisJSON** - For storing and manipulating JSON data
- **RediSearch** - For search and indexing capabilities

#### Redis 8.0+

If you're using Redis 8.0 or higher, both RedisJSON and RediSearch modules are included by default as part of the core Redis distribution. No additional installation is required.

#### Redis < 8.0

If you're using a Redis version lower than 8.0, you'll need to ensure these modules are installed:

- Use [Redis Stack](https://redis.io/docs/stack/), which bundles Redis with these modules
- Or install the modules separately in your Redis instance

Failure to have these modules available will result in errors during index creation and checkpoint operations.

## Installation

Install the library using pip:

```bash
pip install langgraph-checkpoint-redis
```

## Redis Checkpoint Savers

### Important Notes

> [!IMPORTANT]
> When using Redis checkpointers for the first time, make sure to call `.setup()` method on them to create required indices. See examples below.

### Standard Implementation

```python
from langgraph.checkpoint.redis import RedisSaver

write_config = {"configurable": {"thread_id": "1", "checkpoint_ns": ""}}
read_config = {"configurable": {"thread_id": "1"}}

with RedisSaver.from_conn_string("redis://localhost:6379") as checkpointer:
    # Call setup to initialize indices
    checkpointer.setup()
    checkpoint = {
        "v": 1,
        "ts": "2024-07-31T20:14:19.804150+00:00",
        "id": "1ef4f797-8335-6428-8001-8a1503f9b875",
        "channel_values": {
            "my_key": "meow",
            "node": "node"
        },
        "channel_versions": {
            "__start__": 2,
            "my_key": 3,
            "start:node": 3,
            "node": 3
        },
        "versions_seen": {
            "__input__": {},
            "__start__": {
                "__start__": 1
            },
            "node": {
                "start:node": 2
            }
        },
        "pending_sends": [],
    }

    # Store checkpoint
    checkpointer.put(write_config, checkpoint, {}, {})

    # Retrieve checkpoint
    loaded_checkpoint = checkpointer.get(read_config)

    # List all checkpoints
    checkpoints = list(checkpointer.list(read_config))
```

### Async Implementation

```python
from langgraph.checkpoint.redis.aio import AsyncRedisSaver

async def main():
    write_config = {"configurable": {"thread_id": "1", "checkpoint_ns": ""}}
    read_config = {"configurable": {"thread_id": "1"}}

    async with AsyncRedisSaver.from_conn_string("redis://localhost:6379") as checkpointer:
        # Call setup to initialize indices
        await checkpointer.asetup()
        checkpoint = {
            "v": 1,
            "ts": "2024-07-31T20:14:19.804150+00:00",
            "id": "1ef4f797-8335-6428-8001-8a1503f9b875",
            "channel_values": {
                "my_key": "meow",
                "node": "node"
            },
            "channel_versions": {
                "__start__": 2,
                "my_key": 3,
                "start:node": 3,
                "node": 3
            },
            "versions_seen": {
                "__input__": {},
                "__start__": {
                    "__start__": 1
                },
                "node": {
                    "start:node": 2
                }
            },
            "pending_sends": [],
        }

        # Store checkpoint
        await checkpointer.aput(write_config, checkpoint, {}, {})

        # Retrieve checkpoint
        loaded_checkpoint = await checkpointer.aget(read_config)

        # List all checkpoints
        checkpoints = [c async for c in checkpointer.alist(read_config)]

# Run the async main function
import asyncio
asyncio.run(main())
```

### Shallow Implementations

Shallow Redis checkpoint savers store only the latest checkpoint in Redis. These implementations are useful when retaining a complete checkpoint history is unnecessary.

```python
from langgraph.checkpoint.redis.shallow import ShallowRedisSaver
# For async version: from langgraph.checkpoint.redis.ashallow import AsyncShallowRedisSaver

write_config = {"configurable": {"thread_id": "1", "checkpoint_ns": ""}}
read_config = {"configurable": {"thread_id": "1"}}

with ShallowRedisSaver.from_conn_string("redis://localhost:6379") as checkpointer:
    checkpointer.setup()
    # ... rest of the implementation follows similar pattern
```

## Redis Checkpoint TTL Support

Both Redis checkpoint savers and stores support Time-To-Live (TTL) functionality for automatic key expiration:

```python
# Configure TTL for checkpoint savers
ttl_config = {
    "default_ttl": 60,     # Default TTL in minutes
    "refresh_on_read": True,  # Refresh TTL when checkpoint is read
}

# Use with any checkpoint saver implementation
with RedisSaver.from_conn_string("redis://localhost:6379", ttl=ttl_config) as checkpointer:
    checkpointer.setup()
    # Use the checkpointer...
```

This makes it easy to manage storage and ensure ephemeral data is automatically cleaned up.

## Redis Stores

Redis Stores provide a persistent key-value store with optional vector search capabilities.

### Synchronous Implementation

```python
from langgraph.store.redis import RedisStore

# Basic usage
with RedisStore.from_conn_string("redis://localhost:6379") as store:
    store.setup()
    # Use the store...

# With vector search configuration
index_config = {
    "dims": 1536,  # Vector dimensions
    "distance_type": "cosine",  # Distance metric
    "fields": ["text"],  # Fields to index
}

# With TTL configuration
ttl_config = {
    "default_ttl": 60,     # Default TTL in minutes
    "refresh_on_read": True,  # Refresh TTL when store entries are read
}

with RedisStore.from_conn_string(
    "redis://localhost:6379", 
    index=index_config,
    ttl=ttl_config
) as store:
    store.setup()
    # Use the store with vector search and TTL capabilities...
```

### Async Implementation

```python
from langgraph.store.redis.aio import AsyncRedisStore

async def main():
    # TTL also works with async implementations
    ttl_config = {
        "default_ttl": 60,     # Default TTL in minutes
        "refresh_on_read": True,  # Refresh TTL when store entries are read
    }
    
    async with AsyncRedisStore.from_conn_string(
        "redis://localhost:6379", 
        ttl=ttl_config
    ) as store:
        await store.setup()
        # Use the store asynchronously...

asyncio.run(main())
```

## Examples

The `examples` directory contains Jupyter notebooks demonstrating the usage of Redis with LangGraph:

- `persistence_redis.ipynb`: Demonstrates the usage of Redis checkpoint savers with LangGraph
- `create-react-agent-memory.ipynb`: Shows how to create an agent with persistent memory using Redis
- `cross-thread-persistence.ipynb`: Demonstrates cross-thread persistence capabilities
- `persistence-functional.ipynb`: Shows functional persistence patterns with Redis

### Running Example Notebooks

To run the example notebooks with Docker:

1. Navigate to the examples directory:

   ```bash
   cd examples
   ```

2. Start the Docker containers:

   ```bash
   docker compose up
   ```

3. Open the URL shown in the console (typically <http://127.0.0.1:8888/tree>) in your browser to access Jupyter.

4. When finished, stop the containers:

   ```bash
   docker compose down
   ```

## Implementation Details

### Redis Module Usage

This implementation relies on specific Redis modules:

- **RedisJSON**: Used for storing structured JSON data as native Redis objects
- **RediSearch**: Used for creating and querying indices on JSON data

### Indexing

The Redis implementation creates these main indices using RediSearch:

1. **Checkpoints Index**: Stores checkpoint metadata and versioning
2. **Channel Values Index**: Stores channel-specific data
3. **Writes Index**: Tracks pending writes and intermediate states

For Redis Stores with vector search:

1. **Store Index**: Main key-value store
2. **Vector Index**: Optional vector embeddings for similarity search

### TTL Implementation

Both Redis checkpoint savers and stores leverage Redis's native key expiration:

- **Native Redis TTL**: Uses Redis's built-in `EXPIRE` command
- **Automatic Cleanup**: Redis automatically removes expired keys
- **Configurable Default TTL**: Set a default TTL for all keys in minutes
- **TTL Refresh on Read**: Optionally refresh TTL when keys are accessed
- **Applied to All Related Keys**: TTL is applied to all related keys (checkpoint, blobs, writes)

## Contributing

We welcome contributions! Here's how you can help:

### Development Setup

1. Clone the repository:

   ```bash
   git clone https://github.com/redis-developer/langgraph-redis
   cd langgraph-redis
   ```

2. Install dependencies:

   ```bash
   `poetry install --all-extras`
   ```

### Available Commands

The project includes several make commands for development:

- **Testing**:

  ```bash
  make test           # Run all tests
  make test-all       # Run all tests including API tests
  ```

- **Linting and Formatting**:

  ```bash
  make format        # Format all files with Black and isort
  make lint          # Run formatting, type checking, and other linters
  make check-types   # Run mypy type checking
  ```

- **Redis for Development/Testing**:

  ```bash
  make redis-start   # Start Redis Stack in Docker (includes RedisJSON and RediSearch modules)
  make redis-stop    # Stop Redis container
  ```

### Contribution Guidelines

1. Create a new branch for your changes
2. Write tests for new functionality
3. Ensure all tests pass: `make test`
4. Format your code: `make format`
5. Run linting checks: `make lint`
6. Submit a pull request with a clear description of your changes
7. Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages

## License

This project is licensed under the MIT License.

================
File: scripts.py
================
def format()
def check_format()
def sort_imports()
def check_sort_imports()
def check_lint()
def check_mypy()
def test()
⋮----
test_cmd = ["python", "-m", "pytest", "-n", "auto", "--log-level=CRITICAL"]
# Get any extra arguments passed to the script
extra_args = sys.argv[1:]
⋮----
def test_verbose()
⋮----
test_cmd = ["python", "-m", "pytest", "-n", "auto", "-vv", "-s", "--log-level=CRITICAL"]

================
File: test_key_parsing_focus.py
================
"""Focused test for Redis key parsing fix.
This verifies only the key parsing changes that address the specific issue that was 
causing the "too many values to unpack (expected 6)" error in the notebooks.
"""
⋮----
def test_key_parsing_handles_extra_components()
⋮----
"""Test that the fixed key parsing method can handle keys with more than 6 components."""
# Create various Redis key patterns that would be seen in different scenarios
# Standard key with 6 components (the original expected format)
standard_key = REDIS_KEY_SEPARATOR.join([
# Key from subgraph state access with 8 components
subgraph_key = REDIS_KEY_SEPARATOR.join([
# Key from semantic search with 7 components
search_key = REDIS_KEY_SEPARATOR.join([
# Parse each key with the fixed method
standard_result = BaseRedisSaver._parse_redis_checkpoint_writes_key(standard_key)
subgraph_result = BaseRedisSaver._parse_redis_checkpoint_writes_key(subgraph_key)
search_result = BaseRedisSaver._parse_redis_checkpoint_writes_key(search_key)
# All results should contain exactly the same 5 keys
⋮----
# The values should match the first 6 components of each key
⋮----
parts = key.split(REDIS_KEY_SEPARATOR)
⋮----
# Verify that additional components are ignored
⋮----
# Key with fewer than 6 components should raise an error
invalid_key = REDIS_KEY_SEPARATOR.join([

================
File: test_key_parsing_suite.py
================
"""Comprehensive test suite for Redis key parsing fix.
This suite combines all tests into a single file to verify
our fix for the Redis key parsing issue works in all scenarios.
"""
⋮----
def test_standard_key_parsing()
⋮----
"""Test that standard Redis keys with exactly 6 components work correctly."""
# Create a standard key with exactly 6 components
key = REDIS_KEY_SEPARATOR.join([
# Parse the key
result = BaseRedisSaver._parse_redis_checkpoint_writes_key(key)
# Verify the result structure
⋮----
def test_key_with_extra_components()
⋮----
"""Test that keys with extra components are parsed correctly."""
# Create a key with extra components (8 parts)
⋮----
# Parse the key with the fixed method
⋮----
# Verify that only the first 6 components are used
⋮----
# Verify that extra components are ignored
⋮----
def test_subgraph_key_pattern()
⋮----
"""Test that keys with subgraph components are parsed correctly."""
# Create a key with a pattern seen in subgraph operations
⋮----
# Verify parsing works correctly
⋮----
def test_semantic_search_key_pattern()
⋮----
"""Test that keys with semantic search components are parsed correctly."""
# Create a key with a pattern seen in semantic search operations
⋮----
def test_insufficient_components()
⋮----
"""Test that keys with fewer than 6 components raise an error."""
# Create a key with only 5 components
⋮----
# Attempt to parse the key - should raise ValueError
⋮----
def test_incorrect_prefix()
⋮----
"""Test that keys with an incorrect prefix raise an error."""
# Create a key with an incorrect prefix




================================================================
End of Codebase
================================================================
