mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-18 13:45:44 +02:00
Compare commits
113
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89b0be3a7d | ||
|
|
7ecda42b42 | ||
|
|
bd99471705 | ||
|
|
0e4dbb4c62 | ||
|
|
145220f2a8 | ||
|
|
f9c25bba07 | ||
|
|
7ecad39ecb | ||
|
|
6bfc6be307 | ||
|
|
6b9369876f | ||
|
|
c26b0e78b6 | ||
|
|
1763dd69a7 | ||
|
|
f4bd023ab1 | ||
|
|
d402bf7379 | ||
|
|
e8a73e1505 | ||
|
|
d6492ef048 | ||
|
|
38d9b39f6e | ||
|
|
be8b4a1d7f | ||
|
|
71fbd6a8b4 | ||
|
|
5375af7827 | ||
|
|
dd7ac00953 | ||
|
|
cd64075928 | ||
|
|
144ee31546 | ||
|
|
8bb84c7096 | ||
|
|
e83660885b | ||
|
|
c2a57385c0 | ||
|
|
3626478029 | ||
|
|
13c9bfa282 | ||
|
|
b6fe3937fc | ||
|
|
5805e5709a | ||
|
|
aab6fdf3f3 | ||
|
|
be1d035aba | ||
|
|
6e228f8a9c | ||
|
|
0adbd89d9a | ||
|
|
47c37d140f | ||
|
|
fc887f7a5d | ||
|
|
bbfc2b7370 | ||
|
|
2c984dfe4e | ||
|
|
09c0d9cec3 | ||
|
|
31734fb792 | ||
|
|
eed577ee2a | ||
|
|
bba00506ea | ||
|
|
fa12538a4e | ||
|
|
c1a7bb8902 | ||
|
|
14b500d8a8 | ||
|
|
053a501db3 | ||
|
|
bbe8eacf8d | ||
|
|
931d39124c | ||
|
|
a7bb96da98 | ||
|
|
1e9a372dd7 | ||
|
|
5030f1e89c | ||
|
|
50cb387904 | ||
|
|
003b1883c9 | ||
|
|
17aebb6239 | ||
|
|
d12830e95c | ||
|
|
29b70cbf39 | ||
|
|
6b86fbb0a8 | ||
|
|
4426552c26 | ||
|
|
671f268651 | ||
|
|
e2554c9616 | ||
|
|
f1ee650489 | ||
|
|
b7e4656c91 | ||
|
|
65a41942ef | ||
|
|
c767d86c9c | ||
|
|
01331ef858 | ||
|
|
24a4c67c52 | ||
|
|
c7e43f86d4 | ||
|
|
b18d266f2c | ||
|
|
536ee7b37a | ||
|
|
901273c0e2 | ||
|
|
7bdbd62611 | ||
|
|
0b74e25f72 | ||
|
|
ff843ab005 | ||
|
|
f7fae7e140 | ||
|
|
651ee8bd24 | ||
|
|
54bdba2da9 | ||
|
|
7e5806cb0b | ||
|
|
66f674fead | ||
|
|
f029d615e6 | ||
|
|
bd76773b31 | ||
|
|
9b6e6d67dc | ||
|
|
ecfb3e3850 | ||
|
|
ec71cd6fc1 | ||
|
|
aab4f0b164 | ||
|
|
2db6d9d7b5 | ||
|
|
5db71a32bf | ||
|
|
0f5df797af | ||
|
|
4c0f855329 | ||
|
|
5dc3f14e8b | ||
|
|
f25ea9ecaf | ||
|
|
9940a9f833 | ||
|
|
d175d5e4a8 | ||
|
|
74adbb744b | ||
|
|
fe63440d98 | ||
|
|
0d50c62283 | ||
|
|
a9073241b2 | ||
|
|
0a04262f05 | ||
|
|
381796c474 | ||
|
|
09ae5ce05d | ||
|
|
59e7751a8c | ||
|
|
b9816e31ff | ||
|
|
3ea7f9469a | ||
|
|
24e61fafa9 | ||
|
|
b52b32b38e | ||
|
|
5aefa5dc8c | ||
|
|
2b370cf3b7 | ||
|
|
839bea0f33 | ||
|
|
cfb121ee8f | ||
|
|
d27beeed18 | ||
|
|
fba5ca7ab9 | ||
|
|
f141089811 | ||
|
|
38487cb158 | ||
|
|
8534212a25 | ||
|
|
e39255a792 |
@@ -17,21 +17,11 @@ jobs:
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
- "3.13"
|
||||
core-version:
|
||||
- "latest"
|
||||
ff-send-v2:
|
||||
- "false"
|
||||
include:
|
||||
- python-version: "3.11"
|
||||
core-version: ">=0.2.42,<0.3.0"
|
||||
- python-version: "3.11"
|
||||
core-version: "latest"
|
||||
ff-send-v2: "true"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: libs/langgraph
|
||||
name: "test #${{ matrix.python-version }} (langchain-core: ${{ matrix.core-version }}, ff-send-v2: ${{ matrix.ff-send-v2 }})"
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
@@ -51,14 +41,9 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
poetry install --with dev
|
||||
if [ "${{ matrix.core-version }}" != "latest" ]; then
|
||||
poetry run pip install "langchain-core${{ matrix.core-version }}"
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
env:
|
||||
LANGGRAPH_FF_SEND_V2: ${{ matrix.ff-send-v2 }}
|
||||
run: |
|
||||
make test_parallel
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ jobs:
|
||||
"libs/cli",
|
||||
"libs/checkpoint",
|
||||
"libs/checkpoint-sqlite",
|
||||
"libs/checkpoint-duckdb",
|
||||
"libs/checkpoint-postgres",
|
||||
"libs/scheduler-kafka",
|
||||
]
|
||||
@@ -44,12 +43,12 @@ jobs:
|
||||
name: cd ${{ matrix.working-directory }}
|
||||
strategy:
|
||||
matrix:
|
||||
working-directory: [
|
||||
working-directory:
|
||||
[
|
||||
"libs/cli",
|
||||
"libs/checkpoint",
|
||||
"libs/checkpoint-sqlite",
|
||||
"libs/checkpoint-duckdb",
|
||||
"libs/checkpoint-postgres"
|
||||
"libs/checkpoint-postgres",
|
||||
]
|
||||
uses: ./.github/workflows/_test.yml
|
||||
with:
|
||||
@@ -76,7 +75,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: "3.11"
|
||||
- name: Run check_sdk_methods script
|
||||
run: python .github/scripts/check_sdk_methods.py
|
||||
|
||||
@@ -133,7 +132,16 @@ jobs:
|
||||
|
||||
ci_success:
|
||||
name: "CI Success"
|
||||
needs: [lint, lint-js, test, test-langgraph, test-scheduler-kafka, integration-test, test-js]
|
||||
needs:
|
||||
[
|
||||
lint,
|
||||
lint-js,
|
||||
test,
|
||||
test-langgraph,
|
||||
test-scheduler-kafka,
|
||||
integration-test,
|
||||
test-js,
|
||||
]
|
||||
if: |
|
||||
always()
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ] || ([ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == "refs/heads/main" ]); then
|
||||
echo "Running link check on all HTML files matching notebooks in docs directory..."
|
||||
poetry run pytest -v \
|
||||
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://(api|web|docs|academy)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://x.com/.*" \
|
||||
--check-links-ignore "https://github\.com/.*" \
|
||||
--check-links-ignore "http://localhost:8123/.*" \
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
if [ -n "${CHANGED_FILES}" ]; then
|
||||
echo "Running link check on HTML files matching changed notebook files..."
|
||||
poetry run pytest -v \
|
||||
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://(api|web|docs|academy)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "http://localhost:8123/.*" \
|
||||
--check-links-ignore "http://localhost:2024.*" \
|
||||
--check-links-ignore "http://127.0.0.1:.*" \
|
||||
|
||||
@@ -42,10 +42,13 @@ NOTEBOOKS_NO_EXECUTION = [
|
||||
"docs/docs/tutorials/storm/storm.ipynb", # issues only when running with VCR
|
||||
"docs/docs/tutorials/lats/lats.ipynb", # issues only when running with VCR
|
||||
"docs/docs/tutorials/rag/langgraph_crag.ipynb", # flakiness from tavily
|
||||
"docs/docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # Cannot create a consistent method resolution error from VCR
|
||||
"docs/docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # flakiness only when running in GHA
|
||||
"docs/docs/tutorials/rag/langgraph_self_rag.ipynb", # flakiness only when running in GHA
|
||||
"docs/docs/tutorials/rag/langgraph_agentic_rag.ipynb", # flakiness only when running in GHA
|
||||
"docs/docs/how-tos/map-reduce.ipynb", # flakiness from structured output, only when running with VCR
|
||||
"docs/docs/tutorials/tot/tot.ipynb",
|
||||
"docs/docs/how-tos/visualization.ipynb"
|
||||
"docs/docs/how-tos/visualization.ipynb",
|
||||
"docs/docs/tutorials/llm-compiler/LLMCompiler.ipynb"
|
||||
]
|
||||
|
||||
|
||||
@@ -127,7 +130,18 @@ def add_vcr_to_notebook(
|
||||
uses_langsmith = True
|
||||
|
||||
# Add import statement
|
||||
vcr_import_lines = [
|
||||
vcr_import_lines = []
|
||||
if uses_langsmith:
|
||||
vcr_import_lines.extend([
|
||||
# patch urllib3 to handle vcr errors, see more here:
|
||||
# https://github.com/langchain-ai/langsmith-sdk/blob/main/python/langsmith/_internal/_patch.py
|
||||
"import sys",
|
||||
f"sys.path.insert(0, '{os.path.join(DOCS_PATH, '_scripts')}')",
|
||||
"import _patch as patch_urllib3",
|
||||
"patch_urllib3.patch_urllib3()",
|
||||
])
|
||||
|
||||
vcr_import_lines.extend([
|
||||
"import nest_asyncio",
|
||||
"nest_asyncio.apply()",
|
||||
"import vcr",
|
||||
@@ -157,16 +171,8 @@ def add_vcr_to_notebook(
|
||||
"",
|
||||
"custom_vcr.register_serializer('advanced_compressed', AdvancedCompressedSerializer())",
|
||||
"custom_vcr.serializer = 'advanced_compressed'",
|
||||
]
|
||||
if uses_langsmith:
|
||||
vcr_import_lines.extend(
|
||||
# patch urllib3 to handle vcr errors, see more here:
|
||||
# https://github.com/langchain-ai/langsmith-sdk/blob/main/python/langsmith/_internal/_patch.py
|
||||
"import sys",
|
||||
f"sys.path.insert(0, '{os.path.join(DOCS_PATH, '_scripts')}')",
|
||||
"import _patch as patch_urllib3",
|
||||
"patch_urllib3.patch_urllib3()",
|
||||
)
|
||||
])
|
||||
|
||||
import_cell = nbformat.v4.new_code_cell(source="\n".join(vcr_import_lines))
|
||||
import_cell.pop("id", None)
|
||||
notebook.cells.insert(0, import_cell)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
eNrtVwt0FNUZxsQKpaUgyqNFcLpomyOZzczuJtkkLphsQkgg5LEhDwiGuzN3s5PMY5nHJksMryDlJTo5PCo9YCAh0RAiCrWACYo8jAZiEQUiFbVqtSqS+qqoQO+d3ZhN4ZyeVj2nnsOcs8nMvf/9n9//3/9f2uyHssJJ4g2tnKhCGTAq+lDqljbLcL4GFXVZkwBVr8Q25mS78hs0meu5y6uqPiUxJgb4OLPkgyLgzIwkxPjpGMYL1Bj07uOhwabRLbGB1yJbqk0CVBRQBhVTIjGn2sRISJaoog9TsaQRQIYEEAlY5YOySgCVkCVN5cQyAhCaAmXCUATxI1QJLfkho0qygn6QkGSiEroJBQKZ8ZpLRAI9+V44gAYLA5yoEKzEaAISqxAy5IEKWYNfGV6JJnwyUlsloFjGiRDKSHo00oklAIsdBGQO8EgzFTAVSkjOLAUS6r/J8kj92ioE1tgLMZnk4xjFTGSjT7mSU2A0NgyrToZUN0UTJlniIXaJElBUKJhqookBnir0SkQlx/OG0BR0DFkkA49KeDgkneAMYcTMqTOCy1PCWWI3mmrmohVBYiGPl8p8KmmTSIETOYMSKj6kMyxFJggAC6w2qQGfcbpckcRShfFCAWDS8E9MFvbKQoWROR82Hx/MQ3GEYVGUA9jlWE1BQiqjMEA/EFWCBSpQJE1moOEIFAuMBM6AC2L67e61ZKRzfihehRTGK0mG4w0sQYJT8Xs/VjByBkDJEAxFTcAINYVt4HV0rjR4zoR9qHJq0K2p/Zrh5ZC7FBXDx1RTY7h1vsbJkDW4hhkygI3hplzsnnA2krscaYFXAMty2CrA54R7xgN4BWIhIhCuwQerweBAqrIGDWXQCgRC30ksCgqIHVA1GZ+nzFRNsxcCFgH+3KARjV4UI71tYJY/DhgGIuRAkZFYZKW+s2wB54smWOjBKdWC8CpCo4boLRUQ+kjAo/g0BU/pu4DPx3MMwPsxGEatIXyT2Oirt1twUEkjRfU92UiJ5IyYnAAqRyJBm2MTzPG7qkgFJzePygvJA6RPk8/Yfzp8w4eyFjEhQ6VObwoebgunkRR9exZgsl0DWOKQ69uBLMTZdoevy5qocgLUm505V4sLbfaLs5pp2pzwxADGSkBk9O1GIPaFb3ghj0JCBouu/rQbqsCMy6o5rKyafSj54Z8GyISqHCAZCYnWt1JtfW7lUT1TvXpDgtX+aF+G1zahY6qmLG1EIYTHOptDtXlb9vT+6I9pRNiGeke+hnBC24hMVJ8tlCWWoOMTKXsibSfSs/JbnSEx+deM3hP5MhAVD7ImrQ8tzYxXEysg2+K8Jk46ME6QNVh9VLpIdCOgLCZDWumtRWRe8FYiM1J3B0FJSnIZELkFhlj9MYwB5C5O3BPaRrUEs0TCSUHRG6xUfFtopy88LcguiqQpkqL3V5EoGyDPCRzynfE3dA0idFgp9Oy9mkKVKqCo6I/RsVTwORBOI6PSiEosEt/PyZKAnvZrU33LzYaJEijb/oF0CgzTqcEiKHuv3g/x2EYprVV9xCTH6j13oI/SBCvN0gxtod2UNRb94q1xVjft8SSwFktCrIfaFywcpIqj55NkFd1RDLr41YDeEy2AKpyPDisda41Dtiahe4fhNRa6NHeqhI1QktBdCnkJsI87p5JOgC4H0mUATm9OLZ6ZnJXhbHEhJZ2SVMHButduiCwtZTylbsHhVbiAP1dMp83JXt5jL/BlCKIrvTwuWc0syJoaSJFnl+fN5qnYYs1O0khre7zNloBezZSZNtNkWVlqBlMk+3IqK3jA5Fam8rJWmV9U6VSycktTAmx6ZnZeQd4sZ5Erc3qZtzxu2ux4uhxkFc0v4rLESq88W82TK7IKCyg1S81jrfksSFfnJyNrgOp1xCTh9gRVcsURSgkSpQQZTAhrX0IkoR4D+8BhHlg1k4hpqGfKFvlAEuHCzoToPyraLk6FjpmSCHvWIR9ofo515M7iirwsR7lFl5bjTBdVl1mU4gGdZqHLrMXJwgKn1y9ML7WKUlaYE+xUPEmF/BBH2ewGDvtV/x+1eqqIDM9wMtu4dFEcRUkROY+nyQVllEV6C8NLGosuABk2oZjnJRfrexIo2mMFFsh6KGBnaDeZVpi3q4/bt/WgEd8ezYBHGPMz+m6v1WFKtNmspiRCAA57nI2ijBZySVPwTj1yw2O3rx4yyHgi0e/KlTV5XeJZakT7h5NWOOYsOf/ihm1zqr3D931WtMd846qSDD6qjijsPtT7zJh1t01PHkmMGe/xT7V2Oe5cs6QuxzJi/G9PJG1eqJ05d2Xyx8fmXv7wzKtHvvj6095e5UrVP88/f3JCQS74cFHU+2naRP6Cre5Jz7b702eOnRt/cWONXtYj0CM71j/a0Kqt88SdX5/7d3/H8Ze1LeKq1ft3Z85bXHcpYyzzkW0hM3PiP27OfOreDsKx/Nk5yz5JPDy2asfEiLtvHLVi3NJXZwytGHJ87CV10AsrBydVLRkcNbx3sTbqoeoT499szw/wy59lZKu35uBLp8e+9lT32cSSLyedi2pIKQy8c3DTl133nDrYueWWo5fXar8rHPZs4GxSS+e5hpXL//CR/5PaQJd1Ton9ycOfp+zpKrxwZtbIlZPGj/464mDa7+QN3V3v1ub7k+9Y/bJ0qP09x6jCpktrLs0YsmRtTkF3Q/5ftbcCx5j2gw8ebZs+IdJ5z6L8iEl577/tnMZ/Wd/ctbj31iFft+Q2pV1o39TdMH/WgbSHP12w8afTul9YZMQjclDC9oec0Sg43+sEsfn6BPG9TBDIM9hVWCjuuJAAT1B/QoCCJAeuDw3Xh4bvMjTcfH1o+NEODU2M0ZHpPZ/8nzdkP0CrdPXAZIn97wam0f9hYEr4kQ5MFgv14xqY4r/3gcltZRBs4qwJVhuMtdti4+Ogx80ytjjG5kFdbuwPPzB990bcarG5rewP2ognBxvxUYe37HzOv77z1M7k2Z/l12/cljx3iG1fXZGrZGnJncN+fqWj/qOioZsiOt6ndls3FX9M3xKxZnnm4g/u2HHu7De1z5+acMB/+eShTze/PtlxX/VXVVcuPv3xM1Xa4rS8P18ec9fC3A7XK9Wr+c7N3Kgcy29Oz7p42e8afcSfNNLB7ck4taD7Zy9Wvz3jwL1tq+8r/tuO42mv67ljt46cYvn1MvvaN9ad5L4Ztr7IfO9DE5h1ZIo2fmRERM+yxWtOpG5YC2h91W0N4xpur6y/s2uaM2dvbfngBx/9fQo7dG/tyZLRu365bcaJ1+ed/8mUuUpReU19TXtvYTvdfbjtF1ET9332Uuc7gM1+eMSU5w8c2jH0zcjPt1Q0Re5n994lvrSQ2hlwx49Zu3L75pXiqiPj/H+cuuHo0Z7n7sl4deuWxpP+d+uHHV/BF3x1gZICj6wed/ryg5dSo+7fknt6cn3cB9pbXPS8xOcWPTB34903LV/x1omo1p5504ZvfUKvdTxy0+H3Os+cfmA+edPtxy5O/lXpxopXtv/ljVuHPun6YnCwER88fE0viYLzLzZBOXA=
|
||||
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
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
|
||||
+1
@@ -0,0 +1 @@
|
||||
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
|
||||
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
eNrtVU9vG0UUV8UXGY2QkJDXXttrO1mUQxSFFtGooAYk1Earyezb3Wl2Z7Yzs3HcyAdCrxyWTwAkSqqoBQ6IC1TiyIEvEO58D97s2g2EoFa9ICEsWV6/v7/33u+9PTrbB22EkjeeCmlBM27xj/ni6EzDwwqMfXxagM1UfHxzc/u40uLizcza0oS9HitF1xTCZt2cyZRnTMguV0VPyESd7Kp49vNZBizG8I/PPzKgvfUUpK2/d9aNn1fOen633+0PVr9d5xxK621KrmIh0/pZ+kiUHRJDkjMLp626/o6VZS44cxh7D4yS5xtKSmgw1+d7AKXHcrEPTzSYEsuAz06NZbYyRycYF3795awAY1gKX995fwnu81PEhk2oz6p9wZWW32BMi0i92yBTm9XHwcroKV/I7KyEv8N47vAZ4zlPrXJv86BUBrxbbYb6q7efccYz8Hirr59I5TWSkxirq5+/q0WHDCbkLpRk4A8C0l8J+6PQ75ObW9s/Xgm+nudq6t3RIhWy/vKna7Vbzcxc5gtyrcGGhhjrESw39YnVFRx/LFh9jsMgqVJpDj9ccdtiB26A9fHY98/Wc+vd3ef1RTcbrtEwCIb0HVKwtcFodeD7ficbeoPVaxTXg71sU0ObT3FmGjnw243fD+mCnTSkfnfSDXzaodh5wNFGcFAK3cwgsqIAGsoqzzt0l1meReiP5I2w4YlIaXhIK/QoqtyKkmkbgYxLhYSnoau9Qw1nOURVGT004hFEmD9NQdOw76q51EqbaQRrolwggVE9XipjNZWRhKK0s0vvALUu3NK6ifVCEO3OLBgaDvzVSX808OcdKiTSVXKIkPWpcbBxZXApLURMRLiPeobQ2W4O8RK50mnEEVTTh1iYhTLBwbq6MjWNrM2jSiwdLO44VihAR3G16F/MZk22XMnU7Q8GCBqwmdJ2IegHCNAA09jdKximSu+Z0oU1XJUQOUxC7oumvCWSYWSs0rh7f/Wez//51Ky97NTgF6Wmp/Oih6G9UiucQM+dDGP/UzdoJQj+v0H/wg164/YhbVkWZcxkeIdGfhAMWNIfDmHcH03GMAlGwzEfj0ZjDgn0E8b7wYSz2B8OkmA4meyOfR4MxzDmMd8dA16wgkmRIEPdygncg3v0Ba1R25LY4BNKLP5s4M8HjXAbD4zjIt3BM8hxJ3GdkQyICqeKiCuOK4Yee1Om2/ux4Bo+33ulXLcqBLfVOr1uzjboy4pbWHXo66axS4+QfqIqwjQQJgkzRrgjakmiNGnuCm6Ix6SZgpsosczsmS7Ba0BsBmjl5u8UpQAkBlEJ0YDDB7x6pFm5A0usIm2ExmcZtUveS8gMc8dKvmXJnlTTRt+adsiDylhi2AyFzF4xXCLQAMSAW2yXvGAHoqgKjBATd0r+FM5h4cJA9778cJE/JIdLKHNyX260YFG6gO2E641zSN3LpaxstM+0cOfXMYIuvd38WxfX/mVjI+xggawIaeK160Dn+Nl55VBzfGPAAcNojc3O/A+x+ufw
|
||||
eNqFVM1u20YQbnvsrW9AED0VorSkaClS4YPhGkl/jBSIGuS2WJIjcpvl7mZ3aVsOfKibF+AbJLEgBUbSBAnQS2ugxx76Au6hz9IhbTmI7KIAL9yZ+eb7Zr7d4+UeGMuV/Pgllw4MSx3+2Pp4aeBRBdY9WZTgCpWd3N6ZnFSGn39eOKftuNdjmndtyV3RFUzmacG47Kaq7HE5VfNEZbM/lgWwDOGfnP5gwQRbOUhXv2uy27pAz3qkG3VD8norTUG7YEemKuMyr1/lh1x3vAymgjlYXITrN0xrwVPWUOz9aJU83VZSQku5Pn0IoAMm+B68MGA1qoCfF9YxV9njOeLCX38uS7CW5fD87rcrbv989Nm51+BbGyCaM0oEW0Ko/WDbQIaEORO2njtTwa9rabvsoNFUnwwIedkcYnYwmWm4TnT+FcqozyYVdLww9r5h0otItOGFwzEZjPuxd3t38vpB0DQQwc6B5qigfkbePgiwkGUqDya4Ggi+zupzL86GJIFpmkYbybTfj2KShDEM4mx0Kx5G/WTxvWF5yeoXUgUpSwtYrhDPJkXV8UjYEghHQ+IRMm6/hsBvN47hruE5l/XT5ZZwwb29tD7vFv1NfxzHff9Lr2Sb0cYoIoR0in4QjW4I/LIazXcgc1fguIbR7ze22m2dhsK/OFuLowBlIbhzsTNMeLXdCFvF67e6SnDgHWx7EOCCNwfk5hbvEeb3mZnViwt5J/c5q0/DbujlSuUCWv/+hO4x6Ma/P3n62L+8Jv7YJ91b3RHxOz6X6C6ZAkWT5tYfP/YToRJqncL5AwXJEgGZP26801mPYVNAsHt9BMpwxxYchQNWagGWlpVwXDPj1kH+PwPvGN5iB5RxihfYzNYTlMlpaqD1Js24vQxO0eYY1WxW4q7WizSqV5IJitX2epXt/5dqC8ykxbXTQu1T5wSt+OrINf6mjoOhWWUu2bFZO1ahZN7ca6yP0WhNuXGXB2F81PH3lXlodQNgU6WhYUm53OMO7BXHQ+syiu+TxovQbPJDTgiSMIdMcd/46mGinPK8aV5Z+GDamVb4Ul4pSZkAWmn6yPJD5I9uysEgLdISXUWlK3DkmaWC48uH4cEqmKl9SSWU2s3eV8cYbeBW2S3W1QFNZq2wiIyG4UZEjo4+/Rds6Sw0
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
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
|
||||
@@ -1 +1 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
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
|
||||
@@ -1 +1 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
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
|
||||
+1
@@ -0,0 +1 @@
|
||||
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
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
eNrtVnt0FNUZT4iItS1ibZBqhGHByiOzmdn3JiwQNgHyfmwgBAjp3Zm7u0Pmxcxskg3dHkRBEDgwVg8VoTwSNg8DRENRkJCipYoIBSpKACNqESkN2tZzKBSkd2Y3kBywHFp72p52/ti9c+93f9833/f7HgsbqqAkMwIf38LwCpQApaAX+ZmFDRKcF4Sy8mSEg0pAoOsLCzwldUGJ6RwTUBRRTk1JASJjFETIA8ZICVxKFZlCBYCSgtYiC3WYeq9Ah04kLJ9v4KAsAz+UDanYrPkGSkC6eAW9GNwskGXGF8KUAMQYHl1meD+GVEshDMgYZNC+hAGvEFSwagj0N0HCeEExGpIxgySwUIORQ7ICOUM4GeuDHmCG95YKylAyhMvRDifQkNW2/KKCm41WXAlKXkGT5dEuif5FIAGWhWyFIghsBYXWmvE+wMoQncqKBAHXawNpF5H7EIymiTDatT39ZkBgKG1vvkEJiboZviCvu1lTd32tCfCA0wWK0ddqloZjIDG33el9dExDmZIYMSZhoBlKGTUaw8djPKzGkMlKCNP20DGQQrN5/ZwDooiCcF3shgAKEKMwgGVqIY35JIHDABaTxgTvXEgpj8mzeQw9oyphKBmrAmwQjsZEwEhyDJzRSOZl4e3QUewZH1bFgNQoII25sPnh6NqHCFCJ0NEFrAcvJqaLzqosR9JVMY1jxlRWA8kv305jNaKazkLNiy7d9B7LNVSkSztE31UtSMg+yR/kEMswlpEVI4ZNRjbBGqBRPxXTVYwSeOhCTFKqBZdptCFGKZRNKOH60CHqOV1AQvkkKQyMCkhaGPUV5IMa2WYZYimgCQv6QiNzDxAiJYpElDZa+jISpPVbUaDycDhcHm4IQEAjE1bWBwRZUbf2zeJtgKIgSgmIMpFGYOoWfy0jJmM09LFAgc0ot3iou09troRQxJHzqmAkekttRVxgGQpo5ylzZYFvieUirpl483GzlpA4qgu8om4vQEakZ6UUhlC54THSaHEYidYaXFYAw7OofOAsQPZERP38td4HIqAqEQgeK2VqJHp5a28ZQVY35wGqwNMHEkhUQN0MJM5maeu9LwV5heGg2uAuvFld7PCGOrORJI3Ol/oAyyGeUjfr1eGVPpehIoVwSkAY6kZia49/WMj7lYBa5yQcjRKURVQ84RMRdE0JygvrUSzgO281xIropoKcniB2xQ2uz0BxUdtLAsFkzGTDPFDETITJgpHmVLMj1WTHpuSVtLhjakpuGYaXSiTAyz4UisyesDdQgSBfCelm9y0D3q4FHH2NZj4qrjisEQUZ4jGr1JYZeHG0feBZGW1RduGC5Ac8U6urVZu0YKJ2wfDbY8eI+xokUo5zslpHOu1bYyc9fm5G30XgJIET5E6N6hSilWa4KEgKLkMKNSclpHYmc6BG45TLTFrNNoIg0rS+wgZp6Al6MwQO6ZTTMFGCrADoXTU4KtqQZTgGBUH/jTU+xBcSXSZevVlCESohL6uNViL67OktIkFNg/YZ14HqnejZfWuhHiyLJuO02Xb1FZNhL4PqbJz86s3nMYhNhNxS0yOMM7TaORK9VFCUxQa9JGE3QZPVgVxo8tIQWKy000yYnCbbNvdk3A2oAMQ9OtvUhoyy/PS8LHezB2G7BaGSgc+ciE+oqKB8FV7ONZUMmhxUqT8kmGd6PcBsD2baAlXuSdmiAubJ2aaKzIq5/pklM6aWVOOk3WQ3W+1Owo6TRsJIGkmczzZP9ipBoSS9ekqmiQSZQpkxB+YRUzmhFHpKC2vt6d4CxTY5M32S6PbTNXk5FTnQVkqnV9bkskwu7eAn2QRolvM90y0ZxqpqC3Q63EUonqguulLSUA0WUdmTXbF8wFE+4Fo2WFKJnmxIw2idBS5j39qXhk1Fk00Bz4bSUBohOkH0jwq2B/UYVz6q5p3PIh8Eqxja5a8QpOmF7nmiZ14tk50+NehVRJZliqqr+JoQsBc7Q4WWLJnLKbMU9XKCzWLBiZgfbITFoZPnhun/oFU7ZuC90xsv0Ls9iiMvyDzj80U8UEIppDZTrBCkURmXYATFvDi9TN3uoJxmYHfYoc8BLBbCgWeWFrf2oF0vBvVaD9Bnuccj0R6zL75l2LJ74vQnQSk+wJ8kBu0+P3aJ6+Tj7JTBm1748+67Rh/dtOfdRRMPVD6P74P7E71vftHx4LNJWeTMofd4n2ozJ10gv9fv3btN8fvypaIy15xTzRfP1lwr/OGHA3fvUVe2rb50pbbj1fm1F7cR935n5rXXF06YUdp/2vQRY034bMezyzeeH7ku0jayfelv724MPJF6OPvAJ53TTJ+e2MIkFUT2P79zxLQNT+VeWfLkoI+P/2FvXefpoab7V44fk1ibkvNky2sj73Ic3djv6PwzZQ8tLrpvMD0kr5Wbu2VA3APndq+aefH17tSBh8KFxeev/LH7r9KclI5TQy9cO+7flT/4YhtsujJxGxduXbGR4542bojvOtb2bXL4L99q/ugnm9ccKScWxZ17e9ekN/Zak0pPv96eSI/FujbO2vut8UX5RzoOTRiQ7jj13pzfy5POXm5aNtvDPLLlaubC1mmTL5VfKLz86aOnn2dffER5qH/jhKuDZ0x57szac/Y1h0XT+h1XN6yPDM07NOSBHNfIzddGnHmYblozrHtxo7Uxt7N6Sdrqg+a9w8ZRzwnPjT5S1rBr3tL8IxUoLNeuJcQt3bnkQzwhLu4bnOY7Q39nmP8nxu3bD9ThG01v0P9nl3//7BKh9N6gdv7pP7w1/AuK9k1zG2ki72xuS7zN3Ob4b53brNb/wbnNYfrG5zabhXQCh9dmpcxeB3RYCeQTr4WyQ8pBOyzA8bVz2zcwDwCf12u6s3lg1o15QG8+yqr0nDcmDlr0+aXET37+cNYsN2vchJWcy8oYpLZ+kPvTYwfzT63b8IsvO3KesTUtWZ3UdfLzMwdP9o+bdNbQf2P2/ubmvJpwXUdpy5n+XSlv//jSxJW+r3ZHjnFf3j9haVbO4cvfTRzWJGdO41ZZO7umD3yUeiE1z7d/XP7goqM/SDwTmNnS6Fy7/s3hdj4PWNbtG/DyyC2Hl31/Tr+4BeUXubQTSR0Z9x75XWPJZ+3JhfVvjIj76K3tT6/qqvzN0Zd/tjxiOBB47USOZfGhH2Wt31C/KO2z+OPFCbk7T81Zt3CgfcVqbt7JIfLhi+4H3Jndge77vtogbD+2552k7IIdV1/0N2eevZCw4P0LH3/6/v2bqkqOrSXb67iEB8Nrhjz21XnyJUvduPYrK/dLdNOKCZ25X3Zvx3+9INB4/INRV94zT++cfLJfebdiB6/8avxf4qPt+2BDxRfp/eLi/gY7b6A0
|
||||
@@ -0,0 +1 @@
|
||||
eNrtVgt0FNUZJsYa6hEMx6hBpBm3KiFmNjP73oQFwkZICGGXbMiDR+Pdmbu7k8yLmdlNdmMCyKMoap1ADuXwqiQksA0BBMJDwVN7RFTwqKiciNoKUmttiwgqtVB6Z3YjiXj02EOfp/ecZOfe/7v//9///VBXBEoyI/Ap3QyvQAlQCtrIrQ91SXBeGMrK4k4OKiGB7vB6fBXtYYnpywkpiijn5+UBkTEKIuQBY6QELi9C5lEhoOShb5GFOpsOv0BH3069q8nAQVkGQSgb8rHZTQZKQLJ4BW0MbhbIMhOIYkoIYgyPLjN8EEOipSgGZAwy6FzCgF8IK1gDBPpOkDBeUIyGXMwgCSzU2MhRWYGcoTkXG8Q9xNw1EBWWoWRonotOOIGGrHYUFBXcIuggKItIZ1gbECQOaNebDEpU1C/WyQJfK1MhyAENOnCrwXjA6bBypCWSgBA0lCmJETUraASaoZTssRg+HuNhAwY5UYli2hkiAyk6h9fpHBBF9PivYFcAyDCMwgCWiUEaC0gChwEsicYEfx2klDHyHB5DK7seRnOxCGDDcCwmAkaSk8wZzbl+Fn4Xd2RzJoBFGJCfYEhjLqypOfGNDIPVI+7oAtbPLwnTobPr5yJ0JCkxJ6e+AUhB+bskNiAX697XrOjSVe/XXOOKZGlE9K4GQUL6ScEwh7yLsYysGDFsMtIJNgIt5PIxXUS2wEMXmYspDYLLNFZzhqxI6Bz5IQBYGWoHV1zX7+GEGTW0KKGglhRGD9YmFDvIp/oX5MOcFr+GZBxqYEH/0CKqn5EmjA8ampv1kJoXZiRI67cSjOY26xSEgoDrV6m5KwQBjRLxvSHpHSFBVtSewcm1DVAURJEKUYLQiL26NRhjxFyMhgEWKDCOQp6HunXVeD2EIo5sG4GdiVvqdhQqLEMBjZ6nxW53MkVwTemryXEtT3CUrryi7vIgJQpL8rxRVAV4jDRanUb79kZcVgDDsyircRYgfTpFnf70QIIIqHrEBE9WGLUzcblnIEaQ1U1lgPL4BrEEEhVSNwGJs1l2DjyXwrzCcFDtcnuvFpckXhFnNpKk0bljEGM5ylPqJt3o+wYSQpBFTscTtU592g8VYNSqmXFANTOKQJLhnkEyoSJFcUpAotUniZ5+s7KQDyohtd1BkJv7y8qiTnRNCcsPdSAXwiOHu5IlcaOn9Ir3b+8oQu5UD1SEUZiSFmwq4DETYbJipDOfsOWTTmxKWUW3Oymm4hu9t6NCArwcQK+5vz9auqhQmK+HdNz9jXFyQIsT9BpNfVQqcdgoCjLEk1qp3dV4eaIZ4CVFOxNBiQtSEPBMTBerbtFiAJmL4XclySiJNJZIOM7JarvFbu5JUvrdE0fvInCSwAlyfyMuoWezDMcg2+n/k90HRQdJoLX3aoQi1ENeVjebicQ6OBAiofRGNQZJ/4pRhxOtZ74Z1M/LpGGcNuv+wTAZDlCo3cbJe6+mJ1lsJOTuxn4wztBq391oU+uwkWaLyeawkIAknYQfOGnCbydNAbOJsFkt9L5EicIVzXWiICm4DCnUbJWo2pfLgUYtGV1m0mq2oZcWaH2SDdPQF/YXCdob5AJMlCArAHqbezLuBqi84T492tSuoprphWUl7rgPKekWhHoGtr6dklpbSwVq/ZzLZyGExmKxEsRsQpXHL1RPiZaIfrFRcBClhd6wN2yu9UyOKMXhWA1O2s02h4O02p04aSSMpJHEayP24hp7qJzjeKkiNlMiYnUhnptOVnrIUqU8UhOaVVwEgWyx+mpramI1ZVbbvAYfNW2SuypQWWeurSyBDicLpxVJs5SZRTN87jI+bLM1oNegAuvKK0CVXUT1U3Yl8wFH+YAnssHcnw0FGK3bwGUcXDILsGI0p3h4NlqA+TRjQvSLOowPdS7XdNQj+lYiG4QjDO0ivffb5eqZVUWEPIme54PmqWRkhhwkI3SxMQLMM0EwWoM2bNjoGGAEwkriRNIONsLi0KPwiur/oFa91fjA9MY9+gyB/MgLMs8EAp0+KKEUUuMUK4RpVP0l2Il8Xl5Yo+5yEmQA2gmn2eG0EQEHgd9fVb69n9tXxaBDax1dgEUxFqHUnSGzy5BvsZgNBWikcDlsFoLQx7aFnYlO9nzK+qzlQ4foKxX9Xb78aKvnZ8eI9OfP/CXjrbWjVkbW8G2jJp28vvjiLWmPGLa5So7k/4jyVHvfPPP42s6y+A8eLh1+bo29RTC9MG0ocUdOSnxFd0w4t+rgpU/P/23dpYvnQxP4qb/b83FV/oOfbGjZbN8G2jLOfzL61tqKvIzP3jOMkoLd1009ZAk3z99z4szx05darncxu0reir2yZ6XwyFFb3ufRHWsWjx65ZETOWestk5adulB227pDtd6H/eEt5zPb2RHx+hXp6Yc7bzycWUZ/lFlprvgg893ff9m4YGLqqZR4anpD5nBx1uc3sEuzV8y0Vxe86Tn+Ar57/KvutRd2v5cZ9FelrVTnxLJN0cyPxt5y8kSmtPfssPHeM/sCsaJVN/S8PPbuI/XrPuL903L/VK6k2E/VPZb72GPL8YWtcber9MO2lwpHzVrAjvhy3+pnj45Ps/dc2nDW7L3R1d7TPJF7g/vtPvyB/P3zd89dNe6Opcve75pRvfNXT7QsPjR5+OcP3pw+f8qhg/yj9eSFB16Z8tqF/bkr32iAC2cfnt6XpTshdchmNzthNPLINRzV+yq/ZVL/PrP0/8ebf/t48y1zSiel9wG179P/8DbwTyjQX5/RNjps329Eu+1/dUQz2f57RjSH6ZqPaKTDaTVbCZMDWM201QZIivBbaSvpB5C20v5/wYh2DVo/baMCgWvY+rd8vfWXF/IniPRnPr71hGv2kjtP7DhV+qp70fWh5bfhT7hnDL1pX+tq75ylHW/vXXj5J96n8JFlC+YoP+8beTT3C2fqxK3isGy74aVDqy8u+qWwYXzlH46tOnMSnA0HsiZcPtC864uql7OOCY6SGw8+PWbkzfEvWkcFs+t6Xbjr4eIXw/vXrttWeez5Tzf++I5KurU7Qm6QWyrXHj3nb7v9w6b3l+7uWFB/YXXKkHENv8l4LSf2nDVjRerr3qEb752T9dMhr+4sGfpIurLsuclrb+qgR70Y2bLtxcyWtOMX0zq2i6kHhvS+M4m+p3cV9WRXfJjhdO9WsunBhmn+vZH1Z55bP+FkW28+O9Vj2F42f5g6OvJHY+qFN628Z+ovzr2zt3pyC7bmpryY/U7bOPHYJRMhZQQWZxWNCWzamL3rh+1LnsyQ3HXe4Sdu3fr6OMNTM05f/vUzH7x7/K3eluWXJhaXGFtz1o3ZdJp5Hxzx7X/2+KHH7/vzdRR2eXZpTvu9G9YXzrhn1rENNyzllr2xsq3XGv0wPD+r7b7bl2TU9WaHHu8z/jUtMQWsTqv4LBs55+9S/KUT
|
||||
@@ -1 +0,0 @@
|
||||
eNrtVnt4FNUVDy9RHkKNFIrkc7KVV8xsdnY3+4iNNSxJSCImZAMhJDHcnbm7O+zszGQeu9nEBQkY5PXhSJE+REsICUlDeAoYCD5QW0HF+EBiqIiolJe1KmIJQu/MbiAptH72o9/Xh/PH7p17zv2dM+f+zr2/6oYAFESaY/s006wEBUBK6EV8vLpBgOUyFKWF9X4oeTmqLi/XWbBOFuiOBK8k8WJKUhLgaT3HQxbQepLzJwWIJNILpCQ05hmowdS5OCr0fr+1VTo/FEXggaIuBSuu0pEcisVK6EXnYIAo0u4QJnkhRrNoMc16MBRaCGFAxCCN5gUMuDhZwoIQaG+cgLGcpNclYjqBY6AKI4ZECfp14USsF3oQJTRe1LC7F9MsJrp7LpVFKOjCpWjGz1GQUac8vISb9Mm4JAsuTvVl0SyB/nkgAIaBTJnEcUwZicbqF7kBI0JkFSUBAn+PCZQSj2qKYNRIBr1VndNWejmaVOeqdFKI19Jwy6xWezXclbHqwAK/5pCPSqBmGo6CRGv5fdcjMwVFUqD5qIeOoklpwkQMvxdjYRBDKUshTJ1DZiCESljN7gc8j3bmittVB1RRWqIBQ1dCCnMLnB8DWNQb41xzIIl2oITF0DPBB0OJWAAwMpyI8YAWxCg4rTLPxcDvQkeEoN1YgAYpEUAKS8WqwpGxG7HCh9DVDe7Gi7pprsW+UuQdiEZMSPAFgeARvytiEPFPo49axVQt9e7MVVQUSzWi7wpyAspP8Mh+RD2MoUVJj2EZKCdYAdR+SMG0EBM4FqYiJklBLtU4URelFGox1IW96BCpnOYgoCYTJBpGHAR1G7URZGWVbMW6KLVVZ04bqGTuBkKkRDsRoY3a07QAKW1VBKg0HA6Xhhu8EFAohQ9ihtd5OVFSWno39yZAkhA1BUQNSiE4ZaOnkuYTMQq6GSDBJtRyLNQKqDT5IORxVL4ArI+sUjYjNjA0CVR70hyRY5ujLYqrSV5rblJbEkfHBSsp23NREmlZSXkhdAqxGKE32/SGzRW4KAGaZdCpgjMA5VPPa/bdPQ08IH0IBI+ecEp9ZHFLTx9OVNZPBWSusxckEEivsh4Ifot5W895QWYl2g+VBkfeteGixqvhTHqC0Nu39AIWQyyprNfOh529FkNJCOEkhzCUtYZ6kuN8NFQ6vigrI91lLn/qFEI22shCT4gzzXI5gckqp1u8AcekbF4C5WK2sSy9bI5nVsHMKQVBnLAaraZkq91gxQm9QU/oCZzNNmW4JJkrSAtmphsJkM4V6XPgVMMUP1cInYV5ldY0V65kyUhPm8Q7PFTF1JyyHGgppNJ8Ffcz9P2UjZ1k4aBJfMA5wzxZHwiaod3mmHYPhrKTAzSV6injhBl5jnLeWV5JZ6dNkV0SzzD0tGCArQgBa749lGfOEv05ReZpPdKzmM24IZqhxWC2GdSnpZsbDGQ9kldZZzfaNwhQ5NF9AhfUo5JJslhdh3gIX/tDQ/Reqc3NuUrhEXWTESeVtgKvnIgZLZgT8pjRYDRjhCnFZEsx2rDMqQXNjmiYgutScEuBAFjRjWiY3k35BtIrsz5INTmuS/Y2lexoJ9X00dWCwwqeEyEezUppnonnR25UPGvytkhn4ZzgASxdqYVVGlUioxuUZrdHzajzVUgUHPeLyjqjwd4StXRzrAl9lwEnDLiBeFZtdBK1lJo4zwkSLkIS3ddSSOlI9IMKtZ9STUSyyYKKfI961TIyBZ2yazLnRzHFezBegAwHqNYKHF1ZkKH9NNoE7TeqBVCvEOoW7brWQ+J8kBWVDcmGyLO3p4sA1QjqZ1wBqrOjZ8/1nbqxzKqP3UK09nYTYY+E1ln84q5r7VGIWoPYXNHtjNOU0nEXeimzkTY34bZZXSbKBWyUMRlYgI1Q343JkABwkyMDdwDSC3GnxjalYXLRA2lTsxw7ZuI9aYPnancosrOcyNJud70TCmhrlCaS4WQKHY0CrEdY+WlFynYbaTcBq40CbpPJbDbY8PTC/M3daFdIVqeeq5psml8fOblf7rPpzqU3x2hPPyn/ANtpGL7n9N2PpnbOjyuc1lw85Hb7OxOr+4b7y/FvvpXtyikvEh55KfninweMzcxf9sTA6sPHzgxZVLVsYcz0+xKwTWNPLH/3y93LL3y5eVXwubNt5b6uL869sLezqaH2wXmXtlYXzX7z0vhPL+5v3d+4ZXSRcvLYkbXUkNLVr8pbl6w899GOfqMT8gbOgqG9rUtOHGlNfX3kgdPfvNac9cmf4kcc/Wn8Qlug6nPvoIfmFyS6N626qW31iEkpJbf1HXMyr8/Jti8Sh+5j+ovGZeMOWzYein14sfREzsbMY3Pjxp8gJjTKga65X5b/5mc7zl/6a/Gzi4dO79p3fGWXof108Z2MZwMz9d7+5hcWVVbGj+s/qkM6kvuOsXVw/XCs9F2L17ar5vitJbt2LpAGHpzwbMbOBbF74tfUzlsz7/5afuaOB0+JGScu0DeXOmHc2W/Ta/IS1j20xv6LvVWDK8Z1+gaueaDP+9bLAw5NI+3khUfGmUs+OPLVt7OfbPjR8oOjYnNS48ceuKxs7VvU9FnuIuLU0ATn8rgfLzjw2a8+HPbUllezYYJ0qBHMrnV+NTIm5vLlfjGufdtjk/rFxNxI7dzn0D/Rzv9L6tYDpbJuHfMvS9yeINfq3Ewo9aqVqhgBJvKQpN00iZHoqLyOIPs7+aV59fyyf6ivNM+eSiwq6X4QXD8Irv9owWW2Gm6s4LL/twouI/F/KLis1hsuuChDMrBboNVgsbuSXSZIEsBodrmsFjsJIGki/o2Cy24gXXbi+wmu1quCK0a92qX8Ul+n4fbw+U3Dz9snBhynXudwdi2cePpF79qfZExjx8gXPq/zpSR9/NzIjsazNdOHTTqyTjzY3v7HzGXzj9z3yn1nbLeUNp78ZNXFnd/EHu0qFgtPrAixny5dtPr0nUd/fbGlJmv24XODB1V61tzyu7sfuWvjydnHHoutacmqfOqNT0++903OYO/utBFemP52R8dD2c8Ud9JPPnlwxWtrVvVNpc7YXcu2hHcPXOFeMC+e/MvHbbXtVQ/qqG/HvvJ80YiE59tWP7Ur/pdDfru55s3+nfUz5vR/7Jmm4SWNabuKu9pDNy+5e3riuY88l6Yc/lBu3TO88PzRp/XcSsry1ulZ1uk+b/Ed7QPNL3ZVBnSvjBrVUswuLtija4hrjU0hny8t8plvb943d8Ck/G3VzIAc9vDy4xlTks4MuvXz+aFHLwUvJQ96+diOr0vi3k45NCx8cf8Tq7b55j0+jlmvzC20fbw3PKJi6/SGOGnWgA0/vzim4I1TSwZtfa59Yc3IrPdurXlh9PzMZYnlyQkNT5cvGXrb6rNHvz7+TsfK+jGjX2r7/dfKh3fkbli60Ty2df+rc/lax1c3RbTX4I9aZ1jRbv0NFQgXWA==
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
eNptVXtsVFUap2kiu0iEumb9xw13R5ddkTu9t/NurdLOQFtobe2U0oHg5My5Z2Zue1+959zpzGCbFDYE1Ki3sjExi1mhncHa1rI0giA0mw2bJUsJUhMsRHbdXYOK7gYaHjEKnjsPmQZOOtM55/ud7/e9z/ZsAulYVJWyMVEhSAeQ0A02t2d11GsgTH6fkRGJq8JwW2uwY7+hi3Or4oRouLqyEmiiXdWQAkQ7VOXKBF8J44BU0t+ahHJqhiOqkLpQFt1mkxHGIIawrZrZss0GVcqlELqx9dErv8UMiSOmDwH6T2dEhcFR22rGpqsSsjAGRrqtfys9kVUBSdZRTCOsU82BENYoFQpHVV0Gls5tNpLSche7saqEMYwjGVjQ0q0FU4Ccg8UQCRfILZiAMNRFzfLAkjYgssA8ysMABmsIilERMlAkKesWJroILfookDCyDu4SaTqNk07EnP/UfetKqZ3WXSVm6+/P+dNriDoSrEjlkZbjRaQa6UaUpT8HpdcQkIuU/dk4AgLN5qVFFcNxFRNzYmGG3gcQIho3pEBVoHzmeCwtaqsZAUUlQNAozYqCcvk3R3sQ0lggiQmUyd8yJ4GmSSIElrzSiuRYIYusZdu94lErayzNuULMqVZqRF1TZVuKlpLC8HaXz+6ZTLKYAFGRaGmwEqD2ZLSc/FipQAOwhyphC2VqZvKXJ0oxKjZHWgBsDS5QCXQYN0eALrudh0rPdUMhoozMrL/tXrqC8C6dw87zdt/BBYpxSoHmSC7oH5YK4kiiaWbzDWMeiyAC7FZL2Etawq4BHaPDCzgR0VMsVCm1+Q6XgaraIyJz7lo4DKPhiFwbdHJqslHrBGm3uqk1onY1pJq0iJZUvdyGujajzXCEW9clSKORDrG8x+H2enmXx8fyds7O23k2nPA0hjzxdllW9I70Rp1Ld8cV+Tm+s5XfQNoTofjmxgAC2OkKhkOhdKjF5e7tC8Lmev+maGe3I9zZhLw+CTUH9M1kY+D5oL9FMdzuvhqGWmckRKGWb1vrwV0bNwU4XC/0BpFjPZ94Hsf4hNBoTwDHRhBLhehGMuzeEvM4F89yBQvdnNPLWWuiWFISUmIkbu53uLwHig2+I0NDRgy8fZiWLzr992xhpuxr3XC38n85HKClbB7vMGgL8k5mPVCYKq7KxfC+as5dXeVgGlo6xvwFmo77Vu7BDh0oOEozubbYKVkYN5QeJIz679sjx60eoZm0zKdDi0VJTcWILVhljnWx7flpyjYFDuUbklX1GFDEdI7WfNeqf1oqojJVENORYamk5KyMaSC83ERBUizNUeoXx/Icy/FHk6xO3ZZEWaSxy30XxjftDN6K7JF7EUTtQXTSH3Bw+XWiFKLT0SUqFvtPioZ9dH10f1BRV5WF8Xk8RxfCMCoxaL9bxkfulRdU7OPwWLIIZkXBnHuCbsIOxLnpH4ccEWfU7eSQACIurwtEocfhRD7Ph/nxyxIrdZqqExYjSF8rkjLnVssgaQ2iWgfvcrippzX0iYGSIaCgEQmolg+4htF0JKlAeN+/jvUDOrrZYK7azGwg9FxdS5P/gy62tGzYVi3/UmYVFStiNJoJIp2mxhyFkmoIdKLqKEN1tdeFzCkfx0eRp8rthbTOo16OXbupfbKo7aciG7bGcRZI1PYENA/FHbW2aqfTYathZFDrpU5zufd0MJN/Lk6WvbPi5Z8tyq1y+rlz55WhvygXuaU7/z+wZPByUju38uqLV0Yqzyq/+2DpzuaPX60OPfxEemvFfz9bPCd+E/nV5MvLvr9153bT1T0/L9tc95tFrw0dvLlyev6z2e/A/MPTxyfVFRtWLBmQ3p5+pn/u84c6mz5/K1Y/EOyvP7/NP7Z3L/fAUPMp4/j03B++SG+59Mqf+l5qHjLent/ypvrSjPzCnlTv1ZP7ZxsrVl2t/UX9rtlbLRXj+/7ZtjtyvfOrJ9fNVXScX/lrxvvGKgZ+Yo6grpnle9Gf4dezZW3zV8pGy59ufOp/O6YOPH60/I9vDq35djlaLy2G9uvzrh29e5dN7H50YPzwha+TY+WnvpS+CwuXwfDl5l0r/jP212O3/rXjSu35bwaGWnZenBEWz+uxI9NcYvVM/Xzq+mBofNeIu/fgqjW7G9oP6Zfee2xP4sHX2XW3a/7RcGn20wuJO/DZ8sC15tEbg3v+dnP+jDx47osf3vv26L95LrBk2tk+81Z9+oG144+dJo8sDz8eDB++kHjkxbOzaOqEcqP7sHxev7gmceZ2Lg/lixq2bg3YaFJ+BJ3tuZc=
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
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
|
||||
+1
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1,6 +1,4 @@
|
||||
---
|
||||
hide:
|
||||
- navigation
|
||||
title: Concepts
|
||||
description: Conceptual Guide for LangGraph
|
||||
---
|
||||
@@ -15,11 +13,11 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
|
||||
## LangGraph
|
||||
|
||||
**High Level**
|
||||
### High Level
|
||||
|
||||
- [Why LangGraph?](high_level.md): A high-level overview of LangGraph and its goals.
|
||||
|
||||
**Concepts**
|
||||
### Concepts
|
||||
|
||||
- [LangGraph Glossary](low_level.md): LangGraph workflows are designed as graphs, with nodes representing different components and edges representing the flow of information between them. This guide provides an overview of the key concepts associated with LangGraph graph primitives.
|
||||
- [Common Agentic Patterns](agentic_concepts.md): An agent uses an LLM to pick its own control flow to solve more complex problems! Agents are a key building block in many LLM applications. This guide explains the different types of agent architectures and how they can be used to control the flow of an application.
|
||||
|
||||
@@ -6,21 +6,29 @@
|
||||
|
||||
## Overview
|
||||
|
||||
LangGraph's Cloud SaaS is a managed service for deploying LangGraph APIs, regardless of its definition or dependencies. The service offers managed implementations of checkpointers and stores, allowing you to focus on building the right cognitive architecture for your use case. By handling scalable & secure infrastructure, LangGraph Cloud offers the fastest path to getting your LangGraph API deployed to production.
|
||||
LangGraph's Cloud SaaS is a managed service for deploying LangGraph Servers, regardless of its definition or dependencies. The service offers managed implementations of checkpointers and stores, allowing you to focus on building the right cognitive architecture for your use case. By handling scalable & secure infrastructure, LangGraph Cloud SaaS offers the fastest path to getting your LangGraph Server deployed to production.
|
||||
|
||||
## Deployment
|
||||
|
||||
A **deployment** is an instance of a LangGraph API. A single deployment can have many [revisions](#revision). When a deployment is created, all the necessary infrastructure (e.g. database, containers, secrets store) are automatically provisioned. See the [architecture diagram](#architecture) below for more details.
|
||||
A **deployment** is an instance of a LangGraph Server. A single deployment can have many [revisions](#revision). When a deployment is created, all the necessary infrastructure (e.g. database, containers, secrets store) are automatically provisioned. See the [architecture diagram](#architecture) below for more details.
|
||||
|
||||
See the [how-to guide](../cloud/deployment/cloud.md#create-new-deployment) for creating a new deployment.
|
||||
|
||||
## Resource Allocation
|
||||
Resource Allocation:
|
||||
|
||||
| **Deployment Type** | **CPU** | **Memory** | **Scaling** |
|
||||
|---------------------|---------|------------|---------------------|
|
||||
| Development | 1 CPU | 1 GB | Up to 1 container |
|
||||
| Production | 2 CPU | 2 GB | Up to 10 containers |
|
||||
|
||||
See the [how-to guide](../cloud/deployment/cloud.md#create-new-deployment) for creating a new deployment.
|
||||
|
||||
## Persistence
|
||||
|
||||
A dedicated database is automatically created for each deployment. The database serves as the [persistence layer](../concepts/persistence.md) for the deployment.
|
||||
|
||||
When defining a graph to be deployed to LangGraph Cloud SaaS, a [checkpointer](../concepts/persistence.md#checkpointer-libraries) should not be configured by the user. Instead, a checkpointer is automatically configured for the graph.
|
||||
|
||||
There is no direct access to the database. All access to the database occurs through the LangGraph Server APIs.
|
||||
|
||||
## Autoscaling
|
||||
`Production` type deployments automatically scale up to 10 containers. Scaling is based on the current request load for a single container. Specifically, the autoscaling implementation scales the deployment so that each container is processing about 10 concurrent requests. For example...
|
||||
|
||||
|
||||
@@ -60,7 +60,18 @@ LangGraph Studio (desktop) requires Docker Desktop version 4.24 or higher. Pleas
|
||||
|
||||
#### Configuration or environment issues
|
||||
|
||||
Another reason your project might fail to start is because your configuration file is defined incorrectly, or you are missing required environment variables.
|
||||
Another reason your project might fail to start is because your configuration file is defined incorrectly, or you are missing required environment variables.
|
||||
|
||||
!!! Important "Note (desktop only)"
|
||||
|
||||
LangGraph Studio Desktop automatically populates `LANGCHAIN_*` environment variables for license verification and tracing, regardless of the contents of the `.env` file. All other environment variables defined in `.env` will be read as normal.
|
||||
|
||||
#### Incorrect data region (desktop only)
|
||||
|
||||
If you receive a license verification error when attempting to start the LangGraph Server, you may be logged into the incorrect LangSmith data region. Ensure that you're logged into the correct LangSmith data region and ensure that the LangSmith account has access to LangGraph platform.
|
||||
|
||||
1. In the top right-hand corner, click the user icon and select `Logout`.
|
||||
1. At the login screen, click the `Data Region` dropdown menu and select the appropriate data region. Then click `Login to LangSmith`.
|
||||
|
||||
### How does interrupt work?
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
---
|
||||
hide:
|
||||
- navigation
|
||||
title: How-to Guides
|
||||
description: How to accomplish common tasks in LangGraph
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
" return f\"It's sunny in {city}!\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"raw_model = ChatOpenAI()\n",
|
||||
"raw_model = ChatOpenAI(model=\"gpt-4o\")\n",
|
||||
"model = raw_model.with_structured_output(get_weather)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
hide_comments: true
|
||||
hide:
|
||||
- navigation
|
||||
title: Home
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -582,7 +582,7 @@
|
||||
")\n",
|
||||
"\n",
|
||||
"evaluator = prompt | ChatOpenAI(model=\"gpt-4-turbo-preview\").with_structured_output(\n",
|
||||
" RedTeamingResult\n",
|
||||
" RedTeamingResult, method=\"function_calling\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Deployment
|
||||
|
||||
Get started deploying your LangGraph applications locally or on the cloud with
|
||||
[LangGraph Platform](../concepts/langgraph_platform.md).
|
||||
|
||||
## Get Started 🚀 {#quick-start}
|
||||
|
||||
- [LangGraph Server Quickstart](../tutorials/langgraph-platform/local-server.md): Launch a LangGraph server locally and interact with it using REST API and LangGraph Studio Web UI.
|
||||
- [LangGraph Template Quickstart](../concepts/template_applications.md): Start building with LangGraph Platform using a template application.
|
||||
- [Deploy with LangGraph Cloud Quickstart](../cloud/quick_start.md): Deploy a LangGraph app using LangGraph Cloud.
|
||||
|
||||
|
||||
## Deployment Options
|
||||
|
||||
- [Self-Hosted Lite](../concepts/self_hosted.md): A free (up to 1 million nodes executed), limited version of LangGraph Platform that you can run locally or in a self-hosted manner
|
||||
- [Cloud SaaS](../concepts/langgraph_cloud.md): Hosted as part of LangSmith.
|
||||
- [Bring Your Own Cloud](../concepts/bring_your_own_cloud.md): We manage the infrastructure, so you don't have to, but the infrastructure all runs within your cloud.
|
||||
- [Self-Hosted Enterprise](../concepts/self_hosted.md): Completely managed by you.
|
||||
@@ -1,6 +1,4 @@
|
||||
---
|
||||
hide:
|
||||
- navigation
|
||||
title: Tutorials
|
||||
---
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"id": "4a1aae78-88a6-4133-b905-7e46c8e3772f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🚀 LangGraph Quick Start\n",
|
||||
"# 🚀 LangGraph Quickstart\n",
|
||||
"\n",
|
||||
"In this tutorial, we will build a support chatbot in LangGraph that can:\n",
|
||||
"\n",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# QuickStart: Launch Local LangGraph Server
|
||||
# Quickstart: Launch Local LangGraph Server
|
||||
|
||||
This is a quick start guide to help you get a LangGraph app up and running locally.
|
||||
|
||||
|
||||
@@ -1032,7 +1032,9 @@
|
||||
") # You can optionally add examples\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4-turbo-preview\")\n",
|
||||
"\n",
|
||||
"runnable = joiner_prompt | llm.with_structured_output(JoinOutputs)"
|
||||
"runnable = joiner_prompt | llm.with_structured_output(\n",
|
||||
" JoinOutputs, method=\"function_calling\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -114,7 +114,9 @@ def get_math_tool(llm: ChatOpenAI):
|
||||
MessagesPlaceholder(variable_name="context", optional=True),
|
||||
]
|
||||
)
|
||||
extractor = prompt | llm.with_structured_output(ExecuteCode)
|
||||
extractor = prompt | llm.with_structured_output(
|
||||
ExecuteCode, method="function_calling"
|
||||
)
|
||||
|
||||
def calculate_expression(
|
||||
problem: str,
|
||||
|
||||
@@ -42,13 +42,13 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "53d1a740-9fea-4a6e-8f95-fb9dbf1c80a1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"! pip install -U langchain_community tiktoken langchain-openai langchain-cohere langchainhub chromadb langchain langgraph tavily-python"
|
||||
"%pip install -U langchain_community tiktoken langchain-openai langchain-cohere langchainhub chromadb langchain langgraph tavily-python"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -68,7 +68,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"_set_env(\"OPENAI_API_KEY\")\n",
|
||||
"_set_env(\"COHERE_API_KEY\")\n",
|
||||
"# _set_env(\"COHERE_API_KEY\")\n",
|
||||
"_set_env(\"TAVILY_API_KEY\")"
|
||||
]
|
||||
},
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "b224e5ba-50ca-495a-a7fa-0f75a080e03c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -161,7 +161,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"id": "4dec9d98-f3dc-4b7f-abc0-9d01c754f2be",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -196,7 +196,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_router = llm.with_structured_output(RouteQuery)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -221,7 +221,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"id": "856801cb-f42a-44e7-956f-47845e3664ca",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -229,7 +229,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"binary_score='no'\n"
|
||||
"binary_score='yes'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -247,7 +247,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeDocuments)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -271,7 +271,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"id": "2272333e-50b2-42ab-b472-e1055a3b94a8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -279,7 +279,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The design of generative agents combines LLM with memory, planning, and reflection mechanisms to enable agents to behave based on past experience and interact with other agents. Memory stream is a long-term memory module that records agents' experiences in natural language. The retrieval model surfaces context to inform the agent's behavior based on relevance, recency, and importance.\n"
|
||||
"Agent memory in LLM-powered autonomous systems consists of short-term and long-term memory. Short-term memory utilizes in-context learning for immediate tasks, while long-term memory allows agents to retain and recall information over extended periods, often using external storage for efficient retrieval. This memory structure supports the agent's ability to reflect on past actions and improve future performance.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -293,7 +293,7 @@
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Post-processing\n",
|
||||
@@ -311,7 +311,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"id": "f0c08d14-77a0-4eed-b882-2d636abb22a3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -321,7 +321,7 @@
|
||||
"GradeHallucinations(binary_score='yes')"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -340,7 +340,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeHallucinations)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -359,7 +359,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 8,
|
||||
"id": "ded99680-437a-4c9d-b860-619c88949d84",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -369,7 +369,7 @@
|
||||
"GradeAnswer(binary_score='yes')"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -388,7 +388,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeAnswer)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -407,17 +407,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 9,
|
||||
"id": "9d75f1d7-a47a-4577-bb0d-84b504b0867e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"What is the role of memory in an agent's functioning?\""
|
||||
"'What are the key concepts and techniques related to agent memory in artificial intelligence?'"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -426,7 +426,7 @@
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n",
|
||||
@@ -455,7 +455,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 10,
|
||||
"id": "01d829bb-1074-4976-b650-ead41dcb9788",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -481,7 +481,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 11,
|
||||
"id": "e723fcdb-06e6-402d-912e-899795b78408",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -516,7 +516,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 12,
|
||||
"id": "b76b5ec3-0720-443d-85b1-c0e79659ca0a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -736,7 +736,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 13,
|
||||
"id": "67854e07-9293-4c3c-bf9a-bc9a605570ee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -796,7 +796,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 14,
|
||||
"id": "29acc541-d726-4b75-84d1-a215845fe88a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -816,11 +816,9 @@
|
||||
"---DECISION: GENERATION ADDRESSES QUESTION---\n",
|
||||
"\"Node 'generate':\"\n",
|
||||
"'\\n---\\n'\n",
|
||||
"('It is expected that the Chicago Bears could have the opportunity to draft '\n",
|
||||
" 'the first defensive player in the 2024 NFL draft. The Bears have the first '\n",
|
||||
" 'overall pick in the draft, giving them a prime position to select top '\n",
|
||||
" 'talent. The top wide receiver Marvin Harrison Jr. from Ohio State is also '\n",
|
||||
" 'mentioned as a potential pick for the Cardinals.')\n"
|
||||
"('The Chicago Bears are expected to draft quarterback Caleb Williams first '\n",
|
||||
" 'overall in the 2024 NFL Draft. They also have a second first-round pick, '\n",
|
||||
" 'where they selected wide receiver Rome Odunze.')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -843,19 +841,9 @@
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "11fddd00-58bf-4910-bf36-be9e5bfba778",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Trace: \n",
|
||||
"\n",
|
||||
"https://smith.langchain.com/public/7e3aa7e5-c51f-45c2-bc66-b34f17ff2263/r"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 15,
|
||||
"id": "69a985dd-03c6-45af-a67b-b15746a2cb5f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -869,7 +857,7 @@
|
||||
"\"Node 'retrieve':\"\n",
|
||||
"'\\n---\\n'\n",
|
||||
"---CHECK DOCUMENT RELEVANCE TO QUESTION---\n",
|
||||
"---GRADE: DOCUMENT RELEVANT---\n",
|
||||
"---GRADE: DOCUMENT NOT RELEVANT---\n",
|
||||
"---GRADE: DOCUMENT RELEVANT---\n",
|
||||
"---GRADE: DOCUMENT NOT RELEVANT---\n",
|
||||
"---GRADE: DOCUMENT RELEVANT---\n",
|
||||
@@ -884,11 +872,11 @@
|
||||
"---DECISION: GENERATION ADDRESSES QUESTION---\n",
|
||||
"\"Node 'generate':\"\n",
|
||||
"'\\n---\\n'\n",
|
||||
"('The types of agent memory include Sensory Memory, Short-Term Memory (STM) or '\n",
|
||||
" 'Working Memory, and Long-Term Memory (LTM) with subtypes of Explicit / '\n",
|
||||
" 'declarative memory and Implicit / procedural memory. Sensory memory retains '\n",
|
||||
" 'sensory information briefly, STM stores information for cognitive tasks, and '\n",
|
||||
" 'LTM stores information for a long time with different types of memories.')\n"
|
||||
"('The types of agent memory include short-term memory, long-term memory, and '\n",
|
||||
" 'sensory memory. Short-term memory is utilized for in-context learning, while '\n",
|
||||
" 'long-term memory allows for the retention and recall of information over '\n",
|
||||
" 'extended periods. Sensory memory involves learning embedding representations '\n",
|
||||
" 'for various raw inputs, such as text and images.')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -906,16 +894,6 @@
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ebf41097-fc4c-4072-95b3-e7e07731ada1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Trace: \n",
|
||||
"\n",
|
||||
"https://smith.langchain.com/public/fdf0a180-6d15-4d09-bb92-f84f2105ca51/r"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -934,7 +912,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
" binary_score: str = Field(description=\"Relevance score 'yes' or 'no'\")\n",
|
||||
"\n",
|
||||
" # LLM\n",
|
||||
" model = ChatOpenAI(temperature=0, model=\"gpt-4-0125-preview\", streaming=True)\n",
|
||||
" model = ChatOpenAI(temperature=0, model=\"gpt-4o\", streaming=True)\n",
|
||||
"\n",
|
||||
" # LLM with tool and validation\n",
|
||||
" llm_with_tool = model.with_structured_output(grade)\n",
|
||||
@@ -376,7 +376,7 @@
|
||||
" prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
" # LLM\n",
|
||||
" llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0, streaming=True)\n",
|
||||
" llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0, streaming=True)\n",
|
||||
"\n",
|
||||
" # Post-processing\n",
|
||||
" def format_docs(docs):\n",
|
||||
@@ -548,7 +548,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install -U langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph"
|
||||
"%pip install -U langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -197,7 +197,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeDocuments)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -243,7 +243,7 @@
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Post-processing\n",
|
||||
@@ -290,7 +290,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeHallucinations)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -338,7 +338,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeAnswer)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
@@ -376,7 +376,7 @@
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n",
|
||||
@@ -760,18 +760,6 @@
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "548f1c5b-4108-4aae-8abb-ec171b511b92",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"LangSmith Traces - \n",
|
||||
" \n",
|
||||
"* https://smith.langchain.com/public/55d6180f-aab8-42bc-8799-dadce6247d9b/r\n",
|
||||
"\n",
|
||||
"* https://smith.langchain.com/public/1c6bf654-61b2-4fc5-9889-054b020c78aa/r"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -790,7 +778,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
+280
-269
@@ -22,7 +22,6 @@ theme:
|
||||
- header.autohide
|
||||
- navigation.expand
|
||||
- navigation.footer
|
||||
- navigation.indexes
|
||||
- navigation.instant
|
||||
- navigation.sections
|
||||
- navigation.instant.prefetch
|
||||
@@ -30,7 +29,6 @@ theme:
|
||||
- navigation.path
|
||||
- navigation.prune
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- navigation.top
|
||||
- navigation.tracking
|
||||
- search.highlight
|
||||
@@ -89,281 +87,294 @@ plugins:
|
||||
options:
|
||||
filters:
|
||||
- "!^_"
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Tutorials:
|
||||
- tutorials/index.md
|
||||
- Quick Start:
|
||||
- Quick Start: tutorials#quick-start
|
||||
- tutorials/introduction.ipynb
|
||||
- tutorials/langgraph-platform/local-server.md
|
||||
- cloud/quick_start.md
|
||||
- Chatbots:
|
||||
- Chatbots: tutorials#chatbots
|
||||
- tutorials/customer-support/customer-support.ipynb
|
||||
- tutorials/chatbots/information-gather-prompting.ipynb
|
||||
- tutorials/code_assistant/langgraph_code_assistant.ipynb
|
||||
- RAG:
|
||||
- RAG: tutorials#rag
|
||||
- tutorials/rag/langgraph_adaptive_rag.ipynb
|
||||
- tutorials/rag/langgraph_adaptive_rag_local.ipynb
|
||||
- tutorials/rag/langgraph_agentic_rag.ipynb
|
||||
- tutorials/rag/langgraph_crag.ipynb
|
||||
- tutorials/rag/langgraph_crag_local.ipynb
|
||||
- tutorials/rag/langgraph_self_rag.ipynb
|
||||
- tutorials/rag/langgraph_self_rag_local.ipynb
|
||||
- tutorials/sql-agent.ipynb
|
||||
- Agent Architectures:
|
||||
- Agent Architectures: tutorials#agent-architectures
|
||||
- Multi-Agent Systems:
|
||||
- Multi-Agent Systems: tutorials#multi-agent-systems
|
||||
- tutorials/multi_agent/multi-agent-collaboration.ipynb
|
||||
- tutorials/multi_agent/agent_supervisor.ipynb
|
||||
- tutorials/multi_agent/hierarchical_agent_teams.ipynb
|
||||
- Planning Agents:
|
||||
- Planning Agents: tutorials#planning-agents
|
||||
- tutorials/plan-and-execute/plan-and-execute.ipynb
|
||||
- tutorials/rewoo/rewoo.ipynb
|
||||
- tutorials/llm-compiler/LLMCompiler.ipynb
|
||||
- Reflection & Critique:
|
||||
- Reflection & Critique: tutorials#reflection-critique
|
||||
- tutorials/reflection/reflection.ipynb
|
||||
- tutorials/reflexion/reflexion.ipynb
|
||||
- tutorials/tot/tot.ipynb
|
||||
- tutorials/lats/lats.ipynb
|
||||
- tutorials/self-discover/self-discover.ipynb
|
||||
- Evaluation & Analysis:
|
||||
- Evaluation & Analysis: tutorials#evaluation
|
||||
- tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb
|
||||
- tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb
|
||||
- Experimental:
|
||||
- Experimental: tutorials#experimental
|
||||
- tutorials/storm/storm.ipynb
|
||||
- tutorials/tnt-llm/tnt-llm.ipynb
|
||||
- tutorials/web-navigation/web_voyager.ipynb
|
||||
- tutorials/usaco/usaco.ipynb
|
||||
- tutorials/extraction/retries.ipynb
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: concepts#langgraph-platform
|
||||
- tutorials/auth/getting_started.md
|
||||
- tutorials/auth/resource_auth.md
|
||||
- tutorials/auth/add_auth_server.md
|
||||
|
||||
- How-to Guides:
|
||||
- how-tos/index.md
|
||||
- LangGraph:
|
||||
- LangGraph: how-tos#langgraph
|
||||
- Controllability:
|
||||
- Controllability: how-tos#controllability
|
||||
- how-tos/branching.ipynb
|
||||
- how-tos/map-reduce.ipynb
|
||||
- how-tos/recursion-limit.ipynb
|
||||
- how-tos/command.ipynb
|
||||
- Persistence:
|
||||
- Persistence: how-tos#persistence
|
||||
- how-tos/persistence.ipynb
|
||||
- how-tos/subgraph-persistence.ipynb
|
||||
- how-tos/cross-thread-persistence.ipynb
|
||||
- how-tos/persistence_postgres.ipynb
|
||||
- how-tos/persistence_mongodb.ipynb
|
||||
- how-tos/persistence_redis.ipynb
|
||||
- Memory:
|
||||
- Memory: how-tos#memory
|
||||
- how-tos/memory/manage-conversation-history.ipynb
|
||||
- how-tos/memory/delete-messages.ipynb
|
||||
- how-tos/memory/add-summary-conversation-history.ipynb
|
||||
- how-tos/memory/semantic-search.ipynb
|
||||
- Human-in-the-loop:
|
||||
- Human-in-the-loop: how-tos#human-in-the-loop
|
||||
- how-tos/human_in_the_loop/breakpoints.ipynb
|
||||
- how-tos/human_in_the_loop/dynamic_breakpoints.ipynb
|
||||
- how-tos/human_in_the_loop/edit-graph-state.ipynb
|
||||
- how-tos/human_in_the_loop/wait-user-input.ipynb
|
||||
- how-tos/human_in_the_loop/time-travel.ipynb
|
||||
- how-tos/human_in_the_loop/review-tool-calls.ipynb
|
||||
- Streaming:
|
||||
- Streaming: how-tos#streaming
|
||||
- how-tos/stream-values.ipynb
|
||||
- how-tos/stream-updates.ipynb
|
||||
- how-tos/streaming-tokens.ipynb
|
||||
- how-tos/streaming-tokens-without-langchain.ipynb
|
||||
- how-tos/streaming-content.ipynb
|
||||
- how-tos/stream-multiple.ipynb
|
||||
- how-tos/streaming-events-from-within-tools.ipynb
|
||||
- how-tos/streaming-events-from-within-tools-without-langchain.ipynb
|
||||
- how-tos/streaming-from-final-node.ipynb
|
||||
- how-tos/streaming-subgraphs.ipynb
|
||||
- how-tos/disable-streaming.ipynb
|
||||
- Tool calling:
|
||||
- Tool calling: how-tos#tool-calling
|
||||
- how-tos/tool-calling.ipynb
|
||||
- how-tos/tool-calling-errors.ipynb
|
||||
- how-tos/pass-run-time-values-to-tools.ipynb
|
||||
- how-tos/update-state-from-tools.ipynb
|
||||
- how-tos/pass-config-to-tools.ipynb
|
||||
- how-tos/many-tools.ipynb
|
||||
- Subgraphs:
|
||||
- Subgraphs: how-tos#subgraphs
|
||||
- how-tos/subgraph.ipynb
|
||||
- how-tos/subgraphs-manage-state.ipynb
|
||||
- how-tos/subgraph-transform-state.ipynb
|
||||
- Multi-agent:
|
||||
- Multi-agent: how-tos#multi-agent
|
||||
- how-tos/agent-handoffs.ipynb
|
||||
- how-tos/multi-agent-network.ipynb
|
||||
- how-tos/multi-agent-multi-turn-convo.ipynb
|
||||
- State Management:
|
||||
- State Management: how-tos#state-management
|
||||
- how-tos/state-model.ipynb
|
||||
- how-tos/input_output_schema.ipynb
|
||||
- how-tos/pass_private_state.ipynb
|
||||
- Other:
|
||||
- Other: how-tos#other
|
||||
- how-tos/async.ipynb
|
||||
- how-tos/visualization.ipynb
|
||||
- how-tos/configuration.ipynb
|
||||
- how-tos/node-retries.ipynb
|
||||
- how-tos/react-agent-structured-output.ipynb
|
||||
- how-tos/run-id-langsmith.ipynb
|
||||
- how-tos/return-when-recursion-limit-hits.ipynb
|
||||
- Prebuilt ReAct Agent:
|
||||
- Prebuilt ReAct Agent: how-tos#prebuilt-react-agent
|
||||
- how-tos/create-react-agent.ipynb
|
||||
- how-tos/create-react-agent-memory.ipynb
|
||||
- how-tos/create-react-agent-system-prompt.ipynb
|
||||
- how-tos/create-react-agent-hitl.ipynb
|
||||
- how-tos/react-agent-from-scratch.ipynb
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: how-tos#langgraph-platform
|
||||
- Application Structure:
|
||||
- Application Structure: how-tos#application-structure
|
||||
- cloud/deployment/setup.md
|
||||
- cloud/deployment/setup_pyproject.md
|
||||
- cloud/deployment/setup_javascript.md
|
||||
- cloud/deployment/semantic_search.md
|
||||
- cloud/deployment/custom_docker.md
|
||||
- cloud/deployment/test_locally.md
|
||||
- cloud/deployment/graph_rebuild.md
|
||||
- Deployment:
|
||||
- Deployment: how-tos#deployment
|
||||
- cloud/deployment/cloud.md
|
||||
- how-tos/deploy-self-hosted.md
|
||||
- how-tos/use-remote-graph.md
|
||||
- Authentication & Access Control:
|
||||
- Authentication & Access Control: how-tos#authentication-access-control
|
||||
- cloud/how-tos/auth/custom_auth_new.md
|
||||
- cloud/how-tos/auth/openapi_security_new.md
|
||||
- Assistants:
|
||||
- Assistants: how-tos#assistants
|
||||
- cloud/how-tos/configuration_cloud.md
|
||||
- cloud/how-tos/assistant_versioning.md
|
||||
- Threads:
|
||||
- Threads: how-tos#threads
|
||||
- cloud/how-tos/copy_threads.md
|
||||
- cloud/how-tos/check_thread_status.md
|
||||
- Runs:
|
||||
- Runs: how-tos#runs
|
||||
- cloud/how-tos/background_run.md
|
||||
- cloud/how-tos/same-thread.md
|
||||
- cloud/how-tos/cron_jobs.md
|
||||
- cloud/how-tos/stateless_runs.md
|
||||
- Streaming:
|
||||
- Streaming: how-tos#streaming_1
|
||||
- cloud/how-tos/stream_values.md
|
||||
- cloud/how-tos/stream_updates.md
|
||||
- cloud/how-tos/stream_messages.md
|
||||
- cloud/how-tos/stream_events.md
|
||||
- cloud/how-tos/stream_debug.md
|
||||
- cloud/how-tos/stream_multiple.md
|
||||
nav:
|
||||
- Home:
|
||||
- Introduction: index.md
|
||||
- Get started:
|
||||
- Learn the basics: tutorials/introduction.ipynb
|
||||
- Deployment:
|
||||
- tutorials/deployment.md
|
||||
- Local Deploy: tutorials/langgraph-platform/local-server.md
|
||||
- Template Applications: concepts/template_applications.md # TODO: make tutorial
|
||||
- Cloud Deploy: cloud/quick_start.md
|
||||
- Guides:
|
||||
- How-to Guides:
|
||||
- how-tos/index.md
|
||||
- LangGraph:
|
||||
- LangGraph: how-tos#langgraph
|
||||
- Controllability:
|
||||
- Controllability: how-tos#controllability
|
||||
- how-tos/branching.ipynb
|
||||
- how-tos/map-reduce.ipynb
|
||||
- how-tos/recursion-limit.ipynb
|
||||
- how-tos/command.ipynb
|
||||
- Persistence:
|
||||
- Persistence: how-tos#persistence
|
||||
- how-tos/persistence.ipynb
|
||||
- how-tos/subgraph-persistence.ipynb
|
||||
- how-tos/cross-thread-persistence.ipynb
|
||||
- how-tos/persistence_postgres.ipynb
|
||||
- how-tos/persistence_mongodb.ipynb
|
||||
- how-tos/persistence_redis.ipynb
|
||||
- Memory:
|
||||
- Memory: how-tos#memory
|
||||
- how-tos/memory/manage-conversation-history.ipynb
|
||||
- how-tos/memory/delete-messages.ipynb
|
||||
- how-tos/memory/add-summary-conversation-history.ipynb
|
||||
- how-tos/memory/semantic-search.ipynb
|
||||
- Human-in-the-loop:
|
||||
- Human-in-the-loop: how-tos#human-in-the-loop_1
|
||||
- cloud/how-tos/human_in_the_loop_breakpoint.md
|
||||
- cloud/how-tos/human_in_the_loop_user_input.md
|
||||
- cloud/how-tos/human_in_the_loop_edit_state.md
|
||||
- cloud/how-tos/human_in_the_loop_time_travel.md
|
||||
- cloud/how-tos/human_in_the_loop_review_tool_calls.md
|
||||
- Double-texting:
|
||||
- Double-texting: how-tos#double-texting
|
||||
- cloud/how-tos/interrupt_concurrent.md
|
||||
- cloud/how-tos/rollback_concurrent.md
|
||||
- cloud/how-tos/reject_concurrent.md
|
||||
- cloud/how-tos/enqueue_concurrent.md
|
||||
- Webhooks:
|
||||
- cloud/how-tos/webhooks.md
|
||||
- Cron Jobs:
|
||||
- cloud/how-tos/cron_jobs.md
|
||||
- LangGraph Studio:
|
||||
- LangGraph Studio: how-tos#langgraph-studio
|
||||
- cloud/how-tos/test_deployment.md
|
||||
- cloud/how-tos/test_local_deployment.md
|
||||
- cloud/how-tos/invoke_studio.md
|
||||
- cloud/how-tos/threads_studio.md
|
||||
- cloud/how-tos/datasets_studio.md
|
||||
- Human-in-the-loop: how-tos#human-in-the-loop
|
||||
- how-tos/human_in_the_loop/breakpoints.ipynb
|
||||
- how-tos/human_in_the_loop/dynamic_breakpoints.ipynb
|
||||
- how-tos/human_in_the_loop/edit-graph-state.ipynb
|
||||
- how-tos/human_in_the_loop/wait-user-input.ipynb
|
||||
- how-tos/human_in_the_loop/time-travel.ipynb
|
||||
- how-tos/human_in_the_loop/review-tool-calls.ipynb
|
||||
- Streaming:
|
||||
- Streaming: how-tos#streaming
|
||||
- how-tos/stream-values.ipynb
|
||||
- how-tos/stream-updates.ipynb
|
||||
- how-tos/streaming-tokens.ipynb
|
||||
- how-tos/streaming-tokens-without-langchain.ipynb
|
||||
- how-tos/streaming-content.ipynb
|
||||
- how-tos/stream-multiple.ipynb
|
||||
- how-tos/streaming-events-from-within-tools.ipynb
|
||||
- how-tos/streaming-events-from-within-tools-without-langchain.ipynb
|
||||
- how-tos/streaming-from-final-node.ipynb
|
||||
- how-tos/streaming-subgraphs.ipynb
|
||||
- how-tos/disable-streaming.ipynb
|
||||
- Tool calling:
|
||||
- Tool calling: how-tos#tool-calling
|
||||
- how-tos/tool-calling.ipynb
|
||||
- how-tos/tool-calling-errors.ipynb
|
||||
- how-tos/pass-run-time-values-to-tools.ipynb
|
||||
- how-tos/update-state-from-tools.ipynb
|
||||
- how-tos/pass-config-to-tools.ipynb
|
||||
- how-tos/many-tools.ipynb
|
||||
- Subgraphs:
|
||||
- Subgraphs: how-tos#subgraphs
|
||||
- how-tos/subgraph.ipynb
|
||||
- how-tos/subgraphs-manage-state.ipynb
|
||||
- how-tos/subgraph-transform-state.ipynb
|
||||
- Multi-agent:
|
||||
- Multi-agent: how-tos#multi-agent
|
||||
- how-tos/agent-handoffs.ipynb
|
||||
- how-tos/multi-agent-network.ipynb
|
||||
- how-tos/multi-agent-multi-turn-convo.ipynb
|
||||
- State Management:
|
||||
- State Management: how-tos#state-management
|
||||
- how-tos/state-model.ipynb
|
||||
- how-tos/input_output_schema.ipynb
|
||||
- how-tos/pass_private_state.ipynb
|
||||
- Other:
|
||||
- Other: how-tos#other
|
||||
- how-tos/async.ipynb
|
||||
- how-tos/visualization.ipynb
|
||||
- how-tos/configuration.ipynb
|
||||
- how-tos/node-retries.ipynb
|
||||
- how-tos/react-agent-structured-output.ipynb
|
||||
- how-tos/run-id-langsmith.ipynb
|
||||
- how-tos/return-when-recursion-limit-hits.ipynb
|
||||
- Prebuilt ReAct Agent:
|
||||
- Prebuilt ReAct Agent: how-tos#prebuilt-react-agent
|
||||
- how-tos/create-react-agent.ipynb
|
||||
- how-tos/create-react-agent-memory.ipynb
|
||||
- how-tos/create-react-agent-system-prompt.ipynb
|
||||
- how-tos/create-react-agent-hitl.ipynb
|
||||
- how-tos/react-agent-from-scratch.ipynb
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: how-tos#langgraph-platform
|
||||
- Application Structure:
|
||||
- Application Structure: how-tos#application-structure
|
||||
- cloud/deployment/setup.md
|
||||
- cloud/deployment/setup_pyproject.md
|
||||
- cloud/deployment/setup_javascript.md
|
||||
- cloud/deployment/semantic_search.md
|
||||
- cloud/deployment/custom_docker.md
|
||||
- cloud/deployment/test_locally.md
|
||||
- cloud/deployment/graph_rebuild.md
|
||||
- Deployment:
|
||||
- Deployment: how-tos#deployment
|
||||
- cloud/deployment/cloud.md
|
||||
- how-tos/deploy-self-hosted.md
|
||||
- how-tos/use-remote-graph.md
|
||||
- Authentication & Access Control:
|
||||
- Authentication & Access Control: how-tos#authentication-access-control
|
||||
- cloud/how-tos/auth/custom_auth_new.md
|
||||
- cloud/how-tos/auth/openapi_security_new.md
|
||||
- Assistants:
|
||||
- Assistants: how-tos#assistants
|
||||
- cloud/how-tos/configuration_cloud.md
|
||||
- cloud/how-tos/assistant_versioning.md
|
||||
- Threads:
|
||||
- Threads: how-tos#threads
|
||||
- cloud/how-tos/copy_threads.md
|
||||
- cloud/how-tos/check_thread_status.md
|
||||
- Runs:
|
||||
- Runs: how-tos#runs
|
||||
- cloud/how-tos/background_run.md
|
||||
- cloud/how-tos/same-thread.md
|
||||
- cloud/how-tos/cron_jobs.md
|
||||
- cloud/how-tos/stateless_runs.md
|
||||
- Streaming:
|
||||
- Streaming: how-tos#streaming_1
|
||||
- cloud/how-tos/stream_values.md
|
||||
- cloud/how-tos/stream_updates.md
|
||||
- cloud/how-tos/stream_messages.md
|
||||
- cloud/how-tos/stream_events.md
|
||||
- cloud/how-tos/stream_debug.md
|
||||
- cloud/how-tos/stream_multiple.md
|
||||
- Human-in-the-loop:
|
||||
- Human-in-the-loop: how-tos#human-in-the-loop_1
|
||||
- cloud/how-tos/human_in_the_loop_breakpoint.md
|
||||
- cloud/how-tos/human_in_the_loop_user_input.md
|
||||
- cloud/how-tos/human_in_the_loop_edit_state.md
|
||||
- cloud/how-tos/human_in_the_loop_time_travel.md
|
||||
- cloud/how-tos/human_in_the_loop_review_tool_calls.md
|
||||
- Double-texting:
|
||||
- Double-texting: how-tos#double-texting
|
||||
- cloud/how-tos/interrupt_concurrent.md
|
||||
- cloud/how-tos/rollback_concurrent.md
|
||||
- cloud/how-tos/reject_concurrent.md
|
||||
- cloud/how-tos/enqueue_concurrent.md
|
||||
- Webhooks:
|
||||
- cloud/how-tos/webhooks.md
|
||||
- Cron Jobs:
|
||||
- cloud/how-tos/cron_jobs.md
|
||||
- LangGraph Studio:
|
||||
- LangGraph Studio: how-tos#langgraph-studio
|
||||
- cloud/how-tos/test_deployment.md
|
||||
- cloud/how-tos/test_local_deployment.md
|
||||
- cloud/how-tos/invoke_studio.md
|
||||
- cloud/how-tos/threads_studio.md
|
||||
- cloud/how-tos/datasets_studio.md
|
||||
- Concepts:
|
||||
- concepts/index.md
|
||||
- LangGraph:
|
||||
- LangGraph: concepts#langgraph
|
||||
- concepts/high_level.md
|
||||
- concepts/low_level.md
|
||||
- concepts/agentic_concepts.md
|
||||
- concepts/multi_agent.md
|
||||
- concepts/breakpoints
|
||||
- concepts/human_in_the_loop.md
|
||||
- concepts/time-travel.md
|
||||
- concepts/persistence.md
|
||||
- concepts/memory.md
|
||||
- concepts/streaming.md
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: concepts#langgraph-platform
|
||||
- High Level:
|
||||
- High Level: concepts#high-level
|
||||
- concepts/langgraph_platform.md
|
||||
- concepts/deployment_options.md
|
||||
- concepts/plans.md
|
||||
- concepts/template_applications.md
|
||||
- Components:
|
||||
- Components: concepts#components
|
||||
- concepts/langgraph_server.md
|
||||
- concepts/langgraph_studio.md
|
||||
- concepts/langgraph_cli.md
|
||||
- concepts/sdk.md
|
||||
- how-tos/use-remote-graph.md
|
||||
- LangGraph Server:
|
||||
- LangGraph Server: concepts#langgraph-server
|
||||
- concepts/application_structure.md
|
||||
- concepts/assistants.md
|
||||
- concepts/double_texting.md
|
||||
- concepts/auth.md
|
||||
- Deployment Options:
|
||||
- Deployment Options: concepts#deployment-options
|
||||
- concepts/self_hosted.md
|
||||
- concepts/langgraph_cloud.md
|
||||
- concepts/bring_your_own_cloud.md
|
||||
- Tutorials:
|
||||
- tutorials/index.md
|
||||
- Quick Start:
|
||||
- Quick Start: tutorials#quick-start
|
||||
- tutorials/introduction.ipynb
|
||||
- tutorials/langgraph-platform/local-server.md
|
||||
- cloud/quick_start.md
|
||||
- Chatbots:
|
||||
- Chatbots: tutorials#chatbots
|
||||
- tutorials/customer-support/customer-support.ipynb
|
||||
- tutorials/chatbots/information-gather-prompting.ipynb
|
||||
- tutorials/code_assistant/langgraph_code_assistant.ipynb
|
||||
- RAG:
|
||||
- RAG: tutorials#rag
|
||||
- tutorials/rag/langgraph_adaptive_rag.ipynb
|
||||
- tutorials/rag/langgraph_adaptive_rag_local.ipynb
|
||||
- tutorials/rag/langgraph_agentic_rag.ipynb
|
||||
- tutorials/rag/langgraph_crag.ipynb
|
||||
- tutorials/rag/langgraph_crag_local.ipynb
|
||||
- tutorials/rag/langgraph_self_rag.ipynb
|
||||
- tutorials/rag/langgraph_self_rag_local.ipynb
|
||||
- tutorials/sql-agent.ipynb
|
||||
- Agent Architectures:
|
||||
- Agent Architectures: tutorials#agent-architectures
|
||||
- Multi-Agent Systems:
|
||||
- Multi-Agent Systems: tutorials#multi-agent-systems
|
||||
- tutorials/multi_agent/multi-agent-collaboration.ipynb
|
||||
- tutorials/multi_agent/agent_supervisor.ipynb
|
||||
- tutorials/multi_agent/hierarchical_agent_teams.ipynb
|
||||
- Planning Agents:
|
||||
- Planning Agents: tutorials#planning-agents
|
||||
- tutorials/plan-and-execute/plan-and-execute.ipynb
|
||||
- tutorials/rewoo/rewoo.ipynb
|
||||
- tutorials/llm-compiler/LLMCompiler.ipynb
|
||||
- Reflection & Critique:
|
||||
- Reflection & Critique: tutorials#reflection-critique
|
||||
- tutorials/reflection/reflection.ipynb
|
||||
- tutorials/reflexion/reflexion.ipynb
|
||||
- tutorials/tot/tot.ipynb
|
||||
- tutorials/lats/lats.ipynb
|
||||
- tutorials/self-discover/self-discover.ipynb
|
||||
- Evaluation & Analysis:
|
||||
- Evaluation & Analysis: tutorials#evaluation
|
||||
- tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb
|
||||
- tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb
|
||||
- Experimental:
|
||||
- Experimental: tutorials#experimental
|
||||
- tutorials/storm/storm.ipynb
|
||||
- tutorials/tnt-llm/tnt-llm.ipynb
|
||||
- tutorials/web-navigation/web_voyager.ipynb
|
||||
- tutorials/usaco/usaco.ipynb
|
||||
- tutorials/extraction/retries.ipynb
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: concepts#langgraph-platform
|
||||
- tutorials/auth/getting_started.md
|
||||
- tutorials/auth/resource_auth.md
|
||||
- tutorials/auth/add_auth_server.md
|
||||
- Resources:
|
||||
- FAQ: concepts/faq.md
|
||||
- Troubleshooting:
|
||||
- Troubleshooting: how-tos#troubleshooting
|
||||
- Troubleshooting: troubleshooting/errors/index.md
|
||||
- troubleshooting/errors/index.md
|
||||
- troubleshooting/errors/GRAPH_RECURSION_LIMIT.md
|
||||
- troubleshooting/errors/INVALID_CONCURRENT_GRAPH_UPDATE.md
|
||||
- troubleshooting/errors/INVALID_GRAPH_NODE_RETURN_VALUE.md
|
||||
- troubleshooting/errors/MULTIPLE_SUBGRAPHS.md
|
||||
|
||||
- Conceptual Guides:
|
||||
- concepts/index.md
|
||||
- LangGraph:
|
||||
- LangGraph: concepts#langgraph
|
||||
- concepts/high_level.md
|
||||
- concepts/low_level.md
|
||||
- concepts/agentic_concepts.md
|
||||
- concepts/multi_agent.md
|
||||
- concepts/human_in_the_loop.md
|
||||
- concepts/persistence.md
|
||||
- concepts/memory.md
|
||||
- concepts/streaming.md
|
||||
- concepts/faq.md
|
||||
- LangGraph Platform:
|
||||
- LangGraph Platform: concepts#langgraph-platform
|
||||
- High Level:
|
||||
- High Level: concepts#high-level
|
||||
- concepts/langgraph_platform.md
|
||||
- concepts/deployment_options.md
|
||||
- concepts/plans.md
|
||||
- concepts/template_applications.md
|
||||
- Components:
|
||||
- Components: concepts#components
|
||||
- concepts/langgraph_server.md
|
||||
- concepts/langgraph_studio.md
|
||||
- concepts/langgraph_cli.md
|
||||
- concepts/sdk.md
|
||||
- how-tos/use-remote-graph.md
|
||||
- LangGraph Server:
|
||||
- LangGraph Server: concepts#langgraph-server
|
||||
- concepts/application_structure.md
|
||||
- concepts/assistants.md
|
||||
- concepts/double_texting.md
|
||||
- Deployment Options:
|
||||
- Deployment Options: concepts#deployment-options
|
||||
- concepts/self_hosted.md
|
||||
- concepts/langgraph_cloud.md
|
||||
- concepts/bring_your_own_cloud.md
|
||||
|
||||
- Reference:
|
||||
- "reference/index.md"
|
||||
- Library:
|
||||
- Graphs: reference/graphs.md
|
||||
- Checkpointing: reference/checkpoints.md
|
||||
- Storage: reference/store.md
|
||||
- Prebuilt Components: reference/prebuilt.md
|
||||
- Channels: reference/channels.md
|
||||
- Errors: reference/errors.md
|
||||
- Types: reference/types.md
|
||||
- Constants: reference/constants.md
|
||||
- LangGraph Platform:
|
||||
- Server API: "cloud/reference/api/api_ref.md"
|
||||
- CLI: "cloud/reference/cli.md"
|
||||
- SDK (Python): "cloud/reference/sdk/python_sdk_ref.md"
|
||||
- SDK (JS/TS): "cloud/reference/sdk/js_ts_sdk_ref.md"
|
||||
- RemoteGraph: reference/remote_graph.md
|
||||
- Environment Variables: "cloud/reference/env_var.md"
|
||||
- troubleshooting/errors/INVALID_CHAT_HISTORY.md
|
||||
- LangGraph Academy Course: https://academy.langchain.com/courses/intro-to-langgraph
|
||||
|
||||
- API reference:
|
||||
- Library:
|
||||
- Graphs: reference/graphs.md
|
||||
- Checkpointing: reference/checkpoints.md
|
||||
- Storage: reference/store.md
|
||||
- Prebuilt components: reference/prebuilt.md
|
||||
- Channels: reference/channels.md
|
||||
- Errors: reference/errors.md
|
||||
- Types: reference/types.md
|
||||
- Constants: reference/constants.md
|
||||
- LangGraph Platform:
|
||||
- Server API: "cloud/reference/api/api_ref.md"
|
||||
- CLI: "cloud/reference/cli.md"
|
||||
- SDK (Python): "cloud/reference/sdk/python_sdk_ref.md"
|
||||
- SDK (JS/TS): "cloud/reference/sdk/js_ts_sdk_ref.md"
|
||||
- RemoteGraph: reference/remote_graph.md
|
||||
- Environment variables: "cloud/reference/env_var.md"
|
||||
|
||||
markdown_extensions:
|
||||
- abbr
|
||||
|
||||
+10
-11
@@ -34,17 +34,6 @@
|
||||
color: #1E88E5;
|
||||
}
|
||||
|
||||
.md-sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show sidebar on mobile */
|
||||
@media screen and (max-width: 1220px) {
|
||||
.md-sidebar--primary {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.md-typeset a:hover {
|
||||
color: #1565C0;
|
||||
}
|
||||
@@ -169,6 +158,16 @@
|
||||
background-color: #CFC9FA;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* control the navbar depth */
|
||||
[data-md-level="2"] .md-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* disable the collapse/expand icon in the navar */
|
||||
.md-nav__icon {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
.PHONY: test test_watch lint format
|
||||
|
||||
######################
|
||||
# TESTING AND COVERAGE
|
||||
######################
|
||||
|
||||
test:
|
||||
poetry run pytest tests
|
||||
|
||||
test_watch:
|
||||
poetry run ptw .
|
||||
|
||||
######################
|
||||
# LINTING AND FORMATTING
|
||||
######################
|
||||
|
||||
# Define a variable for Python and notebook files.
|
||||
PYTHON_FILES=.
|
||||
MYPY_CACHE=.mypy_cache
|
||||
lint format: PYTHON_FILES=.
|
||||
lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --relative --diff-filter=d main . | grep -E '\.py$$|\.ipynb$$')
|
||||
lint_package: PYTHON_FILES=langgraph
|
||||
lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
@@ -1,95 +0,0 @@
|
||||
# LangGraph Checkpoint DuckDB
|
||||
|
||||
Implementation of LangGraph CheckpointSaver that uses DuckDB.
|
||||
|
||||
## Usage
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When using DuckDB checkpointers for the first time, make sure to call `.setup()` method on them to create required tables. See example below.
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.duckdb import DuckDBSaver
|
||||
|
||||
write_config = {"configurable": {"thread_id": "1", "checkpoint_ns": ""}}
|
||||
read_config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
with DuckDBSaver.from_conn_string(":memory:") as checkpointer:
|
||||
# call .setup() the first time you're using the checkpointer
|
||||
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, {}, {})
|
||||
|
||||
# load checkpoint
|
||||
checkpointer.get(read_config)
|
||||
|
||||
# list checkpoints
|
||||
list(checkpointer.list(read_config))
|
||||
```
|
||||
|
||||
### Async
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.duckdb.aio import AsyncDuckDBSaver
|
||||
|
||||
async with AsyncDuckDBSaver.from_conn_string(":memory:") as checkpointer:
|
||||
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, {}, {})
|
||||
|
||||
# load checkpoint
|
||||
await checkpointer.aget(read_config)
|
||||
|
||||
# list checkpoints
|
||||
[c async for c in checkpointer.alist(read_config)]
|
||||
```
|
||||
@@ -1,356 +0,0 @@
|
||||
import threading
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Iterator, Optional, Sequence
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
import duckdb
|
||||
from langgraph.checkpoint.base import (
|
||||
WRITES_IDX_MAP,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointMetadata,
|
||||
CheckpointTuple,
|
||||
get_checkpoint_id,
|
||||
)
|
||||
from langgraph.checkpoint.duckdb.base import BaseDuckDBSaver
|
||||
from langgraph.checkpoint.serde.base import SerializerProtocol
|
||||
|
||||
|
||||
class DuckDBSaver(BaseDuckDBSaver):
|
||||
lock: threading.Lock
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conn: duckdb.DuckDBPyConnection,
|
||||
serde: Optional[SerializerProtocol] = None,
|
||||
) -> None:
|
||||
super().__init__(serde=serde)
|
||||
|
||||
self.conn = conn
|
||||
self.lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def from_conn_string(cls, conn_string: str) -> Iterator["DuckDBSaver"]:
|
||||
"""Create a new DuckDBSaver instance from a connection string.
|
||||
|
||||
Args:
|
||||
conn_string (str): The DuckDB connection info string.
|
||||
|
||||
Returns:
|
||||
DuckDBSaver: A new DuckDBSaver instance.
|
||||
"""
|
||||
with duckdb.connect(conn_string) as conn:
|
||||
yield cls(conn)
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Set up the checkpoint database asynchronously.
|
||||
|
||||
This method creates the necessary tables in the DuckDB database if they don't
|
||||
already exist and runs database migrations. It MUST be called directly by the user
|
||||
the first time checkpointer is used.
|
||||
"""
|
||||
with self.lock, self.conn.cursor() as cur:
|
||||
try:
|
||||
row = cur.execute(
|
||||
"SELECT v FROM checkpoint_migrations ORDER BY v DESC LIMIT 1"
|
||||
).fetchone()
|
||||
if row is None:
|
||||
version = -1
|
||||
else:
|
||||
version = row[0]
|
||||
except duckdb.CatalogException:
|
||||
version = -1
|
||||
for v, migration in zip(
|
||||
range(version + 1, len(self.MIGRATIONS)),
|
||||
self.MIGRATIONS[version + 1 :],
|
||||
):
|
||||
cur.execute(migration)
|
||||
cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (?)", [v])
|
||||
|
||||
def list(
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
*,
|
||||
filter: Optional[dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
) -> Iterator[CheckpointTuple]:
|
||||
"""List checkpoints from the database.
|
||||
|
||||
This method retrieves a list of checkpoint tuples from the DuckDB database based
|
||||
on the provided config. The checkpoints are ordered by checkpoint ID in descending order (newest first).
|
||||
|
||||
Args:
|
||||
config (RunnableConfig): The config to use for listing the checkpoints.
|
||||
filter (Optional[Dict[str, Any]]): Additional filtering criteria for metadata. Defaults to None.
|
||||
before (Optional[RunnableConfig]): If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
|
||||
limit (Optional[int]): The maximum number of checkpoints to return. Defaults to None.
|
||||
|
||||
Yields:
|
||||
Iterator[CheckpointTuple]: An iterator of checkpoint tuples.
|
||||
|
||||
Examples:
|
||||
>>> from langgraph.checkpoint.duckdb import DuckDBSaver
|
||||
>>> with DuckDBSaver.from_conn_string(":memory:") as memory:
|
||||
... # Run a graph, then list the checkpoints
|
||||
>>> config = {"configurable": {"thread_id": "1"}}
|
||||
>>> checkpoints = list(memory.list(config, limit=2))
|
||||
>>> print(checkpoints)
|
||||
[CheckpointTuple(...), CheckpointTuple(...)]
|
||||
|
||||
>>> config = {"configurable": {"thread_id": "1"}}
|
||||
>>> before = {"configurable": {"checkpoint_id": "1ef4f797-8335-6428-8001-8a1503f9b875"}}
|
||||
>>> with DuckDBSaver.from_conn_string(":memory:") as memory:
|
||||
... # Run a graph, then list the checkpoints
|
||||
>>> checkpoints = list(memory.list(config, before=before))
|
||||
>>> print(checkpoints)
|
||||
[CheckpointTuple(...), ...]
|
||||
"""
|
||||
where, args = self._search_where(config, filter, before)
|
||||
query = self.SELECT_SQL + where + " ORDER BY checkpoint_id DESC"
|
||||
if limit:
|
||||
query += f" LIMIT {limit}"
|
||||
# if we change this to use .stream() we need to make sure to close the cursor
|
||||
with self._cursor() as cur:
|
||||
cur.execute(query, args)
|
||||
for value in cur.fetchall():
|
||||
(
|
||||
thread_id,
|
||||
checkpoint,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
parent_checkpoint_id,
|
||||
metadata,
|
||||
channel_values,
|
||||
pending_writes,
|
||||
pending_sends,
|
||||
) = value
|
||||
yield CheckpointTuple(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint_id,
|
||||
}
|
||||
},
|
||||
self._load_checkpoint(
|
||||
checkpoint,
|
||||
channel_values,
|
||||
pending_sends,
|
||||
),
|
||||
self._load_metadata(metadata),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": parent_checkpoint_id,
|
||||
}
|
||||
}
|
||||
if parent_checkpoint_id
|
||||
else None
|
||||
),
|
||||
self._load_writes(pending_writes),
|
||||
)
|
||||
|
||||
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
|
||||
"""Get a checkpoint tuple from the database.
|
||||
|
||||
This method retrieves a checkpoint tuple from the DuckDB database based on the
|
||||
provided config. If the config contains a "checkpoint_id" key, the checkpoint with
|
||||
the matching thread ID and timestamp is retrieved. Otherwise, the latest checkpoint
|
||||
for the given thread ID is retrieved.
|
||||
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
Basic:
|
||||
>>> config = {"configurable": {"thread_id": "1"}}
|
||||
>>> checkpoint_tuple = memory.get_tuple(config)
|
||||
>>> print(checkpoint_tuple)
|
||||
CheckpointTuple(...)
|
||||
|
||||
With timestamp:
|
||||
|
||||
>>> config = {
|
||||
... "configurable": {
|
||||
... "thread_id": "1",
|
||||
... "checkpoint_ns": "",
|
||||
... "checkpoint_id": "1ef4f797-8335-6428-8001-8a1503f9b875",
|
||||
... }
|
||||
... }
|
||||
>>> checkpoint_tuple = memory.get_tuple(config)
|
||||
>>> print(checkpoint_tuple)
|
||||
CheckpointTuple(...)
|
||||
""" # noqa
|
||||
thread_id = config["configurable"]["thread_id"]
|
||||
checkpoint_id = get_checkpoint_id(config)
|
||||
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
||||
if checkpoint_id:
|
||||
args: tuple[Any, ...] = (thread_id, checkpoint_ns, checkpoint_id)
|
||||
where = "WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?"
|
||||
else:
|
||||
args = (thread_id, checkpoint_ns)
|
||||
where = "WHERE thread_id = ? AND checkpoint_ns = ? ORDER BY checkpoint_id DESC LIMIT 1"
|
||||
|
||||
with self._cursor() as cur:
|
||||
cur.execute(
|
||||
self.SELECT_SQL + where,
|
||||
args,
|
||||
)
|
||||
|
||||
value = cur.fetchone()
|
||||
if value:
|
||||
(
|
||||
thread_id,
|
||||
checkpoint,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
parent_checkpoint_id,
|
||||
metadata,
|
||||
channel_values,
|
||||
pending_writes,
|
||||
pending_sends,
|
||||
) = value
|
||||
return CheckpointTuple(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint_id,
|
||||
}
|
||||
},
|
||||
self._load_checkpoint(
|
||||
checkpoint,
|
||||
channel_values,
|
||||
pending_sends,
|
||||
),
|
||||
self._load_metadata(metadata),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": parent_checkpoint_id,
|
||||
}
|
||||
}
|
||||
if parent_checkpoint_id
|
||||
else None
|
||||
),
|
||||
self._load_writes(pending_writes),
|
||||
)
|
||||
|
||||
def put(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
new_versions: ChannelVersions,
|
||||
) -> RunnableConfig:
|
||||
"""Save a checkpoint to the database.
|
||||
|
||||
This method saves a checkpoint to the DuckDB database. The checkpoint is associated
|
||||
with the provided config and its parent config (if any).
|
||||
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> from langgraph.checkpoint.duckdb import DuckDBSaver
|
||||
>>> with DuckDBSaver.from_conn_string(":memory:") as memory:
|
||||
>>> config = {"configurable": {"thread_id": "1", "checkpoint_ns": ""}}
|
||||
>>> checkpoint = {"ts": "2024-05-04T06:32:42.235444+00:00", "id": "1ef4f797-8335-6428-8001-8a1503f9b875", "channel_values": {"key": "value"}}
|
||||
>>> saved_config = memory.put(config, checkpoint, {"source": "input", "step": 1, "writes": {"key": "value"}}, {})
|
||||
>>> print(saved_config)
|
||||
{'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef4f797-8335-6428-8001-8a1503f9b875'}}
|
||||
"""
|
||||
configurable = config["configurable"].copy()
|
||||
thread_id = configurable.pop("thread_id")
|
||||
checkpoint_ns = configurable.pop("checkpoint_ns")
|
||||
checkpoint_id = configurable.pop(
|
||||
"checkpoint_id", configurable.pop("thread_ts", None)
|
||||
)
|
||||
|
||||
copy = checkpoint.copy()
|
||||
next_config = {
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint["id"],
|
||||
}
|
||||
}
|
||||
checkpoint_blobs = self._dump_blobs(
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
copy.pop("channel_values"), # type: ignore[misc]
|
||||
new_versions,
|
||||
)
|
||||
with self._cursor() as cur:
|
||||
if checkpoint_blobs:
|
||||
cur.executemany(self.UPSERT_CHECKPOINT_BLOBS_SQL, checkpoint_blobs)
|
||||
cur.execute(
|
||||
self.UPSERT_CHECKPOINTS_SQL,
|
||||
(
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint["id"],
|
||||
checkpoint_id,
|
||||
self._dump_checkpoint(copy),
|
||||
self._dump_metadata(metadata),
|
||||
),
|
||||
)
|
||||
return next_config
|
||||
|
||||
def put_writes(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
task_id: str,
|
||||
) -> None:
|
||||
"""Store intermediate writes linked to a checkpoint.
|
||||
|
||||
This method saves intermediate writes associated with a checkpoint to the DuckDB database.
|
||||
|
||||
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.
|
||||
"""
|
||||
query = (
|
||||
self.UPSERT_CHECKPOINT_WRITES_SQL
|
||||
if all(w[0] in WRITES_IDX_MAP for w in writes)
|
||||
else self.INSERT_CHECKPOINT_WRITES_SQL
|
||||
)
|
||||
with self._cursor() as cur:
|
||||
cur.executemany(
|
||||
query,
|
||||
self._dump_writes(
|
||||
config["configurable"]["thread_id"],
|
||||
config["configurable"]["checkpoint_ns"],
|
||||
config["configurable"]["checkpoint_id"],
|
||||
task_id,
|
||||
writes,
|
||||
),
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def _cursor(self) -> Iterator[duckdb.DuckDBPyConnection]:
|
||||
with self.lock, self.conn.cursor() as cur:
|
||||
yield cur
|
||||
|
||||
|
||||
__all__ = ["DuckDBSaver", "Conn"]
|
||||
@@ -1,431 +0,0 @@
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, AsyncIterator, Iterator, Optional, Sequence
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
import duckdb
|
||||
from langgraph.checkpoint.base import (
|
||||
WRITES_IDX_MAP,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointMetadata,
|
||||
CheckpointTuple,
|
||||
get_checkpoint_id,
|
||||
)
|
||||
from langgraph.checkpoint.duckdb.base import BaseDuckDBSaver
|
||||
from langgraph.checkpoint.serde.base import SerializerProtocol
|
||||
|
||||
|
||||
class AsyncDuckDBSaver(BaseDuckDBSaver):
|
||||
lock: asyncio.Lock
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conn: duckdb.DuckDBPyConnection,
|
||||
serde: Optional[SerializerProtocol] = None,
|
||||
) -> None:
|
||||
super().__init__(serde=serde)
|
||||
self.conn = conn
|
||||
self.lock = asyncio.Lock()
|
||||
self.loop = asyncio.get_running_loop()
|
||||
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def from_conn_string(
|
||||
cls,
|
||||
conn_string: str,
|
||||
) -> AsyncIterator["AsyncDuckDBSaver"]:
|
||||
"""Create a new AsyncDuckDBSaver instance from a connection string.
|
||||
|
||||
Args:
|
||||
conn_string (str): The DuckDB connection info string.
|
||||
|
||||
Returns:
|
||||
AsyncDuckDBSaver: A new AsyncDuckDBSaver instance.
|
||||
"""
|
||||
with duckdb.connect(conn_string) as conn:
|
||||
yield cls(conn)
|
||||
|
||||
async def setup(self) -> None:
|
||||
"""Set up the checkpoint database asynchronously.
|
||||
|
||||
This method creates the necessary tables in the DuckDB database if they don't
|
||||
already exist and runs database migrations. It MUST be called directly by the user
|
||||
the first time checkpointer is used.
|
||||
"""
|
||||
async with self.lock:
|
||||
with self.conn.cursor() as cur:
|
||||
try:
|
||||
await asyncio.to_thread(
|
||||
cur.execute,
|
||||
"SELECT v FROM checkpoint_migrations ORDER BY v DESC LIMIT 1",
|
||||
)
|
||||
row = await asyncio.to_thread(cur.fetchone)
|
||||
if row is None:
|
||||
version = -1
|
||||
else:
|
||||
version = row[0]
|
||||
except duckdb.CatalogException:
|
||||
version = -1
|
||||
for v, migration in zip(
|
||||
range(version + 1, len(self.MIGRATIONS)),
|
||||
self.MIGRATIONS[version + 1 :],
|
||||
):
|
||||
await asyncio.to_thread(cur.execute, migration)
|
||||
await asyncio.to_thread(
|
||||
cur.execute,
|
||||
"INSERT INTO checkpoint_migrations (v) VALUES (?)",
|
||||
[v],
|
||||
)
|
||||
|
||||
async def alist(
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
*,
|
||||
filter: Optional[dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
) -> AsyncIterator[CheckpointTuple]:
|
||||
"""List checkpoints from the database asynchronously.
|
||||
|
||||
This method retrieves a list of checkpoint tuples from the DuckDB database based
|
||||
on the provided config. The checkpoints are ordered by checkpoint ID in descending order (newest first).
|
||||
|
||||
Args:
|
||||
config (Optional[RunnableConfig]): Base configuration for filtering checkpoints.
|
||||
filter (Optional[Dict[str, Any]]): Additional filtering criteria for metadata.
|
||||
before (Optional[RunnableConfig]): If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
|
||||
limit (Optional[int]): Maximum number of checkpoints to return.
|
||||
|
||||
Yields:
|
||||
AsyncIterator[CheckpointTuple]: An asynchronous iterator of matching checkpoint tuples.
|
||||
"""
|
||||
where, args = self._search_where(config, filter, before)
|
||||
query = self.SELECT_SQL + where + " ORDER BY checkpoint_id DESC"
|
||||
if limit:
|
||||
query += f" LIMIT {limit}"
|
||||
# if we change this to use .stream() we need to make sure to close the cursor
|
||||
async with self._cursor() as cur:
|
||||
await asyncio.to_thread(cur.execute, query, args)
|
||||
results = await asyncio.to_thread(cur.fetchall)
|
||||
for value in results:
|
||||
(
|
||||
thread_id,
|
||||
checkpoint,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
parent_checkpoint_id,
|
||||
metadata,
|
||||
channel_values,
|
||||
pending_writes,
|
||||
pending_sends,
|
||||
) = value
|
||||
yield CheckpointTuple(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint_id,
|
||||
}
|
||||
},
|
||||
await asyncio.to_thread(
|
||||
self._load_checkpoint,
|
||||
checkpoint,
|
||||
channel_values,
|
||||
pending_sends,
|
||||
),
|
||||
self._load_metadata(metadata),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": parent_checkpoint_id,
|
||||
}
|
||||
}
|
||||
if parent_checkpoint_id
|
||||
else None
|
||||
),
|
||||
await asyncio.to_thread(self._load_writes, pending_writes),
|
||||
)
|
||||
|
||||
async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
|
||||
"""Get a checkpoint tuple from the database asynchronously.
|
||||
|
||||
This method retrieves a checkpoint tuple from the DuckDBdatabase based on the
|
||||
provided config. If the config contains a "checkpoint_id" key, the checkpoint with
|
||||
the matching thread ID and "checkpoint_id" is retrieved. Otherwise, the latest checkpoint
|
||||
for the given thread ID is retrieved.
|
||||
|
||||
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", "")
|
||||
if checkpoint_id:
|
||||
args: tuple[Any, ...] = (thread_id, checkpoint_ns, checkpoint_id)
|
||||
where = "WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?"
|
||||
else:
|
||||
args = (thread_id, checkpoint_ns)
|
||||
where = "WHERE thread_id = ? AND checkpoint_ns = ? ORDER BY checkpoint_id DESC LIMIT 1"
|
||||
|
||||
async with self._cursor() as cur:
|
||||
await asyncio.to_thread(
|
||||
cur.execute,
|
||||
self.SELECT_SQL + where,
|
||||
args,
|
||||
)
|
||||
|
||||
value = await asyncio.to_thread(cur.fetchone)
|
||||
if value:
|
||||
(
|
||||
thread_id,
|
||||
checkpoint,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
parent_checkpoint_id,
|
||||
metadata,
|
||||
channel_values,
|
||||
pending_writes,
|
||||
pending_sends,
|
||||
) = value
|
||||
return CheckpointTuple(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint_id,
|
||||
}
|
||||
},
|
||||
await asyncio.to_thread(
|
||||
self._load_checkpoint,
|
||||
checkpoint,
|
||||
channel_values,
|
||||
pending_sends,
|
||||
),
|
||||
self._load_metadata(metadata),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": parent_checkpoint_id,
|
||||
}
|
||||
}
|
||||
if parent_checkpoint_id
|
||||
else None
|
||||
),
|
||||
await asyncio.to_thread(self._load_writes, pending_writes),
|
||||
)
|
||||
|
||||
async def aput(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
new_versions: ChannelVersions,
|
||||
) -> RunnableConfig:
|
||||
"""Save a checkpoint to the database asynchronously.
|
||||
|
||||
This method saves a checkpoint to the DuckDB database. The checkpoint is associated
|
||||
with the provided config and its parent config (if any).
|
||||
|
||||
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.
|
||||
"""
|
||||
configurable = config["configurable"].copy()
|
||||
thread_id = configurable.pop("thread_id")
|
||||
checkpoint_ns = configurable.pop("checkpoint_ns")
|
||||
checkpoint_id = configurable.pop(
|
||||
"checkpoint_id", configurable.pop("thread_ts", None)
|
||||
)
|
||||
|
||||
copy = checkpoint.copy()
|
||||
next_config = {
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint["id"],
|
||||
}
|
||||
}
|
||||
|
||||
checkpoint_blobs = await asyncio.to_thread(
|
||||
self._dump_blobs,
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
copy.pop("channel_values"), # type: ignore[misc]
|
||||
new_versions,
|
||||
)
|
||||
async with self._cursor() as cur:
|
||||
if checkpoint_blobs:
|
||||
await asyncio.to_thread(
|
||||
cur.executemany, self.UPSERT_CHECKPOINT_BLOBS_SQL, checkpoint_blobs
|
||||
)
|
||||
await asyncio.to_thread(
|
||||
cur.execute,
|
||||
self.UPSERT_CHECKPOINTS_SQL,
|
||||
(
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint["id"],
|
||||
checkpoint_id,
|
||||
self._dump_checkpoint(copy),
|
||||
self._dump_metadata(metadata),
|
||||
),
|
||||
)
|
||||
|
||||
return next_config
|
||||
|
||||
async def aput_writes(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
task_id: str,
|
||||
) -> None:
|
||||
"""Store intermediate writes linked to a checkpoint asynchronously.
|
||||
|
||||
This method saves intermediate writes associated with a checkpoint to the database.
|
||||
|
||||
Args:
|
||||
config (RunnableConfig): Configuration of the related checkpoint.
|
||||
writes (Sequence[Tuple[str, Any]]): List of writes to store, each as (channel, value) pair.
|
||||
task_id (str): Identifier for the task creating the writes.
|
||||
"""
|
||||
query = (
|
||||
self.UPSERT_CHECKPOINT_WRITES_SQL
|
||||
if all(w[0] in WRITES_IDX_MAP for w in writes)
|
||||
else self.INSERT_CHECKPOINT_WRITES_SQL
|
||||
)
|
||||
params = await asyncio.to_thread(
|
||||
self._dump_writes,
|
||||
config["configurable"]["thread_id"],
|
||||
config["configurable"]["checkpoint_ns"],
|
||||
config["configurable"]["checkpoint_id"],
|
||||
task_id,
|
||||
writes,
|
||||
)
|
||||
async with self._cursor() as cur:
|
||||
await asyncio.to_thread(cur.executemany, query, params)
|
||||
|
||||
@asynccontextmanager
|
||||
async def _cursor(self) -> AsyncIterator[duckdb.DuckDBPyConnection]:
|
||||
async with self.lock:
|
||||
with self.conn.cursor() as cur:
|
||||
yield cur
|
||||
|
||||
def list(
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
*,
|
||||
filter: Optional[dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
) -> Iterator[CheckpointTuple]:
|
||||
"""List checkpoints from the database.
|
||||
|
||||
This method retrieves a list of checkpoint tuples from the DuckDB database based
|
||||
on the provided config. The checkpoints are ordered by checkpoint ID in descending order (newest first).
|
||||
|
||||
Args:
|
||||
config (Optional[RunnableConfig]): Base configuration for filtering checkpoints.
|
||||
filter (Optional[Dict[str, Any]]): Additional filtering criteria for metadata.
|
||||
before (Optional[RunnableConfig]): If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
|
||||
limit (Optional[int]): Maximum number of checkpoints to return.
|
||||
|
||||
Yields:
|
||||
Iterator[CheckpointTuple]: An iterator of matching checkpoint tuples.
|
||||
"""
|
||||
aiter_ = self.alist(config, filter=filter, before=before, limit=limit)
|
||||
while True:
|
||||
try:
|
||||
yield asyncio.run_coroutine_threadsafe(
|
||||
anext(aiter_),
|
||||
self.loop,
|
||||
).result()
|
||||
except StopAsyncIteration:
|
||||
break
|
||||
|
||||
def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
|
||||
"""Get a checkpoint tuple from the database.
|
||||
|
||||
This method retrieves a checkpoint tuple from the DuckDB database based on the
|
||||
provided config. If the config contains a "checkpoint_id" key, the checkpoint with
|
||||
the matching thread ID and "checkpoint_id" is retrieved. Otherwise, the latest checkpoint
|
||||
for the given thread ID is retrieved.
|
||||
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
# check if we are in the main thread, only bg threads can block
|
||||
# we don't check in other methods to avoid the overhead
|
||||
if asyncio.get_running_loop() is self.loop:
|
||||
raise asyncio.InvalidStateError(
|
||||
"Synchronous calls to AsyncDuckDBSaver are only allowed from a "
|
||||
"different thread. From the main thread, use the async interface."
|
||||
"For example, use `await checkpointer.aget_tuple(...)` or `await "
|
||||
"graph.ainvoke(...)`."
|
||||
)
|
||||
except RuntimeError:
|
||||
pass
|
||||
return asyncio.run_coroutine_threadsafe(
|
||||
self.aget_tuple(config), self.loop
|
||||
).result()
|
||||
|
||||
def put(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
new_versions: ChannelVersions,
|
||||
) -> RunnableConfig:
|
||||
"""Save a checkpoint to the database.
|
||||
|
||||
This method saves a checkpoint to the DuckDB database. The checkpoint is associated
|
||||
with the provided config and its parent config (if any).
|
||||
|
||||
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.
|
||||
"""
|
||||
return asyncio.run_coroutine_threadsafe(
|
||||
self.aput(config, checkpoint, metadata, new_versions), self.loop
|
||||
).result()
|
||||
|
||||
def put_writes(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
task_id: str,
|
||||
) -> None:
|
||||
"""Store intermediate writes linked to a checkpoint.
|
||||
|
||||
This method saves intermediate writes associated with a checkpoint to the database.
|
||||
|
||||
Args:
|
||||
config (RunnableConfig): Configuration of the related checkpoint.
|
||||
writes (Sequence[Tuple[str, Any]]): List of writes to store, each as (channel, value) pair.
|
||||
task_id (str): Identifier for the task creating the writes.
|
||||
"""
|
||||
return asyncio.run_coroutine_threadsafe(
|
||||
self.aput_writes(config, writes, task_id), self.loop
|
||||
).result()
|
||||
@@ -1,290 +0,0 @@
|
||||
import json
|
||||
import random
|
||||
from typing import Any, List, Optional, Sequence, Tuple, cast
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from langgraph.checkpoint.base import (
|
||||
WRITES_IDX_MAP,
|
||||
BaseCheckpointSaver,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointMetadata,
|
||||
get_checkpoint_id,
|
||||
)
|
||||
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
||||
from langgraph.checkpoint.serde.types import TASKS, ChannelProtocol
|
||||
|
||||
MetadataInput = Optional[dict[str, Any]]
|
||||
|
||||
"""
|
||||
To add a new migration, add a new string to the MIGRATIONS list.
|
||||
The position of the migration in the list is the version number.
|
||||
"""
|
||||
MIGRATIONS = [
|
||||
"""CREATE TABLE IF NOT EXISTS checkpoint_migrations (
|
||||
v INTEGER PRIMARY KEY
|
||||
);""",
|
||||
"""CREATE TABLE IF NOT EXISTS checkpoints (
|
||||
thread_id TEXT NOT NULL,
|
||||
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
||||
checkpoint_id TEXT NOT NULL,
|
||||
parent_checkpoint_id TEXT,
|
||||
type TEXT,
|
||||
checkpoint JSON NOT NULL,
|
||||
metadata JSON NOT NULL DEFAULT '{}',
|
||||
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
|
||||
);""",
|
||||
"""CREATE TABLE IF NOT EXISTS checkpoint_blobs (
|
||||
thread_id TEXT NOT NULL,
|
||||
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
||||
channel TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
blob BLOB,
|
||||
PRIMARY KEY (thread_id, checkpoint_ns, channel, version)
|
||||
);""",
|
||||
"""CREATE TABLE IF NOT EXISTS checkpoint_writes (
|
||||
thread_id TEXT NOT NULL,
|
||||
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
||||
checkpoint_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
idx INTEGER NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
type TEXT,
|
||||
blob BLOB NOT NULL,
|
||||
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
|
||||
);""",
|
||||
]
|
||||
|
||||
SELECT_SQL = f"""
|
||||
select
|
||||
thread_id,
|
||||
checkpoint,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
parent_checkpoint_id,
|
||||
metadata,
|
||||
(
|
||||
select array_agg(array[bl.channel::bytea, bl.type::bytea, bl.blob])
|
||||
from (
|
||||
SELECT unnest(json_keys(json_extract(checkpoint, '$.channel_versions'))) as key
|
||||
) cv
|
||||
inner join checkpoint_blobs bl
|
||||
on bl.thread_id = checkpoints.thread_id
|
||||
and bl.checkpoint_ns = checkpoints.checkpoint_ns
|
||||
and bl.channel = cv.key
|
||||
and bl.version = json_extract_string(checkpoint, '$.channel_versions.' || cv.key)
|
||||
) as channel_values,
|
||||
(
|
||||
select
|
||||
array_agg(array[cw.task_id::blob, cw.channel::blob, cw.type::blob, cw.blob])
|
||||
from checkpoint_writes cw
|
||||
where cw.thread_id = checkpoints.thread_id
|
||||
and cw.checkpoint_ns = checkpoints.checkpoint_ns
|
||||
and cw.checkpoint_id = checkpoints.checkpoint_id
|
||||
) as pending_writes,
|
||||
(
|
||||
select array_agg(array[cw.type::blob, cw.blob])
|
||||
from checkpoint_writes cw
|
||||
where cw.thread_id = checkpoints.thread_id
|
||||
and cw.checkpoint_ns = checkpoints.checkpoint_ns
|
||||
and cw.checkpoint_id = checkpoints.parent_checkpoint_id
|
||||
and cw.channel = '{TASKS}'
|
||||
) as pending_sends
|
||||
from checkpoints """
|
||||
|
||||
UPSERT_CHECKPOINT_BLOBS_SQL = """
|
||||
INSERT INTO checkpoint_blobs (thread_id, checkpoint_ns, channel, version, type, blob)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (thread_id, checkpoint_ns, channel, version) DO NOTHING
|
||||
"""
|
||||
|
||||
UPSERT_CHECKPOINTS_SQL = """
|
||||
INSERT INTO checkpoints (thread_id, checkpoint_ns, checkpoint_id, parent_checkpoint_id, checkpoint, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (thread_id, checkpoint_ns, checkpoint_id)
|
||||
DO UPDATE SET
|
||||
checkpoint = EXCLUDED.checkpoint,
|
||||
metadata = EXCLUDED.metadata;
|
||||
"""
|
||||
|
||||
UPSERT_CHECKPOINT_WRITES_SQL = """
|
||||
INSERT INTO checkpoint_writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, blob)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (thread_id, checkpoint_ns, checkpoint_id, task_id, idx) DO UPDATE SET
|
||||
channel = EXCLUDED.channel,
|
||||
type = EXCLUDED.type,
|
||||
blob = EXCLUDED.blob;
|
||||
"""
|
||||
|
||||
INSERT_CHECKPOINT_WRITES_SQL = """
|
||||
INSERT INTO checkpoint_writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, blob)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (thread_id, checkpoint_ns, checkpoint_id, task_id, idx) DO NOTHING
|
||||
"""
|
||||
|
||||
|
||||
class BaseDuckDBSaver(BaseCheckpointSaver[str]):
|
||||
SELECT_SQL = SELECT_SQL
|
||||
MIGRATIONS = MIGRATIONS
|
||||
UPSERT_CHECKPOINT_BLOBS_SQL = UPSERT_CHECKPOINT_BLOBS_SQL
|
||||
UPSERT_CHECKPOINTS_SQL = UPSERT_CHECKPOINTS_SQL
|
||||
UPSERT_CHECKPOINT_WRITES_SQL = UPSERT_CHECKPOINT_WRITES_SQL
|
||||
INSERT_CHECKPOINT_WRITES_SQL = INSERT_CHECKPOINT_WRITES_SQL
|
||||
|
||||
jsonplus_serde = JsonPlusSerializer()
|
||||
|
||||
def _load_checkpoint(
|
||||
self,
|
||||
checkpoint_json_str: str,
|
||||
channel_values: list[tuple[bytes, bytes, bytes]],
|
||||
pending_sends: list[tuple[bytes, bytes]],
|
||||
) -> Checkpoint:
|
||||
checkpoint = json.loads(checkpoint_json_str)
|
||||
return {
|
||||
**checkpoint,
|
||||
"pending_sends": [
|
||||
self.serde.loads_typed((c.decode(), b)) for c, b in pending_sends or []
|
||||
],
|
||||
"channel_values": self._load_blobs(channel_values),
|
||||
}
|
||||
|
||||
def _dump_checkpoint(self, checkpoint: Checkpoint) -> dict[str, Any]:
|
||||
return {**checkpoint, "pending_sends": []}
|
||||
|
||||
def _load_blobs(
|
||||
self, blob_values: list[tuple[bytes, bytes, bytes]]
|
||||
) -> dict[str, Any]:
|
||||
if not blob_values:
|
||||
return {}
|
||||
return {
|
||||
k.decode(): self.serde.loads_typed((t.decode(), v))
|
||||
for k, t, v in blob_values
|
||||
if t.decode() != "empty"
|
||||
}
|
||||
|
||||
def _dump_blobs(
|
||||
self,
|
||||
thread_id: str,
|
||||
checkpoint_ns: str,
|
||||
values: dict[str, Any],
|
||||
versions: ChannelVersions,
|
||||
) -> list[tuple[str, str, str, str, str, Optional[bytes]]]:
|
||||
if not versions:
|
||||
return []
|
||||
|
||||
return [
|
||||
(
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
k,
|
||||
cast(str, ver),
|
||||
*(
|
||||
self.serde.dumps_typed(values[k])
|
||||
if k in values
|
||||
else ("empty", None)
|
||||
),
|
||||
)
|
||||
for k, ver in versions.items()
|
||||
]
|
||||
|
||||
def _load_writes(
|
||||
self, writes: list[tuple[bytes, bytes, bytes, bytes]]
|
||||
) -> list[tuple[str, str, Any]]:
|
||||
return (
|
||||
[
|
||||
(
|
||||
tid.decode(),
|
||||
channel.decode(),
|
||||
self.serde.loads_typed((t.decode(), v)),
|
||||
)
|
||||
for tid, channel, t, v in writes
|
||||
]
|
||||
if writes
|
||||
else []
|
||||
)
|
||||
|
||||
def _dump_writes(
|
||||
self,
|
||||
thread_id: str,
|
||||
checkpoint_ns: str,
|
||||
checkpoint_id: str,
|
||||
task_id: str,
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
) -> list[tuple[str, str, str, str, int, str, str, bytes]]:
|
||||
return [
|
||||
(
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id,
|
||||
task_id,
|
||||
WRITES_IDX_MAP.get(channel, idx),
|
||||
channel,
|
||||
*self.serde.dumps_typed(value),
|
||||
)
|
||||
for idx, (channel, value) in enumerate(writes)
|
||||
]
|
||||
|
||||
def _load_metadata(self, metadata_json_str: str) -> CheckpointMetadata:
|
||||
return self.jsonplus_serde.loads(metadata_json_str.encode())
|
||||
|
||||
def _dump_metadata(self, metadata: CheckpointMetadata) -> str:
|
||||
serialized_metadata = self.jsonplus_serde.dumps(metadata)
|
||||
# NOTE: we're using JSON serializer (not msgpack), so we need to remove null characters before writing
|
||||
return serialized_metadata.decode().replace("\\u0000", "")
|
||||
|
||||
def get_next_version(self, current: Optional[str], channel: ChannelProtocol) -> str:
|
||||
if current is None:
|
||||
current_v = 0
|
||||
elif isinstance(current, int):
|
||||
current_v = current
|
||||
else:
|
||||
current_v = int(current.split(".")[0])
|
||||
next_v = current_v + 1
|
||||
next_h = random.random()
|
||||
return f"{next_v:032}.{next_h:016}"
|
||||
|
||||
def _search_where(
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
filter: MetadataInput,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
) -> Tuple[str, List[Any]]:
|
||||
"""Return WHERE clause predicates for alist() given config, filter, before.
|
||||
|
||||
This method returns a tuple of a string and a tuple of values. The string
|
||||
is the parametered WHERE clause predicate (including the WHERE keyword):
|
||||
"WHERE column1 = $1 AND column2 IS $2". The list of values contains the
|
||||
values for each of the corresponding parameters.
|
||||
"""
|
||||
wheres = []
|
||||
param_values = []
|
||||
|
||||
# construct predicate for config filter
|
||||
if config:
|
||||
wheres.append("thread_id = ?")
|
||||
param_values.append(config["configurable"]["thread_id"])
|
||||
checkpoint_ns = config["configurable"].get("checkpoint_ns")
|
||||
if checkpoint_ns is not None:
|
||||
wheres.append("checkpoint_ns = ?")
|
||||
param_values.append(checkpoint_ns)
|
||||
|
||||
if checkpoint_id := get_checkpoint_id(config):
|
||||
wheres.append("checkpoint_id = ?")
|
||||
param_values.append(checkpoint_id)
|
||||
|
||||
# construct predicate for metadata filter
|
||||
if filter:
|
||||
wheres.append("json_contains(metadata, ?)")
|
||||
param_values.append(json.dumps(filter))
|
||||
|
||||
# construct predicate for `before`
|
||||
if before is not None:
|
||||
wheres.append("checkpoint_id < ?")
|
||||
param_values.append(get_checkpoint_id(before))
|
||||
|
||||
return (
|
||||
"WHERE " + " AND ".join(wheres) if wheres else "",
|
||||
param_values,
|
||||
)
|
||||
@@ -1,4 +0,0 @@
|
||||
from langgraph.store.duckdb.aio import AsyncDuckDBStore
|
||||
from langgraph.store.duckdb.base import DuckDBStore
|
||||
|
||||
__all__ = ["AsyncDuckDBStore", "DuckDBStore"]
|
||||
@@ -1,195 +0,0 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import (
|
||||
AsyncIterator,
|
||||
Iterable,
|
||||
Sequence,
|
||||
cast,
|
||||
)
|
||||
|
||||
import duckdb
|
||||
from langgraph.store.base import GetOp, ListNamespacesOp, Op, PutOp, Result, SearchOp
|
||||
from langgraph.store.base.batch import AsyncBatchedBaseStore
|
||||
from langgraph.store.duckdb.base import (
|
||||
BaseDuckDBStore,
|
||||
_convert_ns,
|
||||
_group_ops,
|
||||
_row_to_item,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AsyncDuckDBStore(AsyncBatchedBaseStore, BaseDuckDBStore):
|
||||
def __init__(
|
||||
self,
|
||||
conn: duckdb.DuckDBPyConnection,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.conn = conn
|
||||
self.loop = asyncio.get_running_loop()
|
||||
|
||||
async def abatch(self, ops: Iterable[Op]) -> list[Result]:
|
||||
grouped_ops, num_ops = _group_ops(ops)
|
||||
results: list[Result] = [None] * num_ops
|
||||
|
||||
tasks = []
|
||||
|
||||
if GetOp in grouped_ops:
|
||||
tasks.append(
|
||||
self._batch_get_ops(
|
||||
cast(Sequence[tuple[int, GetOp]], grouped_ops[GetOp]), results
|
||||
)
|
||||
)
|
||||
|
||||
if PutOp in grouped_ops:
|
||||
tasks.append(
|
||||
self._batch_put_ops(
|
||||
cast(Sequence[tuple[int, PutOp]], grouped_ops[PutOp])
|
||||
)
|
||||
)
|
||||
|
||||
if SearchOp in grouped_ops:
|
||||
tasks.append(
|
||||
self._batch_search_ops(
|
||||
cast(Sequence[tuple[int, SearchOp]], grouped_ops[SearchOp]),
|
||||
results,
|
||||
)
|
||||
)
|
||||
|
||||
if ListNamespacesOp in grouped_ops:
|
||||
tasks.append(
|
||||
self._batch_list_namespaces_ops(
|
||||
cast(
|
||||
Sequence[tuple[int, ListNamespacesOp]],
|
||||
grouped_ops[ListNamespacesOp],
|
||||
),
|
||||
results,
|
||||
)
|
||||
)
|
||||
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
return results
|
||||
|
||||
def batch(self, ops: Iterable[Op]) -> list[Result]:
|
||||
return asyncio.run_coroutine_threadsafe(self.abatch(ops), self.loop).result()
|
||||
|
||||
async def _batch_get_ops(
|
||||
self,
|
||||
get_ops: Sequence[tuple[int, GetOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
cursors = []
|
||||
for query, params, namespace, items in self._get_batch_GET_ops_queries(get_ops):
|
||||
cur = self.conn.cursor()
|
||||
await asyncio.to_thread(cur.execute, query, params)
|
||||
cursors.append((cur, namespace, items))
|
||||
|
||||
for cur, namespace, items in cursors:
|
||||
rows = await asyncio.to_thread(cur.fetchall)
|
||||
key_to_row = {row[1]: row for row in rows}
|
||||
for idx, key in items:
|
||||
row = key_to_row.get(key)
|
||||
if row:
|
||||
results[idx] = _row_to_item(namespace, row)
|
||||
else:
|
||||
results[idx] = None
|
||||
|
||||
async def _batch_put_ops(
|
||||
self,
|
||||
put_ops: Sequence[tuple[int, PutOp]],
|
||||
) -> None:
|
||||
queries = self._get_batch_PUT_queries(put_ops)
|
||||
for query, params in queries:
|
||||
cur = self.conn.cursor()
|
||||
await asyncio.to_thread(cur.execute, query, params)
|
||||
|
||||
async def _batch_search_ops(
|
||||
self,
|
||||
search_ops: Sequence[tuple[int, SearchOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
queries = self._get_batch_search_queries(search_ops)
|
||||
cursors: list[tuple[duckdb.DuckDBPyConnection, int]] = []
|
||||
|
||||
for (query, params), (idx, _) in zip(queries, search_ops):
|
||||
cur = self.conn.cursor()
|
||||
await asyncio.to_thread(cur.execute, query, params)
|
||||
cursors.append((cur, idx))
|
||||
|
||||
for cur, idx in cursors:
|
||||
rows = await asyncio.to_thread(cur.fetchall)
|
||||
items = [_row_to_item(_convert_ns(row[0]), row) for row in rows]
|
||||
results[idx] = items
|
||||
|
||||
async def _batch_list_namespaces_ops(
|
||||
self,
|
||||
list_ops: Sequence[tuple[int, ListNamespacesOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
queries = self._get_batch_list_namespaces_queries(list_ops)
|
||||
cursors: list[tuple[duckdb.DuckDBPyConnection, int]] = []
|
||||
for (query, params), (idx, _) in zip(queries, list_ops):
|
||||
cur = self.conn.cursor()
|
||||
await asyncio.to_thread(cur.execute, query, params)
|
||||
cursors.append((cur, idx))
|
||||
|
||||
for cur, idx in cursors:
|
||||
rows = cast(list[tuple], await asyncio.to_thread(cur.fetchall))
|
||||
namespaces = [_convert_ns(row[0]) for row in rows]
|
||||
results[idx] = namespaces
|
||||
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def from_conn_string(
|
||||
cls,
|
||||
conn_string: str,
|
||||
) -> AsyncIterator["AsyncDuckDBStore"]:
|
||||
"""Create a new AsyncDuckDBStore instance from a connection string.
|
||||
|
||||
Args:
|
||||
conn_string (str): The DuckDB connection info string.
|
||||
|
||||
Returns:
|
||||
AsyncDuckDBStore: A new AsyncDuckDBStore instance.
|
||||
"""
|
||||
with duckdb.connect(conn_string) as conn:
|
||||
yield cls(conn)
|
||||
|
||||
async def setup(self) -> None:
|
||||
"""Set up the store database asynchronously.
|
||||
|
||||
This method creates the necessary tables in the DuckDB database if they don't
|
||||
already exist and runs database migrations. It is called automatically when needed and should not be called
|
||||
directly by the user.
|
||||
"""
|
||||
cur = self.conn.cursor()
|
||||
try:
|
||||
await asyncio.to_thread(
|
||||
cur.execute, "SELECT v FROM store_migrations ORDER BY v DESC LIMIT 1"
|
||||
)
|
||||
row = await asyncio.to_thread(cur.fetchone)
|
||||
if row is None:
|
||||
version = -1
|
||||
else:
|
||||
version = row[0]
|
||||
except duckdb.CatalogException:
|
||||
version = -1
|
||||
# Create store_migrations table if it doesn't exist
|
||||
await asyncio.to_thread(
|
||||
cur.execute,
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS store_migrations (
|
||||
v INTEGER PRIMARY KEY
|
||||
)
|
||||
""",
|
||||
)
|
||||
for v, migration in enumerate(
|
||||
self.MIGRATIONS[version + 1 :], start=version + 1
|
||||
):
|
||||
await asyncio.to_thread(cur.execute, migration)
|
||||
await asyncio.to_thread(
|
||||
cur.execute, "INSERT INTO store_migrations (v) VALUES (?)", (v,)
|
||||
)
|
||||
@@ -1,408 +0,0 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from typing import (
|
||||
Any,
|
||||
Generic,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
import duckdb
|
||||
from langgraph.store.base import (
|
||||
BaseStore,
|
||||
GetOp,
|
||||
Item,
|
||||
ListNamespacesOp,
|
||||
Op,
|
||||
PutOp,
|
||||
Result,
|
||||
SearchItem,
|
||||
SearchOp,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MIGRATIONS = [
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS store (
|
||||
prefix TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value JSON NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT now(),
|
||||
updated_at TIMESTAMP DEFAULT now(),
|
||||
PRIMARY KEY (prefix, key)
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS store_prefix_idx ON store (prefix);
|
||||
""",
|
||||
]
|
||||
|
||||
C = TypeVar("C", bound=duckdb.DuckDBPyConnection)
|
||||
|
||||
|
||||
class BaseDuckDBStore(Generic[C]):
|
||||
MIGRATIONS = MIGRATIONS
|
||||
conn: C
|
||||
|
||||
def _get_batch_GET_ops_queries(
|
||||
self,
|
||||
get_ops: Sequence[tuple[int, GetOp]],
|
||||
) -> list[tuple[str, tuple, tuple[str, ...], list]]:
|
||||
namespace_groups = defaultdict(list)
|
||||
for idx, op in get_ops:
|
||||
namespace_groups[op.namespace].append((idx, op.key))
|
||||
results = []
|
||||
for namespace, items in namespace_groups.items():
|
||||
_, keys = zip(*items)
|
||||
keys_to_query = ",".join(["?"] * len(keys))
|
||||
query = f"""
|
||||
SELECT prefix, key, value, created_at, updated_at
|
||||
FROM store
|
||||
WHERE prefix = ? AND key IN ({keys_to_query})
|
||||
"""
|
||||
params = (_namespace_to_text(namespace), *keys)
|
||||
results.append((query, params, namespace, items))
|
||||
return results
|
||||
|
||||
def _get_batch_PUT_queries(
|
||||
self,
|
||||
put_ops: Sequence[tuple[int, PutOp]],
|
||||
) -> list[tuple[str, Sequence]]:
|
||||
inserts: list[PutOp] = []
|
||||
deletes: list[PutOp] = []
|
||||
for _, op in put_ops:
|
||||
if op.value is None:
|
||||
deletes.append(op)
|
||||
else:
|
||||
inserts.append(op)
|
||||
|
||||
queries: list[tuple[str, Sequence]] = []
|
||||
|
||||
if deletes:
|
||||
namespace_groups: dict[tuple[str, ...], list[str]] = defaultdict(list)
|
||||
for op in deletes:
|
||||
namespace_groups[op.namespace].append(op.key)
|
||||
for namespace, keys in namespace_groups.items():
|
||||
placeholders = ",".join(["?"] * len(keys))
|
||||
query = (
|
||||
f"DELETE FROM store WHERE prefix = ? AND key IN ({placeholders})"
|
||||
)
|
||||
params = (_namespace_to_text(namespace), *keys)
|
||||
queries.append((query, params))
|
||||
if inserts:
|
||||
values = []
|
||||
insertion_params = []
|
||||
for op in inserts:
|
||||
values.append("(?, ?, ?, now(), now())")
|
||||
insertion_params.extend(
|
||||
[
|
||||
_namespace_to_text(op.namespace),
|
||||
op.key,
|
||||
json.dumps(op.value),
|
||||
]
|
||||
)
|
||||
values_str = ",".join(values)
|
||||
query = f"""
|
||||
INSERT INTO store (prefix, key, value, created_at, updated_at)
|
||||
VALUES {values_str}
|
||||
ON CONFLICT (prefix, key) DO UPDATE
|
||||
SET value = EXCLUDED.value, updated_at = now()
|
||||
"""
|
||||
queries.append((query, insertion_params))
|
||||
|
||||
return queries
|
||||
|
||||
def _get_batch_search_queries(
|
||||
self,
|
||||
search_ops: Sequence[tuple[int, SearchOp]],
|
||||
) -> list[tuple[str, Sequence]]:
|
||||
queries: list[tuple[str, Sequence]] = []
|
||||
for _, op in search_ops:
|
||||
query = """
|
||||
SELECT prefix, key, value, created_at, updated_at
|
||||
FROM store
|
||||
WHERE prefix LIKE ?
|
||||
"""
|
||||
params: list = [f"{_namespace_to_text(op.namespace_prefix)}%"]
|
||||
|
||||
if op.filter:
|
||||
filter_conditions = []
|
||||
for key, value in op.filter.items():
|
||||
filter_conditions.append(f"json_extract(value, '$.{key}') = ?")
|
||||
params.append(json.dumps(value))
|
||||
query += " AND " + " AND ".join(filter_conditions)
|
||||
|
||||
query += " ORDER BY updated_at DESC LIMIT ? OFFSET ?"
|
||||
params.extend([op.limit, op.offset])
|
||||
|
||||
queries.append((query, params))
|
||||
return queries
|
||||
|
||||
def _get_batch_list_namespaces_queries(
|
||||
self,
|
||||
list_ops: Sequence[tuple[int, ListNamespacesOp]],
|
||||
) -> list[tuple[str, Sequence]]:
|
||||
queries: list[tuple[str, Sequence]] = []
|
||||
for _, op in list_ops:
|
||||
query = """
|
||||
WITH split_prefix AS (
|
||||
SELECT
|
||||
prefix,
|
||||
string_split(prefix, '.') AS parts
|
||||
FROM store
|
||||
)
|
||||
SELECT DISTINCT ON (truncated_prefix)
|
||||
CASE
|
||||
WHEN ? IS NOT NULL THEN
|
||||
array_to_string(array_slice(parts, 1, ?), '.')
|
||||
ELSE prefix
|
||||
END AS truncated_prefix,
|
||||
prefix
|
||||
FROM split_prefix
|
||||
"""
|
||||
params: list[Any] = [op.max_depth, op.max_depth]
|
||||
|
||||
conditions = []
|
||||
if op.match_conditions:
|
||||
for condition in op.match_conditions:
|
||||
if condition.match_type == "prefix":
|
||||
conditions.append("prefix LIKE ?")
|
||||
params.append(
|
||||
f"{_namespace_to_text(condition.path, handle_wildcards=True)}%"
|
||||
)
|
||||
elif condition.match_type == "suffix":
|
||||
conditions.append("prefix LIKE ?")
|
||||
params.append(
|
||||
f"%{_namespace_to_text(condition.path, handle_wildcards=True)}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Unknown match_type in list_namespaces: {condition.match_type}"
|
||||
)
|
||||
|
||||
if conditions:
|
||||
query += " WHERE " + " AND ".join(conditions)
|
||||
|
||||
query += " ORDER BY prefix LIMIT ? OFFSET ?"
|
||||
params.extend([op.limit, op.offset])
|
||||
queries.append((query, params))
|
||||
|
||||
return queries
|
||||
|
||||
|
||||
class DuckDBStore(BaseStore, BaseDuckDBStore[duckdb.DuckDBPyConnection]):
|
||||
def __init__(
|
||||
self,
|
||||
conn: duckdb.DuckDBPyConnection,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.conn = conn
|
||||
|
||||
def batch(self, ops: Iterable[Op]) -> list[Result]:
|
||||
grouped_ops, num_ops = _group_ops(ops)
|
||||
results: list[Result] = [None] * num_ops
|
||||
|
||||
if GetOp in grouped_ops:
|
||||
self._batch_get_ops(
|
||||
cast(Sequence[tuple[int, GetOp]], grouped_ops[GetOp]), results
|
||||
)
|
||||
|
||||
if PutOp in grouped_ops:
|
||||
self._batch_put_ops(cast(Sequence[tuple[int, PutOp]], grouped_ops[PutOp]))
|
||||
|
||||
if SearchOp in grouped_ops:
|
||||
self._batch_search_ops(
|
||||
cast(Sequence[tuple[int, SearchOp]], grouped_ops[SearchOp]),
|
||||
results,
|
||||
)
|
||||
|
||||
if ListNamespacesOp in grouped_ops:
|
||||
self._batch_list_namespaces_ops(
|
||||
cast(
|
||||
Sequence[tuple[int, ListNamespacesOp]],
|
||||
grouped_ops[ListNamespacesOp],
|
||||
),
|
||||
results,
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
async def abatch(self, ops: Iterable[Op]) -> list[Result]:
|
||||
return await asyncio.get_running_loop().run_in_executor(None, self.batch, ops)
|
||||
|
||||
def _batch_get_ops(
|
||||
self,
|
||||
get_ops: Sequence[tuple[int, GetOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
cursors = []
|
||||
for query, params, namespace, items in self._get_batch_GET_ops_queries(get_ops):
|
||||
cur = self.conn.cursor()
|
||||
cur.execute(query, params)
|
||||
cursors.append((cur, namespace, items))
|
||||
|
||||
for cur, namespace, items in cursors:
|
||||
rows = cur.fetchall()
|
||||
key_to_row = {row[1]: row for row in rows}
|
||||
for idx, key in items:
|
||||
row = key_to_row.get(key)
|
||||
if row:
|
||||
results[idx] = _row_to_item(namespace, row)
|
||||
else:
|
||||
results[idx] = None
|
||||
|
||||
def _batch_put_ops(
|
||||
self,
|
||||
put_ops: Sequence[tuple[int, PutOp]],
|
||||
) -> None:
|
||||
queries = self._get_batch_PUT_queries(put_ops)
|
||||
for query, params in queries:
|
||||
cur = self.conn.cursor()
|
||||
cur.execute(query, params)
|
||||
|
||||
def _batch_search_ops(
|
||||
self,
|
||||
search_ops: Sequence[tuple[int, SearchOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
queries = self._get_batch_search_queries(search_ops)
|
||||
cursors: list[tuple[duckdb.DuckDBPyConnection, int]] = []
|
||||
|
||||
for (query, params), (idx, _) in zip(queries, search_ops):
|
||||
cur = self.conn.cursor()
|
||||
cur.execute(query, params)
|
||||
cursors.append((cur, idx))
|
||||
|
||||
for cur, idx in cursors:
|
||||
rows = cur.fetchall()
|
||||
items = [_row_to_search_item(_convert_ns(row[0]), row) for row in rows]
|
||||
results[idx] = items
|
||||
|
||||
def _batch_list_namespaces_ops(
|
||||
self,
|
||||
list_ops: Sequence[tuple[int, ListNamespacesOp]],
|
||||
results: list[Result],
|
||||
) -> None:
|
||||
queries = self._get_batch_list_namespaces_queries(list_ops)
|
||||
cursors: list[tuple[duckdb.DuckDBPyConnection, int]] = []
|
||||
for (query, params), (idx, _) in zip(queries, list_ops):
|
||||
cur = self.conn.cursor()
|
||||
cur.execute(query, params)
|
||||
cursors.append((cur, idx))
|
||||
|
||||
for cur, idx in cursors:
|
||||
rows = cast(list[dict], cur.fetchall())
|
||||
namespaces = [_convert_ns(row[0]) for row in rows]
|
||||
results[idx] = namespaces
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def from_conn_string(
|
||||
cls,
|
||||
conn_string: str,
|
||||
) -> Iterator["DuckDBStore"]:
|
||||
"""Create a new BaseDuckDBStore instance from a connection string.
|
||||
|
||||
Args:
|
||||
conn_string (str): The DuckDB connection info string.
|
||||
|
||||
Returns:
|
||||
DuckDBStore: A new DuckDBStore instance.
|
||||
"""
|
||||
with duckdb.connect(conn_string) as conn:
|
||||
yield cls(conn=conn)
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Set up the store database.
|
||||
|
||||
This method creates the necessary tables in the DuckDB database if they don't
|
||||
already exist and runs database migrations. It is called automatically when needed and should not be called
|
||||
directly by the user.
|
||||
"""
|
||||
with self.conn.cursor() as cur:
|
||||
try:
|
||||
cur.execute("SELECT v FROM store_migrations ORDER BY v DESC LIMIT 1")
|
||||
row = cast(dict, cur.fetchone())
|
||||
if row is None:
|
||||
version = -1
|
||||
else:
|
||||
version = row["v"]
|
||||
except duckdb.CatalogException:
|
||||
version = -1
|
||||
# Create store_migrations table if it doesn't exist
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS store_migrations (
|
||||
v INTEGER PRIMARY KEY
|
||||
)
|
||||
"""
|
||||
)
|
||||
for v, migration in enumerate(
|
||||
self.MIGRATIONS[version + 1 :], start=version + 1
|
||||
):
|
||||
cur.execute(migration)
|
||||
cur.execute("INSERT INTO store_migrations (v) VALUES (?)", (v,))
|
||||
|
||||
|
||||
def _namespace_to_text(
|
||||
namespace: tuple[str, ...], handle_wildcards: bool = False
|
||||
) -> str:
|
||||
"""Convert namespace tuple to text string."""
|
||||
if handle_wildcards:
|
||||
namespace = tuple("%" if val == "*" else val for val in namespace)
|
||||
return ".".join(namespace)
|
||||
|
||||
|
||||
def _row_to_item(
|
||||
namespace: tuple[str, ...],
|
||||
row: tuple,
|
||||
) -> Item:
|
||||
"""Convert a row from the database into an Item."""
|
||||
_, key, val, created_at, updated_at = row
|
||||
return Item(
|
||||
value=val if isinstance(val, dict) else json.loads(val),
|
||||
key=key,
|
||||
namespace=namespace,
|
||||
created_at=created_at,
|
||||
updated_at=updated_at,
|
||||
)
|
||||
|
||||
|
||||
def _row_to_search_item(
|
||||
namespace: tuple[str, ...],
|
||||
row: tuple,
|
||||
) -> SearchItem:
|
||||
"""Convert a row from the database into an SearchItem."""
|
||||
# TODO: Add support for search
|
||||
_, key, val, created_at, updated_at = row
|
||||
return SearchItem(
|
||||
value=val if isinstance(val, dict) else json.loads(val),
|
||||
key=key,
|
||||
namespace=namespace,
|
||||
created_at=created_at,
|
||||
updated_at=updated_at,
|
||||
)
|
||||
|
||||
|
||||
def _group_ops(ops: Iterable[Op]) -> tuple[dict[type, list[tuple[int, Op]]], int]:
|
||||
grouped_ops: dict[type, list[tuple[int, Op]]] = defaultdict(list)
|
||||
tot = 0
|
||||
for idx, op in enumerate(ops):
|
||||
grouped_ops[type(op)].append((idx, op))
|
||||
tot += 1
|
||||
return grouped_ops, tot
|
||||
|
||||
|
||||
def _convert_ns(namespace: Union[str, list]) -> tuple[str, ...]:
|
||||
if isinstance(namespace, list):
|
||||
return tuple(namespace)
|
||||
return tuple(namespace.split("."))
|
||||
Generated
-1058
File diff suppressed because it is too large
Load Diff
@@ -1,60 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "langgraph-checkpoint-duckdb"
|
||||
version = "2.0.1"
|
||||
description = "Library with a DuckDB implementation of LangGraph checkpoint saver."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9.0,<4.0"
|
||||
langgraph-checkpoint = "^2.0.2"
|
||||
duckdb = ">=1.1.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
anyio = "^4.4.0"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watch = "^4.2.0"
|
||||
mypy = "^1.10.0"
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
|
||||
[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"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
"F", # Pyflakes
|
||||
"UP", # pyupgrade
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
]
|
||||
lint.ignore = ["E501", "B008", "UP007", "UP006"]
|
||||
|
||||
[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"
|
||||
disable_error_code = "typeddict-item, return-value"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user