diff --git a/test/langgraph/aggregator.ipynb b/test/langgraph/aggregator.ipynb new file mode 100644 index 0000000..f69ce6f --- /dev/null +++ b/test/langgraph/aggregator.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-09-14T00:10:52.305914Z", + "start_time": "2025-09-14T00:10:51.828275Z" + } + }, + "source": [ + "import os\n", + "\n", + "os.environ['DASHSCOPE_API_KEY'] = 'sk-e2a05bbcfac84e53b73f98acef15a009'\n", + "\n", + "# Step 0: Define tools and model\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_community.chat_models.tongyi import ChatTongyi\n", + "\n", + "llm = ChatTongyi(\n", + " model=\"qwen-max\", # 此处以qwen-max为例,您可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models\n", + " streaming=True,\n", + " # other params...\n", + ")" + ], + "outputs": [], + "execution_count": 1 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:13:15.270547Z", + "start_time": "2025-09-14T00:13:15.236537Z" + } + }, + "cell_type": "code", + "source": [ + "from typing_extensions import TypedDict\n", + "from langgraph.graph import StateGraph, START, END\n", + "from IPython.display import Image, display\n", + "\n", + "\n", + "# Graph state\n", + "class State(TypedDict):\n", + " topic: str\n", + " joke: str\n", + " story: str\n", + " poem: str\n", + " combined_output: str\n", + "\n", + "\n", + "# Nodes\n", + "def call_llm_1(state: State):\n", + " \"\"\"First LLM call to generate initial joke\"\"\"\n", + "\n", + " msg = llm.invoke(f\"Write a joke about {state['topic']}\")\n", + " return {\"joke\": msg.content}\n", + "\n", + "\n", + "def call_llm_2(state: State):\n", + " \"\"\"Second LLM call to generate story\"\"\"\n", + "\n", + " msg = llm.invoke(f\"Write a story about {state['topic']}\")\n", + " return {\"story\": msg.content}\n", + "\n", + "\n", + "def call_llm_3(state: State):\n", + " \"\"\"Third LLM call to generate poem\"\"\"\n", + "\n", + " msg = llm.invoke(f\"Write a poem about {state['topic']}\")\n", + " return {\"poem\": msg.content}\n", + "\n", + "\n", + "def aggregator(state: State):\n", + " \"\"\"Combine the joke and story into a single output\"\"\"\n", + "\n", + " combined = f\"Here's a story, joke, and poem about {state['topic']}!\\n\\n\"\n", + " combined += f\"STORY:\\n{state['story']}\\n\\n\"\n", + " combined += f\"JOKE:\\n{state['joke']}\\n\\n\"\n", + " combined += f\"POEM:\\n{state['poem']}\"\n", + " return {\"combined_output\": combined}\n", + "\n", + "\n", + "# Build workflow\n", + "parallel_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "parallel_builder.add_node(\"call_llm_1\", call_llm_1)\n", + "parallel_builder.add_node(\"call_llm_2\", call_llm_2)\n", + "parallel_builder.add_node(\"call_llm_3\", call_llm_3)\n", + "parallel_builder.add_node(\"aggregator\", aggregator)\n", + "\n", + "# Add edges to connect nodes\n", + "parallel_builder.add_edge(START, \"call_llm_1\")\n", + "parallel_builder.add_edge(START, \"call_llm_2\")\n", + "parallel_builder.add_edge(START, \"call_llm_3\")\n", + "parallel_builder.add_edge(\"call_llm_1\", \"aggregator\")\n", + "parallel_builder.add_edge(\"call_llm_2\", \"aggregator\")\n", + "parallel_builder.add_edge(\"call_llm_3\", \"aggregator\")\n", + "parallel_builder.add_edge(\"aggregator\", END)\n", + "parallel_workflow = parallel_builder.compile()" + ], + "id": "ca97abef33e37605", + "outputs": [], + "execution_count": 2 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:13:17.609433Z", + "start_time": "2025-09-14T00:13:16.738137Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Show workflow\n", + "display(Image(parallel_workflow.get_graph().draw_mermaid_png()))" + ], + "id": "b0f300553c496de0", + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaMAAAFNCAIAAAAiuZdRAAAQAElEQVR4nOzdB3wTZR8H8OeSNOmedDI62WXKFgVkKVOGqIAigsqUIchWpoqMFydDURnKFGSoICAgu6yyV0t3aemeaTPu3n9ybUjbtDSluebu/l/51ORyuab33P3yjBsyhmEIQggJmowghJDQYdIhhIQPkw4hJHyYdAgh4cOkQwgJHyYdQkj4MOmQCRcOpyVHKZV5Wq2GqFUMJSGEgf+IhKJomqEoCp7DT0Y/lWEIzCChJFotrXuJIoxuHkLpZ5ZIdFNore4BPJVKJFqalkgITcMM+vcS/ZIlRUsuOuyJIhJC0cWHQFES3a+EGQxP4SdDPzlASiaHSbSNQuLuo2jSycm3nj1ByAiFx9MhgwMbEpJiClRKRmZDbGwlEBwQSVoVBUFWlD+QRLQ+nHRxQ9iYI/qkg3+0hujySZdBuhTTR55+NlL0AH7CDMTwkw05UrQ0xngKpfs/Zfwq0b+l+Cm7TAPKRve8UEnDh6e1RCIlzu6yTgM9gpo6EYQw6RBr99dxyTGFtvaSwFCHLkNrSaVSwmfXT6XfOJ2TmaJW2Ev6vOvrF2RHkLhh0ondrfOZ//2e6uAq6/W2l09doTX69m9IiLtb4OErfWNmAEEihkknagfWx8dHFnQeWKvZ865EuH6cH6FRk3HLQwgSK0w68bp6Iu3iPxnvfyaK/f+vnxLiHig/+BzDTqQw6URq79r4lPiC95eJaM8/vCUx6qZy3PJggsRHQpD4nPw9OSW2UFQxB3q/5RfQ2P7H+Q8JEh9MOjG6eSZnzKIAIj4vv+MrlZG938UTJDKYdKKzcUGkX7CdVM7v40iqbPTCoISIAmWuiiAxwaQTl7thWco8ZtDE2kTEPOvId65OIEhMMOnE5fSBNL9ABRG31z+ql5OhBQSJBiaduBTk0n3H+hLRs3eS7l//iCDRwKQTkSO/JsnkRGHH6WUdIiMj+/XrR8w3e/bsffv2Ecuo18jucVwhQaKBSSciiQ8L3LzlhFu3b98mVVLlN1ZG254uGhUeSSoimHQiUpCv9aprqU66nJycFStWDBw48IUXXvjggw/++OMPmLhu3bpFixYlJSW1adPm119/hSk7duyYNGlS165de/fuPWfOnPj4ogM+tm/fDlNOnDjRrl27lStXwvyJiYlLliyBOYkFuHjaSSXk/tUsgsQBk05EaA3jU89SSQeJdv36dQiv3bt3h4aGfv755/B03Lhxb7/9to+Pz6VLl0aMGBEeHg5p2KJFC8gymD89PX3+/Pns2+VyeV5eHrx38eLFw4YNO3PmDExcsGABZB+xDEpGJUfjsSZigVfiFBGaJq4+lkq6K1euQKh16NABHk+ePLlHjx6urqWvGtCsWbOdO3fWq1dPJtNteGq1etq0aVlZWS4uLhRFFRQUjBo1qm3btvBSYaHFO9FkUkluFg6/igUmnZjoLqRpqQOGW7ZsuXXr1szMzNatW3fs2LFx48Zl55FKpdBcXbVq1c2bN6EGx06Emh0kHfu4adOmhCsMYSTYUyca2HoVEYphstMLiGUsXLhw+PDh586dmz59es+ePdeuXavRaErNc/LkSXi1SZMmP/zww8WLF7/99ttSM0AblnBFq6HleEFi0cA6nYhIoGcqpqBBa2diAc7Ozu++++7o0aOvXbt2/PjxjRs3Ojk5jRw50nievXv3QtVv4sSJ7FMYxCA1R6MmXnVsCRIHTDoRsbWXJMdapP8L+toOHToEA6+2trYt9e7du3f37t2ys/n6Pjlu+d9//yU1RF2gZWjStIOQrz+KjGHrVURq1ZFnJFtktBFGGDZs2DBr1iyo0KWlpf35558Qc5B38BKMP6SmpsIQakxMTIMGDc6fPw/jsNCwZQ86AY8emThXQaFQeHl5GWYm1e3MwRT2vj9IJDDpRKTrUK9CpUU64R0cHFasWPH48eMxY8b07t178+bNU6dOHTx4MLzUuXNniLwZM2YcPnx4woQJnTp1gq46GLJISkpatGgR9Nl9+OGHUB8su0xoC0Nf3kcffaRUKkl1i7iW5+aNDRoRwWsOi8vamRF1G9r3G+tHxO3baRFDp/r5+ONtYcUC63Ti0qKLS+zdfCJuf6yNl9tSGHOighV4cenUz/P66ayj2x71eNP0FU2WLl169OhRky9Bfxl7xG9ZCxcutNBpW6CCJVfwkXbs2OHt7W3ypfj7BX3GehIkJth6FZ3Y+7n71yZN+p/pm0gUFBSo1WqTL1UQK3Z2duW99OwqOBilgo8EXYcSiYkmy6/Lo2CTHzk7kCAxwaQTo73fx2c+Vo9eKLq9/eKRtEv/ZIxfgfdCFB3spxOjfu95qzTKHz+9S8QkM10ZdijDq8M1wwVUkHhgnU5Ebt26deHChfPnz1+/fr1Dhw4dAyYpcyTvLAgiIvDgWvY/mx5PXB3y2WefwUqgKEq3Bjp2hJ8KhdgvNy8GmHQCl5SUBDv2uXPnIODq1avXvn172Lefe+459tUtn0UXKrVjlwj8Zs+/fxuXFFU4cdWTRmtcXBysEHa1NGnShI08Lq8vgDiGSSdAMKTA7saQcTDCAOnG7slOTibOaD+0KTHiWr5fsGLwxLpEcC4fT794KF1qI3lvablV16tXr7KRB/FnqOh5eXkRJCCYdMJx+/bt83pFjdOOHSHjAgICnvpGrVa7eUlsfo7Ww9umTR/3kFAhXOLj702JMXfyNSrSrJNzl6GViq3s7GxDRQ++FQypB01dgngOk47fjBundevW7aBnaJyaJfpu7n+7U3MzNIQitg5SR1epnZNUrpBotSX2c4lEd0XPJ08pQuu3IEr3X4mtic0HdkrJx4x+w9NNNPzUv7/oKSl+IKEYLU0VL/3JdOPZpBKipYlUStQqbX62Nj+HVuZqtGpiY0uCmzv2eNOHVElkZKQh9dq1a8dGXv369QniJ0w6/mEbp6ynNk6r4PrpjIc38rPTCzUqSqumNSWPrqMgfugn20xRTumySJc/JeYskW4Qb7r/iP4SmDCVggUVZVzRIkpFGDyAWWgtU2pplD5bqeJklEh180hlut8A8zu5yXwC5F2GVDHgTDJ8l2RkZMDa7tSpE6ztsldURtYMk443SjVOWZVpnFqbEydOHDhwYNWqVYRvUlNTIfXOnj0LpeDj4wPrH1KvajVoxDFMOqtWjY1T63H06NEjR44sX76c8Nndu3fZ5m14eHhHPSgaf39/gqwSJp3VsXTjtMYdOnTo1KlTy5YtI4Kg0WjO6UF5qVQqwziGg4MDQVYDz/C3FmUbp6+99hofG6dPVcHJqnwEf8sLekR/VVEowcOHDy9atCg4OJhNvebNmxNU07BOV5MMjVP4WadOHWE0Tp9q7969t27dMtzpVajgG4tt3kZERLC1PPhpfHF5xCVMOq6ZbJzCT2dni9zIxgrt2rUrMjJy9uzZRBzy8/PZti38lMvlhuatjY0NQVzBpOOIYEZOn922bdsSEhJmzJhBxCcmJsZwmF6LFi3Y1GvUqBFBFoZJZ0HibJw+1ZYtW9LS0qZOnUrE7fLly+xQBmwnhoperVq1CLIATLpqBo1Tw3Eh4mycPtVPP/2kVCoNd31FmZmZhoqeq6sre8wKbDMEVR9Muuph3Dg1HBcizsbpU23YsAG2ug8++ICgMmD4gq3ohYWFGSp6MIxL0LPBpKs6bJxWzffff69QKMaMGUNQ+WDHNFT0srOzDaO32DioGkw682Dj9Nl9/fXXLi4uo0aNIqhyUlJSDKO38J3Kpl7r1q0JqjRMukrBxmk1Wr16tbe394gRIwgyH2yKbOrdunXL0LytW1eA1xasXph05WIbp2zAYeO0Gi1fvjwwMHDYsGEEPYPCwkJD85amaTb1gK2tLUFlYNKVULZxygYcNk6r0bJlyxo3bjx48GCCqklCQgKbeqBRo0ZsRS80NJSgYph0Otg45dKiRYtatWo1YMAAgiwgPDyc/aqOjo5mrysFm7SPT3VesI+PxJt02DitKQsWLIDvkj59+hBkSbm5ubBtnz17FrZzOzs7Q6eeVCol4iOupNNoNIauDWyc1pTZs2d37969Z8+eBHElKirKsOXDoC0beQ0bNiSiIYqkMzROr127Zvhmw8ZpTZkxY0bfvn27detGUE24ePEiG3kpKSmG3cHd3Z0ImmCTDhunVmvq1KlDhgxhL+iGalB6erqhoufp6cmmXtu2bYkQCSrpsHHKC5MmTRoxYgTsVARZjXv37rH7zpUrVwwVvcDAQCIUQkg6bJzyy7hx48aMGSPUugPfabVaQ3VBqVQa7oXm6OhI+IyvSYeNU/6CmJs8eXLLli0Jsm7sXsbeCw2qDmw1gqcFx7OkS0xM/PXXX7FxymujRo2aOXMmHtfKLzdv3mTrenfv3oW866VH+INnNy6Bzux+/fqtWrUKG6f8BcN84jyki9dC9caOHQuVDMg7qHBAexYatoQnJIRXMjIyIOkw5ngtKytLpVIRxE+2trbdunXz8/ODnZHwB8/qdDKZDAZYCeIzLEQB4F0hYtIhrmEhCgAmnWXhTiIAWIgCgElnWbiTCAAWogBg0lkW7iQCgIUoAJh0loU7iQBgIQoAJp1l4U4iAFiIAmBjY6NWqwl/8Ox4OtxJBAALUQCwTmdZuJMIABaiAGDSWRbuJAKAhSgAmHSWhTuJAGAhCgAmnWXhTiIAWIgCgElnWbiTCAAWogBg0lkW7iQCgIUoALwrRDzKBHENC1EAsE5nWbiTCAAWogDwrhD5cXX13r17p6amwkel9GiahseBgYF79uwhiCf69u2blJTEFiI7BcqxTp06Bw4cIIgnhg4dGhERIZVKoewkEgmbHl5eXocOHSLWjR+t1+7du8M6hTXL7iTwwMbG5s033ySIP9544w2oCLCFyCL6kiWIPyZMmODu7g5lB2EHP9mwa9asGbF6/Ei6t956q27dusZT6tWr9+qrrxLEHyNHjoQanPEUKMRhw4YRxB8vvfRS/fr1jaf4+PgMHz6cWD1+JJ2vr6/xjYjg+6Rfv35QrSOIP6AKAN9YCoXCMKVt27Z+fn4E8cro0aON78YXFBTUqlUrYvV4M/YK3xuGah1UDaC/gCC+gWq4oVoHnTu8qAugUjp06NC0aVP2sYuLy4gRIwgf8Cbp3NzcXnnlFaKvGvTo0YPvdxQXrbffftve3h4eQEUAqgME8dDYsWOht47oK3R8uRHi08deY+/nPbiSU1jwZAp0KdNG74JOSZrW9S5DFzNMlkoYLU2VfbX4qe43Gt4toQhd8vfD67R+dI6dB+ani+fQarUXwi4wWm2btu2gEcTOY2IJRm8h8JsZ+CMJQ5X+u6QSSlvynSZn0/+SMlPh7XI6oJFDw9YuhA/SH+deO5Gjyqe0+qf6wYAno6DsX8gUlyC7Sg1FAI90ZaotWpSUYrRGq0kqIVq66DG8Bd5reEpMrWSYcv5CWL4yv2WLllAjgF8LH6LUNmj4VGWXw34q3VvoJ8VSav4nn1z3RP8f/EGGz8z+qXpyq/KzxgAAEABJREFUG8ajrrzVix6ED2D7P/3HY2Ue7FBFFRTjlW+8qo2n6zDEsK5K7B1ld0D9ymUfUvpVZ7ynG/+K69evp6WlhYY29PQs0f9Qavcv3qJKLMf4V5coLN3HgxH5ErOVt5s/+Y1S4uYta9erFqnQU5Ju4ycRhfnERiFRFzLlfRpKH2ZPPpOMojVGM0spWmv0VEYYWvev+L1PHhf/YRS7C7IfDEZ4tNoSRcHOpX+vPhTLLEEiJbTWsDT9eiy1OvV0S4aVZzSZoXT/lZrNeMcwJpMzGg2xkZF3FwZK5VZ9n+bNS6NyMrVyOaXVFBWcfmMuWs+6LKeLoq54lT75SfRfbJSUaIvL1LCrsCvGuLihLHTHABmVl1RGaTUlk04/xZCyUM760jHKKXYbIIzppNMXN2yBWuZJYemWRBVvBrqplHHqsYVvMgfltpRaRcPS+o71qRNi1a2EbSuj05M00DUN60tbfAVM403deE+Bnd/wzaSLK6PNnH3pyUqAzDTel41WDmwAuq8IusTOSxsdQgclJFNItKoS5Vtq92e3olLxWjSnPkZKJ53RX2T8kUwugWWj0L0FtrrnerpWkHcVJd36ORG1/GS93g4gqBzn/kqKuJw7ekk9Ozs5sUqbl0bDF/OgSdhOLNeNs2nh/2YMHOdTO9hKw27fuvjH8QVvzAwhqBzRd7JO7UnpMsijaUc3kzOUm3Q/zIuoU9+286A6BFUo8mb6uX3p47+0xq1w0+KHUG3p90EgQU+zeUnEe5/Vk8ut7htrx5oYZZZ6yFSMuafbujSiyzCPJm1NhJ3pEYlzBx9DhRBjrjKCQ91lcnLwpzhiZRIe5ubl0BhzleTsIf39q0RifVIT1N1G1CaoEnyCFOf/TDf5kumki31QYOvEs1Nia5CrpyItQUuszK0z2XIFz67gUIN8/O1zM6zuRM7wk2nQR+nuaUdQJQQ1cy7MN91INb0nqPNpQhNUSTIbaaHS6taXMp/QGh6c1GwlFHYytZoiVgYKseSIHKqISy05Xc63lemKm1Y3PGp1pW61YFukra5KpxtRpRksxEqDHmvr+2KQwICj9W1aVovRSJlyqhzYREUICR8mHUIsw9WkrAlFYb28WmDSVQOJ7vI1Vrc9SsqefIAqYpWXatQdIE1QZZW/F2LSVQOaYco7gLsG0foTEQiqJP3pIojfyt8LTSed7swSLHUkKgxDsPYkXKaPMtGdjotHmVSeVe4hlMQqO56sGa4u4TJdp9OdhI/fb5VnnXuI7urXBJnBGvvpKCv9IrVO2E8nQoyWtsKj/KyYVfbTSQjBmnnlmdtPhxCyCth7aBas0yH0VNhjw3vm1ukkUorBAxbNgmuL96xzi8djX8xQQUO/nC5rhnBQaX51cI/NW36EB7/v2d6jV/tqn98sp8+c6Nv/xfmffETMV3zzUutCGV9712KspxAfPoz4eNakAQO7DRrSc868qbduXSdCwEXrdeGiWTNmTiD6ddite5sbN8Krd/7Kg1KDfXDAqy8NHtpr1uzJV8MvmfX2Crb3co4yoa3yeHHL0Gq169Z/tWTpXAeHKl5yVnexcOs7KEfXwyOazuzMzIyPZ08qVBV++unyeXOXZmVlwlOYSBB/xMZGfzRzfE5O9rgPpgx/852k5Edz501NS0sl1QH76cj9B3dPnDyy9rvN69avIYif9h/4XanMX/75N7a2tvDU3c1jzHtvXLl68aVuvSq5BAoPyqlpu3b/WsvD83+r1kskupJo26bjO+++Fn7tcveXepNnVm1JBzUj+KCbNm+Ax00aN3tn1AfNmrWEx1FRkfsP7IZtLikpMcA/qE+fVwcOqM5btUJrCH5XfHzs73u2ubq6dezwwqSJMz77YsGZMyfr1vUfOfzdXr36VrwEL0/vDet/c3ZyJlWlayda305StercuXOnvvpmeUrK45DgBq++OuyVlwfAxNzc3F27t4ZdPBcdHenhXqtTpy7vjh7PZkq1WLR4NuQMlN2KVUukUmmjhk0Xfrr8j327YHNydnbp3asffMlX3EHwxutvv/jCS4aP5OOju2EVZB+pNOusmFcNTwvxo+nzjJ/K9LeuN+969+aOvepGJIh5NvzwzX//HVu8aKWqsPDU6eOz5kxe9/2WevUCvvt+FWTc9Onz4I+E2ulXXy/39vbt0P55Uk1sbGy279g0/M3Rh/8+e+ToXytXLY2MvP/GG6MWfrJ8y9YfYaV37PSik6NTBUvw8KhFno2unWiFrVdiNthDFnw6Y9bHC+E74+7dW1+uWGxjI+/R/eU9e7f/tu0XaBW6uLjm5uZ88+0K2JQ/eP9DUk1kMtm161ecnJx37fgbmpxj339zyrT3urzY/eD+k/fu357+0bhWLdt06NC5giXA/hAQ8OSuQKdO/Qs/GzRoTHiOMX9Egr+FaCwhMf6zzxdAbal9O3OywtyxV0bLaM3ZdbOys3bu2jp1yuy2bTrA0/btn8/Pz0tLT4WkW7Dgc3jsq/+OhT/10KH9YRfPVmPSgfohjQb0HwIPunbpCUnXtGnzbl17wtNuXXtB53dsTBRMIRZmjR1i5kfdz7+sg5pRzx66O4hDUebl5ULZweNhr42ETdbfv+iWFDdvXoNCrMadBKhUKqiMw/cW7IdBgSEarWb0O+OIfpuBPTby4YPK7ySwm61dvwY+cP2QhoTnKPNHJPheiDAKAbEIDxrUb/TZsjXVdQ+jcpLOzMHt6KhI+NmoUdOihcpkixetKF4Ws2fP9gthZ+LiYtgJvr7VfPsPyFP2gYODA/wMCAhmn9rZ6e4VDx2cxMKscuiVvWyTGbPTNA3bYg/9HsKC5gb7ALbdi5fOfbH804jI+xqN7vLVbm7upFrVrl3XRt9aAXb29tC8MrzkYO8AdZBKLgfqAnPmTmkW2nLunCXEPNZ4PIfu5GVzbiYMTXC+F2JwcIPVq9bBQMQvv6ybMnXsqhVrK9/qquBLoXq6l9i/wVZRus0PO8/suVOuhl98b+yk/fuOHz92KTS0BalupXZotjuTS7oeHqu8tJlZA+jwhQzlpVCY6LiBrolNmzb07Tto6+Y/oBBHDB9NqlupUqtaIUJ1YNy4kbC/LZj/mbl1AYZtKloZ2LAYc07pKyws5HshQo851AGhuQ295+npafv276r8eys4Sbh6RiTY4zPYSrIxGNaEnoKVK75/rnU7dgpkomctLyIsui9e/g/bwbcxbJrQ2Ck1HfLywMHfhw4Z3q/vIHZK5WtYXHr4MGL2nA979exbqmO7kqCdKIALl0K+87cQL4Sdhc9p6Nqyt7f3860dHf2w8ktgyq+Xm95BpVLzdt2QkIZsf2TR72MYqModPnwwKysTnhqiDT60WZ+bL3RfvPwftoP+6YYNm9y4+eQo0B9+/Pa771er1WqlUlmruBCh6nf23H/EyhQUFHy66GMY+Js2dQ4REMbMUzcg5vhbiHv2bPv557WGp1CmCYlxnl7elV8CZfaRw2aeV+zo6NizR599+3b9fWg/tCBgWOfy5QuNG4cG+AdBAu7YuSU7JxsGXmE6dJEmJT8i1gR6duAzwz/o0YNoZh9X1/GKNUiiO/bFvObYwP5DL148B+UFa2Df/t3btm8KDAyGagL0hELJwoqC9fPlysXQCwbrKi8vj1gNGFhMTIzv3asffN2yJQj/YJMjPEdVuPeaxN9CHDToDWgFfv3Nl2zxLVk2F/oTB/QbQqpDtV2fbsqHs9Z89cWq1cu0Wm1IcIPFC1ewAwUwqr1p84aBr74EvSfz5iyBAdkFn8wYNXropp93E+tw8OCe7Ts2G56y4z4zZyzo88pAwme6XiczS7F3737ZOVlQXrADQDfw++9NZlfCgnmffff9qndGD7W1tZ0wfnrLlm3Cws4OGtJj0y+/E+tw+84N6J+aO3+a8URoqVWtJctr/C1EaLfOmbVo245Ne//YCU+bNWv5xWdfGwaLnxFlstd605JohqaGTPUnqBIOb05IjS8ctzyIWJM/1iYkRReOmGtdn8pqhR9Pu34qc+KqYGJNLvyZeulo5tsLQwiqhNQ41Z8/xk5aY2J1ma7T4bX/zEJZ5RrTnbmB5VhpuuFzaxxBR2agzD1HAuanOd9JbtwInztvanmvbt3yh4uLK6mS/gO6lvfSrFkLOz/flTwbxiovbabra62JT2WhtT1n3tSb5Vwzo0+fV8ePm0pQ9bFQIf627Zdt234x+ZJ/QNC3X/9Eng1j7jkSuu82zr/foFm+YcNv5b1a5ZgDFSzWzbV6Dp60wtqTzIaysamBj2WhtT1j+nyVWmXyJXv9IeKCVFPXzLNQIfbvP6RbOddckEkte7WR8pdeE/sue9IYXxZrzArrdBo1o1bXwMey0Np+9tOTn8Iq7/dK1dCZGxYqRCdHp4pPQn9GDGXhI4eRFbLO64NaL+vso8P7SJiDYsw8clgiIxL+H/QvcoyZZ4OJHmaKkJXTT6e1xssQWTMrrD3BdxXW6SqPwjurCoDZ9wbDEjeH7lQE68sU3XcVlmOlMRRjpfc9wq+rysP7vVqU1Z73ivd3EwL8uqoOppNOZiPB27/zHUNwJzETfjEIl+mk06hphsZiRyKDXwzCha1XwaIkeJQJQkUw6QSLEdNNexGqmOmkk9tJGQ121FWWTAZrzOqqTzZyysYWK3WVBfVfqYJYHSl8Kjy0tbI0jKa8k8pMr0Q7B1JQgElXWTmZKoWt1VWfnNylmkIsxMpKS1bKbIi18Q1QmHebPnFLilKWd2sc05O7DaulzMWWT2XlZmibveBGrMyLg7xhAD0h0hrv+WCFUuJU9Ro5ECtTr5GTVEZdPPyYoEp4eD2nlp/pOyWZTjoXDzufQPmvn0cQ9DQ7V0Q4ukubP291SQcat3f8d1syQU+zb22UREJ6jfAl1ufFoR53wix+J08BOL4jriBX89rUeiZfpSrotT5/KOXqv1m+Qfa169vZ2Zd/TzmKvZSHiS4hdqrJs2wMb6D01wCniqcw5fwCSvdJqYo+gtEyScnHZeckpmZ78pHKmcd45oJCdVJk/qPI/LqN7V9+y+LXSqmyuHu5+39I8qwr92/s6OgoZ4qHY43/Rv1jXSGYeI3oT5vWr/myq9TkSpbozs4oPVlfehV1GlL664QZz6J/yBifss2UmKHolxuKxvizU+xR08abZfGbjRdCqzVJscq4B/l2jtTwmYHEWuVmqX5ZFFurtqJOI1v3WnY0XfSHl9lZiteJ6evqP1ljJYq3+ClTfGut0ksu85sYfRWp1DwUebIjV3S6SdH+bjSL0fJL7o+M8WmWxp+ixPJpzeMEVcydbGjBjFlc7iWjqYrH5yDs7pzPLcjXatXlzqNbrXSFR11W4jSbkhtxmeU/ZeWVV7TV/2EMpDZErpAENLXr/oY1VgSM3buSce5gRmEeXc7l3apbpQujMuu5UipzHpepYIBChH++QXb9x1Tz3darXVZ67oF1qTlZ0OdO6HI67spbn5TZNxThE2jdy2SMqwoWA7oAABAASURBVK/Nax/6VzAbxa8jEbZu3ZqSkjJt2jSCeOvkyZP79u1bvXo1QbwVGRk5Z86cnTt3Ep7g2fF0Go1GJsNjAPkNC1EAeFeImHSIa1iIAoBJZ1lqtdrGxvqOekLmwKQTAN4VIs8Ov8akEwAsRAGApONXIWLrFXENC1EAoBClUinhD0w6xDUsRAHAfjrLwp1EALAQBQCTzrJwJxEA7KcTAN4VIiYd4hoWogDg2Ktl4U4iAFinEwBMOsvCpBMALEQBwH46y8KdRACwEAUAk86ysOEjAJh0AoAjEpaFO4kAYCEKANbpLAt3EgHAQhQATDrLwp1EALAQBQDHXi0L++kEAAtRAHhXiHiUCeIaFqIAYOvVsnAnEQAsRAHApLMs3EkEAAtRADDpLAt3EgHAQhQATDrLws5sAcBCFAA8ctiyYP1idYDvsE4nALzbE3k29tqzZ89x48Zt3LgxJiaGIH6qU6cO1ul4imGYI0eOfPzxx9euXWvZsiXhD559tc6ZM+f+/ftHjx6dNm2aXC7voRcQEEAQfyQkJKhUKoJ4BXa6f/75599//4U9rnfv3l9++SXhFQpCmvDTgwcPjupBLZqNvMDAQIKs3nvvvTd+/PjWrVsTZPWOHTsGAQc/u3fv3qtXL/hJ+InHSWcQERHBRp5EIoGSgMgLDg4myFpBzI0ePbpdu3YEWSuINtihoKH60ksvQcDBPkV4TghJZxAZGcmWEE3TbC0vJCSEICszefLkN998s1OnTgRZmePHjx/R69atG+w+0C1OURQRBEElnUFUVBRby4NhPqjlQYHVr1+fIOsAfayDBg168cUXCbIOJ06cgCYq7C9QKD31oHlEhEWYSWcQHR0NtTz4joIucLaW16BBA4Jq1MyZM1955RVoFhFUo06ePMkGXOfOndkmKr9uVm0WgSedQUxMDFvLKygoYPvyGjVqRFBNgAF0aBzBrkVQTYCAY/vgoAOBDTgxHN4olqQziI2NZfvycnNz2Z4IjDyOLViwoGPHjn369CGIQ6dOnWL74GDls1u+qI5qFF3SGcTHx7PfbBB5bC2vSZMmBFneokWLWrVqNWDAAIIs7/Tp02wTFQa72T44uVxOxEe8SWcAkcfW8jIzM9m+vKZNmxJkMcuWLWvcuPHgwYMJspgzZ86wAdemTRu2iapQKIiIYdI9kZiYyPblpaensyO2oaGhBFW35cuXBwYGDhs2jKDqBgHHtlRat27NBpytrS1BmHQmPXr0iB2xTU1NZSOvWbNmBFWTVatW+fr6Dh8+nKBqcu7cObYPDroF2D44Ozs7goxg0lUkKSmJjbzHjx+zfXktWrQg6Nl89dVXbm5ub7/9NkHP5vz582wTtXnz5mwfnL29PUGmYNJVSnJyMtuXBy1cti+PXxdysCrfffcd1DjeffddgqrkwoULbMBB7wrbRHVwcCCoQph05klJSWH78mAcg23YQnuBIHOsX7+eoqj333+fIHNAwLF9cE2aNGEDztHRkaDKwaSrIujCYxu2sbGxbC0PL85RSRs3biwsLJwwYQJBlRAWFsYGHAxYs31wTk5OBJkJk+5ZpaWlsbW86OhoNvKee+45gsq3adOmrKysDz/8kKDyXbp0iW2iNmzYkA04Z2dngqoKk67apKenw3YJFb2IiAh202zTpg1BxXr37g0VYdjeoOlq+FmrVi3YnwkqdvnyZTbgQkJC2Caqi4sLQc8Mk676ZWZmss0NiDx2xBavxQa++eYbqM0ZT9FqtX379l26dCkRPQg42GBgswkODobvSNhmXF1dCao+mHQWBJHHjtjevXuXbdi2b9+eiBU088eOHRsXF2eYAhW6NWvWiPm84ytXrrBfioGBgWzAubm5EWQBmHRcyM7OZvvybt++zUZehw4diPjAqOsPP/xgeNqtW7cVK1YQ8QkPD2ebqP7+/mxHh7u7O0GWhEnHqZycHDbybt68yR6k0rFjRyIaubm577zzDgzdwGPoX1+5cqWoBqwh4Ngmap06ddg+OA8PD4I4gUlXM2CfZw9SuX79OmzxkHrPP/88EYEtW7ZAhx1N0xDx8ICIwLVr19iAq127NttEhWY7QdzCpKtheXl57IgtdNmwDZnqjbzoOzlatekrZVOEMOU8LfUSQyiKlLud6F5j4GWGIpTJ5RseMJRuvpUrVmZmZY4cObJxo8Zll1ZqOex7S32ep/4tZRdFFU0pB83UbSiX21Xn5YzgO4ztg/P19WUDztPTk6AagklnLZRKJbtjwDAc25f3wgsvkGewa01sSrwKYkijIU+564k+SypKEzbMnr4MrlTyl1X6M0llkHXE1o4a9KGfu+cznRsP/RJsH5y3tzf71eXl5UVQTcOkszoFBQVsX15YWBgbeVW4ucy2FdGF+UynAR6+QXi4aWWd2JUYczt/zOJAO0ezb6cAAcc2UaFlyvbBQdIRZDUw6axXYWEhG3nnz59nI69Lly5lZ4NhXGgJTpo0yTBl05KHlIwZNAFvelsVmxZFjF8RaHzvmLt3706ZMsXGxubgwYOlZr516xYbcDC2wDZRfXx8CLI+mHQ8oFKp2Mg7c+YM2yDq2rWr4VUYvpTL5f369Zs/fz48vX8l89i21JHz8Ua3VfTnD7EaNTNyjj/7FL5mli1b9ujRI1tb29OnT7MTb9++zXY1uLm5sQEHnXEEWTHh3xNIACDI+uhpNBrYwQ4cODBr1iz27IulS5dKJBKYDtUNGND85JNPbpzJtnUQ2s06ueTf3D78SCb7+OLFiwsXLkxNTSX6jlSo3LF9cC4uLrDy169f7+fnRxAfYJ2OlyDa2LMv4KfhJsTQvIL6RWPHMWoNNWhiPYKq5FG08sjmhImrQi5cuLBgwYL09HTDS9CknTBhAmRc7dq1CeIVTDp+g6ar8e3WYVd8o9NaF2e316YHElQlkHSHf07oMCIDYo6tzRkYN2ARv2Azh8egt84Qc9B0hS8ttVoNQ7cEPQv9Vz9bm6P1DLWB/Px8gvgJ++l4DHqOPD092Zs/Qc8R9BlBq0oaj9fAqAZTp05NS0uLjY2NioqCmp1MJoNxoZycHOgfgIEIgvgGk47HoCMJRmO9vLx8fHwM16Hd/mWcSoU9ElVH6f+9/PLLxhOTkpKSk5MTExNfeeUVgngIk47fyp46plbRGjVNUFUxps4V8dHDO8PxFyYdQkj4MOkQQsKHSSc0upPVae7OtRceiqYYXH+Cg0knNFoN0WpxRKLqGInu4lJIYDDpECoFa3QChEmHUClYoxMgTDqESmAorNMJECad0MjlEtxXn4WEYQh21AkOnvcqNCoVrVFpiaAtWjz7r7/3EcvQhRwOvgoOJh3in3v3bhOEzIGtV0SioiL3H9h95erFpKTEAP+gPn1eHThgKPtSRkb65198cuv29Xp1AwYOfC0+PvbU6eObft5NdNfdvbHmqy/iE2KbNWv19six6zZ8FRQYMm3qnIcPI8a898bny9asXL3U1dXtxw3bNBrNxp++P3/h9OPHSaGhLQcNHNahQ+enLv/cuVP/Hj98/cbV7Oysxo1C33prbKuWbWB6t+66nytWLlm77n8H9p2Ax2fOnNy0eUNMbJSLi2tISMMpk2d5e+sucT5wUHf4YP+d/vf69avHj10iSMQw6RD57vtVkHHTp8+jKCo2Nvqrr5d7e/t2aK87o/bLlYtj46JXfPm9t5fPt9+thCRirxNVUFAwd/60hg0aL160MjsnCyIvPT01OKg+0V8QFH5u3vrj68PeglyDx19/8+Xfh/ZPnjSzS5ceZ86c+HTRx3PnLOnyYveKl7/s8/mtW7WbPWsRPD158ui8+dO2bv7D3d3j0F9nXu7z/MwZC/q8MhBeunT5wicLZ44fN7Vnjz7w9tVrPlvz9ReQs+wnOfjX3tat2701cmzl10ZFN3xEvIVJJzQyhdkjEgsWfJ6fn+fro7tQONSbDh3aH3bxLCRdVlbm+fOnIaGaNA6Flz6aPv/N4f1qeeru6QcVNHj1g/en+Pj4wr/3xk6a/tE4dmmU/re3bdPhtaEjiP6+P4f/OTj8zXcG9B8CTyGebt68tnnLD5B0FSzf1tb2xw3b7ezsoJoGT6FOt2//7hs3w9l8NPbTz2tffOGloUOGE92lq1wnjJ8+Y+aEu/duN2rYBD6Js7PL5IkziDl0AxIECQ0mndBoCs2/lgnD7Nmz/ULYmbi4GHaCr6/u6uGRDx/Az9DQogt4ODo6Qv0IqmBE1+CNgKdBQUX35YF8dHIqcbvFBvWL7lp9//4dlUrVtk1Hw0stWzwHVbys7KwKlk90l73M+3Hjt+HXLqelFV34NzMzo+xnf/jwgXH8NWzQhOju5nULks7w1EwYdAKESSd2NE3PnjtFrVZBvawlBJaj0+QpY9iXcnKy4aeDg6NhZqgiFb2Um2Nv72C8HOiSM34qVyjYB7m5OfDTsEyDjPS0CpafnJw0ZdpYaL0umPdZkybNoHbWs3cHUkZubi7UGRUKW8MUe3t7ok/Joo8hlxOEMOnQg4h7UANaueL751q3Y6dANnnW0jUh2QRRq1SGmTMyi24fY6uwVRlNB2lpKSaX71HLk+hapvNq165rPN3Lyycp+VF5yz9x8ggsHzrpoAFLyqnNEX0jl+g69ZSGKXn6jPNwr0WqDA+mEyI8ykTsoLMMfrLRBqKjH8I/9nHdurp7nkZFR7JPoQJ15UoY+xhiC9InPT2NfXo1/FJ591ioU7ueQl+/gxYu+w+Gd/3rBULlq4Llw3grNIfZmAMn/ztmcuEymQxGRW7dum6Ywj4OCq5PqoqicUhCgDDphAZGJGRyaeXnh9CBvNixc0t2TjYMvH7z7QoYTGBrW7X96vj7B27avCEhMR5iaM1Xn7P9d6BD+85SqRRmzsvLi0+I27LlR09PL5PLh0R7Z9QHMARx40Y4VNMgs2Z8PAHGaiteflBQfeie23/gd41GcyHsLCQgjDY8fpxEdDVNBfyuS5fOQ7zCq4Neff30mRO//74NPj9M+X7t6tat2tYPaUiqSnclE+ypExzpwoULCRKQ8OOZMCLRtKNbJeeHcYB69QKOHvt73bo1Fy+dm/rhLHi6c9fW4yf+GfTqMBhYOHf+1M8/rwsLO9uuXSc7Wzuox/XvPwTyy8PDEwYWfv3tpzt3b741cuyVqxfr1vGHeaA6tvePHT179oEgY38FjDkEBATv3L119f+WwWzBQfVnzFhgq28al7f8oMAQmtbu/v239Ru+zsrKgMavUpkPcZyentqx4wtyuQJ+9bFjfw8cOKxx41CZzGbP3u0bf/o+/NqlVi3bTp82l23V7tr9a0hwg9bFrfJKys3URoZnt3/ZnSABwfu9Cs2WpTFqNV1d93uFtm1BQQF7IC6YM2+qTCpbsnglPIaKGDQwnfVDrrAV9RvQ5d13xg8Z8qY5i69o+TXlUZTy8C8Jk9eEECQgOCKBKrJo8eykpMTx46c1b9YK2pKXL19YtvR/RJ9QEyaOghrQ72pfAAALCklEQVTTmDET3dzcN278TkJJunbtScxU3vIRql5YpxOaaq7TZWetWLkY+u9SUpKhRw9aqc8/34V96c6dmz/8+G1sXLSqsBCakBMnfATNXmKmCpZfU5KilIewTic4mHRC89sXsWoVM3iKP0FVgq1XQcLWq9BoNYxWi/d7RagETDqEkPBh0iFUEh5MJ0SYdAiVhB3XQoRJh1ApWKkTIEw6oZFIKPZilqiqsFInQJh0QkPTDE3j2CtCJWDSIYSED5MOISR8mHQIIeHDpBMaGzkMHuKIRNVJdWM6BAkMFqnQKBwlWg2OSFRdSkK+zIYggcGkE5qW3ZyUuVqCqirieqazuxkXbUa8gEknNIGNXZ3cZbvXPCTIfCkJWZmPtcNnVc81r5D1wKs2CdO+dfEpcQXNurg3aY9XCa+UzHTluX0pqfGqCSvxek0ChEknWBB2j6IKtBrC0CZuS0+VeyoAU8nToXQLLTmjbmOiyvwihjDUU6Y89Xc+dSHwayvekEt/2pK/USrRPbVzpEYvDCZIiDDpBE6ZoVQWSg0XrDMEnIShaAqyQpcYhpkhC2AqXXzDU8OrFKObnZQMFAgH2mge/WxUcfo8WaZE/8R4I7t69cqZ06cnTf7wye+Fj0No4/A1PGZ/o+4TSYp+NVWccsbLlDKU1ug+rVTxIgyTJBShjd5g+IuKXpVoPXzsCBIuPMpE4Ozc7KxtD5beyVNqH3v6yQlCXMGkQ1zTaDQyGW54iFO4wSGuYdIh7uEGh7iGSYe4hxsc4homHeIebnCIa5h0iHu4wSGuYdIh7uEGh7iGSYe4hxsc4homHeIebnCIayqVSi7Hw4YRp/BaJohrWKdD3MMNDnENkw5xDzc4xDVMOsQ93OAQ19RqtY0NXr8ccQqTDnEN63SIe7jBIa5h0iHu4QaHuIZJh7iHGxziGiYd4h5ucIhrmHSIe7jBIa5h0iHu4QaHuIZJh7iHGxziGh5Ph7iHSYe4hnU6xD3c4BDX6tSpg9cyQRzDpENci4uLgwYsQYhDmHSIa9B0hQYsQYhDmHSIa5h0iHuYdIhrmHSIe5h0iGuYdIh7mHSIa5h0iHuYdIhrmHSIe5h0iGuYdIh7mHSIa5h0iHuYdIhrmHSIe5h0iGuYdIh7mHSIa5h0iHuYdIhrmHSIe5h0iGuYdIh7mHSIa5h0iHsSghC3bGxs8KpNiGOYdIhrWKdD3KMYhiEIWV7//v21Wi3U5vLy8uCBRCKBvHN2dj527BhByMKwToc4EhoampycnJGRoVKp2MijabpVq1YEIcvDpEMcee+997y9vY2neHp6vvbaawQhy8OkQxwJCgrq1KmT8ZTg4OD27dsThCwPkw5x5913361duzb72NXVFSt0iDOYdIg7fn5+Xbt2pSiK6O+FCI8JQpzApEOcGjVqFGScg4PD66+/ThDiCh5lgkx4FJUbdjgzPUldkKelacLAv5IzUAxhqJKTYA6qohlgQ6Oo0r/IxHLYjbLMrPppZWYlRcuUUEQmpxzdpSHNHdr19iQIlYRJh0o4sTvp/pU8dSEjkVEKB7mdi62di0ymsJUW1f4pfaQZYk3/lJ3GTip6ndIHW4klF80FcWW8yTH6dgVTci4IP4oxGYJlwSwajbYgT5WfpszPKdQoaZjo5iMdMSuQIFQMkw4VuXcp6/juVKi+OXk51GnK42pRRkJ20oM0rYr4BimGTK5LEMKkQ6xdX8U+jlW513XybViLCIKqUBNxNg5ateOWhxAkeph0iPw4P5KmJQ1eqEcEJ+5WSnZi7phlgbZ2UoJEDJNO7LavislM1TZ60Z8IVGF+4YMziWMWB9o5YtiJFyadqG38NJJmSP2OAUTQtCrtnROxk/6HzVjxwuPpxOuPtfEqpfBjDkjlUo8A5+8+iiBIrDDpRColXhl/v6BxtwAiDr4NPGxspb8ujyFIlDDpROqPtY8ca9kRMWnQuV7GY3XaIyVB4oNJJ0a3L2aqCuiA1j5EZBQO8oM/JBMkPph0YhT2d4bCSU6sVfiNozMWtM/NyyDVLfA575wMvLC7GGHSiVFeptanoTsRH5lcJrEhf/2cSJDI4F0QRSfscBqhiKOruDrpDGwd5YmR2FUnOph0ohNzO08is2Bd/uKVg+cu7n2UHOHrHdKyWY8XOr7BXoNky465hFCtW7y8Y8/iwsJ8/7rN+vae5F83lH3XwUPfXLr2l0Ju36p5b69aFjxbw9HT7vF9FUEig61X0clM1dgoLHW2wJVrh3fsXVLHr+Hc6Xtf6Tn+v7Pb9/31P/YliUQWE3fjcvjfU8b98tknJ2U28u17FrMvnQ37/WzY7sF9Z0754GcPN78jxzcSi/HwcyFIfDDpREejZmwUlqrLh13eF+TfanD/j50c3esHtend/f0zF3bl5Kazr0JV7vVB8z3ca0ulstbNe6ekxsAUmH763M7mTbs3D33J3t65bet+IUFtiMVI5bqUT4zOJ0hMMOlER3fZN5lF6nQ0TUfFXm9Q/8lNcCDsGIaOig5nn3p5BigU9uxjW1sn+JmvzGYYJjU9ztvryeXk6vg1IhZFUco8miAxwX460ZFKKIqxyH6u0ai0WvWho+vgn/H0nLyiOh1FmfhmLSjMo2mtIQGBXG7h0RKGUcjxbH9xwaQTHZmcUqm0xALkclsYUniuZZ/mTV8yng7N1QreZatwkEikanWBYUqhysJNS4p4+GJrRlww6UTH3kmak2GRpAN+vg2UBTkhQc+xTzUadVpGgquLdwVvgZFZN1ff6NgbXZ4vmnLn3hliMVnJ2RKK2DkqCBIT/GYTHc96Co3aUucJ9Ok5/uadkxcu79f12cWEb905b/3PE6FVW/G7WoT2uHH7ePiNo/D431ObY+JvEovJTFbKFJW4PwUSFkw60XlxgDttsROiAv1bThu/GYYgFi5/ef0vk5UFuaNHrLCxeUoFqkeX0e2fG/jHX6tmLGgPFboBr0wl+puBEQsoyCp097HeM+GQheCVOMXoh7mRcmc7/xbeRHxuHYkaOM6nTgNHgsQE63RiFNTCMT9djAeUxV5LlttKMOZECEckxKj76973L+U+jsrwCnQzOcPN2ye3711s8iV7O+d8ZbbJl6AF2v/lD0k1gW6+jVs/MvkSTWspSmLyRted2g7p02sCKUdOav5zPVwJEh9svYrUyT3Jt8/lNn4pwOSrhSplXjkXTSosVCoUpo93k8vtHR2qM0fSM8y+6IhC4eBgb/p8r+irSZq8wrHLgggSH0w68fp5YRRNSYPb1SYiAGPBt4/FTFqNN80RKeynE6/RCwMLc1WPYzKJCNw9EduqmzNBYoVJJ2oTVoQ8vpeRGivwsLt5LCqgif3z/b0IEitsvSLy7fQINz+H2k2FGQS3/41+4VXPZs9jhU7UMOmQzoY5kUQiadDZgpfA5F7s9eTs5PzmnV1fHFyLIHHDpENFdn0V+zhGZesiF8AYRcKd1MyEHJmMenteHTtnPMUVYdIhI+mPVfvXJeRmaqU2Egc3hauvk7OXA+EJlUqVFp2TnZynVmolUtK4vVO318R4EggyCZMOlabKU/35S3JKolpdQBOGUBKiv5xdOWfFU7pre5Z+VnJiideKXmKKFmhyzmK6mcrOUHKKRKrbhtnNmNLdCcimVVenZp3FeOczVAFMOlSRR1HKpNj8/ExGq3nKdlIUXZTuksblzFJhqpVdICyHMrmsEsuRSGi5vdTVy6ZBK7xBBCoXJh1CSPjwvFeEkPBh0iGEhA+TDiEkfJh0CCHhw6RDCAkfJh1CSPj+DwAA///5T9hSAAAABklEQVQDABxBfgxl/3ztAAAAAElFTkSuQmCC", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + } + ], + "execution_count": 3 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:13:36.700225Z", + "start_time": "2025-09-14T00:13:25.108178Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Invoke\n", + "state = parallel_workflow.invoke({\"topic\": \"cats\"})\n", + "print(state[\"combined_output\"])" + ], + "id": "4526cc9bbb346e0b", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Here's a story, joke, and poem about cats!\n", + "\n", + "STORY:\n", + "In the heart of a bustling city, nestled between towering skyscrapers and busy streets, lay a hidden gem: a small, enchanting garden. This garden was no ordinary place; it was a sanctuary for a group of cats, each with their own unique story and personality.\n", + "\n", + "At the center of the garden stood an ancient, gnarled tree, its branches reaching out like welcoming arms. Beneath this tree, a wise old cat named Whisker lived. Whisker had been in the garden longer than any of the other cats could remember, and he was the unofficial leader of the feline community. His fur was a soft, silvery gray, and his eyes held the wisdom of countless sunrises and moonlit nights.\n", + "\n", + "One sunny morning, a tiny, scruffy kitten stumbled into the garden. Her fur was matted, and her eyes were wide with fear and wonder. The other cats, curious but cautious, watched from a distance as the little one explored her new surroundings. Whisker, sensing the kitten's distress, approached her gently.\n", + "\n", + "\"Hello, little one,\" Whisker said in a soothing voice. \"What is your name?\"\n", + "\n", + "The kitten, still trembling, looked up at Whisker with big, round eyes. \"I... I don't have a name,\" she stammered. \"I've been on my own for as long as I can remember.\"\n", + "\n", + "Whisker's heart ached for the young cat. \"Well, then, we shall give you a name,\" he declared. \"How about Luna? It means 'moon,' and you have such bright, shining eyes.\"\n", + "\n", + "Luna's ears perked up, and a small smile crept across her face. \"Luna... I like that,\" she said, her voice growing stronger.\n", + "\n", + "Over the next few weeks, Luna settled into the garden, making friends with the other cats. There was Shadow, a sleek black cat who loved to play hide-and-seek among the shadows of the garden. Then there was Paws, a plump, orange tabby who was always eager to share a meal or a nap in the sun. And there was Bella, a graceful, white-furred cat with a gentle spirit and a love for storytelling.\n", + "\n", + "As the days turned into weeks, Luna grew more confident and happy. She learned the secrets of the garden, from the best spots to catch the warmest rays of sunlight to the most comfortable places to curl up for a nap. Whisker took her under his wing, teaching her the ways of the garden and the importance of looking out for one another.\n", + "\n", + "One evening, as the sun set and the sky painted itself in hues of pink and gold, the cats gathered around the ancient tree. Whisker, sitting on a high branch, began to tell a story. It was a tale of bravery, friendship, and the unbreakable bond between the cats of the garden.\n", + "\n", + "Luna listened intently, her heart swelling with a sense of belonging. She knew that she had found her true home, not just in the garden, but in the hearts of her new family.\n", + "\n", + "And so, life in the garden continued, filled with laughter, adventures, and the quiet, contented purrs of cats who had found their place in the world.\n", + "\n", + "JOKE:\n", + "Why did the cat join a band? Because it already had its own drumsticks!\n", + "\n", + "POEM:\n", + "In the quiet of night, when the world is asleep,\n", + "Soft paws tiptoe, in shadows they creep.\n", + "Eyes like lanterns, aglow with a gleam,\n", + "Silent whispers, in moonlight, they dream.\n", + "\n", + "Fur as soft as the clouds in the sky,\n", + "Whiskers twitching, beneath the moon's eye.\n", + "Graceful dancers, on invisible strings,\n", + "Leaping, bounding, their joy it brings.\n", + "\n", + "Purr of contentment, a soothing sound,\n", + "In the heart of the night, all around.\n", + "Kittens mewling, in cozy retreat,\n", + "Nestled warmly, where love and dreams meet.\n", + "\n", + "Guardians of secrets, keepers of lore,\n", + "In every alley, by every door.\n", + "Mysteries wrapped in a velvet cloak,\n", + "Wise beyond years, in silence they spoke.\n", + "\n", + "Cats, oh cats, with your enigmatic grace,\n", + "In sunbeams you bask, in life's gentle pace.\n", + "Forever weaving, through time's endless thread,\n", + "A tapestry rich, in which we are led.\n" + ] + } + ], + "execution_count": 4 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "", + "id": "faff8da474589197" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/langgraph/orchestrator.ipynb b/test/langgraph/orchestrator.ipynb new file mode 100644 index 0000000..78bf990 --- /dev/null +++ b/test/langgraph/orchestrator.ipynb @@ -0,0 +1,247 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-09-14T07:07:20.035830Z", + "start_time": "2025-09-14T07:07:18.952675Z" + } + }, + "source": [ + "import os\n", + "\n", + "os.environ['DASHSCOPE_API_KEY'] = 'sk-e2a05bbcfac84e53b73f98acef15a009'\n", + "\n", + "# Step 0: Define tools and model\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_community.chat_models.tongyi import ChatTongyi\n", + "\n", + "llm = ChatTongyi(\n", + " model=\"qwen-max\", # 此处以qwen-max为例,您可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models\n", + " streaming=True,\n", + " # other params...\n", + ")" + ], + "outputs": [], + "execution_count": 1 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T07:08:19.248026Z", + "start_time": "2025-09-14T07:08:19.242281Z" + } + }, + "cell_type": "code", + "source": [ + "from typing import Annotated, List\n", + "import operator\n", + "from pydantic import BaseModel, Field\n", + "\n", + "\n", + "# Schema for structured output to use in planning\n", + "class Section(BaseModel):\n", + " name: str = Field(\n", + " description=\"Name for this section of the report.\",\n", + " )\n", + " description: str = Field(\n", + " description=\"Brief overview of the main topics and concepts to be covered in this section.\",\n", + " )\n", + "\n", + "\n", + "class Sections(BaseModel):\n", + " sections: List[Section] = Field(\n", + " description=\"Sections of the report.\",\n", + " )\n", + "\n", + "\n", + "# Augment the LLM with schema for structured output\n", + "planner = llm.with_structured_output(Sections)" + ], + "id": "b9c22b9f90a9810b", + "outputs": [], + "execution_count": 2 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T07:10:47.458182Z", + "start_time": "2025-09-14T07:10:47.448495Z" + } + }, + "cell_type": "code", + "source": [ + "from typing import TypedDict\n", + "from IPython.core.display import Image\n", + "from langgraph.constants import START, END\n", + "from langgraph.graph import StateGraph\n", + "from langchain_core.messages import SystemMessage, HumanMessage\n", + "from langgraph.types import Send\n", + "\n", + "\n", + "# Graph state\n", + "class State(TypedDict):\n", + " topic: str # Report topic\n", + " sections: list[Section] # List of report sections\n", + " completed_sections: Annotated[\n", + " list, operator.add\n", + " ] # All workers write to this key in parallel\n", + " final_report: str # Final report\n", + "\n", + "\n", + "# Worker state\n", + "class WorkerState(TypedDict):\n", + " section: Section\n", + " completed_sections: Annotated[list, operator.add]\n", + "\n", + "\n", + "# Nodes\n", + "def orchestrator(state: State):\n", + " \"\"\"Orchestrator that generates a plan for the report\"\"\"\n", + "\n", + " # Generate queries\n", + " report_sections = planner.invoke(\n", + " [\n", + " SystemMessage(content=\"Generate a plan for the report.\"),\n", + " HumanMessage(content=f\"Here is the report topic: {state['topic']}\"),\n", + " ]\n", + " )\n", + "\n", + " return {\"sections\": report_sections.sections}\n", + "\n", + "\n", + "def llm_call(state: WorkerState):\n", + " \"\"\"Worker writes a section of the report\"\"\"\n", + "\n", + " # Generate section\n", + " section = llm.invoke(\n", + " [\n", + " SystemMessage(\n", + " content=\"Write a report section following the provided name and description. Include no preamble for each section. Use markdown formatting.\"\n", + " ),\n", + " HumanMessage(\n", + " content=f\"Here is the section name: {state['section'].name} and description: {state['section'].description}\"\n", + " ),\n", + " ]\n", + " )\n", + "\n", + " # Write the updated section to completed sections\n", + " return {\"completed_sections\": [section.content]}\n", + "\n", + "\n", + "def synthesizer(state: State):\n", + " \"\"\"Synthesize full report from sections\"\"\"\n", + "\n", + " # List of completed sections\n", + " completed_sections = state[\"completed_sections\"]\n", + "\n", + " # Format completed section to str to use as context for final sections\n", + " completed_report_sections = \"\\n\\n---\\n\\n\".join(completed_sections)\n", + "\n", + " return {\"final_report\": completed_report_sections}\n", + "\n", + "\n", + "# Conditional edge function to create llm_call workers that each write a section of the report\n", + "def assign_workers(state: State):\n", + " \"\"\"Assign a worker to each section in the plan\"\"\"\n", + "\n", + " # Kick off section writing in parallel via Send() API\n", + " return [Send(\"llm_call\", {\"section\": s}) for s in state[\"sections\"]]\n", + "\n", + "\n", + "# Build workflow\n", + "orchestrator_worker_builder = StateGraph(State)\n", + "\n", + "# Add the nodes\n", + "orchestrator_worker_builder.add_node(\"orchestrator\", orchestrator)\n", + "orchestrator_worker_builder.add_node(\"llm_call\", llm_call)\n", + "orchestrator_worker_builder.add_node(\"synthesizer\", synthesizer)\n", + "\n", + "# Add edges to connect nodes\n", + "orchestrator_worker_builder.add_edge(START, \"orchestrator\")\n", + "orchestrator_worker_builder.add_conditional_edges(\n", + " \"orchestrator\", assign_workers, [\"llm_call\"]\n", + ")\n", + "orchestrator_worker_builder.add_edge(\"llm_call\", \"synthesizer\")\n", + "orchestrator_worker_builder.add_edge(\"synthesizer\", END)\n", + "\n", + "# Compile the workflow\n", + "orchestrator_worker = orchestrator_worker_builder.compile()" + ], + "id": "afcc65ff28c2e69b", + "outputs": [], + "execution_count": 4 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T07:10:50.990699Z", + "start_time": "2025-09-14T07:10:50.153622Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Show the workflow\n", + "display(Image(orchestrator_worker.get_graph().draw_mermaid_png()))" + ], + "id": "8b97064947c2cb7a", + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + } + ], + "execution_count": 5 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": [ + "\n", + "# Invoke\n", + "state = orchestrator_worker.invoke({\"topic\": \"Create a report on LLM scaling laws\"})\n", + "\n", + "from IPython.display import Markdown\n", + "Markdown(state[\"final_report\"])" + ], + "id": "c0de9dd437183722" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/langgraph/routing.ipynb b/test/langgraph/routing.ipynb new file mode 100644 index 0000000..64131af --- /dev/null +++ b/test/langgraph/routing.ipynb @@ -0,0 +1,233 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-09-14T01:29:58.461138Z", + "start_time": "2025-09-14T01:29:57.277998Z" + } + }, + "source": [ + "import os\n", + "\n", + "os.environ['DASHSCOPE_API_KEY'] = 'sk-e2a05bbcfac84e53b73f98acef15a009'\n", + "\n", + "# Step 0: Define tools and model\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_community.chat_models.tongyi import ChatTongyi\n", + "\n", + "llm = ChatTongyi(\n", + " model=\"qwen-max\", # 此处以qwen-max为例,您可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models\n", + " streaming=True,\n", + " # other params...\n", + ")" + ], + "outputs": [], + "execution_count": 1 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T01:31:26.890560Z", + "start_time": "2025-09-14T01:31:26.576161Z" + } + }, + "cell_type": "code", + "source": [ + "from pydantic import BaseModel, Field\n", + "from typing_extensions import TypedDict\n", + "from langgraph.graph import StateGraph, START, END\n", + "from IPython.display import Image, display\n", + "from typing_extensions import Literal\n", + "from langchain_core.messages import HumanMessage, SystemMessage\n", + "\n", + "# Schema for structured output to use as routing logic\n", + "class Route(BaseModel):\n", + " step: Literal[\"poem\", \"story\", \"joke\"] = Field(\n", + " None, description=\"The next step in the routing process\"\n", + " )\n", + "\n", + "\n", + "# Augment the LLM with schema for structured output\n", + "router = llm.with_structured_output(Route)\n", + "\n", + "\n", + "# State\n", + "class State(TypedDict):\n", + " input: str\n", + " decision: str\n", + " output: str\n", + "\n", + "\n", + "# Nodes\n", + "def llm_call_1(state: State):\n", + " \"\"\"Write a story\"\"\"\n", + "\n", + " result = llm.invoke(state[\"input\"])\n", + " return {\"output\": result.content}\n", + "\n", + "\n", + "def llm_call_2(state: State):\n", + " \"\"\"Write a joke\"\"\"\n", + "\n", + " result = llm.invoke(state[\"input\"])\n", + " return {\"output\": result.content}\n", + "\n", + "\n", + "def llm_call_3(state: State):\n", + " \"\"\"Write a poem\"\"\"\n", + "\n", + " result = llm.invoke(state[\"input\"])\n", + " return {\"output\": result.content}\n", + "\n", + "\n", + "def llm_call_router(state: State):\n", + " \"\"\"Route the input to the appropriate node\"\"\"\n", + "\n", + " # Run the augmented LLM with structured output to serve as routing logic\n", + " decision = router.invoke(\n", + " [\n", + " SystemMessage(\n", + " content=\"Route the input to story, joke, or poem based on the user's request.\"\n", + " ),\n", + " HumanMessage(content=state[\"input\"]),\n", + " ]\n", + " )\n", + "\n", + " return {\"decision\": decision.step}\n", + "\n", + "\n", + "# Conditional edge function to route to the appropriate node\n", + "def route_decision(state: State):\n", + " # Return the node name you want to visit next\n", + " if state[\"decision\"] == \"story\":\n", + " return \"llm_call_1\"\n", + " elif state[\"decision\"] == \"joke\":\n", + " return \"llm_call_2\"\n", + " elif state[\"decision\"] == \"poem\":\n", + " return \"llm_call_3\"\n", + "\n", + "\n", + "# Build workflow\n", + "router_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "router_builder.add_node(\"llm_call_1\", llm_call_1)\n", + "router_builder.add_node(\"llm_call_2\", llm_call_2)\n", + "router_builder.add_node(\"llm_call_3\", llm_call_3)\n", + "router_builder.add_node(\"llm_call_router\", llm_call_router)\n", + "\n", + "# Add edges to connect nodes\n", + "router_builder.add_edge(START, \"llm_call_router\")\n", + "router_builder.add_conditional_edges(\n", + " \"llm_call_router\",\n", + " route_decision,\n", + " { # Name returned by route_decision : Name of next node to visit\n", + " \"llm_call_1\": \"llm_call_1\",\n", + " \"llm_call_2\": \"llm_call_2\",\n", + " \"llm_call_3\": \"llm_call_3\",\n", + " },\n", + ")\n", + "router_builder.add_edge(\"llm_call_1\", END)\n", + "router_builder.add_edge(\"llm_call_2\", END)\n", + "router_builder.add_edge(\"llm_call_3\", END)\n", + "\n", + "# Compile workflow\n", + "router_workflow = router_builder.compile()" + ], + "id": "3cde4ac7b799ee9c", + "outputs": [], + "execution_count": 2 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T01:31:29.539579Z", + "start_time": "2025-09-14T01:31:28.619753Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Show the workflow\n", + "display(Image(router_workflow.get_graph().draw_mermaid_png()))" + ], + "id": "695f80a7d393be4e", + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + } + ], + "execution_count": 3 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T01:35:03.035331Z", + "start_time": "2025-09-14T01:35:00.799287Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Invoke\n", + "state = router_workflow.invoke({\"input\": \"Write me a joke about cats\"})\n", + "print(state[\"output\"])" + ], + "id": "721ff52941281949", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Why did the cat join the gym? Because it wanted to improve its mouse-cles!\n" + ] + } + ], + "execution_count": 4 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "", + "id": "f1709e07d4bb0b66" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/langgraph/workflows-agents.ipynb b/test/langgraph/workflows-agents.ipynb new file mode 100644 index 0000000..aa8f42c --- /dev/null +++ b/test/langgraph/workflows-agents.ipynb @@ -0,0 +1,267 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-09-14T00:07:57.910209Z", + "start_time": "2025-09-14T00:07:56.596730Z" + } + }, + "source": [ + "import os\n", + "\n", + "os.environ['DASHSCOPE_API_KEY'] = 'sk-e2a05bbcfac84e53b73f98acef15a009'\n", + "\n", + "# Step 0: Define tools and model\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_community.chat_models.tongyi import ChatTongyi\n", + "\n", + "llm = ChatTongyi(\n", + " model=\"qwen-max\", # 此处以qwen-max为例,您可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models\n", + " streaming=True,\n", + " # other params...\n", + ")" + ], + "outputs": [], + "execution_count": 1 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:08:02.725584Z", + "start_time": "2025-09-14T00:07:59.739323Z" + } + }, + "cell_type": "code", + "source": [ + "# Schema for structured output\n", + "from pydantic import BaseModel, Field\n", + "\n", + "class SearchQuery(BaseModel):\n", + " search_query: str = Field(None, description=\"Query that is optimized web search.\")\n", + " justification: str = Field(\n", + " None, description=\"Why this query is relevant to the user's request.\"\n", + " )\n", + "\n", + "\n", + "# Augment the LLM with schema for structured output\n", + "structured_llm = llm.with_structured_output(SearchQuery)\n", + "\n", + "# Invoke the augmented LLM\n", + "output = structured_llm.invoke(\"How does Calcium CT score relate to high cholesterol?\")\n", + "\n", + "# Define a tool\n", + "def multiply(a: int, b: int) -> int:\n", + " return a * b\n", + "\n", + "# Augment the LLM with tools\n", + "llm_with_tools = llm.bind_tools([multiply])\n", + "\n", + "# Invoke the LLM with input that triggers the tool call\n", + "msg = llm_with_tools.invoke(\"What is 2 times 3?\")\n", + "\n", + "# Get the tool call\n", + "msg.tool_calls" + ], + "id": "ffc3ae3a2187bb05", + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'multiply',\n", + " 'args': {'a': 2, 'b': 3},\n", + " 'id': 'call_a59e5004790d42dc827469',\n", + " 'type': 'tool_call'}]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 2 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:08:05.086490Z", + "start_time": "2025-09-14T00:08:04.747991Z" + } + }, + "cell_type": "code", + "source": [ + "# Graph API\n", + "from typing_extensions import TypedDict\n", + "from langgraph.graph import StateGraph, START, END\n", + "from IPython.display import Image, display\n", + "\n", + "\n", + "# Graph state\n", + "class State(TypedDict):\n", + " topic: str\n", + " joke: str\n", + " improved_joke: str\n", + " final_joke: str\n", + "\n", + "\n", + "# Nodes\n", + "def generate_joke(state: State):\n", + " \"\"\"First LLM call to generate initial joke\"\"\"\n", + "\n", + " msg = llm.invoke(f\"Write a short joke about {state['topic']}\")\n", + " return {\"joke\": msg.content}\n", + "\n", + "\n", + "def check_punchline(state: State):\n", + " \"\"\"Gate function to check if the joke has a punchline\"\"\"\n", + "\n", + " # Simple check - does the joke contain \"?\" or \"!\"\n", + " if \"?\" in state[\"joke\"] or \"!\" in state[\"joke\"]:\n", + " return \"Pass\"\n", + " return \"Fail\"\n", + "\n", + "\n", + "def improve_joke(state: State):\n", + " \"\"\"Second LLM call to improve the joke\"\"\"\n", + "\n", + " msg = llm.invoke(f\"Make this joke funnier by adding wordplay: {state['joke']}\")\n", + " return {\"improved_joke\": msg.content}\n", + "\n", + "\n", + "def polish_joke(state: State):\n", + " \"\"\"Third LLM call for final polish\"\"\"\n", + " msg = llm.invoke(f\"Add a surprising twist to this joke: {state['improved_joke']}\")\n", + " return {\"final_joke\": msg.content}\n", + "\n", + "\n", + "# Build workflow\n", + "workflow = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "workflow.add_node(\"generate_joke\", generate_joke)\n", + "workflow.add_node(\"improve_joke\", improve_joke)\n", + "workflow.add_node(\"polish_joke\", polish_joke)\n", + "\n", + "# Add edges to connect nodes\n", + "workflow.add_edge(START, \"generate_joke\")\n", + "workflow.add_conditional_edges(\n", + " \"generate_joke\", check_punchline, {\"Fail\": \"improve_joke\", \"Pass\": END}\n", + ")\n", + "workflow.add_edge(\"improve_joke\", \"polish_joke\")\n", + "workflow.add_edge(\"polish_joke\", END)\n", + "\n", + "# Compile\n", + "chain = workflow.compile()" + ], + "id": "a4c40dfac5828bfc", + "outputs": [], + "execution_count": 3 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:08:07.323059Z", + "start_time": "2025-09-14T00:08:06.397007Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Show workflow\n", + "display(Image(chain.get_graph().draw_mermaid_png()))" + ], + "id": "52d1e4c5b839e17f", + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + } + ], + "execution_count": 4 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-09-14T00:08:10.237014Z", + "start_time": "2025-09-14T00:08:09.399345Z" + } + }, + "cell_type": "code", + "source": [ + "\n", + "# Invoke\n", + "state = chain.invoke({\"topic\": \"cats\"})\n", + "print(\"Initial joke:\")\n", + "print(state[\"joke\"])\n", + "print(\"\\n--- --- ---\\n\")\n", + "if \"improved_joke\" in state:\n", + " print(\"Improved joke:\")\n", + " print(state[\"improved_joke\"])\n", + " print(\"\\n--- --- ---\\n\")\n", + "\n", + " print(\"Final joke:\")\n", + " print(state[\"final_joke\"])\n", + "else:\n", + " print(\"Joke failed quality gate - no punchline detected!\")" + ], + "id": "aef7038dd23aea84", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initial joke:\n", + "Why did the cat join the gym? Because it wanted to improve its mew-scles!\n", + "\n", + "--- --- ---\n", + "\n", + "Joke failed quality gate - no punchline detected!\n" + ] + } + ], + "execution_count": 5 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "", + "id": "5667aff79b939054" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/mcp/math_server.py b/test/mcp/math_server.py new file mode 100644 index 0000000..7ae36be --- /dev/null +++ b/test/mcp/math_server.py @@ -0,0 +1,16 @@ +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("Math") + +@mcp.tool() +def add(a: int, b: int) -> int: + """Add two numbers""" + return a + b + +@mcp.tool() +def multiply(a: int, b: int) -> int: + """Multiply two numbers""" + return a * b + +if __name__ == "__main__": + mcp.run(transport="stdio") \ No newline at end of file