mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-13 05:07:51 +02:00
Merge pull request #260 from langchain-ai/nc/1apr/test-both-checkpoint-at
Add tests for both values of CheckpointAt
This commit is contained in:
File diff suppressed because one or more lines are too long
+19
-21
@@ -369,7 +369,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 9,
|
||||
"id": "cfd140f0-a5a6-4697-8115-322242f197b5",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -377,23 +377,23 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='Hello Bob! How can I assist you today?'\n"
|
||||
"content='Hello Bob! How can I assist you today?' id='a34fcaf6-a812-47f8-921d-4ba57b8271cd'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"\n",
|
||||
"inputs = [HumanMessage(content=\"hi! I'm bob\")]\n",
|
||||
"for event in app.stream(inputs, {\"configurable\": {\"thread_id\": \"2\"}}):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
"thread = {\"configurable\": {\"thread_id\": \"2\"}}\n",
|
||||
"inputs = HumanMessage(content=\"hi! I'm bob\")\n",
|
||||
"for event in app.stream(inputs, thread):\n",
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 10,
|
||||
"id": "08ae8246-11d5-40e1-8567-361e5bef8917",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -401,16 +401,15 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='Your name is Bob.'\n"
|
||||
"content='Your name is Bob. How can I help you, Bob?' id='dcf332e8-f9be-41c1-8a09-7a815a7de6d0'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = [HumanMessage(content=\"what is my name?\")]\n",
|
||||
"for event in app.stream(inputs, {\"configurable\": {\"thread_id\": \"2\"}}):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
"inputs = HumanMessage(content=\"what is my name?\")\n",
|
||||
"for event in app.stream(inputs, thread):\n",
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -423,7 +422,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 11,
|
||||
"id": "273d56a8-f40f-4a51-a27f-7c6bb2bda0ba",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -431,16 +430,15 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content=\"I'm sorry, but I don't have access to personal information.\"\n"
|
||||
"content=\"I'm sorry, but I don't have access to your personal information, including your name. How can I assist you today?\" id='d7ef71f4-0393-4770-b8aa-969756791ec6'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = [HumanMessage(content=\"what is my name?\")]\n",
|
||||
"inputs = HumanMessage(content=\"what is my name?\")\n",
|
||||
"for event in app.stream(inputs, {\"configurable\": {\"thread_id\": \"3\"}}):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -468,7 +466,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
+28
-48
@@ -377,7 +377,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='Hello Bob! How can I assist you today?' response_metadata={'finish_reason': 'stop', 'logprobs': None} id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'\n"
|
||||
"content='Hello Bob! How can I assist you today?' id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -385,11 +385,10 @@
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"\n",
|
||||
"thread = {\"configurable\": {\"thread_id\": '3'}}\n",
|
||||
"inputs = [HumanMessage(content=\"hi! I'm bob\")]\n",
|
||||
"inputs = HumanMessage(content=\"hi! I'm bob\")\n",
|
||||
"for event in app.stream(inputs, thread):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -409,7 +408,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:10.780714+00:00'}})"
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:43.801248+00:00'}}, parent_config=None)"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
@@ -439,16 +438,15 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='' additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}} response_metadata={'finish_reason': 'function_call', 'logprobs': None} id='af48af47-01bc-4099-a7d6-14cc2df5709e'\n"
|
||||
"content='' additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}} id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = [HumanMessage(content=\"what is the weather in sf currently\")]\n",
|
||||
"inputs = HumanMessage(content=\"what is the weather in sf currently\")\n",
|
||||
"for event in app.stream(inputs, thread):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -511,7 +509,7 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'configurable': {'thread_id': '3',\n",
|
||||
" 'thread_ts': '2024-03-15T21:24:37.208417+00:00'}}"
|
||||
" 'thread_ts': '2024-04-02T00:09:44.323782+00:00'}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
@@ -540,7 +538,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e')], next=('agent:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:37.208417+00:00'}})"
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'), HumanMessage(content='what is the weather in sf currently', id='4138a1b0-3053-4b12-ad15-6198be969947'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f')], next=('action',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:44.323782+00:00'}}, parent_config=None)"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
@@ -570,16 +568,15 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='[{\\'url\\': \\'https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US\\', \\'content\\': \"Today\\'s and tonight\\'s San Francisco, CA weather forecast, weather conditions and Doppler radar from The Weather Channel and Weather.com\"}]' name='tavily_search_results_json' id='dbba7582-c1b3-4d6a-8727-9421c431f7dc'\n",
|
||||
"content='You can check the current weather in San Francisco by visiting [The Weather Channel](https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US).' response_metadata={'finish_reason': 'stop', 'logprobs': None} id='4a32c94c-63bc-4f65-b9dd-bbaeaff3c54a'\n"
|
||||
"content=\"[{'url': 'https://forecast.weather.gov/zipcity.php?inputstring=San francisco,CA', 'content': 'Detailed Forecast. Today. Mostly sunny, with a high near 62. Light and variable wind becoming west southwest 5 to 8 mph in the afternoon. Tonight. Mostly clear, with a low around 49. West wind 5 to 8 mph becoming north northwest after midnight. Monday. Sunny, with a high near 67.'}]\" name='tavily_search_results_json' id='388a34ca-6b79-4eb2-84e7-34849ce7c4cf'\n",
|
||||
"content='The weather in San Francisco today is mostly sunny with a high near 62 degrees Fahrenheit. The wind is light and variable, becoming west-southwest at 5 to 8 mph in the afternoon. Tonight will be mostly clear with a low around 49 degrees Fahrenheit. Tomorrow is expected to be sunny with a high near 67 degrees Fahrenheit.' id='5d5ac23c-0e64-462d-b9ef-c499a417e906'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for event in app.stream(None, thread):\n",
|
||||
" for k, v in event.items():\n",
|
||||
" if k != \"__end__\":\n",
|
||||
" print(v)"
|
||||
" for v in event.values():\n",
|
||||
" print(v)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -594,7 +591,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 19,
|
||||
"id": "8578a66d-6489-4e03-8c23-fd0530278455",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -602,31 +599,13 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e'), FunctionMessage(content='[{\\'url\\': \\'https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US\\', \\'content\\': \"Today\\'s and tonight\\'s San Francisco, CA weather forecast, weather conditions and Doppler radar from The Weather Channel and Weather.com\"}]', name='tavily_search_results_json', id='dbba7582-c1b3-4d6a-8727-9421c431f7dc'), AIMessage(content='You can check the current weather in San Francisco by visiting [The Weather Channel](https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US).', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='4a32c94c-63bc-4f65-b9dd-bbaeaff3c54a')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:59.635105+00:00'}})\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'), HumanMessage(content='what is the weather in sf currently', id='4138a1b0-3053-4b12-ad15-6198be969947'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f'), FunctionMessage(content=\"[{'url': 'https://forecast.weather.gov/zipcity.php?inputstring=San francisco,CA', 'content': 'Detailed Forecast. Today. Mostly sunny, with a high near 62. Light and variable wind becoming west southwest 5 to 8 mph in the afternoon. Tonight. Mostly clear, with a low around 49. West wind 5 to 8 mph becoming north northwest after midnight. Monday. Sunny, with a high near 67.'}]\", name='tavily_search_results_json', id='388a34ca-6b79-4eb2-84e7-34849ce7c4cf'), AIMessage(content='The weather in San Francisco today is mostly sunny with a high near 62 degrees Fahrenheit. The wind is light and variable, becoming west-southwest at 5 to 8 mph in the afternoon. Tonight will be mostly clear with a low around 49 degrees Fahrenheit. Tomorrow is expected to be sunny with a high near 67 degrees Fahrenheit.', id='5d5ac23c-0e64-462d-b9ef-c499a417e906')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:48.961894+00:00'}}, parent_config=None)\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e'), FunctionMessage(content='[{\\'url\\': \\'https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US\\', \\'content\\': \"Today\\'s and tonight\\'s San Francisco, CA weather forecast, weather conditions and Doppler radar from The Weather Channel and Weather.com\"}]', name='tavily_search_results_json', id='dbba7582-c1b3-4d6a-8727-9421c431f7dc'), AIMessage(content='You can check the current weather in San Francisco by visiting [The Weather Channel](https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US).', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='4a32c94c-63bc-4f65-b9dd-bbaeaff3c54a')], next=('agent:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:59.629900+00:00'}})\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'), HumanMessage(content='what is the weather in sf currently', id='4138a1b0-3053-4b12-ad15-6198be969947'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f')], next=('action',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:44.323782+00:00'}}, parent_config=None)\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e'), FunctionMessage(content='[{\\'url\\': \\'https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US\\', \\'content\\': \"Today\\'s and tonight\\'s San Francisco, CA weather forecast, weather conditions and Doppler radar from The Weather Channel and Weather.com\"}]', name='tavily_search_results_json', id='dbba7582-c1b3-4d6a-8727-9421c431f7dc')], next=('agent',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:58.501730+00:00'}})\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'), HumanMessage(content='what is the weather in sf currently', id='4138a1b0-3053-4b12-ad15-6198be969947'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}}, id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f')], next=('action',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:44.242352+00:00'}}, parent_config=None)\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e'), FunctionMessage(content='[{\\'url\\': \\'https://weather.com/weather/today/l/San+Francisco+CA+USCA0987:1:US\\', \\'content\\': \"Today\\'s and tonight\\'s San Francisco, CA weather forecast, weather conditions and Doppler radar from The Weather Channel and Weather.com\"}]', name='tavily_search_results_json', id='dbba7582-c1b3-4d6a-8727-9421c431f7dc')], next=('action:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:58.498596+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e')], next=('action',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:57.092121+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco today\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e')], next=('agent:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:37.208417+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}}, response_metadata={'finish_reason': 'function_call', 'logprobs': None}, id='af48af47-01bc-4099-a7d6-14cc2df5709e')], next=('agent:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:20.413423+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63')], next=('agent',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:19.721216+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63')], next=('__start__:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:19.717873+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:10.780714+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1')], next=('agent:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:10.773938+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d')], next=('agent',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:10.163843+00:00'}})\n",
|
||||
"--\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d')], next=('__start__:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:10.162581+00:00'}})\n",
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0')], next=(), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:43.801248+00:00'}}, parent_config=None)\n",
|
||||
"--\n"
|
||||
]
|
||||
}
|
||||
@@ -635,7 +614,7 @@
|
||||
"for state in app.get_state_history(thread):\n",
|
||||
" print(state)\n",
|
||||
" print('--')\n",
|
||||
" if len(state.values) == 3:\n",
|
||||
" if len(state.values) == 4:\n",
|
||||
" to_replay = state"
|
||||
]
|
||||
},
|
||||
@@ -649,17 +628,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 20,
|
||||
"id": "21e7fc18-6fd9-4e11-a84b-e0325c9640c8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='5e326da4-44ee-4334-b2e6-869dd6d3218d'), AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None}, id='9467300a-f8ea-47db-9ade-3d9130c9f6d1'), HumanMessage(content='what is the weather in sf currently', id='bc16849a-fa20-4f07-815c-b254c6d77f63')], next=('__start__:edges',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-03-15T21:24:19.717873+00:00'}})"
|
||||
"StateSnapshot(values=[HumanMessage(content=\"hi! I'm bob\", id='774c03a0-4f61-49f1-8447-6fa5cbc3e56e'), AIMessage(content='Hello Bob! How can I assist you today?', id='019fe7a8-f43d-4f83-8bbd-b1c553f32aa0'), HumanMessage(content='what is the weather in sf currently', id='4138a1b0-3053-4b12-ad15-6198be969947'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}}, id='79bcb08c-0cb4-4b37-aa68-4df08b3a290f')], next=('action',), config={'configurable': {'thread_id': '3', 'thread_ts': '2024-04-02T00:09:44.242352+00:00'}}, parent_config=None)"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -678,7 +657,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": 21,
|
||||
"id": "e986f94f-706f-4b6f-b3c4-f95483b9e9b8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -686,7 +665,8 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"content='' additional_kwargs={'function_call': {'arguments': '{\"query\":\"current weather in San Francisco\"}', 'name': 'tavily_search_results_json'}} response_metadata={'finish_reason': 'function_call', 'logprobs': None} id='3a853de3-bdd9-4186-9f10-56bd151f63da'\n"
|
||||
"content=\"[{'url': 'https://www.accuweather.com/en/us/san-francisco/94103/current-weather/347629', 'content': 'Current weather in San Francisco, CA. Check current conditions in San Francisco, CA with radar, hourly, and more.'}]\" name='tavily_search_results_json' id='6ce0ce23-4fb7-4d3a-acbf-f882f8d52054'\n",
|
||||
"content='You can check the current weather in San Francisco by visiting [AccuWeather](https://www.accuweather.com/en/us/san-francisco/94103/current-weather/347629).' id='0f071adc-a949-4242-ac3e-7ccd2132653d'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -722,7 +702,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -407,7 +407,9 @@ class Pregel(
|
||||
)
|
||||
# if two nodes updated the state at the same time, it's ambiguous
|
||||
if last_seen_by_node:
|
||||
if last_seen_by_node[-1][0] != last_seen_by_node[-2][0]:
|
||||
if len(last_seen_by_node) == 1:
|
||||
as_node = last_seen_by_node[0][1]
|
||||
elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]:
|
||||
as_node = last_seen_by_node[-1][1]
|
||||
if as_node is None:
|
||||
raise InvalidUpdateError("Ambiguous update, specify as_node")
|
||||
@@ -464,7 +466,9 @@ class Pregel(
|
||||
)
|
||||
# if two nodes updated the state at the same time, it's ambiguous
|
||||
if last_seen_by_node:
|
||||
if last_seen_by_node[-1][0] != last_seen_by_node[-2][0]:
|
||||
if len(last_seen_by_node) == 1:
|
||||
as_node = last_seen_by_node[0][1]
|
||||
elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]:
|
||||
as_node = last_seen_by_node[-1][1]
|
||||
if as_node is None:
|
||||
raise InvalidUpdateError("Ambiguous update, specify as_node")
|
||||
@@ -611,7 +615,7 @@ class Pregel(
|
||||
# channels are guaranteed to be immutable for the duration of the step,
|
||||
# with channel updates applied only at the transition between steps
|
||||
for step in range(config["recursion_limit"] + 1):
|
||||
checkpoint, next_tasks = _prepare_next_tasks(
|
||||
next_checkpoint, next_tasks = _prepare_next_tasks(
|
||||
checkpoint, processes, channels, for_execution=True
|
||||
)
|
||||
|
||||
@@ -636,6 +640,8 @@ class Pregel(
|
||||
next_tasks,
|
||||
):
|
||||
break
|
||||
else:
|
||||
checkpoint = next_checkpoint
|
||||
|
||||
if debug:
|
||||
print_step_start(step, next_tasks)
|
||||
@@ -816,7 +822,7 @@ class Pregel(
|
||||
# channels are guaranteed to be immutable for the duration of the step,
|
||||
# channel updates being applied only at the transition between steps
|
||||
for step in range(config["recursion_limit"] + 1):
|
||||
checkpoint, next_tasks = _prepare_next_tasks(
|
||||
next_checkpoint, next_tasks = _prepare_next_tasks(
|
||||
checkpoint, processes, channels, for_execution=True
|
||||
)
|
||||
|
||||
@@ -841,6 +847,8 @@ class Pregel(
|
||||
next_tasks,
|
||||
):
|
||||
break
|
||||
else:
|
||||
checkpoint = next_checkpoint
|
||||
|
||||
if debug:
|
||||
print_step_start(step, next_tasks)
|
||||
@@ -1038,11 +1046,12 @@ def _should_interrupt(
|
||||
snapshot_channels: Sequence[str],
|
||||
tasks: list[PregelExecutableTask],
|
||||
) -> bool:
|
||||
# defaultdicts are mutated on access :( so we need to copy
|
||||
seen = checkpoint["versions_seen"].copy()[INTERRUPT].copy()
|
||||
return (
|
||||
# interrupt if any of snapshopt_channels has been updated since last interrupt
|
||||
any(
|
||||
checkpoint["channel_versions"][chan]
|
||||
> checkpoint["versions_seen"][INTERRUPT][chan]
|
||||
checkpoint["channel_versions"][chan] > seen[chan]
|
||||
for chan in snapshot_channels
|
||||
)
|
||||
# and any channel written to is in interrupt_nodes list
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+73
-21
@@ -16,6 +16,7 @@ from langgraph.channels.binop import BinaryOperatorAggregate
|
||||
from langgraph.channels.context import Context
|
||||
from langgraph.channels.last_value import LastValue
|
||||
from langgraph.channels.topic import Topic
|
||||
from langgraph.checkpoint.base import CheckpointAt
|
||||
from langgraph.checkpoint.sqlite import SqliteSaver
|
||||
from langgraph.graph import END, Graph
|
||||
from langgraph.graph.message import MessageGraph
|
||||
@@ -188,12 +189,17 @@ def test_invoke_two_processes_in_out(mocker: MockerFixture) -> None:
|
||||
assert step == 2
|
||||
|
||||
|
||||
def test_invoke_two_processes_in_out_interrupt(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_invoke_two_processes_in_out_interrupt(
|
||||
mocker: MockerFixture, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x + 1)
|
||||
one = Channel.subscribe_to("input") | add_one | Channel.write_to("inbox")
|
||||
two = Channel.subscribe_to("inbox") | add_one | Channel.write_to("output")
|
||||
|
||||
memory = MemorySaverAssertImmutable()
|
||||
memory = MemorySaverAssertImmutable(at=checkpoint_at)
|
||||
app = Pregel(
|
||||
nodes={"one": one, "two": two},
|
||||
checkpointer=memory,
|
||||
@@ -391,7 +397,10 @@ def test_invoke_two_processes_two_in_two_out_valid(mocker: MockerFixture) -> Non
|
||||
assert app.invoke(2) == [3, 3]
|
||||
|
||||
|
||||
def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_invoke_checkpoint(mocker: MockerFixture, checkpoint_at: CheckpointAt) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x["total"] + x["input"])
|
||||
|
||||
def raise_if_above_10(input: int) -> int:
|
||||
@@ -406,7 +415,7 @@ def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
| raise_if_above_10
|
||||
)
|
||||
|
||||
memory = MemorySaverAssertImmutable()
|
||||
memory = MemorySaverAssertImmutable(at=checkpoint_at)
|
||||
|
||||
app = Pregel(
|
||||
nodes={"one": one},
|
||||
@@ -441,7 +450,12 @@ def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
assert checkpoint["channel_values"].get("total") == 5
|
||||
|
||||
|
||||
def test_invoke_checkpoint_sqlite(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_invoke_checkpoint_sqlite(
|
||||
mocker: MockerFixture, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x["total"] + x["input"])
|
||||
|
||||
def raise_if_above_10(input: int) -> int:
|
||||
@@ -457,6 +471,7 @@ def test_invoke_checkpoint_sqlite(mocker: MockerFixture) -> None:
|
||||
)
|
||||
|
||||
with SqliteSaver.from_conn_string(":memory:") as memory:
|
||||
memory.at = checkpoint_at
|
||||
app = Pregel(
|
||||
nodes={"one": one},
|
||||
channels={"total": BinaryOperatorAggregate(int, operator.add)},
|
||||
@@ -683,7 +698,12 @@ def test_channel_enter_exit_timing(mocker: MockerFixture) -> None:
|
||||
assert cleanup.call_count == 1, "Expected cleanup to be called once"
|
||||
|
||||
|
||||
def test_conditional_graph(snapshot: SnapshotAssertion) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_conditional_graph(
|
||||
snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
from copy import deepcopy
|
||||
|
||||
from langchain.llms.fake import FakeStreamingListLLM
|
||||
@@ -885,7 +905,8 @@ def test_conditional_graph(snapshot: SnapshotAssertion) -> None:
|
||||
# test state get/update methods with interrupt_after
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -1034,7 +1055,8 @@ def test_conditional_graph(snapshot: SnapshotAssertion) -> None:
|
||||
# test state get/update methods with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["tools"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
llm.i = 0 # reset the llm
|
||||
@@ -1178,7 +1200,8 @@ def test_conditional_graph(snapshot: SnapshotAssertion) -> None:
|
||||
# test re-invoke to continue with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["tools"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
llm.i = 0 # reset the llm
|
||||
@@ -1344,7 +1367,12 @@ def test_conditional_entrypoint_graph(snapshot: SnapshotAssertion) -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_conditional_graph_state(snapshot: SnapshotAssertion) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_conditional_graph_state(
|
||||
snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
from langchain.llms.fake import FakeStreamingListLLM
|
||||
from langchain_community.tools import tool
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
@@ -1511,7 +1539,8 @@ def test_conditional_graph_state(snapshot: SnapshotAssertion) -> None:
|
||||
# test state get/update methods with interrupt_after
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -1625,7 +1654,7 @@ def test_conditional_graph_state(snapshot: SnapshotAssertion) -> None:
|
||||
# test state get/update methods with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(),
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
debug=True,
|
||||
)
|
||||
@@ -2104,8 +2133,13 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None:
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_message_graph(
|
||||
snapshot: SnapshotAssertion, deterministic_uuids: MockerFixture
|
||||
snapshot: SnapshotAssertion,
|
||||
checkpoint_at: CheckpointAt,
|
||||
deterministic_uuids: MockerFixture,
|
||||
) -> None:
|
||||
from copy import deepcopy
|
||||
|
||||
@@ -2317,7 +2351,8 @@ def test_message_graph(
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2463,7 +2498,8 @@ def test_message_graph(
|
||||
)
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["action"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["action"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
model.i = 0 # reset the llm
|
||||
@@ -2676,7 +2712,10 @@ def test_in_one_fan_out_out_one_graph_state() -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge(checkpoint_at: CheckpointAt) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2776,7 +2815,8 @@ def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2796,7 +2836,12 @@ def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2898,7 +2943,8 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2918,7 +2964,12 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_plus_regular() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_plus_regular(
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2987,7 +3038,8 @@ def test_in_one_fan_out_state_graph_waiting_edge_plus_regular() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
|
||||
+70
-20
@@ -23,6 +23,7 @@ from langgraph.channels.context import Context
|
||||
from langgraph.channels.last_value import LastValue
|
||||
from langgraph.channels.topic import Topic
|
||||
from langgraph.checkpoint.aiosqlite import AsyncSqliteSaver
|
||||
from langgraph.checkpoint.base import CheckpointAt
|
||||
from langgraph.graph import END, Graph, StateGraph
|
||||
from langgraph.graph.message import MessageGraph
|
||||
from langgraph.prebuilt.chat_agent_executor import (
|
||||
@@ -196,12 +197,17 @@ async def test_invoke_two_processes_in_out(mocker: MockerFixture) -> None:
|
||||
assert step == 2
|
||||
|
||||
|
||||
async def test_invoke_two_processes_in_out_interrupt(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_invoke_two_processes_in_out_interrupt(
|
||||
mocker: MockerFixture, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x + 1)
|
||||
one = Channel.subscribe_to("input") | add_one | Channel.write_to("inbox")
|
||||
two = Channel.subscribe_to("inbox") | add_one | Channel.write_to("output")
|
||||
|
||||
memory = MemorySaverAssertImmutable()
|
||||
memory = MemorySaverAssertImmutable(at=checkpoint_at)
|
||||
app = Pregel(
|
||||
nodes={"one": one, "two": two},
|
||||
checkpointer=memory,
|
||||
@@ -408,7 +414,12 @@ async def test_invoke_two_processes_two_in_two_out_valid(mocker: MockerFixture)
|
||||
assert await app.ainvoke(2) == [3, 3]
|
||||
|
||||
|
||||
async def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_invoke_checkpoint(
|
||||
mocker: MockerFixture, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x["total"] + x["input"])
|
||||
|
||||
def raise_if_above_10(input: int) -> int:
|
||||
@@ -423,7 +434,7 @@ async def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
| raise_if_above_10
|
||||
)
|
||||
|
||||
memory = MemorySaverAssertImmutable()
|
||||
memory = MemorySaverAssertImmutable(at=checkpoint_at)
|
||||
|
||||
app = Pregel(
|
||||
nodes={"one": one},
|
||||
@@ -458,7 +469,12 @@ async def test_invoke_checkpoint(mocker: MockerFixture) -> None:
|
||||
assert checkpoint["channel_values"].get("total") == 5
|
||||
|
||||
|
||||
async def test_invoke_checkpoint_aiosqlite(mocker: MockerFixture) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_invoke_checkpoint_aiosqlite(
|
||||
mocker: MockerFixture, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x["total"] + x["input"])
|
||||
|
||||
def raise_if_above_10(input: int) -> int:
|
||||
@@ -474,6 +490,7 @@ async def test_invoke_checkpoint_aiosqlite(mocker: MockerFixture) -> None:
|
||||
)
|
||||
|
||||
async with AsyncSqliteSaver.from_conn_string(":memory:") as memory:
|
||||
memory.at = checkpoint_at
|
||||
app = Pregel(
|
||||
nodes={"one": one},
|
||||
channels={"total": BinaryOperatorAggregate(int, operator.add)},
|
||||
@@ -726,7 +743,10 @@ async def test_channel_enter_exit_timing(mocker: MockerFixture) -> None:
|
||||
assert cleanup_async.call_count == 1, "Expected cleanup to be called once"
|
||||
|
||||
|
||||
async def test_conditional_graph() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_conditional_graph(checkpoint_at: CheckpointAt) -> None:
|
||||
from copy import deepcopy
|
||||
|
||||
from langchain.llms.fake import FakeStreamingListLLM
|
||||
@@ -931,7 +951,8 @@ async def test_conditional_graph() -> None:
|
||||
# test state get/update methods with interrupt_after
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -1077,7 +1098,8 @@ async def test_conditional_graph() -> None:
|
||||
# test state get/update methods with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["tools"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
llm.i = 0
|
||||
@@ -1224,7 +1246,8 @@ async def test_conditional_graph() -> None:
|
||||
# test re-invoke to continue with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["tools"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
llm.i = 0 # reset the llm
|
||||
@@ -1349,7 +1372,10 @@ async def test_conditional_graph() -> None:
|
||||
]
|
||||
|
||||
|
||||
async def test_conditional_graph_state() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_conditional_graph_state(checkpoint_at: CheckpointAt) -> None:
|
||||
from langchain.llms.fake import FakeStreamingListLLM
|
||||
from langchain_community.tools import tool
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
@@ -1511,7 +1537,8 @@ async def test_conditional_graph_state() -> None:
|
||||
# test state get/update methods with interrupt_after
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -1630,7 +1657,8 @@ async def test_conditional_graph_state() -> None:
|
||||
# test state get/update methods with interrupt_before
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_before=["tools"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_before=["tools"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "2"}}
|
||||
llm.i = 0 # reset the llm
|
||||
@@ -2137,7 +2165,10 @@ async def test_prebuilt_chat() -> None:
|
||||
]
|
||||
|
||||
|
||||
async def test_message_graph() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_message_graph(checkpoint_at: CheckpointAt) -> None:
|
||||
from langchain.chat_models.fake import FakeMessagesListChatModel
|
||||
from langchain_community.tools import tool
|
||||
from langchain_core.agents import AgentAction
|
||||
@@ -2306,7 +2337,8 @@ async def test_message_graph() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["agent"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["agent"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2511,7 +2543,12 @@ async def test_in_one_fan_out_out_one_graph_state() -> None:
|
||||
]
|
||||
|
||||
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge(
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2575,7 +2612,8 @@ async def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2598,7 +2636,12 @@ async def test_in_one_fan_out_state_graph_waiting_edge() -> None:
|
||||
]
|
||||
|
||||
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2700,7 +2743,8 @@ async def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
@@ -2723,7 +2767,12 @@ async def test_in_one_fan_out_state_graph_waiting_edge_via_branch() -> None:
|
||||
]
|
||||
|
||||
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_plus_regular() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_plus_regular(
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2792,7 +2841,8 @@ async def test_in_one_fan_out_state_graph_waiting_edge_plus_regular() -> None:
|
||||
]
|
||||
|
||||
app_w_interrupt = workflow.compile(
|
||||
checkpointer=MemorySaverAssertImmutable(), interrupt_after=["retriever_one"]
|
||||
checkpointer=MemorySaverAssertImmutable(at=checkpoint_at),
|
||||
interrupt_after=["retriever_one"],
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user