From d22fe6b4c51b17a0e89a777be3e4fe915f77acc4 Mon Sep 17 00:00:00 2001 From: Sidney Radcliffe Date: Sun, 8 Oct 2023 16:21:08 +0100 Subject: [PATCH] feat: (breaking) added docs, more examples in readme, renamed functions, support action_graph as a function, refactorings (#2) --- .pre-commit-config.yaml | 15 +- Makefile | 13 + README.ipynb | 594 +++++++++++++--- README.md | 469 ++++++++++--- README_files/README_3_0.jpg | Bin 11205 -> 0 bytes README_files/README_3_0.png | Bin 15580 -> 0 bytes README_files/README_4_1.jpg | Bin 0 -> 13241 bytes README_files/README_4_1.png | Bin 0 -> 16391 bytes README_files/README_6_0.jpg | Bin 11205 -> 0 bytes README_files/README_6_0.png | Bin 15580 -> 0 bytes README_files/README_7_1.jpg | Bin 0 -> 14659 bytes README_files/README_7_1.png | Bin 0 -> 19343 bytes TODO | 12 - docs/.nojekyll | 0 docs/_sources/index.rst.txt | 32 + docs/_static/alabaster.css | 703 +++++++++++++++++++ docs/_static/basic.css | 925 +++++++++++++++++++++++++ docs/_static/custom.css | 1 + docs/_static/doctools.js | 156 +++++ docs/_static/documentation_options.js | 13 + docs/_static/file.png | Bin 0 -> 286 bytes docs/_static/language_data.js | 199 ++++++ docs/_static/minus.png | Bin 0 -> 90 bytes docs/_static/plus.png | Bin 0 -> 90 bytes docs/_static/pygments.css | 84 +++ docs/_static/searchtools.js | 574 +++++++++++++++ docs/_static/sphinx_highlight.js | 154 ++++ docs/genindex.html | 157 +++++ docs/index.html | 167 +++++ docs/objects.inv | 6 + docs/py-modindex.html | 116 ++++ docs/search.html | 117 ++++ docs/searchindex.js | 1 + docs_creator/Makefile | 20 + docs_creator/make.bat | 35 + docs_creator/source/conf.py | 41 ++ docs_creator/source/index.rst | 32 + examples/email_verification_example.py | 77 ++ make_agents/__init__.py | 19 +- make_agents/bonus.py | 62 ++ make_agents/gpt.py | 30 +- make_agents/make_agents.py | 280 +++++--- poetry.lock | 332 ++++++++- pyproject.toml | 4 +- tests/test_init.py | 5 - tests/test_make_agents.py | 14 +- 46 files changed, 5136 insertions(+), 323 deletions(-) delete mode 100644 README_files/README_3_0.jpg delete mode 100644 README_files/README_3_0.png create mode 100644 README_files/README_4_1.jpg create mode 100644 README_files/README_4_1.png delete mode 100644 README_files/README_6_0.jpg delete mode 100644 README_files/README_6_0.png create mode 100644 README_files/README_7_1.jpg create mode 100644 README_files/README_7_1.png delete mode 100644 TODO create mode 100644 docs/.nojekyll create mode 100644 docs/_sources/index.rst.txt create mode 100644 docs/_static/alabaster.css create mode 100644 docs/_static/basic.css create mode 100644 docs/_static/custom.css create mode 100644 docs/_static/doctools.js create mode 100644 docs/_static/documentation_options.js create mode 100644 docs/_static/file.png create mode 100644 docs/_static/language_data.js create mode 100644 docs/_static/minus.png create mode 100644 docs/_static/plus.png create mode 100644 docs/_static/pygments.css create mode 100644 docs/_static/searchtools.js create mode 100644 docs/_static/sphinx_highlight.js create mode 100644 docs/genindex.html create mode 100644 docs/index.html create mode 100644 docs/objects.inv create mode 100644 docs/py-modindex.html create mode 100644 docs/search.html create mode 100644 docs/searchindex.js create mode 100644 docs_creator/Makefile create mode 100644 docs_creator/make.bat create mode 100644 docs_creator/source/conf.py create mode 100644 docs_creator/source/index.rst create mode 100644 examples/email_verification_example.py create mode 100644 make_agents/bonus.py delete mode 100644 tests/test_init.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c89f5c..8c0ec4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,20 +3,27 @@ repos: rev: v2.3.0 hooks: - id: check-yaml - exclude: README.md + exclude: README.md|docs/ - id: end-of-file-fixer - exclude: README.md + exclude: README.md|docs/ - id: trailing-whitespace - exclude: README.md + exclude: README.md|docs/ - repo: local hooks: - - id: format-python + - id: make-format name: Python - autoflake, isort, black entry: bash -c 'make format' language: system files: \.(py|ipynb)$ pass_filenames: false + # - id: make-docs + # name: Generate docs using Sphinx + # entry: bash -c 'make docs' + # # track docs/ but ignore docs/_sources + # files: ^docs/((?!_sources).)*$ + # language: system + # pass_filenames: false - id: make-readme name: Generate readme from ipynb... entry: bash -c 'make readme' diff --git a/Makefile b/Makefile index f83553b..f6543e0 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: docs + format: poetry run autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place . \ && poetry run black --line-length 90 . \ @@ -19,6 +21,17 @@ readme: poetry run python -m nbconvert --to markdown --output README.md README.ipynb \ && poetry run python scripts/replace_readme_image_links.py +docs: + cd docs_creator \ + && $(MAKE) html \ + && rm -rf ../docs \ + && mkdir ../docs \ + && cp -r build/html/* ../docs \ + && touch ../docs/.nojekyll + +serve_docs: docs + cd docs && python -m http.server + # run semantic release, publish to github + pypi release: if poetry run semantic-release --strict version; then \ diff --git a/README.ipynb b/README.ipynb index 3b5ff3f..aea3ff7 100644 --- a/README.ipynb +++ b/README.ipynb @@ -4,56 +4,128 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "`pip install make_agents`\n", + " \n", "

\n", " \n", "

\n", "\n", - "# MakeAgents\n", + "# MakeAgents \n", "\n", - "MakeAgents is a micro framework for creating LLM-powered agents.\n", - "It consists of tools and a paridigm for creating agents." + "MakeAgents is a micro framework for creating LLM-driven agents.\n", + "\n", + "The MakeAgents paradigm is to define an agent's behaviour and capabilities entirely through **action functions**, and an **action graph**.\n", + "\n", + "Currently supports OpenAI's GPT chat models out of the box.\n", + "\n", + "## Setup\n", + "\n", + "- Install `make_agents` using your package manager, e.g. `pip install make_agents`\n", + "- Set the `OPENAI_API_KEY` environment variable, with your [OpenAI api key](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key).\n", + " - Alternatively, `import openai` and set `openai.api_key = ...`\n", + "- See the examples below, and the reference docs.\n", + "\n", + "## Examples\n", + "\n", + "### Example 1: A conversational agent tasked with getting the user's name" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from IPython.display import display, Markdown\n", + "from pydantic import BaseModel, Field\n", + "import make_agents as ma" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Quickstart examples\n", + "#### Action function, `message_user`\n", "\n", - "### Example 1: A simple conversational agent" + "An action function must have at most one argument, and that argument must be annotated with a Pydantic model. \n", + "The function's name, its docstring, and the Pydantic model, is provided to the LLM, and should be considered as part of the promping strategy.\n", + "It's not recommended to annotate the arg in the docstring, since it is done via the Pydantic model already.\n", + "\n", + "This action function allows the agent to message the user, and get a response." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "import json\n", - "import pprint\n", + "class MessageUserArg(BaseModel):\n", + " message: str = Field(description=\"Message to send user\")\n", "\n", - "import make_agents as ma\n", "\n", - "from pydantic import BaseModel, Field" + "@ma.action\n", + "def message_user(arg: MessageUserArg):\n", + " \"\"\"Send the user a message, and get their response.\"\"\"\n", + " response = \"\"\n", + " while response == \"\":\n", + " response = input(arg.message).strip()\n", + " return response" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAS0AAAEaCAIAAADL/pasAAA8o0lEQVR4Ae3defx1U7kA8FtpUiTlljEyhiuURIjMQ9Jr5iUZM8ZVZlHIlI/5umQMkZm45rEkQ8TtZh4K73WTNF6Nt/v1Llb73ef8zu+c8zv7jM/+4/dbe501Pns963nWM603/P3vf/+neAICAYGeQuCNPe09Og8IBARehUDgYayDgEDvIRB42PtvECMICAQexhoICPQeAtP1fggtjuDll19W409/+tP//u//5qpve9vb3v72t3t917ve9cY3xuaSAROJwYBA3+HhH//4x6def55++ulfTn1enPpIFnFvLADDyfe+973vec97/vmf/3mWWWaR+MAHPvDB158ZZphhrIqRHxDoFQTe0Fu9xd/+9rdHH330gdefRx55ZMqUKSVYvPOd74RXkMrfd7zjHe9+97sVyAQwFf7zn//8hz/8Qfq3v/3t73//+5deegnS/s///I/XUmswc8EFF1x88cWXmPosuuiib37zm0tl4jUg0GUI9AAP0bQ777zztttuu/322yFgJnEo1cILL/w63XrtP/SDcm0D5S9/+QuEfJ2+vvr/ySef/OlPf5qYW82+5S1v+Zd/+Zfll19+pZVWWmGFFWaaaaa2+4qKAYG2IdA9PLz//vuvvPLKW2655Z577kG+jBhlW2qppRJd8ne++ebr2tHuZz/72es0+IF77733hRdeMJ43velNH/7whz/1qU+tu+66yy67rNe2wRoVAwItQaBaPPy///u/H/zgB1dfffXll1/+2GOPGRkm8+Mf//gqq6zyiU98Yumll+4TnhCdvOmmm77//e+j0s8++6xxOlWutdZaG2644WqrrfbWt761JZhG4YBAqxCoCg+JWM4888yzzjrr+eefN6Z55pln0qRJn/3sZyFhn9OZH//4x3YNz3/+538a+cwzzzx58uRtt90W+9oqcKN8QKBZCJDTdPBxHrvwwguRu8Rhzj333AceeCAOsINddK2pxx9//MgjjyTISaD82Mc+dtppp5EGdW0A0dHoQOCfOjVVCr1zzjlngQUWsGoJP9ZZZ52LLrror3/9a6fa72E799133/bbb58UHmS2Bx100K9+9asejie6Hj4IdAAPCTy/8Y1vzDrrrDDQMj3kkEN+8YtfDB+kqEBOOukkFN40WQvst99+tCPD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/GA482hQMCQC1Up/0QpqwOFblL/97W8ff/xxEXSQaLtPSgiuk+CAcxZdDkKmB4oWrREyrIYgEXjY1Y+IkbPabr/99jumPhnx8JZ5wS2xxBI4NWxkV0fWN50RHT3xxBMPPvhg2p4eeOCB3/zmN2l0OIJPfOIT7gv45Cc/+dGPfnSYmNjAw24swIceeui2226Dft/73vecmnRpX3c12tJLL512emzYMK2qDsLUzoVgJpy8995777nnnkQtHXqXWWYZCOn5+Mc/PujQCzzs4JqZpinLRchQtp1ccp999lm/EU4gdMstt9wqq6yy8sori7c9TYV4aQICdJtI5U033QS2WApsrUpwcqWVVhIga911133/+9/fRDN9VyTwsMOfBLm78sorr776amslSVm45FoiEA8Ghkqtg+DGwaKQt956K2hLME6w0y277LLrrLPOeuutB+wd7KvqpgIPOwPhHLfX/Up/+ctfLIjFF1/cgoCBOM/O9BGtjA0BChIIifuwCSYiSb615ZZbbrXVVlQ4Y9frm1/w3/G0DQFxe2+++ebPf/7zyeiEbnqttdY677zziOnbbjMqTgQCLBCuvfZa6JdYD+dGu+GFF14ofyLNVl331UjP8bQBAcprV0TYdNOOKuEWlxdeeKGNpqJKFRCAeFddddWGG26YRDgs8nbbbTdKyyr6mnibgYctw5Aa+stf/jK7ShjIZmXfffelc2+5lajQLQgw6DnuuOOYBPheLPsmT5583333davzZvsJPGwWUsrRMvuKyTASAUQPGVi2UD+K9g4CThDI46c+9anEv6y44op33nln74ZT7jnwsAyRuu+koLvvvrvd1FdcbbXVXCNBOle3ZGT2OQSoPZznE7P62c9+luluPww48HCcr+CYwZPd6QIGUrtTx49TIX4eBAg89thjG2ywATtB3M0OO+zQ84N94GGjVcMOhvoBBqawvEEDGwFrAH9jncMAwPd12ndZXQ9nEHhYH/h0gF/72tdwLyw/Dz/8cCrj+uUid/AhcMkll3AQg42uMe+VwinwsM46IhH92Mc+5sMwAXWcqFMisoYLAmSqTHB88dlmm40ZcPcnF3hYhjkxGgsMdtgUEjxuyz/H+/BC4Oyzz2aPQRr3zW9+s8uzDDycBuBsozgceS677LJpfujEywUXXGDHTY/LJzrRZLTRYQjwvU6GqYccckiHm27Y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", + "text/markdown": [ + "### Action graph" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/jpeg": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAToAAAEhCAIAAABgMftTAAA/zklEQVR4Ae3ddYBsxZUG8ATdsDgPggZNILgHd3f3DbroYlkWCbD4YgGCh0CQYMHdSYCHE3g4wSFBF/cAAXZ/zAn17uvu6ZnpaZ2u+uN23bqlX9VXcupU9ff/7//+73vZZAQyAp2AwGidkMmcx4xARuBbBDJdczvICHQMAmN0TE5zRvtC4Kuvvvr444/5+vvf/z7WWGONPvroo4022gQTTNBXuPy9YxDIdO2YqoqMIuSTTz75/PPPv9hjXnnllbfffvudHvOPf/yjYmHGHXfcYcOG/fCHP/ScbrrpZpxxxhlmmGHmmWf+8Y9/jNIVg2TH9kTg+1nU1J4Vk3L19ddfP/roo3fcccf999//8MMPIyriYRrKMT/60Y+QkJl00kknmmgioX7wgx/8y7/8C8uHH374zTff4DAuv/vuu55vvPHGX//61xdeeAHTn332WVU/55xzzjvvvIssssjSSy895ZRTpkSzpT0RyHRtz3r5HoJdffXVV111FaLK4uKLL77wwguj1jzzzDPZZJMNPtN6AYzF/xEjRgwfPtzTqLv88suvvfbaSy655Bhj5GnX4DGufwyZrvXHdDAxfvbZZ5deeuk555xz5513IifyLLfccnPPPXejZ60fffSRFK+//vorr7zyiy++WGuttbbeemsdxGDKksPWHYFM17pDWmOEZrnHHnvsBRdcYH6LKuutt95UU01VY1yDCGb+/MADD5zfY6aYYooddthhq622GmeccQYRZQ5aPwQsYLJpLQKPP/74xhtvbMG5+eabW6C2NjMpdeLl8847b7755jP3Puywwwy/6VO2tAqB77Uq4ZwuBN56663tttvO2LXTTju9/PLL7YnJjTfeaOU8+eSTn3HGGVa87ZnJLslVpmvLKvp3v/udTVGrU9PglmWi3wlfdtllBNELLLDAU0891e9A2WOdEch0rTOg/YnOngqW2jgxcPXHf5v4+fzzz/faay+7uCeddFKbZKnbspFFTfUTA/QvJtsnq6666uyzz25uOckkk/QvUBv5srG02WabrbDCCr/5zW/GHHPMNspZF2Ql6ww3tZJJkuyOkPpefvnlnchVYNmVDYUNnQ5xVFPh6/rE8ujavCZg1bfEEkv893//9y677NK8VBuT0ieffLLKKqtYe19x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5tVXXy0+Ez+xVFNGBo1Py4vGxx5tNBooe4w28VqRYBUS7mQnJP9nL/Xhh3ieOqyw69EAmDDEbf1LsBd1p5hiChqUU089dfGZuptORqXOee9eujpuRvc9zMsvv8zyxhtvoKiGZZwxIGhDzJRTThlPrYqZbLLJPLWzvPwbTEvE4bfffjsR+M033wT+66+/np7Gc/OIILBTCtOOalB9MKl3btihT1dyF2x89tlnn3nmmeeffx4tg5ymdliXmsF0001nlzLx09S0cyt1CORcZ4q6wd5XXnnln91qzw8mqyb1pe48HeWdeeaZaVua5gyBglcvwlCjqznYE0888fTTT+NnUPSFF14gSqGdp1JnmmmmVM0qW/9dHZ38tQ0RwOHE3pdeekktP/fccyht9RE60sHen/70pyxDbBnS2XS1BFJVjz/++KOPPurJGDnNoGjPFmsOM0cfffQ2bHk5S/VCgCQseBt9tOdf/vIX8rDZZpttjjnmmPM7U37GsF4ZaE48nUfXF1988d57773vvvvuv/9+/LRzoErmmmuuVCuWnc3BLqfSzggQBxp7ox9/rMeYZ5E70M20Kc0suOCCJILtXITyvHUAXQl+kPPuu+/2ZEgp5plnHnD/7Gc/m2+++cxy88hZXq/ZpRwBI7CFkms3oiEhswmzhrTwwgsvvvjipmPlQdrNpX3p+tRTT93SY+644w57dAD9tktcaCE6sV0rGGy31tPR+bEFgLfmaPfccw+LebLNambZZZclg2zPorUXXe3O33rrrZdeeulNN91kFF1iiSUCwdlnn7094cu5GhoIUBe78847tT0DhBWW6dtqq622wQYbGH7bqoBtQVd7LbfffvtFF110+eWX2xwH0yqrrOKisNCGayu8cmaGPAJGXbzVFK+//nq7RM4Ma5D2FNqi4FbkLTT2yg866CDbaMwuu+xigUrFr4X56aCk77rrLkK1J598soPyHFntlJxTtzz//PPXWGMNi69FFlnkkksusRPRWrRbppBNaWH77be3xYKiZ511FnWi448/Hij2SFvejelcZYNYos+c9N9nlaj23ntvyTE33nhjFW8ln1K/VuI+oNe65L96iuVJ1CXn1ROty1cSE9dfXXXVVZSu1l9//f/6r/8ijjrhhBOoW9Yl/hoiaQFd6dzuuuuulgc0xS3xLVNXXHHFbj7JccQRR9gkHGjlkb1R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tNMS1UYr7xmuJp188sknm2yysBeJV8881SOuGP/1LD09zNt6G3OB1157TdEU0KtElEUBUdfpn2QmmWSSeqSf4/gnApmutTcFKkTPPPPME0888dhjjz311FPsL774ouimn376aK+UFrRg4w+KDuGGawzHZP0U9r788ss6Kz3Vs88+y9EcART0N2afffY555zTM+8t1d7gnLIqSikGE1E3hI3DaA888MCIESMef/zxp59+2gR1ttlmi4Y488wzzzTTTPhp3tgNaPRZxk8++QRvsRdQ4NKpeTVdn6PHOJPws5/9jJZk+ZI4ZuniNwm3CmCJJUCfKQ55D5mufVSxAfOuu+5C0fvuu0+DM0hqZDQQos0ZSMtbWx8xdvFn/Z1pCBgZJ/70eiHxcrDe0tdXq4kq8CCthX2YiSee2IJ5yimn9DRiG8NnmGEGO1hVgg+BT5muFSrRLO5P3xl25ERRZqGFFrI2qxAgO9WEgNUE3l577bWoi8am0+YpSy+99BJLLAFtq3rr+RCDiT7E46RfYayirZ9Nv12dIeALL7xAcm5qY47j9ox55pmHuggCD7HONNP1nw3NouDBBx+8/PLLr7nmGpM3lb3ssssus8wyznzn7cqayDjgQLgXnaSLbN555x2kXXvttddcc03jZ3/iEjwm3gbthx9+2PTbOLzUUkvhv6o0/PYnkjb30+10xdLhw4dfeumlV111lZXSaqutttZaa6ldNd3mNTe0s0dSdd1119mRuvfee61y8XaDDTYY0NTG0G0Jc9ttt91+++133303scK66667/vrrkzV0MHTaa3ca+xCHHXYY4a1J1/bbb3/zzTfbiuxOKNq51Oa6p59++iqrrGJdutJKK11yySUmvQPNMNnVhRdeuN5665koLbbYYhdccEENkQw00Ub4//Yarm4zREdrrLGG6tcILrvssszSjmgAr7/++uGHH25Oa5t6r732QuMask1b7LTTTjPA2lpjsQVVQyQtDNJddDUvshy1l7DffvvRLmoh7jnp2hCgfakSV199dTpbLn/E4RriEYkB1sSKeJ/AooYYWhWkW+hqP4buu137Aw88kI5Rq+DO6dYLAcIkR45Mbg866KDa5kdC/c///A/aH3XUUQhcr4w1NJ6hT1cTnmOPPZaS7Y477mgPoKFo5sibjICx0Z6NQfL++++vLWl7SNNNN91OO+3UEYwd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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### Agent execution log" + ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -63,82 +135,54 @@ "name": "stdout", "output_type": "stream", "text": [ - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\"next_function\": \"message_user\"}',\n", - " 'name': 'select_next_func'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '{\"next_function\": \"message_user\"}',\n", - " 'name': 'select_next_func',\n", - " 'role': 'function'}\n", - "\n", - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\"question\": \"What is your first name?\"}',\n", - " 'name': 'message_user'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '\"Uh, well, it\\'s Bill\"',\n", - " 'name': 'message_user',\n", - " 'role': 'function'}\n", - "\n", - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\"next_function\": \"message_user\"}',\n", - " 'name': 'select_next_func'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '{\"next_function\": \"message_user\"}',\n", - " 'name': 'select_next_func',\n", - " 'role': 'function'}\n", - "\n", - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\"question\": \"And what is your last name?\"}',\n", - " 'name': 'message_user'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '\"And that... would be BoBaggins\"',\n", - " 'name': 'message_user',\n", - " 'role': 'function'}\n", - "\n", - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\"next_function\": \"log_name\"}',\n", - " 'name': 'select_next_func'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '{\"next_function\": \"log_name\"}',\n", - " 'name': 'select_next_func',\n", - " 'role': 'function'}\n", - "\n", - "{ 'content': None,\n", - " 'function_call': { 'arguments': '{\\n'\n", - " '\"first_name\": \"Bill\",\\n'\n", - " '\"last_name\": \"BoBaggins\"\\n'\n", - " '}',\n", - " 'name': 'log_name'},\n", - " 'role': 'assistant'}\n", - "\n", - "{ 'content': '{\"first_name\": \"Bill\", \"last_name\": \"BoBaggins\"}',\n", - " 'name': 'log_name',\n", - " 'role': 'function'}\n", + "call `get_task_instructions`: \n", + "\n", + "`get_task_instructions` result: \"Your task is to get both the user's first and last name.\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"Hello, may I have your first name please?\"\n", + "}\n", + "\n", + "`message_user` result: \"Sure, it's Bill\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"I have obtained the user's first name, now I need to get their last name. So, I will call the 'message_user' function again to request for their last name.\",\n", + " \"next_function\": \"message_user\"\n", + "}\n", + "\n", + "`select_next_func` result: \"message_user\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"Nice to meet you, Bill. Could you please tell me your last name as well?\"\n", + "}\n", + "\n", + "`message_user` result: \"BoBaggins\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"Now that I have both the first and last name of the user, the next step is to record both. I will accomplish this task by calling the 'record_first_and_last_name' function.\",\n", + " \"next_function\": \"record_first_and_last_name\"\n", + "}\n", + "\n", + "`select_next_func` result: \"record_first_and_last_name\"\n", + "\n", + "call `record_first_and_last_name`: {\n", + " \"first_name\": \"Bill\",\n", + " \"last_name\": \"BoBaggins\"\n", + "}\n", + "\n", + "`record_first_and_last_name` result: {\"first_name\": \"Bill\", \"last_name\": \"BoBaggins\"}\n", "\n", "Retrieved user_name: {'first_name': 'Bill', 'last_name': 'BoBaggins'}\n" ] } ], "source": [ - "# Define the functions the agent will use\n", + "# Define the remaining action functions.\n", "\n", "\n", - "class MessageUserArg(BaseModel):\n", - " question: str = Field(description=\"Question to ask user\")\n", - "\n", - "\n", - "@ma.llm_func\n", - "def message_user(arg: MessageUserArg):\n", - " \"\"\"Send the user a message, and get their response.\"\"\"\n", - " response = \"\"\n", - " while response == \"\":\n", - " response = input(arg.question).strip()\n", - " return response\n", + "@ma.action\n", + "def get_task_instructions():\n", + " return \"Your task is to get both the user's first and last name.\"\n", "\n", "\n", "class LogNameArg(BaseModel):\n", @@ -146,32 +190,364 @@ " last_name: str = Field(description=\"User's last name\")\n", "\n", "\n", - "@ma.llm_func\n", - "def log_name(arg: LogNameArg):\n", - " \"\"\"Log the name of the user. Only do this if you are certain.\"\"\"\n", + "@ma.action\n", + "def record_first_and_last_name(arg: LogNameArg):\n", + " \"\"\"Record the users first and last name.\"\"\"\n", " return {\"first_name\": arg.first_name, \"last_name\": arg.last_name}\n", "\n", "\n", - "# Define the agent, as a graph of functions\n", - "agent_graph = {\n", - " ma.Start: [message_user],\n", - " message_user: [message_user, log_name],\n", + "# Then define action graph\n", + "action_graph = {\n", + " ma.Start: [get_task_instructions],\n", + " get_task_instructions: [message_user],\n", + " message_user: [message_user, record_first_and_last_name],\n", "}\n", - "display(ma.draw_graph(agent_graph))\n", + "display(Markdown(\"### Action graph\"))\n", + "display(ma.bonus.draw_graph(action_graph))\n", "\n", - "# Initialise the message stack with a system prompt\n", - "messages_init = [\n", - " {\n", - " \"role\": \"system\",\n", - " \"content\": \"Get the first and last name of the user.\",\n", - " }\n", - "]\n", + "# Finally, run the agent\n", + "display(Markdown(\"### Agent execution log\"))\n", + "for messages in ma.run_agent(action_graph):\n", + " ma.bonus.pretty_print(messages[-1]) # print most recent message on stack\n", + "print(f\"Retrieved user_name: {json.loads(messages[-1]['content'])}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: A system assistant — _dangerous_\n", + "\n", + "In this example, the agent is allowed to run bash commands on your system, and read the results... Validation is implemented, so please make sure you understand the command before allowing it to run." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import shlex" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "### Action graph" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/jpeg": "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YdMzLMR6GZwAP+AoD6GumooAo6Vo9holobbT7cRIzF3Yks8jHqzsclmPqSTV6iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOd8fMR8PvECqcF7CaP/vpSv8AWuhVQqhVGABgCud8e/8AIkamPVEX83UV0dAHyr8cvEM138UilpK6nSY4oo2Q9JP9YWHuC2P+A19G+DvEUXivwlp2sx7Q1xEPNUfwSDh1/BgfwrN8V6TpqXmiXK6farNJrEXmSCFQz5VwcnHNdakaRqFjRVUdlGBQA6iiigAooooAhurS2vrZ7a7t4riCQYeKVA6sPcHg1z//AAicum/N4c1a400DpaS5uLU+3lscoP8AcZa6aigDmf8AhItU0n5fEGiyCIdb7TA1zD9WQDzE/wC+WA/vVtabqun6xai5029gu4Scb4ZAwB9DjofY1crF1LwppGpXRvWga11DGBe2chgm/F1wWHs2R7UAbVFcxs8WaMP3clv4gtR/DLi2ugP94fu3P1CfWrVh4u0q8ulsp3l07UG4Fnfp5MjH/Zz8r/VCwoA3aKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA5zx7x4J1I/3VRvydTXR14R+0PaaxZx6frFlqV9Hp8w+y3NslwwiDjLK2zOORuB/3RXTfAazu18BPql9cTTzahcu6vK5Y+WvyAc/7Qf8AOgDrvFv+s8Pj11iH/wBBc/0ro65zxR8+qeFYh1fV849ltp2/pXR0AFFFFABRRRQAUUUUAFFFFABVa+0+y1S0e1v7SC6t3+9FNGHU/gas0UAcz/wjN9pXzeHdZmt0HSyvt1zb/Rcnen4Ngf3aP+EqudL+XxJpE9io63ttm4tfqWUbkH++oA9a6aigCCzvbXULVLqyuYbm3cZSWFw6t9COKnrAu/B+mS3L3tgZtJv3OWudPfyi59XTBST/AIEpqD7X4o0bi8s4tctB/wAt7ECG4A/2omO1vqrD2WgDpqKytK8SaTrMjw2d2PtUYzJazKYp4/8AejYBh9cYrVoAKKKKACiiigAooooAKKKKACiiigAooooAKKiubm3s7d7i6nighQZaSVwqqPcngVz/APwmlnd8aLY6hrJPR7ODEJ/7bSFYyPoxoA6Wiub8/wAZX3+qs9I0pD0a4le7k/FE2KP++zR/wjusXP8Ax/8Aiy/I7x2MEVun5lWf/wAeoA6Sqd5q2m6eM3uoWltj/ntMqfzNY/8Awg2iSc3i31+3c3t/PMD/AMBZ9o/AVcs/Cnh3TyDZ6DpkB/vR2iKfzAzQBVbx74UDFY9fsbhh1W2k84/kmab/AMJvpT/8e9trNyf+mOkXRH5+Xj9a6NVVFCooVR0AGAKWgDm/+Etkf/U+GfEEv/bskf8A6G60v/CRay/+q8GasB6zXNon8pjXR0UAc5/bPiVvueE9v/XXUYx/6CDR/afi0/d8M6eP9/ViP5QmujooA4HxdpXinxd4Vv8ARbjQNGjW4TCSf2vIxjcHKsB9nHQgcZGeRmrHh2z8X6B4b07SYNF0HZaW6Rbm1WUFiByxAtyAScnr3rtqKAOK1C28Z3uraVenSNCA0+WSUINVlO8tG0fX7PxgMexrSOoeMAefDmkH/c1lz/O3FdHRQBzn9q+K1+94XtD/ALmqg/zjFH9ueIl+/wCELhv+uV/Af/QmFdHRQBzn/CTalH/r/B2uJ7o9rIP/AB2bP6Un/CYRp/r9C8QQ/wDcOeT/ANF7q6SigDm/+E70Bf8AXTXlt6/atOuIcf8AfaCp7fxr4Wum2Q+I9KZ/7n2tA35E5rdqC4s7W7Xbc20My+kqBh+tAD4Z4biMSQSpKh6MjBh+YqSufm8DeFZpDIfD+nxynrJBAIn/AO+kwao3nw9sLhdtrrfiTT1xgLaazOAPoGZgKAOuorzg/DTVLU5tPFup3aj+DUL27JP/AAKKdAP++T9KT+xNX0//AI/NO16ZB1l0rxJPP+PlzMp/AbvxoA9IorgLGTTbu6W0tvGviDT74/dtL4okpPsk8RLfhke9bf8AZXiq35tvE1tcY/hv9NDZ/GJ0x+VAHSUVzf8Aafiqx/4/fD9tfRjrJpl4N/8A37lCj8nNWtN8VaVqV2LISyWuoYz9ivImgmI9QrAbh7rke9AFnVdB0vXI0TUrKKcocxyEYkjPqjjDKfcEVlf2X4j0c50nVF1O2H/LnqpO8D0WdRn/AL7V/rXTUUAeeeL/AIoL4W8OXVzc6Td2errtWC1u4yY5WJGSsqEowAyeCDx0FbvgjxzpPjrRhfac+yZMC4tXPzwt7+oPY9/YggZfxF+GyfEIWSz6xcWUdoG2Rxxh1ZmxljyOwA/P1rza0+FGreCPFlgfC3ik3Gsv85t2s9kawZwzTMHPyHGMYyT05GQAe+Xl7a6favdXtzDbW6DLyzOEVfqTwKwR4yt7v/kDaVqurA9JLe38uI/SSUopHupNO07wnCJotQ12X+1tWX5vOnGY4W9IY/uoPfG49ya6OgDm/wC1PFkvMPhmxiH/AE9artP5JE4/Wl+3+MY+X8PaPIP+mOryZ/Jrcfzro6KAObPii+tP+Qp4X1a3QdZbYJdJ+UbF/wDxytLStf0nW1f+zb+G4eP/AFkYOJI/ZkOGU/UCtKsrV/Dml60Uku7fF1F/qbuFjHPEf9mRcMPpnB7g0AatFcvb6nqPh2+g0/XpvtdjO4itdU2hTvPCxzgcBj0DjAY8YBxnqKACsTXdXu7a5tdK0mGOXVLwO0Zmz5cEa43SvjkgFlAUYJJAyBkjbrA17T79NRs9d0mNJ7y0jeGS1dtouYXKllDHhXBRSpPHUHGcgAZa+D7H7Ql5rEkms36nImvQGSM/9M4vuJ9QM+pNdF0GBWRpPibTNYla2hlaC+QZlsblDFPH9UPJH+0Mg9ia16ACiiigDjI/FviG61LW9OsfDNrPc6VMkb51MokodFddrGL72G5BAA/vGmf8J1eDwlDrp0eDjUfsNxAL0/J/pHkbkby/n+bBwQvHer3h7StasPFHiC+voNPW01OZJozBdvJIhSNIwCpjUchS2d3B4561gX3gzxKdBvNEsZNLa0Oqf2jBJNNIrupuBMYnAQhcHPzAtnA+UZyADbuvFGsp4rvNAs9BtriaG0S8ilN+UV42Zlw37s7XypwBuB9RW14e1qHxF4fsdXt43jju4hIEfqh7g/Q5FYcWmeI4/Gt5rps9K8qbTI7RIvt8m7ejO4J/c8KWfbnkgDODnAueBtH1HQPCNnpOpraie1BQNbTNIrrnIPzIpB5Ixg9OvPABmeOLm607W/Ct3YtdvNLqP2ZreK6eOOZTDIQHXO0jcFO4gkAfhV/T/FF5dXOq6bPo/l6zp6xv9liuQ6TI+drJIwXjIYHIBGO/FR+LNG1vVtT0KbSvsEcenXf2t3uZXDMQjpsCqh4O/wC9u49DWRqHgrW9Zh8QXt1eWlpqmpxwQQx28jvFHDE27Yz4VjvywYgDAPegDZPi2awk1ODWtNW2ubGwbUQlrP8AaFmhXOdpKodwIxgjuME0abrF74hhW3vtLNvZ3tm1xDeWGoNIuAVGwyIEKP8AMCNpOQGweDWDZeC9a0/xFNqel6f4d0UT6XJZEWJLGKUncsuPKUS8gDB28dz0q7pnhbVdP16bWYNP0bT3No8TWdjcSLFeSkqRJIfLATGDjCsfmPJoAz/BPi7U4/C3hI6rZSS22phbVb+W7LzNMVZgXQj7p2EBtxPTIFek15vbeEvE9r4V8J6UsGkPNol3HPKxvpQsgjVlAX9yTk7znPTA6549IoA5BvGV/ctqlxpGgPqGn6ZO9vNItxtmlkT74hi2nfjpyy5IwM1HdeOrhItX1Cx0U3ej6PM8N3c/aQkhMfMpjj2kMF56sucHGaS08O+IdFfVbLRrqwSx1C8ku0upS3nWhkOXCx7SsmDkrkjGeQcVVm8H69badr2h6Zcaf/ZeszzSm4nZxNaib/WqqBdr9WKksuM85xQBcbxlqc+v3Gl6ZolteYsI9Rtp/t5RZYXLAZzF8rHbwPmB7kUmmePX1FdBvDpDQaVrb+TbXDTgyLLsZ8PHtwFOxgCGPToM0lj4e1rSfFM19ZWmnPp8ekw6dbJJfSLIfKLspbEJAzvwcE4xnnOKy7Pwl4otPDXhHTBBo7yaHdrPK/26UCUKjoAv7ngnzCeem0dc8AF668earaN4i3eHYXTQGDXbx35w8RjEm5MxjLBTkqcDj7xrU0/xVPc69Zafd6Z9li1G1e7sZhPvMipsyrrtGxsOpwCw96xbvwz4luIPG0Qt9JH9vpsgJvpP3Y8lYct+59Bu4zzx/tVbXQ/ET6/4ZvpLfS0h0yzltp9l7IzEybAWQGEA4EYPJGS2OMZIBoy+I9RsdQsk1TR0tbG9uRawzLdiSRZDnZvQLgBsYyrNgkZrKuPHWr28niJf+EdhkXQCGumTUDh4jGJcpmIZfaeVOB/tGsGP4ca1a22l+XYeHrjUtO1CO7bVJZZFub1VYth38pihORnlxx0ral8OeJZY/Gg+zaSDry7bf/TpP3f7lYfm/c/3QW4zz8v+1QBq3viqS41W10fRtPF9c3NiL6Rpbk26RQMdqncFY7ic4AHYnIqt8Nrq4uvDt41xJduY9Uu4kW7naWSNFlYKhZiSdoAHU9Kr2nhrxBpupabrNommNeppiabe2slzIImVGykiSCPOeTkFR1xnjJ0vBOi6voVlqFrqrWcrTX891HPbSMfM812c7kZRsIyOAW+vHIB09FFFAFa/06x1S1a11Czgu7dusU8YdT+BrnZ9I1Pw1Gbrw/PPd2UQzJpFxIZMr38iRvmRvRSSp6fL1rqmZUUsxCqBkknAArl7rxPJq5ksPCii8uCSj6hjNrbdid/SRh2Rc89So5oA6DT7+31TTba/tH321zEssTYxlWGR/Oo9U0jT9atDa6jaRXMOcgOOVPZlPVSOxGCKNI0yHRtHs9MtyxhtYViVnOWYAYyfc9asXNzBZ20lxczRwwRqWeSRgqqPUk8AUAYGiz3mla3J4dvrmS7jMBubC6lOZHiVgrxuf4mQsnzdSHGeQSekrl9HeTxB4jPiFYnj063tntLAyKVacOytJLg8hT5aBc8nBPQiuooACQASTgCuZ8ER/adGOvzDN3rTfbGY9REf9Sg9lj2/iWPc10rKGUqwyCMEVzngWTy/DEGkynF1o/8AxLp1PUGIBVb6Mmxh7MKAOkrzXxE1tB8U4re+uvEBsbjSXuGt9Oub5h5wlRQ2yAkqNuRwAuTzya9Krjb/AEnxIvxA/wCEgsbLSZraPTmsUjn1CSJ23OrlziFgMFSMZOeuR0oAr2mpHRPBer+JdJ+26hZr5lylpqt3MskSRArIA8nmMCSjMFIHXBxWjpviy6uNY06x1DSRZR6navc2cgufMJCBSyyLtGxsODwWHvUOq6d4q1rwx4gsLiLS459Qga2tYVvHaOFWjKsxfyQxOSTjb7ZFQvoXiCbXfC95Ja6WINMtpILlReyFm8wKp2DyecBAeSMkkcYyQDA8SeK9Q1vwpb63YWV1Z6c+oQJZ3sGoOkjqbhULSRKANjLuABZuo4HUeqV5pN4K8UQ+DI/CtnNpT2VncxSW0800iySxpMJFjcBCEIAxuBbOBwM5HpEJlaCMzIiSlQXVGLKrY5AJAyM98D6CgCHUdPttV064sLyIS21xGY5EPcH+R96y/CF5c3fh9I72Uy3lnNLZTynrI0TlN592Chv+BVsXFxDaW0tzcSLFBChkkkY4CqBkk+2Kw/BcUo8PfbZ42il1G4mvvLYYKLK5ZFI7EIVB980AdDRRRQBQ1XQ9L1uFYtTsYLoIcoZF+aM+qt1U+4INZP8AwjeqWH/IG8SXkSDpb6iovIh7ZJEv/j9dLRQBzf8AaHi2y4utDstQT+/p955bn/tnKAB/32aP+EztYONR0rWtPPczWDyKPq8W9R+ddJRQBh2vjPwzeSeXDr2nGX/nk1wqOP8AgJIP6VtRyxzIHidXQ9GU5BqK6sbS+TZd2sFwn92WMOP1rFk8CeFXcyLoNjBIer20QhY/imDQB0NZepeGtB1m4W41TRNNvp1QIsl1apKwXJOAWBOMknHuaz/+EK06P/j1v9btfaLVrgr/AN8s5H6Uf8Ivfx823i/XY/QP9nlH/j8RP60AbGm6TpujW7W+l6faWMDOXaO1hWJS2AMkKAM4AGfYVcrm/wCxvEqf6vxYX/676dE3/oJWl+xeMo/ua7oso/6aaTIp/MXH9KAOjornNnjVf+W/h+X/ALYzJ/7M1HmeNR/y7aA//bxMv/shoA6Ouf8A+EE8H/8AQqaH/wCC6H/4mozceNR00vw+f+4jMP8A2hSm68a540fQD9dVm/8AkegDo6K5z7T41I/5BGgA/wDYUmP/ALb0ef41P/MP0BP+36Zv/aIoA6Oiuc3eNW/5Z6An/A5m/oKPJ8at/wAv/h+L/tymf/2qtAHR0Vzn9m+LpP8AWeJNNT2g0lh/6FM1J/YOvyf67xjeoO/2ezt0/wDQkagDpKK5v/hEpH/4+PE3iCf1/wBKWLP/AH7RaP8AhBdCf/j4S/uz3+16lcTA/gzkfpQBtXepWFgu68vba2HrNKqfzNcjrfxe8D6GrCTW4buUDIisR55b23L8oP1IretPB/hqwbda+H9MifrvW0TcfqcZqzqmgaRrdoLXU9NtbuADCpNEG2/T0/CgDxdv2iJL/X7S00vRYobJ5gskt7OFYp39FT6ksK7H/hO7jUOP+Eg8PaZGf+fYSX8oHscIoP4MPrUUHwQ8Oab4p0/XdHmubJrS4WY2xbzY2APQZ+YfXJ+lem0AebrL4YvGD6nceIPEsgOQk2nXMkOf+uUcQi/MH610UfiS6aNYtM8I6u8ajC+YkNsijtw7hh+C101FAHNb/GV/wsOkaRGf4nd7yQf8BAjUH/gTU+Dwhay3Ed1rV3c61cxtuT7YR5UbeqwqAgPoSC3vXRUUAFFFFABXPaxpF7DqQ13QjGNQCCO4tpG2x3sY6Kx/hcZO1/cg8HjoaKAMfSPElhq8r2ql7XUYhmawuhsnj99v8S/7S5U9jWxWfquh6ZrcSR6jZRT+WcxueHjPqjjDKfcEVlDw9rNjxpPie5EY6Q6nCLtB7bsrJ+bmgDpaK5vf41i48jw/df7XnTQZ/Da/86PP8avx/Z3h+L3+3zSfp5K/zoA6SqeparYaPZtd6jdxW0AON8jYyewHqT2A5NY50vxVe8XniG1soz1XTbEB8f78rOP/AB0VZ07wppWn3a3zRy3moAYF5eymeUf7pb7g9lAHtQBmmC98ZzRte2stl4eRg62067Zr4g5BkX+CPPOw/M3cAcHraKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//2Q==", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAV4AAAEhCAIAAAC4PV9ZAABLVklEQVR4Ae3dd4A0RbU2cLwIIjkpUQQBQXLOOUtOApJzTpKjgKCggMgliWTJQZJkAUkCkpWMIEmyhA/wInCR7/e+dSn6nZ2dnent2Z2dPf3HTHV1pT5V9dSpE6q/8vnnn48WV1AgKBAUGJUC/zXqbdwFBYICQYERFAhoiHEQFAgK1KHAV+vERVQ7KfA///M/H3/8sRo++OCD8cYbT2Csscb6+te/3s46o+ygQMsUCGhomWTNZHjllVeeeOKJv4+8nnvuuTfeeOOfX1y9ZZ/0i2uKKab4zhfXrLPO6ra3LBEfFGgfBb4SYshKiPvhhx/eddddd95554MPPvjwww+/8847Zvf000+ffieffPJvjLxM/7HHHluN448//uijj/7ZZ5+9//77brESoOPNN9986623XnvttYwpAvLNPffc88477xJLLLHIIouMM844lTQ4CgkKNKZAQENj+vTx9PHHH7/iiiuuu+66+++/f5pppllyySUXWGABM3mOOeaoZI/wr3/9669//Susue+++26//XbMiPJXWWWVtdZaa+aZZ+6jcfE4KNAPCgQ0lCHeSy+9dNZZZ11wwQX/+Mc/VlpppdVXX32ZZZb51re+VaasVvK88MILt95669VXX33TTTdNO+20G2644RZbbDHVVFO1UkakDQo0RwEbiriapMB//vOfa6+9dsUVVxxzzDHBweWXX25VbzJvtcnsXy655BLsg5asuuqqkKLa8qO0oEBwDU0hqIHyu9/97mc/+9mrr7666667Wqs7RDpoi3HGGWeceOKJmIgDDjhgjTXW+MpXvtLUK0WioEBDCgQ0NCTPyIf33nvv7rvvDhT23XffLbfcshIhQt+1tpKCFPO00077xS9+Md100/3qV7+ab775WskdaYMCdSgQJk91iJKjmB5svfXWyy+/PNb96aef3mmnnToQF7SW1mO33XZ75plnll566aWWWmrHHXcEFvktIhAUKEGB4Bp6Jdo999yz8cYb0z6SOE499dS9puuwB88///zmm2/++uuvn3/++cE+dFjnDKXmBNdQv7d++9vfYhZ23nlnEr4hhAtexp6CFoM0BAdBVFn/9SI2KNAnBUIS25MCP/3pTyeccMKbb76556MhFEOZwrDquOOOG0JtjqZ2DgXC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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### Agent execution log" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call `get_task_instructions`: \n", + "\n", + "`get_task_instructions` result: \"Your task is help the user with their computer system, until they ask to end the chat. Please give the user only the relevant information.\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"Hello! How may I assist you with your computer system today?\"\n", + "}\n", + "\n", + "`message_user` result: \"Hi, how much space do I have left on my hard drive?\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"To check the available space on the user's hard drive, I can utilize the bash command 'df -h'. Therefore, the 'run_bash_command' function should be the next one to use.\",\n", + " \"next_function\": \"run_bash_command\"\n", + "}\n", + "\n", + "`select_next_func` result: \"run_bash_command\"\n", + "\n", + "call `run_bash_command`: {\n", + " \"plan\": \"To check the available space on the hard drive\",\n", + " \"command\": \"df -h\"\n", + "}\n", + "\n", + "`run_bash_command` result: {\"stout\": \"Filesystem Size Used Avail Use% Mounted on\\nudev 7.8G 0 7.8G 0% /dev\\ntmpfs 1.6G 2.0M 1.6G 1% /run\\n/dev/sda5 909G 790G 82G 91% /\\ntmpfs 7.8G 0 7.8G 0% /dev/shm\\ntmpfs 5.0M 4.0K 5.0M 1% /run/lock\\ntmpfs 7.8G 0 7.8G 0% /sys/fs/cgroup\\n/dev/loop0 128K 128K 0 100% /snap/bare/5\\n/dev/loop1 304M 304M 0 100% /snap/code/141\\n/dev/loop2 304M 304M 0 100% /snap/code/142\\n/dev/loop3 106M 106M 0 100% /snap/core/16091\\n/dev/loop4 106M 106M 0 100% /snap/core/16202\\n/dev/loop5 56M 56M 0 100% /snap/core18/2785\\n/dev/loop6 56M 56M 0 100% /snap/core18/2790\\n/dev/loop7 64M 64M 0 100% /snap/core20/1974\\n/dev/loop8 64M 64M 0 100% /snap/core20/2015\\n/dev/loop9 74M 74M 0 100% /snap/core22/858\\n/dev/loop10 74M 74M 0 100% /snap/core22/864\\n/dev/loop11 219M 219M 0 100% /snap/gnome-3-34-1804/90\\n/dev/loop12 219M 219M 0 100% /snap/gnome-3-34-1804/93\\n/dev/loop14 350M 350M 0 100% /snap/gnome-3-38-2004/143\\n/dev/loop13 350M 350M 0 100% /snap/gnome-3-38-2004/140\\n/dev/loop16 497M 497M 0 100% /snap/gnome-42-2204/132\\n/dev/loop15 497M 497M 0 100% /snap/gnome-42-2204/141\\n/dev/loop18 92M 92M 0 100% /snap/gtk-common-themes/1535\\n/dev/loop17 82M 82M 0 100% /snap/gtk-common-themes/1534\\n/dev/loop19 46M 46M 0 100% /snap/snap-store/638\\n/dev/loop20 13M 13M 0 100% /snap/snap-store/959\\n/dev/loop21 41M 41M 0 100% /snap/snapd/19993\\n/dev/loop22 41M 41M 0 100% /snap/snapd/20092\\n/dev/loop23 2.3M 2.3M 0 100% /snap/starship/1766\\n/dev/loop24 3.0M 3.0M 0 100% /snap/starship/2049\\n/dev/loop25 296M 296M 0 100% /snap/vlc/2344\\n/dev/loop26 321M 321M 0 100% /snap/vlc/3078\\ntmpfs 1.6G 16K 1.6G 1% /run/user/125\\ntmpfs 1.6G 4.0K 1.6G 1% /run/user/1000\\n\", \"stderr\": \"\"}\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The bash command has been successfully executed and returned the status of the user's hard drive space. The relevant information here for the user is that on the root directory ('/') which is of size 909G, 790G is used and 82G is still available. Currently, 91% of the storage space is used. Therefore, the 'message_user' function should be the next one to use, to provide this relevant information to the user.\",\n", + " \"next_function\": \"message_user\"\n", + "}\n", + "\n", + "`select_next_func` result: \"message_user\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"You have a total of 909GB on your root directory. Currently, about 790GB is used, leaving you with approximately 82GB remaining. This means you've used about 91% of your total space.\"\n", + "}\n", + "\n", + "`message_user` result: \"thanks, that's all\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The user has indicated that they are finished and needs no further assistance. Therefore, the 'End' function should be the next one to use.\",\n", + " \"next_function\": \"End\"\n", + "}\n", + "\n", + "`select_next_func` result: \"End\"\n", + "\n" + ] + } + ], + "source": [ + "@ma.action\n", + "def get_task_instructions():\n", + " return \"Your task is help the user with their computer system, until they ask to end the chat. Please give the user only the relevant information.\"\n", + "\n", + "\n", + "class RunBashCommandArg(BaseModel):\n", + " plan: str = Field(description=\"Plan what to run\")\n", + " command: str = Field(description=\"Command to run\")\n", + "\n", + "\n", + "@ma.action\n", + "def run_bash_command(arg: RunBashCommandArg):\n", + " \"\"\"Record the users first and last name.\"\"\"\n", + " result = subprocess.run(shlex.split(arg.command), capture_output=True, text=True)\n", + " return {\"stout\": result.stdout, \"stderr\": result.stderr}\n", + "\n", + "\n", + "# Define action graph\n", + "action_graph = {\n", + " ma.Start: [get_task_instructions],\n", + " get_task_instructions: [message_user],\n", + " message_user: [message_user, run_bash_command, ma.End],\n", + " run_bash_command: [message_user, run_bash_command],\n", + "}\n", + "display(Markdown(\"### Action graph\"))\n", + "display(ma.bonus.draw_graph(action_graph))\n", "\n", "# Run the agent\n", - "for messages in ma.run_agent(agent_graph, messages_init):\n", - " pprint.pprint(messages[-1], indent=2)\n", - " print()\n", - "print(f\"Retrieved user_name: {json.loads(messages[-1]['content'])}\")" + "display(Markdown(\"### Agent execution log\"))\n", + "for messages in ma.run_agent(action_graph):\n", + " ma.bonus.pretty_print(messages[-1])\n", + " if messages[-1].get(\"function_call\", {}).get(\"name\", \"\") == run_bash_command.__name__:\n", + " command = json.loads(messages[-1][\"function_call\"][\"arguments\"])[\"command\"]\n", + " input(\n", + " f\"Please validate the bash command, before pressing enter to continue. Command: `{command}`\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 3: A dynamic action graph to carry out validation before making actions accessible" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "### Agent execution log" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call `get_task_instructions`: \n", + "\n", + "`get_task_instructions` result: \"Get the users email address, and validate it.\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"Could you please share your email address?\"\n", + "}\n", + "\n", + "`message_user` result: \"sure, it's bob@bill.io\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"Now that I have the users email, I can proceed to validate it. The best function to use for this is 'send_validation_email'.\",\n", + " \"next_function\": \"send_validation_email\"\n", + "}\n", + "\n", + "`select_next_func` result: \"send_validation_email\"\n", + "\n", + "call `send_validation_email`: {\n", + " \"users_email_address\": \"bob@bill.io\"\n", + "}\n", + "\n", + "`send_validation_email` result: {\"status\": \"error\", \"description\": \"Email address must end with `.com`\"}\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The validation email could not be sent because the users email address does not end with '.com'. I will ask the user to provide a valid email address\",\n", + " \"next_function\": \"message_user\"\n", + "}\n", + "\n", + "`select_next_func` result: \"message_user\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"I'm sorry, but your email address must end with '.com'. Could you please provide a valid email address?\"\n", + "}\n", + "\n", + "`message_user` result: \"Ah, no prob, it's bob@bill.com\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"Now that I have received a correct email ending with '.com', I should now send the validation email. The best function to use is 'send_validation_email'.\",\n", + " \"next_function\": \"send_validation_email\"\n", + "}\n", + "\n", + "`select_next_func` result: \"send_validation_email\"\n", + "\n", + "call `send_validation_email`: {\n", + " \"users_email_address\": \"bob@bill.com\"\n", + "}\n", + "\n", + "`send_validation_email` result: {\"status\": \"success\", \"description\": \"Validation code sent\"}\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"A validation code has been sent to your email. Could you please provide the code to confirm your email address?\"\n", + "}\n", + "\n", + "`message_user` result: \"uh, I think 1234\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The user has provided a validation code. I should now check if the code is correct using the 'check_validation_code' function.\",\n", + " \"next_function\": \"check_validation_code\"\n", + "}\n", + "\n", + "`select_next_func` result: \"check_validation_code\"\n", + "\n", + "call `check_validation_code`: {\n", + " \"validation_code\": \"1234\"\n", + "}\n", + "\n", + "`check_validation_code` result: {\"status\": \"error\", \"description\": \"Validation code must be 6 digits\"}\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The validation code provided by the user is invalid as it is not 6 digits. I should ask the user again to provide the correct validation code\",\n", + " \"next_function\": \"message_user\"\n", + "}\n", + "\n", + "`select_next_func` result: \"message_user\"\n", + "\n", + "call `message_user`: {\n", + " \"message\": \"I'm sorry, but the validation code should be 6 digits. Could you please check your email and provide the correct validation code?\"\n", + "}\n", + "\n", + "`message_user` result: \"123456\"\n", + "\n", + "call `select_next_func`: {\n", + " \"thought_process\": \"The user has provided a 6 digits validation code. I will now validate it using the 'check_validation_code' function.\",\n", + " \"next_function\": \"check_validation_code\"\n", + "}\n", + "\n", + "`select_next_func` result: \"check_validation_code\"\n", + "\n", + "call `check_validation_code`: {\n", + " \"validation_code\": \"123456\"\n", + "}\n", + "\n", + "`check_validation_code` result: {\"status\": \"success\", \"description\": \"Validation code correct\"}\n", + "\n" + ] + } + ], + "source": [ + "@ma.action\n", + "def get_task_instructions():\n", + " return \"Get the users email address, and validate it.\"\n", + "\n", + "\n", + "class SendValidationEmailArg(BaseModel):\n", + " users_email_address: str = Field(description=\"The users email address\")\n", + "\n", + "\n", + "@ma.action\n", + "def send_validation_email(arg: SendValidationEmailArg):\n", + " \"\"\"Send the user a validation email.\"\"\"\n", + " if not arg.users_email_address.endswith(\".com\"):\n", + " return {\"status\": \"error\", \"description\": \"Email address must end with `.com`\"}\n", + " else:\n", + " return {\"status\": \"success\", \"description\": \"Validation code sent\"}\n", + "\n", + "\n", + "class CheckValidationCodeArg(BaseModel):\n", + " validation_code: str = Field(description=\"The validation code (6 digits)\")\n", + "\n", + "\n", + "@ma.action\n", + "def check_validation_code(arg: CheckValidationCodeArg):\n", + " \"\"\"Send the user a validation email.\"\"\"\n", + " if len(arg.validation_code) != 6:\n", + " return {\"status\": \"error\", \"description\": \"Validation code must be 6 digits\"}\n", + " elif arg.validation_code == \"123456\":\n", + " return {\"status\": \"success\", \"description\": \"Validation code correct\"}\n", + " else:\n", + " return {\"status\": \"error\", \"description\": \"Validation code incorrect\"}\n", + "\n", + "\n", + "def action_graph(current_action: callable, current_action_result: dict) -> list[callable]:\n", + " \"\"\"Return the next action(s) to run, given the current action and its result.\"\"\"\n", + " if current_action == ma.Start:\n", + " return [get_task_instructions]\n", + " elif current_action == get_task_instructions:\n", + " return [message_user]\n", + " elif current_action == message_user:\n", + " return [message_user, send_validation_email, check_validation_code]\n", + " elif current_action == send_validation_email:\n", + " if current_action_result[\"status\"] == \"success\":\n", + " return [message_user]\n", + " else:\n", + " return [message_user, send_validation_email]\n", + " elif current_action == check_validation_code:\n", + " if current_action_result[\"status\"] == \"success\":\n", + " return [ma.End]\n", + " else:\n", + " return [message_user, check_validation_code]\n", + " else:\n", + " raise ValueError(f\"Unknown action: {current_action}\")\n", + "\n", + "\n", + "# We lose the ability to plot the graph when using a function to define the action graph.\n", + "\n", + "# Run the agent\n", + "display(Markdown(\"### Agent execution log\"))\n", + "for messages in ma.run_agent(action_graph):\n", + " ma.bonus.pretty_print(messages[-1])" ] }, { @@ -180,8 +556,16 @@ "source": [ "### Notes:\n", "\n", - "Prompting has a big impact on the performance of the agent. The `llm_func` function names, Pydantic models and docstrings can all be considered part of the prompt.\n", + "- Prompting has a big impact on the performance of the agent. Action function names, Pydantic models and docstrings can all be considered part of the prompting strategy.\n", + "- The current preferred way to deal with exceptions due to the model not providing correct function args is to modify the prompts / action graph, to reduce the error rate.\n", + "- \"gpt-4\" is used by default, and performs better than \"gpt-3.5-turbo\", (at least with the current set up and prompts).\n", + "\n", + "\n", + "### Contributing\n", "\n", + "- For any ideas/comments/suggestions, create a GitHub issue, or comment in a relevant issue.\n", + "- For the development of the framework itself, the aspiration is take an \"example driven\" development approach. \n", + " I.e. find compelling examples where a feature / change would be helpful before adding it.\n", "\n", "### Dev setup\n", "\n", diff --git a/README.md b/README.md index cd6c72f..a6a7c40 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,84 @@ - + + + -[![](https://github.com/sradc/make_agents/workflows/Python%20package/badge.svg?branch=main)](https://github.com/sradc/make_agents/commits/) +`pip install make_agents` +

-# MakeAgents +# MakeAgents -MakeAgents is a micro framework for creating LLM-powered agents. -It consists of tools and a paridigm for creating agents. +MakeAgents is a micro framework for creating LLM-driven agents. -## Quickstart examples +The MakeAgents paradigm is to define an agent's behaviour and capabilities entirely through **action functions**, and an **action graph**. -### Example 1: A simple conversational agent +Currently supports OpenAI's GPT chat models out of the box. +## Setup -```python -import json -import pprint +- Install `make_agents` using your package manager, e.g. `pip install make_agents` +- Set the `OPENAI_API_KEY` environment variable, with your [OpenAI api key](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key). + - Alternatively, `import openai` and set `openai.api_key = ...` +- See the examples below, and the reference docs. -import make_agents as ma +## Examples +### Example 1: A conversational agent tasked with getting the user's name + + +```python +import json +from IPython.display import display, Markdown from pydantic import BaseModel, Field +import make_agents as ma ``` +#### Action function, `message_user` -```python -# Define the functions the agent will use +An action function must have at most one argument, and that argument must be annotated with a Pydantic model. +The function's name, its docstring, and the Pydantic model, is provided to the LLM, and should be considered as part of the promping strategy. +It's not recommended to annotate the arg in the docstring, since it is done via the Pydantic model already. + +This action function allows the agent to message the user, and get a response. +```python class MessageUserArg(BaseModel): - question: str = Field(description="Question to ask user") + message: str = Field(description="Message to send user") -@ma.llm_func +@ma.action def message_user(arg: MessageUserArg): """Send the user a message, and get their response.""" response = "" while response == "": - response = input(arg.question).strip() + response = input(arg.message).strip() return response +``` + + +```python +# Define the remaining action functions. + + +@ma.action +def get_task_instructions(): + return "Your task is to get both the user's first and last name." class LogNameArg(BaseModel): @@ -50,104 +86,371 @@ class LogNameArg(BaseModel): last_name: str = Field(description="User's last name") -@ma.llm_func -def log_name(arg: LogNameArg): - """Log the name of the user. Only do this if you are certain.""" +@ma.action +def record_first_and_last_name(arg: LogNameArg): + """Record the users first and last name.""" return {"first_name": arg.first_name, "last_name": arg.last_name} -# Define the agent, as a graph of functions -agent_graph = { - ma.Start: [message_user], - message_user: [message_user, log_name], +# Then define action graph +action_graph = { + ma.Start: [get_task_instructions], + get_task_instructions: [message_user], + message_user: [message_user, record_first_and_last_name], } -display(ma.draw_graph(agent_graph)) +display(Markdown("### Action graph")) +display(ma.bonus.draw_graph(action_graph)) + +# Finally, run the agent +display(Markdown("### Agent execution log")) +for messages in ma.run_agent(action_graph): + ma.bonus.pretty_print(messages[-1]) # print most recent message on stack +print(f"Retrieved user_name: {json.loads(messages[-1]['content'])}") +``` + + +### Action graph + + + + +![png](https://raw.githubusercontent.com/sradc/MakeAgents/master/README_files/README_4_1.png) + + + + +### Agent execution log -# Initialise the message stack with a system prompt -messages_init = [ - { - "role": "system", - "content": "Get the first and last name of the user.", + + call `get_task_instructions`: + + `get_task_instructions` result: "Your task is to get both the user's first and last name." + + call `message_user`: { + "message": "Hello, may I have your first name please?" + } + + `message_user` result: "Sure, it's Bill" + + call `select_next_func`: { + "thought_process": "I have obtained the user's first name, now I need to get their last name. So, I will call the 'message_user' function again to request for their last name.", + "next_function": "message_user" + } + + `select_next_func` result: "message_user" + + call `message_user`: { + "message": "Nice to meet you, Bill. Could you please tell me your last name as well?" } -] + + `message_user` result: "BoBaggins" + + call `select_next_func`: { + "thought_process": "Now that I have both the first and last name of the user, the next step is to record both. I will accomplish this task by calling the 'record_first_and_last_name' function.", + "next_function": "record_first_and_last_name" + } + + `select_next_func` result: "record_first_and_last_name" + + call `record_first_and_last_name`: { + "first_name": "Bill", + "last_name": "BoBaggins" + } + + `record_first_and_last_name` result: {"first_name": "Bill", "last_name": "BoBaggins"} + + Retrieved user_name: {'first_name': 'Bill', 'last_name': 'BoBaggins'} + + +### Example 2: A system assistant — _dangerous_ + +In this example, the agent is allowed to run bash commands on your system, and read the results... Validation is implemented, so please make sure you understand the command before allowing it to run. + + +```python +import subprocess +import shlex +``` + + +```python +@ma.action +def get_task_instructions(): + return "Your task is help the user with their computer system, until they ask to end the chat. Please give the user only the relevant information." + + +class RunBashCommandArg(BaseModel): + plan: str = Field(description="Plan what to run") + command: str = Field(description="Command to run") + + +@ma.action +def run_bash_command(arg: RunBashCommandArg): + """Record the users first and last name.""" + result = subprocess.run(shlex.split(arg.command), capture_output=True, text=True) + return {"stout": result.stdout, "stderr": result.stderr} + + +# Define action graph +action_graph = { + ma.Start: [get_task_instructions], + get_task_instructions: [message_user], + message_user: [message_user, run_bash_command, ma.End], + run_bash_command: [message_user, run_bash_command], +} +display(Markdown("### Action graph")) +display(ma.bonus.draw_graph(action_graph)) # Run the agent -for messages in ma.run_agent(agent_graph, messages_init): - pprint.pprint(messages[-1], indent=2) - print() -print(f"Retrieved user_name: {json.loads(messages[-1]['content'])}") +display(Markdown("### Agent execution log")) +for messages in ma.run_agent(action_graph): + ma.bonus.pretty_print(messages[-1]) + if messages[-1].get("function_call", {}).get("name", "") == run_bash_command.__name__: + command = json.loads(messages[-1]["function_call"]["arguments"])["command"] + input( + f"Please validate the bash command, before pressing enter to continue. Command: `{command}`" + ) ``` +### Action graph + + + -![png](https://raw.githubusercontent.com/sradc/MakeAgents/master/README_files/README_3_0.png) +![png](https://raw.githubusercontent.com/sradc/MakeAgents/master/README_files/README_7_1.png) - { 'content': None, - 'function_call': { 'arguments': '{"next_function": "message_user"}', - 'name': 'select_next_func'}, - 'role': 'assistant'} + +### Agent execution log + + + call `get_task_instructions`: - { 'content': '{"next_function": "message_user"}', - 'name': 'select_next_func', - 'role': 'function'} + `get_task_instructions` result: "Your task is help the user with their computer system, until they ask to end the chat. Please give the user only the relevant information." - { 'content': None, - 'function_call': { 'arguments': '{"question": "What is your first name?"}', - 'name': 'message_user'}, - 'role': 'assistant'} + call `message_user`: { + "message": "Hello! How may I assist you with your computer system today?" + } - { 'content': '"Uh, well, it\'s Bill"', - 'name': 'message_user', - 'role': 'function'} + `message_user` result: "Hi, how much space do I have left on my hard drive?" - { 'content': None, - 'function_call': { 'arguments': '{"next_function": "message_user"}', - 'name': 'select_next_func'}, - 'role': 'assistant'} + call `select_next_func`: { + "thought_process": "To check the available space on the user's hard drive, I can utilize the bash command 'df -h'. Therefore, the 'run_bash_command' function should be the next one to use.", + "next_function": "run_bash_command" + } - { 'content': '{"next_function": "message_user"}', - 'name': 'select_next_func', - 'role': 'function'} + `select_next_func` result: "run_bash_command" - { 'content': None, - 'function_call': { 'arguments': '{"question": "And what is your last name?"}', - 'name': 'message_user'}, - 'role': 'assistant'} + call `run_bash_command`: { + "plan": "To check the available space on the hard drive", + "command": "df -h" + } - { 'content': '"And that... would be BoBaggins"', - 'name': 'message_user', - 'role': 'function'} + `run_bash_command` result: {"stout": "Filesystem Size Used Avail Use% Mounted on\nudev 7.8G 0 7.8G 0% /dev\ntmpfs 1.6G 2.0M 1.6G 1% /run\n/dev/sda5 909G 790G 82G 91% /\ntmpfs 7.8G 0 7.8G 0% /dev/shm\ntmpfs 5.0M 4.0K 5.0M 1% /run/lock\ntmpfs 7.8G 0 7.8G 0% /sys/fs/cgroup\n/dev/loop0 128K 128K 0 100% /snap/bare/5\n/dev/loop1 304M 304M 0 100% /snap/code/141\n/dev/loop2 304M 304M 0 100% /snap/code/142\n/dev/loop3 106M 106M 0 100% /snap/core/16091\n/dev/loop4 106M 106M 0 100% /snap/core/16202\n/dev/loop5 56M 56M 0 100% /snap/core18/2785\n/dev/loop6 56M 56M 0 100% /snap/core18/2790\n/dev/loop7 64M 64M 0 100% /snap/core20/1974\n/dev/loop8 64M 64M 0 100% /snap/core20/2015\n/dev/loop9 74M 74M 0 100% /snap/core22/858\n/dev/loop10 74M 74M 0 100% /snap/core22/864\n/dev/loop11 219M 219M 0 100% /snap/gnome-3-34-1804/90\n/dev/loop12 219M 219M 0 100% /snap/gnome-3-34-1804/93\n/dev/loop14 350M 350M 0 100% /snap/gnome-3-38-2004/143\n/dev/loop13 350M 350M 0 100% /snap/gnome-3-38-2004/140\n/dev/loop16 497M 497M 0 100% /snap/gnome-42-2204/132\n/dev/loop15 497M 497M 0 100% /snap/gnome-42-2204/141\n/dev/loop18 92M 92M 0 100% /snap/gtk-common-themes/1535\n/dev/loop17 82M 82M 0 100% /snap/gtk-common-themes/1534\n/dev/loop19 46M 46M 0 100% /snap/snap-store/638\n/dev/loop20 13M 13M 0 100% /snap/snap-store/959\n/dev/loop21 41M 41M 0 100% /snap/snapd/19993\n/dev/loop22 41M 41M 0 100% /snap/snapd/20092\n/dev/loop23 2.3M 2.3M 0 100% /snap/starship/1766\n/dev/loop24 3.0M 3.0M 0 100% /snap/starship/2049\n/dev/loop25 296M 296M 0 100% /snap/vlc/2344\n/dev/loop26 321M 321M 0 100% /snap/vlc/3078\ntmpfs 1.6G 16K 1.6G 1% /run/user/125\ntmpfs 1.6G 4.0K 1.6G 1% /run/user/1000\n", "stderr": ""} - { 'content': None, - 'function_call': { 'arguments': '{"next_function": "log_name"}', - 'name': 'select_next_func'}, - 'role': 'assistant'} + call `select_next_func`: { + "thought_process": "The bash command has been successfully executed and returned the status of the user's hard drive space. The relevant information here for the user is that on the root directory ('/') which is of size 909G, 790G is used and 82G is still available. Currently, 91% of the storage space is used. Therefore, the 'message_user' function should be the next one to use, to provide this relevant information to the user.", + "next_function": "message_user" + } - { 'content': '{"next_function": "log_name"}', - 'name': 'select_next_func', - 'role': 'function'} + `select_next_func` result: "message_user" - { 'content': None, - 'function_call': { 'arguments': '{\n' - '"first_name": "Bill",\n' - '"last_name": "BoBaggins"\n' - '}', - 'name': 'log_name'}, - 'role': 'assistant'} + call `message_user`: { + "message": "You have a total of 909GB on your root directory. Currently, about 790GB is used, leaving you with approximately 82GB remaining. This means you've used about 91% of your total space." + } - { 'content': '{"first_name": "Bill", "last_name": "BoBaggins"}', - 'name': 'log_name', - 'role': 'function'} + `message_user` result: "thanks, that's all" + + call `select_next_func`: { + "thought_process": "The user has indicated that they are finished and needs no further assistance. Therefore, the 'End' function should be the next one to use.", + "next_function": "End" + } + + `select_next_func` result: "End" + + + +### Example 3: A dynamic action graph to carry out validation before making actions accessible + + +```python +@ma.action +def get_task_instructions(): + return "Get the users email address, and validate it." + + +class SendValidationEmailArg(BaseModel): + users_email_address: str = Field(description="The users email address") + + +@ma.action +def send_validation_email(arg: SendValidationEmailArg): + """Send the user a validation email.""" + if not arg.users_email_address.endswith(".com"): + return {"status": "error", "description": "Email address must end with `.com`"} + else: + return {"status": "success", "description": "Validation code sent"} + + +class CheckValidationCodeArg(BaseModel): + validation_code: str = Field(description="The validation code (6 digits)") + + +@ma.action +def check_validation_code(arg: CheckValidationCodeArg): + """Send the user a validation email.""" + if len(arg.validation_code) != 6: + return {"status": "error", "description": "Validation code must be 6 digits"} + elif arg.validation_code == "123456": + return {"status": "success", "description": "Validation code correct"} + else: + return {"status": "error", "description": "Validation code incorrect"} + + +def action_graph(current_action: callable, current_action_result: dict) -> list[callable]: + """Return the next action(s) to run, given the current action and its result.""" + if current_action == ma.Start: + return [get_task_instructions] + elif current_action == get_task_instructions: + return [message_user] + elif current_action == message_user: + return [message_user, send_validation_email, check_validation_code] + elif current_action == send_validation_email: + if current_action_result["status"] == "success": + return [message_user] + else: + return [message_user, send_validation_email] + elif current_action == check_validation_code: + if current_action_result["status"] == "success": + return [ma.End] + else: + return [message_user, check_validation_code] + else: + raise ValueError(f"Unknown action: {current_action}") + + +# We lose the ability to plot the graph when using a function to define the action graph. + +# Run the agent +display(Markdown("### Agent execution log")) +for messages in ma.run_agent(action_graph): + ma.bonus.pretty_print(messages[-1]) +``` + + +### Agent execution log + + + call `get_task_instructions`: + + `get_task_instructions` result: "Get the users email address, and validate it." + + call `message_user`: { + "message": "Could you please share your email address?" + } + + `message_user` result: "sure, it's bob@bill.io" + + call `select_next_func`: { + "thought_process": "Now that I have the users email, I can proceed to validate it. The best function to use for this is 'send_validation_email'.", + "next_function": "send_validation_email" + } + + `select_next_func` result: "send_validation_email" + + call `send_validation_email`: { + "users_email_address": "bob@bill.io" + } + + `send_validation_email` result: {"status": "error", "description": "Email address must end with `.com`"} + + call `select_next_func`: { + "thought_process": "The validation email could not be sent because the users email address does not end with '.com'. I will ask the user to provide a valid email address", + "next_function": "message_user" + } + + `select_next_func` result: "message_user" + + call `message_user`: { + "message": "I'm sorry, but your email address must end with '.com'. Could you please provide a valid email address?" + } + + `message_user` result: "Ah, no prob, it's bob@bill.com" + + call `select_next_func`: { + "thought_process": "Now that I have received a correct email ending with '.com', I should now send the validation email. The best function to use is 'send_validation_email'.", + "next_function": "send_validation_email" + } + + `select_next_func` result: "send_validation_email" + + call `send_validation_email`: { + "users_email_address": "bob@bill.com" + } + + `send_validation_email` result: {"status": "success", "description": "Validation code sent"} + + call `message_user`: { + "message": "A validation code has been sent to your email. Could you please provide the code to confirm your email address?" + } + + `message_user` result: "uh, I think 1234" + + call `select_next_func`: { + "thought_process": "The user has provided a validation code. I should now check if the code is correct using the 'check_validation_code' function.", + "next_function": "check_validation_code" + } + + `select_next_func` result: "check_validation_code" + + call `check_validation_code`: { + "validation_code": "1234" + } + + `check_validation_code` result: {"status": "error", "description": "Validation code must be 6 digits"} + + call `select_next_func`: { + "thought_process": "The validation code provided by the user is invalid as it is not 6 digits. I should ask the user again to provide the correct validation code", + "next_function": "message_user" + } + + `select_next_func` result: "message_user" + + call `message_user`: { + "message": "I'm sorry, but the validation code should be 6 digits. Could you please check your email and provide the correct validation code?" + } + + `message_user` result: "123456" + + call `select_next_func`: { + "thought_process": "The user has provided a 6 digits validation code. I will now validate it using the 'check_validation_code' function.", + "next_function": "check_validation_code" + } + + `select_next_func` result: "check_validation_code" + + call `check_validation_code`: { + "validation_code": "123456" + } + + `check_validation_code` result: {"status": "success", "description": "Validation code correct"} - Retrieved user_name: {'first_name': 'Bill', 'last_name': 'BoBaggins'} ### Notes: -Prompting has a big impact on the performance of the agent. The `llm_func` function names, Pydantic models and docstrings can all be considered part of the prompt. +- Prompting has a big impact on the performance of the agent. Action function names, Pydantic models and docstrings can all be considered part of the prompting strategy. +- The current preferred way to deal with exceptions due to the model not providing correct function args is to modify the prompts / action graph, to reduce the error rate. +- "gpt-4" is used by default, and performs better than "gpt-3.5-turbo", (at least with the current set up and prompts). + + +### Contributing +- For any ideas/comments/suggestions, create a GitHub issue, or comment in a relevant issue. +- For the development of the framework itself, the aspiration is take an "example driven" development approach. + I.e. find compelling examples where a feature / change would be helpful before adding it. ### Dev setup diff --git a/README_files/README_3_0.jpg b/README_files/README_3_0.jpg deleted file mode 100644 index 468842e3f7c8b0079802bf9c4f4cb3d0b14a4c48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11205 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 literal 0 HcmV?d00001 diff --git a/README_files/README_6_0.jpg b/README_files/README_6_0.jpg deleted file mode 100644 index 468842e3f7c8b0079802bf9c4f4cb3d0b14a4c48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11205 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 V?pyvRhVuXL4BCHf4j4K6-vAUW1>pby literal 0 HcmV?d00001 diff --git a/README_files/README_7_1.png b/README_files/README_7_1.png new file mode 100644 index 0000000000000000000000000000000000000000..aa47d0b5126c4dcdc5258d1d1487b1bbc25b2ea3 GIT binary patch literal 19343 zcmdp-Wm8;Tu(n}v4KlbxAP@-d7Tn$4-CYvgJ-EB(!QI{6-95qG3A~&0{)hA7P(@KS zHM94ycCTLD*L`=mf}A)CB0eGn1O$qtgoqLZ1f(ADO9l@DUTNmj*MxwOR+AJFRPlg3 z+i>?|6>VU5Xon}~qEUo~6m**8l9mI-JIlov_~qvM4O51jj0nb?KuUt*lcGo^YlA*r zk~_p*JaoF!JZAwvM`tTpS$zM7WL#Z~_*L)b|HCZpx_751_dO0wy?r0c%v`6uub|Knxfr>>hx9KtR~|`S$AeYKWw3H-fgZ?Y#BnqRThkvRKn;gc3)? z0FuQshU@UR$*;>Q-+w2-FKv0tMdyoM*FU%MuxoWq)1-z)E$hQH^Ya47D^BX^Pl(X0 ziDaA73>_|un&!F=JuvT&L*Lc3EQ=P@^}W+flb;rwI#r9y)vntwdy$$pf1G^0J}1i| z%X3ywVAs5Zt!=}!prYKMBvy!m$SMv=5Ye1o*zNmvlVe@g;WmJ!PUt+&)YEg2qHm{a z&urf?a;i_?vX5~Zm|Wg^TpF9ddRu5|m(C#?}8%RrDTH5!$?Ygl|iDzQz+ z-QpNS7w_V~qmrCW%aSZ^G@Sb6MdPLox1YdjCF}h-DoWO}8X|Is#(w%njs~U{^xww+ zLAeS%t2!W}=J?||<@oOAJBm-L;ikU7zaD1TzdfH+<@mlm_3*oB{6Q`{gkDnUYVvtm z+cLs-+w?TY=p+k5(i_YUzz$@Eh7(lLe7_@Sr=I=-BZRS*0Q&y#Nd8NvLvMzCXUm@g zGO%!3Z}oBWUJQYdZA`HnDon3JD>kp>i|^Bzeyw$;ZR3hn4$6NoN$PrdFV*W#V^{Ob zR%I0-Dr*Hbf%5*bY*Djtr}(~)eI%W0cl!zQc33!%vei0N@sF!ZonkgEXN_yjR)cuO`L;64_`LVa#`=r%KYqKA zMKW}9x*is1*gfB`**EFg?&Ed>+v&0P;$hXAfnJ8dwtgl>Q8tK^^YVugr0c_myRzHv z4zQ3q_#z%NBIo`Rpo`yMQF$-B{eROP|HVcOD&Kkk5r1-9l%hAPTe^^4wAxP>tMIzy z8{Ms`7A}H?{iHT#Y5i;*!(}$f^=IAp{a@F=GyA57eCl>^)S8NKra+mfofZW1jF zQ+x}w+p=M3_?K+XjeEM!=9W~9dr>U4tIremK65g=5}06ILH|j`WMn((&XwMq0GpXt22N7M=d02W(%!HB1PcgXGOL03 zc%b`SV$bX_BZ^~4m@Y#&5&(ikp%{c%6O63m(liqAx*qugY@>VlL?T;O3^=h-FZzgs zTdp#7ZQBOmanPGfPIn9N48h-ON-#NYFftXodjacv!XG`HD}VxHB+O(ab))a{hH99hd`R;CD6Y2PRc|YUtz|(Lvv$$(pqD_o!sg)nTk@1KH9gg5vozQr>PwAJ zGUiGs0t^UW%%$FvcSZ#Bu4L@L&y#!)I$xSslByNV@ErTm#c$PejVYM$<$@_%>f>{q zC%Le1()-UY_pW2)ip&0^wgYw<4catg0G$S`7>X;xywd0Y{3-Od12A(4SJXHd(kPJz zQuKYE8rSSF2M2&KHuE{5s$`FM*~{2^HES>xv)qvK_X|PiJ3(fP5}qn*OVj)QH#ZO! zNaRMNRX%rRZg>eY%#sl@QWKfolGUgol$tKh1qsFjC3SzC16uvlg^r6Y3I9_XO{6Kw ztaYsQzz0hJ(TOVvH@CdGX{B$cLFoH>#hZIYBognHPXu*gYx41cf4l_Hn4KEnh5=|yneI)p65ds?EKKd9-hdC?El90ckNZcQ= zmORLmAEniU9%SA6GxxHBL?Bt`fzOH{{?fbm|zmPR?gjF zx?0hx#{g+H!8}y{j=pi(C~I}xLS0-LxbgGG-c2LF&zaMy5W-9Tb1o)$PdAM6Uw=8T;2t2Oy$SK$ID>KJ%QhuAJ%hu5yH zKYig`D6~IXB&(m|e<{DvX|^%k1~tivOVrGply?8BE1D&DRLG?SVys@IQY?Jr-GbFC zZBRyTt26+W-|WCiB`UCRHnGoHb{lSt4+K50Aml{* z&E&}q7j$&A7TQz*XyIc?xV5mQp10orZLxODT@cu}8?L^%z6o(Nu2!*5x=WasaZWGP zR3^x><2^c?bj}FbRvM^QAEGo z3e}TQPdfs;{BJXTI4ledw?D8DS<^@}A-MuWA9vns{frL=|Ahvw;v&Q#LJ$BV5(kn@ z%t^e3f+P=Ula6(M@kgE2&%rSS|P*w8<0ny zs^0N?8lLFSIl7)EQuKa|xYy8DFd`x$CYA`;P>;@4E@Ya$4HDR&;=F7>KXkokfsELJ zTC6)*?FjE^?IXQ6#y2%yMzgQ}t()R63m@ROY2FE8i*qCZ!szeTfqR$5?^Ct2BWI9iF2g^jmyPyJ( z9yX7m5h!^-3ikn@V7NROMbnQ<_lM*&AU5ngom6%41GTTB%(86z^Kn@e{;Gl`7!Ujx z2PVM!FV54bDUg^(aDY?R6Gz*oraRb)HA?5KSO8b5DXw>%@H^0pv|^RF9HzT(c|T}b zSKI;h>!K{<+vDCI%`tz(aBeq*0x3I~BJB0;<>I|7E+uWet>VK1L|p?J zbMVvmR|V`Ypv0kB`vcT6Q?^p1qzocxzb}n6_<-((Cf0`hNk@irC#d|(5Uz2Q;}DU0 zd&zE|q!c4?q64+G#(X}e45Pn?rOs@#Yf=7J8$Iws68Nu{1!E{sE1mhqv;zz9?cMH) zK>#9w3({YIZnMg^Ym;qjoG{_*1ZBc#1p?I|MH5gS#dwe37=-aW@?$ zB~3XB-^{XszIHZK0nM1?mn@ffNvfJ5bO}TIjXFtm7D8%9mAoDm&%J1lx2s>i^RMae zKuu3YD#0@0@IJ`VY1mxifBp~|&gkEwh}=em`Y$WD8a{?AGfo8}l}aMOG%O^<=Szl7 zEpWuQ9^!ye;aXAp+)phms#7tyyB>EV=_3EN>iIk!K*Jq&RWuuN4}g^&%w@XlbVW(S z&VC#*MPt$gz`(m5#VqOl{PwSPK`{J~bWeNhd9kUh88;BKfvRhE79}hyP}*P4AJ*$) z>+PPS5}N)#01qCSpJgl77-!!d=-3=j0$?6DQ=bAkf8e?Zg$9AU1C(3zq)E>g8)*Y! zXsa%K9I>MN;cBiFU30*=gcgiZ#6YP61S+?ozy7J8H;*`lSihJO2&7@ag_zthrFU8n z-S{)LE$E^t%6YY&wlY< z4|h;h0Xu)n_x)vy>7AQj7uNq=9^-BDi$Z;M1vpo@=eyRo*>p02Al^P?7CO#rEsvex z=rk8~s+EF@dO87AK3qYo z28J48I86mQ>*yj;Y)zAJ%H&+9<1|X6RbcxH2HFL}_~Lr1D`f(idh@C0+t zloJuLmyen#nnA@cPknS!^88PqQ#E|{gY)1A>nZ~CkXa4Td>-1ixKN&Ac+N?AWON&V zrYV?5K)dZL;*i3+*Uf|_jgf2Xe!O%ot2+$sOiaxTh_yPp<$9Esq4RN9+rGoK^XX9A zeZy5<$8kW@EUk!WB7!*(6ip*g?G41`N58nN8aW5FfL(+5){I#8nY7uB0+77gIXEkj z33}{x%Pj@J_|uh2r|9BH6}N#-zP3279)&9aIVl;_iNtkY(RqrG`jd?$8EFA}XlfF^~c8mO_ zFsUky*wK`$nsxvZ6PIi^o=AW1auB8*r@x7#$1HV6Ls2+xxhkT7D!(pteT5m}?X7U- zLCgZrL83CQ(KBkQV_P=}DOLxS8!yxj>R14Zp@Zb#U-ABkvWiB0grYtaXkej1iVNkCghQk>Qo0ZQtyncxrYA~J*AWmYcpoG)I(l;@p64JHj7 zP58>%fs0~MdJdc#HVp|B*}x*6!bHneh`py&atVRTP8XjQ~olI4~@>_vM4w8KJXgLwPZ zeW+tcCyUxm|5~X1&ziRQpF;lJGmA{BoQPc;fElGQ@^Xkav`|q(tN4V#a*h#P1Krj2ZdLnrguGdidf7-4 zzVh64;)5R)ssa`FI6yXRu;&tKACz22R6ReBP8YOr2CC|LxZ`jlvZf|%y=*>k3ZgFa zB$hT>;pNHZL=?oOVxzrzF>lkS$ zaS8`w<}j9d_TrqBA}q;NyR;o7YZsdk<~$(@h&7=cSFF9MB+9zh45lZtO?O)cC;gDC zfXGqoR?t=w&-@B`SfXdj59`5*CSNNn8}6G(|;Y$02h>{uRbcySk>Hre1pSZT9bJ?GN5Eg z!q_Wvt`wOf556hjhjP8ysGLmJI??nmdj1GS&m` zbLgX}kZUPoI6>bT8~{pyJy!2>vrT>ypjOFuzZ|$8!_HJBDFX+SB;Gr)qD?2#OjUGS z9NS{6?N8GTtj=GGNKFe`;Lza|&RGkiqNEXE@@FK*iux!OB}3G2>ep@U?-uL3dNKmE z*61sGiDK!Nsj;-3#+imZOz}znjYUPXg4;~=r)8e5<9msL+<*NwZ65nqOCKr^c&v)x z{H*Ahs**RJ&>TS7dWOMRr)abc?PEyghD#MW0AwcKQ%(ksf>jsk_o2$3+kzG^AkD2L*q)g-3Y(1!b zybo_Jokfa@QkEN?R-PCUV8Y7ivX{(!*5Cx!H|F>qCf{MoOUWzxr{rK!`@7s5U^ zDKbAc${VE8Zfv|De3r23%_8XZ2@4p$G!?(q@}f$buHpkW&olJ;?<;Iz?b&1L1NJ5w zzX_)LRDc7~}%g~~KE zh*1F@Zz*xv;DXSjR$EiwXinUll{S%sy z=LnWK2|bRu{gGA)Jq4*P*$v6Ua_@vwWY|jVUX_g#thm3V!C$=oIWb*JBK!sIUEW>dRAd+Lh^^ zDKWBhAl7~NTtOGu%sMh#rZ9xRJabpE?d!YFN@-H)zmq{~RBO<}lLI)(;PKzijGvXC zaa|YUxs%jj@gvSW-k$y_+e0Fi^N1RAE3RHJK6Tr~;iFe3gA!|xhM!!1v-&2Rrs%R) z2(Mq3RnSV6SA26OH}p4!(tfOdha7feadJ@y@(A+KRKp^53L3DCri7KwYDD zQjC6DG}N+ZEb$sK8k;6K`ueL18Q)54kce}q0~HqUHdlq1mFNeDE^co+og>dWwckX) zD?ahjiR!rFM(v@AP%<`F7xGjHfhPC9_ZkceTb1}w-pdWCL9amUNa<4gc8aYpBBO2j z2yTvULA4fOB z1q?I+2cnq_JdHr9Jw<4z-Kr8i7--Jz^rvvUa2Ek~NWsIUT!-;@GQQ9F96s>E z!h1N<^0I+JU<&n4bPvVMPh6SBD5#?J^d-~KVwaeIR*^Z^owf$O;T`7K&1VhG=W8V4 z;~_*jX)O4XikI6ni*PkLY+42pu}D^0d7_n&2lTiTMx|TWe$L@3(?q(XQ$!;*T~s5; z4|uRO9m!~=f3HgDuKB`DrAGTt$bZ++Yv5kQBUK@HEHAG}k2OoDIHARyK1~s1Hz6}O zRiSU_+%R>NjycatIyIaA7$nXXXU8(S4_#c}z-=rW@FkVfe}oRAcOSzKl#9~c698>h z({wDk6L$20(K-~1JFc*u|7>aQ^kI~D1jWpbDdnn{oYumKb><2v8np{6tkd`pRAP+Rm*An0^2cM%sfuIcm;Bt0<2RuS^1rC6_{LU;?H2B~9 z(w7UP(ovwF1%4NE9}?%0<#X>Dmb%W4bs~+~f~xvm_~_=fn3sZq9}U`8J0WKP0GR$L z%W)m>T(sXTRK*CGkR0A0SwZFu-U~hhc!{P{^1DM8I@_J7|FXiZXx?#l#uI=KYxGY8 z$JFbT1;zm>3cp!_sHmV25qK;LkmwlWcm+@WcoJ8 zuJuR{=ueF*0Ht-0Wr|lhD%?TAV44OHlLXv*0EJT#%zc&LHXI6xbD2lV0{q@QC|`X4pbDV3i0ovIj$ z?eEVK$cVR*pdPl{NUsBCpr_qO`c@y?9{H_NV_S#|U#!winH0HbO?C(Upw9huJMdEk zTLqvHf8eE0K9_;O-zMDe02HFs9x*Zr3IsvP11rcXeFM~3TVmTe@2`K}2|POhiu5LH z(?#P@F>-cBqn&VA=`7T>T-#*aHoyaK-v5sV+s;TL7m4 zvylJ$Jmjs~`dZ0a@Ba2-<7)us9VPC_5m{g6sOYNHdL6`O3aKHlrSmm`6CuAH;K<< z9;_DtjYJtoe?!N!s*?Q|%-G8>stg0q)(NPA@_g4A%H@>3g8r~z+?Z*D0N^!WW7+Mc zJD_VMB5i-Y9y`o#So-|DIBZ=zLdB9&oN9#FML%crdNpJ(Fe>N^O@oo?9}e)Hi4Qcr z)1jkx3ABK)lAGg5O1YDaeaPjEx*!PCX#x_IzpRKZ-&r+WNq(Nzh>PpV^Ik!VFv`nZ zvvRz)%HWor0Przk@kZ{XP--=hV`n{0Aa+GnNUSS}RF4GxqmN$^*Q=Xg*G4H(mW0ZW z%@g&f4}z6G09x{g54H;Q{zwYj)!kUD4(2*0x4rA6TVTAJ|nr{$oN*J3{`G zM{j0YLP7p8&ZMeFwABbv%pamUWwHjyJWe7@q)>{2l5&&`+Z91L?ep{l3%V-6eQN(+0~M9(&OtN5J;5=fuw%qpKSSdLQekV$1~C?0~*fG3?An z%WYihEzdt$_vmHiwW9!8Z2+D(2gTScKpnGv-#jyL2s%Q=^z3?|b2Y90D{?sy!IWh! z|GP56z{8sBe|!#vY7jgCO`sO%HFUi17MS1-iCDvoBc&GZ&yLHg78&|6x8C0#?#A$~ z%j5P@W!c9aJyJWr$FyeyN;7T^fCI__Ak!noD%pKcdE#QxJ(>-87;to5X52e(h_HiT zw}8HMmETEZ7!+SmDV z&U4j|$Y|ASTK98n1JP%~=rAfCymeTE0DDa=s(+S5Q{3T5y7UL?9HnCFK=sy7wR zrb0kUJe^_zOLGXo=kGvXv;p=>P&%bl7~Q`-0dv8?XxcUk<^gH?mY~n=-x;ty;j%zK zE`i{yhJ;4zA3RtUZV;Tc{15&cfOs7!*1@$V2 zIi3TVaB|ZO07Obejv;GCWPloc0o+vY55;8#se$=>N{*9&k+9%IOkPpFc}y1Z9ro0g zBcR*wuKs4_hgbOl03?84DV`BCQL_pw@!(k$vZACb(tQqg2iR3&mg?_Rz<|TL@nJcl z5w!nyTi+DHd<5MH0C*EWkzc8Z<@-1=;IXbU9ZOmO%m6Zh9=-j(RX8XR8AVQD?dL^@ zwv{7G_*|TnS@8zVl(aLJvQ?ST_>miMA{o+(MrKDa|K#HXTUSG|2r7n=hW8PQmh=@Nwa1gh!tb+0E=zhS`V4_mKY_-J9fH?MLyOK3ZYQ_ zLw3GFoZea4C(MWQ3`9=YD`W9dx-ui z7);Z)wO^NN_hKV9x!{#+H<|*wv}i5ICHh4SN;o z3<*vmMzf*##WOK{SVCrQr!_;&?JfWA*Al9}pr?zw?wxT0D2@xRa;AooUP)eu@}e~HxJ8fkc1UBYDs+wU)utreiL|4ZS&kU;T(b>{){J>Q zjCQ6sk5Lu$f4x8{VJ=9; z%X2rcJV(4)(0T>^QcN4u*9eL<1fiuKz>@s!nz2&}-mIt2LSLfoKR_?C2>qn)KJl)_-) z(Gw1P=Qej+h-anZtr;g*+}3Ql;!BPlFrBhoW+hMYw1dLUuEn5It&4-V|&r!r;2_NEL_XhPat?6R2?Q3xZcRTJM06klV#3P zt&2!`I&}~fn%0!fceTpc;RRk8ZV*0eiuW3zBMvQ2k4{&N1w4??kS@81q}vVv0jT*S zn{}%vFgsqd2&F*X%wFnEDW7jTfv=2@NO>4zg+iJO(FKvUaFIuHK24wfRh-1To=o*T zsg*%Jp7i$h?2?1BjSt9YBc?c-Cg?iCEsOXVuWU60`{f5xVLAuY)0)$RhXJs} zaEgiW$v^vs*)GA`IRWCiRg|`r*K|t|5QWsJ;VIn7%N7ry$gL_tbK_uDbRxk=Y16!tCLObU#l`7G}* z_g!(-ub}dy-ghj?EXbmk-II7)N@$Bo70iQxLdkh`n~X?9#+QH|R#^6%m28?Pex}ho zo~)2Je|L;_)^V_m97!&X>35}>KAMijM*)TBMiJnYea_*9z+}u{xKdAnM%N4gp+V|F z!6X}u5Cp@fYeL_kWmCvvOJs^9b`wHTZ-Ytsp)?x{f3SCEt$apVOR=#K5Wf0{BJ~CJ z(!va6gp0D1me}gL=V-EIhj1-yCv#H!4vYu%7kYy*m9Pb7=S?6DYZ1NC4jo$UZ4mnS^*3uTA5IYj zgC9SW8Rw0|U@?p@62BCa$0P)S+)Q-Xi9SP8$ARpV(DIlPs-VsFoE}7kWkmSD1)vD= z?;HJ2pk4h1>I32bxUbJ*G{jB4Jc)&=8!-KD!&Gk;fPm{S%r1$0(*nI->;NalWbt;o zuUkx8A_mzNBZ_vZq&IPxbB2~o{Mw=sX#n{b-xAMz*MCe|9^($r7|tCK*OZl(EpjvR ze>1*}VqioTAl!kyew}y zoGPKdC1lfotw3>7>Ub((1+;3U_2ydxJH1|eE(SeK%w?-$G~o|HFcDoxO&ANPKli+7vFy*E3b&+8==Pie~*eYBm_JvKxjs<>Xb!#R*BBdEeBi8r4G_ah zaNj+nwG)gSFr@-<8<2Z&*TDH}>J<*elRqgG2@K<}ZaTXm)?Fd{UI6d>IcHAf~nVu3Rn6ETkh)1j+UY9ezUbTS18c2Ezf0n(p&j-a4 z>uKzuC>%n2gj27J3<9>bYM|$nCvD;d=?Z_idE6=6V3U!e%dS+B%wV{p`NH-}Ovjms zunhFB0!dyWGOr?sRxh)+GkbrO4(b&YtP)0BCILXCLaF@V_7tCBjb>S#^Njw2$_YjX z%c+?KYI3Ywgfo0Bw8cwiJx>NGnRTB##mw0`wSXfAgr8obI+Exu1hqAtjSQkPa%f1M z==w%ROBE2r?2R}6z?L~6V0JW+!SGqh4Ig;>>Lvy7=1JZf{dVU02phK}CkW@|U&? zKVgyMXx??AF?Dj!4wK{%7vSKlYp1F^tz9Dgx-sL$1NHycp}ji8-SCv(Vpv5wKFq{( z!f}iS!^hM402=)-^~cWY;CbZXXmtPD*qw%SN)^eZi+wMse6C&ol`Ay>!6>X>1Y(;0 z=Um24Y zfc&=_&`oOVOs)>bPa56jRi=@EhxCI?#&zbKBZ@77Hwn{VJkBc$ZSISaW?74BQaENuSg!JDr7_&wLs`0g#F_U>GGYy)})c z_udfnsrc9l5Z)#?YBB(^TOm0bw>C=znt8%KYm&o=gyQGVTH(-;D3oD~TFHJPi+>k% ztT6D-OJ7%x7-l7vk!$v1!w$oiR7(9qiihWOM84TlKi_Vj%S*R1S0yyhNh*7B%X1xo z^^uD%Oh{&u7SPo`m^*e?yN!_;_ama(M7Y{HQJf37Cn$HZBh%^=|OrTopV#nMGSGDj5j;AA_( z&+P5qNqc~;+9{aYP~)SOEQpVE&B5_x$xEt8+Tf`rP0TUo=6Py0B*!zBmlPykh_kb6 zce8P_G&ZD@yXu1_^cTsl3{{V)TTN7t8mqkeo;?MG+gi8ss(^_VvOAH=Zn>V`B>NNC z0PEk<$x`Ir)YL?xDdk`PV|RfYgMaCi8oj6$+Ff2|ccaEy)p4Fk@`QT@Wx-%(ZI;T= z^#NEKO6`X<=9Q|VpEWEGeCRGu`_E9Ix(3{b0s6GF?74Xo(Z_u;TJD)HS}B(`(Z7VC z2{rVK6DEFIscr2Vs^#0m14MVB>v^H-(SlQy3blRm9|=K(OhhB}hXICLS`<^Jvh(>k z`oseeY^i{LpgVX_rS#s?38JLE8A!EBnV;HG1H=46ti-i!Ew{VPYX>m6%j;*Q8h(j) zz@gOJ6$}n+Kq!(Dc|=!Gd^JlG!{~7zQ=f$U{XAJlm7CY$-5a|j{{308HtC*ecrXcu zm9=;Zg7*%E2~H0RUK<%>6fnl6#~lD}}F+tPiY59e9rU;E&>1AFjRY!k@waEDLaUEH{}4;Xp|w$E#x>^i@imPyco48gRnRYT0tn z5T&b|C1ztYZaH=Hf`x_U%Kvi6&v<1J7vW{R904l{`j~)%iz__k;)f%R86ev_guu;z zVdeCT8&21*4o;p^>U$yo0|b!Pm-DtXo8th0 z1umeo(M#D8^~B9{0!ov437ce2Cor*uz?1ELuUUYU3%HY55xEAv}styVWfFD0iRJoiGPm^+SB34%fKrf_ z@Rv}EY-9ZZB%iU!!u`2a)H<#E*}^(Tg@i4@0Ou!W0Bngr?6trbPN8?E77SFB=HIWC zmWSkVqc)H+uB-U@yIg&`my>lx0Rrkvj7XBFlN7UGYB6Idz*^;1*XRV z+g5RzZDSZoJ_E$(E!LWh_8PPSBX=`}=WT1WoSDafM}GgRVZ|)N zvCnu!iNn;FTyx^-_Ye$@j$e-^P%z?I3*@MA^`HjBQu~E*76CRLa=;q9ht-x1m%kI% zxyY(vP_1?HEIcv1h75D655d%lY=V|f51J*`ja^qzt$Yg&el)zC?$hfh3WiD2dxA_|1W$PY{de-Xag2aChH%p->qQ= zHnpTFjGT2M*5Bg_@3%LZx;|#l{zJy^n)l-qtq0}~F*JZ_ri1Ag!4BIX*g{Ac=tiF& zNTax(W1lXUYRQ|WT+=HHP6a=m4d{Ya7KzvNdxPGT(_|ed$*zehTWOBeZF1N5;mH3m zLbeQy@7hmm$5xj=rBX*=;EQ?C@WSX+i`aMlyBGBz%XO#K;wlF3IRGsFhP(oC z^|yd(5_k9gO!5lEd5h_n^g1>JwtN_LKK_*Z z8XYoM=aohSXNi?#{M5AK2_Ta2wWzUg&V33wb5BZ=J@Mgiu7^gcS#yC;V?~_BoaJbO3 zU3F|IVcvcP5!%*c^wkosB>Y|CFDuTf5QN%`1{#MTFGm2O7YUToSPyUDV?Gy)klfxE z+cbf3r~UXx#HwYdWB4VaxC$=`xuK zOewI`PwX&s9S0$%T(wYaw0u9XGiCxDYi?dwd`8EG!r$>UrJ=?n#RdJ{l>Ih)RO%}j z%m4e*g4Li!5q$_uG}|TgVvhKRBY%WL$%;}lueX6_URU>y$iAoP5>|MNgZFfwE})#qRw%A zmL9k1XG2#s+&MZP?R%C)z7~P)za|{#O}Nw$Qybs-q;!PKCSsRLlSEoheHlj@ZT{;f zoYF|e$6cc8JoFBa+n=p!Y;N?>3tGrpQxTF5H$6J5upa$S+5{D*bnwp&QgO(%3R%s*}_}^6`ahoFr zCaXg8Gb1Dq`@AG5F)^|$Ju<1Xck08U+rv%47XVHVf~4?DqGAbRn_yH;w}f>YzYeZb zO|iwV@cGI(a1(J=n|>^!yi0Chg7P2vgh9fW!DbT=xNX9i)8w^J!TQPFO^F`;JP+54 zZ1(EHCO$rTi{vsI5*Mkcy@ZSw0+<^p|Fv+H@LS#Cc;2a(wK)9{mrmB^w9f5e?h~WM zp=D$=;jqC?#gdhH4uSnja_%D6Lc`uvu_F>$(X`pBA<&M7B6sR~D}&W0cT6adb7LrZ<(5)v|@&2~NVBHyD!U z2`=2GhpA0Aquxk-PFvejg6JCT%&5_qZc#Ft_EQ)rqi5Y0T;Q)&ZsQgu?-JUog*_}A z?>&l$s37rkFowElMPckwYWLYK82MCUSDP-8=@sJ$o%^5N%or1wK`Xb!(6@?aEVK82 z4;-o8L3emBu?YzKW*Q?c$n16@niChlJ@V;TC6k|aB^#YasE9YC;e297?7FPb)9hF7 zn`NU^Tl%@weNG4ywkjn|GC{k`Rfi^`rH8|AaCBd^gzgmb$*C3+(^H2qJZpYlhQXMJ zx7V6f&eiv`&3`k27`7wOBOLJ-hQdxM)5(!zaNR2T9!EJoDO;*jFraV$VG+*fq{!M# zQq#TuX2z}~;$40w{54dZNcs{OV&9;F0_5umVb_QQoD4GiJu_U@>vaDCjG&W2 zYrP2haA#2~bY*94W&4S_^tH_99_=NSV|L86uFSPvZEzpW9>lBFFK>(8Jgo}UQ~dqM zV>K2j1|0eYaz%1`S#0{xqPJ7>&c5N6m@Af65yQ(Itlcmc>8~SB!f^btVqKJcs=Ad( zC+s{|BS&!uS`c2P!^}dIW^lB~lT1E7%FGtM7ep4d-IR^>hjkafwLNm;j-U~(b!A6e zuhN$mF=_F;TxF3Wn=E_iIz=60v6LIx#_iy4gq(Epu{$gwsohfpuC_19)*>+O?!QPk zm@IjV3S1E2Gfrv6n#le0jP^N{v>=^dtZbkZxNyDU5gAvcR>Prq9B>8ifS zU}qj=V!U8{5xOEcgRR?8S8i2&5_-+1x8^~W&F@D5J;cQ}%oOJyvUvC&{$){j7%X&P zG(#>+N%07qk}BuUl*j>N?*ywP$@QEX6l}c~Fu80X<8KTPpXop?mf;bg6vMsS!AwSt z0;9aHHfGQ7z1L)?gNg~Y?FtN6FS|jW>%Z>eM9-_(cz+WQWbTZ8#Sn#_g;aOabj5lG)3d4zM-|^`n21kIZnl!vB79|tr<3`N}ll{ zCn?Sp^&`N5Rg)T=*lFP#(R5ltxuTG66mOi`)Q9a9HR6Wi^(ILL69XJw0Y@ zd0SCG{@j{&V6Tpiy~v^b1b4SKu3_O44WFlpy{>{PZ-H1zMvb-K_Q7c3KU3DEwoVbU z*sKi|X}m}%wmEjVc&C!$mk7=4<;tK_HNnAI+&SUWTL)${#Xby{Rs~xv4Z1BJ$PtX1k)ha;Xw!*@8=aN!UqjHYQPo* zS?t#kfrx-JqE%k9XTTC&6S;1dVH-Ak>X$CL#fbv7k{>Oz*H3f@-jiGsbJDR-HV@}pa`hNkC25W}zHS7?RSyEwjurd3CZHoDXl-66Mm^HviW;O5oT zo^J=Z8AG}{E=(-Vh%?x9{rJZ}X5vxqb%x1~lyPWX&j$1t8ghx~t{NuyHX6PTKo7*z zsoEoSa$OPVCJNNu5tz5UFbD$Jeb_j#Jw5BJv&v0o%mwkgkx5KQ*ruGU0BQ#r*{9Q^ zIo71}(E(Ygb;tzj(xEbF$fct@J7U5J-wx3kbbHi-h2BT0Sh7((#qpoLAoFD6p** zFbvox`4A&vwgJGDfbpWm+z27AGAbPXXgMJZAj4|1B?Oanhq5!;A&=nFBAgWM*n1aa zxh9LMDjArw%Z5;RACAja6Jm8qA1IF(cQq#pLaPpz$^$iervoZ??57O+Yl%N0@#WA zwZh?2A&cdjX}=MlEbAMW*4C#+I1Y#Ta1&E*U0_juk$wRy@i^`tWp~ego zY#c1Jeuaq@Fcf4}mS0%lVuT72UBOE^ciH$+tCzn!pv#ou1F=8mf_TMWuqJn{fH6|k~&BwScwg%x}b7kkxA13Cp7dbX9n2beIz47fDr z3_EyX3?-!S%ex_cQeD8fF%Wx}6ERg$z$E9yfs=O$IMhgwxbzN9zGXZkcwzu8>0Cb~ zzU;Eg(pJL4jwz^V4^^K8HIdzSi(*{P`0wEzd`inPAAqmjugN1ujWiLwX$XCZ`I@h> zbD^Q`JK!+$a1>y&ZfP<`;UZa-(P?=B7A{C(!AZY%R@R(gH|mnK7-{jx`MW?clDb7+ zb34&l44-90W^`or#w5dn#L5X+=Zi)44C4gtZ~3qTeLG6F2`LRpem)1dXRa=bv18(@ zO8|jU+F*kXC}jy;E5``3JUT&JIo-#n!KWc?)mx?H5QB!^Ue8domC8tgA}&Kb4Q{`- zua+!e;Yu)pakZw^{N~%v+_?7k`VHdEKzn|dL0JssTf_kbevymh47-j#=08G#R8Z@} zl=%g?;?tQr4Qg!sNKa8(@u_zvu$20R+bhUuoU6;8lDQDcpMU=OTzGYaC7l%3lFllz zoG>0$vDgleQ!I6_GVL9xNE~-wu0RCu5@_gi=-BV&l`ug@ah?>zq+dz{0Ibge*0Y37$ln*s!1W%Y1g5uqoe)Ot4jh zUn1d=QRTPv(oxf-cHCO=YGAY-wG4?eZH+DdE?@4$0;k2y{!8Q2U?ZBv&+1c94pQ-V zg!FKPw4OA#T2?opVMvUbXk<`epefMz0dND|qNpz@5ESSD3IsHCKqo{Kf&!gKfq;h2 i>yv0vP@n@S@c#jik=#ZY7KefW0000 .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 0000000..30fee9d --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/docs/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js new file mode 100644 index 0000000..7e4c114 --- /dev/null +++ b/docs/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/_static/file.png b/docs/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/docs/_static/language_data.js b/docs/_static/language_data.js new file mode 100644 index 0000000..250f566 --- /dev/null +++ b/docs/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/docs/_static/minus.png b/docs/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/docs/_static/plus.png b/docs/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css new file mode 100644 index 0000000..57c7df3 --- /dev/null +++ b/docs/_static/pygments.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000 } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_static/searchtools.js b/docs/_static/searchtools.js new file mode 100644 index 0000000..7918c3f --- /dev/null +++ b/docs/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/_static/sphinx_highlight.js b/docs/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/docs/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs/genindex.html b/docs/genindex.html new file mode 100644 index 0000000..b3eae06 --- /dev/null +++ b/docs/genindex.html @@ -0,0 +1,157 @@ + + + + + + + Index — MakeAgents documentation + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ A + | E + | M + | R + | S + +
+

A

+ + +
+ +

E

+ + +
+ +

M

+ + + +
    +
  • + make_agents + +
  • +
+ +

R

+ + +
+ +

S

+ + +
+ + + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..1510423 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,167 @@ + + + + + + + + MakeAgents 0.1.0 — MakeAgents documentation + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

MakeAgents 0.1.0

+
+
+
+
+class make_agents.End
+

Can be used to end the action graph.

+
+ +
+
+class make_agents.Start
+

Used to mark the start of the action graph.

+
+ +
+
+make_agents.action(func: callable) callable
+

A decorator to create action functions — functions to be used by the agent. +An action function must have at most one parameter, which must be annotated with a Pydantic model.

+

Note that the following should be considered part of the “prompt” for the agent:

+
    +
  • The name of the function

  • +
  • The Pydantic model, if the function has a parameter

  • +
  • The function’s docstring (don’t annotate the parameter in the docstring, use the Pydantic model for this)

  • +
+
+
Parameters:
+

func (callable) – The function to be decorated.

+
+
Returns:
+

The same function, with metadata attached.

+
+
Return type:
+

callable

+
+
Raises:
+

ValueError – If the function has more than one parameter, or if the parameter is not annotated with a Pydantic model.

+
+
+
+ +
+
+make_agents.run_agent(action_graph: dict | callable, messages_init: list[dict] | None = None, completion: callable | None = <function get_completion_func.<locals>.completion>, pre_llm_callback: callable | None = <function identity>) Iterator[list[dict[str, str]]]
+

Run an agent. This is a generator that yields the list of messages after each step. +Be mindful that the yielded messages are mutable, allowing them to be modified in place, +(make copies if you want to avoid this).

+
+
Parameters:
+
    +
  • action_graph (Union[dict[callable, list[callable]], callable]) – The graph of actions that the agent can take. Can either be a dictionary +or a callable. Use a callable to create a dynamic action graph. (See examples in the README)

  • +
  • messages_init (Optional[list[dict]], optional) – Optionally initialise the list of messages, e.g. to specify a custom system prompt. +If not provided, the default system prompt will be used.

  • +
  • completion (Optional[callable], optional) – The function that will be used to get completions from the LLM.

  • +
  • pre_llm_callback (Optional[callable], optional) – This function is called before any LLM calls. +It will be passed the list of messages, and can modify it in place. +Can be used for, e.g. reducing the list of messages to only the most recent ones, +or reducing the list by summarising, etc.

  • +
+
+
Yields:
+

Iterator[list[dict[str, str]]] – At each step, the list of messages is yielded, +i.e. the same list that was yielded in the previous step, with one more message appended.

+
+
+
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/objects.inv b/docs/objects.inv new file mode 100644 index 0000000..6f65544 --- /dev/null +++ b/docs/objects.inv @@ -0,0 +1,6 @@ +# Sphinx inventory version 2 +# Project: MakeAgents +# Version: 0.1.0 +# The remainder of this file is compressed using zlib. +xڍ0>%0B,Вr(t]{+(,4}Vղ +(T/VJP{hv428B:7,ɋ• 3XoȶCˮ rNfߔsFuJ2a2px,!P_bofwL䶊 y \ No newline at end of file diff --git a/docs/py-modindex.html b/docs/py-modindex.html new file mode 100644 index 0000000..74f23b3 --- /dev/null +++ b/docs/py-modindex.html @@ -0,0 +1,116 @@ + + + + + + + Python Module Index — MakeAgents documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Python Module Index

+ +
+ m +
+ + + + + + + +
 
+ m
+ make_agents +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/search.html b/docs/search.html new file mode 100644 index 0000000..dcd1110 --- /dev/null +++ b/docs/search.html @@ -0,0 +1,117 @@ + + + + + + + Search — MakeAgents documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/searchindex.js b/docs/searchindex.js new file mode 100644 index 0000000..457d8b9 --- /dev/null +++ b/docs/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["index"], "filenames": ["index.rst"], "titles": ["MakeAgents 0.1.0"], "terms": {"index": [], "modul": [], "search": [], "page": [], "bonu": [], "content": [], "i": 0, "e": 0, "essenti": [], "prioritis": [], "develop": [], "nice": [], "have": 0, "make_ag": 0, "action": 0, "func": 0, "A": 0, "decor": 0, "function": 0, "can": 0, "call": 0, "llm": 0, "_type_": [], "_description_": [], "valueerror": 0, "paramet": 0, "return": 0, "type": 0, "rais": 0, "callabl": 0, "mark": 0, "an": 0, "The": 0, "must": 0, "most": 0, "1": [], "annot": 0, "pydant": 0, "model": 0, "same": 0, "metadata": 0, "attach": 0, "If": 0, "ha": 0, "more": 0, "than": 0, "version": [], "distribution_nam": [], "str": 0, "get": 0, "string": [], "name": 0, "packag": [], "distribut": [], "queri": [], "defin": [], "kei": [], "creat": 0, "_action": [], "functions_": [], "us": 0, "agent": 0, "which": 0, "follow": 0, "should": 0, "consid": 0, "part": 0, "prompt": 0, "docstr": 0, "don": 0, "t": 0, "thi": 0, "": 0, "one": 0, "class": 0, "start": 0, "graph": 0, "run_ag": 0, "action_graph": 0, "dict": 0, "list": 0, "messages_init": 0, "none": 0, "complet": 0, "get_complet": [], "local": 0, "iter": 0, "run": 0, "gener": 0, "yield": 0, "messag": 0, "after": 0, "each": 0, "step": 0, "Be": 0, "mind": 0, "ar": 0, "mutabl": 0, "allow": 0, "them": 0, "modifi": 0, "place": 0, "make": 0, "copi": 0, "you": 0, "want": 0, "avoid": 0, "take": 0, "option": 0, "initialis": 0, "g": 0, "specifi": 0, "custom": 0, "system": 0, "provid": 0, "default": 0, "from": 0, "At": 0, "wa": 0, "previou": 0, "append": 0, "note": 0, "end": 0, "get_completion_func": 0, "pre_llm_callback": 0, "ident": 0, "union": 0, "either": 0, "dictionari": 0, "dynam": 0, "see": 0, "exampl": 0, "readm": 0, "befor": 0, "ani": 0, "It": 0, "pass": 0, "reduc": 0, "onli": 0, "recent": 0, "ones": 0, "summaris": 0, "etc": 0}, "objects": {"": [[0, 0, 0, "-", "make_agents"]], "make_agents": [[0, 1, 1, "", "End"], [0, 1, 1, "", "Start"], [0, 2, 1, "", "action"], [0, 2, 1, "", "run_agent"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:function"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "function", "Python function"]}, "titleterms": {"welcom": [], "makeag": 0, "": [], "document": [], "indic": [], "tabl": [], "paramet": [], "return": [], "rais": [], "version": [], "0": 0, "1": 0}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 60}, "alltitles": {"MakeAgents 0.1.0": [[0, "makeagents-version"]]}, "indexentries": {"end (class in make_agents)": [[0, "make_agents.End"]], "start (class in make_agents)": [[0, "make_agents.Start"]], "action() (in module make_agents)": [[0, "make_agents.action"]], "make_agents": [[0, "module-make_agents"]], "module": [[0, "module-make_agents"]], "run_agent() (in module make_agents)": [[0, "make_agents.run_agent"]]}}) \ No newline at end of file diff --git a/docs_creator/Makefile b/docs_creator/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs_creator/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs_creator/make.bat b/docs_creator/make.bat new file mode 100644 index 0000000..747ffb7 --- /dev/null +++ b/docs_creator/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs_creator/source/conf.py b/docs_creator/source/conf.py new file mode 100644 index 0000000..70d7ecd --- /dev/null +++ b/docs_creator/source/conf.py @@ -0,0 +1,41 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "MakeAgents" +copyright = "2023, Sidney Radcliffe" +author = "Sidney Radcliffe" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"] + +templates_path = ["_templates"] +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "alabaster" +html_static_path = ["_static"] + +import make_agents + +version = make_agents.__version__ # access this in index.rst using {version} +print("version", version) + +# Source: https://stackoverflow.com/a/69211912 +variables_to_export = [ + "version", +] +frozen_locals = dict(locals()) +rst_epilog = "\n".join( + map(lambda x: f".. |{x}| replace:: {frozen_locals[x]}", variables_to_export) +) +del frozen_locals diff --git a/docs_creator/source/index.rst b/docs_creator/source/index.rst new file mode 100644 index 0000000..ad9109c --- /dev/null +++ b/docs_creator/source/index.rst @@ -0,0 +1,32 @@ +.. MakeAgents documentation master file, created by + sphinx-quickstart on Sat Oct 7 11:08:49 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +MakeAgents |version| +====================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +.. automodule:: make_agents + :members: + :imported-members: + :exclude-members: version + +.. .. automodule:: make_agents.bonus +.. :members: + +.. .. automodule:: make_agents.gpt +.. :members: + +.. .. automodule:: make_agents.make_agents +.. :members: + +.. Indices and tables +.. ================== + +.. * :ref:`genindex` +.. * :ref:`modindex` +.. * :ref:`search` diff --git a/examples/email_verification_example.py b/examples/email_verification_example.py new file mode 100644 index 0000000..4657a7f --- /dev/null +++ b/examples/email_verification_example.py @@ -0,0 +1,77 @@ +from pydantic import BaseModel, Field + +import make_agents as ma + + +@ma.action +def get_task_instructions(): + return "Get the users email address, and validate it." + + +class MessageUserArg(BaseModel): + message: str = Field(description="Message to send user") + + +@ma.action +def message_user(arg: MessageUserArg): + """Send the user a message, and get their response.""" + response = "" + while response == "": + response = input(arg.message + "\n").strip() + return response + + +class SendValidationEmailArg(BaseModel): + users_email_address: str = Field(description="The users email address") + + +@ma.action +def send_validation_email(arg: SendValidationEmailArg): + """Send the user a validation email.""" + if not arg.users_email_address.endswith(".com"): + return {"status": "error", "description": "Email address must end with `.com`"} + else: + return {"status": "success", "description": "Validation code sent"} + + +class CheckValidationCodeArg(BaseModel): + validation_code: str = Field(description="The validation code (6 digits)") + + +@ma.action +def check_validation_code(arg: CheckValidationCodeArg): + """Send the user a validation email.""" + if len(arg.validation_code) != 6: + return {"status": "error", "description": "Validation code must be 6 digits"} + elif arg.validation_code == "123456": + return {"status": "success", "description": "Validation code correct"} + else: + return {"status": "error", "description": "Validation code incorrect"} + + +def action_graph(current_action: callable, current_action_result: dict) -> list[callable]: + """Return the next action(s) to run, given the current action and its result.""" + if current_action == ma.Start: + return [get_task_instructions] + elif current_action == get_task_instructions: + return [message_user] + elif current_action == message_user: + return [message_user, send_validation_email, check_validation_code] + elif current_action == send_validation_email: + if current_action_result["status"] == "success": + return [message_user] + else: + return [message_user, send_validation_email] + elif current_action == check_validation_code: + if current_action_result["status"] == "success": + return [ma.End] + else: + return [message_user, check_validation_code] + else: + raise ValueError(f"Unknown action: {current_action}") + + +# Run the agent +print("Starting agent...\n\n") +for messages in ma.run_agent(action_graph): + ma.bonus.pretty_print(messages[-1]) diff --git a/make_agents/__init__.py b/make_agents/__init__.py index bca8909..c80827f 100644 --- a/make_agents/__init__.py +++ b/make_agents/__init__.py @@ -1,6 +1,21 @@ +# Copyright 2023 Sidney Radcliffe + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from importlib_metadata import version __version__ = version(__package__) -# Expose the main api: -from make_agents.make_agents import Start, draw_graph, llm_func, run_agent # noqa: F401 +# Expose the objects that are part of the API +import make_agents.bonus as bonus # noqa: F401 +import make_agents.gpt as gpt # noqa: F401 +from make_agents.make_agents import End, Start, action, run_agent # noqa: F401 diff --git a/make_agents/bonus.py b/make_agents/bonus.py new file mode 100644 index 0000000..100cf85 --- /dev/null +++ b/make_agents/bonus.py @@ -0,0 +1,62 @@ +# Copyright 2023 Sidney Radcliffe + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Bonus content. +Not properly documented or tested, and dependencies not captured in Poetry. +Use at your own risk. +""" + + +def draw_graph(agent_graph: dict[callable, list[callable]]): + import io + import warnings + + try: + import graphviz + from PIL import Image + + dot = graphviz.Digraph(comment="graph", format="png", graph_attr={"dpi": "80"}) + for node in agent_graph: + dot.node(node.__name__, node.__name__) + for node, children in agent_graph.items(): + if isinstance(children, list): + for child in children: + dot.edge(node.__name__, child.__name__) + else: + dot.edge(node.__name__, children.__name__) + gvz_graph = dot.pipe(format="png") + image = Image.open(io.BytesIO(gvz_graph), mode="r", formats=["png"]).convert( + "RGB" + ) + return image + except ImportError: + warnings.warn( + "Could not import graphviz, so could not draw graph. (Note that make_agents does not install this package)." + ) + return None + + +def pretty_print(message: dict): + # Originally based on: https://github.com/openai/openai-cookbook/blob/f52ffdaca42073066f8f43f7d65a59dcc01c9349/examples/How_to_call_functions_with_chat_models.ipynb + if message["role"] == "system": + print(f"system message: {message['content']}\n") + elif message["role"] == "user": + print(f"user message: {message['content']}\n") + elif message["role"] == "assistant" and message.get("function_call"): + arguments = message["function_call"]["arguments"] + arguments = "" if arguments == "null" else arguments + print(f"call `{message['function_call']['name']}`: {arguments}\n") + elif message["role"] == "assistant" and not message.get("function_call"): + print(f"assistant message: {message['content']}\n") + elif message["role"] == "function": + print(f"`{message['name']}` result: {message['content']}\n") diff --git a/make_agents/gpt.py b/make_agents/gpt.py index 56230cb..11815ef 100644 --- a/make_agents/gpt.py +++ b/make_agents/gpt.py @@ -1,3 +1,16 @@ +# Copyright 2023 Sidney Radcliffe + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import openai from tenacity import ( retry, @@ -7,7 +20,22 @@ ) -def get_completion(model: str = "gpt-3.5-turbo", **kwargs) -> callable: +def get_completion_func(model: str = "gpt-4", **kwargs) -> callable: + """Returns a function for getting completions from OpenAI. + Can specify more parameters, e.g. temperature, etc. via kwargs, see: + https://platform.openai.com/docs/api-reference/introduction?lang=python + + Parameters + ---------- + model : str, optional + The chat model to use, by default "gpt-4". + + Returns + ------- + callable + A function that is used to get completions from OpenAI, to drive agents. + """ + @retry( retry=retry_if_exception_type( (openai.error.Timeout, openai.error.RateLimitError) diff --git a/make_agents/make_agents.py b/make_agents/make_agents.py index 8c513ea..abf73bf 100644 --- a/make_agents/make_agents.py +++ b/make_agents/make_agents.py @@ -1,88 +1,132 @@ +# Copyright 2023 Sidney Radcliffe + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import inspect -import io import json from copy import deepcopy from enum import Enum +from typing import Iterator, Optional, Union from pydantic import BaseModel, Field -from make_agents.gpt import get_completion +from make_agents.gpt import get_completion_func + +default_completion = get_completion_func() + +default_system_prompt = """You are a helpful assistant. You will be given tasks, via function calls. You will be given the ability to run different functions at different times. Please use them to complete the most recent task you have been given.""" + + +def action(func: callable) -> callable: + """A decorator to create *action functions* — functions to be used by the agent. + An action function must have *at most* one parameter, which must be annotated with a Pydantic model. + + Note that the following should be considered part of the "prompt" for the agent: + + - The name of the function + + - The Pydantic model, if the function has a parameter -default_completion = get_completion() + - The function's docstring (don't annotate the parameter in the docstring, use the Pydantic model for this) + Parameters + ---------- + func : callable + The function to be decorated. -def llm_func(func): - # Restrict `func` to have exactly 1 parameter, that must be annotated with a pydantic model, - # to keep the logic simple. - # We'll just attach metadata to the function, so it can still be used as normal. + Returns + ------- + callable + The same function, with metadata attached. + + Raises + ------ + ValueError + If the function has more than one parameter, or if the parameter is not annotated with a Pydantic model. + """ parameters = inspect.signature(func).parameters if len(parameters) == 0: - # A func with no parameters will be called without asking the LLM for args, - # but we still want the name and description to be available, and we will attach the response. func.description_for_llm = { "name": func.__name__, "description": func.__doc__, "parameters": None, } elif len(parameters) == 1: - pydantic_model = get_llm_func_pydantic_model(func) func.description_for_llm = { "name": func.__name__, "description": func.__doc__, - "parameters": pydantic_model.model_json_schema(), + "parameters": get_pydantic_model_from_action_func(func).model_json_schema(), } else: - raise ValueError(f"Function {func.__name__} must have exactly 1 parameter.") + raise ValueError(f"Function {func.__name__} must have at most one parameter.") return func -def get_llm_func_pydantic_model(func) -> BaseModel: +def get_pydantic_model_from_action_func(func: callable) -> BaseModel: (arg,) = inspect.signature(func).parameters.values() - if not getattr(arg.annotation, "model_json_schema", None): + try: + arg.annotation.model_json_schema + except AttributeError: raise ValueError( f"The parameter of {func.__name__} must be annotated with a pydantic model." ) return arg.annotation -def select_next_func_factory(functions: list[callable]): +def select_next_action_factory(options: list[callable]) -> callable: + names = [description(x)["name"] for x in options] + if len(names) != len(set(names)): + raise ValueError(f"Duplicate function names: {names}") + class SelectNextFuncArg(BaseModel): + thought_process: str = Field( + ..., + description="Describe your thought process for selecting the next function in a few words.", + ) next_function: Enum( "function_names", - {description(x)["name"]: description(x)["name"] for x in functions}, + {description(x)["name"]: description(x)["name"] for x in options}, ) = Field(..., description="Name of the function to call next") - def select_next_func(arg: SelectNextFuncArg) -> str: - return {"next_function": arg.next_function.value} + def select_next_func(arg: SelectNextFuncArg): + return arg.next_function.value select_next_func.__doc__ = ( - "Given the following functions, choose the one that will most help you achieve your goal: " - + ", ".join([json.dumps(description(x)) for x in functions]) + "Given the following functions, choose the one that will most help you achieve" + " your goal: " + ", ".join([json.dumps(description(x)) for x in options]) ) - return llm_func(select_next_func) + return action(select_next_func) -def description(llm_func: callable) -> dict: +def description(action_func: callable) -> dict: try: - return llm_func.description_for_llm + return action_func.description_for_llm except AttributeError: raise ValueError( - f"Missing metadata. Has function {llm_func.__name__} been decorated with `llm_func`?" + f"Missing metadata. Has function {action_func.__name__} been decorated with" + f" `{action.__name__}`?" ) -def get_func_input_from_llm( - messages: list[dict], llm_func: callable, completion: callable -): +def get_func_input_from_llm(messages: list[dict], func: callable, completion: callable): response = completion( messages=messages, - functions=[description(llm_func)], + functions=[description(func)], function_call={ - "name": description(llm_func)["name"] + "name": description(func)["name"] }, # force the function to be called ) # Validate the arg - pydantic_model = get_llm_func_pydantic_model(llm_func) + pydantic_model = get_pydantic_model_from_action_func(func) func_arg = pydantic_model( **json.loads(response.choices[0].message.function_call.arguments) ) @@ -93,98 +137,132 @@ def get_func_input_from_llm( return func_arg_message, func_arg -def run_func_for_llm(llm_func: callable, arg): - func_result = llm_func(arg) +def run_func_for_llm(func: callable, arg: Optional[BaseModel]): + func_result = func(arg) if arg else func() func_result_message = { "role": "function", - "name": llm_func.description_for_llm["name"], + "name": func.description_for_llm["name"], "content": json.dumps(func_result), } return func_result_message, func_result class Start: - pass + """Used to mark the start of the action graph.""" + + +class End: + """Can be used to end the action graph.""" + + description_for_llm = { + "name": "End", + "description": "End your assistance with immediate effect.", + "parameters": None, + } + + +def identity(x): + return x def run_agent( - agent_graph: dict[callable, list[callable]], - messages_init: list[dict], - completion: callable = default_completion, -): - messages = deepcopy(messages_init) - options = agent_graph[Start] + action_graph: Union[dict, callable], + messages_init: Optional[list[dict]] = None, + completion: Optional[callable] = default_completion, + pre_llm_callback: Optional[callable] = identity, +) -> Iterator[list[dict[str, str]]]: + """Run an agent. This is a generator that yields the list of messages after each step. + Be mindful that the yielded messages are mutable, allowing them to be modified in place, + (make copies if you want to avoid this). + + Parameters + ---------- + action_graph : Union[dict[callable, list[callable]], callable] + The graph of actions that the agent can take. Can either be a dictionary + or a callable. Use a callable to create a dynamic action graph. (See examples in the README) + messages_init : Optional[list[dict]], optional + Optionally initialise the list of messages, e.g. to specify a custom system prompt. + If not provided, the default system prompt will be used. + completion : Optional[callable], optional + The function that will be used to get completions from the LLM. + pre_llm_callback : Optional[callable], optional + This function is called before any LLM calls. + It will be passed the list of messages, and can modify it in place. + Can be used for, e.g. reducing the list of messages to only the most recent ones, + or reducing the list by summarising, etc. + + Yields + ------ + Iterator[list[dict[str, str]]] + At each step, the list of messages is yielded, + i.e. the same list that was yielded in the previous step, with one more message appended. + """ + if isinstance(action_graph, dict): + action_graph = dict_to_action_graph_func(action_graph) + messages = ( + deepcopy(messages_init) + if messages_init + else [{"role": "system", "content": default_system_prompt}] + ) + current_action = Start + current_action_result = None while True: - # Decide which function to run next - if len(options) == 1: - current_node = options[0] - arguments = json.dumps({"next_function": description(current_node)["name"]}) - # Pretend that we asked the LLM to select the next function (so it's in the history) - messages.append( - { - "role": "assistant", - "content": None, - "function_call": { - "name": "select_next_func", - "arguments": arguments, - }, - }, - ) - yield messages - messages.append( - { - "role": "function", - "name": "select_next_func", - "content": arguments, - }, - ) - yield messages + next_action_options = action_graph( + current_action=current_action, current_action_result=current_action_result + ) + if not next_action_options: + break + # DECIDE NEXT ACTION + if len(next_action_options) == 1: + current_action = next_action_options[0] else: - # llm decides the next function - select_next_func = select_next_func_factory(options) + pre_llm_callback(messages) + select_next_action: callable = select_next_action_factory(next_action_options) func_arg_message, func_arg = get_func_input_from_llm( - messages, select_next_func, completion + messages, select_next_action, completion ) messages.append(func_arg_message) - yield messages + yield deepcopy(messages) + pre_llm_callback(messages) func_result_message, func_result = run_func_for_llm( - select_next_func, func_arg + select_next_action, func_arg ) messages.append(func_result_message) - yield messages - next_function = func_result["next_function"] - current_node = next( - x for x in options if description(x)["name"] == next_function + yield deepcopy(messages) + current_action = next( + x for x in next_action_options if description(x)["name"] == func_result ) - # Run the function that was selected - func_arg_message, func_arg = get_func_input_from_llm( - messages, current_node, completion - ) + if current_action == End: + break + # RUN THE ACTION + if description(current_action)["parameters"]: + pre_llm_callback(messages) + func_arg_message, func_arg = get_func_input_from_llm( + messages, current_action, completion + ) + else: + func_arg_message = { + "role": "assistant", + "content": None, + "function_call": { + "name": description(current_action)["name"], + "arguments": "null", + }, + } + func_arg = None messages.append(func_arg_message) - yield messages - func_result_message, func_result = run_func_for_llm(current_node, func_arg) + yield deepcopy(messages) + pre_llm_callback(messages) + func_result_message, func_result = run_func_for_llm(current_action, func_arg) messages.append(func_result_message) - yield messages - options = agent_graph.get(current_node, None) - if not options: - break + yield deepcopy(messages) + current_action_result = func_result -def draw_graph(agent_graph: dict[callable, list[callable]]): - try: - import graphviz - from PIL import Image - except ImportError: - raise ImportError("You need to install graphviz and PIL to use this function.") - dot = graphviz.Digraph(comment="graph", format="png", graph_attr={"dpi": "120"}) - for node in agent_graph: - dot.node(node.__name__, node.__name__) - for node, children in agent_graph.items(): - if isinstance(children, list): - for child in children: - dot.edge(node.__name__, child.__name__) - else: - dot.edge(node.__name__, children.__name__) - gvz_graph = dot.pipe(format="png", engine="neato", renderer="cairo") - image = Image.open(io.BytesIO(gvz_graph), mode="r", formats=["png"]).convert("RGB") - return image +def dict_to_action_graph_func(action_graph: dict) -> callable: + def action_graph_func( + current_action: callable, current_action_result: Union[dict, None] + ): + return action_graph.get(current_action, None) + + return action_graph_func diff --git a/poetry.lock b/poetry.lock index 2f4b2a0..95e0338 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,6 +122,17 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + [[package]] name = "annotated-types" version = "0.5.0" @@ -205,6 +216,20 @@ files = [ pyflakes = ">=3.0.0" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +[[package]] +name = "babel" +version = "2.13.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, + {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + [[package]] name = "backcall" version = "0.2.0" @@ -722,6 +747,17 @@ files = [ {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] +[[package]] +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, +] + [[package]] name = "dotty-dict" version = "1.3.0" @@ -988,6 +1024,17 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + [[package]] name = "importlib-metadata" version = "6.8.0" @@ -1778,6 +1825,47 @@ files = [ {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, ] +[[package]] +name = "numpy" +version = "1.26.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "numpy-1.26.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd"}, + {file = "numpy-1.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be"}, + {file = "numpy-1.26.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3"}, + {file = "numpy-1.26.0-cp310-cp310-win32.whl", hash = "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896"}, + {file = "numpy-1.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c"}, + {file = "numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148"}, + {file = "numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229"}, + {file = "numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505"}, + {file = "numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69"}, + {file = "numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95"}, + {file = "numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c"}, + {file = "numpy-1.26.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49"}, + {file = "numpy-1.26.0-cp39-cp39-win32.whl", hash = "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b"}, + {file = "numpy-1.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299"}, + {file = "numpy-1.26.0.tar.gz", hash = "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf"}, +] + [[package]] name = "openai" version = "0.28.1" @@ -1811,6 +1899,74 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pandas" +version = "2.1.1" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58d997dbee0d4b64f3cb881a24f918b5f25dd64ddf31f467bb9b67ae4c63a1e4"}, + {file = "pandas-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02304e11582c5d090e5a52aec726f31fe3f42895d6bfc1f28738f9b64b6f0614"}, + {file = "pandas-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffa8f0966de2c22de408d0e322db2faed6f6e74265aa0856f3824813cf124363"}, + {file = "pandas-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1f84c144dee086fe4f04a472b5cd51e680f061adf75c1ae4fc3a9275560f8f4"}, + {file = "pandas-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ce97667d06d69396d72be074f0556698c7f662029322027c226fd7a26965cb"}, + {file = "pandas-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:4c3f32fd7c4dccd035f71734df39231ac1a6ff95e8bdab8d891167197b7018d2"}, + {file = "pandas-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e2959720b70e106bb1d8b6eadd8ecd7c8e99ccdbe03ee03260877184bb2877d"}, + {file = "pandas-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25e8474a8eb258e391e30c288eecec565bfed3e026f312b0cbd709a63906b6f8"}, + {file = "pandas-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8bd1685556f3374520466998929bade3076aeae77c3e67ada5ed2b90b4de7f0"}, + {file = "pandas-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc3657869c7902810f32bd072f0740487f9e030c1a3ab03e0af093db35a9d14e"}, + {file = "pandas-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:05674536bd477af36aa2effd4ec8f71b92234ce0cc174de34fd21e2ee99adbc2"}, + {file = "pandas-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:b407381258a667df49d58a1b637be33e514b07f9285feb27769cedb3ab3d0b3a"}, + {file = "pandas-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c747793c4e9dcece7bb20156179529898abf505fe32cb40c4052107a3c620b49"}, + {file = "pandas-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3bcad1e6fb34b727b016775bea407311f7721db87e5b409e6542f4546a4951ea"}, + {file = "pandas-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5ec7740f9ccb90aec64edd71434711f58ee0ea7f5ed4ac48be11cfa9abf7317"}, + {file = "pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29deb61de5a8a93bdd033df328441a79fcf8dd3c12d5ed0b41a395eef9cd76f0"}, + {file = "pandas-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f99bebf19b7e03cf80a4e770a3e65eee9dd4e2679039f542d7c1ace7b7b1daa"}, + {file = "pandas-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:84e7e910096416adec68075dc87b986ff202920fb8704e6d9c8c9897fe7332d6"}, + {file = "pandas-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366da7b0e540d1b908886d4feb3d951f2f1e572e655c1160f5fde28ad4abb750"}, + {file = "pandas-2.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e50e72b667415a816ac27dfcfe686dc5a0b02202e06196b943d54c4f9c7693e"}, + {file = "pandas-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1ab6a25da197f03ebe6d8fa17273126120874386b4ac11c1d687df288542dd"}, + {file = "pandas-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0dbfea0dd3901ad4ce2306575c54348d98499c95be01b8d885a2737fe4d7a98"}, + {file = "pandas-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0489b0e6aa3d907e909aef92975edae89b1ee1654db5eafb9be633b0124abe97"}, + {file = "pandas-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:4cdb0fab0400c2cb46dafcf1a0fe084c8bb2480a1fa8d81e19d15e12e6d4ded2"}, + {file = "pandas-2.1.1.tar.gz", hash = "sha256:fecb198dc389429be557cde50a2d46da8434a17fe37d7d41ff102e3987fd947b"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"] +aws = ["s3fs (>=2022.05.0)"] +clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"] +compression = ["zstandard (>=0.17.0)"] +computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2022.05.0)"] +gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"] +hdf5 = ["tables (>=3.7.0)"] +html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"] +mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"] +spss = ["pyreadstat (>=1.1.5)"] +sql-other = ["SQLAlchemy (>=1.4.36)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.8.0)"] + [[package]] name = "pandocfilters" version = "1.5.0" @@ -2345,6 +2501,17 @@ docs = ["Sphinx (<=6.0.0)", "furo (>=2023.3.27)", "sphinx-autobuild (==2021.03.1 mypy = ["mypy", "types-requests"] test = ["coverage[toml] (>=6,<8)", "pytest (>=7,<8)", "pytest-cov (>=4,<5)", "pytest-lazy-fixture (>=0.6.3,<0.7.0)", "pytest-mock (>=3,<4)", "pytest-pretty (>=1.2.0,<2)", "pytest-xdist (>=2,<4)", "requests-mock (>=1.10.0,<2)", "responses (==0.23.3)", "types-pytest-lazy-fixture (>=0.6.3.3)"] +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + [[package]] name = "pywin32" version = "306" @@ -2766,6 +2933,17 @@ files = [ {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + [[package]] name = "soupsieve" version = "2.4.1" @@ -2777,6 +2955,145 @@ files = [ {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, ] +[[package]] +name = "sphinx" +version = "7.2.6" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, + {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.21" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.7" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"}, + {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"}, +] + +[package.dependencies] +Sphinx = ">=5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.5" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"}, + {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"}, +] + +[package.dependencies] +Sphinx = ">=5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.4" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"}, + {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"}, +] + +[package.dependencies] +Sphinx = ">=5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.6" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"}, + {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"}, +] + +[package.dependencies] +Sphinx = ">=5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.9" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"}, + {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"}, +] + +[package.dependencies] +Sphinx = ">=5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + [[package]] name = "stack-data" version = "0.6.2" @@ -2927,6 +3244,17 @@ files = [ {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + [[package]] name = "urllib3" version = "2.0.4" @@ -3090,5 +3418,5 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" -python-versions = ">=3.9" -content-hash = "762eee41a78eafda03cb8db4351e5fef59def7f27d9b3117052e398f39c3109d" +python-versions = ">=3.9,<3.13" +content-hash = "e65b79c0ea4da19a7d444399fe7c2540d76f4b6c84773b00b3a8de2a7ed76a1f" diff --git a/pyproject.toml b/pyproject.toml index 426e075..4a08288 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ repository = "https://github.com/sradc/make_agents" [tool.poetry.dependencies] -python = ">=3.9" +python =">=3.9,<3.13" importlib-metadata = "^6.8.0" openai = "^0.28.1" tenacity = "^8.2.3" @@ -35,6 +35,8 @@ pre-commit = "^3.3.3" python-semantic-release = "^8.0.7" nbconvert = "^7.7.4" graphviz = "^0.20.1" +sphinx = "^7.2.6" +pandas = "^2.1.1" [tool.semantic_release] version_toml = [ diff --git a/tests/test_init.py b/tests/test_init.py deleted file mode 100644 index 487864b..0000000 --- a/tests/test_init.py +++ /dev/null @@ -1,5 +0,0 @@ -import make_agents - - -def test_version(): - assert isinstance(make_agents.__version__, str) diff --git a/tests/test_make_agents.py b/tests/test_make_agents.py index 067ee3d..ed93f91 100644 --- a/tests/test_make_agents.py +++ b/tests/test_make_agents.py @@ -1,14 +1,14 @@ import pytest from pydantic import BaseModel, Field -from make_agents.make_agents import llm_func +from make_agents.make_agents import action -def test_llm_func(): +def test_action_decorator(): class ExampleFuncArg(BaseModel): example_field: str = Field(description="This is an example field.") - @llm_func + @action def example_func(arg: ExampleFuncArg): """This is an example docstring""" return arg.example_field @@ -37,25 +37,25 @@ def example_func(arg: ExampleFuncArg): # Check that the function raises an error if there are multiple parameters with pytest.raises(ValueError): - @llm_func + @action def example_func(arg: ExampleFuncArg, arg2: ExampleFuncArg): pass # Check that the function doesn't raise an error if there are no parameters - @llm_func + @action def example_func(): pass # Check that the function raises an error if type hint not pydantic with pytest.raises(ValueError): - @llm_func + @action def example_func(arg: str): pass # Check that the function raises an error if no type hint with pytest.raises(ValueError): - @llm_func + @action def example_func(arg): pass